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

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

Reworked the 'skip detecting the slave if there was no master' code to be more complete (includes configurator drives) and to take into account int13h/25h calls. Some of the changes in my last checkin have been rolled back as a result (they are no longer needed). I did take a byte out of RAMVARS, but there was an alignment byte available for the taking. I also added a perl script for padding and adding the checksum byte to a binary image, which can be invoked manually or with 'make checksum'.

File size: 18.9 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_Protocol_Write                    EQU     3
43SerialCommand_Protocol_Read                     EQU     2
44SerialCommand_Protocol_Inquire                  EQU     0
45SerialCommand_Protocol_Header                   EQU     0a0h
46
47;--------------------------------------------------------------------
48; SerialCommand_OutputWithParameters
49;   Parameters:
50;       BH:     Non-zero if 48-bit addressing used
51;               (ignored at present as 48-bit addressing is not supported)
52;       BL:     IDE Status Register bit to poll after command
53;               (ignored at present, since there is no IDE status register to poll)
54;       ES:SI:  Ptr to buffer (for data transfer commands)
55;       DS:DI:  Ptr to DPT (in RAMVARS segment)
56;       SS:BP:  Ptr to IDEREGS_AND_INTPACK
57;   Returns:
58;       AH:     INT 13h Error Code
59;       CF:     Cleared if success, Set if error
60;   Corrupts registers:
61;       AL, BX, CX, DX, (ES:SI for data transfer commands)
62;--------------------------------------------------------------------
63ALIGN JUMP_ALIGN
64SerialCommand_OutputWithParameters:
65
66        mov     ah,(SerialCommand_Protocol_Header | SerialCommand_Protocol_Read)
67
68        mov     al,[bp+IDEPACK.bCommand]
69
70        cmp     al,20h          ; Read Sectors IDE command
71        jz      .readOrWrite
72        inc     ah              ; now SerialCommand_Protocol_Write
73        cmp     al,30h          ; Write Sectors IDE command
74        jz      .readOrWrite
75
76;  all other commands return success
77;  including function 0ech which should return drive information, this is handled with the identify functions
78        xor     ah,ah           ;  also clears carry
79        ret
80
81.readOrWrite:
82        mov     [bp+IDEPACK.bFeatures],ah       ; store protocol command
83
84        mov     dl, byte [di+DPT.bSerialPortAndBaud]
85
86; fall-through
87
88;--------------------------------------------------------------------
89; SerialCommand_OutputWithParameters_DeviceInDL
90;   Parameters:
91;       AH:     Protocol Command
92;       DL:     Packed I/O port and baud rate
93;       ES:SI:  Ptr to buffer (for data transfer commands)
94;       SS:BP:  Ptr to IDEREGS_AND_INTPACK
95;   Returns:
96;       AH:     INT 13h Error Code
97;       CF:     Cleared if success, Set if error
98;   Corrupts registers:
99;       AL, BX, CX, DX, (ES:SI for data transfer commands)
100;--------------------------------------------------------------------
101SerialCommand_OutputWithParameters_DeviceInDL:
102
103        push    si
104        push    di
105        push    bp
106        push    es
107
108;
109; Unpack I/O port and baud from DPT
110;       Port to DX more or less for the remainder of the routine
111;       Baud in CH until UART initialization is complete
112;
113        mov     cl, dl
114
115        and     cl, DEVICE_SERIAL_PACKEDPORTANDBAUD_BAUDMASK
116        shl     cl, 1
117        mov     ch, SerialCommand_UART_divisorLow_startingBaud
118        shr     ch, cl
119        adc     ch, 0
120
121        and     dl, DEVICE_SERIAL_PACKEDPORTANDBAUD_PORTMASK
122        mov     dh, 0
123        shl     dx, 1           ; port offset already x4, needs one more shift to be x8
124        add     dx, DEVICE_SERIAL_PACKEDPORTANDBAUD_STARTINGPORT
125
126;
127; Buffer is referenced through ES:DI throughout, since we need to store faster than we read
128;
129        mov     di,si
130
131        mov     al,[bp+IDEPACK.bSectorCount]
132
133;
134; Command byte and sector count live at the top of the stack, pop/push are used to access
135;
136        push    ax
137
138;       cld     ; Shouldn't be needed. DF has already been cleared (line 24, Int13h.asm)
139
140;----------------------------------------------------------------------
141;
142; Initialize UART
143;
144; We do this each time since DOS (at boot) or another program may have
145; decided to reprogram the UART
146;
147        push    dx
148
149        mov     al,83h
150        add     dl,SerialCommand_UART_lineControl
151        out     dx,al
152
153        mov     al,ch
154        pop     dx              ; divisor low
155        out     dx,al
156
157        xor     ax,ax
158        inc     dx              ; divisor high
159        push    dx
160        out     dx,al
161
162        mov     al,047h
163        inc     dx              ;  fifo
164        out     dx,al
165
166        mov     al,03h
167        inc     dx              ;  linecontrol
168        out     dx,al
169
170        mov     al,0bh
171        inc     dx              ;  modemcontrol
172        out     dx,al
173
174        pop     dx              ; base, interrupts disabled
175        xor     ax,ax
176        out     dx,al
177        dec     dx
178
179;----------------------------------------------------------------------
180;
181; Send Command
182;
183; Sends first six bytes of IDEREGS_AND_INTPACK as the command
184;
185        call    Registers_NormalizeESDI
186
187        push    es              ; save off real buffer location
188        push    di
189
190        mov     di,bp           ; point to IDEREGS for command dispatch;
191        push    ss
192        pop     es
193
194        xor     si,si           ; initialize checksum for write
195        dec     si
196        mov     bp,si
197
198        mov     bl,03h      ; writing 3 words
199
200        call    SerialCommand_WriteProtocol
201
202        pop     di              ; restore real buffer location
203        pop     es
204
205        pop     ax              ; load command byte (done before call to .nextSector on subsequent iterations)
206        push    ax
207
208;
209; Top of the read/write loop, one iteration per sector
210;
211.nextSector:
212        xor     si,si           ; initialize checksum for read or write
213        dec     si
214        mov     bp,si
215
216        mov     bx,0100h
217
218        shr     ah,1            ; command byte, are we doing a write?
219        jnc     .readSector
220        call    SerialCommand_WriteProtocol
221
222        xor     bx,bx
223
224.readSector:
225        mov     cx,bx
226        inc     cx
227
228        mov     bl,dl           ; setup bl with proper values for read loop (bh comes later)
229
230;----------------------------------------------------------------------
231;
232; Timeout
233;
234; During read, we first poll in a tight loop, interrupts off, for the next character to come in
235; If that loop completes, then we assume there is a long delay involved, turn interrupts back on
236; and wait for a given number of timer ticks to pass.
237;
238; To save code space, we use the contents of DL to decide which byte in the word to return for reading.
239;
240.readTimeout:
241        push    cx
242        xor     cx,cx
243.readTimeoutLoop:
244        push    dx
245        or      dl,SerialCommand_UART_lineStatus
246        in      al,dx
247        pop     dx
248        shr     al,1
249        jc      .readTimeoutComplete
250        loop    .readTimeoutLoop
251        sti
252        mov     bh,1
253        call    SerialCommand_WaitAndPoll_Init
254        cli
255.readTimeoutComplete:
256        mov     bh,bl
257        or      bh,SerialCommand_UART_lineStatus
258
259        pop     cx
260        test    dl,1
261        jz      .readByte1Ready
262        jmp     .readByte2Ready
263
264;----------------------------------------------------------------------------
265;
266; Read Block (without interrupts, used when there is a FIFO, high speed)
267;
268; NOTE: This loop is very time sensitive.  Literally, another instruction
269; cannot be inserted into this loop without us falling behind at high
270; speed (460.8K baud) on a 4.77Mhz 8088, making it hard to receive
271; a full 512 byte block.
272;
273.readLoop:
274        stosw                   ; store word in caller's data buffer
275
276        add     bp, ax          ; update Fletcher's checksum
277        adc     bp, 0
278        add     si, bp
279        adc     si, 0
280
281        mov     dl,bh
282        in      al,dx
283        shr     al,1            ; data ready (byte 1)?
284        mov     dl,bl           ; get ready to read data
285        jnc     .readTimeout    ; nope not ready, update timeouts
286
287;
288; Entry point after initial timeout.  We enter here so that the checksum word
289; is not stored (and is left in AX after the loop is complete).
290;
291.readByte1Ready:
292        in      al, dx          ; read data byte 1
293
294        mov     ah, al          ; store byte in ah for now
295
296;
297; note the placement of this reset of dl to bh, and that it is
298; before the return, which is assymetric with where this is done
299; above for byte 1.  The value of dl is used by the timeout routine
300; to know which byte to return to (.read_byte1_ready or
301; .read_byte2_ready)
302;
303        mov     dl,bh
304
305        in      al,dx
306        shr     al,1            ; data ready (byte 2)?
307        jnc     .readTimeout
308.readByte2Ready:
309        mov     dl,bl
310        in      al, dx          ; read data byte 2
311
312        xchg    al, ah          ; ah was holding byte 1, reverse byte order
313
314        loop    .readLoop
315
316        sti                     ; interrupts back on ASAP, if we turned them off
317
318;
319; Compare checksums
320;
321        xor     bp,si
322        cmp     ax,bp
323        jnz     SerialCommand_OutputWithParameters_Error
324
325
326;----------------------------------------------------------------------
327;
328; Clear read buffer
329;
330; In case there are extra characters or an error in the FIFO, clear it out.
331; In theory the initialization of the UART registers above should have
332; taken care of this, but I have seen cases where this is not true.
333;
334.clearBuffer:
335        mov     dl,bh
336        in      al,dx
337        mov     dl,bl
338        test    al,08fh
339        jz      .clearBufferComplete
340        shr     al,1
341        in      al,dx
342        jc      .clearBuffer    ; note CF from shr above
343        jmp     SerialCommand_OutputWithParameters_Error
344
345.clearBufferComplete:
346        pop     ax              ; sector count and command byte
347        dec     al              ; decrememnt sector count
348        push    ax              ; save
349        jz      SerialCommand_OutputWithParameters_ReturnCodeInALCF    ; CF clear from .clearBuffer test above
350
351        cli                     ; interrupts back off for ACK byte to host
352                                ; (host could start sending data immediately)
353        out     dx,al           ; ACK with next sector number
354
355        jmp     .nextSector     ; all is well, time for next sector
356
357;---------------------------------------------------------------------------
358;
359; Cleanup, error reporting, and exit
360;
361
362;
363; Used in situations where a call is underway, such as with SerialCommand_WaitAndPoll
364;
365SerialCommand_OutputWithParameters_ErrorAndPop2Words:
366        pop     ax
367        pop     ax
368
369SerialCommand_OutputWithParameters_Error:
370        stc
371        mov     al,1
372
373SerialCommand_OutputWithParameters_ReturnCodeInALCF:
374        sti
375        mov     ah,al
376
377        pop     bp              ;  recover ax from stack, throw away
378
379        pop     es
380        pop     bp
381        pop     di
382        pop     si
383
384        ret
385
386;--------------------------------------------------------------------
387; SerialCommand_WaitAndPoll
388;
389;   Parameters:
390;       BH:     UART_LineStatus bit to test (20h for write, or 1h for read)
391;       DX:     Port address (OK if already incremented to UART_lineStatus)
392;       Stack:  2 words on the stack below the command/count word
393;   Returns:
394;       Returns when desired UART_LineStatus bit is cleared
395;       Jumps directly to error exit if timeout elapses (and cleans up stack)
396;   Corrupts registers:
397;       CX, flags
398;--------------------------------------------------------------------
399
400SerialCommand_WaitAndPoll_SoftDelayTicks   EQU   20
401
402ALIGN JUMP_ALIGN
403SerialCommand_WaitAndPoll_Init:
404        mov     cl,SerialCommand_WaitAndPoll_SoftDelayTicks
405        call    Timer_InitializeTimeoutWithTicksInCL
406; fall-through
407
408SerialCommand_WaitAndPoll:
409        call    Timer_SetCFifTimeout
410        jc      SerialCommand_OutputWithParameters_ErrorAndPop2Words
411        push    dx
412        push    ax
413        or      dl,SerialCommand_UART_lineStatus
414        in      al,dx
415        test    al,bh
416        pop     ax
417        pop     dx
418        jz      SerialCommand_WaitAndPoll
419; fall-through
420
421SerialCommand_WaitAndPoll_Done:
422        ret
423
424;--------------------------------------------------------------------
425; SerialCommand_WriteProtocol
426;
427;   Parameters:
428;       ES:DI:  Ptr to buffer
429;       BL:     Words to write (1-255, or 0=256)
430;       BP/SI:  Initialized for Checksum (-1 in each)
431;       DX:     I/O Port
432;   Returns:
433;       BP/SI:  Checksum for written bytes, compared against ACK from server in .readLoop
434;   Corrupts registers:
435;       AX, BX, CX, DI
436;--------------------------------------------------------------------
437ALIGN JUMP_ALIGN
438SerialCommand_WriteProtocol:
439        mov     bh,20h
440
441.writeLoop:
442        test    bh,1
443        jnz     SerialCommand_WaitAndPoll_Done
444
445        mov     ax,[es:di]      ; fetch next word
446        inc     di
447        inc     di
448
449        add     bp,ax           ; update checksum
450        adc     bp,0
451        add     si,bp
452        adc     si,0
453
454.writeLoopChecksum:
455        call    SerialCommand_WaitAndPoll_Init
456
457        out     dx,al           ; output first byte
458
459        call    SerialCommand_WaitAndPoll
460
461        mov     al,ah           ; output second byte
462        out     dx,al
463
464        dec     bl
465        jnz     .writeLoop
466
467        inc     bh
468
469        mov     ax,bp           ; merge checksum for possible write (last loop)
470        xor     ax,si
471
472        jmp     .writeLoopChecksum
473
474
475;--------------------------------------------------------------------
476; SerialCommand_IdentifyDeviceToBufferInESSIwithDriveSelectByteInBH
477;   Parameters:
478;       BH:     Drive Select byte for Drive and Head Select Register
479;       DS:     Segment to RAMVARS
480;       ES:SI:  Ptr to buffer to receive 512-byte IDE Information
481;       CS:BP:  Ptr to IDEVARS
482;   Returns:
483;       AH:     INT 13h Error Code
484;               NOTE: Not set (or checked) during drive detection 
485;       CF:     Cleared if success, Set if error
486;   Corrupts registers:
487;       AL, BL, CX, DX, SI, DI, ES
488;--------------------------------------------------------------------
489ALIGN JUMP_ALIGN
490SerialCommand_IdentifyDeviceToBufferInESSIwithDriveSelectByteInBH:
491;
492; To improve boot time, we do our best to avoid looking for slave serial drives when we already know the results 
493; from the looking for a master.  This is particuarly true when doing a COM port scan, as we will end up running
494; through all the COM ports and baud rates a second time. 
495;
496; But drive detection isn't the only case - we also need to get the right drive when called on int13h/25h. 
497;
498; The decision tree:
499;
500;    Master:
501;          bSerialPackedPortAndBaud Non-Zero:   -> Continue with bSerialPackedAndBaud (1)
502;          bSerialPackedPortAndBaud Zero: 
503;                         bLastSerial Zero:     -> Scan (2)
504;                         bLastSerial Non-Zero: -> Continue with bLastSerial (3)
505;                                   
506;    Slave:
507;          bSerialPackedPortAndBaud Non-Zero: 
508;                         bLastSerial Zero:     -> Error - Not Found (4)
509;                         bLastSerial Non-Zero: -> Continue with bSerialPackedAndBaud (5)
510;          bSerialPackedPortAndBaud Zero:     
511;                         bLastSerial Zero:     -> Error - Not Found (4)
512;                         bLastSerial Non-Zero: -> Continue with bLastSerial (6)
513;
514; (1) This was a port/baud that was explicilty set with the configurator.  In the drive detection case, as this 
515;     is the Master, we are checking out a new controller, and so don't care about the value of bLastSerial. 
516;     And as with the int13h/25h case, we just go off and get the needed information using the user's setting.
517; (2) We are using the special .ideVarsSerialAuto strucutre.  During drive detection, we would only be here
518;     if bLastSerial is zero (since we only scan if no explicit drives are set), so we go off to scan.
519; (3) We are using the special .ideVarsSerialAuto strucutre.  We won't get here during drive detection, but
520;     we might get here on an int13h/25h call.  If we have scanned COM drives, they are the ONLY serial drives
521;     in use, and so bLastSerial will reflect the port/baud setting for the scanned COM drives.
522; (4) No master has been found yet, therefore no slave should be found.  Avoiding the slave reduces boot time, 
523;     especially in the full COM port scan case.  Note that this is different from the hardware IDE, where we
524;     will scan for a slave even if a master is not present.  Note that if ANY master had been previously found,
525;     we will do the slave scan, which isn't harmful, it just wates time.  But the most common case (by a wide
526;     margin) will be just one serial controller.
527; (5) A COM port scan for a master had been previously completed, and a drive was found.  In a multiple serial
528;     controller scenario being called with int13h/25h, we need to use the value in bSerialPackedPortAndBaud 
529;     to make sure we get the proper drive.
530; (6) A COM port scan for a master had been previously completed, and a drive was found.  We would only get here 
531;     if no serial drive was explicitly set by the user in the configurator or that drive had not been found. 
532;     Instead of performing the full COM port scan for the slave, use the port/baud value stored during the 
533;     master scan.
534;       
535        mov     dl,[cs:bp+IDEVARS.bSerialPackedPortAndBaud]     
536        mov     al, byte [RAMVARS.xlateVars+XLATEVARS.bLastSerial]
537               
538        test    bh, FLG_DRVNHEAD_DRV
539        jz      .master
540
541        test    al,al           ; Take care of the case that is different between master and slave. 
542        jz      .error          ; Because we do this here, the jz after the "or" below will not be taken
543
544; fall-through
545.master:       
546        test    dl,dl
547        jnz     .identifyDeviceInDL
548
549        or      dl,al           ; Move bLast into position in dl, as well as test for zero
550        jz      .scanSerial
551       
552; fall-through
553.identifyDeviceInDL:   
554
555        push    bp              ; setup fake IDEREGS_AND_INTPACK
556
557        push    dx
558
559        mov     cl,1            ; 1 sector to move
560        push    cx
561
562        mov     bl,0a0h         ; protocol command to ah and onto stack with bh
563        mov     ah,bl
564
565        push    bx
566
567        mov     bp,sp
568        call    SerialCommand_OutputWithParameters_DeviceInDL
569
570        pop     bx
571
572        pop     cx
573        pop     dx
574
575        pop     bp
576; 
577; place packed port/baud in RAMVARS, read by FinalizeDPT and DetectDrives
578;
579; Note that this will be set during an int13h/25h call as well.  Which is OK since it is only used (at the
580; top of this routine) for drives found during a COM scan, and we only COM scan if there were no other 
581; COM drives found.  So we will only reaffirm the port/baud for the one COM port/baud that has a drive.
582; 
583        jc      .notFound                                           ; only store bLastSerial if success
584        mov     byte [RAMVARS.xlateVars+XLATEVARS.bLastSerial], dl
585
586.notFound:     
587        ret
588
589;----------------------------------------------------------------------
590;
591; SerialCommand_AutoSerial
592;
593; When the SerialAuto IDEVARS entry is used, scans the COM ports on the machine for a possible serial connection.
594;
595
596.scanPortAddresses: db  DEVICE_SERIAL_COM7 >> 2
597                    db  DEVICE_SERIAL_COM6 >> 2
598                    db  DEVICE_SERIAL_COM5 >> 2
599                    db  DEVICE_SERIAL_COM4 >> 2
600                    db  DEVICE_SERIAL_COM3 >> 2
601                    db  DEVICE_SERIAL_COM2 >> 2
602                    db  DEVICE_SERIAL_COM1 >> 2
603                    db  0
604
605ALIGN JUMP_ALIGN
606.scanSerial:
607        mov     di,.scanPortAddresses-1
608
609.nextPort:
610        inc     di              ; load next port address
611        mov     dl,[cs:di]
612
613        mov     dh,0            ; shift from one byte to two
614        eSHL_IM dx, 2
615        jz      .error
616
617;
618; Test for COM port presence, write to and read from registers
619;
620        push    dx
621        add     dl,SerialCommand_UART_lineControl
622        mov     al, 09ah
623        out     dx, al
624        in      al, dx
625        pop     dx
626        cmp     al, 09ah
627        jnz     .nextPort
628
629        mov     al, 0ch
630        out     dx, al
631        in      al, dx
632        cmp     al, 0ch
633        jnz     .nextPort
634
635;
636; Pack into dl, baud rate starts at 0
637;
638        add     dx,-(DEVICE_SERIAL_PACKEDPORTANDBAUD_STARTINGPORT)
639        shr     dx,1            ; dh is zero at this point, and will be sent to the server,
640                                ; so we know this is an auto detect
641
642        jmp     .testFirstBaud
643
644;
645; Walk through 4 possible baud rates
646;
647.nextBaud:
648        inc     dx
649        test    dl,3
650        jz      .nextPort
651
652.testFirstBaud:
653        call    .identifyDeviceInDL
654        jc      .nextBaud
655
656        ret
657
658.error: 
659        stc
660        ; mov       ah,1        ; setting the error code is unnecessary as this path can only be taken during
661                                ; drive detection, and drive detection works off CF and does not check AH
662        ret
663
664
Note: See TracBrowser for help on using the repository browser.