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

Last change on this file since 592 was 592, checked in by krille_n_, 6 years ago

Changes:

  • The problem with NASM in the previous revision (r591) has been fixed.
  • The colors used by the boot menu and hotkey bar can now be customized by selecting one of a number of pre-defined color themes. Suggestions for additional themes are more than welcome!
  • Large builds are now 10 KB. Small builds are still 8 KB with the exception of the Tiny build which is now 4 KB. In other words, builds are now as small as possible to make it easier to combine them with other BIOSes.
  • Added code to the library to improve drive error handling. XTIDECFG can now handle "Drive Not Ready" errors.
  • Fixed a couple of potential bugs in AtaID.asm (AtaID_GetMaxPioModeToAXandMinCycleTimeToCX); 1) ATA1.bPioMode was treated as a WORD variable. 2) ATA2.bPIOSupp was assumed to be non-zero which would result in PIO mode 3 being returned if the assumption was wrong.
  • Made the same changes in the equivalent function used by BIOSDRVS (DisplayPioModeInformationUsingAtaInfoFromDSBX in AtaInfo.asm).
  • Fixed a bug from r587 in PDC20x30.asm in PDC20x30_GetMaxPioModeToALandMinPioCycleTimeToBX.
  • Fixed a bug from r523 in XTIDECFG where Auto Configure would only set the IRQ on one IDE interface on AT-builds.
  • XTIDECFG will now restore the default settings for the "Serial port virtual device" when reselecting it in the list of device types. This makes it behave consistently for all device types.
  • The eAAM macro is now used regardless if USE_UNDOC_INTEL is defined or not because it is apparently supported on all processors including the NEC V20/V30 CPUs.
  • Renamed the EXCLUDE_FROM_XTIDE_UNIVERSAL_BIOS define to EXCLUDE_FROM_XUB.
  • Added a define to exclude unused library code from BIOSDRVS (EXCLUDE_FROM_BIOSDRVS). This makes it a lot smaller than in previous revisions.
  • All unnecessary CLD-instructions are now under a new define 'CLD_NEEDED' which is only enabled for the BIOS. It is disabled for XTIDECFG and BIOSDRVS but can be enabled if needed by adding this define to the respective makefile. This change was made because these unnecessary instructions are wasteful and should never be needed. In fact, they only serve to hide bugs (in other peoples code) which I strongly believe should be avoided. I recommend people making their own BIOSes from source to not use this define as it's extremely unlikely to be needed.
  • Updated the copyright info in SerDrive and changed an URL to point to the new site.
  • Updated the copyright info and version number in BIOSDRVS.
  • Updated the copyright info in XTIDECFG.
  • Optimizations in general.
