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

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

Changes:

  • Added a new define (USE_UNDOC_INTEL) that enables optimizations possible by using undocumented instructions available on all Intel processors and truly compatible clones. AFAIK the only exceptions are the NEC V-series and the Sony CXQ70108 processors so this option should be safe for use on the AT builds.
  • Building BIOSDRVS or the BIOS without MODULE_STRINGS_COMPRESSED would fail due to the recent code exclusions so I changed them a bit. Also fixed the mistaken change to Main.asm
  • Changed the Tandy specific info in Configuration_FullMode.txt so it matches the info in the Wiki.
  • Optimizations and fixes in general.
File size: 12.4 KB
RevLine 
[277]1; Project name : Assembly Library
2; Description : Serial Server Support
3
[376]4;
[491]5; XTIDE Universal BIOS and Associated Tools
[376]6; Copyright (C) 2009-2010 by Tomi Tilli, 2011-2012 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.
[491]12;
[376]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
[491]16; GNU General Public License for more details.
[376]17; Visit http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
18;
19
[491]20
[277]21%include "SerialServer.inc"
[293]22
[277]23; Section containing code
24SECTION .text
25
26;--------------------------------------------------------------------
[293]27; SerialServer_SendReceive:
[277]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;--------------------------------------------------------------------
[293]39SerialServer_SendReceive:
40
[277]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]
[293]56
[277]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
[293]63%ifndef EXCLUDE_FROM_XTIDE_UNIVERSAL_BIOS ; DF already cleared in Int13h.asm
[277]64 cld
[293]65%endif
[277]66
67;----------------------------------------------------------------------
68;
69; Initialize UART
70;
71; We do this each time since DOS (at boot) or another program may have
72; decided to reprogram the UART
73;
74 mov bl,dl ; setup BL with proper values for read/write loops (BH comes later)
75
76 mov al,83h
[491]77 add dl, Serial_UART_lineControl ; Clears CF
[277]78 out dx,al
79
80 mov al,ch
81 mov dl,bl ; divisor low
82 out dx,al
83
[491]84%ifdef USE_UNDOC_INTEL
85 eSALC ; Clear AL using CF
86%else
[277]87 xor ax,ax
[491]88%endif
[277]89 inc dx ; divisor high
90 push dx
91 out dx,al
92
93 mov al,047h
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
[491]109%ifdef USE_UNDOC_INTEL
110 eSALC ; Clear AL using CF
111%else
[277]112 xor ax,ax
[491]113%endif
[277]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
[292]146%ifndef SERIALSERVER_NO_ZERO_SECTOR_COUNTS
[277]147 test al,al ; if no sectors to be transferred, wait for the ACK checksum on the command
148 jz .zeroSectors
[292]149%endif
[293]150
[277]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 shr ah,1 ; command byte, are we doing a write?
161 jnc .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 .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 .readByte1Ready
185 jmp .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 .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
218 mov ah, al ; store byte in ah for now
219
220;
221; note the placement of this reset of dl to bh, and that it is
222; before the return, which is assymetric with where this is done
223; above for byte 1. The value of dl is used by the timeout routine
224; to know which byte to return to (.read_byte1_ready or
225; .read_byte2_ready)
226;
227 mov dl,bh
228
229 in al,dx
230 shr al,1 ; data ready (byte 2)?
231 jnc .readTimeout
232.readByte2Ready:
233 mov dl,bl
234 in al, dx ; read data byte 2
235
236 xchg al, ah ; ah was holding byte 1, reverse byte order
237
238 loop .readLoop
239
240 sti ; interrupts back on ASAP, between packets
241
242;
243; Compare checksums
244;
245 xchg ax,bp
246 xor ah,al
247 mov cx,si
248 xor cl,ch
249 mov al,cl
250 cmp ax,bp
251 jnz SerialServer_OutputWithParameters_Error
252
253 pop ax ; sector count and command byte
254 dec al ; decrement sector count
255 push ax ; save
256 jz SerialServer_OutputWithParameters_ReturnCodeInAL
257
258 cli ; interrupts back off for ACK byte to host
259 ; (host could start sending data immediately)
260 out dx,al ; ACK with next sector number
261
262 jmp short .nextSector
263
264;---------------------------------------------------------------------------
265;
266; Cleanup, error reporting, and exit
267;
268
269;
270; Used in situations where a call is underway, such as with SerialServer_WaitAndPoll
271;
272ALIGN JUMP_ALIGN
273SerialServer_OutputWithParameters_ErrorAndPop4Words:
274 add sp,8
275;;; fall-through
276
277ALIGN JUMP_ALIGN
278SerialServer_OutputWithParameters_Error:
279;----------------------------------------------------------------------
280;
281; Clear read buffer
282;
283; In case there are extra characters or an error in the FIFO, clear it out.
284; In theory the initialization of the UART registers above should have
285; taken care of this, but I have seen cases where this is not true.
286;
287 xor cx,cx ; timeout this clearing routine, in case the UART isn't there
288.clearBuffer:
289 mov dl,bh
290 in al,dx
291 mov dl,bl
292 test al,08fh
293 jz .clearBufferComplete
294 test al,1
295 in al,dx
296 loopnz .clearBuffer ; note ZF from test above
297
298.clearBufferComplete:
299 mov al, 3 ; error return code and CF (low order bit)
300
301ALIGN JUMP_ALIGN
302SerialServer_OutputWithParameters_ReturnCodeInAL:
303%if 0
304 sti ; all paths here will already have interrupts turned back on
305%endif
306 mov ah, al ; for success, AL will already be zero
[293]307
[277]308 pop bx ; recover "ax" (command and count) from stack
309 pop cx ; recover saved sector count
[293]310 xor ch, ch
[277]311 sub cl, bl ; subtract off the number of sectors that remained
312
313 pop bp
314 pop di
315 pop si
316
317 shr ah, 1 ; shift down return code and CF
318
319 ret
320
321;--------------------------------------------------------------------
322; SerialServer_WriteProtocol
323;
324; NOTE: As with its read counterpart, this loop is very time sensitive.
325; Although it will still function, adding additional instructions will
326; impact the write throughput, especially on slower machines.
327;
328; Parameters:
329; ES:SI: Ptr to buffer
330; CX: Words to write, plus 1
331; BP/DI: Initialized for Checksum (-1 in each)
332; DH: I/O Port high byte
333; BX: LineStatus Register address (BH) and Receive/Transmit Register address (BL)
334; Returns:
335; BP/SI: Checksum for written bytes, compared against ACK from server in .readLoop
336; CX: Zero
337; DL: Receive/Transmit Register address
338; ES:DI: Ptr to buffer
339; Corrupts registers:
340; AX
341;--------------------------------------------------------------------
342ALIGN JUMP_ALIGN
343SerialServer_WriteProtocol:
344.writeLoop:
345 es lodsw ; fetch next word
346
347 out dx,al ; output first byte
348
349 add bp,ax ; update checksum
350 adc bp,0
351 add di,bp
352 adc di,0
353
354 mov dl,bh ; transmit buffer empty?
355 in al,dx
356 test al,20h
357 jz .writeTimeout2 ; nope, use our polling routine
358
359.writeByte2Ready:
360 mov dl,bl
361 mov al,ah ; output second byte
362 out dx,al
363
364.entry:
365 mov dl,bh ; transmit buffer empty?
366 in al,dx
367 test al,20h
368 mov dl,bl
369 jz .writeTimeout1 ; nope, use our polling routine
370
371.writeByte1Ready:
372 loop .writeLoop
373
374 mov ax,di ; fold Fletcher's checksum and output
375 xor al,ah
376 out dx,al ; byte 1
377
378 call SerialServer_WaitAndPoll_Write
379
380 mov ax,bp
381 xor al,ah
382 out dx,al ; byte 2
383
384 xchg si,di ; preserve checksum word in si, move pointer back to di
385
386 ret
387
388.writeTimeout2:
389 mov dl,ah ; need to preserve AH, but don't need DL (will be reset upon return)
390 call SerialServer_WaitAndPoll_Write
391 mov ah,dl
392 jmp .writeByte2Ready
393
394.writeTimeout1:
[293]395 ePUSH_T ax, .writeByte1Ready ; return address for ret at end of SC_writeTimeout2
[277]396;;; fall-through
397
398;--------------------------------------------------------------------
399; SerialServer_WaitAndPoll
400;
401; Parameters:
402; AH: UART_LineStatus bit to test (20h for write, or 1h for read)
403; One entry point fills in AH with 20h for write
404; DX: Port address (OK if already incremented to UART_lineStatus)
[293]405; BX:
[277]406; Stack: 2 words on the stack below the command/count word
407; Returns:
408; Returns when desired UART_LineStatus bit is cleared
409; Jumps directly to error exit if timeout elapses (and cleans up stack)
410; Corrupts registers:
411; AX
412;--------------------------------------------------------------------
413
414SerialServer_WaitAndPoll_SoftDelayTicks EQU 20
415
416ALIGN JUMP_ALIGN
417SerialServer_WaitAndPoll_Write:
418 mov ah,20h
419;;; fall-through
420
421ALIGN JUMP_ALIGN
422SerialServer_WaitAndPoll_Read:
423 push cx
424 push dx
425
426;
427; We first poll in a tight loop, interrupts off, for the next character to come in/be sent
428;
429 xor cx,cx
430.readTimeoutLoop:
431 mov dl,bh
432 in al,dx
433 test al,ah
434 jnz .readTimeoutComplete
435 loop .readTimeoutLoop
436
437;
438; If that loop completes, then we assume there is a long delay involved, turn interrupts back on
439; and wait for a given number of timer ticks to pass.
440;
441 sti
442 mov cl,SerialServer_WaitAndPoll_SoftDelayTicks
443%ifndef SERIALSERVER_TIMER_LOCATION
444 call Timer_InitializeTimeoutWithTicksInCL
445%else
446 push ax
447 push bx
448 mov ax,SerialServer_WaitAndPoll_SoftDelayTicks
449 mov bx,SERIALSERVER_TIMER_LOCATION
450 call TimerTicks_InitializeTimeoutFromAX
451 pop bx
452 pop ax
453%endif
[293]454
[277]455.WaitAndPoll:
456%ifndef SERIALSERVER_TIMER_LOCATION
457 call Timer_SetCFifTimeout
458%else
459 push ax
460 push bx
461 mov bx,SERIALSERVER_TIMER_LOCATION
462 call TimerTicks_GetTimeoutTicksLeftToAXfromDSBX
463 pop bx
464 pop ax
[293]465%endif
[277]466 jc SerialServer_OutputWithParameters_ErrorAndPop4Words
467 in al,dx
468 test al,ah
469 jz .WaitAndPoll
470 cli
471
472.readTimeoutComplete:
473 pop dx
474 pop cx
475 ret
476
477
Note: See TracBrowser for help on using the repository browser.