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

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

Adding proper serial port support to the Configurator, which required some minor changes elsewhere. Also added an option, off by default, to automatically scan for serial drives at the end of normal drive detection (no ALT key required, although that is still available if the option is off).

File size: 15.7 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
[196]42; note that the actual StaringPoint port address is not achievable (results in 0 which triggers auto detect)
[181]43SerialCommand_PackedPortAndBaud_StartingPort EQU 240h
[196]44
[179]45SerialCommand_PackedPortAndBaud_PortMask EQU 0fch ; upper 6 bits - 240h through 438h
46SerialCommand_PackedPortAndBaud_BaudMask EQU 3 ; lower 2 bits - 4 baud rates
47
48SerialCommand_Protocol_Write EQU 3
49SerialCommand_Protocol_Read EQU 2
50SerialCommand_Protocol_Inquire EQU 0
51SerialCommand_Protocol_Header EQU 0a0h
[181]52
[150]53;--------------------------------------------------------------------
[179]54; SerialCommand_OutputWithParameters
[150]55; Parameters:
[179]56; BH: Non-zero if 48-bit addressing used
57; (ignored at present as 48-bit addressing is not supported)
58; BL: IDE Status Register bit to poll after command
59; (ignored at present, since there is no IDE status register to poll)
60; ES:SI: Ptr to buffer (for data transfer commands)
61; DS:DI: Ptr to DPT (in RAMVARS segment)
62; SS:BP: Ptr to IDEREGS_AND_INTPACK
[150]63; Returns:
64; AH: INT 13h Error Code
65; CF: Cleared if success, Set if error
66; Corrupts registers:
[179]67; AL, BX, CX, DX, (ES:SI for data transfer commands)
[150]68;--------------------------------------------------------------------
69ALIGN JUMP_ALIGN
[179]70SerialCommand_OutputWithParameters:
[181]71
[179]72 mov ah,(SerialCommand_Protocol_Header | SerialCommand_Protocol_Read)
[181]73
[179]74 mov al,[bp+IDEPACK.bCommand]
[150]75
[179]76 cmp al,20h ; Read Sectors IDE command
77 jz .readOrWrite
78 inc ah ; now SerialCommand_Protocol_Write
79 cmp al,30h ; Write Sectors IDE command
80 jz .readOrWrite
[181]81
[179]82; all other commands return success
83; including function 0ech which should return drive information, this is handled with the identify functions
84 xor ah,ah ; also clears carry
85 ret
[181]86
87.readOrWrite:
[179]88 mov [bp+IDEPACK.bFeatures],ah ; store protocol command
[181]89
90 mov dl, byte [di+DPT.bSerialPortAndBaud]
91
[179]92; fall-through
[150]93
94;--------------------------------------------------------------------
[179]95; SerialCommand_OutputWithParameters_DeviceInDL
[150]96; Parameters:
[181]97; AH: Protocol Command
[179]98; DL: Packed I/O port and baud rate
[150]99; ES:SI: Ptr to buffer (for data transfer commands)
100; SS:BP: Ptr to IDEREGS_AND_INTPACK
101; Returns:
102; AH: INT 13h Error Code
103; CF: Cleared if success, Set if error
104; Corrupts registers:
105; AL, BX, CX, DX, (ES:SI for data transfer commands)
[181]106;--------------------------------------------------------------------
[179]107SerialCommand_OutputWithParameters_DeviceInDL:
[181]108
[179]109 push si
110 push di
111 push bp
112 push es
113
[181]114;
[179]115; Unpack I/O port and baud from DPT
116; Port to DX more or less for the remainder of the routine
117; Baud in CH until UART initialization is complete
118;
119 mov cl, dl
[181]120
[199]121 and cl, DEVICE_SERIAL_PACKEDPORTANDBAUD_BAUDMASK
[179]122 shl cl, 1
123 mov ch, SerialCommand_UART_divisorLow_startingBaud
124 shr ch, cl
125 adc ch, 0
126
[199]127 and dl, DEVICE_SERIAL_PACKEDPORTANDBAUD_PORTMASK
[181]128 mov dh, 0
[196]129 shl dx, 1 ; port offset already x4, needs one more shift to be x8
[199]130 add dx, DEVICE_SERIAL_PACKEDPORTANDBAUD_STARTINGPORT
[179]131
132;
133; Buffer is referenced through ES:DI throughout, since we need to store faster than we read
[181]134;
[179]135 mov di,si
136
137 mov al,[bp+IDEPACK.bSectorCount]
138
139;
140; Command byte and sector count live at the top of the stack, pop/push are used to access
[181]141;
[179]142 push ax
143
[181]144; cld ; Shouldn't be needed. DF has already been cleared (line 24, Int13h.asm)
145
[179]146;----------------------------------------------------------------------
147;
148; Initialize UART
149;
[181]150; We do this each time since DOS (at boot) or another program may have
[179]151; decided to reprogram the UART
152;
153 push dx
[181]154
[179]155 mov al,83h
156 add dl,SerialCommand_UART_lineControl
157 out dx,al
158
159 mov al,ch
160 pop dx ; divisor low
161 out dx,al
162
163 xor ax,ax
164 inc dx ; divisor high
165 push dx
166 out dx,al
167
168 mov al,047h
169 inc dx ; fifo
[181]170 out dx,al
[179]171
172 mov al,03h
173 inc dx ; linecontrol
174 out dx,al
175
176 mov al,0bh
177 inc dx ; modemcontrol
178 out dx,al
179
180 pop dx ; base, interrupts disabled
181 xor ax,ax
182 out dx,al
183 dec dx
184
185;----------------------------------------------------------------------
186;
187; Send Command
188;
189; Sends first six bytes of IDEREGS_AND_INTPACK as the command
190;
191 call Registers_NormalizeESDI
192
193 push es ; save off real buffer location
194 push di
[181]195
196 mov di,bp ; point to IDEREGS for command dispatch;
[179]197 push ss
198 pop es
199
200 xor si,si ; initialize checksum for write
[181]201 dec si
[179]202 mov bp,si
203
204 mov bl,03h ; writing 3 words
[181]205
[179]206 call SerialCommand_WriteProtocol
207
208 pop di ; restore real buffer location
209 pop es
210
211 pop ax ; load command byte (done before call to .nextSector on subsequent iterations)
212 push ax
213
214;
215; Top of the read/write loop, one iteration per sector
[181]216;
[179]217.nextSector:
218 xor si,si ; initialize checksum for read or write
219 dec si
220 mov bp,si
221
222 mov bx,0100h
[181]223
[179]224 shr ah,1 ; command byte, are we doing a write?
225 jnc .readSector
226 call SerialCommand_WriteProtocol
[181]227
[179]228 xor bx,bx
229
230.readSector:
231 mov cx,bx
232 inc cx
[181]233
[179]234 mov bl,dl ; setup bl with proper values for read loop (bh comes later)
235
236;----------------------------------------------------------------------
237;
238; Timeout
239;
240; During read, we first poll in a tight loop, interrupts off, for the next character to come in
241; If that loop completes, then we assume there is a long delay involved, turn interrupts back on
242; and wait for a given number of timer ticks to pass.
243;
244; To save code space, we use the contents of DL to decide which byte in the word to return for reading.
245;
246.readTimeout:
247 push cx
248 xor cx,cx
[181]249.readTimeoutLoop:
[179]250 push dx
251 or dl,SerialCommand_UART_lineStatus
252 in al,dx
253 pop dx
254 shr al,1
255 jc .readTimeoutComplete
256 loop .readTimeoutLoop
257 sti
258 mov bh,1
259 call SerialCommand_WaitAndPoll_Init
[181]260 cli
[179]261.readTimeoutComplete:
262 mov bh,bl
263 or bh,SerialCommand_UART_lineStatus
[181]264
[179]265 pop cx
266 test dl,1
267 jz .readByte1Ready
268 jmp .readByte2Ready
269
270;----------------------------------------------------------------------------
271;
272; Read Block (without interrupts, used when there is a FIFO, high speed)
273;
[181]274; NOTE: This loop is very time sensitive. Literally, another instruction
[179]275; cannot be inserted into this loop without us falling behind at high
[181]276; speed (460.8K baud) on a 4.77Mhz 8088, making it hard to receive
[179]277; a full 512 byte block.
278;
[181]279.readLoop:
280 stosw ; store word in caller's data buffer
281
[179]282 add bp, ax ; update Fletcher's checksum
283 adc bp, 0
284 add si, bp
285 adc si, 0
286
[181]287 mov dl,bh
288 in al,dx
[179]289 shr al,1 ; data ready (byte 1)?
[181]290 mov dl,bl ; get ready to read data
[179]291 jnc .readTimeout ; nope not ready, update timeouts
[181]292
293;
[179]294; Entry point after initial timeout. We enter here so that the checksum word
295; is not stored (and is left in AX after the loop is complete).
[181]296;
297.readByte1Ready:
[179]298 in al, dx ; read data byte 1
299
300 mov ah, al ; store byte in ah for now
[181]301
[179]302;
[181]303; note the placement of this reset of dl to bh, and that it is
304; before the return, which is assymetric with where this is done
305; above for byte 1. The value of dl is used by the timeout routine
306; to know which byte to return to (.read_byte1_ready or
[179]307; .read_byte2_ready)
308;
[181]309 mov dl,bh
310
[179]311 in al,dx
312 shr al,1 ; data ready (byte 2)?
313 jnc .readTimeout
[181]314.readByte2Ready:
315 mov dl,bl
[179]316 in al, dx ; read data byte 2
317
318 xchg al, ah ; ah was holding byte 1, reverse byte order
[181]319
[179]320 loop .readLoop
321
[181]322 sti ; interrupts back on ASAP, if we turned them off
323
[179]324;
325; Compare checksums
[181]326;
[179]327 xor bp,si
328 cmp ax,bp
329 jnz SerialCommand_OutputWithParameters_Error
330
[181]331
[179]332;----------------------------------------------------------------------
[181]333;
[179]334; Clear read buffer
335;
336; In case there are extra characters or an error in the FIFO, clear it out.
[181]337; In theory the initialization of the UART registers above should have
[179]338; taken care of this, but I have seen cases where this is not true.
339;
340.clearBuffer:
[181]341 mov dl,bh
[179]342 in al,dx
[181]343 mov dl,bl
[179]344 test al,08fh
345 jz .clearBufferComplete
346 shr al,1
[181]347 in al,dx
[179]348 jc .clearBuffer ; note CF from shr above
349 jmp SerialCommand_OutputWithParameters_Error
[181]350
351.clearBufferComplete:
[179]352 pop ax ; sector count and command byte
353 dec al ; decrememnt sector count
354 push ax ; save
355 jz SerialCommand_OutputWithParameters_ReturnCodeInALCF ; CF clear from .clearBuffer test above
[181]356
[179]357 cli ; interrupts back off for ACK byte to host
358 ; (host could start sending data immediately)
359 out dx,al ; ACK with next sector number
[181]360
[179]361 jmp .nextSector ; all is well, time for next sector
362
363;---------------------------------------------------------------------------
364;
365; Cleanup, error reporting, and exit
366;
[181]367
368;
[179]369; Used in situations where a call is underway, such as with SerialCommand_WaitAndPoll
[181]370;
[179]371SerialCommand_OutputWithParameters_ErrorAndPop2Words:
372 pop ax
373 pop ax
374
[181]375SerialCommand_OutputWithParameters_Error:
376 stc
[179]377 mov al,1
378
[181]379SerialCommand_OutputWithParameters_ReturnCodeInALCF:
380 sti
[179]381 mov ah,al
382
383 pop bp ; recover ax from stack, throw away
384
385 pop es
386 pop bp
387 pop di
388 pop si
389
390 ret
391
[150]392;--------------------------------------------------------------------
[179]393; SerialCommand_WaitAndPoll
394;
395; Parameters:
396; BH: UART_LineStatus bit to test (20h for write, or 1h for read)
397; DX: Port address (OK if already incremented to UART_lineStatus)
398; Stack: 2 words on the stack below the command/count word
399; Returns:
400; Returns when desired UART_LineStatus bit is cleared
401; Jumps directly to error exit if timeout elapses (and cleans up stack)
[181]402; Corrupts registers:
[179]403; CX, flags
404;--------------------------------------------------------------------
405
406SerialCommand_WaitAndPoll_SoftDelayTicks EQU 20
407
[181]408ALIGN JUMP_ALIGN
[179]409SerialCommand_WaitAndPoll_Init:
410 mov cl,SerialCommand_WaitAndPoll_SoftDelayTicks
411 call Timer_InitializeTimeoutWithTicksInCL
412; fall-through
[181]413
[179]414SerialCommand_WaitAndPoll:
415 call Timer_SetCFifTimeout
416 jc SerialCommand_OutputWithParameters_ErrorAndPop2Words
417 push dx
418 push ax
419 or dl,SerialCommand_UART_lineStatus
420 in al,dx
421 test al,bh
422 pop ax
423 pop dx
424 jz SerialCommand_WaitAndPoll
425; fall-through
[181]426
427SerialCommand_WaitAndPoll_Done:
[179]428 ret
429
430;--------------------------------------------------------------------
431; SerialCommand_WriteProtocol
432;
433; Parameters:
434; ES:DI: Ptr to buffer
435; BL: Words to write (1-255, or 0=256)
436; BP/SI: Initialized for Checksum (-1 in each)
437; DX: I/O Port
438; Returns:
439; BP/SI: Checksum for written bytes, compared against ACK from server in .readLoop
440; Corrupts registers:
441; AX, BX, CX, DI
442;--------------------------------------------------------------------
[150]443ALIGN JUMP_ALIGN
[179]444SerialCommand_WriteProtocol:
445 mov bh,20h
[181]446
[179]447.writeLoop:
448 test bh,1
449 jnz SerialCommand_WaitAndPoll_Done
[181]450
[179]451 mov ax,[es:di] ; fetch next word
452 inc di
453 inc di
[181]454
[179]455 add bp,ax ; update checksum
456 adc bp,0
457 add si,bp
458 adc si,0
[150]459
[179]460.writeLoopChecksum:
461 call SerialCommand_WaitAndPoll_Init
[181]462
[179]463 out dx,al ; output first byte
[150]464
[179]465 call SerialCommand_WaitAndPoll
[181]466
[179]467 mov al,ah ; output second byte
468 out dx,al
469
470 dec bl
471 jnz .writeLoop
[181]472
[179]473 inc bh
[181]474
[179]475 mov ax,bp ; merge checksum for possible write (last loop)
[181]476 xor ax,si
477
[179]478 jmp .writeLoopChecksum
479
480
481; To return the port number and baud rate to the FinalizeDPT routine, we
482; stuff the value in a "vendor" specific area of the 512-byte IdentifyDevice
483; sector.
484;
485SerialCommand_IdentifyDevice_PackedPortAndBaud equ (157*2)
486
487;--------------------------------------------------------------------
488; SerialCommand_IdentifyDeviceToBufferInESSIwithDriveSelectByteInBH
489; Parameters:
490; BH: Drive Select byte for Drive and Head Select Register
491; DS: Segment to RAMVARS
492; ES:SI: Ptr to buffer to receive 512-byte IDE Information
493; CS:BP: Ptr to IDEVARS
494; Returns:
495; AH: INT 13h Error Code
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
[199]503 mov dx,[cs:bp+IDEVARS.bSerialCOMDigit]
[196]504 test dl,dl
[179]505 jz SerialCommand_AutoSerial
506
[199]507 xchg dh,dl ; dh (the COM character to print) will be transmitted to the server,
508 ; so we know this is not an auto detect
509
[179]510; fall-through
[181]511SerialCommand_IdentifyDeviceInDL_DriveInBH:
[179]512
513 push bp ; setup fake IDEREGS_AND_INTPACK
514
515 push dx
516
517 mov cl,1 ; 1 sector to move
518 push cx
519
520 mov bl,0a0h ; protocol command to ah and onto stack with bh
521 mov ah,bl
[181]522
[179]523 push bx
524
525 mov bp,sp
526
527 call SerialCommand_OutputWithParameters_DeviceInDL
528
529 pop bx
[181]530
531 pop cx
[179]532 pop dx
533
534 pop bp
535
[181]536; place packed port/baud in vendor area of packet, read by FinalizeDPT
537 mov byte [es:si+SerialCommand_IdentifyDevice_PackedPortAndBaud], dl
[179]538
539 ret
540
541;----------------------------------------------------------------------
542;
543; SerialCommand_AutoSerial
544;
545; When the SerialAuto IDEVARS entry is used, scans the COM ports on the machine for a possible serial connection.
[181]546;
547
[199]548SerialCommand_ScanPortAddresses: db DEVICE_SERIAL_COM7 >> 2
549 db DEVICE_SERIAL_COM6 >> 2
550 db DEVICE_SERIAL_COM5 >> 2
551 db DEVICE_SERIAL_COM4 >> 2
552 db DEVICE_SERIAL_COM3 >> 2
553 db DEVICE_SERIAL_COM2 >> 2
554 db DEVICE_SERIAL_COM1 >> 2
555 db 0
[179]556
[181]557ALIGN JUMP_ALIGN
558SerialCommand_AutoSerial:
[179]559 mov di,SerialCommand_ScanPortAddresses-1
[181]560
[179]561.nextPort:
562 inc di ; load next port address
563 mov dl,[cs:di]
[181]564
[179]565 mov dh,0 ; shift from one byte to two
[181]566 eSHL_IM dx, 2
[179]567 jz .exitNotFound
568
569;
570; Test for COM port presence, write to and read from registers
[181]571;
572 push dx
[179]573 add dl,SerialCommand_UART_lineControl
574 mov al, 09ah
575 out dx, al
576 in al, dx
577 pop dx
578 cmp al, 09ah
[181]579 jnz .nextPort
[179]580
581 mov al, 0ch
582 out dx, al
583 in al, dx
584 cmp al, 0ch
585 jnz .nextPort
586
587;
588; Pack into dl, baud rate starts at 0
[181]589;
[199]590 add dx,-(DEVICE_SERIAL_PACKEDPORTANDBAUD_STARTINGPORT)
591 shr dx,1 ; dh is zero at this point, and will be sent to the server,
592 ; so we know this is an auto detect
[181]593
[179]594 jmp .testFirstBaud
595
596;
597; Walk through 4 possible baud rates
[181]598;
599.nextBaud:
[179]600 inc dx
601 test dl,3
602 jz .nextPort
[181]603
604.testFirstBaud:
[179]605 call SerialCommand_IdentifyDeviceInDL_DriveInBH
606 jc .nextBaud
607
608 ret
[181]609
[179]610.exitNotFound:
[181]611 stc
[179]612 mov ah,1
613
614 ret
Note: See TracBrowser for help on using the repository browser.