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

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

Main checkin, adding virtual IDE drive over serial port connection

File size: 15.5 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;--------------------------------------------------------------------
67
68ALIGN JUMP_ALIGN
69SerialCommand_OutputWithParameters:
70       
71        mov     ah,(SerialCommand_Protocol_Header | SerialCommand_Protocol_Read)
72       
73        mov     al,[bp+IDEPACK.bCommand]
74
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
92
93;--------------------------------------------------------------------
94; SerialCommand_OutputWithParameters_DeviceInDL
95;   Parameters:
96;       AH:     Protocol Command 
97;       DL:     Packed I/O port and baud rate
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)
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
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        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.