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

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

Serial Port: Missed updating some comments in the code from the last checkin.

File size: 23.6 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
16
17;
18; Values for UART_divisorLow:
19; 60h = 1200, 30h = 2400, 18h = 4800, 0ch = 9600, 6 = 19200, 4 = 28800, 3 = 38400, 2 = 57600, 1 = 115200
20;
21SerialCommand_UART_divisorLow                   EQU     0
22
23;
24; UART_divisorHigh is zero for all speeds including and above 1200 baud (which is all we do)
25;
26SerialCommand_UART_divisorHigh                  EQU     1
27
28SerialCommand_UART_interruptIdent               EQU     2
29SerialCommand_UART_FIFOControl                  EQU     2
30
31SerialCommand_UART_lineControl                  EQU     3
32
33SerialCommand_UART_modemControl                 EQU     4
34
35SerialCommand_UART_lineStatus                   EQU     5
36
37SerialCommand_UART_modemStatus                  EQU     6
38
39SerialCommand_UART_scratch                      EQU     7
40
41SerialCommand_Protocol_Write                    EQU     3
42SerialCommand_Protocol_Read                     EQU     2
43SerialCommand_Protocol_Inquire                  EQU     0
44SerialCommand_Protocol_Header                   EQU     0a0h
45
46;--------------------------------------------------------------------
47; SerialCommand_OutputWithParameters
48;   Parameters:
49;       BH:     Non-zero if 48-bit addressing used
50;               (ignored at present as 48-bit addressing is not supported)
51;       BL:     IDE Status Register bit to poll after command
52;               (ignored at present, since there is no IDE status register to poll)
53;       ES:SI:  Ptr to buffer (for data transfer commands)
54;       DS:DI:  Ptr to DPT (in RAMVARS segment)
55;       SS:BP:  Ptr to IDEREGS_AND_INTPACK
56;   Returns:
57;       AH:     INT 13h Error Code
58;       CF:     Cleared if success, Set if error
59;   Corrupts registers:
60;       AL, BX, CX, DX, (ES:SI for data transfer commands)
61;--------------------------------------------------------------------
62ALIGN JUMP_ALIGN
63SerialCommand_OutputWithParameters:
64
65        mov     ah,(SerialCommand_Protocol_Header | SerialCommand_Protocol_Read)
66
67        mov     al,[bp+IDEPACK.bCommand]
68
69        cmp     al,20h          ; Read Sectors IDE command
70        jz      .readOrWrite
71        inc     ah              ; now SerialCommand_Protocol_Write
72        cmp     al,30h          ; Write Sectors IDE command
73        jz      .readOrWrite
74
75;  all other commands return success
76;  including function 0ech which should return drive information, this is handled with the identify functions
77;
78        xor     ah,ah           ;  also clears carry
79        ret
80
81.readOrWrite:
82        mov     [bp+IDEPACK.bFeatures],ah       ; store protocol command
83
84        mov     dx, [di+DPT_SERIAL.wSerialPortAndBaud]
85
86; fall-through
87
88;--------------------------------------------------------------------
89; SerialCommand_OutputWithParameters_DeviceInDX
90;   Parameters:
91;       AH:     Protocol Command
92;       DX:     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_DeviceInDX:
102
103        push    si
104        push    di
105        push    bp
106
107;
108; Unpack I/O port and baud from DPT
109;       Port to DX for the remainder of the routine (+/- different register offsets)
110;       Baud in CH until UART initialization is complete
111;
112        mov     ch,dh
113        xor     dh,dh
114        eSHL_IM dx, 2           ; shift from one byte to two       
115       
116        mov     al,[bp+IDEPACK.bSectorCount]
117
118;
119; Command byte and sector count live at the top of the stack, pop/push are used to access
120;
121        push    ax
122
123%if 0
124        cld     ; Shouldn't be needed. DF has already been cleared (line 24, Int13h.asm)
125%endif
126
127;----------------------------------------------------------------------
128;
129; Initialize UART
130;
131; We do this each time since DOS (at boot) or another program may have
132; decided to reprogram the UART
133;
134        mov     bl,dl           ; setup BL with proper values for read/write loops (BH comes later)
135
136        mov     al,83h
137        add     dl,SerialCommand_UART_lineControl
138        out     dx,al
139
140        mov     al,ch
141        mov     dl,bl           ; divisor low
142        out     dx,al
143
144        xor     ax,ax
145        inc     dx              ; divisor high
146        push    dx
147        out     dx,al
148
149        mov     al,047h
150        inc     dx              ;  fifo
151        out     dx,al
152
153        mov     al,03h
154        inc     dx              ;  linecontrol
155        out     dx,al
156
157        mov     al,0bh
158        inc     dx              ;  modemcontrol
159        out     dx,al
160
161        inc     dx              ;  linestatus (no output now, just setting up BH for later use)
162        mov     bh,dl
163
164        pop     dx              ; base, interrupts disabled
165        xor     ax,ax
166        out     dx,al
167
168;----------------------------------------------------------------------
169;
170; Send Command
171;
172; Sends first six bytes of IDEREGS_AND_INTPACK as the command
173;
174        push    es              ; save off real buffer location
175        push    si
176
177        mov     si,bp           ; point to IDEREGS for command dispatch;
178        push    ss
179        pop     es
180
181        mov     di,0ffffh       ; initialize checksum for write
182        mov     bp,di
183
184        mov     cx,4            ; writing 3 words (plus 1)
185
186        cli                     ; interrupts off...
187
188        call    SerialCommand_WriteProtocol.entry
189
190        pop     di              ; restore real buffer location (note change from SI to DI)
191                                ; Buffer is primarily referenced through ES:DI throughout, since
192                                ; we need to store (read sector) faster than we read (write sector)
193        pop     es
194
195%if 0
196;;; no longer needed, since the pointer is normalized before we are called and we do not support
197;;; more than 128 sectors (and for 128 specifically, the pointer must be segment aligned).
198;;; See comments below at the point this entry point was called for more details...
199.nextSectorNormalize:
200        call    Registers_NormalizeESDI
201%endif
202
203        pop     ax              ; load command byte (done before call to .nextSector on subsequent iterations)
204        push    ax
205
206;
207; Top of the read/write loop, one iteration per sector
208;
209.nextSector:
210        mov     si,0ffffh       ; initialize checksum for read or write
211        mov     bp,si
212
213        mov     cx,0101h        ; writing 256 words (plus 1)
214
215        shr     ah,1            ; command byte, are we doing a write?
216        jnc     .readEntry
217
218        xchg    si,di
219        call    SerialCommand_WriteProtocol.entry
220        xchg    si,di
221
222        inc     cx              ; CX = 1 now (0 out of WriteProtocol)
223        jmp     .readEntry
224
225;----------------------------------------------------------------------
226;
227; Timeout
228;
229; To save code space, we use the contents of DL to decide which byte in the word to return for reading.
230;
231.readTimeout:
232        push    ax              ; not only does this push preserve AX (which we need), but it also
233                                ; means the stack has the same number of bytes on it as when we are
234                                ; sending a packet, important for error cleanup and exit
235        mov     ah,1
236        call    SerialCommand_WaitAndPoll_Read
237        pop     ax
238        test    dl,1
239        jz      .readByte1Ready
240        jmp     .readByte2Ready
241
242;----------------------------------------------------------------------------
243;
244; Read Block (without interrupts, used when there is a FIFO, high speed)
245;
246; NOTE: This loop is very time sensitive.  Literally, another instruction
247; cannot be inserted into this loop without us falling behind at high
248; speed (460.8K baud) on a 4.77Mhz 8088, making it hard to receive
249; a full 512 byte block.
250;
251.readLoop:
252        stosw                   ; store word in caller's data buffer
253
254        add     bp, ax          ; update Fletcher's checksum
255        adc     bp, 0
256        add     si, bp
257        adc     si, 0
258
259.readEntry:
260        mov     dl,bh
261        in      al,dx
262        shr     al,1            ; data ready (byte 1)?
263        mov     dl,bl           ; get ready to read data
264        jnc     .readTimeout    ; nope not ready, update timeouts
265
266;
267; Entry point after initial timeout.  We enter here so that the checksum word
268; is not stored (and is left in AX after the loop is complete).
269;
270.readByte1Ready:
271        in      al, dx          ; read data byte 1
272
273        mov     ah, al          ; store byte in ah for now
274
275;
276; note the placement of this reset of dl to bh, and that it is
277; before the return, which is assymetric with where this is done
278; above for byte 1.  The value of dl is used by the timeout routine
279; to know which byte to return to (.read_byte1_ready or
280; .read_byte2_ready)
281;
282        mov     dl,bh
283
284        in      al,dx
285        shr     al,1            ; data ready (byte 2)?
286        jnc     .readTimeout
287.readByte2Ready:
288        mov     dl,bl
289        in      al, dx          ; read data byte 2
290
291        xchg    al, ah          ; ah was holding byte 1, reverse byte order
292
293        loop    .readLoop
294
295        sti                     ; interrupts back on ASAP, between packets
296
297;
298; Compare checksums
299;
300        xchg    ax,bp
301        xor     ah,al
302        mov     cx,si
303        xor     cl,ch
304        mov     al,cl
305        cmp     ax,bp
306        jnz     SerialCommand_OutputWithParameters_Error
307
308        pop     ax              ; sector count and command byte
309        dec     al              ; decrement sector count
310        push    ax              ; save
311        jz      SerialCommand_OutputWithParameters_ReturnCodeInALCF    ; CF=0 from "cmp ax,bp" returning Zero above
312
313        cli                     ; interrupts back off for ACK byte to host
314                                ; (host could start sending data immediately)
315        out     dx,al           ; ACK with next sector number
316
317%if 0
318;;; This code is no longer needed as we do not support more than 128 sectors, and for 128 the pointer
319;;; must be segment aligned.  If we ever do want to support more sectors, the code can help...
320
321;
322; Normalize buffer pointer for next go round, if needed.
323;
324; We need to re-normalize the pointer in ES:DI after processing every 7f sectors.  That number could
325; have been 80 if we knew the offset was on a segment boundary, but this may not be the case.
326;
327; We re-normalize based on the sector count (flags from "dec al" above)...
328;    a) we normalize before the first sector goes out, immediately after sending the command packet (above)
329;    b) on transitions from FF to FE, very rare case for writing 255 or 256 sectors
330;    c) on transitions from 80 to 7F, a large read/write
331;    d) on transitions from 00 to FF, very, very rare case of writing 256 sectors
332;       We don't need to renormalize in this case, but it isn't worth the memory/effort to not do
333;       the extra work, and it does no harm.
334;
335; I really don't care much about (d) because I have not seen cases where any OS makes a request
336; for more than 127 sectors.  Back in the day, it appears that some BIOS could not support more than 127
337; sectors, so that may be the practical limit for OS and application developers.  The Extended BIOS
338; function also appear to be capped at 127 sectors.  So although this can support the full 256 sectors
339; if needed, we are optimized for that 1-127 range.
340;
341; Assume we start with 0000:000f, with 256 sectors to write...
342;    After first packet, 0000:020f
343;    First decrement of AL, transition from 00 to FF: renormalize to 0020:000f (unnecessary)
344;    After second packet, 0020:020f
345;    Second derement of AL, transition from FF to FE: renormalize to 0040:000f
346;    After 7f more packets, 0040:fe0f
347;    Decrement of AL, transition from 80 to 7F: renormalize to 1020:000f
348;    After 7f more packets, 1020:fe0f or 2000:000f if normalized
349;    Decrement of AL, from 1 to 0: exit
350;
351        jge     short .nextSector       ; OF=SF, branch for 1-7e, most common case
352                                        ; (0 kicked out above for return success)
353
354        add     al,2                    ; 7f-ff moves to 81-01
355                                        ; (0-7e kicked out before we get here)
356                                        ; 7f moves to 81 and OF=1, so OF=SF
357                                        ; fe moves to 0 and OF=0, SF=0, so OF=SF
358                                        ; ff moves to 1 and OF=0, SF=0, so OF=SF
359                                        ; 80-fd moves to 82-ff and OF=0, so OF<>SF
360
361        jl      short .nextSector       ; OF<>SF, branch for all cases but 7f, fe, and ff
362
363;       jpo     short .nextSector       ; if we really wanted to avoid normalizing for ff, this
364                                        ; is one way to do it, but it adds more memory and more
365                                        ; cycles for the 7f and fe cases.  IMHO, given that I've
366                                        ; never seen a request for more than 7f, this seems unnecessary.
367
368        jmp     short .nextSectorNormalize  ; our two renormalization cases (plus one for ff)
369
370%else
371
372        jmp     short .nextSector
373
374%endif
375
376;---------------------------------------------------------------------------
377;
378; Cleanup, error reporting, and exit
379;
380
381;
382; Used in situations where a call is underway, such as with SerialCommand_WaitAndPoll
383;
384ALIGN JUMP_ALIGN
385SerialCommand_OutputWithParameters_ErrorAndPop4Words:
386        add     sp,8
387;;; fall-through
388
389ALIGN JUMP_ALIGN
390SerialCommand_OutputWithParameters_Error:
391;----------------------------------------------------------------------
392;
393; Clear read buffer
394;
395; In case there are extra characters or an error in the FIFO, clear it out.
396; In theory the initialization of the UART registers above should have
397; taken care of this, but I have seen cases where this is not true.
398;
399        xor     cx,cx                   ; timeout this clearing routine, in case the UART isn't there
400.clearBuffer:
401        mov     dl,bh
402        in      al,dx
403        mov     dl,bl
404        test    al,08fh
405        jz      .clearBufferComplete
406        test    al,1
407        in      al,dx
408        loopnz  .clearBuffer            ; note ZF from test above
409
410.clearBufferComplete:
411        stc
412        mov     al,1
413
414ALIGN JUMP_ALIGN
415SerialCommand_OutputWithParameters_ReturnCodeInALCF:
416%if 0
417        sti                     ;  all paths here will already have interrupts turned back on
418%endif
419        mov     ah,al
420
421        pop     bp              ;  recover ax (command and count) from stack, throw away
422
423        pop     bp
424        pop     di
425        pop     si
426
427        ret
428
429;--------------------------------------------------------------------
430; SerialCommand_WriteProtocol
431;
432; NOTE: As with its read counterpart, this loop is very time sensitive.
433; Although it will still function, adding additional instructions will
434; impact the write throughput, especially on slower machines.
435;
436;   Parameters:
437;       ES:SI:  Ptr to buffer
438;       CX:     Words to write, plus 1
439;       BP/DI:  Initialized for Checksum (-1 in each)
440;       DH:     I/O Port high byte
441;       BX:     LineStatus Register address (BH) and Receive/Transmit Register address (BL)
442;   Returns:
443;       BP/DI:  Checksum for written bytes, compared against ACK from server in .readLoop
444;       CX:     Zero
445;       DL:     Receive/Transmit Register address
446;   Corrupts registers:
447;       AX
448;--------------------------------------------------------------------
449ALIGN JUMP_ALIGN
450SerialCommand_WriteProtocol:
451.writeLoop:
452        es lodsw                ; fetch next word
453
454        out     dx,al           ; output first byte
455
456        add     bp,ax           ; update checksum
457        adc     bp,0
458        add     di,bp
459        adc     di,0
460
461        mov     dl,bh           ; transmit buffer empty?
462        in      al,dx
463        test    al,20h
464        jz      .writeTimeout2  ; nope, use our polling routine
465
466.writeByte2Ready:
467        mov     dl,bl
468        mov     al,ah           ; output second byte
469        out     dx,al
470
471.entry:
472        mov     dl,bh           ; transmit buffer empty?
473        in      al,dx
474        test    al,20h
475        mov     dl,bl
476        jz      .writeTimeout1  ; nope, use our polling routine
477
478.writeByte1Ready:
479        loop    .writeLoop
480
481        mov     ax,di           ; fold Fletcher's checksum and output
482        xor     al,ah
483        out     dx,al           ; byte 1
484
485        call    SerialCommand_WaitAndPoll_Write
486
487        mov     ax,bp
488        xor     al,ah
489        out     dx,al           ; byte 2
490
491        ret
492
493.writeTimeout2:
494        mov     dl,ah           ; need to preserve AH, but don't need DL (will be reset upon return)
495        call    SerialCommand_WaitAndPoll_Write
496        mov     ah,dl
497        jmp     .writeByte2Ready
498
499.writeTimeout1:
500%ifndef USE_186
501        mov     ax,.writeByte1Ready
502        push    ax              ; return address for ret at end of SC_writeTimeout2
503%else
504        push    .writeByte1Ready
505%endif
506;;; fall-through
507
508;--------------------------------------------------------------------
509; SerialCommand_WaitAndPoll
510;
511;   Parameters:
512;       AH:     UART_LineStatus bit to test (20h for write, or 1h for read)
513;               One entry point fills in AH with 20h for write
514;       DX:     Port address (OK if already incremented to UART_lineStatus)
515;       BX:
516;       Stack:  2 words on the stack below the command/count word
517;   Returns:
518;       Returns when desired UART_LineStatus bit is cleared
519;       Jumps directly to error exit if timeout elapses (and cleans up stack)
520;   Corrupts registers:
521;       AX
522;--------------------------------------------------------------------
523
524SerialCommand_WaitAndPoll_SoftDelayTicks   EQU   20
525
526ALIGN JUMP_ALIGN
527SerialCommand_WaitAndPoll_Write:
528        mov     ah,20h
529;;; fall-through
530
531ALIGN JUMP_ALIGN
532SerialCommand_WaitAndPoll_Read:
533        push    cx
534        push    dx
535
536;
537; We first poll in a tight loop, interrupts off, for the next character to come in/be sent
538;
539        xor     cx,cx
540.readTimeoutLoop:
541        mov     dl,bh
542        in      al,dx
543        test    al,ah
544        jnz     .readTimeoutComplete
545        loop    .readTimeoutLoop
546
547;
548; If that loop completes, then we assume there is a long delay involved, turn interrupts back on
549; and wait for a given number of timer ticks to pass.
550;
551        sti
552        mov     cl,SerialCommand_WaitAndPoll_SoftDelayTicks
553        call    Timer_InitializeTimeoutWithTicksInCL
554.WaitAndPoll:
555        call    Timer_SetCFifTimeout
556        jc      SerialCommand_OutputWithParameters_ErrorAndPop4Words
557        in      al,dx
558        test    al,ah
559        jz      .WaitAndPoll
560        cli
561
562.readTimeoutComplete:
563        pop     dx
564        pop     cx
565        ret
566
567;--------------------------------------------------------------------
568; SerialCommand_IdentifyDeviceToBufferInESSIwithDriveSelectByteInBH
569;   Parameters:
570;       BH:     Drive Select byte for Drive and Head Select Register
571;       DS:     Segment to RAMVARS
572;       ES:SI:  Ptr to buffer to receive 512-byte IDE Information
573;       CS:BP:  Ptr to IDEVARS
574;   Returns:
575;       AH:     INT 13h Error Code
576;               NOTE: Not set (or checked) during drive detection
577;       CF:     Cleared if success, Set if error
578;   Corrupts registers:
579;       AL, BL, CX, DX, SI, DI, ES
580;--------------------------------------------------------------------
581ALIGN JUMP_ALIGN
582SerialCommand_IdentifyDeviceToBufferInESSIwithDriveSelectByteInBH:
583;
584; To improve boot time, we do our best to avoid looking for slave serial drives when we already know the results
585; from the looking for a master.  This is particularly true when doing a COM port scan, as we will end up running
586; through all the COM ports and baud rates a second time.
587;
588; But drive detection isn't the only case - we also need to get the right drive when called on int13h/25h.
589;
590; The decision tree:
591;
592;    Master:
593;          wSerialPortAndBaud Non-Zero:           -> Continue with wSerialPortAndBaud (1)
594;          wSerialPortAndBaud Zero:
595;              previous serial drive not found:   -> Scan (2)
596;              previous serial drive found:       -> Continue with previous serial drive info (3)
597;
598;    Slave:
599;          wSerialPortAndBaud Non-Zero:
600;              previous serial drive not found:   -> Error - Not Found (4)
601;              previosu serial drive found:       -> Continue with wSerialPackedAndBaud (5)
602;          wSerialPortAndBaud Zero:
603;              previous serial drive not found:   -> Error - Not Found (4)
604;              previous serial drive found:       -> Continue with previous serial drive info (6)
605;
606; (1) This was a port/baud that was explicitly set with the configurator.  In the drive detection case, as this
607;     is the Master, we are checking out a new controller, and so don't care if we already have a serial drive.
608;     And as with the int13h/25h case, we just go off and get the needed information using the user's setting.
609; (2) We are using the special .ideVarsSerialAuto structure.  During drive detection, we would only be here
610;     if we hand't already seen a serial drive (since we only scan if no explicit drives are set),
611;     so we go off to scan.
612; (3) We are using the special .ideVarsSerialAuto structure.  We won't get here during drive detection, but
613;     we might get here on an int13h/25h call.  If we have scanned COM drives, they are the ONLY serial drives
614;     in use, and so we use the values from the previously seen serial drive DPT.
615; (4) No master has been found yet, therefore no slave should be found.  Avoiding the slave reduces boot time,
616;     especially in the full COM port scan case.  Note that this is different from the hardware IDE, where we
617;     will scan for a slave even if a master is not present.  Note that if ANY master had been previously found,
618;     we will do the slave scan, which isn't harmful, it just wastes time.  But the most common case (by a wide
619;     margin) will be just one serial controller.
620; (5) A COM port scan for a master had been previously completed, and a drive was found.  In a multiple serial
621;     controller scenario being called with int13h/25h, we need to use the value in bSerialPackedPortAndBaud
622;     to make sure we get the proper drive.
623; (6) A COM port scan for a master had been previously completed, and a drive was found.  We would only get here
624;     if no serial drive was explicitly set by the user in the configurator or that drive had not been found.
625;     Instead of performing the full COM port scan for the slave, use the port/baud value stored during the
626;     master scan.
627;
628        mov     cx,1            ; 1 sector to move, 0 for non-scan
629        mov     dx,[cs:bp+IDEVARS.wSerialPortAndBaud]
630        xor     ax,ax
631        push    si
632        call    FindDPT_ToDSDIforSerialDevice
633        pop     si
634        jnc     .notfounddpt
635        mov     ax,[ds:di+DPT_SERIAL.wSerialPortAndBaud]
636.notfounddpt:   
637
638        test    bh, FLG_DRVNHEAD_DRV
639        jz      .master
640
641        test    ax,ax           ; Take care of the case that is different between master and slave.
642        jz      .error          ; Because we do this here, the jz after the "or" below will not be taken
643
644; fall-through
645.master:
646        test    dx,dx
647        jnz     .identifyDeviceInDX
648
649        or      dx,ax           ; Since DX is zero, this effectively moves the previously found serial drive 
650                                ; information to dx, as well as test for zero
651        jz      .scanSerial
652
653; fall-through
654.identifyDeviceInDX:
655
656        push    bp              ; setup fake IDEREGS_AND_INTPACK
657
658        push    dx
659
660        push    cx
661
662        mov     bl,0a0h         ; protocol command to ah and onto stack with bh
663        mov     ah,bl
664
665        push    bx
666
667        mov     bp,sp
668        call    SerialCommand_OutputWithParameters_DeviceInDX
669
670        pop     bx
671
672        pop     cx
673        pop     dx
674       
675        pop     bp
676;
677; place port and baud word in to the return sector, in a vendor specific area, 
678; which is read by FinalizeDPT and DetectDrives
679;
680        mov     [es:si+ATA6.wVendor],dx
681
682.notFound:
683        ret
684
685;----------------------------------------------------------------------
686;
687; SerialCommand_AutoSerial
688;
689; When the SerialAuto IDEVARS entry is used, scans the COM ports on the machine for a possible serial connection.
690;
691
692.scanPortAddresses: db  DEVICE_SERIAL_COM7 >> 2
693                    db  DEVICE_SERIAL_COM6 >> 2
694                    db  DEVICE_SERIAL_COM5 >> 2
695                    db  DEVICE_SERIAL_COM4 >> 2
696                    db  DEVICE_SERIAL_COM3 >> 2
697                    db  DEVICE_SERIAL_COM2 >> 2
698                    db  DEVICE_SERIAL_COM1 >> 2
699                    db  0
700
701ALIGN JUMP_ALIGN
702.scanSerial:
703        mov     di,.scanPortAddresses-1
704        mov     ch,1            ;  tell server that we are scanning
705
706.nextPort:
707        inc     di              ; load next port address
708        xor     dh, dh
709        mov     dl,[cs:di]
710        eSHL_IM dx, 2           ; shift from one byte to two
711        jz      .error
712
713;
714; Test for COM port presence, write to and read from registers
715;
716        push    dx
717        add     dl,SerialCommand_UART_lineControl
718        mov     al, 09ah
719        out     dx, al
720        in      al, dx
721        pop     dx
722        cmp     al, 09ah
723        jnz     .nextPort
724
725        mov     al, 0ch
726        out     dx, al
727        in      al, dx
728        cmp     al, 0ch
729        jnz     .nextPort
730
731;
732; Begin baud rate scan on this port...
733;
734; On a scan, we support 6 baud rates, starting here and going higher by a factor of two each step, with a 
735; small jump between 9600 and 38800.  These 6 were selected since we wanted to support 9600 baud and 115200,
736; *on the server side* if the client side had a 4x clock multiplier, a 2x clock multiplier, or no clock multiplier. 
737;
738; Starting with 30h, that means 30h (2400 baud), 18h (4800 baud), 0ch (9600 baud), and
739;                               04h (28800 baud), 02h (57600 baud), 01h (115200 baud)
740;
741; Note: hardware baud multipliers (2x, 4x) will impact the final baud rate and are not known at this level
742;
743        mov     dh,030h * 2     ; multiply by 2 since we are about to divide by 2
744        mov     dl,[cs:di]      ; restore single byte port address for scan
745
746.nextBaud:
747        shr     dh,1
748        jz      .nextPort
749        cmp     dh,6            ; skip from 6 to 4, to move from the top of the 9600 baud range 
750        jnz     .testBaud       ; to the bottom of the 115200 baud range
751        mov     dh,4
752
753.testBaud:
754        call    .identifyDeviceInDX
755        jc      .nextBaud
756
757        ret
758
759.error:
760        stc
761%if 0
762        mov     ah,1        ; setting the error code is unnecessary as this path can only be taken during
763                            ; drive detection, and drive detection works off CF and does not check AH
764%endif
765        ret
766
767
Note: See TracBrowser for help on using the repository browser.