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

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

Added buffer pointer denormalization check within the serial port code and reused the last portion of the NormalizeESSI and NormalizeESDI routines to save memory.

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