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_@…, 12 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
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
[181]42SerialCommand_PackedPortAndBaud_StartingPort    EQU     240h
[179]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
[181]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;--------------------------------------------------------------------
67ALIGN JUMP_ALIGN
[179]68SerialCommand_OutputWithParameters:
[181]69
[179]70        mov     ah,(SerialCommand_Protocol_Header | SerialCommand_Protocol_Read)
[181]71
[179]72        mov     al,[bp+IDEPACK.bCommand]
[150]73
[179]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
[181]79
[179]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
[181]84
85.readOrWrite:
[179]86        mov     [bp+IDEPACK.bFeatures],ah       ; store protocol command
[181]87
88        mov     dl, byte [di+DPT.bSerialPortAndBaud]
89
[179]90; fall-through
[150]91
92;--------------------------------------------------------------------
[179]93; SerialCommand_OutputWithParameters_DeviceInDL
[150]94;   Parameters:
[181]95;       AH:     Protocol Command
[179]96;       DL:     Packed I/O port and baud rate
[150]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)
[181]104;--------------------------------------------------------------------
[179]105SerialCommand_OutputWithParameters_DeviceInDL:
[181]106
[179]107        push    si
108        push    di
109        push    bp
110        push    es
111
[181]112;
[179]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
[181]118
[179]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
[181]126        mov     dh, 0
[179]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
[181]132;
[179]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
[181]139;
[179]140        push    ax
141
[181]142;       cld     ; Shouldn't be needed. DF has already been cleared (line 24, Int13h.asm)
143
[179]144;----------------------------------------------------------------------
145;
146; Initialize UART
147;
[181]148; We do this each time since DOS (at boot) or another program may have
[179]149; decided to reprogram the UART
150;
151        push    dx
[181]152
[179]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
[181]168        out     dx,al
[179]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
[181]193
194        mov     di,bp           ; point to IDEREGS for command dispatch;
[179]195        push    ss
196        pop     es
197
198        xor     si,si           ; initialize checksum for write
[181]199        dec     si
[179]200        mov     bp,si
201
202        mov     bl,03h      ; writing 3 words
[181]203
[179]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
[181]214;
[179]215.nextSector:
216        xor     si,si           ; initialize checksum for read or write
217        dec     si
218        mov     bp,si
219
220        mov     bx,0100h
[181]221
[179]222        shr     ah,1            ; command byte, are we doing a write?
223        jnc     .readSector
224        call    SerialCommand_WriteProtocol
[181]225
[179]226        xor     bx,bx
227
228.readSector:
229        mov     cx,bx
230        inc     cx
[181]231
[179]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
[181]247.readTimeoutLoop:
[179]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
[181]258        cli
[179]259.readTimeoutComplete:
260        mov     bh,bl
261        or      bh,SerialCommand_UART_lineStatus
[181]262
[179]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;
[181]272; NOTE: This loop is very time sensitive.  Literally, another instruction
[179]273; cannot be inserted into this loop without us falling behind at high
[181]274; speed (460.8K baud) on a 4.77Mhz 8088, making it hard to receive
[179]275; a full 512 byte block.
276;
[181]277.readLoop:
278        stosw                   ; store word in caller's data buffer
279
[179]280        add     bp, ax          ; update Fletcher's checksum
281        adc     bp, 0
282        add     si, bp
283        adc     si, 0
284
[181]285        mov     dl,bh
286        in      al,dx
[179]287        shr     al,1            ; data ready (byte 1)?
[181]288        mov     dl,bl           ; get ready to read data
[179]289        jnc     .readTimeout    ; nope not ready, update timeouts
[181]290
291;
[179]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).
[181]294;
295.readByte1Ready:
[179]296        in      al, dx          ; read data byte 1
297
298        mov     ah, al          ; store byte in ah for now
[181]299
[179]300;
[181]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
[179]305; .read_byte2_ready)
306;
[181]307        mov     dl,bh
308
[179]309        in      al,dx
310        shr     al,1            ; data ready (byte 2)?
311        jnc     .readTimeout
[181]312.readByte2Ready:
313        mov     dl,bl
[179]314        in      al, dx          ; read data byte 2
315
316        xchg    al, ah          ; ah was holding byte 1, reverse byte order
[181]317
[179]318        loop    .readLoop
319
[181]320        sti                     ; interrupts back on ASAP, if we turned them off
321
[179]322;
323; Compare checksums
[181]324;
[179]325        xor     bp,si
326        cmp     ax,bp
327        jnz     SerialCommand_OutputWithParameters_Error
328
[181]329
[179]330;----------------------------------------------------------------------
[181]331;
[179]332; Clear read buffer
333;
334; In case there are extra characters or an error in the FIFO, clear it out.
[181]335; In theory the initialization of the UART registers above should have
[179]336; taken care of this, but I have seen cases where this is not true.
337;
338.clearBuffer:
[181]339        mov     dl,bh
[179]340        in      al,dx
[181]341        mov     dl,bl
[179]342        test    al,08fh
343        jz      .clearBufferComplete
344        shr     al,1
[181]345        in      al,dx
[179]346        jc      .clearBuffer    ; note CF from shr above
347        jmp     SerialCommand_OutputWithParameters_Error
[181]348
349.clearBufferComplete:
[179]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
[181]354
[179]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
[181]358
[179]359        jmp     .nextSector     ; all is well, time for next sector
360
361;---------------------------------------------------------------------------
362;
363; Cleanup, error reporting, and exit
364;
[181]365
366;
[179]367; Used in situations where a call is underway, such as with SerialCommand_WaitAndPoll
[181]368;
[179]369SerialCommand_OutputWithParameters_ErrorAndPop2Words:
370        pop     ax
371        pop     ax
372
[181]373SerialCommand_OutputWithParameters_Error:
374        stc
[179]375        mov     al,1
376
[181]377SerialCommand_OutputWithParameters_ReturnCodeInALCF:
378        sti
[179]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
[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)
[181]400;   Corrupts registers:
[179]401;       CX, flags
402;--------------------------------------------------------------------
403
404SerialCommand_WaitAndPoll_SoftDelayTicks   EQU   20
405
[181]406ALIGN JUMP_ALIGN
[179]407SerialCommand_WaitAndPoll_Init:
408        mov     cl,SerialCommand_WaitAndPoll_SoftDelayTicks
409        call    Timer_InitializeTimeoutWithTicksInCL
410; fall-through
[181]411
[179]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
[181]424
425SerialCommand_WaitAndPoll_Done:
[179]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
[181]444
[179]445.writeLoop:
446        test    bh,1
447        jnz     SerialCommand_WaitAndPoll_Done
[181]448
[179]449        mov     ax,[es:di]      ; fetch next word
450        inc     di
451        inc     di
[181]452
[179]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
[181]460
[179]461        out     dx,al           ; output first byte
[150]462
[179]463        call    SerialCommand_WaitAndPoll
[181]464
[179]465        mov     al,ah           ; output second byte
466        out     dx,al
467
468        dec     bl
469        jnz     .writeLoop
[181]470
[179]471        inc     bh
[181]472
[179]473        mov     ax,bp           ; merge checksum for possible write (last loop)
[181]474        xor     ax,si
475
[179]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
[181]503        dec     dx
[179]504        jz      SerialCommand_AutoSerial
505
506; fall-through
[181]507SerialCommand_IdentifyDeviceInDL_DriveInBH:
[179]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
[181]518
[179]519        push    bx
520
521        mov     bp,sp
522
523        call    SerialCommand_OutputWithParameters_DeviceInDL
524
525        pop     bx
[181]526
527        pop     cx
[179]528        pop     dx
529
530        pop     bp
531
[181]532; place packed port/baud in vendor area of packet, read by FinalizeDPT
533        mov     byte [es:si+SerialCommand_IdentifyDevice_PackedPortAndBaud], dl
[179]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.
[181]542;
543
[179]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
[181]548ALIGN JUMP_ALIGN
549SerialCommand_AutoSerial:
[179]550        mov     di,SerialCommand_ScanPortAddresses-1
[181]551
[179]552.nextPort:
553        inc     di              ; load next port address
554        mov     dl,[cs:di]
[181]555
[179]556        mov     dh,0            ; shift from one byte to two
[181]557        eSHL_IM dx, 2
[179]558        jz      .exitNotFound
559
560;
561; Test for COM port presence, write to and read from registers
[181]562;
563        push    dx
[179]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
[181]570        jnz     .nextPort
[179]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
[181]580;
[179]581        add     dx,-(SerialCommand_PackedPortAndBaud_StartingPort)
582        shr     dx,1
[181]583
[179]584        jmp     .testFirstBaud
585
586;
587; Walk through 4 possible baud rates
[181]588;
589.nextBaud:
[179]590        inc     dx
591        test    dl,3
592        jz      .nextPort
[181]593
594.testFirstBaud:
[179]595        call    SerialCommand_IdentifyDeviceInDL_DriveInBH
596        jc      .nextBaud
597
598        ret
[181]599
[179]600.exitNotFound:
[181]601        stc
[179]602        mov     ah,1
603
604        ret
Note: See TracBrowser for help on using the repository browser.