File size: 12.4 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
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_XUB        ; DF already cleared in Int13h.asm
64%ifdef CLD_NEEDED
65        cld
66%endif
67%endif
68
69;----------------------------------------------------------------------
70;
71; Initialize UART
72;
73; We do this each time since DOS (at boot) or another program may have
74; decided to reprogram the UART
75;
76        mov     bl,dl           ; setup BL with proper values for read/write loops (BH comes later)
77
78        mov     al,83h
79        add     dl, Serial_UART_lineControl ; Clears CF
80        out     dx,al
81
82        mov     al,ch
83        mov     dl,bl           ; divisor low
84        out     dx,al
85
86%ifdef USE_UNDOC_INTEL
87        salc    ; Clear AL using CF
88%else
89        xor     al, al
90%endif
91        inc     dx              ; divisor high
92        push    dx
93        out     dx,al
94
95        mov     al,047h
96        inc     dx              ; fifo
97        out     dx,al
98
99        mov     al,03h
100        inc     dx              ; linecontrol
101        out     dx,al
102
103        mov     al,0bh
104        inc     dx              ; modemcontrol
105        out     dx,al
106
107        inc     dx              ; linestatus (no output now, just setting up BH for later use)
108        mov     bh,dl
109
110        pop     dx              ; base, interrupts disabled
111%ifdef USE_UNDOC_INTEL
112        salc    ; Clear AL using CF
113%else
114        xor     al, al
115%endif
116        out     dx,al
117
118;----------------------------------------------------------------------
119;
120; Send Command
121;
122; Sends first six bytes of IDEREGS_AND_INTPACK as the command
123;
124        push    es              ; save off real buffer location
125        push    si
126
127        mov     si,bp           ; point to IDEREGS for command dispatch;
128        push    ss
129        pop     es
130
131        mov     di,0ffffh       ; initialize checksum for write
132        mov     bp,di
133
134        mov     cx,4            ; writing 3 words (plus 1)
135
136        cli                     ; interrupts off...
137
138        call    SerialServer_WriteProtocol.entry
139
140        pop     di              ; restore real buffer location (note change from SI to DI)
141                                ; Buffer is primarily referenced through ES:DI throughout, since
142                                ; we need to store (read sector) faster than we read (write sector)
143        pop     es
144
145        pop     ax              ; load command byte (done before call to .nextSector on subsequent iterations)
146        push    ax
147
148%ifndef SERIALSERVER_NO_ZERO_SECTOR_COUNTS
149        test    al,al           ; if no sectors to be transferred, wait for the ACK checksum on the command
150        jz      .zeroSectors
151%endif
152
153;
154; Top of the read/write loop, one iteration per sector
155;
156.nextSector:
157        mov     si,0ffffh       ; initialize checksum for read or write
158        mov     bp,si
159
160        mov     cx,0101h        ; writing 256 words (plus 1)
161
162        sahf                    ; command byte, are we doing a write?
163        jnc     .readEntry
164
165        xchg    si,di           ; swap pointer and checksum, will be re-swap'ed in WriteProtocol
166        call    SerialServer_WriteProtocol.entry
167
168.zeroSectors:
169        inc     cx              ; CX = 1 now (0 out of WriteProtocol)
170        jmp     .readEntry
171
172;----------------------------------------------------------------------
173;
174; Timeout
175;
176; To save code space, we use the contents of DL to decide which byte in the word to return for reading.
177;
178.readTimeout:
179        push    ax              ; not only does this push preserve AX (which we need), but it also
180                                ; means the stack has the same number of bytes on it as when we are
181                                ; sending a packet, important for error cleanup and exit
182        mov     ah,1
183        call    SerialServer_WaitAndPoll_Read
184        pop     ax
185        test    dl,1
186        jz      .readByte1Ready
187        jmp     .readByte2Ready
188
189;----------------------------------------------------------------------------
190;
191; Read Block (without interrupts, used when there is a FIFO, high speed)
192;
193; NOTE: This loop is very time sensitive.  Literally, another instruction
194; cannot be inserted into this loop without us falling behind at high
195; speed (460.8K baud) on a 4.77Mhz 8088, making it hard to receive
196; a full 512 byte block.
197;
198.readLoop:
199        stosw                   ; store word in caller's data buffer
200
201        add     bp, ax          ; update Fletcher's checksum
202        adc     bp, 0
203        add     si, bp
204        adc     si, 0
205
206.readEntry:
207        mov     dl,bh
208        in      al,dx
209        shr     al,1            ; data ready (byte 1)?
210        mov     dl,bl           ; get ready to read data
211        jnc     .readTimeout    ; nope not ready, update timeouts
212
213;
214; Entry point after initial timeout.  We enter here so that the checksum word
215; is not stored (and is left in AX after the loop is complete).
216;
217.readByte1Ready:
218        in      al, dx          ; read data byte 1
219
220        mov     ah, al          ; store byte in ah for now
221
222;
223; note the placement of this reset of dl to bh, and that it is
224; before the return, which is assymetric with where this is done
225; above for byte 1.  The value of dl is used by the timeout routine
226; to know which byte to return to (.read_byte1_ready or
227; .read_byte2_ready)
228;
229        mov     dl,bh
230
231        in      al,dx
232        shr     al,1            ; data ready (byte 2)?
233        jnc     .readTimeout
234.readByte2Ready:
235        mov     dl,bl
236        in      al, dx          ; read data byte 2
237
238        xchg    al, ah          ; ah was holding byte 1, reverse byte order
239
240        loop    .readLoop
241
242        sti                     ; interrupts back on ASAP, between packets
243
244;
245; Compare checksums
246;
247        xchg    ax,bp
248        xor     ah,al
249        mov     cx,si
250        xor     cl,ch
251        mov     al,cl
252        cmp     ax,bp
253        jnz     SerialServer_OutputWithParameters_Error
254
255        pop     ax              ; sector count and command byte
256        dec     al              ; decrement sector count
257        push    ax              ; save
258        jz      SerialServer_OutputWithParameters_ReturnCodeInAL
259
260        cli                     ; interrupts back off for ACK byte to host
261                                ; (host could start sending data immediately)
262        out     dx,al           ; ACK with next sector number
263
264        jmp     short .nextSector
265
266;---------------------------------------------------------------------------
267;
268; Cleanup, error reporting, and exit
269;
270
271;
272; Used in situations where a call is underway, such as with SerialServer_WaitAndPoll
273;
274ALIGN JUMP_ALIGN
275SerialServer_OutputWithParameters_ErrorAndPop4Words:
276        add     sp,8
277;;; fall-through
278
279ALIGN JUMP_ALIGN
280SerialServer_OutputWithParameters_Error:
281;----------------------------------------------------------------------
282;
283; Clear read buffer
284;
285; In case there are extra characters or an error in the FIFO, clear it out.
286; In theory the initialization of the UART registers above should have
287; taken care of this, but I have seen cases where this is not true.
288;
289        xor     cx,cx           ; timeout this clearing routine, in case the UART isn't there
290.clearBuffer:
291        mov     dl,bh
292        in      al,dx
293        mov     dl,bl
294        test    al,08fh
295        jz      .clearBufferComplete
296        test    al,1
297        in      al,dx
298        loopnz  .clearBuffer    ; note ZF from test above
299
300.clearBufferComplete:
301        mov     al, 1           ; error return code
302
303ALIGN JUMP_ALIGN
304SerialServer_OutputWithParameters_ReturnCodeInAL:
305%if 0
306        sti                     ; all paths here will already have interrupts turned back on
307%endif
308        mov     ah, al          ; for success, AL will already be zero
309
310        pop     bx              ; recover "ax" (command and count) from stack
311        pop     cx              ; recover saved sector count
312        xor     ch, ch
313        sub     cl, bl          ; subtract off the number of sectors that remained
314
315        pop     bp
316        pop     di
317        pop     si
318
319        sahf                    ; error return code to CF
320
321        ret
322
323;--------------------------------------------------------------------
324; SerialServer_WriteProtocol
325;
326; NOTE: As with its read counterpart, this loop is very time sensitive.
327; Although it will still function, adding additional instructions will
328; impact the write throughput, especially on slower machines.
329;
330;   Parameters:
331;       ES:SI:  Ptr to buffer
332;       CX:     Words to write, plus 1
333;       BP/DI:  Initialized for Checksum (-1 in each)
334;       DH:     I/O Port high byte
335;       BX:     LineStatus Register address (BH) and Receive/Transmit Register address (BL)
336;   Returns:
337;       BP/SI:  Checksum for written bytes, compared against ACK from server in .readLoop
338;       CX:     Zero
339;       DL:     Receive/Transmit Register address
340;       ES:DI:  Ptr to buffer
341;   Corrupts registers:
342;       AX
343;--------------------------------------------------------------------
344ALIGN JUMP_ALIGN
345SerialServer_WriteProtocol:
346.writeLoop:
347        es lodsw                ; fetch next word
348
349        out     dx,al           ; output first byte
350
351        add     bp,ax           ; update checksum
352        adc     bp,0
353        add     di,bp
354        adc     di,0
355
356        mov     dl,bh           ; transmit buffer empty?
357        in      al,dx
358        test    al,20h
359        jz      .writeTimeout2  ; nope, use our polling routine
360
361.writeByte2Ready:
362        mov     dl,bl
363        mov     al,ah           ; output second byte
364        out     dx,al
365
366.entry:
367        mov     dl,bh           ; transmit buffer empty?
368        in      al,dx
369        test    al,20h
370        mov     dl,bl
371        jz      .writeTimeout1  ; nope, use our polling routine
372
373.writeByte1Ready:
374        loop    .writeLoop
375
376        mov     ax,di           ; fold Fletcher's checksum and output
377        xor     al,ah
378        out     dx,al           ; byte 1
379
380        call    SerialServer_WaitAndPoll_Write
381
382        mov     ax,bp
383        xor     al,ah
384        out     dx,al           ; byte 2
385
386        xchg    si,di           ; preserve checksum word in si, move pointer back to di
387
388        ret
389
390.writeTimeout2:
391        mov     dl,ah           ; need to preserve AH, but don't need DL (will be reset upon return)
392        call    SerialServer_WaitAndPoll_Write
393        mov     ah,dl
394        jmp     .writeByte2Ready
395
396.writeTimeout1:
397        ePUSH_T ax, .writeByte1Ready    ; return address for ret at end of SC_writeTimeout2
398;;; fall-through
399
400;--------------------------------------------------------------------
401; SerialServer_WaitAndPoll
402;
403;   Parameters:
404;       AH:     UART_LineStatus bit to test (20h for write, or 1h for read)
405;               One entry point fills in AH with 20h for write
406;       DX:     Port address (OK if already incremented to UART_lineStatus)
407;       BX:
408;       Stack:  2 words on the stack below the command/count word
409;   Returns:
410;       Returns when desired UART_LineStatus bit is cleared
411;       Jumps directly to error exit if timeout elapses (and cleans up stack)
412;   Corrupts registers:
413;       AX
414;--------------------------------------------------------------------
415
416SerialServer_WaitAndPoll_SoftDelayTicks   EQU   20
417
418ALIGN JUMP_ALIGN
419SerialServer_WaitAndPoll_Write:
420        mov     ah,20h
421;;; fall-through
422
423ALIGN JUMP_ALIGN
424SerialServer_WaitAndPoll_Read:
425        push    cx
426        push    dx
427
428;
429; We first poll in a tight loop, interrupts off, for the next character to come in/be sent
430;
431        xor     cx,cx
432.readTimeoutLoop:
433        mov     dl,bh
434        in      al,dx
435        test    al,ah
436        jnz     .readTimeoutComplete
437        loop    .readTimeoutLoop
438
439;
440; If that loop completes, then we assume there is a long delay involved, turn interrupts back on
441; and wait for a given number of timer ticks to pass.
442;
443        sti
444%ifndef SERIALSERVER_TIMER_LOCATION
445        mov     cl,SerialServer_WaitAndPoll_SoftDelayTicks
446        call    Timer_InitializeTimeoutWithTicksInCL
447%else
448        push    ax
449        push    bx
450        mov     ax,SerialServer_WaitAndPoll_SoftDelayTicks
451        mov     bx,SERIALSERVER_TIMER_LOCATION
452        call    TimerTicks_InitializeTimeoutFromAX
453        pop     bx
454        pop     ax
455%endif
456
457.WaitAndPoll:
458%ifndef SERIALSERVER_TIMER_LOCATION
459        call    Timer_SetCFifTimeout
460%else
461        push    ax
462        push    bx
463        mov     bx,SERIALSERVER_TIMER_LOCATION
464        call    TimerTicks_GetTimeoutTicksLeftToAXfromDSBX
465        pop     bx
466        pop     ax
467%endif
468        jc      SerialServer_OutputWithParameters_ErrorAndPop4Words
469        in      al,dx
470        test    al,ah
471        jz      .WaitAndPoll
472        cli
473
474.readTimeoutComplete:
475        pop     dx
476        pop     cx
477        ret
478
479
Note: See TracBrowser for help on using the repository browser.