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

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

Changes to all parts of the project:

  • Size optimizations.
  • Added a define (EXCLUDE_FROM_XTIDECFG) to exclude unused library code from XTIDECFG.
  • Tried to minimize time spent with interrupts disabled.
  • Some minor attempts to improve speed (reordering instructions etc).
  • Tried to improve readability, did some cleanup and fixed some errors in comments.
File size: 15.3 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_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
51;--------------------------------------------------------------------
52; SerialCommand_OutputWithParameters
53; Parameters:
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
61; Returns:
62; AH: INT 13h Error Code
63; CF: Cleared if success, Set if error
64; Corrupts registers:
65; AL, BX, CX, DX, (ES:SI for data transfer commands)
66;--------------------------------------------------------------------
67ALIGN JUMP_ALIGN
68SerialCommand_OutputWithParameters:
69
70 mov ah,(SerialCommand_Protocol_Header | SerialCommand_Protocol_Read)
71
72 mov al,[bp+IDEPACK.bCommand]
73
74 cmp al,20h ; Read Sectors IDE command
75 jz .readOrWrite
76 inc ah ; now SerialCommand_Protocol_Write
77 cmp al,30h ; Write Sectors IDE command
78 jz .readOrWrite
79
80; all other commands return success
81; including function 0ech which should return drive information, this is handled with the identify functions
82 xor ah,ah ; also clears carry
83 ret
84
85.readOrWrite:
86 mov [bp+IDEPACK.bFeatures],ah ; store protocol command
87
88 mov dl, byte [di+DPT.bSerialPortAndBaud]
89
90; fall-through
91
92;--------------------------------------------------------------------
93; SerialCommand_OutputWithParameters_DeviceInDL
94; Parameters:
95; AH: Protocol Command
96; DL: Packed I/O port and baud rate
97; ES:SI: Ptr to buffer (for data transfer commands)
98; SS:BP: Ptr to IDEREGS_AND_INTPACK
99; Returns:
100; AH: INT 13h Error Code
101; CF: Cleared if success, Set if error
102; Corrupts registers:
103; AL, BX, CX, DX, (ES:SI for data transfer commands)
104;--------------------------------------------------------------------
105SerialCommand_OutputWithParameters_DeviceInDL:
106
107 push si
108 push di
109 push bp
110 push es
111
112;
113; Unpack I/O port and baud from DPT
114; Port to DX more or less for the remainder of the routine
115; Baud in CH until UART initialization is complete
116;
117 mov cl, dl
118
119 and cl, SerialCommand_PackedPortAndBaud_BaudMask
120 shl cl, 1
121 mov ch, SerialCommand_UART_divisorLow_startingBaud
122 shr ch, cl
123 adc ch, 0
124
125 and dl, SerialCommand_PackedPortAndBaud_PortMask
126 mov dh, 0
127 shl dx, 1
128 add dx, SerialCommand_PackedPortAndBaud_StartingPort
129
130;
131; Buffer is referenced through ES:DI throughout, since we need to store faster than we read
132;
133 mov di,si
134
135 mov al,[bp+IDEPACK.bSectorCount]
136
137;
138; Command byte and sector count live at the top of the stack, pop/push are used to access
139;
140 push ax
141
142; cld ; Shouldn't be needed. DF has already been cleared (line 24, Int13h.asm)
143
144;----------------------------------------------------------------------
145;
146; Initialize UART
147;
148; We do this each time since DOS (at boot) or another program may have
149; decided to reprogram the UART
150;
151 push dx
152
153 mov al,83h
154 add dl,SerialCommand_UART_lineControl
155 out dx,al
156
157 mov al,ch
158 pop dx ; divisor low
159 out dx,al
160
161 xor ax,ax
162 inc dx ; divisor high
163 push dx
164 out dx,al
165
166 mov al,047h
167 inc dx ; fifo
168 out dx,al
169
170 mov al,03h
171 inc dx ; linecontrol
172 out dx,al
173
174 mov al,0bh
175 inc dx ; modemcontrol
176 out dx,al
177
178 pop dx ; base, interrupts disabled
179 xor ax,ax
180 out dx,al
181 dec dx
182
183;----------------------------------------------------------------------
184;
185; Send Command
186;
187; Sends first six bytes of IDEREGS_AND_INTPACK as the command
188;
189 call Registers_NormalizeESDI
190
191 push es ; save off real buffer location
192 push di
193
194 mov di,bp ; point to IDEREGS for command dispatch;
195 push ss
196 pop es
197
198 xor si,si ; initialize checksum for write
199 dec si
200 mov bp,si
201
202 mov bl,03h ; writing 3 words
203
204 call SerialCommand_WriteProtocol
205
206 pop di ; restore real buffer location
207 pop es
208
209 pop ax ; load command byte (done before call to .nextSector on subsequent iterations)
210 push ax
211
212;
213; Top of the read/write loop, one iteration per sector
214;
215.nextSector:
216 xor si,si ; initialize checksum for read or write
217 dec si
218 mov bp,si
219
220 mov bx,0100h
221
222 shr ah,1 ; command byte, are we doing a write?
223 jnc .readSector
224 call SerialCommand_WriteProtocol
225
226 xor bx,bx
227
228.readSector:
229 mov cx,bx
230 inc cx
231
232 mov bl,dl ; setup bl with proper values for read loop (bh comes later)
233
234;----------------------------------------------------------------------
235;
236; Timeout
237;
238; During read, we first poll in a tight loop, interrupts off, for the next character to come in
239; If that loop completes, then we assume there is a long delay involved, turn interrupts back on
240; and wait for a given number of timer ticks to pass.
241;
242; To save code space, we use the contents of DL to decide which byte in the word to return for reading.
243;
244.readTimeout:
245 push cx
246 xor cx,cx
247.readTimeoutLoop:
248 push dx
249 or dl,SerialCommand_UART_lineStatus
250 in al,dx
251 pop dx
252 shr al,1
253 jc .readTimeoutComplete
254 loop .readTimeoutLoop
255 sti
256 mov bh,1
257 call SerialCommand_WaitAndPoll_Init
258 cli
259.readTimeoutComplete:
260 mov bh,bl
261 or bh,SerialCommand_UART_lineStatus
262
263 pop cx
264 test dl,1
265 jz .readByte1Ready
266 jmp .readByte2Ready
267
268;----------------------------------------------------------------------------
269;
270; Read Block (without interrupts, used when there is a FIFO, high speed)
271;
272; NOTE: This loop is very time sensitive. Literally, another instruction
273; cannot be inserted into this loop without us falling behind at high
274; speed (460.8K baud) on a 4.77Mhz 8088, making it hard to receive
275; a full 512 byte block.
276;
277.readLoop:
278 stosw ; store word in caller's data buffer
279
280 add bp, ax ; update Fletcher's checksum
281 adc bp, 0
282 add si, bp
283 adc si, 0
284
285 mov dl,bh
286 in al,dx
287 shr al,1 ; data ready (byte 1)?
288 mov dl,bl ; get ready to read data
289 jnc .readTimeout ; nope not ready, update timeouts
290
291;
292; Entry point after initial timeout. We enter here so that the checksum word
293; is not stored (and is left in AX after the loop is complete).
294;
295.readByte1Ready:
296 in al, dx ; read data byte 1
297
298 mov ah, al ; store byte in ah for now
299
300;
301; note the placement of this reset of dl to bh, and that it is
302; before the return, which is assymetric with where this is done
303; above for byte 1. The value of dl is used by the timeout routine
304; to know which byte to return to (.read_byte1_ready or
305; .read_byte2_ready)
306;
307 mov dl,bh
308
309 in al,dx
310 shr al,1 ; data ready (byte 2)?
311 jnc .readTimeout
312.readByte2Ready:
313 mov dl,bl
314 in al, dx ; read data byte 2
315
316 xchg al, ah ; ah was holding byte 1, reverse byte order
317
318 loop .readLoop
319
320 sti ; interrupts back on ASAP, if we turned them off
321
322;
323; Compare checksums
324;
325 xor bp,si
326 cmp ax,bp
327 jnz SerialCommand_OutputWithParameters_Error
328
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 stc
375 mov al,1
376
377SerialCommand_OutputWithParameters_ReturnCodeInALCF:
378 sti
379 mov ah,al
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
390;--------------------------------------------------------------------
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;--------------------------------------------------------------------
441ALIGN JUMP_ALIGN
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
457
458.writeLoopChecksum:
459 call SerialCommand_WaitAndPoll_Init
460
461 out dx,al ; output first byte
462
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 eSHL_IM dx, 2
558 jz .exitNotFound
559
560;
561; Test for COM port presence, write to and read from registers
562;
563 push dx
564 add dl,SerialCommand_UART_lineControl
565 mov al, 09ah
566 out dx, al
567 in al, dx
568 pop dx
569 cmp al, 09ah
570 jnz .nextPort
571
572 mov al, 0ch
573 out dx, al
574 in al, dx
575 cmp al, 0ch
576 jnz .nextPort
577
578;
579; Pack into dl, baud rate starts at 0
580;
581 add dx,-(SerialCommand_PackedPortAndBaud_StartingPort)
582 shr dx,1
583
584 jmp .testFirstBaud
585
586;
587; Walk through 4 possible baud rates
588;
589.nextBaud:
590 inc dx
591 test dl,3
592 jz .nextPort
593
594.testFirstBaud:
595 call SerialCommand_IdentifyDeviceInDL_DriveInBH
596 jc .nextBaud
597
598 ret
599
600.exitNotFound:
601 stc
602 mov ah,1
603
604 ret
Note: See TracBrowser for help on using the repository browser.