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

Last change on this file since 609 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
RevLine 
[277]1; Project name : Assembly Library
2; Description : Serial Server Support
3
[376]4;
[491]5; XTIDE Universal BIOS and Associated Tools
[526]6; Copyright (C) 2009-2010 by Tomi Tilli, 2011-2013 by XTIDE Universal BIOS Team.
[376]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;--------------------------------------------------------------------
[603]27; SerialServer_SendReceive
[277]28; Parameters:
[567]29; DX: Packed I/O port and baud rate
[277]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
[567]34; CX: Number of 512-byte blocks transferred
[277]35; CF: Cleared if success, Set if error
36; Corrupts registers:
37; AL, BX, CX, DX
38;--------------------------------------------------------------------
[293]39SerialServer_SendReceive:
[601]40 push si
41 push di
42 push bp
[293]43
[277]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;
[601]49 mov ch, dh
50 xor dh, dh
51 eSHL_IM dx, 2 ; shift from one byte to two
[277]52
[601]53 mov al, [bp+SerialServer_Command.bSectorCount]
54 mov ah, [bp+SerialServer_Command.bCommand]
[293]55
[277]56;
57; Command byte and sector count live at the top of the stack, pop/push are used to access
58;
[601]59 push ax ; save sector count for return value
60 push ax ; working copy on the top of the stack
[277]61
[601]62%ifndef EXCLUDE_FROM_XUB ; DF already cleared in Int13h.asm
[592]63%ifdef CLD_NEEDED
[601]64 cld
[293]65%endif
[592]66%endif
[277]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;
[601]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
[277]79
[601]80 mov al, ch
81 mov dl, bl ; divisor low
82 out dx, al
[277]83
[491]84%ifdef USE_UNDOC_INTEL
[601]85 salc ; Clear AL using CF
[491]86%else
[601]87 xor al, al
[491]88%endif
[601]89 inc dx ; divisor high
90 push dx
91 out dx, al
[277]92
[601]93 mov al, 47h
94 inc dx ; fifo
95 out dx, al
[277]96
[601]97 mov al, 03h
98 inc dx ; linecontrol
99 out dx, al
[277]100
[601]101 mov al, 0Bh
102 inc dx ; modemcontrol
103 out dx, al
[277]104
[601]105 inc dx ; linestatus (no output now, just setting up BH for later use)
106 mov bh, dl
[277]107
[601]108 pop dx ; base, interrupts disabled
[491]109%ifdef USE_UNDOC_INTEL
[601]110 salc ; Clear AL using CF
[491]111%else
[601]112 xor al, al
[491]113%endif
[601]114 out dx, al
[277]115
116;----------------------------------------------------------------------
117;
118; Send Command
119;
120; Sends first six bytes of IDEREGS_AND_INTPACK as the command
121;
[601]122 push es ; save off real buffer location
123 push si
[277]124
[601]125 mov si, bp ; point to IDEREGS for command dispatch;
126 push ss
127 pop es
[277]128
[601]129 mov di, 0FFFFh ; initialize checksum for write
130 mov bp, di
[277]131
[601]132 mov cx, 4 ; writing 3 words (plus 1)
[277]133
[601]134 cli ; interrupts off...
[277]135
[601]136 call SerialServer_WriteProtocol.entry
[277]137
[601]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
[277]142
[601]143 pop ax ; load command byte (done before call to .nextSector on subsequent iterations)
144 push ax
[277]145
[292]146%ifndef SERIALSERVER_NO_ZERO_SECTOR_COUNTS
[601]147 test al, al ; if no sectors to be transferred, wait for the ACK checksum on the command
148 jz SHORT .zeroSectors
[292]149%endif
[293]150
[277]151;
152; Top of the read/write loop, one iteration per sector
153;
154.nextSector:
[601]155 mov si, 0FFFFh ; initialize checksum for read or write
156 mov bp, si
[277]157
[601]158 mov cx, 0101h ; writing 256 words (plus 1)
[277]159
[601]160 sahf ; command byte, are we doing a write?
161 jnc SHORT .readEntry
[277]162
[601]163 xchg si, di ; swap pointer and checksum, will be re-swap'ed in WriteProtocol
164 call SerialServer_WriteProtocol.entry
[277]165
166.zeroSectors:
[601]167 inc cx ; CX = 1 now (0 out of WriteProtocol)
168 jmp SHORT .readEntry
[277]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:
[601]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
[277]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:
[601]197 stosw ; store word in caller's data buffer
[277]198
[601]199 add bp, ax ; update Fletcher's checksum
200 adc bp, 0
201 add si, bp
202 adc si, 0
[277]203
204.readEntry:
[601]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
[277]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:
[601]216 in al, dx ; read data byte 1
217 mov ah, al ; store byte in ah for now
[277]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;
[601]226 mov dl, bh
[277]227
[601]228 in al, dx
229 shr al, 1 ; data ready (byte 2)?
230 jnc SHORT .readTimeout
[277]231.readByte2Ready:
[601]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
[277]237
238;
239; Compare checksums
240;
[601]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
[277]248
[601]249 pop ax ; sector count and command byte
250 dec al ; decrement sector count
251 push ax ; save
252 jz SHORT SerialServer_OutputWithParameters_ReturnCodeInAL
[277]253
[601]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
[277]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:
[601]269 add sp, 8
[277]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;
[601]282 xor cx, cx ; timeout this clearing routine, in case the UART isn't there
[277]283.clearBuffer:
[601]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
[277]292
293.clearBufferComplete:
[601]294 mov al, 1 ; error return code
[277]295
296ALIGN JUMP_ALIGN
297SerialServer_OutputWithParameters_ReturnCodeInAL:
298%if 0
[601]299 sti ; all paths here will already have interrupts turned back on
[277]300%endif
[601]301 mov ah, al ; for success, AL will already be zero
[293]302
[601]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
[277]307
[601]308 pop bp
309 pop di
310 pop si
[277]311
[601]312 sahf ; error return code to CF
313 ret
[277]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
[567]331; CX: Zero
[277]332; DL: Receive/Transmit Register address
[567]333; ES:DI: Ptr to buffer
[277]334; Corrupts registers:
335; AX
336;--------------------------------------------------------------------
337ALIGN JUMP_ALIGN
338SerialServer_WriteProtocol:
339.writeLoop:
[601]340 es lodsw ; fetch next word
[277]341
[601]342 out dx, al ; output first byte
[277]343
[601]344 add bp, ax ; update checksum
345 adc bp, 0
346 add di, bp
347 adc di, 0
[277]348
[601]349 mov dl, bh ; transmit buffer empty?
350 in al, dx
351 test al, 20h
352 jz SHORT .writeTimeout2 ; nope, use our polling routine
[277]353
354.writeByte2Ready:
[601]355 mov dl, bl
356 mov al, ah ; output second byte
357 out dx, al
[277]358
359.entry:
[601]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
[277]365
366.writeByte1Ready:
[601]367 loop .writeLoop
[277]368
[601]369 mov ax, di ; fold Fletcher's checksum and output
370 xor al, ah
371 out dx, al ; byte 1
[277]372
[601]373 call SerialServer_WaitAndPoll_Write
[277]374
[601]375 mov ax, bp
376 xor al, ah
377 out dx, al ; byte 2
[277]378
[601]379 xchg si, di ; preserve checksum word in si, move pointer back to di
380 ret
[277]381
382.writeTimeout2:
[601]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
[277]387
388.writeTimeout1:
[601]389 ePUSH_T ax, .writeByte1Ready ; return address for ret at end of SC_writeTimeout2
[277]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)
[567]397; One entry point fills in AH with 20h for write
[277]398; DX: Port address (OK if already incremented to UART_lineStatus)
[567]399; BX:
400; Stack: 2 words on the stack below the command/count word
[277]401; Returns:
[567]402; Returns when desired UART_LineStatus bit is cleared
403; Jumps directly to error exit if timeout elapses (and cleans up stack)
[277]404; Corrupts registers:
[567]405; AX
[277]406;--------------------------------------------------------------------
407
408SerialServer_WaitAndPoll_SoftDelayTicks EQU 20
409
410ALIGN JUMP_ALIGN
411SerialServer_WaitAndPoll_Write:
[601]412 mov ah, 20h
[277]413;;; fall-through
414
415ALIGN JUMP_ALIGN
416SerialServer_WaitAndPoll_Read:
[601]417 push cx
418 push dx
[277]419
420;
421; We first poll in a tight loop, interrupts off, for the next character to come in/be sent
422;
[601]423 xor cx, cx
[277]424.readTimeoutLoop:
[601]425 mov dl, bh
426 in al, dx
427 test al, ah
428 jnz SHORT .readTimeoutComplete
429 loop .readTimeoutLoop
[277]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;
[601]435 sti
[566]436%ifndef SERIALSERVER_TIMER_LOCATION
[601]437 mov cl, SerialServer_WaitAndPoll_SoftDelayTicks
438 call Timer_InitializeTimeoutWithTicksInCL
[277]439%else
[601]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
[277]447%endif
[293]448
[277]449.WaitAndPoll:
450%ifndef SERIALSERVER_TIMER_LOCATION
[601]451 call Timer_SetCFifTimeout
[277]452%else
[601]453 push ax
454 push bx
455 mov bx, SERIALSERVER_TIMER_LOCATION
456 call TimerTicks_GetTimeoutTicksLeftToAXfromDSBX
457 pop bx
458 pop ax
[293]459%endif
[601]460 jc SerialServer_OutputWithParameters_ErrorAndPop4Words
461 in al, dx
462 test al, ah
463 jz SHORT .WaitAndPoll
464 cli
[277]465
466.readTimeoutComplete:
[601]467 pop dx
468 pop cx
469 ret
[277]470
Note: See TracBrowser for help on using the repository browser.