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

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

WIDE checkin... Added copyright and license information to sorce files, as per the GPL instructions for usage.

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