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

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

Reworked the 'skip detecting the slave if there was no master' code to be more complete (includes configurator drives) and to take into account int13h/25h calls. Some of the changes in my last checkin have been rolled back as a result (they are no longer needed). I did take a byte out of RAMVARS, but there was an alignment byte available for the taking. I also added a perl script for padding and adding the checksum byte to a binary image, which can be invoked manually or with 'make checksum'.

File size: 18.9 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;
181; Send Command
182;
183; Sends first six bytes of IDEREGS_AND_INTPACK as the command
184;
185 call Registers_NormalizeESDI
186
187 push es ; save off real buffer location
188 push di
189
190 mov di,bp ; point to IDEREGS for command dispatch;
191 push ss
192 pop es
193
194 xor si,si ; initialize checksum for write
195 dec si
196 mov bp,si
197
198 mov bl,03h ; writing 3 words
199
200 call SerialCommand_WriteProtocol
201
202 pop di ; restore real buffer location
203 pop es
204
205 pop ax ; load command byte (done before call to .nextSector on subsequent iterations)
206 push ax
207
208;
209; Top of the read/write loop, one iteration per sector
210;
211.nextSector:
212 xor si,si ; initialize checksum for read or write
213 dec si
214 mov bp,si
215
216 mov bx,0100h
217
218 shr ah,1 ; command byte, are we doing a write?
219 jnc .readSector
220 call SerialCommand_WriteProtocol
221
222 xor bx,bx
223
224.readSector:
225 mov cx,bx
226 inc cx
227
228 mov bl,dl ; setup bl with proper values for read loop (bh comes later)
229
230;----------------------------------------------------------------------
231;
232; Timeout
233;
234; During read, we first poll in a tight loop, interrupts off, for the next character to come in
235; If that loop completes, then we assume there is a long delay involved, turn interrupts back on
236; and wait for a given number of timer ticks to pass.
237;
238; To save code space, we use the contents of DL to decide which byte in the word to return for reading.
239;
240.readTimeout:
241 push cx
242 xor cx,cx
243.readTimeoutLoop:
244 push dx
245 or dl,SerialCommand_UART_lineStatus
246 in al,dx
247 pop dx
248 shr al,1
249 jc .readTimeoutComplete
250 loop .readTimeoutLoop
251 sti
252 mov bh,1
253 call SerialCommand_WaitAndPoll_Init
254 cli
255.readTimeoutComplete:
256 mov bh,bl
257 or bh,SerialCommand_UART_lineStatus
258
259 pop cx
260 test dl,1
261 jz .readByte1Ready
262 jmp .readByte2Ready
263
264;----------------------------------------------------------------------------
265;
266; Read Block (without interrupts, used when there is a FIFO, high speed)
267;
268; NOTE: This loop is very time sensitive. Literally, another instruction
269; cannot be inserted into this loop without us falling behind at high
270; speed (460.8K baud) on a 4.77Mhz 8088, making it hard to receive
271; a full 512 byte block.
272;
273.readLoop:
274 stosw ; store word in caller's data buffer
275
276 add bp, ax ; update Fletcher's checksum
277 adc bp, 0
278 add si, bp
279 adc si, 0
280
281 mov dl,bh
282 in al,dx
283 shr al,1 ; data ready (byte 1)?
284 mov dl,bl ; get ready to read data
285 jnc .readTimeout ; nope not ready, update timeouts
286
287;
288; Entry point after initial timeout. We enter here so that the checksum word
289; is not stored (and is left in AX after the loop is complete).
290;
291.readByte1Ready:
292 in al, dx ; read data byte 1
293
294 mov ah, al ; store byte in ah for now
295
296;
297; note the placement of this reset of dl to bh, and that it is
298; before the return, which is assymetric with where this is done
299; above for byte 1. The value of dl is used by the timeout routine
300; to know which byte to return to (.read_byte1_ready or
301; .read_byte2_ready)
302;
303 mov dl,bh
304
305 in al,dx
306 shr al,1 ; data ready (byte 2)?
307 jnc .readTimeout
308.readByte2Ready:
309 mov dl,bl
310 in al, dx ; read data byte 2
311
312 xchg al, ah ; ah was holding byte 1, reverse byte order
313
314 loop .readLoop
315
316 sti ; interrupts back on ASAP, if we turned them off
317
318;
319; Compare checksums
320;
321 xor bp,si
322 cmp ax,bp
323 jnz SerialCommand_OutputWithParameters_Error
324
325
326;----------------------------------------------------------------------
327;
328; Clear read buffer
329;
330; In case there are extra characters or an error in the FIFO, clear it out.
331; In theory the initialization of the UART registers above should have
332; taken care of this, but I have seen cases where this is not true.
333;
334.clearBuffer:
335 mov dl,bh
336 in al,dx
337 mov dl,bl
338 test al,08fh
339 jz .clearBufferComplete
340 shr al,1
341 in al,dx
342 jc .clearBuffer ; note CF from shr above
343 jmp SerialCommand_OutputWithParameters_Error
344
345.clearBufferComplete:
346 pop ax ; sector count and command byte
347 dec al ; decrememnt sector count
348 push ax ; save
349 jz SerialCommand_OutputWithParameters_ReturnCodeInALCF ; CF clear from .clearBuffer test above
350
351 cli ; interrupts back off for ACK byte to host
352 ; (host could start sending data immediately)
353 out dx,al ; ACK with next sector number
354
355 jmp .nextSector ; all is well, time for next sector
356
357;---------------------------------------------------------------------------
358;
359; Cleanup, error reporting, and exit
360;
361
362;
363; Used in situations where a call is underway, such as with SerialCommand_WaitAndPoll
364;
365SerialCommand_OutputWithParameters_ErrorAndPop2Words:
366 pop ax
367 pop ax
368
369SerialCommand_OutputWithParameters_Error:
370 stc
371 mov al,1
372
373SerialCommand_OutputWithParameters_ReturnCodeInALCF:
374 sti
375 mov ah,al
376
377 pop bp ; recover ax from stack, throw away
378
379 pop es
380 pop bp
381 pop di
382 pop si
383
384 ret
385
386;--------------------------------------------------------------------
387; SerialCommand_WaitAndPoll
388;
389; Parameters:
390; BH: UART_LineStatus bit to test (20h for write, or 1h for read)
391; DX: Port address (OK if already incremented to UART_lineStatus)
392; Stack: 2 words on the stack below the command/count word
393; Returns:
394; Returns when desired UART_LineStatus bit is cleared
395; Jumps directly to error exit if timeout elapses (and cleans up stack)
396; Corrupts registers:
397; CX, flags
398;--------------------------------------------------------------------
399
400SerialCommand_WaitAndPoll_SoftDelayTicks EQU 20
401
402ALIGN JUMP_ALIGN
403SerialCommand_WaitAndPoll_Init:
404 mov cl,SerialCommand_WaitAndPoll_SoftDelayTicks
405 call Timer_InitializeTimeoutWithTicksInCL
406; fall-through
407
408SerialCommand_WaitAndPoll:
409 call Timer_SetCFifTimeout
410 jc SerialCommand_OutputWithParameters_ErrorAndPop2Words
411 push dx
412 push ax
413 or dl,SerialCommand_UART_lineStatus
414 in al,dx
415 test al,bh
416 pop ax
417 pop dx
418 jz SerialCommand_WaitAndPoll
419; fall-through
420
421SerialCommand_WaitAndPoll_Done:
422 ret
423
424;--------------------------------------------------------------------
425; SerialCommand_WriteProtocol
426;
427; Parameters:
428; ES:DI: Ptr to buffer
429; BL: Words to write (1-255, or 0=256)
430; BP/SI: Initialized for Checksum (-1 in each)
431; DX: I/O Port
432; Returns:
433; BP/SI: Checksum for written bytes, compared against ACK from server in .readLoop
434; Corrupts registers:
435; AX, BX, CX, DI
436;--------------------------------------------------------------------
437ALIGN JUMP_ALIGN
438SerialCommand_WriteProtocol:
439 mov bh,20h
440
441.writeLoop:
442 test bh,1
443 jnz SerialCommand_WaitAndPoll_Done
444
445 mov ax,[es:di] ; fetch next word
446 inc di
447 inc di
448
449 add bp,ax ; update checksum
450 adc bp,0
451 add si,bp
452 adc si,0
453
454.writeLoopChecksum:
455 call SerialCommand_WaitAndPoll_Init
456
457 out dx,al ; output first byte
458
459 call SerialCommand_WaitAndPoll
460
461 mov al,ah ; output second byte
462 out dx,al
463
464 dec bl
465 jnz .writeLoop
466
467 inc bh
468
469 mov ax,bp ; merge checksum for possible write (last loop)
470 xor ax,si
471
472 jmp .writeLoopChecksum
473
474
475;--------------------------------------------------------------------
476; SerialCommand_IdentifyDeviceToBufferInESSIwithDriveSelectByteInBH
477; Parameters:
478; BH: Drive Select byte for Drive and Head Select Register
479; DS: Segment to RAMVARS
480; ES:SI: Ptr to buffer to receive 512-byte IDE Information
481; CS:BP: Ptr to IDEVARS
482; Returns:
483; AH: INT 13h Error Code
484; NOTE: Not set (or checked) during drive detection
485; CF: Cleared if success, Set if error
486; Corrupts registers:
487; AL, BL, CX, DX, SI, DI, ES
488;--------------------------------------------------------------------
489ALIGN JUMP_ALIGN
490SerialCommand_IdentifyDeviceToBufferInESSIwithDriveSelectByteInBH:
491;
492; To improve boot time, we do our best to avoid looking for slave serial drives when we already know the results
493; from the looking for a master. This is particuarly true when doing a COM port scan, as we will end up running
494; through all the COM ports and baud rates a second time.
495;
496; But drive detection isn't the only case - we also need to get the right drive when called on int13h/25h.
497;
498; The decision tree:
499;
500; Master:
501; bSerialPackedPortAndBaud Non-Zero: -> Continue with bSerialPackedAndBaud (1)
502; bSerialPackedPortAndBaud Zero:
503; bLastSerial Zero: -> Scan (2)
504; bLastSerial Non-Zero: -> Continue with bLastSerial (3)
505;
506; Slave:
507; bSerialPackedPortAndBaud Non-Zero:
508; bLastSerial Zero: -> Error - Not Found (4)
509; bLastSerial Non-Zero: -> Continue with bSerialPackedAndBaud (5)
510; bSerialPackedPortAndBaud Zero:
511; bLastSerial Zero: -> Error - Not Found (4)
512; bLastSerial Non-Zero: -> Continue with bLastSerial (6)
513;
514; (1) This was a port/baud that was explicilty set with the configurator. In the drive detection case, as this
515; is the Master, we are checking out a new controller, and so don't care about the value of bLastSerial.
516; And as with the int13h/25h case, we just go off and get the needed information using the user's setting.
517; (2) We are using the special .ideVarsSerialAuto strucutre. During drive detection, we would only be here
518; if bLastSerial is zero (since we only scan if no explicit drives are set), so we go off to scan.
519; (3) We are using the special .ideVarsSerialAuto strucutre. We won't get here during drive detection, but
520; we might get here on an int13h/25h call. If we have scanned COM drives, they are the ONLY serial drives
521; in use, and so bLastSerial will reflect the port/baud setting for the scanned COM drives.
522; (4) No master has been found yet, therefore no slave should be found. Avoiding the slave reduces boot time,
523; especially in the full COM port scan case. Note that this is different from the hardware IDE, where we
524; will scan for a slave even if a master is not present. Note that if ANY master had been previously found,
525; we will do the slave scan, which isn't harmful, it just wates time. But the most common case (by a wide
526; margin) will be just one serial controller.
527; (5) A COM port scan for a master had been previously completed, and a drive was found. In a multiple serial
528; controller scenario being called with int13h/25h, we need to use the value in bSerialPackedPortAndBaud
529; to make sure we get the proper drive.
530; (6) A COM port scan for a master had been previously completed, and a drive was found. We would only get here
531; if no serial drive was explicitly set by the user in the configurator or that drive had not been found.
532; Instead of performing the full COM port scan for the slave, use the port/baud value stored during the
533; master scan.
534;
535 mov dl,[cs:bp+IDEVARS.bSerialPackedPortAndBaud]
536 mov al, byte [RAMVARS.xlateVars+XLATEVARS.bLastSerial]
537
538 test bh, FLG_DRVNHEAD_DRV
539 jz .master
540
541 test al,al ; Take care of the case that is different between master and slave.
542 jz .error ; Because we do this here, the jz after the "or" below will not be taken
543
544; fall-through
545.master:
546 test dl,dl
547 jnz .identifyDeviceInDL
548
549 or dl,al ; Move bLast into position in dl, as well as test for zero
550 jz .scanSerial
551
552; fall-through
553.identifyDeviceInDL:
554
555 push bp ; setup fake IDEREGS_AND_INTPACK
556
557 push dx
558
559 mov cl,1 ; 1 sector to move
560 push cx
561
562 mov bl,0a0h ; protocol command to ah and onto stack with bh
563 mov ah,bl
564
565 push bx
566
567 mov bp,sp
568 call SerialCommand_OutputWithParameters_DeviceInDL
569
570 pop bx
571
572 pop cx
573 pop dx
574
575 pop bp
576;
577; place packed port/baud in RAMVARS, read by FinalizeDPT and DetectDrives
578;
579; Note that this will be set during an int13h/25h call as well. Which is OK since it is only used (at the
580; top of this routine) for drives found during a COM scan, and we only COM scan if there were no other
581; COM drives found. So we will only reaffirm the port/baud for the one COM port/baud that has a drive.
582;
583 jc .notFound ; only store bLastSerial if success
584 mov byte [RAMVARS.xlateVars+XLATEVARS.bLastSerial], dl
585
586.notFound:
587 ret
588
589;----------------------------------------------------------------------
590;
591; SerialCommand_AutoSerial
592;
593; When the SerialAuto IDEVARS entry is used, scans the COM ports on the machine for a possible serial connection.
594;
595
596.scanPortAddresses: db DEVICE_SERIAL_COM7 >> 2
597 db DEVICE_SERIAL_COM6 >> 2
598 db DEVICE_SERIAL_COM5 >> 2
599 db DEVICE_SERIAL_COM4 >> 2
600 db DEVICE_SERIAL_COM3 >> 2
601 db DEVICE_SERIAL_COM2 >> 2
602 db DEVICE_SERIAL_COM1 >> 2
603 db 0
604
605ALIGN JUMP_ALIGN
606.scanSerial:
607 mov di,.scanPortAddresses-1
608
609.nextPort:
610 inc di ; load next port address
611 mov dl,[cs:di]
612
613 mov dh,0 ; shift from one byte to two
614 eSHL_IM dx, 2
615 jz .error
616
617;
618; Test for COM port presence, write to and read from registers
619;
620 push dx
621 add dl,SerialCommand_UART_lineControl
622 mov al, 09ah
623 out dx, al
624 in al, dx
625 pop dx
626 cmp al, 09ah
627 jnz .nextPort
628
629 mov al, 0ch
630 out dx, al
631 in al, dx
632 cmp al, 0ch
633 jnz .nextPort
634
635;
636; Pack into dl, baud rate starts at 0
637;
638 add dx,-(DEVICE_SERIAL_PACKEDPORTANDBAUD_STARTINGPORT)
639 shr dx,1 ; dh is zero at this point, and will be sent to the server,
640 ; so we know this is an auto detect
641
642 jmp .testFirstBaud
643
644;
645; Walk through 4 possible baud rates
646;
647.nextBaud:
648 inc dx
649 test dl,3
650 jz .nextPort
651
652.testFirstBaud:
653 call .identifyDeviceInDL
654 jc .nextBaud
655
656 ret
657
658.error:
659 stc
660 ; mov ah,1 ; setting the error code is unnecessary as this path can only be taken during
661 ; drive detection, and drive detection works off CF and does not check AH
662 ret
663
664
Note: See TracBrowser for help on using the repository browser.