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

Last change on this file since 340 was 293, checked in by krille_n_@…, 13 years ago

Commit 1/2 (Library, Configurators and Serial Server):

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