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

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

Small optimization, moved around some serial code to avoid a few jumps and unnecessary number of sectors check.

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