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

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

Added printing of COM port and baud rate, when set explicitly by idecfg. Although it eats some bytes, I think it is worth it, since the BIOS will be looking for a server on a particular com port and baud rate, and it could be hard to troubleshoot a mismatch without this information. However, if we become space crunched, this change can be backed out.

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