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

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

Added printing of COM port and baud rate, when set explicitly by idecfg. Although it eats some bytes, I think it is worth it, since the BIOS will be looking for a server on a particular com port and baud rate, and it could be hard to troubleshoot a mismatch without this information. However, if we become space crunched, this change can be backed out.

File size: 15.6 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
[179]121 and cl, SerialCommand_PackedPortAndBaud_BaudMask
122 shl cl, 1
123 mov ch, SerialCommand_UART_divisorLow_startingBaud
124 shr ch, cl
125 adc ch, 0
126
127 and dl, SerialCommand_PackedPortAndBaud_PortMask
[181]128 mov dh, 0
[196]129 shl dx, 1 ; port offset already x4, needs one more shift to be x8
[179]130 add dx, SerialCommand_PackedPortAndBaud_StartingPort
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
[196]503 mov dl,[cs:bp+IDEVARS.bSerialPackedPortAndBaud]
504 test dl,dl
[179]505 jz SerialCommand_AutoSerial
506
507; fall-through
[181]508SerialCommand_IdentifyDeviceInDL_DriveInBH:
[179]509
510 push bp ; setup fake IDEREGS_AND_INTPACK
511
512 push dx
513
514 mov cl,1 ; 1 sector to move
515 push cx
516
517 mov bl,0a0h ; protocol command to ah and onto stack with bh
518 mov ah,bl
[181]519
[179]520 push bx
521
522 mov bp,sp
523
524 call SerialCommand_OutputWithParameters_DeviceInDL
525
526 pop bx
[181]527
528 pop cx
[179]529 pop dx
530
531 pop bp
532
[181]533; place packed port/baud in vendor area of packet, read by FinalizeDPT
534 mov byte [es:si+SerialCommand_IdentifyDevice_PackedPortAndBaud], dl
[179]535
536 ret
537
538;----------------------------------------------------------------------
539;
540; SerialCommand_AutoSerial
541;
542; When the SerialAuto IDEVARS entry is used, scans the COM ports on the machine for a possible serial connection.
[181]543;
544
[196]545SerialCommand_ScanPortAddresses: db 0b8h, 0f8h, 0bch, 0bah, 0fah, 0beh, 0feh, 0
546; Corresponds to I/O port: 3f8, 2f8, 3e8, 2e8, 2f0, 3e0, 2e0, 260, 368, 268, 360, 270
547; COM Assignments: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
548; Corresponds to Packed I/O port (hex): 37, 17, 35, 15, 16, 34, 14, 4, 25, 5, 24, 6
[179]549
[181]550ALIGN JUMP_ALIGN
551SerialCommand_AutoSerial:
[179]552 mov di,SerialCommand_ScanPortAddresses-1
[181]553
[179]554.nextPort:
555 inc di ; load next port address
556 mov dl,[cs:di]
[181]557
[179]558 mov dh,0 ; shift from one byte to two
[181]559 eSHL_IM dx, 2
[179]560 jz .exitNotFound
561
562;
563; Test for COM port presence, write to and read from registers
[181]564;
565 push dx
[179]566 add dl,SerialCommand_UART_lineControl
567 mov al, 09ah
568 out dx, al
569 in al, dx
570 pop dx
571 cmp al, 09ah
[181]572 jnz .nextPort
[179]573
574 mov al, 0ch
575 out dx, al
576 in al, dx
577 cmp al, 0ch
578 jnz .nextPort
579
580;
581; Pack into dl, baud rate starts at 0
[181]582;
[179]583 add dx,-(SerialCommand_PackedPortAndBaud_StartingPort)
584 shr dx,1
[181]585
[179]586 jmp .testFirstBaud
587
588;
589; Walk through 4 possible baud rates
[181]590;
591.nextBaud:
[179]592 inc dx
593 test dl,3
594 jz .nextPort
[181]595
596.testFirstBaud:
[179]597 call SerialCommand_IdentifyDeviceInDL_DriveInBH
598 jc .nextBaud
599
600 ret
[181]601
[179]602.exitNotFound:
[181]603 stc
[179]604 mov ah,1
605
606 ret
Note: See TracBrowser for help on using the repository browser.