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

Last change on this file since 601 was 601, checked in by krille_n_, 5 years ago

Changes:

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