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
RevLine 
[150]1; Project name : XTIDE Universal BIOS
2; Description : Serial Device Command functions.
3
4; Section containing code
5SECTION .text
6
[179]7;--------------- UART Equates -----------------------------
8;
9; Serial Programming References:
10; http://en.wikibooks.org/wiki/Serial_Programming
11;
[181]12
[179]13SerialCommand_UART_base EQU 0
14SerialCommand_UART_transmitByte EQU 0
15SerialCommand_UART_receiveByte EQU 0
16SerialCommand_UART_divisorLow EQU 0
[181]17; Values for UART_divisorLow:
[179]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
[181]25SerialCommand_UART_interruptEnable EQU 1
[179]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
[181]33
[179]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
[181]46
[150]47;--------------------------------------------------------------------
[179]48; SerialCommand_OutputWithParameters
[150]49; Parameters:
[179]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
[150]57; Returns:
58; AH: INT 13h Error Code
59; CF: Cleared if success, Set if error
60; Corrupts registers:
[179]61; AL, BX, CX, DX, (ES:SI for data transfer commands)
[150]62;--------------------------------------------------------------------
63ALIGN JUMP_ALIGN
[179]64SerialCommand_OutputWithParameters:
[181]65
[179]66 mov ah,(SerialCommand_Protocol_Header | SerialCommand_Protocol_Read)
[181]67
[179]68 mov al,[bp+IDEPACK.bCommand]
[150]69
[179]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
[181]75
[179]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
[181]80
81.readOrWrite:
[179]82 mov [bp+IDEPACK.bFeatures],ah ; store protocol command
[181]83
84 mov dl, byte [di+DPT.bSerialPortAndBaud]
85
[179]86; fall-through
[150]87
88;--------------------------------------------------------------------
[179]89; SerialCommand_OutputWithParameters_DeviceInDL
[150]90; Parameters:
[181]91; AH: Protocol Command
[179]92; DL: Packed I/O port and baud rate
[150]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)
[181]100;--------------------------------------------------------------------
[179]101SerialCommand_OutputWithParameters_DeviceInDL:
[181]102
[179]103 push si
104 push di
105 push bp
106 push es
107
[181]108;
[179]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
[181]114
[199]115 and cl, DEVICE_SERIAL_PACKEDPORTANDBAUD_BAUDMASK
[179]116 shl cl, 1
117 mov ch, SerialCommand_UART_divisorLow_startingBaud
118 shr ch, cl
119 adc ch, 0
120
[199]121 and dl, DEVICE_SERIAL_PACKEDPORTANDBAUD_PORTMASK
[181]122 mov dh, 0
[196]123 shl dx, 1 ; port offset already x4, needs one more shift to be x8
[199]124 add dx, DEVICE_SERIAL_PACKEDPORTANDBAUD_STARTINGPORT
[179]125
126;
127; Buffer is referenced through ES:DI throughout, since we need to store faster than we read
[181]128;
[179]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
[181]135;
[179]136 push ax
137
[181]138; cld ; Shouldn't be needed. DF has already been cleared (line 24, Int13h.asm)
139
[179]140;----------------------------------------------------------------------
141;
142; Initialize UART
143;
[181]144; We do this each time since DOS (at boot) or another program may have
[179]145; decided to reprogram the UART
146;
147 push dx
[181]148
[179]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
[181]164 out dx,al
[179]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
[207]179;
180; Start off with a normalized buffer pointer
181;
182 call Registers_NormalizeESDI
183
[179]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
[181]192
193 mov di,bp ; point to IDEREGS for command dispatch;
[179]194 push ss
195 pop es
196
197 xor si,si ; initialize checksum for write
[181]198 dec si
[179]199 mov bp,si
200
201 mov bl,03h ; writing 3 words
[181]202
[179]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
[181]213;
[179]214.nextSector:
215 xor si,si ; initialize checksum for read or write
216 dec si
217 mov bp,si
218
219 mov bx,0100h
[181]220
[179]221 shr ah,1 ; command byte, are we doing a write?
222 jnc .readSector
223 call SerialCommand_WriteProtocol
[181]224
[179]225 xor bx,bx
226
227.readSector:
228 mov cx,bx
229 inc cx
[181]230
[179]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
[181]246.readTimeoutLoop:
[179]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
[181]257 cli
[179]258.readTimeoutComplete:
259 mov bh,bl
260 or bh,SerialCommand_UART_lineStatus
[181]261
[179]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;
[181]271; NOTE: This loop is very time sensitive. Literally, another instruction
[179]272; cannot be inserted into this loop without us falling behind at high
[181]273; speed (460.8K baud) on a 4.77Mhz 8088, making it hard to receive
[179]274; a full 512 byte block.
275;
[181]276.readLoop:
277 stosw ; store word in caller's data buffer
278
[179]279 add bp, ax ; update Fletcher's checksum
280 adc bp, 0
281 add si, bp
282 adc si, 0
283
[181]284 mov dl,bh
285 in al,dx
[179]286 shr al,1 ; data ready (byte 1)?
[181]287 mov dl,bl ; get ready to read data
[179]288 jnc .readTimeout ; nope not ready, update timeouts
[181]289
290;
[179]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).
[181]293;
294.readByte1Ready:
[179]295 in al, dx ; read data byte 1
296
297 mov ah, al ; store byte in ah for now
[181]298
[179]299;
[181]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
[179]304; .read_byte2_ready)
305;
[181]306 mov dl,bh
307
[179]308 in al,dx
309 shr al,1 ; data ready (byte 2)?
310 jnc .readTimeout
[181]311.readByte2Ready:
312 mov dl,bl
[179]313 in al, dx ; read data byte 2
314
315 xchg al, ah ; ah was holding byte 1, reverse byte order
[181]316
[179]317 loop .readLoop
318
[181]319 sti ; interrupts back on ASAP, if we turned them off
320
[179]321;
322; Compare checksums
[181]323;
[179]324 xor bp,si
325 cmp ax,bp
326 jnz SerialCommand_OutputWithParameters_Error
327
[207]328;
329; Normalize buffer pointer for next go round, if needed
330;
331 test di,di
332 jns .clearBuffer
333 call Registers_NormalizeESDI
[181]334
[179]335;----------------------------------------------------------------------
[181]336;
[179]337; Clear read buffer
338;
339; In case there are extra characters or an error in the FIFO, clear it out.
[181]340; In theory the initialization of the UART registers above should have
[179]341; taken care of this, but I have seen cases where this is not true.
342;
[207]343
[179]344.clearBuffer:
[181]345 mov dl,bh
[179]346 in al,dx
[181]347 mov dl,bl
[179]348 test al,08fh
349 jz .clearBufferComplete
350 shr al,1
[181]351 in al,dx
[179]352 jc .clearBuffer ; note CF from shr above
353 jmp SerialCommand_OutputWithParameters_Error
[181]354
355.clearBufferComplete:
[207]356
[179]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
[181]361
[179]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
[181]365
[179]366 jmp .nextSector ; all is well, time for next sector
367
368;---------------------------------------------------------------------------
369;
370; Cleanup, error reporting, and exit
371;
[181]372
373;
[179]374; Used in situations where a call is underway, such as with SerialCommand_WaitAndPoll
[181]375;
[179]376SerialCommand_OutputWithParameters_ErrorAndPop2Words:
377 pop ax
378 pop ax
379
[181]380SerialCommand_OutputWithParameters_Error:
381 stc
[179]382 mov al,1
383
[181]384SerialCommand_OutputWithParameters_ReturnCodeInALCF:
385 sti
[179]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
[150]397;--------------------------------------------------------------------
[179]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)
[181]407; Corrupts registers:
[179]408; CX, flags
409;--------------------------------------------------------------------
410
411SerialCommand_WaitAndPoll_SoftDelayTicks EQU 20
412
[181]413ALIGN JUMP_ALIGN
[179]414SerialCommand_WaitAndPoll_Init:
415 mov cl,SerialCommand_WaitAndPoll_SoftDelayTicks
416 call Timer_InitializeTimeoutWithTicksInCL
417; fall-through
[181]418
[179]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
[181]431
432SerialCommand_WaitAndPoll_Done:
[179]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;--------------------------------------------------------------------
[150]448ALIGN JUMP_ALIGN
[179]449SerialCommand_WriteProtocol:
450 mov bh,20h
[181]451
[179]452.writeLoop:
453 test bh,1
454 jnz SerialCommand_WaitAndPoll_Done
[181]455
[179]456 mov ax,[es:di] ; fetch next word
457 inc di
458 inc di
[181]459
[179]460 add bp,ax ; update checksum
461 adc bp,0
462 add si,bp
463 adc si,0
[150]464
[179]465.writeLoopChecksum:
466 call SerialCommand_WaitAndPoll_Init
[181]467
[179]468 out dx,al ; output first byte
[150]469
[179]470 call SerialCommand_WaitAndPoll
[181]471
[179]472 mov al,ah ; output second byte
473 out dx,al
474
475 dec bl
476 jnz .writeLoop
[181]477
[179]478 inc bh
[181]479
[179]480 mov ax,bp ; merge checksum for possible write (last loop)
[181]481 xor ax,si
482
[179]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
[203]495; NOTE: Not set (or checked) during drive detection
[179]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:
[203]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
[179]551
[203]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:
[196]557 test dl,dl
[203]558 jnz .identifyDeviceInDL
[179]559
[203]560 or dl,al ; Move bLast into position in dl, as well as test for zero
561 jz .scanSerial
[199]562
[179]563; fall-through
[203]564.identifyDeviceInDL:
[179]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
[181]575
[179]576 push bx
577
578 mov bp,sp
579 call SerialCommand_OutputWithParameters_DeviceInDL
580
581 pop bx
[181]582
583 pop cx
[179]584 pop dx
585
586 pop bp
[203]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
[179]596
[203]597.notFound:
[179]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.
[181]605;
606
[203]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
[179]615
[181]616ALIGN JUMP_ALIGN
[203]617.scanSerial:
618 mov di,.scanPortAddresses-1
[181]619
[179]620.nextPort:
621 inc di ; load next port address
622 mov dl,[cs:di]
[181]623
[179]624 mov dh,0 ; shift from one byte to two
[181]625 eSHL_IM dx, 2
[203]626 jz .error
[179]627
628;
629; Test for COM port presence, write to and read from registers
[181]630;
631 push dx
[179]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
[181]638 jnz .nextPort
[179]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
[181]648;
[199]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
[181]652
[179]653 jmp .testFirstBaud
654
655;
656; Walk through 4 possible baud rates
[181]657;
658.nextBaud:
[179]659 inc dx
660 test dl,3
661 jz .nextPort
[181]662
663.testFirstBaud:
[203]664 call .identifyDeviceInDL
[179]665 jc .nextBaud
666
667 ret
[181]668
[203]669.error:
[181]670 stc
[203]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
[179]674
[203]675
Note: See TracBrowser for help on using the repository browser.