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

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

Main checkin, adding virtual IDE drive over serial port connection

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