source: xtideuniversalbios/trunk/Assembly_Library/Src/Serial/SerialServer.asm @ 376

Last change on this file since 376 was 376, checked in by gregli@…, 12 years ago

WIDE checkin... Added copyright and license information to sorce files, as per the GPL instructions for usage.

File size: 12.3 KB
Line 
1; Project name  :   Assembly Library
2; Description   :   Serial Server Support
3
4;
5; XTIDE Universal BIOS and Associated Tools 
6; Copyright (C) 2009-2010 by Tomi Tilli, 2011-2012 by XTIDE Universal BIOS Team.
7;
8; This program is free software; you can redistribute it and/or modify
9; it under the terms of the GNU General Public License as published by
10; the Free Software Foundation; either version 2 of the License, or
11; (at your option) any later version.
12; 
13; This program is distributed in the hope that it will be useful,
14; but WITHOUT ANY WARRANTY; without even the implied warranty of
15; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16; GNU General Public License for more details.     
17; Visit http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
18;
19       
20
21%include "SerialServer.inc"
22
23; Section containing code
24SECTION .text
25
26;--------------------------------------------------------------------
27; SerialServer_SendReceive:
28;   Parameters:
29;       DX:     Packed I/O port and baud rate
30;       ES:SI:  Ptr to buffer (for data transfer commands)
31;       SS:BP:  Ptr to SerialServer_Command structure
32;   Returns:
33;       AH:     INT 13h Error Code
34;       CX:     Number of 512-byte blocks transferred
35;       CF:     Cleared if success, Set if error
36;   Corrupts registers:
37;       AL, BX, CX, DX
38;--------------------------------------------------------------------
39SerialServer_SendReceive:
40
41        push    si
42        push    di
43        push    bp
44
45;
46; Unpack I/O port and baud from DPT
47;       Port to DX for the remainder of the routine (+/- different register offsets)
48;       Baud in CH until UART initialization is complete
49;
50        mov     ch,dh
51        xor     dh,dh
52        eSHL_IM dx, 2           ; shift from one byte to two
53
54        mov     al,[bp+SerialServer_Command.bSectorCount]
55        mov     ah,[bp+SerialServer_Command.bCommand]
56
57;
58; Command byte and sector count live at the top of the stack, pop/push are used to access
59;
60        push    ax              ; save sector count for return value
61        push    ax              ; working copy on the top of the stack
62
63%ifndef EXCLUDE_FROM_XTIDE_UNIVERSAL_BIOS   ; DF already cleared in Int13h.asm
64        cld
65%endif
66
67;----------------------------------------------------------------------
68;
69; Initialize UART
70;
71; We do this each time since DOS (at boot) or another program may have
72; decided to reprogram the UART
73;
74        mov     bl,dl           ; setup BL with proper values for read/write loops (BH comes later)
75
76        mov     al,83h
77        add     dl,Serial_UART_lineControl
78        out     dx,al
79
80        mov     al,ch
81        mov     dl,bl           ; divisor low
82        out     dx,al
83
84        xor     ax,ax
85        inc     dx              ; divisor high
86        push    dx
87        out     dx,al
88
89        mov     al,047h
90        inc     dx              ;  fifo
91        out     dx,al
92
93        mov     al,03h
94        inc     dx              ;  linecontrol
95        out     dx,al
96
97        mov     al,0bh
98        inc     dx              ;  modemcontrol
99        out     dx,al
100
101        inc     dx              ;  linestatus (no output now, just setting up BH for later use)
102        mov     bh,dl
103
104        pop     dx              ; base, interrupts disabled
105        xor     ax,ax
106        out     dx,al
107
108;----------------------------------------------------------------------
109;
110; Send Command
111;
112; Sends first six bytes of IDEREGS_AND_INTPACK as the command
113;
114        push    es              ; save off real buffer location
115        push    si
116
117        mov     si,bp           ; point to IDEREGS for command dispatch;
118        push    ss
119        pop     es
120
121        mov     di,0ffffh       ; initialize checksum for write
122        mov     bp,di
123
124        mov     cx,4            ; writing 3 words (plus 1)
125
126        cli                     ; interrupts off...
127
128        call    SerialServer_WriteProtocol.entry
129
130        pop     di              ; restore real buffer location (note change from SI to DI)
131                                ; Buffer is primarily referenced through ES:DI throughout, since
132                                ; we need to store (read sector) faster than we read (write sector)
133        pop     es
134
135        pop     ax              ; load command byte (done before call to .nextSector on subsequent iterations)
136        push    ax
137
138%ifndef SERIALSERVER_NO_ZERO_SECTOR_COUNTS
139        test    al,al           ; if no sectors to be transferred, wait for the ACK checksum on the command
140        jz      .zeroSectors
141%endif
142
143;
144; Top of the read/write loop, one iteration per sector
145;
146.nextSector:
147        mov     si,0ffffh       ; initialize checksum for read or write
148        mov     bp,si
149
150        mov     cx,0101h        ; writing 256 words (plus 1)
151
152        shr     ah,1            ; command byte, are we doing a write?
153        jnc     .readEntry
154
155        xchg    si,di           ; swap pointer and checksum, will be re-swap'ed in WriteProtocol
156        call    SerialServer_WriteProtocol.entry
157
158.zeroSectors:
159        inc     cx              ; CX = 1 now (0 out of WriteProtocol)
160        jmp     .readEntry
161
162;----------------------------------------------------------------------
163;
164; Timeout
165;
166; To save code space, we use the contents of DL to decide which byte in the word to return for reading.
167;
168.readTimeout:
169        push    ax              ; not only does this push preserve AX (which we need), but it also
170                                ; means the stack has the same number of bytes on it as when we are
171                                ; sending a packet, important for error cleanup and exit
172        mov     ah,1
173        call    SerialServer_WaitAndPoll_Read
174        pop     ax
175        test    dl,1
176        jz      .readByte1Ready
177        jmp     .readByte2Ready
178
179;----------------------------------------------------------------------------
180;
181; Read Block (without interrupts, used when there is a FIFO, high speed)
182;
183; NOTE: This loop is very time sensitive.  Literally, another instruction
184; cannot be inserted into this loop without us falling behind at high
185; speed (460.8K baud) on a 4.77Mhz 8088, making it hard to receive
186; a full 512 byte block.
187;
188.readLoop:
189        stosw                   ; store word in caller's data buffer
190
191        add     bp, ax          ; update Fletcher's checksum
192        adc     bp, 0
193        add     si, bp
194        adc     si, 0
195
196.readEntry:
197        mov     dl,bh
198        in      al,dx
199        shr     al,1            ; data ready (byte 1)?
200        mov     dl,bl           ; get ready to read data
201        jnc     .readTimeout    ; nope not ready, update timeouts
202
203;
204; Entry point after initial timeout.  We enter here so that the checksum word
205; is not stored (and is left in AX after the loop is complete).
206;
207.readByte1Ready:
208        in      al, dx          ; read data byte 1
209
210        mov     ah, al          ; store byte in ah for now
211
212;
213; note the placement of this reset of dl to bh, and that it is
214; before the return, which is assymetric with where this is done
215; above for byte 1.  The value of dl is used by the timeout routine
216; to know which byte to return to (.read_byte1_ready or
217; .read_byte2_ready)
218;
219        mov     dl,bh
220
221        in      al,dx
222        shr     al,1            ; data ready (byte 2)?
223        jnc     .readTimeout
224.readByte2Ready:
225        mov     dl,bl
226        in      al, dx          ; read data byte 2
227
228        xchg    al, ah          ; ah was holding byte 1, reverse byte order
229
230        loop    .readLoop
231
232        sti                     ; interrupts back on ASAP, between packets
233
234;
235; Compare checksums
236;
237        xchg    ax,bp
238        xor     ah,al
239        mov     cx,si
240        xor     cl,ch
241        mov     al,cl
242        cmp     ax,bp
243        jnz     SerialServer_OutputWithParameters_Error
244
245        pop     ax              ; sector count and command byte
246        dec     al              ; decrement sector count
247        push    ax              ; save
248        jz      SerialServer_OutputWithParameters_ReturnCodeInAL
249
250        cli                     ; interrupts back off for ACK byte to host
251                                ; (host could start sending data immediately)
252        out     dx,al           ; ACK with next sector number
253
254        jmp     short .nextSector
255
256;---------------------------------------------------------------------------
257;
258; Cleanup, error reporting, and exit
259;
260
261;
262; Used in situations where a call is underway, such as with SerialServer_WaitAndPoll
263;
264ALIGN JUMP_ALIGN
265SerialServer_OutputWithParameters_ErrorAndPop4Words:
266        add     sp,8
267;;; fall-through
268
269ALIGN JUMP_ALIGN
270SerialServer_OutputWithParameters_Error:
271;----------------------------------------------------------------------
272;
273; Clear read buffer
274;
275; In case there are extra characters or an error in the FIFO, clear it out.
276; In theory the initialization of the UART registers above should have
277; taken care of this, but I have seen cases where this is not true.
278;
279        xor     cx,cx                   ; timeout this clearing routine, in case the UART isn't there
280.clearBuffer:
281        mov     dl,bh
282        in      al,dx
283        mov     dl,bl
284        test    al,08fh
285        jz      .clearBufferComplete
286        test    al,1
287        in      al,dx
288        loopnz  .clearBuffer            ; note ZF from test above
289
290.clearBufferComplete:
291        mov     al, 3           ;  error return code and CF (low order bit)
292
293ALIGN JUMP_ALIGN
294SerialServer_OutputWithParameters_ReturnCodeInAL:
295%if 0
296        sti                     ;  all paths here will already have interrupts turned back on
297%endif
298        mov     ah, al          ;  for success, AL will already be zero
299
300        pop     bx              ;  recover "ax" (command and count) from stack
301        pop     cx              ;  recover saved sector count
302        xor     ch, ch
303        sub     cl, bl          ; subtract off the number of sectors that remained
304
305        pop     bp
306        pop     di
307        pop     si
308
309        shr     ah, 1           ; shift down return code and CF
310
311        ret
312
313;--------------------------------------------------------------------
314; SerialServer_WriteProtocol
315;
316; NOTE: As with its read counterpart, this loop is very time sensitive.
317; Although it will still function, adding additional instructions will
318; impact the write throughput, especially on slower machines.
319;
320;   Parameters:
321;       ES:SI:  Ptr to buffer
322;       CX:     Words to write, plus 1
323;       BP/DI:  Initialized for Checksum (-1 in each)
324;       DH:     I/O Port high byte
325;       BX:     LineStatus Register address (BH) and Receive/Transmit Register address (BL)
326;   Returns:
327;       BP/SI:  Checksum for written bytes, compared against ACK from server in .readLoop
328;       CX:     Zero
329;       DL:     Receive/Transmit Register address
330;       ES:DI:  Ptr to buffer
331;   Corrupts registers:
332;       AX
333;--------------------------------------------------------------------
334ALIGN JUMP_ALIGN
335SerialServer_WriteProtocol:
336.writeLoop:
337        es lodsw                ; fetch next word
338
339        out     dx,al           ; output first byte
340
341        add     bp,ax           ; update checksum
342        adc     bp,0
343        add     di,bp
344        adc     di,0
345
346        mov     dl,bh           ; transmit buffer empty?
347        in      al,dx
348        test    al,20h
349        jz      .writeTimeout2  ; nope, use our polling routine
350
351.writeByte2Ready:
352        mov     dl,bl
353        mov     al,ah           ; output second byte
354        out     dx,al
355
356.entry:
357        mov     dl,bh           ; transmit buffer empty?
358        in      al,dx
359        test    al,20h
360        mov     dl,bl
361        jz      .writeTimeout1  ; nope, use our polling routine
362
363.writeByte1Ready:
364        loop    .writeLoop
365
366        mov     ax,di           ; fold Fletcher's checksum and output
367        xor     al,ah
368        out     dx,al           ; byte 1
369
370        call    SerialServer_WaitAndPoll_Write
371
372        mov     ax,bp
373        xor     al,ah
374        out     dx,al           ; byte 2
375
376        xchg    si,di           ; preserve checksum word in si, move pointer back to di
377
378        ret
379
380.writeTimeout2:
381        mov     dl,ah           ; need to preserve AH, but don't need DL (will be reset upon return)
382        call    SerialServer_WaitAndPoll_Write
383        mov     ah,dl
384        jmp     .writeByte2Ready
385
386.writeTimeout1:
387        ePUSH_T ax, .writeByte1Ready    ; return address for ret at end of SC_writeTimeout2
388;;; fall-through
389
390;--------------------------------------------------------------------
391; SerialServer_WaitAndPoll
392;
393;   Parameters:
394;       AH:     UART_LineStatus bit to test (20h for write, or 1h for read)
395;               One entry point fills in AH with 20h for write
396;       DX:     Port address (OK if already incremented to UART_lineStatus)
397;       BX:
398;       Stack:  2 words on the stack below the command/count word
399;   Returns:
400;       Returns when desired UART_LineStatus bit is cleared
401;       Jumps directly to error exit if timeout elapses (and cleans up stack)
402;   Corrupts registers:
403;       AX
404;--------------------------------------------------------------------
405
406SerialServer_WaitAndPoll_SoftDelayTicks   EQU   20
407
408ALIGN JUMP_ALIGN
409SerialServer_WaitAndPoll_Write:
410        mov     ah,20h
411;;; fall-through
412
413ALIGN JUMP_ALIGN
414SerialServer_WaitAndPoll_Read:
415        push    cx
416        push    dx
417
418;
419; We first poll in a tight loop, interrupts off, for the next character to come in/be sent
420;
421        xor     cx,cx
422.readTimeoutLoop:
423        mov     dl,bh
424        in      al,dx
425        test    al,ah
426        jnz     .readTimeoutComplete
427        loop    .readTimeoutLoop
428
429;
430; If that loop completes, then we assume there is a long delay involved, turn interrupts back on
431; and wait for a given number of timer ticks to pass.
432;
433        sti
434        mov     cl,SerialServer_WaitAndPoll_SoftDelayTicks
435%ifndef SERIALSERVER_TIMER_LOCATION
436        call    Timer_InitializeTimeoutWithTicksInCL
437%else
438        push    ax
439        push    bx
440        mov     ax,SerialServer_WaitAndPoll_SoftDelayTicks
441        mov     bx,SERIALSERVER_TIMER_LOCATION
442        call    TimerTicks_InitializeTimeoutFromAX
443        pop     bx
444        pop     ax
445%endif
446
447.WaitAndPoll:
448%ifndef SERIALSERVER_TIMER_LOCATION
449        call    Timer_SetCFifTimeout
450%else
451        push    ax
452        push    bx
453        mov     bx,SERIALSERVER_TIMER_LOCATION
454        call    TimerTicks_GetTimeoutTicksLeftToAXfromDSBX
455        pop     bx
456        pop     ax
457%endif
458        jc      SerialServer_OutputWithParameters_ErrorAndPop4Words
459        in      al,dx
460        test    al,ah
461        jz      .WaitAndPoll
462        cli
463
464.readTimeoutComplete:
465        pop     dx
466        pop     cx
467        ret
468
469
Note: See TracBrowser for help on using the repository browser.