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

Last change on this file since 596 was 592, checked in by Krister Nordvall, 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.