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

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

Moved the bulk of the serial code to the assembly library, for inclusion in other utilities. Fixed a bug in int13h.asm when floppy support was not enabled that was preventing foreign drives from working properly.

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