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

Last change on this file since 292 was 292, checked in by gregli@…, 13 years ago

Small optimization, moved around some serial code to avoid a few jumps and unnecessary number of sectors check.

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