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

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

Commit 1/2 (Library, Configurators and Serial Server):

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