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

Last change on this file since 199 was 199, checked in by gregli@…, 11 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
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
42; note that the actual StaringPoint port address is not achievable (results in 0 which triggers auto detect)
43SerialCommand_PackedPortAndBaud_StartingPort    EQU     240h
44       
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
52
53;--------------------------------------------------------------------
54; SerialCommand_OutputWithParameters
55;   Parameters:
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
63;   Returns:
64;       AH:     INT 13h Error Code
65;       CF:     Cleared if success, Set if error
66;   Corrupts registers:
67;       AL, BX, CX, DX, (ES:SI for data transfer commands)
68;--------------------------------------------------------------------
69ALIGN JUMP_ALIGN
70SerialCommand_OutputWithParameters:
71
72        mov     ah,(SerialCommand_Protocol_Header | SerialCommand_Protocol_Read)
73
74        mov     al,[bp+IDEPACK.bCommand]
75
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
81
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
86
87.readOrWrite:
88        mov     [bp+IDEPACK.bFeatures],ah       ; store protocol command
89
90        mov     dl, byte [di+DPT.bSerialPortAndBaud]
91
92; fall-through
93
94;--------------------------------------------------------------------
95; SerialCommand_OutputWithParameters_DeviceInDL
96;   Parameters:
97;       AH:     Protocol Command
98;       DL:     Packed I/O port and baud rate
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)
106;--------------------------------------------------------------------
107SerialCommand_OutputWithParameters_DeviceInDL:
108
109        push    si
110        push    di
111        push    bp
112        push    es
113
114;
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
120
121        and     cl, DEVICE_SERIAL_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, DEVICE_SERIAL_PACKEDPORTANDBAUD_PORTMASK
128        mov     dh, 0
129        shl     dx, 1           ; port offset already x4, needs one more shift to be x8
130        add     dx, DEVICE_SERIAL_PACKEDPORTANDBAUD_STARTINGPORT
131
132;
133; Buffer is referenced through ES:DI throughout, since we need to store faster than we read
134;
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
141;
142        push    ax
143
144;       cld     ; Shouldn't be needed. DF has already been cleared (line 24, Int13h.asm)
145
146;----------------------------------------------------------------------
147;
148; Initialize UART
149;
150; We do this each time since DOS (at boot) or another program may have
151; decided to reprogram the UART
152;
153        push    dx
154
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
170        out     dx,al
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
195
196        mov     di,bp           ; point to IDEREGS for command dispatch;
197        push    ss
198        pop     es
199
200        xor     si,si           ; initialize checksum for write
201        dec     si
202        mov     bp,si
203
204        mov     bl,03h      ; writing 3 words
205
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
216;
217.nextSector:
218        xor     si,si           ; initialize checksum for read or write
219        dec     si
220        mov     bp,si
221
222        mov     bx,0100h
223
224        shr     ah,1            ; command byte, are we doing a write?
225        jnc     .readSector
226        call    SerialCommand_WriteProtocol
227
228        xor     bx,bx
229
230.readSector:
231        mov     cx,bx
232        inc     cx
233
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
249.readTimeoutLoop:
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
260        cli
261.readTimeoutComplete:
262        mov     bh,bl
263        or      bh,SerialCommand_UART_lineStatus
264
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;
274; NOTE: This loop is very time sensitive.  Literally, another instruction
275; cannot be inserted into this loop without us falling behind at high
276; speed (460.8K baud) on a 4.77Mhz 8088, making it hard to receive
277; a full 512 byte block.
278;
279.readLoop:
280        stosw                   ; store word in caller's data buffer
281
282        add     bp, ax          ; update Fletcher's checksum
283        adc     bp, 0
284        add     si, bp
285        adc     si, 0
286
287        mov     dl,bh
288        in      al,dx
289        shr     al,1            ; data ready (byte 1)?
290        mov     dl,bl           ; get ready to read data
291        jnc     .readTimeout    ; nope not ready, update timeouts
292
293;
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).
296;
297.readByte1Ready:
298        in      al, dx          ; read data byte 1
299
300        mov     ah, al          ; store byte in ah for now
301
302;
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
307; .read_byte2_ready)
308;
309        mov     dl,bh
310
311        in      al,dx
312        shr     al,1            ; data ready (byte 2)?
313        jnc     .readTimeout
314.readByte2Ready:
315        mov     dl,bl
316        in      al, dx          ; read data byte 2
317
318        xchg    al, ah          ; ah was holding byte 1, reverse byte order
319
320        loop    .readLoop
321
322        sti                     ; interrupts back on ASAP, if we turned them off
323
324;
325; Compare checksums
326;
327        xor     bp,si
328        cmp     ax,bp
329        jnz     SerialCommand_OutputWithParameters_Error
330
331
332;----------------------------------------------------------------------
333;
334; Clear read buffer
335;
336; In case there are extra characters or an error in the FIFO, clear it out.
337; In theory the initialization of the UART registers above should have
338; taken care of this, but I have seen cases where this is not true.
339;
340.clearBuffer:
341        mov     dl,bh
342        in      al,dx
343        mov     dl,bl
344        test    al,08fh
345        jz      .clearBufferComplete
346        shr     al,1
347        in      al,dx
348        jc      .clearBuffer    ; note CF from shr above
349        jmp     SerialCommand_OutputWithParameters_Error
350
351.clearBufferComplete:
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
356
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
360
361        jmp     .nextSector     ; all is well, time for next sector
362
363;---------------------------------------------------------------------------
364;
365; Cleanup, error reporting, and exit
366;
367
368;
369; Used in situations where a call is underway, such as with SerialCommand_WaitAndPoll
370;
371SerialCommand_OutputWithParameters_ErrorAndPop2Words:
372        pop     ax
373        pop     ax
374
375SerialCommand_OutputWithParameters_Error:
376        stc
377        mov     al,1
378
379SerialCommand_OutputWithParameters_ReturnCodeInALCF:
380        sti
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
392;--------------------------------------------------------------------
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)
402;   Corrupts registers:
403;       CX, flags
404;--------------------------------------------------------------------
405
406SerialCommand_WaitAndPoll_SoftDelayTicks   EQU   20
407
408ALIGN JUMP_ALIGN
409SerialCommand_WaitAndPoll_Init:
410        mov     cl,SerialCommand_WaitAndPoll_SoftDelayTicks
411        call    Timer_InitializeTimeoutWithTicksInCL
412; fall-through
413
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
426
427SerialCommand_WaitAndPoll_Done:
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;--------------------------------------------------------------------
443ALIGN JUMP_ALIGN
444SerialCommand_WriteProtocol:
445        mov     bh,20h
446
447.writeLoop:
448        test    bh,1
449        jnz     SerialCommand_WaitAndPoll_Done
450
451        mov     ax,[es:di]      ; fetch next word
452        inc     di
453        inc     di
454
455        add     bp,ax           ; update checksum
456        adc     bp,0
457        add     si,bp
458        adc     si,0
459
460.writeLoopChecksum:
461        call    SerialCommand_WaitAndPoll_Init
462
463        out     dx,al           ; output first byte
464
465        call    SerialCommand_WaitAndPoll
466
467        mov     al,ah           ; output second byte
468        out     dx,al
469
470        dec     bl
471        jnz     .writeLoop
472
473        inc     bh
474
475        mov     ax,bp           ; merge checksum for possible write (last loop)
476        xor     ax,si
477
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
503        mov     dx,[cs:bp+IDEVARS.bSerialCOMDigit]
504        test    dl,dl
505        jz      SerialCommand_AutoSerial
506
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       
510; fall-through
511SerialCommand_IdentifyDeviceInDL_DriveInBH:
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
522
523        push    bx
524
525        mov     bp,sp
526
527        call    SerialCommand_OutputWithParameters_DeviceInDL
528
529        pop     bx
530
531        pop     cx
532        pop     dx
533
534        pop     bp
535
536; place packed port/baud in vendor area of packet, read by FinalizeDPT
537        mov     byte [es:si+SerialCommand_IdentifyDevice_PackedPortAndBaud], dl
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.
546;
547
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
556
557ALIGN JUMP_ALIGN
558SerialCommand_AutoSerial:
559        mov     di,SerialCommand_ScanPortAddresses-1
560
561.nextPort:
562        inc     di              ; load next port address
563        mov     dl,[cs:di]
564
565        mov     dh,0            ; shift from one byte to two
566        eSHL_IM dx, 2
567        jz      .exitNotFound
568
569;
570; Test for COM port presence, write to and read from registers
571;
572        push    dx
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
579        jnz     .nextPort
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
589;
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
593
594        jmp     .testFirstBaud
595
596;
597; Walk through 4 possible baud rates
598;
599.nextBaud:
600        inc     dx
601        test    dl,3
602        jz      .nextPort
603
604.testFirstBaud:
605        call    SerialCommand_IdentifyDeviceInDL_DriveInBH
606        jc      .nextBaud
607
608        ret
609
610.exitNotFound:
611        stc
612        mov     ah,1
613
614        ret
Note: See TracBrowser for help on using the repository browser.