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

Last change on this file since 625 was 603, checked in by Krister Nordvall, 4 years ago

Changes:

  • Fixed a bug in Strings.asm from r589 where building the BIOS without MODULE_STRINGS_COMPRESSED would fail. Also removed a redundant CR to save a byte when building without MODULE_STRINGS_COMPRESSED.
  • Optimized the code in MODULE_HOTKEYS to reduce ROM space usage, stack usage and time spent in the user timer tick interrupt handler (1Ch). I hope this will fix the problem with hangs introduced in r599.
File size: 12.5 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 push si
41 push di
42 push bp
43
44;
45; Unpack I/O port and baud from DPT
46; Port to DX for the remainder of the routine (+/- different register offsets)
47; Baud in CH until UART initialization is complete
48;
49 mov ch, dh
50 xor dh, dh
51 eSHL_IM dx, 2 ; shift from one byte to two
52
53 mov al, [bp+SerialServer_Command.bSectorCount]
54 mov ah, [bp+SerialServer_Command.bCommand]
55
56;
57; Command byte and sector count live at the top of the stack, pop/push are used to access
58;
59 push ax ; save sector count for return value
60 push ax ; working copy on the top of the stack
61
62%ifndef EXCLUDE_FROM_XUB ; DF already cleared in Int13h.asm
63%ifdef CLD_NEEDED
64 cld
65%endif
66%endif
67
68;----------------------------------------------------------------------
69;
70; Initialize UART
71;
72; We do this each time since DOS (at boot) or another program may have
73; decided to reprogram the UART
74;
75 mov bl, dl ; setup BL with proper values for read/write loops (BH comes later)
76 mov al, 83h
77 add dl, Serial_UART_lineControl ; Clears CF
78 out dx, al
79
80 mov al, ch
81 mov dl, bl ; divisor low
82 out dx, al
83
84%ifdef USE_UNDOC_INTEL
85 salc ; Clear AL using CF
86%else
87 xor al, al
88%endif
89 inc dx ; divisor high
90 push dx
91 out dx, al
92
93 mov al, 47h
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
109%ifdef USE_UNDOC_INTEL
110 salc ; Clear AL using CF
111%else
112 xor al, al
113%endif
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
146%ifndef SERIALSERVER_NO_ZERO_SECTOR_COUNTS
147 test al, al ; if no sectors to be transferred, wait for the ACK checksum on the command
148 jz SHORT .zeroSectors
149%endif
150
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 sahf ; command byte, are we doing a write?
161 jnc SHORT .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 SHORT .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 SHORT .readByte1Ready
185 jmp SHORT .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 SHORT .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 mov ah, al ; store byte in ah for now
218
219;
220; note the placement of this reset of dl to bh, and that it is
221; before the return, which is assymetric with where this is done
222; above for byte 1. The value of dl is used by the timeout routine
223; to know which byte to return to (.read_byte1_ready or
224; .read_byte2_ready)
225;
226 mov dl, bh
227
228 in al, dx
229 shr al, 1 ; data ready (byte 2)?
230 jnc SHORT .readTimeout
231.readByte2Ready:
232 mov dl, bl
233 in al, dx ; read data byte 2
234 xchg al, ah ; ah was holding byte 1, reverse byte order
235 loop .readLoop
236 sti ; interrupts back on ASAP, between packets
237
238;
239; Compare checksums
240;
241 xchg ax, bp
242 xor ah, al
243 mov cx, si
244 xor cl, ch
245 mov al, cl
246 cmp ax, bp
247 jne SHORT SerialServer_OutputWithParameters_Error
248
249 pop ax ; sector count and command byte
250 dec al ; decrement sector count
251 push ax ; save
252 jz SHORT SerialServer_OutputWithParameters_ReturnCodeInAL
253
254 cli ; interrupts back off for ACK byte to host
255 ; (host could start sending data immediately)
256 out dx, al ; ACK with next sector number
257 jmp SHORT .nextSector
258
259;---------------------------------------------------------------------------
260;
261; Cleanup, error reporting, and exit
262;
263
264;
265; Used in situations where a call is underway, such as with SerialServer_WaitAndPoll
266;
267ALIGN JUMP_ALIGN
268SerialServer_OutputWithParameters_ErrorAndPop4Words:
269 add sp, 8
270;;; fall-through
271
272ALIGN JUMP_ALIGN
273SerialServer_OutputWithParameters_Error:
274;----------------------------------------------------------------------
275;
276; Clear read buffer
277;
278; In case there are extra characters or an error in the FIFO, clear it out.
279; In theory the initialization of the UART registers above should have
280; taken care of this, but I have seen cases where this is not true.
281;
282 xor cx, cx ; timeout this clearing routine, in case the UART isn't there
283.clearBuffer:
284 mov dl, bh
285 in al, dx
286 mov dl, bl
287 test al, 8Fh
288 jz SHORT .clearBufferComplete
289 test al, 1
290 in al, dx
291 loopnz .clearBuffer ; note ZF from test above
292
293.clearBufferComplete:
294 mov al, 1 ; error return code
295
296ALIGN JUMP_ALIGN
297SerialServer_OutputWithParameters_ReturnCodeInAL:
298%if 0
299 sti ; all paths here will already have interrupts turned back on
300%endif
301 mov ah, al ; for success, AL will already be zero
302
303 pop bx ; recover "ax" (command and count) from stack
304 pop cx ; recover saved sector count
305 xor ch, ch
306 sub cl, bl ; subtract off the number of sectors that remained
307
308 pop bp
309 pop di
310 pop si
311
312 sahf ; error return code to CF
313 ret
314
315
316;--------------------------------------------------------------------
317; SerialServer_WriteProtocol
318;
319; NOTE: As with its read counterpart, this loop is very time sensitive.
320; Although it will still function, adding additional instructions will
321; impact the write throughput, especially on slower machines.
322;
323; Parameters:
324; ES:SI: Ptr to buffer
325; CX: Words to write, plus 1
326; BP/DI: Initialized for Checksum (-1 in each)
327; DH: I/O Port high byte
328; BX: LineStatus Register address (BH) and Receive/Transmit Register address (BL)
329; Returns:
330; BP/SI: Checksum for written bytes, compared against ACK from server in .readLoop
331; CX: Zero
332; DL: Receive/Transmit Register address
333; ES:DI: Ptr to buffer
334; Corrupts registers:
335; AX
336;--------------------------------------------------------------------
337ALIGN JUMP_ALIGN
338SerialServer_WriteProtocol:
339.writeLoop:
340 es lodsw ; fetch next word
341
342 out dx, al ; output first byte
343
344 add bp, ax ; update checksum
345 adc bp, 0
346 add di, bp
347 adc di, 0
348
349 mov dl, bh ; transmit buffer empty?
350 in al, dx
351 test al, 20h
352 jz SHORT .writeTimeout2 ; nope, use our polling routine
353
354.writeByte2Ready:
355 mov dl, bl
356 mov al, ah ; output second byte
357 out dx, al
358
359.entry:
360 mov dl, bh ; transmit buffer empty?
361 in al, dx
362 test al, 20h
363 mov dl, bl
364 jz SHORT .writeTimeout1 ; nope, use our polling routine
365
366.writeByte1Ready:
367 loop .writeLoop
368
369 mov ax, di ; fold Fletcher's checksum and output
370 xor al, ah
371 out dx, al ; byte 1
372
373 call SerialServer_WaitAndPoll_Write
374
375 mov ax, bp
376 xor al, ah
377 out dx, al ; byte 2
378
379 xchg si, di ; preserve checksum word in si, move pointer back to di
380 ret
381
382.writeTimeout2:
383 mov dl, ah ; need to preserve AH, but don't need DL (will be reset upon return)
384 call SerialServer_WaitAndPoll_Write
385 mov ah, dl
386 jmp SHORT .writeByte2Ready
387
388.writeTimeout1:
389 ePUSH_T ax, .writeByte1Ready ; return address for ret at end of SC_writeTimeout2
390;;; fall-through
391
392;--------------------------------------------------------------------
393; SerialServer_WaitAndPoll
394;
395; Parameters:
396; AH: UART_LineStatus bit to test (20h for write, or 1h for read)
397; One entry point fills in AH with 20h for write
398; DX: Port address (OK if already incremented to UART_lineStatus)
399; BX:
400; Stack: 2 words on the stack below the command/count word
401; Returns:
402; Returns when desired UART_LineStatus bit is cleared
403; Jumps directly to error exit if timeout elapses (and cleans up stack)
404; Corrupts registers:
405; AX
406;--------------------------------------------------------------------
407
408SerialServer_WaitAndPoll_SoftDelayTicks EQU 20
409
410ALIGN JUMP_ALIGN
411SerialServer_WaitAndPoll_Write:
412 mov ah, 20h
413;;; fall-through
414
415ALIGN JUMP_ALIGN
416SerialServer_WaitAndPoll_Read:
417 push cx
418 push dx
419
420;
421; We first poll in a tight loop, interrupts off, for the next character to come in/be sent
422;
423 xor cx, cx
424.readTimeoutLoop:
425 mov dl, bh
426 in al, dx
427 test al, ah
428 jnz SHORT .readTimeoutComplete
429 loop .readTimeoutLoop
430
431;
432; If that loop completes, then we assume there is a long delay involved, turn interrupts back on
433; and wait for a given number of timer ticks to pass.
434;
435 sti
436%ifndef SERIALSERVER_TIMER_LOCATION
437 mov cl, SerialServer_WaitAndPoll_SoftDelayTicks
438 call Timer_InitializeTimeoutWithTicksInCL
439%else
440 push ax
441 push bx
442 mov ax, SerialServer_WaitAndPoll_SoftDelayTicks
443 mov bx, SERIALSERVER_TIMER_LOCATION
444 call TimerTicks_InitializeTimeoutFromAX
445 pop bx
446 pop ax
447%endif
448
449.WaitAndPoll:
450%ifndef SERIALSERVER_TIMER_LOCATION
451 call Timer_SetCFifTimeout
452%else
453 push ax
454 push bx
455 mov bx, SERIALSERVER_TIMER_LOCATION
456 call TimerTicks_GetTimeoutTicksLeftToAXfromDSBX
457 pop bx
458 pop ax
459%endif
460 jc SerialServer_OutputWithParameters_ErrorAndPop4Words
461 in al, dx
462 test al, ah
463 jz SHORT .WaitAndPoll
464 cli
465
466.readTimeoutComplete:
467 pop dx
468 pop cx
469 ret
470
Note: See TracBrowser for help on using the repository browser.