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
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.