source: xtideuniversalbios/trunk/XTIDE_Universal_BIOS/Src/Device/Serial/SerialCommand.asm@ 207

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

Added buffer pointer denormalization check within the serial port code and reused the last portion of the NormalizeESSI and NormalizeESDI routines to save memory.

File size: 19.1 KB
Line 
1; Project name : XTIDE Universal BIOS
2; Description : Serial Device Command functions.
3
4; Section containing code
5SECTION .text
6
7;--------------- UART Equates -----------------------------
8;
9; Serial Programming References:
10; http://en.wikibooks.org/wiki/Serial_Programming
11;
12
13SerialCommand_UART_base EQU 0
14SerialCommand_UART_transmitByte EQU 0
15SerialCommand_UART_receiveByte EQU 0
16SerialCommand_UART_divisorLow EQU 0
17; Values for UART_divisorLow:
18; 60h = 1200, 30h = 2400, 18h = 4800, 0ch = 9600, 6 = 19200, 3 = 38400, 2 = 57600, 1 = 115200
19
20SerialCommand_UART_divisorLow_startingBaud EQU 030h
21; We support 4 baud rates, starting here going higher and skipping every other baud rate
22; Starting with 30h, that means 30h (1200 baud), 0ch (9600 baud), 3 (38400 baud), and 1 (115200 baud)
23; Note: hardware baud multipliers (2x, 4x) will impact the final baud rate and are not known at this level
24
25SerialCommand_UART_interruptEnable EQU 1
26SerialCommand_UART_divisorHigh EQU 1
27; UART_divisorHigh is zero for all speeds including and above 1200 baud
28
29SerialCommand_UART_interruptIdent EQU 2
30SerialCommand_UART_FIFOControl EQU 2
31
32SerialCommand_UART_lineControl EQU 3
33
34SerialCommand_UART_modemControl EQU 4
35
36SerialCommand_UART_lineStatus EQU 5
37
38SerialCommand_UART_modemStatus EQU 6
39
40SerialCommand_UART_scratch EQU 7
41
42SerialCommand_Protocol_Write EQU 3
43SerialCommand_Protocol_Read EQU 2
44SerialCommand_Protocol_Inquire EQU 0
45SerialCommand_Protocol_Header EQU 0a0h
46
47;--------------------------------------------------------------------
48; SerialCommand_OutputWithParameters
49; Parameters:
50; BH: Non-zero if 48-bit addressing used
51; (ignored at present as 48-bit addressing is not supported)
52; BL: IDE Status Register bit to poll after command
53; (ignored at present, since there is no IDE status register to poll)
54; ES:SI: Ptr to buffer (for data transfer commands)
55; DS:DI: Ptr to DPT (in RAMVARS segment)
56; SS:BP: Ptr to IDEREGS_AND_INTPACK
57; Returns:
58; AH: INT 13h Error Code
59; CF: Cleared if success, Set if error
60; Corrupts registers:
61; AL, BX, CX, DX, (ES:SI for data transfer commands)
62;--------------------------------------------------------------------
63ALIGN JUMP_ALIGN
64SerialCommand_OutputWithParameters:
65
66 mov ah,(SerialCommand_Protocol_Header | SerialCommand_Protocol_Read)
67
68 mov al,[bp+IDEPACK.bCommand]
69
70 cmp al,20h ; Read Sectors IDE command
71 jz .readOrWrite
72 inc ah ; now SerialCommand_Protocol_Write
73 cmp al,30h ; Write Sectors IDE command
74 jz .readOrWrite
75
76; all other commands return success
77; including function 0ech which should return drive information, this is handled with the identify functions
78 xor ah,ah ; also clears carry
79 ret
80
81.readOrWrite:
82 mov [bp+IDEPACK.bFeatures],ah ; store protocol command
83
84 mov dl, byte [di+DPT.bSerialPortAndBaud]
85
86; fall-through
87
88;--------------------------------------------------------------------
89; SerialCommand_OutputWithParameters_DeviceInDL
90; Parameters:
91; AH: Protocol Command
92; DL: Packed I/O port and baud rate
93; ES:SI: Ptr to buffer (for data transfer commands)
94; SS:BP: Ptr to IDEREGS_AND_INTPACK
95; Returns:
96; AH: INT 13h Error Code
97; CF: Cleared if success, Set if error
98; Corrupts registers:
99; AL, BX, CX, DX, (ES:SI for data transfer commands)
100;--------------------------------------------------------------------
101SerialCommand_OutputWithParameters_DeviceInDL:
102
103 push si
104 push di
105 push bp
106 push es
107
108;
109; Unpack I/O port and baud from DPT
110; Port to DX more or less for the remainder of the routine
111; Baud in CH until UART initialization is complete
112;
113 mov cl, dl
114
115 and cl, DEVICE_SERIAL_PACKEDPORTANDBAUD_BAUDMASK
116 shl cl, 1
117 mov ch, SerialCommand_UART_divisorLow_startingBaud
118 shr ch, cl
119 adc ch, 0
120
121 and dl, DEVICE_SERIAL_PACKEDPORTANDBAUD_PORTMASK
122 mov dh, 0
123 shl dx, 1 ; port offset already x4, needs one more shift to be x8
124 add dx, DEVICE_SERIAL_PACKEDPORTANDBAUD_STARTINGPORT
125
126;
127; Buffer is referenced through ES:DI throughout, since we need to store faster than we read
128;
129 mov di,si
130
131 mov al,[bp+IDEPACK.bSectorCount]
132
133;
134; Command byte and sector count live at the top of the stack, pop/push are used to access
135;
136 push ax
137
138; cld ; Shouldn't be needed. DF has already been cleared (line 24, Int13h.asm)
139
140;----------------------------------------------------------------------
141;
142; Initialize UART
143;
144; We do this each time since DOS (at boot) or another program may have
145; decided to reprogram the UART
146;
147 push dx
148
149 mov al,83h
150 add dl,SerialCommand_UART_lineControl
151 out dx,al
152
153 mov al,ch
154 pop dx ; divisor low
155 out dx,al
156
157 xor ax,ax
158 inc dx ; divisor high
159 push dx
160 out dx,al
161
162 mov al,047h
163 inc dx ; fifo
164 out dx,al
165
166 mov al,03h
167 inc dx ; linecontrol
168 out dx,al
169
170 mov al,0bh
171 inc dx ; modemcontrol
172 out dx,al
173
174 pop dx ; base, interrupts disabled
175 xor ax,ax
176 out dx,al
177 dec dx
178
179;
180; Start off with a normalized buffer pointer
181;
182 call Registers_NormalizeESDI
183
184;----------------------------------------------------------------------
185;
186; Send Command
187;
188; Sends first six bytes of IDEREGS_AND_INTPACK as the command
189;
190 push es ; save off real buffer location
191 push di
192
193 mov di,bp ; point to IDEREGS for command dispatch;
194 push ss
195 pop es
196
197 xor si,si ; initialize checksum for write
198 dec si
199 mov bp,si
200
201 mov bl,03h ; writing 3 words
202
203 call SerialCommand_WriteProtocol
204
205 pop di ; restore real buffer location
206 pop es
207
208 pop ax ; load command byte (done before call to .nextSector on subsequent iterations)
209 push ax
210
211;
212; Top of the read/write loop, one iteration per sector
213;
214.nextSector:
215 xor si,si ; initialize checksum for read or write
216 dec si
217 mov bp,si
218
219 mov bx,0100h
220
221 shr ah,1 ; command byte, are we doing a write?
222 jnc .readSector
223 call SerialCommand_WriteProtocol
224
225 xor bx,bx
226
227.readSector:
228 mov cx,bx
229 inc cx
230
231 mov bl,dl ; setup bl with proper values for read loop (bh comes later)
232
233;----------------------------------------------------------------------
234;
235; Timeout
236;
237; During read, we first poll in a tight loop, interrupts off, for the next character to come in
238; If that loop completes, then we assume there is a long delay involved, turn interrupts back on
239; and wait for a given number of timer ticks to pass.
240;
241; To save code space, we use the contents of DL to decide which byte in the word to return for reading.
242;
243.readTimeout:
244 push cx
245 xor cx,cx
246.readTimeoutLoop:
247 push dx
248 or dl,SerialCommand_UART_lineStatus
249 in al,dx
250 pop dx
251 shr al,1
252 jc .readTimeoutComplete
253 loop .readTimeoutLoop
254 sti
255 mov bh,1
256 call SerialCommand_WaitAndPoll_Init
257 cli
258.readTimeoutComplete:
259 mov bh,bl
260 or bh,SerialCommand_UART_lineStatus
261
262 pop cx
263 test dl,1
264 jz .readByte1Ready
265 jmp .readByte2Ready
266
267;----------------------------------------------------------------------------
268;
269; Read Block (without interrupts, used when there is a FIFO, high speed)
270;
271; NOTE: This loop is very time sensitive. Literally, another instruction
272; cannot be inserted into this loop without us falling behind at high
273; speed (460.8K baud) on a 4.77Mhz 8088, making it hard to receive
274; a full 512 byte block.
275;
276.readLoop:
277 stosw ; store word in caller's data buffer
278
279 add bp, ax ; update Fletcher's checksum
280 adc bp, 0
281 add si, bp
282 adc si, 0
283
284 mov dl,bh
285 in al,dx
286 shr al,1 ; data ready (byte 1)?
287 mov dl,bl ; get ready to read data
288 jnc .readTimeout ; nope not ready, update timeouts
289
290;
291; Entry point after initial timeout. We enter here so that the checksum word
292; is not stored (and is left in AX after the loop is complete).
293;
294.readByte1Ready:
295 in al, dx ; read data byte 1
296
297 mov ah, al ; store byte in ah for now
298
299;
300; note the placement of this reset of dl to bh, and that it is
301; before the return, which is assymetric with where this is done
302; above for byte 1. The value of dl is used by the timeout routine
303; to know which byte to return to (.read_byte1_ready or
304; .read_byte2_ready)
305;
306 mov dl,bh
307
308 in al,dx
309 shr al,1 ; data ready (byte 2)?
310 jnc .readTimeout
311.readByte2Ready:
312 mov dl,bl
313 in al, dx ; read data byte 2
314
315 xchg al, ah ; ah was holding byte 1, reverse byte order
316
317 loop .readLoop
318
319 sti ; interrupts back on ASAP, if we turned them off
320
321;
322; Compare checksums
323;
324 xor bp,si
325 cmp ax,bp
326 jnz SerialCommand_OutputWithParameters_Error
327
328;
329; Normalize buffer pointer for next go round, if needed
330;
331 test di,di
332 jns .clearBuffer
333 call Registers_NormalizeESDI
334
335;----------------------------------------------------------------------
336;
337; Clear read buffer
338;
339; In case there are extra characters or an error in the FIFO, clear it out.
340; In theory the initialization of the UART registers above should have
341; taken care of this, but I have seen cases where this is not true.
342;
343
344.clearBuffer:
345 mov dl,bh
346 in al,dx
347 mov dl,bl
348 test al,08fh
349 jz .clearBufferComplete
350 shr al,1
351 in al,dx
352 jc .clearBuffer ; note CF from shr above
353 jmp SerialCommand_OutputWithParameters_Error
354
355.clearBufferComplete:
356
357 pop ax ; sector count and command byte
358 dec al ; decrememnt sector count
359 push ax ; save
360 jz SerialCommand_OutputWithParameters_ReturnCodeInALCF ; CF clear from .clearBuffer test above
361
362 cli ; interrupts back off for ACK byte to host
363 ; (host could start sending data immediately)
364 out dx,al ; ACK with next sector number
365
366 jmp .nextSector ; all is well, time for next sector
367
368;---------------------------------------------------------------------------
369;
370; Cleanup, error reporting, and exit
371;
372
373;
374; Used in situations where a call is underway, such as with SerialCommand_WaitAndPoll
375;
376SerialCommand_OutputWithParameters_ErrorAndPop2Words:
377 pop ax
378 pop ax
379
380SerialCommand_OutputWithParameters_Error:
381 stc
382 mov al,1
383
384SerialCommand_OutputWithParameters_ReturnCodeInALCF:
385 sti
386 mov ah,al
387
388 pop bp ; recover ax from stack, throw away
389
390 pop es
391 pop bp
392 pop di
393 pop si
394
395 ret
396
397;--------------------------------------------------------------------
398; SerialCommand_WaitAndPoll
399;
400; Parameters:
401; BH: UART_LineStatus bit to test (20h for write, or 1h for read)
402; DX: Port address (OK if already incremented to UART_lineStatus)
403; Stack: 2 words on the stack below the command/count word
404; Returns:
405; Returns when desired UART_LineStatus bit is cleared
406; Jumps directly to error exit if timeout elapses (and cleans up stack)
407; Corrupts registers:
408; CX, flags
409;--------------------------------------------------------------------
410
411SerialCommand_WaitAndPoll_SoftDelayTicks EQU 20
412
413ALIGN JUMP_ALIGN
414SerialCommand_WaitAndPoll_Init:
415 mov cl,SerialCommand_WaitAndPoll_SoftDelayTicks
416 call Timer_InitializeTimeoutWithTicksInCL
417; fall-through
418
419SerialCommand_WaitAndPoll:
420 call Timer_SetCFifTimeout
421 jc SerialCommand_OutputWithParameters_ErrorAndPop2Words
422 push dx
423 push ax
424 or dl,SerialCommand_UART_lineStatus
425 in al,dx
426 test al,bh
427 pop ax
428 pop dx
429 jz SerialCommand_WaitAndPoll
430; fall-through
431
432SerialCommand_WaitAndPoll_Done:
433 ret
434
435;--------------------------------------------------------------------
436; SerialCommand_WriteProtocol
437;
438; Parameters:
439; ES:DI: Ptr to buffer
440; BL: Words to write (1-255, or 0=256)
441; BP/SI: Initialized for Checksum (-1 in each)
442; DX: I/O Port
443; Returns:
444; BP/SI: Checksum for written bytes, compared against ACK from server in .readLoop
445; Corrupts registers:
446; AX, BX, CX, DI
447;--------------------------------------------------------------------
448ALIGN JUMP_ALIGN
449SerialCommand_WriteProtocol:
450 mov bh,20h
451
452.writeLoop:
453 test bh,1
454 jnz SerialCommand_WaitAndPoll_Done
455
456 mov ax,[es:di] ; fetch next word
457 inc di
458 inc di
459
460 add bp,ax ; update checksum
461 adc bp,0
462 add si,bp
463 adc si,0
464
465.writeLoopChecksum:
466 call SerialCommand_WaitAndPoll_Init
467
468 out dx,al ; output first byte
469
470 call SerialCommand_WaitAndPoll
471
472 mov al,ah ; output second byte
473 out dx,al
474
475 dec bl
476 jnz .writeLoop
477
478 inc bh
479
480 mov ax,bp ; merge checksum for possible write (last loop)
481 xor ax,si
482
483 jmp .writeLoopChecksum
484
485
486;--------------------------------------------------------------------
487; SerialCommand_IdentifyDeviceToBufferInESSIwithDriveSelectByteInBH
488; Parameters:
489; BH: Drive Select byte for Drive and Head Select Register
490; DS: Segment to RAMVARS
491; ES:SI: Ptr to buffer to receive 512-byte IDE Information
492; CS:BP: Ptr to IDEVARS
493; Returns:
494; AH: INT 13h Error Code
495; NOTE: Not set (or checked) during drive detection
496; CF: Cleared if success, Set if error
497; Corrupts registers:
498; AL, BL, CX, DX, SI, DI, ES
499;--------------------------------------------------------------------
500ALIGN JUMP_ALIGN
501SerialCommand_IdentifyDeviceToBufferInESSIwithDriveSelectByteInBH:
502;
503; To improve boot time, we do our best to avoid looking for slave serial drives when we already know the results
504; from the looking for a master. This is particuarly true when doing a COM port scan, as we will end up running
505; through all the COM ports and baud rates a second time.
506;
507; But drive detection isn't the only case - we also need to get the right drive when called on int13h/25h.
508;
509; The decision tree:
510;
511; Master:
512; bSerialPackedPortAndBaud Non-Zero: -> Continue with bSerialPackedAndBaud (1)
513; bSerialPackedPortAndBaud Zero:
514; bLastSerial Zero: -> Scan (2)
515; bLastSerial Non-Zero: -> Continue with bLastSerial (3)
516;
517; Slave:
518; bSerialPackedPortAndBaud Non-Zero:
519; bLastSerial Zero: -> Error - Not Found (4)
520; bLastSerial Non-Zero: -> Continue with bSerialPackedAndBaud (5)
521; bSerialPackedPortAndBaud Zero:
522; bLastSerial Zero: -> Error - Not Found (4)
523; bLastSerial Non-Zero: -> Continue with bLastSerial (6)
524;
525; (1) This was a port/baud that was explicilty set with the configurator. In the drive detection case, as this
526; is the Master, we are checking out a new controller, and so don't care about the value of bLastSerial.
527; And as with the int13h/25h case, we just go off and get the needed information using the user's setting.
528; (2) We are using the special .ideVarsSerialAuto strucutre. During drive detection, we would only be here
529; if bLastSerial is zero (since we only scan if no explicit drives are set), so we go off to scan.
530; (3) We are using the special .ideVarsSerialAuto strucutre. We won't get here during drive detection, but
531; we might get here on an int13h/25h call. If we have scanned COM drives, they are the ONLY serial drives
532; in use, and so bLastSerial will reflect the port/baud setting for the scanned COM drives.
533; (4) No master has been found yet, therefore no slave should be found. Avoiding the slave reduces boot time,
534; especially in the full COM port scan case. Note that this is different from the hardware IDE, where we
535; will scan for a slave even if a master is not present. Note that if ANY master had been previously found,
536; we will do the slave scan, which isn't harmful, it just wates time. But the most common case (by a wide
537; margin) will be just one serial controller.
538; (5) A COM port scan for a master had been previously completed, and a drive was found. In a multiple serial
539; controller scenario being called with int13h/25h, we need to use the value in bSerialPackedPortAndBaud
540; to make sure we get the proper drive.
541; (6) A COM port scan for a master had been previously completed, and a drive was found. We would only get here
542; if no serial drive was explicitly set by the user in the configurator or that drive had not been found.
543; Instead of performing the full COM port scan for the slave, use the port/baud value stored during the
544; master scan.
545;
546 mov dl,[cs:bp+IDEVARS.bSerialPackedPortAndBaud]
547 mov al, byte [RAMVARS.xlateVars+XLATEVARS.bLastSerial]
548
549 test bh, FLG_DRVNHEAD_DRV
550 jz .master
551
552 test al,al ; Take care of the case that is different between master and slave.
553 jz .error ; Because we do this here, the jz after the "or" below will not be taken
554
555; fall-through
556.master:
557 test dl,dl
558 jnz .identifyDeviceInDL
559
560 or dl,al ; Move bLast into position in dl, as well as test for zero
561 jz .scanSerial
562
563; fall-through
564.identifyDeviceInDL:
565
566 push bp ; setup fake IDEREGS_AND_INTPACK
567
568 push dx
569
570 mov cl,1 ; 1 sector to move
571 push cx
572
573 mov bl,0a0h ; protocol command to ah and onto stack with bh
574 mov ah,bl
575
576 push bx
577
578 mov bp,sp
579 call SerialCommand_OutputWithParameters_DeviceInDL
580
581 pop bx
582
583 pop cx
584 pop dx
585
586 pop bp
587;
588; place packed port/baud in RAMVARS, read by FinalizeDPT and DetectDrives
589;
590; Note that this will be set during an int13h/25h call as well. Which is OK since it is only used (at the
591; top of this routine) for drives found during a COM scan, and we only COM scan if there were no other
592; COM drives found. So we will only reaffirm the port/baud for the one COM port/baud that has a drive.
593;
594 jc .notFound ; only store bLastSerial if success
595 mov byte [RAMVARS.xlateVars+XLATEVARS.bLastSerial], dl
596
597.notFound:
598 ret
599
600;----------------------------------------------------------------------
601;
602; SerialCommand_AutoSerial
603;
604; When the SerialAuto IDEVARS entry is used, scans the COM ports on the machine for a possible serial connection.
605;
606
607.scanPortAddresses: db DEVICE_SERIAL_COM7 >> 2
608 db DEVICE_SERIAL_COM6 >> 2
609 db DEVICE_SERIAL_COM5 >> 2
610 db DEVICE_SERIAL_COM4 >> 2
611 db DEVICE_SERIAL_COM3 >> 2
612 db DEVICE_SERIAL_COM2 >> 2
613 db DEVICE_SERIAL_COM1 >> 2
614 db 0
615
616ALIGN JUMP_ALIGN
617.scanSerial:
618 mov di,.scanPortAddresses-1
619
620.nextPort:
621 inc di ; load next port address
622 mov dl,[cs:di]
623
624 mov dh,0 ; shift from one byte to two
625 eSHL_IM dx, 2
626 jz .error
627
628;
629; Test for COM port presence, write to and read from registers
630;
631 push dx
632 add dl,SerialCommand_UART_lineControl
633 mov al, 09ah
634 out dx, al
635 in al, dx
636 pop dx
637 cmp al, 09ah
638 jnz .nextPort
639
640 mov al, 0ch
641 out dx, al
642 in al, dx
643 cmp al, 0ch
644 jnz .nextPort
645
646;
647; Pack into dl, baud rate starts at 0
648;
649 add dx,-(DEVICE_SERIAL_PACKEDPORTANDBAUD_STARTINGPORT)
650 shr dx,1 ; dh is zero at this point, and will be sent to the server,
651 ; so we know this is an auto detect
652
653 jmp .testFirstBaud
654
655;
656; Walk through 4 possible baud rates
657;
658.nextBaud:
659 inc dx
660 test dl,3
661 jz .nextPort
662
663.testFirstBaud:
664 call .identifyDeviceInDL
665 jc .nextBaud
666
667 ret
668
669.error:
670 stc
671 ; mov ah,1 ; setting the error code is unnecessary as this path can only be taken during
672 ; drive detection, and drive detection works off CF and does not check AH
673 ret
674
675
Note: See TracBrowser for help on using the repository browser.