• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /////////////////////////////////////////////////////////////////////////
2 // $Id$
3 /////////////////////////////////////////////////////////////////////////
4 //
5 //  Copyright (C) 2002  MandrakeSoft S.A.
6 //
7 //    MandrakeSoft S.A.
8 //    43, rue d'Aboukir
9 //    75002 Paris - France
10 //    http://www.linux-mandrake.com/
11 //    http://www.mandrakesoft.com/
12 //
13 //  This library is free software; you can redistribute it and/or
14 //  modify it under the terms of the GNU Lesser General Public
15 //  License as published by the Free Software Foundation; either
16 //  version 2 of the License, or (at your option) any later version.
17 //
18 //  This library is distributed in the hope that it will be useful,
19 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
20 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21 //  Lesser General Public License for more details.
22 //
23 //  You should have received a copy of the GNU Lesser General Public
24 //  License along with this library; if not, write to the Free Software
25 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
26 
27 // ROM BIOS for use with Bochs/Plex86/QEMU emulation environment
28 
29 
30 // ROM BIOS compatability entry points:
31 // ===================================
32 // $e05b ; POST Entry Point
33 // $e2c3 ; NMI Handler Entry Point
34 // $e3fe ; INT 13h Fixed Disk Services Entry Point
35 // $e401 ; Fixed Disk Parameter Table
36 // $e6f2 ; INT 19h Boot Load Service Entry Point
37 // $e6f5 ; Configuration Data Table
38 // $e729 ; Baud Rate Generator Table
39 // $e739 ; INT 14h Serial Communications Service Entry Point
40 // $e82e ; INT 16h Keyboard Service Entry Point
41 // $e987 ; INT 09h Keyboard Service Entry Point
42 // $ec59 ; INT 13h Diskette Service Entry Point
43 // $ef57 ; INT 0Eh Diskette Hardware ISR Entry Point
44 // $efc7 ; Diskette Controller Parameter Table
45 // $efd2 ; INT 17h Printer Service Entry Point
46 // $f045 ; INT 10 Functions 0-Fh Entry Point
47 // $f065 ; INT 10h Video Support Service Entry Point
48 // $f0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
49 // $f841 ; INT 12h Memory Size Service Entry Point
50 // $f84d ; INT 11h Equipment List Service Entry Point
51 // $f859 ; INT 15h System Services Entry Point
52 // $fa6e ; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
53 // $fe6e ; INT 1Ah Time-of-day Service Entry Point
54 // $fea5 ; INT 08h System Timer ISR Entry Point
55 // $fef3 ; Initial Interrupt Vector Offsets Loaded by POST
56 // $ff53 ; IRET Instruction for Dummy Interrupt Handler
57 // $ff54 ; INT 05h Print Screen Service Entry Point
58 // $fff0 ; Power-up Entry Point
59 // $fff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
60 // $fffe ; System Model ID
61 
62 // NOTES for ATA/ATAPI driver (cbbochs@free.fr)
63 //   Features
64 //     - supports up to 4 ATA interfaces
65 //     - device/geometry detection
66 //     - 16bits/32bits device access
67 //     - pchs/lba access
68 //     - datain/dataout/packet command support
69 //
70 // NOTES for El-Torito Boot (cbbochs@free.fr)
71 //   - CD-ROM booting is only available if ATA/ATAPI Driver is available
72 //   - Current code is only able to boot mono-session cds
73 //   - Current code can not boot and emulate a hard-disk
74 //     the bios will panic otherwise
75 //   - Current code also use memory in EBDA segement.
76 //   - I used cmos byte 0x3D to store extended information on boot-device
77 //   - Code has to be modified modified to handle multiple cdrom drives
78 //   - Here are the cdrom boot failure codes:
79 //       1 : no atapi device found
80 //       2 : no atapi cdrom found
81 //       3 : can not read cd - BRVD
82 //       4 : cd is not eltorito (BRVD)
83 //       5 : cd is not eltorito (ISO TAG)
84 //       6 : cd is not eltorito (ELTORITO TAG)
85 //       7 : can not read cd - boot catalog
86 //       8 : boot catalog : bad header
87 //       9 : boot catalog : bad platform
88 //      10 : boot catalog : bad signature
89 //      11 : boot catalog : bootable flag not set
90 //      12 : can not read cd - boot image
91 //
92 //   ATA driver
93 //   - EBDA segment.
94 //     I used memory starting at 0x121 in the segment
95 //   - the translation policy is defined in cmos regs 0x39 & 0x3a
96 //
97 // TODO :
98 //
99 //   int74
100 //     - needs to be reworked.  Uses direct [bp] offsets. (?)
101 //
102 //   int13:
103 //     - f04 (verify sectors) isn't complete  (?)
104 //     - f02/03/04 should set current cyl,etc in BDA  (?)
105 //     - rewrite int13_relocated & clean up int13 entry code
106 //
107 //   NOTES:
108 //   - NMI access (bit7 of addr written to 70h)
109 //
110 //   ATA driver
111 //   - should handle the "don't detect" bit (cmos regs 0x3b & 0x3c)
112 //   - could send the multiple-sector read/write commands
113 //
114 //   El-Torito
115 //   - Emulate a Hard-disk (currently only diskette can be emulated) see "FIXME ElTorito Harddisk"
116 //   - Implement remaining int13_cdemu functions (as defined by El-Torito specs)
117 //   - cdrom drive is hardcoded to ide 0 device 1 in several places. see "FIXME ElTorito Hardcoded"
118 //   - int13 Fix DL when emulating a cd. In that case DL is decremented before calling real int13.
119 //     This is ok. But DL should be reincremented afterwards.
120 //   - Fix all "FIXME ElTorito Various"
121 //   - should be able to boot any cdrom instead of the first one
122 //
123 //   BCC Bug: find a generic way to handle the bug of #asm after an "if"  (fixed in 0.16.7)
124 
125 #include "rombios.h"
126 
127 #define DEBUG_ATA          0
128 #define DEBUG_INT13_HD     0
129 #define DEBUG_INT13_CD     0
130 #define DEBUG_INT13_ET     0
131 #define DEBUG_INT13_FL     0
132 #define DEBUG_INT15        0
133 #define DEBUG_INT16        0
134 #define DEBUG_INT1A        0
135 #define DEBUG_INT74        0
136 #define DEBUG_APM          0
137 
138 #define BX_CPU           3
139 #define BX_USE_PS2_MOUSE 1
140 #define BX_CALL_INT15_4F 1
141 #define BX_USE_EBDA      1
142 #define BX_SUPPORT_FLOPPY 1
143 #define BX_FLOPPY_ON_CNT 37   /* 2 seconds */
144 #define BX_PCIBIOS       1
145 #define BX_APM           1
146 
147 #define BX_USE_ATADRV    1
148 #define BX_ELTORITO_BOOT 1
149 
150 #define BX_MAX_ATA_INTERFACES   4
151 #define BX_MAX_ATA_DEVICES      (BX_MAX_ATA_INTERFACES*2)
152 
153 #define BX_VIRTUAL_PORTS 1 /* normal output to Bochs ports */
154 #define BX_DEBUG_SERIAL  0 /* output to COM1 */
155 
156    /* model byte 0xFC = AT */
157 #define SYS_MODEL_ID     0xFC
158 #define SYS_SUBMODEL_ID  0x00
159 #define BIOS_REVISION    1
160 #define BIOS_CONFIG_TABLE 0xe6f5
161 
162 #ifndef BIOS_BUILD_DATE
163 #  define BIOS_BUILD_DATE "06/23/99"
164 #endif
165 
166   // 1K of base memory used for Extended Bios Data Area (EBDA)
167   // EBDA is used for PS/2 mouse support, and IDE BIOS, etc.
168 #define EBDA_SEG           0x9FC0
169 #define EBDA_SIZE          1              // In KiB
170 #define BASE_MEM_IN_K   (640 - EBDA_SIZE)
171 
172 /* 256 bytes at 0x9ff00 -- 0x9ffff is used for the IPL boot table. */
173 #define IPL_SEG              0x9ff0
174 #define IPL_TABLE_OFFSET     0x0000
175 #define IPL_TABLE_ENTRIES    8
176 #define IPL_COUNT_OFFSET     0x0080  /* u16: number of valid table entries */
177 #define IPL_SEQUENCE_OFFSET  0x0082  /* u16: next boot device */
178 #define IPL_BOOTFIRST_OFFSET 0x0084  /* u16: user selected device */
179 #define IPL_SIZE             0xff
180 #define IPL_TYPE_FLOPPY      0x01
181 #define IPL_TYPE_HARDDISK    0x02
182 #define IPL_TYPE_CDROM       0x03
183 #define IPL_TYPE_BEV         0x80
184 
185   // Sanity Checks
186 #if BX_USE_ATADRV && BX_CPU<3
187 #    error The ATA/ATAPI Driver can only to be used with a 386+ cpu
188 #endif
189 #if BX_USE_ATADRV && !BX_USE_EBDA
190 #    error ATA/ATAPI Driver can only be used if EBDA is available
191 #endif
192 #if BX_ELTORITO_BOOT && !BX_USE_ATADRV
193 #    error El-Torito Boot can only be use if ATA/ATAPI Driver is available
194 #endif
195 #if BX_PCIBIOS && BX_CPU<3
196 #    error PCI BIOS can only be used with 386+ cpu
197 #endif
198 #if BX_APM && BX_CPU<3
199 #    error APM BIOS can only be used with 386+ cpu
200 #endif
201 
202 // define this if you want to make PCIBIOS working on a specific bridge only
203 // undef enables PCIBIOS when at least one PCI device is found
204 // i440FX is emulated by Bochs and QEMU
205 #define PCI_FIXED_HOST_BRIDGE 0x12378086 ;; i440FX PCI bridge
206 
207 // #20  is dec 20
208 // #$20 is hex 20 = 32
209 // #0x20 is hex 20 = 32
210 // LDA  #$20
211 // JSR  $E820
212 // LDD  .i,S
213 // JSR  $C682
214 // mov al, #$20
215 
216 // all hex literals should be prefixed with '0x'
217 //   grep "#[0-9a-fA-F][0-9a-fA-F]" rombios.c
218 // no mov SEG-REG, #value, must mov register into seg-reg
219 //   grep -i "mov[ ]*.s" rombios.c
220 
221 // This is for compiling with gcc2 and gcc3
222 #define ASM_START #asm
223 #define ASM_END #endasm
224 
225 ASM_START
226 .rom
227 
228 .org 0x0000
229 
230 #if BX_CPU >= 3
231 use16 386
232 #else
233 use16 286
234 #endif
235 
236 MACRO HALT
237   ;; the HALT macro is called with the line number of the HALT call.
238   ;; The line number is then sent to the PANIC_PORT, causing Bochs/Plex
239   ;; to print a BX_PANIC message.  This will normally halt the simulation
240   ;; with a message such as "BIOS panic at rombios.c, line 4091".
241   ;; However, users can choose to make panics non-fatal and continue.
242 #if BX_VIRTUAL_PORTS
243   mov dx,#PANIC_PORT
244   mov ax,#?1
245   out dx,ax
246 #else
247   mov dx,#0x80
248   mov ax,#?1
249   out dx,al
250 #endif
251 MEND
252 
253 MACRO JMP_AP
254   db 0xea
255   dw ?2
256   dw ?1
257 MEND
258 
259 MACRO SET_INT_VECTOR
260   mov ax, ?3
261   mov ?1*4, ax
262   mov ax, ?2
263   mov ?1*4+2, ax
264 MEND
265 
266 ASM_END
267 
268 typedef unsigned char  Bit8u;
269 typedef unsigned short Bit16u;
270 typedef unsigned short bx_bool;
271 typedef unsigned long  Bit32u;
272 
273 
274   void memsetb(seg,offset,value,count);
275   void memcpyb(dseg,doffset,sseg,soffset,count);
276   void memcpyd(dseg,doffset,sseg,soffset,count);
277 
278   // memset of count bytes
279     void
memsetb(seg,offset,value,count)280   memsetb(seg,offset,value,count)
281     Bit16u seg;
282     Bit16u offset;
283     Bit16u value;
284     Bit16u count;
285   {
286   ASM_START
287     push bp
288     mov  bp, sp
289 
290       push ax
291       push cx
292       push es
293       push di
294 
295       mov  cx, 10[bp] ; count
296       test cx, cx
297       je   memsetb_end
298       mov  ax, 4[bp] ; segment
299       mov  es, ax
300       mov  ax, 6[bp] ; offset
301       mov  di, ax
302       mov  al, 8[bp] ; value
303       cld
304       rep
305        stosb
306 
307   memsetb_end:
308       pop di
309       pop es
310       pop cx
311       pop ax
312 
313     pop bp
314   ASM_END
315   }
316 
317   // memcpy of count bytes
318     void
memcpyb(dseg,doffset,sseg,soffset,count)319   memcpyb(dseg,doffset,sseg,soffset,count)
320     Bit16u dseg;
321     Bit16u doffset;
322     Bit16u sseg;
323     Bit16u soffset;
324     Bit16u count;
325   {
326   ASM_START
327     push bp
328     mov  bp, sp
329 
330       push ax
331       push cx
332       push es
333       push di
334       push ds
335       push si
336 
337       mov  cx, 12[bp] ; count
338       test cx, cx
339       je   memcpyb_end
340       mov  ax, 4[bp] ; dsegment
341       mov  es, ax
342       mov  ax, 6[bp] ; doffset
343       mov  di, ax
344       mov  ax, 8[bp] ; ssegment
345       mov  ds, ax
346       mov  ax, 10[bp] ; soffset
347       mov  si, ax
348       cld
349       rep
350        movsb
351 
352   memcpyb_end:
353       pop si
354       pop ds
355       pop di
356       pop es
357       pop cx
358       pop ax
359 
360     pop bp
361   ASM_END
362   }
363 
364   // memcpy of count dword
365     void
memcpyd(dseg,doffset,sseg,soffset,count)366   memcpyd(dseg,doffset,sseg,soffset,count)
367     Bit16u dseg;
368     Bit16u doffset;
369     Bit16u sseg;
370     Bit16u soffset;
371     Bit16u count;
372   {
373   ASM_START
374     push bp
375     mov  bp, sp
376 
377       push ax
378       push cx
379       push es
380       push di
381       push ds
382       push si
383 
384       mov  cx, 12[bp] ; count
385       test cx, cx
386       je   memcpyd_end
387       mov  ax, 4[bp] ; dsegment
388       mov  es, ax
389       mov  ax, 6[bp] ; doffset
390       mov  di, ax
391       mov  ax, 8[bp] ; ssegment
392       mov  ds, ax
393       mov  ax, 10[bp] ; soffset
394       mov  si, ax
395       cld
396       rep
397        movsd
398 
399   memcpyd_end:
400       pop si
401       pop ds
402       pop di
403       pop es
404       pop cx
405       pop ax
406 
407     pop bp
408   ASM_END
409   }
410 
411   // read_dword and write_dword functions
412   static Bit32u         read_dword();
413   static void           write_dword();
414 
415     Bit32u
read_dword(seg,offset)416   read_dword(seg, offset)
417     Bit16u seg;
418     Bit16u offset;
419   {
420   ASM_START
421     push bp
422     mov  bp, sp
423 
424       push bx
425       push ds
426       mov  ax, 4[bp] ; segment
427       mov  ds, ax
428       mov  bx, 6[bp] ; offset
429       mov  ax, [bx]
430       add  bx, #2
431       mov  dx, [bx]
432       ;; ax = return value (word)
433       ;; dx = return value (word)
434       pop  ds
435       pop  bx
436 
437     pop  bp
438   ASM_END
439   }
440 
441     void
442   write_dword(seg, offset, data)
443     Bit16u seg;
444     Bit16u offset;
445     Bit32u data;
446   {
447   ASM_START
448     push bp
449     mov  bp, sp
450 
451       push ax
452       push bx
453       push ds
454       mov  ax, 4[bp] ; segment
455       mov  ds, ax
456       mov  bx, 6[bp] ; offset
457       mov  ax, 8[bp] ; data word
458       mov  [bx], ax  ; write data word
459       add  bx, #2
460       mov  ax, 10[bp] ; data word
461       mov  [bx], ax  ; write data word
462       pop  ds
463       pop  bx
464       pop  ax
465 
466     pop  bp
467   ASM_END
468   }
469 
470   // Bit32u (unsigned long) and long helper functions
471   ASM_START
472 
473   ;; and function
474   landl:
475   landul:
476     SEG SS
477       and ax,[di]
478     SEG SS
479       and bx,2[di]
480     ret
481 
482   ;; add function
483   laddl:
484   laddul:
485     SEG SS
486       add ax,[di]
487     SEG SS
488       adc bx,2[di]
489     ret
490 
491   ;; cmp function
492   lcmpl:
493   lcmpul:
494     and eax, #0x0000FFFF
495     shl ebx, #16
496     or  eax, ebx
497     shr ebx, #16
498     SEG SS
499       cmp eax, dword ptr [di]
500     ret
501 
502   ;; sub function
503   lsubl:
504   lsubul:
505     SEG SS
506     sub ax,[di]
507     SEG SS
508     sbb bx,2[di]
509     ret
510 
511   ;; mul function
512   lmull:
513   lmulul:
514     and eax, #0x0000FFFF
515     shl ebx, #16
516     or  eax, ebx
517     SEG SS
518     mul eax, dword ptr [di]
519     mov ebx, eax
520     shr ebx, #16
521     ret
522 
523   ;; dec function
524   ldecl:
525   ldecul:
526     SEG SS
527     dec dword ptr [bx]
528     ret
529 
530   ;; or function
531   lorl:
532   lorul:
533     SEG SS
534     or  ax,[di]
535     SEG SS
536     or  bx,2[di]
537     ret
538 
539   ;; inc function
540   lincl:
541   lincul:
542     SEG SS
543     inc dword ptr [bx]
544     ret
545 
546   ;; tst function
547   ltstl:
548   ltstul:
549     and eax, #0x0000FFFF
550     shl ebx, #16
551     or  eax, ebx
552     shr ebx, #16
553     test eax, eax
554     ret
555 
556   ;; sr function
557   lsrul:
558     mov  cx,di
559     jcxz lsr_exit
560     and  eax, #0x0000FFFF
561     shl  ebx, #16
562     or   eax, ebx
563   lsr_loop:
564     shr  eax, #1
565     loop lsr_loop
566     mov  ebx, eax
567     shr  ebx, #16
568   lsr_exit:
569     ret
570 
571   ;; sl function
572   lsll:
573   lslul:
574     mov  cx,di
575     jcxz lsl_exit
576     and  eax, #0x0000FFFF
577     shl  ebx, #16
578     or   eax, ebx
579   lsl_loop:
580     shl  eax, #1
581     loop lsl_loop
582     mov  ebx, eax
583     shr  ebx, #16
584   lsl_exit:
585     ret
586 
587   idiv_:
588     cwd
589     idiv bx
590     ret
591 
592   idiv_u:
593     xor dx,dx
594     div bx
595     ret
596 
597   ldivul:
598     and  eax, #0x0000FFFF
599     shl  ebx, #16
600     or   eax, ebx
601     xor  edx, edx
602     SEG SS
603     mov  bx,  2[di]
604     shl  ebx, #16
605     SEG SS
606     mov  bx,  [di]
607     div  ebx
608     mov  ebx, eax
609     shr  ebx, #16
610     ret
611 
612   ASM_END
613 
614 // for access to RAM area which is used by interrupt vectors
615 // and BIOS Data Area
616 
617 typedef struct {
618   unsigned char filler1[0x400];
619   unsigned char filler2[0x6c];
620   Bit16u ticks_low;
621   Bit16u ticks_high;
622   Bit8u  midnight_flag;
623   } bios_data_t;
624 
625 #define BiosData ((bios_data_t  *) 0)
626 
627 #if BX_USE_ATADRV
628   typedef struct {
629     Bit16u heads;      // # heads
630     Bit16u cylinders;  // # cylinders
631     Bit16u spt;        // # sectors / track
632     } chs_t;
633 
634   // DPTE definition
635   typedef struct {
636     Bit16u iobase1;
637     Bit16u iobase2;
638     Bit8u  prefix;
639     Bit8u  unused;
640     Bit8u  irq;
641     Bit8u  blkcount;
642     Bit8u  dma;
643     Bit8u  pio;
644     Bit16u options;
645     Bit16u reserved;
646     Bit8u  revision;
647     Bit8u  checksum;
648     } dpte_t;
649 
650   typedef struct {
651     Bit8u  iface;        // ISA or PCI
652     Bit16u iobase1;      // IO Base 1
653     Bit16u iobase2;      // IO Base 2
654     Bit8u  irq;          // IRQ
655     } ata_channel_t;
656 
657   typedef struct {
658     Bit8u  type;         // Detected type of ata (ata/atapi/none/unknown)
659     Bit8u  device;       // Detected type of attached devices (hd/cd/none)
660     Bit8u  removable;    // Removable device flag
661     Bit8u  lock;         // Locks for removable devices
662     Bit8u  mode;         // transfer mode : PIO 16/32 bits - IRQ - ISADMA - PCIDMA
663     Bit16u blksize;      // block size
664 
665     Bit8u  translation;  // type of translation
666     chs_t  lchs;         // Logical CHS
667     chs_t  pchs;         // Physical CHS
668 
669     Bit32u sectors_low;  // Total sectors count
670     Bit32u sectors_high;
671     } ata_device_t;
672 
673   typedef struct {
674     // ATA channels info
675     ata_channel_t channels[BX_MAX_ATA_INTERFACES];
676 
677     // ATA devices info
678     ata_device_t  devices[BX_MAX_ATA_DEVICES];
679     //
680     // map between (bios hd id - 0x80) and ata channels
681     Bit8u  hdcount, hdidmap[BX_MAX_ATA_DEVICES];
682 
683     // map between (bios cd id - 0xE0) and ata channels
684     Bit8u  cdcount, cdidmap[BX_MAX_ATA_DEVICES];
685 
686     // Buffer for DPTE table
687     dpte_t dpte;
688 
689     // Count of transferred sectors and bytes
690     Bit16u trsfsectors;
691     Bit32u trsfbytes;
692 
693     } ata_t;
694 
695 #if BX_ELTORITO_BOOT
696   // ElTorito Device Emulation data
697   typedef struct {
698     Bit8u  active;
699     Bit8u  media;
700     Bit8u  emulated_drive;
701     Bit8u  controller_index;
702     Bit16u device_spec;
703     Bit32u ilba;
704     Bit16u buffer_segment;
705     Bit16u load_segment;
706     Bit16u sector_count;
707 
708     // Virtual device
709     chs_t  vdevice;
710     } cdemu_t;
711 #endif // BX_ELTORITO_BOOT
712 
713   // for access to EBDA area
714   //     The EBDA structure should conform to
715   //     http://www.frontiernet.net/~fys/rombios.htm document
716   //     I made the ata and cdemu structs begin at 0x121 in the EBDA seg
717   // EBDA must be at most 768 bytes; it lives at EBDA_SEG, and the boot
718   // device tables are at IPL_SEG
719   typedef struct {
720     unsigned char filler1[0x3D];
721 
722     // FDPT - Can be splitted in data members if needed
723     unsigned char fdpt0[0x10];
724     unsigned char fdpt1[0x10];
725 
726     unsigned char filler2[0xC4];
727 
728     // ATA Driver data
729     ata_t   ata;
730 
731 #if BX_ELTORITO_BOOT
732     // El Torito Emulation data
733     cdemu_t cdemu;
734 #endif // BX_ELTORITO_BOOT
735 
736     } ebda_data_t;
737 
738   #define EbdaData ((ebda_data_t *) 0)
739 
740   // for access to the int13ext structure
741   typedef struct {
742     Bit8u  size;
743     Bit8u  reserved;
744     Bit16u count;
745     Bit16u offset;
746     Bit16u segment;
747     Bit32u lba1;
748     Bit32u lba2;
749     } int13ext_t;
750 
751   #define Int13Ext ((int13ext_t *) 0)
752 
753   // Disk Physical Table definition
754   typedef struct {
755     Bit16u  size;
756     Bit16u  infos;
757     Bit32u  cylinders;
758     Bit32u  heads;
759     Bit32u  spt;
760     Bit32u  sector_count1;
761     Bit32u  sector_count2;
762     Bit16u  blksize;
763     Bit16u  dpte_offset;
764     Bit16u  dpte_segment;
765     Bit16u  key;
766     Bit8u   dpi_length;
767     Bit8u   reserved1;
768     Bit16u  reserved2;
769     Bit8u   host_bus[4];
770     Bit8u   iface_type[8];
771     Bit8u   iface_path[8];
772     Bit8u   device_path[8];
773     Bit8u   reserved3;
774     Bit8u   checksum;
775     } dpt_t;
776 
777   #define Int13DPT ((dpt_t *) 0)
778 
779 #endif // BX_USE_ATADRV
780 
781 typedef struct {
782   union {
783     struct {
784       Bit16u di, si, bp, sp;
785       Bit16u bx, dx, cx, ax;
786       } r16;
787     struct {
788       Bit16u filler[4];
789       Bit8u  bl, bh, dl, dh, cl, ch, al, ah;
790       } r8;
791     } u;
792   } pusha_regs_t;
793 
794 typedef struct {
795  union {
796   struct {
797     Bit32u edi, esi, ebp, esp;
798     Bit32u ebx, edx, ecx, eax;
799     } r32;
800   struct {
801     Bit16u di, filler1, si, filler2, bp, filler3, sp, filler4;
802     Bit16u bx, filler5, dx, filler6, cx, filler7, ax, filler8;
803     } r16;
804   struct {
805     Bit32u filler[4];
806     Bit8u  bl, bh;
807     Bit16u filler1;
808     Bit8u  dl, dh;
809     Bit16u filler2;
810     Bit8u  cl, ch;
811     Bit16u filler3;
812     Bit8u  al, ah;
813     Bit16u filler4;
814     } r8;
815   } u;
816 } pushad_regs_t;
817 
818 typedef struct {
819   union {
820     struct {
821       Bit16u flags;
822       } r16;
823     struct {
824       Bit8u  flagsl;
825       Bit8u  flagsh;
826       } r8;
827     } u;
828   } flags_t;
829 
830 #define SetCF(x)   x.u.r8.flagsl |= 0x01
831 #define SetZF(x)   x.u.r8.flagsl |= 0x40
832 #define ClearCF(x) x.u.r8.flagsl &= 0xfe
833 #define ClearZF(x) x.u.r8.flagsl &= 0xbf
834 #define GetCF(x)   (x.u.r8.flagsl & 0x01)
835 
836 typedef struct {
837   Bit16u ip;
838   Bit16u cs;
839   flags_t flags;
840   } iret_addr_t;
841 
842 typedef struct {
843   Bit16u type;
844   Bit16u flags;
845   Bit32u vector;
846   Bit32u description;
847   Bit32u reserved;
848   } ipl_entry_t;
849 
850 
851 
852 static Bit8u          inb();
853 static Bit8u          inb_cmos();
854 static void           outb();
855 static void           outb_cmos();
856 static Bit16u         inw();
857 static void           outw();
858 static void           init_rtc();
859 static bx_bool        rtc_updating();
860 
861 static Bit8u          read_byte();
862 static Bit16u         read_word();
863 static void           write_byte();
864 static void           write_word();
865 static void           bios_printf();
866 
867 static Bit8u          inhibit_mouse_int_and_events();
868 static void           enable_mouse_int_and_events();
869 static Bit8u          send_to_mouse_ctrl();
870 static Bit8u          get_mouse_data();
871 static void           set_kbd_command_byte();
872 
873 static void           int09_function();
874 static void           int13_harddisk();
875 static void           int13_cdrom();
876 static void           int13_cdemu();
877 static void           int13_eltorito();
878 static void           int13_diskette_function();
879 static void           int14_function();
880 static void           int15_function();
881 static void           int16_function();
882 static void           int17_function();
883 static void           int19_function();
884 static void           int1a_function();
885 static void           int70_function();
886 static void           int74_function();
887 static Bit16u         get_CS();
888 static Bit16u         get_SS();
889 static unsigned int   enqueue_key();
890 static unsigned int   dequeue_key();
891 static void           get_hd_geometry();
892 static void           set_diskette_ret_status();
893 static void           set_diskette_current_cyl();
894 static void           determine_floppy_media();
895 static bx_bool        floppy_drive_exists();
896 static bx_bool        floppy_drive_recal();
897 static bx_bool        floppy_media_known();
898 static bx_bool        floppy_media_sense();
899 static bx_bool        set_enable_a20();
900 static void           debugger_on();
901 static void           debugger_off();
902 static void           keyboard_init();
903 static void           keyboard_panic();
904 static void           shutdown_status_panic();
905 static void           nmi_handler_msg();
906 static void           delay_ticks();
907 static void           delay_ticks_and_check_for_keystroke();
908 
909 static void           interactive_bootkey();
910 static void           print_bios_banner();
911 static void           print_boot_device();
912 static void           print_boot_failure();
913 static void           print_cdromboot_failure();
914 
915 # if BX_USE_ATADRV
916 
917 // ATA / ATAPI driver
918 void   ata_init();
919 void   ata_detect();
920 void   ata_reset();
921 
922 Bit16u ata_cmd_non_data();
923 Bit16u ata_cmd_data_in();
924 Bit16u ata_cmd_data_out();
925 Bit16u ata_cmd_packet();
926 
927 Bit16u atapi_get_sense();
928 Bit16u atapi_is_ready();
929 Bit16u atapi_is_cdrom();
930 
931 #endif // BX_USE_ATADRV
932 
933 #if BX_ELTORITO_BOOT
934 
935 void   cdemu_init();
936 Bit8u  cdemu_isactive();
937 Bit8u  cdemu_emulated_drive();
938 
939 Bit16u cdrom_boot();
940 
941 #endif // BX_ELTORITO_BOOT
942 
943 static char bios_cvs_version_string[] = "$Revision$ $Date$";
944 
945 #define BIOS_COPYRIGHT_STRING "(c) 2002 MandrakeSoft S.A. Written by Kevin Lawton & the Bochs team."
946 
947 #if DEBUG_ATA
948 #  define BX_DEBUG_ATA(a...) BX_DEBUG(a)
949 #else
950 #  define BX_DEBUG_ATA(a...)
951 #endif
952 #if DEBUG_INT13_HD
953 #  define BX_DEBUG_INT13_HD(a...) BX_DEBUG(a)
954 #else
955 #  define BX_DEBUG_INT13_HD(a...)
956 #endif
957 #if DEBUG_INT13_CD
958 #  define BX_DEBUG_INT13_CD(a...) BX_DEBUG(a)
959 #else
960 #  define BX_DEBUG_INT13_CD(a...)
961 #endif
962 #if DEBUG_INT13_ET
963 #  define BX_DEBUG_INT13_ET(a...) BX_DEBUG(a)
964 #else
965 #  define BX_DEBUG_INT13_ET(a...)
966 #endif
967 #if DEBUG_INT13_FL
968 #  define BX_DEBUG_INT13_FL(a...) BX_DEBUG(a)
969 #else
970 #  define BX_DEBUG_INT13_FL(a...)
971 #endif
972 #if DEBUG_INT15
973 #  define BX_DEBUG_INT15(a...) BX_DEBUG(a)
974 #else
975 #  define BX_DEBUG_INT15(a...)
976 #endif
977 #if DEBUG_INT16
978 #  define BX_DEBUG_INT16(a...) BX_DEBUG(a)
979 #else
980 #  define BX_DEBUG_INT16(a...)
981 #endif
982 #if DEBUG_INT1A
983 #  define BX_DEBUG_INT1A(a...) BX_DEBUG(a)
984 #else
985 #  define BX_DEBUG_INT1A(a...)
986 #endif
987 #if DEBUG_INT74
988 #  define BX_DEBUG_INT74(a...) BX_DEBUG(a)
989 #else
990 #  define BX_DEBUG_INT74(a...)
991 #endif
992 
993 #define SET_AL(val8) AX = ((AX & 0xff00) | (val8))
994 #define SET_BL(val8) BX = ((BX & 0xff00) | (val8))
995 #define SET_CL(val8) CX = ((CX & 0xff00) | (val8))
996 #define SET_DL(val8) DX = ((DX & 0xff00) | (val8))
997 #define SET_AH(val8) AX = ((AX & 0x00ff) | ((val8) << 8))
998 #define SET_BH(val8) BX = ((BX & 0x00ff) | ((val8) << 8))
999 #define SET_CH(val8) CX = ((CX & 0x00ff) | ((val8) << 8))
1000 #define SET_DH(val8) DX = ((DX & 0x00ff) | ((val8) << 8))
1001 
1002 #define GET_AL() ( AX & 0x00ff )
1003 #define GET_BL() ( BX & 0x00ff )
1004 #define GET_CL() ( CX & 0x00ff )
1005 #define GET_DL() ( DX & 0x00ff )
1006 #define GET_AH() ( AX >> 8 )
1007 #define GET_BH() ( BX >> 8 )
1008 #define GET_CH() ( CX >> 8 )
1009 #define GET_DH() ( DX >> 8 )
1010 
1011 #define GET_ELDL() ( ELDX & 0x00ff )
1012 #define GET_ELDH() ( ELDX >> 8 )
1013 
1014 #define SET_CF()     FLAGS |= 0x0001
1015 #define CLEAR_CF()   FLAGS &= 0xfffe
1016 #define GET_CF()     (FLAGS & 0x0001)
1017 
1018 #define SET_ZF()     FLAGS |= 0x0040
1019 #define CLEAR_ZF()   FLAGS &= 0xffbf
1020 #define GET_ZF()     (FLAGS & 0x0040)
1021 
1022 #define UNSUPPORTED_FUNCTION 0x86
1023 
1024 #define none 0
1025 #define MAX_SCAN_CODE 0x58
1026 
1027 static struct {
1028   Bit16u normal;
1029   Bit16u shift;
1030   Bit16u control;
1031   Bit16u alt;
1032   Bit8u lock_flags;
1033   } scan_to_scanascii[MAX_SCAN_CODE + 1] = {
1034       {   none,   none,   none,   none, none },
1035       { 0x011b, 0x011b, 0x011b, 0x0100, none }, /* escape */
1036       { 0x0231, 0x0221,   none, 0x7800, none }, /* 1! */
1037       { 0x0332, 0x0340, 0x0300, 0x7900, none }, /* 2@ */
1038       { 0x0433, 0x0423,   none, 0x7a00, none }, /* 3# */
1039       { 0x0534, 0x0524,   none, 0x7b00, none }, /* 4$ */
1040       { 0x0635, 0x0625,   none, 0x7c00, none }, /* 5% */
1041       { 0x0736, 0x075e, 0x071e, 0x7d00, none }, /* 6^ */
1042       { 0x0837, 0x0826,   none, 0x7e00, none }, /* 7& */
1043       { 0x0938, 0x092a,   none, 0x7f00, none }, /* 8* */
1044       { 0x0a39, 0x0a28,   none, 0x8000, none }, /* 9( */
1045       { 0x0b30, 0x0b29,   none, 0x8100, none }, /* 0) */
1046       { 0x0c2d, 0x0c5f, 0x0c1f, 0x8200, none }, /* -_ */
1047       { 0x0d3d, 0x0d2b,   none, 0x8300, none }, /* =+ */
1048       { 0x0e08, 0x0e08, 0x0e7f,   none, none }, /* backspace */
1049       { 0x0f09, 0x0f00,   none,   none, none }, /* tab */
1050       { 0x1071, 0x1051, 0x1011, 0x1000, 0x40 }, /* Q */
1051       { 0x1177, 0x1157, 0x1117, 0x1100, 0x40 }, /* W */
1052       { 0x1265, 0x1245, 0x1205, 0x1200, 0x40 }, /* E */
1053       { 0x1372, 0x1352, 0x1312, 0x1300, 0x40 }, /* R */
1054       { 0x1474, 0x1454, 0x1414, 0x1400, 0x40 }, /* T */
1055       { 0x1579, 0x1559, 0x1519, 0x1500, 0x40 }, /* Y */
1056       { 0x1675, 0x1655, 0x1615, 0x1600, 0x40 }, /* U */
1057       { 0x1769, 0x1749, 0x1709, 0x1700, 0x40 }, /* I */
1058       { 0x186f, 0x184f, 0x180f, 0x1800, 0x40 }, /* O */
1059       { 0x1970, 0x1950, 0x1910, 0x1900, 0x40 }, /* P */
1060       { 0x1a5b, 0x1a7b, 0x1a1b,   none, none }, /* [{ */
1061       { 0x1b5d, 0x1b7d, 0x1b1d,   none, none }, /* ]} */
1062       { 0x1c0d, 0x1c0d, 0x1c0a,   none, none }, /* Enter */
1063       {   none,   none,   none,   none, none }, /* L Ctrl */
1064       { 0x1e61, 0x1e41, 0x1e01, 0x1e00, 0x40 }, /* A */
1065       { 0x1f73, 0x1f53, 0x1f13, 0x1f00, 0x40 }, /* S */
1066       { 0x2064, 0x2044, 0x2004, 0x2000, 0x40 }, /* D */
1067       { 0x2166, 0x2146, 0x2106, 0x2100, 0x40 }, /* F */
1068       { 0x2267, 0x2247, 0x2207, 0x2200, 0x40 }, /* G */
1069       { 0x2368, 0x2348, 0x2308, 0x2300, 0x40 }, /* H */
1070       { 0x246a, 0x244a, 0x240a, 0x2400, 0x40 }, /* J */
1071       { 0x256b, 0x254b, 0x250b, 0x2500, 0x40 }, /* K */
1072       { 0x266c, 0x264c, 0x260c, 0x2600, 0x40 }, /* L */
1073       { 0x273b, 0x273a,   none,   none, none }, /* ;: */
1074       { 0x2827, 0x2822,   none,   none, none }, /* '" */
1075       { 0x2960, 0x297e,   none,   none, none }, /* `~ */
1076       {   none,   none,   none,   none, none }, /* L shift */
1077       { 0x2b5c, 0x2b7c, 0x2b1c,   none, none }, /* |\ */
1078       { 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00, 0x40 }, /* Z */
1079       { 0x2d78, 0x2d58, 0x2d18, 0x2d00, 0x40 }, /* X */
1080       { 0x2e63, 0x2e43, 0x2e03, 0x2e00, 0x40 }, /* C */
1081       { 0x2f76, 0x2f56, 0x2f16, 0x2f00, 0x40 }, /* V */
1082       { 0x3062, 0x3042, 0x3002, 0x3000, 0x40 }, /* B */
1083       { 0x316e, 0x314e, 0x310e, 0x3100, 0x40 }, /* N */
1084       { 0x326d, 0x324d, 0x320d, 0x3200, 0x40 }, /* M */
1085       { 0x332c, 0x333c,   none,   none, none }, /* ,< */
1086       { 0x342e, 0x343e,   none,   none, none }, /* .> */
1087       { 0x352f, 0x353f,   none,   none, none }, /* /? */
1088       {   none,   none,   none,   none, none }, /* R Shift */
1089       { 0x372a, 0x372a,   none,   none, none }, /* * */
1090       {   none,   none,   none,   none, none }, /* L Alt */
1091       { 0x3920, 0x3920, 0x3920, 0x3920, none }, /* space */
1092       {   none,   none,   none,   none, none }, /* caps lock */
1093       { 0x3b00, 0x5400, 0x5e00, 0x6800, none }, /* F1 */
1094       { 0x3c00, 0x5500, 0x5f00, 0x6900, none }, /* F2 */
1095       { 0x3d00, 0x5600, 0x6000, 0x6a00, none }, /* F3 */
1096       { 0x3e00, 0x5700, 0x6100, 0x6b00, none }, /* F4 */
1097       { 0x3f00, 0x5800, 0x6200, 0x6c00, none }, /* F5 */
1098       { 0x4000, 0x5900, 0x6300, 0x6d00, none }, /* F6 */
1099       { 0x4100, 0x5a00, 0x6400, 0x6e00, none }, /* F7 */
1100       { 0x4200, 0x5b00, 0x6500, 0x6f00, none }, /* F8 */
1101       { 0x4300, 0x5c00, 0x6600, 0x7000, none }, /* F9 */
1102       { 0x4400, 0x5d00, 0x6700, 0x7100, none }, /* F10 */
1103       {   none,   none,   none,   none, none }, /* Num Lock */
1104       {   none,   none,   none,   none, none }, /* Scroll Lock */
1105       { 0x4700, 0x4737, 0x7700,   none, 0x20 }, /* 7 Home */
1106       { 0x4800, 0x4838,   none,   none, 0x20 }, /* 8 UP */
1107       { 0x4900, 0x4939, 0x8400,   none, 0x20 }, /* 9 PgUp */
1108       { 0x4a2d, 0x4a2d,   none,   none, none }, /* - */
1109       { 0x4b00, 0x4b34, 0x7300,   none, 0x20 }, /* 4 Left */
1110       { 0x4c00, 0x4c35,   none,   none, 0x20 }, /* 5 */
1111       { 0x4d00, 0x4d36, 0x7400,   none, 0x20 }, /* 6 Right */
1112       { 0x4e2b, 0x4e2b,   none,   none, none }, /* + */
1113       { 0x4f00, 0x4f31, 0x7500,   none, 0x20 }, /* 1 End */
1114       { 0x5000, 0x5032,   none,   none, 0x20 }, /* 2 Down */
1115       { 0x5100, 0x5133, 0x7600,   none, 0x20 }, /* 3 PgDn */
1116       { 0x5200, 0x5230,   none,   none, 0x20 }, /* 0 Ins */
1117       { 0x5300, 0x532e,   none,   none, 0x20 }, /* Del */
1118       {   none,   none,   none,   none, none },
1119       {   none,   none,   none,   none, none },
1120       { 0x565c, 0x567c,   none,   none, none }, /* \| */
1121       { 0x8500, 0x8700, 0x8900, 0x8b00, none }, /* F11 */
1122       { 0x8600, 0x8800, 0x8a00, 0x8c00, none }, /* F12 */
1123       };
1124 
1125   Bit8u
1126 inb(port)
1127   Bit16u port;
1128 {
1129 ASM_START
1130   push bp
1131   mov  bp, sp
1132 
1133     push dx
1134     mov  dx, 4[bp]
1135     in   al, dx
1136     pop  dx
1137 
1138   pop  bp
1139 ASM_END
1140 }
1141 
1142 #if BX_USE_ATADRV
1143   Bit16u
1144 inw(port)
1145   Bit16u port;
1146 {
1147 ASM_START
1148   push bp
1149   mov  bp, sp
1150 
1151     push dx
1152     mov  dx, 4[bp]
1153     in   ax, dx
1154     pop  dx
1155 
1156   pop  bp
1157 ASM_END
1158 }
1159 #endif
1160 
1161   void
1162 outb(port, val)
1163   Bit16u port;
1164   Bit8u  val;
1165 {
1166 ASM_START
1167   push bp
1168   mov  bp, sp
1169 
1170     push ax
1171     push dx
1172     mov  dx, 4[bp]
1173     mov  al, 6[bp]
1174     out  dx, al
1175     pop  dx
1176     pop  ax
1177 
1178   pop  bp
1179 ASM_END
1180 }
1181 
1182 #if BX_USE_ATADRV
1183   void
1184 outw(port, val)
1185   Bit16u port;
1186   Bit16u  val;
1187 {
1188 ASM_START
1189   push bp
1190   mov  bp, sp
1191 
1192     push ax
1193     push dx
1194     mov  dx, 4[bp]
1195     mov  ax, 6[bp]
1196     out  dx, ax
1197     pop  dx
1198     pop  ax
1199 
1200   pop  bp
1201 ASM_END
1202 }
1203 #endif
1204 
1205   void
1206 outb_cmos(cmos_reg, val)
1207   Bit8u cmos_reg;
1208   Bit8u val;
1209 {
1210 ASM_START
1211   push bp
1212   mov  bp, sp
1213 
1214     mov  al, 4[bp] ;; cmos_reg
1215     out  0x70, al
1216     mov  al, 6[bp] ;; val
1217     out  0x71, al
1218 
1219   pop  bp
1220 ASM_END
1221 }
1222 
1223   Bit8u
1224 inb_cmos(cmos_reg)
1225   Bit8u cmos_reg;
1226 {
1227 ASM_START
1228   push bp
1229   mov  bp, sp
1230 
1231     mov  al, 4[bp] ;; cmos_reg
1232     out 0x70, al
1233     in  al, 0x71
1234 
1235   pop  bp
1236 ASM_END
1237 }
1238 
1239   void
1240 init_rtc()
1241 {
1242   outb_cmos(0x0a, 0x26);
1243   outb_cmos(0x0b, 0x02);
1244   inb_cmos(0x0c);
1245   inb_cmos(0x0d);
1246 }
1247 
1248   bx_bool
1249 rtc_updating()
1250 {
1251   // This function checks to see if the update-in-progress bit
1252   // is set in CMOS Status Register A.  If not, it returns 0.
1253   // If it is set, it tries to wait until there is a transition
1254   // to 0, and will return 0 if such a transition occurs.  A 1
1255   // is returned only after timing out.  The maximum period
1256   // that this bit should be set is constrained to 244useconds.
1257   // The count I use below guarantees coverage or more than
1258   // this time, with any reasonable IPS setting.
1259 
1260   Bit16u count;
1261 
1262   count = 25000;
1263   while (--count != 0) {
1264     if ( (inb_cmos(0x0a) & 0x80) == 0 )
1265       return(0);
1266     }
1267   return(1); // update-in-progress never transitioned to 0
1268 }
1269 
1270 
1271   Bit8u
1272 read_byte(seg, offset)
1273   Bit16u seg;
1274   Bit16u offset;
1275 {
1276 ASM_START
1277   push bp
1278   mov  bp, sp
1279 
1280     push bx
1281     push ds
1282     mov  ax, 4[bp] ; segment
1283     mov  ds, ax
1284     mov  bx, 6[bp] ; offset
1285     mov  al, [bx]
1286     ;; al = return value (byte)
1287     pop  ds
1288     pop  bx
1289 
1290   pop  bp
1291 ASM_END
1292 }
1293 
1294   Bit16u
1295 read_word(seg, offset)
1296   Bit16u seg;
1297   Bit16u offset;
1298 {
1299 ASM_START
1300   push bp
1301   mov  bp, sp
1302 
1303     push bx
1304     push ds
1305     mov  ax, 4[bp] ; segment
1306     mov  ds, ax
1307     mov  bx, 6[bp] ; offset
1308     mov  ax, [bx]
1309     ;; ax = return value (word)
1310     pop  ds
1311     pop  bx
1312 
1313   pop  bp
1314 ASM_END
1315 }
1316 
1317   void
1318 write_byte(seg, offset, data)
1319   Bit16u seg;
1320   Bit16u offset;
1321   Bit8u data;
1322 {
1323 ASM_START
1324   push bp
1325   mov  bp, sp
1326 
1327     push ax
1328     push bx
1329     push ds
1330     mov  ax, 4[bp] ; segment
1331     mov  ds, ax
1332     mov  bx, 6[bp] ; offset
1333     mov  al, 8[bp] ; data byte
1334     mov  [bx], al  ; write data byte
1335     pop  ds
1336     pop  bx
1337     pop  ax
1338 
1339   pop  bp
1340 ASM_END
1341 }
1342 
1343   void
1344 write_word(seg, offset, data)
1345   Bit16u seg;
1346   Bit16u offset;
1347   Bit16u data;
1348 {
1349 ASM_START
1350   push bp
1351   mov  bp, sp
1352 
1353     push ax
1354     push bx
1355     push ds
1356     mov  ax, 4[bp] ; segment
1357     mov  ds, ax
1358     mov  bx, 6[bp] ; offset
1359     mov  ax, 8[bp] ; data word
1360     mov  [bx], ax  ; write data word
1361     pop  ds
1362     pop  bx
1363     pop  ax
1364 
1365   pop  bp
1366 ASM_END
1367 }
1368 
1369   Bit16u
1370 get_CS()
1371 {
1372 ASM_START
1373   mov  ax, cs
1374 ASM_END
1375 }
1376 
1377   Bit16u
1378 get_SS()
1379 {
1380 ASM_START
1381   mov  ax, ss
1382 ASM_END
1383 }
1384 
1385 #if BX_DEBUG_SERIAL
1386 /* serial debug port*/
1387 #define BX_DEBUG_PORT 0x03f8
1388 
1389 /* data */
1390 #define UART_RBR 0x00
1391 #define UART_THR 0x00
1392 
1393 /* control */
1394 #define UART_IER 0x01
1395 #define UART_IIR 0x02
1396 #define UART_FCR 0x02
1397 #define UART_LCR 0x03
1398 #define UART_MCR 0x04
1399 #define UART_DLL 0x00
1400 #define UART_DLM 0x01
1401 
1402 /* status */
1403 #define UART_LSR 0x05
1404 #define UART_MSR 0x06
1405 #define UART_SCR 0x07
1406 
1407 int uart_can_tx_byte(base_port)
1408     Bit16u base_port;
1409 {
1410     return inb(base_port + UART_LSR) & 0x20;
1411 }
1412 
1413 void uart_wait_to_tx_byte(base_port)
1414     Bit16u base_port;
1415 {
1416     while (!uart_can_tx_byte(base_port));
1417 }
1418 
1419 void uart_wait_until_sent(base_port)
1420     Bit16u base_port;
1421 {
1422     while (!(inb(base_port + UART_LSR) & 0x40));
1423 }
1424 
1425 void uart_tx_byte(base_port, data)
1426     Bit16u base_port;
1427     Bit8u data;
1428 {
1429     uart_wait_to_tx_byte(base_port);
1430     outb(base_port + UART_THR, data);
1431     uart_wait_until_sent(base_port);
1432 }
1433 #endif
1434 
1435   void
1436 wrch(c)
1437   Bit8u  c;
1438 {
1439   ASM_START
1440   push bp
1441   mov  bp, sp
1442 
1443   push bx
1444   mov  ah, #0x0e
1445   mov  al, 4[bp]
1446   xor  bx,bx
1447   int  #0x10
1448   pop  bx
1449 
1450   pop  bp
1451   ASM_END
1452 }
1453 
1454   void
1455 send(action, c)
1456   Bit16u action;
1457   Bit8u  c;
1458 {
1459 #if BX_DEBUG_SERIAL
1460   if (c == '\n') uart_tx_byte(BX_DEBUG_PORT, '\r');
1461   uart_tx_byte(BX_DEBUG_PORT, c);
1462 #endif
1463 #if BX_VIRTUAL_PORTS
1464   if (action & BIOS_PRINTF_DEBUG) outb(DEBUG_PORT, c);
1465   if (action & BIOS_PRINTF_INFO) outb(INFO_PORT, c);
1466 #endif
1467   if (action & BIOS_PRINTF_SCREEN) {
1468     if (c == '\n') wrch('\r');
1469     wrch(c);
1470   }
1471 }
1472 
1473   void
1474 put_int(action, val, width, neg)
1475   Bit16u action;
1476   short val, width;
1477   bx_bool neg;
1478 {
1479   short nval = val / 10;
1480   if (nval)
1481     put_int(action, nval, width - 1, neg);
1482   else {
1483     while (--width > 0) send(action, ' ');
1484     if (neg) send(action, '-');
1485   }
1486   send(action, val - (nval * 10) + '0');
1487 }
1488 
1489   void
1490 put_uint(action, val, width, neg)
1491   Bit16u action;
1492   unsigned short val;
1493   short width;
1494   bx_bool neg;
1495 {
1496   unsigned short nval = val / 10;
1497   if (nval)
1498     put_uint(action, nval, width - 1, neg);
1499   else {
1500     while (--width > 0) send(action, ' ');
1501     if (neg) send(action, '-');
1502   }
1503   send(action, val - (nval * 10) + '0');
1504 }
1505 
1506   void
1507 put_luint(action, val, width, neg)
1508   Bit16u action;
1509   unsigned long val;
1510   short width;
1511   bx_bool neg;
1512 {
1513   unsigned long nval = val / 10;
1514   if (nval)
1515     put_luint(action, nval, width - 1, neg);
1516   else {
1517     while (--width > 0) send(action, ' ');
1518     if (neg) send(action, '-');
1519   }
1520   send(action, val - (nval * 10) + '0');
1521 }
1522 
1523 void put_str(action, segment, offset)
1524   Bit16u action;
1525   Bit16u segment;
1526   Bit16u offset;
1527 {
1528   Bit8u c;
1529 
1530   while (c = read_byte(segment, offset)) {
1531     send(action, c);
1532     offset++;
1533   }
1534 }
1535 
1536   void
1537 delay_ticks(ticks)
1538   Bit16u ticks;
1539 {
1540   long ticks_to_wait, delta;
1541   Bit32u prev_ticks, t;
1542 
1543    /*
1544     * The 0:046c wraps around at 'midnight' according to a 18.2Hz clock.
1545     * We also have to be careful about interrupt storms.
1546     */
1547 ASM_START
1548   pushf
1549   sti
1550 ASM_END
1551   ticks_to_wait = ticks;
1552   prev_ticks = read_dword(0x0, 0x46c);
1553   do
1554   {
1555 ASM_START
1556     hlt
1557 ASM_END
1558     t = read_dword(0x0, 0x46c);
1559     if (t > prev_ticks)
1560     {
1561       delta = t - prev_ticks;     /* The temp var is required or bcc screws up. */
1562       ticks_to_wait -= delta;
1563     }
1564     else if (t < prev_ticks)
1565     {
1566       ticks_to_wait -= t;         /* wrapped */
1567     }
1568 
1569     prev_ticks = t;
1570   } while (ticks_to_wait > 0);
1571 ASM_START
1572   cli
1573   popf
1574 ASM_END
1575 }
1576 
1577   Bit8u
1578 check_for_keystroke()
1579 {
1580 ASM_START
1581   mov  ax, #0x100
1582   int  #0x16
1583   jz   no_key
1584   mov  al, #1
1585   jmp  done
1586 no_key:
1587   xor  al, al
1588 done:
1589 ASM_END
1590 }
1591 
1592   Bit8u
1593 get_keystroke()
1594 {
1595 ASM_START
1596   mov  ax, #0x0
1597   int  #0x16
1598   xchg ah, al
1599 ASM_END
1600 }
1601 
1602   void
1603 delay_ticks_and_check_for_keystroke(ticks, count)
1604   Bit16u ticks, count;
1605 {
1606   Bit16u i;
1607   for (i = 1; i <= count; i++) {
1608     delay_ticks(ticks);
1609     if (check_for_keystroke())
1610       break;
1611   }
1612 }
1613 
1614 //--------------------------------------------------------------------------
1615 // bios_printf()
1616 //   A compact variable argument printf function.
1617 //
1618 //   Supports %[format_width][length]format
1619 //   where format can be x,X,u,d,s,S,c
1620 //   and the optional length modifier is l (ell)
1621 //--------------------------------------------------------------------------
1622   void
1623 bios_printf(action, s)
1624   Bit16u action;
1625   Bit8u *s;
1626 {
1627   Bit8u c, format_char;
1628   bx_bool  in_format;
1629   short i;
1630   Bit16u  *arg_ptr;
1631   Bit16u   arg_seg, arg, nibble, hibyte, shift_count, format_width, hexadd;
1632 
1633   arg_ptr = &s;
1634   arg_seg = get_SS();
1635 
1636   in_format = 0;
1637   format_width = 0;
1638 
1639   if ((action & BIOS_PRINTF_DEBHALT) == BIOS_PRINTF_DEBHALT) {
1640 #if BX_VIRTUAL_PORTS
1641     outb(PANIC_PORT2, 0x00);
1642 #endif
1643     bios_printf (BIOS_PRINTF_SCREEN, "FATAL: ");
1644   }
1645 
1646   while (c = read_byte(get_CS(), s)) {
1647     if ( c == '%' ) {
1648       in_format = 1;
1649       format_width = 0;
1650       }
1651     else if (in_format) {
1652       if ( (c>='0') && (c<='9') ) {
1653         format_width = (format_width * 10) + (c - '0');
1654         }
1655       else {
1656         arg_ptr++; // increment to next arg
1657         arg = read_word(arg_seg, arg_ptr);
1658         if (c == 'x' || c == 'X') {
1659           if (format_width == 0)
1660             format_width = 4;
1661           if (c == 'x')
1662             hexadd = 'a';
1663           else
1664             hexadd = 'A';
1665           for (i=format_width-1; i>=0; i--) {
1666             nibble = (arg >> (4 * i)) & 0x000f;
1667             send (action, (nibble<=9)? (nibble+'0') : (nibble-10+hexadd));
1668             }
1669           }
1670         else if (c == 'u') {
1671           put_uint(action, arg, format_width, 0);
1672           }
1673         else if (c == 'l') {
1674           s++;
1675           c = read_byte(get_CS(), s); /* is it ld,lx,lu? */
1676           arg_ptr++; /* increment to next arg */
1677           hibyte = read_word(arg_seg, arg_ptr);
1678           if (c == 'd') {
1679             if (hibyte & 0x8000)
1680               put_luint(action, 0L-(((Bit32u) hibyte << 16) | arg), format_width-1, 1);
1681             else
1682               put_luint(action, ((Bit32u) hibyte << 16) | arg, format_width, 0);
1683            }
1684           else if (c == 'u') {
1685             put_luint(action, ((Bit32u) hibyte << 16) | arg, format_width, 0);
1686            }
1687           else if (c == 'x' || c == 'X')
1688            {
1689             if (format_width == 0)
1690               format_width = 8;
1691             if (c == 'x')
1692               hexadd = 'a';
1693             else
1694               hexadd = 'A';
1695             for (i=format_width-1; i>=0; i--) {
1696               nibble = ((((Bit32u) hibyte <<16) | arg) >> (4 * i)) & 0x000f;
1697               send (action, (nibble<=9)? (nibble+'0') : (nibble-10+hexadd));
1698               }
1699            }
1700           }
1701         else if (c == 'd') {
1702           if (arg & 0x8000)
1703             put_int(action, -arg, format_width - 1, 1);
1704           else
1705             put_int(action, arg, format_width, 0);
1706           }
1707         else if (c == 's') {
1708           put_str(action, get_CS(), arg);
1709           }
1710         else if (c == 'S') {
1711           hibyte = arg;
1712           arg_ptr++;
1713           arg = read_word(arg_seg, arg_ptr);
1714           put_str(action, hibyte, arg);
1715           }
1716         else if (c == 'c') {
1717           send(action, arg);
1718           }
1719         else
1720           BX_PANIC("bios_printf: unknown format\n");
1721           in_format = 0;
1722         }
1723       }
1724     else {
1725       send(action, c);
1726       }
1727     s ++;
1728     }
1729 
1730   if (action & BIOS_PRINTF_HALT) {
1731     // freeze in a busy loop.
1732 ASM_START
1733     cli
1734  halt2_loop:
1735     hlt
1736     jmp halt2_loop
1737 ASM_END
1738     }
1739 }
1740 
1741 //--------------------------------------------------------------------------
1742 // keyboard_init
1743 //--------------------------------------------------------------------------
1744 // this file is based on LinuxBIOS implementation of keyboard.c
1745 // could convert to #asm to gain space
1746   void
1747 keyboard_init()
1748 {
1749     Bit16u max;
1750 
1751     /* ------------------- Flush buffers ------------------------*/
1752     /* Wait until buffer is empty */
1753     max=0xffff;
1754     while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
1755 
1756     /* flush incoming keys */
1757     max=0x2000;
1758     while (--max > 0) {
1759         outb(0x80, 0x00);
1760         if (inb(0x64) & 0x01) {
1761             inb(0x60);
1762             max = 0x2000;
1763             }
1764         }
1765 
1766     // Due to timer issues, and if the IPS setting is > 15000000,
1767     // the incoming keys might not be flushed here. That will
1768     // cause a panic a few lines below.  See sourceforge bug report :
1769     // [ 642031 ] FATAL: Keyboard RESET error:993
1770 
1771     /* ------------------- controller side ----------------------*/
1772     /* send cmd = 0xAA, self test 8042 */
1773     outb(0x64, 0xaa);
1774 
1775     /* Wait until buffer is empty */
1776     max=0xffff;
1777     while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
1778     if (max==0x0) keyboard_panic(00);
1779 
1780     /* Wait for data */
1781     max=0xffff;
1782     while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x01);
1783     if (max==0x0) keyboard_panic(01);
1784 
1785     /* read self-test result, 0x55 should be returned from 0x60 */
1786     if ((inb(0x60) != 0x55)){
1787         keyboard_panic(991);
1788     }
1789 
1790     /* send cmd = 0xAB, keyboard interface test */
1791     outb(0x64,0xab);
1792 
1793     /* Wait until buffer is empty */
1794     max=0xffff;
1795     while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x10);
1796     if (max==0x0) keyboard_panic(10);
1797 
1798     /* Wait for data */
1799     max=0xffff;
1800     while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x11);
1801     if (max==0x0) keyboard_panic(11);
1802 
1803     /* read keyboard interface test result, */
1804     /* 0x00 should be returned form 0x60 */
1805     if ((inb(0x60) != 0x00)) {
1806         keyboard_panic(992);
1807     }
1808 
1809     /* Enable Keyboard clock */
1810     outb(0x64,0xae);
1811     outb(0x64,0xa8);
1812 
1813     /* ------------------- keyboard side ------------------------*/
1814     /* reset kerboard and self test  (keyboard side) */
1815     outb(0x60, 0xff);
1816 
1817     /* Wait until buffer is empty */
1818     max=0xffff;
1819     while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x20);
1820     if (max==0x0) keyboard_panic(20);
1821 
1822     /* Wait for data */
1823     max=0xffff;
1824     while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x21);
1825     if (max==0x0) keyboard_panic(21);
1826 
1827     /* keyboard should return ACK */
1828     if ((inb(0x60) != 0xfa)) {
1829         keyboard_panic(993);
1830     }
1831 
1832     /* Wait for data */
1833     max=0xffff;
1834     while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x31);
1835     if (max==0x0) keyboard_panic(31);
1836 
1837     if ((inb(0x60) != 0xaa)) {
1838         keyboard_panic(994);
1839     }
1840 
1841     /* Disable keyboard */
1842     outb(0x60, 0xf5);
1843 
1844     /* Wait until buffer is empty */
1845     max=0xffff;
1846     while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x40);
1847     if (max==0x0) keyboard_panic(40);
1848 
1849     /* Wait for data */
1850     max=0xffff;
1851     while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x41);
1852     if (max==0x0) keyboard_panic(41);
1853 
1854     /* keyboard should return ACK */
1855     if ((inb(0x60) != 0xfa)) {
1856         keyboard_panic(995);
1857     }
1858 
1859     /* Write Keyboard Mode */
1860     outb(0x64, 0x60);
1861 
1862     /* Wait until buffer is empty */
1863     max=0xffff;
1864     while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x50);
1865     if (max==0x0) keyboard_panic(50);
1866 
1867     /* send cmd: scan code convert, disable mouse, enable IRQ 1 */
1868     outb(0x60, 0x61);
1869 
1870     /* Wait until buffer is empty */
1871     max=0xffff;
1872     while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x60);
1873     if (max==0x0) keyboard_panic(60);
1874 
1875     /* Enable keyboard */
1876     outb(0x60, 0xf4);
1877 
1878     /* Wait until buffer is empty */
1879     max=0xffff;
1880     while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x70);
1881     if (max==0x0) keyboard_panic(70);
1882 
1883     /* Wait for data */
1884     max=0xffff;
1885     while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x71);
1886     if (max==0x0) keyboard_panic(70);
1887 
1888     /* keyboard should return ACK */
1889     if ((inb(0x60) != 0xfa)) {
1890         keyboard_panic(996);
1891     }
1892 
1893     outb(0x80, 0x77);
1894 }
1895 
1896 //--------------------------------------------------------------------------
1897 // keyboard_panic
1898 //--------------------------------------------------------------------------
1899   void
1900 keyboard_panic(status)
1901   Bit16u status;
1902 {
1903   // If you're getting a 993 keyboard panic here,
1904   // please see the comment in keyboard_init
1905 
1906   BX_PANIC("Keyboard error:%u\n",status);
1907 }
1908 
1909 //--------------------------------------------------------------------------
1910 // shutdown_status_panic
1911 //   called when the shutdown statsu is not implemented, displays the status
1912 //--------------------------------------------------------------------------
1913   void
1914 shutdown_status_panic(status)
1915   Bit16u status;
1916 {
1917   BX_PANIC("Unimplemented shutdown status: %02x\n",(Bit8u)status);
1918 }
1919 
1920 void s3_resume_panic()
1921 {
1922   BX_PANIC("Returned from s3_resume.\n");
1923 }
1924 
1925 //--------------------------------------------------------------------------
1926 // print_bios_banner
1927 //   displays a the bios version
1928 //--------------------------------------------------------------------------
1929 void
1930 print_bios_banner()
1931 {
1932   printf(BX_APPNAME" BIOS - build: %s\n%s\nOptions: ",
1933     BIOS_BUILD_DATE, bios_cvs_version_string);
1934   printf(
1935 #if BX_APM
1936   "apmbios "
1937 #endif
1938 #if BX_PCIBIOS
1939   "pcibios "
1940 #endif
1941 #if BX_ELTORITO_BOOT
1942   "eltorito "
1943 #endif
1944 #if BX_ROMBIOS32
1945   "rombios32 "
1946 #endif
1947   "\n\n");
1948 }
1949 
1950 //--------------------------------------------------------------------------
1951 // BIOS Boot Specification 1.0.1 compatibility
1952 //
1953 // Very basic support for the BIOS Boot Specification, which allows expansion
1954 // ROMs to register themselves as boot devices, instead of just stealing the
1955 // INT 19h boot vector.
1956 //
1957 // This is a hack: to do it properly requires a proper PnP BIOS and we aren't
1958 // one; we just lie to the option ROMs to make them behave correctly.
1959 // We also don't support letting option ROMs register as bootable disk
1960 // drives (BCVs), only as bootable devices (BEVs).
1961 //
1962 // http://www.phoenix.com/en/Customer+Services/White+Papers-Specs/pc+industry+specifications.htm
1963 //--------------------------------------------------------------------------
1964 
1965 static char drivetypes[][10]={"", "Floppy","Hard Disk","CD-Rom", "Network"};
1966 
1967 static void
1968 init_boot_vectors()
1969 {
1970   ipl_entry_t e;
1971   Bit16u count = 0;
1972   Bit16u ss = get_SS();
1973 
1974   /* Clear out the IPL table. */
1975   memsetb(IPL_SEG, IPL_TABLE_OFFSET, 0, IPL_SIZE);
1976 
1977   /* User selected device not set */
1978   write_word(IPL_SEG, IPL_BOOTFIRST_OFFSET, 0xFFFF);
1979 
1980   /* Floppy drive */
1981   e.type = IPL_TYPE_FLOPPY; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
1982   memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
1983   count++;
1984 
1985   /* First HDD */
1986   e.type = IPL_TYPE_HARDDISK; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
1987   memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
1988   count++;
1989 
1990 #if BX_ELTORITO_BOOT
1991   /* CDROM */
1992   e.type = IPL_TYPE_CDROM; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
1993   memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
1994   count++;
1995 #endif
1996 
1997   /* Remember how many devices we have */
1998   write_word(IPL_SEG, IPL_COUNT_OFFSET, count);
1999   /* Not tried booting anything yet */
2000   write_word(IPL_SEG, IPL_SEQUENCE_OFFSET, 0xffff);
2001 }
2002 
2003 static Bit8u
2004 get_boot_vector(i, e)
2005 Bit16u i; ipl_entry_t *e;
2006 {
2007   Bit16u count;
2008   Bit16u ss = get_SS();
2009   /* Get the count of boot devices, and refuse to overrun the array */
2010   count = read_word(IPL_SEG, IPL_COUNT_OFFSET);
2011   if (i >= count) return 0;
2012   /* OK to read this device */
2013   memcpyb(ss, e, IPL_SEG, IPL_TABLE_OFFSET + i * sizeof (*e), sizeof (*e));
2014   return 1;
2015 }
2016 
2017 #if BX_ELTORITO_BOOT
2018   void
2019 interactive_bootkey()
2020 {
2021   ipl_entry_t e;
2022   Bit16u count;
2023   char description[33];
2024   Bit8u scan_code;
2025   Bit8u i;
2026   Bit16u ss = get_SS();
2027   Bit16u valid_choice = 0;
2028 
2029   while (check_for_keystroke())
2030     get_keystroke();
2031 
2032   printf("Press F12 for boot menu.\n\n");
2033 
2034   delay_ticks_and_check_for_keystroke(11, 5); /* ~3 seconds */
2035   if (check_for_keystroke())
2036   {
2037     scan_code = get_keystroke();
2038     if (scan_code == 0x86) /* F12 */
2039     {
2040       while (check_for_keystroke())
2041         get_keystroke();
2042 
2043       printf("Select boot device:\n\n");
2044 
2045       count = read_word(IPL_SEG, IPL_COUNT_OFFSET);
2046       for (i = 0; i < count; i++)
2047       {
2048         memcpyb(ss, &e, IPL_SEG, IPL_TABLE_OFFSET + i * sizeof (e), sizeof (e));
2049         printf("%d. ", i+1);
2050         switch(e.type)
2051         {
2052           case IPL_TYPE_FLOPPY:
2053           case IPL_TYPE_HARDDISK:
2054           case IPL_TYPE_CDROM:
2055             printf("%s\n", drivetypes[e.type]);
2056             break;
2057           case IPL_TYPE_BEV:
2058             printf("%s", drivetypes[4]);
2059             if (e.description != 0)
2060             {
2061               memcpyb(ss, &description, (Bit16u)(e.description >> 16), (Bit16u)(e.description & 0xffff), 32);
2062               description[32] = 0;
2063               printf(" [%S]", ss, description);
2064            }
2065            printf("\n");
2066            break;
2067         }
2068       }
2069 
2070       count++;
2071       while (!valid_choice) {
2072         scan_code = get_keystroke();
2073         if (scan_code == 0x01 || scan_code == 0x58) /* ESC or F12 */
2074         {
2075           valid_choice = 1;
2076         }
2077         else if (scan_code <= count)
2078         {
2079           valid_choice = 1;
2080           scan_code -= 1;
2081           /* Set user selected device */
2082           write_word(IPL_SEG, IPL_BOOTFIRST_OFFSET, scan_code);
2083         }
2084       }
2085     printf("\n");
2086     }
2087   }
2088 }
2089 #endif // BX_ELTORITO_BOOT
2090 
2091 //--------------------------------------------------------------------------
2092 // print_boot_device
2093 //   displays the boot device
2094 //--------------------------------------------------------------------------
2095 
2096 void
2097 print_boot_device(e)
2098   ipl_entry_t *e;
2099 {
2100   Bit16u type;
2101   char description[33];
2102   Bit16u ss = get_SS();
2103   type = e->type;
2104   /* NIC appears as type 0x80 */
2105   if (type == IPL_TYPE_BEV) type = 0x4;
2106   if (type == 0 || type > 0x4) BX_PANIC("Bad drive type\n");
2107   printf("Booting from %s", drivetypes[type]);
2108   /* print product string if BEV */
2109   if (type == 4 && e->description != 0) {
2110     /* first 32 bytes are significant */
2111     memcpyb(ss, &description, (Bit16u)(e->description >> 16), (Bit16u)(e->description & 0xffff), 32);
2112     /* terminate string */
2113     description[32] = 0;
2114     printf(" [%S]", ss, description);
2115   }
2116   printf("...\n");
2117 }
2118 
2119 //--------------------------------------------------------------------------
2120 // print_boot_failure
2121 //   displays the reason why boot failed
2122 //--------------------------------------------------------------------------
2123   void
2124 print_boot_failure(type, reason)
2125   Bit16u type; Bit8u reason;
2126 {
2127   if (type == 0 || type > 0x3) BX_PANIC("Bad drive type\n");
2128 
2129   printf("Boot failed");
2130   if (type < 4) {
2131     /* Report the reason too */
2132     if (reason==0)
2133       printf(": not a bootable disk");
2134     else
2135       printf(": could not read the boot disk");
2136   }
2137   printf("\n\n");
2138 }
2139 
2140 //--------------------------------------------------------------------------
2141 // print_cdromboot_failure
2142 //   displays the reason why boot failed
2143 //--------------------------------------------------------------------------
2144   void
2145 print_cdromboot_failure( code )
2146   Bit16u code;
2147 {
2148   bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, "CDROM boot failure code : %04x\n",code);
2149 
2150   return;
2151 }
2152 
2153 void
2154 nmi_handler_msg()
2155 {
2156   BX_PANIC("NMI Handler called\n");
2157 }
2158 
2159 void
2160 int18_panic_msg()
2161 {
2162   BX_PANIC("INT18: BOOT FAILURE\n");
2163 }
2164 
2165 void
2166 log_bios_start()
2167 {
2168 #if BX_DEBUG_SERIAL
2169   outb(BX_DEBUG_PORT+UART_LCR, 0x03); /* setup for serial logging: 8N1 */
2170 #endif
2171   BX_INFO("%s\n", bios_cvs_version_string);
2172 }
2173 
2174   bx_bool
2175 set_enable_a20(val)
2176   bx_bool val;
2177 {
2178   Bit8u  oldval;
2179 
2180   // Use PS2 System Control port A to set A20 enable
2181 
2182   // get current setting first
2183   oldval = inb(0x92);
2184 
2185   // change A20 status
2186   if (val)
2187     outb(0x92, oldval | 0x02);
2188   else
2189     outb(0x92, oldval & 0xfd);
2190 
2191   return((oldval & 0x02) != 0);
2192 }
2193 
2194   void
2195 debugger_on()
2196 {
2197   outb(0xfedc, 0x01);
2198 }
2199 
2200   void
2201 debugger_off()
2202 {
2203   outb(0xfedc, 0x00);
2204 }
2205 
2206 int
2207 s3_resume()
2208 {
2209     Bit32u s3_wakeup_vector;
2210     Bit8u s3_resume_flag;
2211 
2212     s3_resume_flag = read_byte(0x40, 0xb0);
2213     s3_wakeup_vector = read_dword(0x40, 0xb2);
2214 
2215     BX_INFO("S3 resume called %x 0x%lx\n", s3_resume_flag, s3_wakeup_vector);
2216     if (s3_resume_flag != 0xFE || !s3_wakeup_vector)
2217 	    return 0;
2218 
2219     write_byte(0x40, 0xb0, 0);
2220 
2221     /* setup wakeup vector */
2222     write_word(0x40, 0xb6, (s3_wakeup_vector & 0xF)); /* IP */
2223     write_word(0x40, 0xb8, (s3_wakeup_vector >> 4)); /* CS */
2224 
2225     BX_INFO("S3 resume jump to %x:%x\n", (s3_wakeup_vector >> 4),
2226 		    (s3_wakeup_vector & 0xF));
2227 ASM_START
2228     jmpf [0x04b6]
2229 ASM_END
2230     return 1;
2231 }
2232 
2233 #if BX_USE_ATADRV
2234 
2235 // ---------------------------------------------------------------------------
2236 // Start of ATA/ATAPI Driver
2237 // ---------------------------------------------------------------------------
2238 
2239 // Global defines -- ATA register and register bits.
2240 // command block & control block regs
2241 #define ATA_CB_DATA  0   // data reg         in/out pio_base_addr1+0
2242 #define ATA_CB_ERR   1   // error            in     pio_base_addr1+1
2243 #define ATA_CB_FR    1   // feature reg         out pio_base_addr1+1
2244 #define ATA_CB_SC    2   // sector count     in/out pio_base_addr1+2
2245 #define ATA_CB_SN    3   // sector number    in/out pio_base_addr1+3
2246 #define ATA_CB_CL    4   // cylinder low     in/out pio_base_addr1+4
2247 #define ATA_CB_CH    5   // cylinder high    in/out pio_base_addr1+5
2248 #define ATA_CB_DH    6   // device head      in/out pio_base_addr1+6
2249 #define ATA_CB_STAT  7   // primary status   in     pio_base_addr1+7
2250 #define ATA_CB_CMD   7   // command             out pio_base_addr1+7
2251 #define ATA_CB_ASTAT 6   // alternate status in     pio_base_addr2+6
2252 #define ATA_CB_DC    6   // device control      out pio_base_addr2+6
2253 #define ATA_CB_DA    7   // device address   in     pio_base_addr2+7
2254 
2255 #define ATA_CB_ER_ICRC 0x80    // ATA Ultra DMA bad CRC
2256 #define ATA_CB_ER_BBK  0x80    // ATA bad block
2257 #define ATA_CB_ER_UNC  0x40    // ATA uncorrected error
2258 #define ATA_CB_ER_MC   0x20    // ATA media change
2259 #define ATA_CB_ER_IDNF 0x10    // ATA id not found
2260 #define ATA_CB_ER_MCR  0x08    // ATA media change request
2261 #define ATA_CB_ER_ABRT 0x04    // ATA command aborted
2262 #define ATA_CB_ER_NTK0 0x02    // ATA track 0 not found
2263 #define ATA_CB_ER_NDAM 0x01    // ATA address mark not found
2264 
2265 #define ATA_CB_ER_P_SNSKEY 0xf0   // ATAPI sense key (mask)
2266 #define ATA_CB_ER_P_MCR    0x08   // ATAPI Media Change Request
2267 #define ATA_CB_ER_P_ABRT   0x04   // ATAPI command abort
2268 #define ATA_CB_ER_P_EOM    0x02   // ATAPI End of Media
2269 #define ATA_CB_ER_P_ILI    0x01   // ATAPI Illegal Length Indication
2270 
2271 // ATAPI Interrupt Reason bits in the Sector Count reg (CB_SC)
2272 #define ATA_CB_SC_P_TAG    0xf8   // ATAPI tag (mask)
2273 #define ATA_CB_SC_P_REL    0x04   // ATAPI release
2274 #define ATA_CB_SC_P_IO     0x02   // ATAPI I/O
2275 #define ATA_CB_SC_P_CD     0x01   // ATAPI C/D
2276 
2277 // bits 7-4 of the device/head (CB_DH) reg
2278 #define ATA_CB_DH_DEV0 0xa0    // select device 0
2279 #define ATA_CB_DH_DEV1 0xb0    // select device 1
2280 #define ATA_CB_DH_LBA 0x40    // use LBA
2281 
2282 // status reg (CB_STAT and CB_ASTAT) bits
2283 #define ATA_CB_STAT_BSY  0x80  // busy
2284 #define ATA_CB_STAT_RDY  0x40  // ready
2285 #define ATA_CB_STAT_DF   0x20  // device fault
2286 #define ATA_CB_STAT_WFT  0x20  // write fault (old name)
2287 #define ATA_CB_STAT_SKC  0x10  // seek complete
2288 #define ATA_CB_STAT_SERV 0x10  // service
2289 #define ATA_CB_STAT_DRQ  0x08  // data request
2290 #define ATA_CB_STAT_CORR 0x04  // corrected
2291 #define ATA_CB_STAT_IDX  0x02  // index
2292 #define ATA_CB_STAT_ERR  0x01  // error (ATA)
2293 #define ATA_CB_STAT_CHK  0x01  // check (ATAPI)
2294 
2295 // device control reg (CB_DC) bits
2296 #define ATA_CB_DC_HD15   0x08  // bit should always be set to one
2297 #define ATA_CB_DC_SRST   0x04  // soft reset
2298 #define ATA_CB_DC_NIEN   0x02  // disable interrupts
2299 
2300 // Most mandtory and optional ATA commands (from ATA-3),
2301 #define ATA_CMD_CFA_ERASE_SECTORS            0xC0
2302 #define ATA_CMD_CFA_REQUEST_EXT_ERR_CODE     0x03
2303 #define ATA_CMD_CFA_TRANSLATE_SECTOR         0x87
2304 #define ATA_CMD_CFA_WRITE_MULTIPLE_WO_ERASE  0xCD
2305 #define ATA_CMD_CFA_WRITE_SECTORS_WO_ERASE   0x38
2306 #define ATA_CMD_CHECK_POWER_MODE1            0xE5
2307 #define ATA_CMD_CHECK_POWER_MODE2            0x98
2308 #define ATA_CMD_DEVICE_RESET                 0x08
2309 #define ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC    0x90
2310 #define ATA_CMD_FLUSH_CACHE                  0xE7
2311 #define ATA_CMD_FORMAT_TRACK                 0x50
2312 #define ATA_CMD_IDENTIFY_DEVICE              0xEC
2313 #define ATA_CMD_IDENTIFY_DEVICE_PACKET       0xA1
2314 #define ATA_CMD_IDENTIFY_PACKET_DEVICE       0xA1
2315 #define ATA_CMD_IDLE1                        0xE3
2316 #define ATA_CMD_IDLE2                        0x97
2317 #define ATA_CMD_IDLE_IMMEDIATE1              0xE1
2318 #define ATA_CMD_IDLE_IMMEDIATE2              0x95
2319 #define ATA_CMD_INITIALIZE_DRIVE_PARAMETERS  0x91
2320 #define ATA_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91
2321 #define ATA_CMD_NOP                          0x00
2322 #define ATA_CMD_PACKET                       0xA0
2323 #define ATA_CMD_READ_BUFFER                  0xE4
2324 #define ATA_CMD_READ_DMA                     0xC8
2325 #define ATA_CMD_READ_DMA_QUEUED              0xC7
2326 #define ATA_CMD_READ_MULTIPLE                0xC4
2327 #define ATA_CMD_READ_SECTORS                 0x20
2328 #define ATA_CMD_READ_VERIFY_SECTORS          0x40
2329 #define ATA_CMD_RECALIBRATE                  0x10
2330 #define ATA_CMD_REQUEST_SENSE                0x03
2331 #define ATA_CMD_SEEK                         0x70
2332 #define ATA_CMD_SET_FEATURES                 0xEF
2333 #define ATA_CMD_SET_MULTIPLE_MODE            0xC6
2334 #define ATA_CMD_SLEEP1                       0xE6
2335 #define ATA_CMD_SLEEP2                       0x99
2336 #define ATA_CMD_STANDBY1                     0xE2
2337 #define ATA_CMD_STANDBY2                     0x96
2338 #define ATA_CMD_STANDBY_IMMEDIATE1           0xE0
2339 #define ATA_CMD_STANDBY_IMMEDIATE2           0x94
2340 #define ATA_CMD_WRITE_BUFFER                 0xE8
2341 #define ATA_CMD_WRITE_DMA                    0xCA
2342 #define ATA_CMD_WRITE_DMA_QUEUED             0xCC
2343 #define ATA_CMD_WRITE_MULTIPLE               0xC5
2344 #define ATA_CMD_WRITE_SECTORS                0x30
2345 #define ATA_CMD_WRITE_VERIFY                 0x3C
2346 
2347 #define ATA_IFACE_NONE    0x00
2348 #define ATA_IFACE_ISA     0x00
2349 #define ATA_IFACE_PCI     0x01
2350 
2351 #define ATA_TYPE_NONE     0x00
2352 #define ATA_TYPE_UNKNOWN  0x01
2353 #define ATA_TYPE_ATA      0x02
2354 #define ATA_TYPE_ATAPI    0x03
2355 
2356 #define ATA_DEVICE_NONE  0x00
2357 #define ATA_DEVICE_HD    0xFF
2358 #define ATA_DEVICE_CDROM 0x05
2359 
2360 #define ATA_MODE_NONE    0x00
2361 #define ATA_MODE_PIO16   0x00
2362 #define ATA_MODE_PIO32   0x01
2363 #define ATA_MODE_ISADMA  0x02
2364 #define ATA_MODE_PCIDMA  0x03
2365 #define ATA_MODE_USEIRQ  0x10
2366 
2367 #define ATA_TRANSLATION_NONE  0
2368 #define ATA_TRANSLATION_LBA   1
2369 #define ATA_TRANSLATION_LARGE 2
2370 #define ATA_TRANSLATION_RECHS 3
2371 
2372 #define ATA_DATA_NO      0x00
2373 #define ATA_DATA_IN      0x01
2374 #define ATA_DATA_OUT     0x02
2375 
2376 // ---------------------------------------------------------------------------
2377 // ATA/ATAPI driver : initialization
2378 // ---------------------------------------------------------------------------
2379 void ata_init( )
2380 {
2381   Bit16u ebda_seg=read_word(0x0040,0x000E);
2382   Bit8u  channel, device;
2383 
2384   // Channels info init.
2385   for (channel=0; channel<BX_MAX_ATA_INTERFACES; channel++) {
2386     write_byte(ebda_seg,&EbdaData->ata.channels[channel].iface,ATA_IFACE_NONE);
2387     write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1,0x0);
2388     write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2,0x0);
2389     write_byte(ebda_seg,&EbdaData->ata.channels[channel].irq,0);
2390     }
2391 
2392   // Devices info init.
2393   for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2394     write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
2395     write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_NONE);
2396     write_byte(ebda_seg,&EbdaData->ata.devices[device].removable,0);
2397     write_byte(ebda_seg,&EbdaData->ata.devices[device].lock,0);
2398     write_byte(ebda_seg,&EbdaData->ata.devices[device].mode,ATA_MODE_NONE);
2399     write_word(ebda_seg,&EbdaData->ata.devices[device].blksize,0);
2400     write_byte(ebda_seg,&EbdaData->ata.devices[device].translation,ATA_TRANSLATION_NONE);
2401     write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads,0);
2402     write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders,0);
2403     write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt,0);
2404     write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads,0);
2405     write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders,0);
2406     write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt,0);
2407 
2408     write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low,0L);
2409     write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_high,0L);
2410     }
2411 
2412   // hdidmap  and cdidmap init.
2413   for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2414     write_byte(ebda_seg,&EbdaData->ata.hdidmap[device],BX_MAX_ATA_DEVICES);
2415     write_byte(ebda_seg,&EbdaData->ata.cdidmap[device],BX_MAX_ATA_DEVICES);
2416     }
2417 
2418   write_byte(ebda_seg,&EbdaData->ata.hdcount,0);
2419   write_byte(ebda_seg,&EbdaData->ata.cdcount,0);
2420 }
2421 
2422 #define TIMEOUT 0
2423 #define BSY 1
2424 #define NOT_BSY 2
2425 #define NOT_BSY_DRQ 3
2426 #define NOT_BSY_NOT_DRQ 4
2427 #define NOT_BSY_RDY 5
2428 
2429 #define IDE_TIMEOUT 32000u //32 seconds max for IDE ops
2430 
2431 int await_ide();
2432 static int await_ide(when_done,base,timeout)
2433   Bit8u when_done;
2434   Bit16u base;
2435   Bit16u timeout;
2436 {
2437   Bit32u time=0,last=0;
2438   Bit16u status;
2439   Bit8u result;
2440   status = inb(base + ATA_CB_STAT); // for the times you're supposed to throw one away
2441   for(;;) {
2442     status = inb(base+ATA_CB_STAT);
2443     time++;
2444     if (when_done == BSY)
2445       result = status & ATA_CB_STAT_BSY;
2446     else if (when_done == NOT_BSY)
2447       result = !(status & ATA_CB_STAT_BSY);
2448     else if (when_done == NOT_BSY_DRQ)
2449       result = !(status & ATA_CB_STAT_BSY) && (status & ATA_CB_STAT_DRQ);
2450     else if (when_done == NOT_BSY_NOT_DRQ)
2451       result = !(status & ATA_CB_STAT_BSY) && !(status & ATA_CB_STAT_DRQ);
2452     else if (when_done == NOT_BSY_RDY)
2453       result = !(status & ATA_CB_STAT_BSY) && (status & ATA_CB_STAT_RDY);
2454     else if (when_done == TIMEOUT)
2455       result = 0;
2456 
2457     if (result) return 0;
2458     if (time>>16 != last) // mod 2048 each 16 ms
2459     {
2460       last = time >>16;
2461       BX_DEBUG_ATA("await_ide: (TIMEOUT,BSY,!BSY,!BSY_DRQ,!BSY_!DRQ,!BSY_RDY) %d time= %ld timeout= %d\n",when_done,time>>11, timeout);
2462     }
2463     if (status & ATA_CB_STAT_ERR)
2464     {
2465       BX_DEBUG_ATA("await_ide: ERROR (TIMEOUT,BSY,!BSY,!BSY_DRQ,!BSY_!DRQ,!BSY_RDY) %d time= %ld timeout= %d\n",when_done,time>>11, timeout);
2466       return -1;
2467     }
2468     if ((timeout == 0) || ((time>>11) > timeout)) break;
2469   }
2470   BX_INFO("IDE time out\n");
2471   return -1;
2472 }
2473 
2474 // ---------------------------------------------------------------------------
2475 // ATA/ATAPI driver : device detection
2476 // ---------------------------------------------------------------------------
2477 
2478 void ata_detect( )
2479 {
2480   Bit16u ebda_seg=read_word(0x0040,0x000E);
2481   Bit8u  hdcount, cdcount, device, type;
2482   Bit8u  buffer[0x0200];
2483 
2484 #if BX_MAX_ATA_INTERFACES > 0
2485   write_byte(ebda_seg,&EbdaData->ata.channels[0].iface,ATA_IFACE_ISA);
2486   write_word(ebda_seg,&EbdaData->ata.channels[0].iobase1,0x1f0);
2487   write_word(ebda_seg,&EbdaData->ata.channels[0].iobase2,0x3f0);
2488   write_byte(ebda_seg,&EbdaData->ata.channels[0].irq,14);
2489 #endif
2490 #if BX_MAX_ATA_INTERFACES > 1
2491   write_byte(ebda_seg,&EbdaData->ata.channels[1].iface,ATA_IFACE_ISA);
2492   write_word(ebda_seg,&EbdaData->ata.channels[1].iobase1,0x170);
2493   write_word(ebda_seg,&EbdaData->ata.channels[1].iobase2,0x370);
2494   write_byte(ebda_seg,&EbdaData->ata.channels[1].irq,15);
2495 #endif
2496 #if BX_MAX_ATA_INTERFACES > 2
2497   write_byte(ebda_seg,&EbdaData->ata.channels[2].iface,ATA_IFACE_ISA);
2498   write_word(ebda_seg,&EbdaData->ata.channels[2].iobase1,0x1e8);
2499   write_word(ebda_seg,&EbdaData->ata.channels[2].iobase2,0x3e0);
2500   write_byte(ebda_seg,&EbdaData->ata.channels[2].irq,12);
2501 #endif
2502 #if BX_MAX_ATA_INTERFACES > 3
2503   write_byte(ebda_seg,&EbdaData->ata.channels[3].iface,ATA_IFACE_ISA);
2504   write_word(ebda_seg,&EbdaData->ata.channels[3].iobase1,0x168);
2505   write_word(ebda_seg,&EbdaData->ata.channels[3].iobase2,0x360);
2506   write_byte(ebda_seg,&EbdaData->ata.channels[3].irq,11);
2507 #endif
2508 #if BX_MAX_ATA_INTERFACES > 4
2509 #error Please fill the ATA interface informations
2510 #endif
2511 
2512   // Device detection
2513   hdcount=cdcount=0;
2514 
2515   for(device=0; device<BX_MAX_ATA_DEVICES; device++) {
2516     Bit16u iobase1, iobase2;
2517     Bit8u  channel, slave, shift;
2518     Bit8u  sc, sn, cl, ch, st;
2519 
2520     channel = device / 2;
2521     slave = device % 2;
2522 
2523     iobase1 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1);
2524     iobase2 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2);
2525 
2526     // Disable interrupts
2527     outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2528 
2529     // Look for device
2530     outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2531     outb(iobase1+ATA_CB_SC, 0x55);
2532     outb(iobase1+ATA_CB_SN, 0xaa);
2533     outb(iobase1+ATA_CB_SC, 0xaa);
2534     outb(iobase1+ATA_CB_SN, 0x55);
2535     outb(iobase1+ATA_CB_SC, 0x55);
2536     outb(iobase1+ATA_CB_SN, 0xaa);
2537 
2538     // If we found something
2539     sc = inb(iobase1+ATA_CB_SC);
2540     sn = inb(iobase1+ATA_CB_SN);
2541 
2542     if ( (sc == 0x55) && (sn == 0xaa) ) {
2543       write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_UNKNOWN);
2544 
2545       // reset the channel
2546       ata_reset(device);
2547 
2548       // check for ATA or ATAPI
2549       outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2550       sc = inb(iobase1+ATA_CB_SC);
2551       sn = inb(iobase1+ATA_CB_SN);
2552       if ((sc==0x01) && (sn==0x01)) {
2553         cl = inb(iobase1+ATA_CB_CL);
2554         ch = inb(iobase1+ATA_CB_CH);
2555         st = inb(iobase1+ATA_CB_STAT);
2556 
2557         if ((cl==0x14) && (ch==0xeb)) {
2558           write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATAPI);
2559         } else if ((cl==0x00) && (ch==0x00) && (st!=0x00)) {
2560           write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATA);
2561         } else if ((cl==0xff) && (ch==0xff)) {
2562           write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
2563         }
2564       }
2565     }
2566 
2567     type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type);
2568 
2569     // Now we send a IDENTIFY command to ATA device
2570     if(type == ATA_TYPE_ATA) {
2571       Bit32u sectors_low, sectors_high;
2572       Bit16u cylinders, heads, spt, blksize;
2573       Bit8u  translation, removable, mode;
2574 
2575       //Temporary values to do the transfer
2576       write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2577       write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2578 
2579       if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE, 1, 0, 0, 0, 0L, 0L, get_SS(),buffer) !=0 )
2580         BX_PANIC("ata-detect: Failed to detect ATA device\n");
2581 
2582       removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2583       mode      = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2584       blksize   = read_word(get_SS(),buffer+10);
2585 
2586       cylinders = read_word(get_SS(),buffer+(1*2)); // word 1
2587       heads     = read_word(get_SS(),buffer+(3*2)); // word 3
2588       spt       = read_word(get_SS(),buffer+(6*2)); // word 6
2589 
2590       if (read_word(get_SS(),buffer+(83*2)) & (1 << 10)) { // word 83 - lba48 support
2591         sectors_low  = read_dword(get_SS(),buffer+(100*2)); // word 100 and word 101
2592         sectors_high = read_dword(get_SS(),buffer+(102*2)); // word 102 and word 103
2593       } else {
2594         sectors_low = read_dword(get_SS(),buffer+(60*2)); // word 60 and word 61
2595         sectors_high = 0;
2596       }
2597 
2598       write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2599       write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2600       write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2601       write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2602       write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads, heads);
2603       write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders, cylinders);
2604       write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt, spt);
2605       write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low, sectors_low);
2606       write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_high, sectors_high);
2607       BX_INFO("ata%d-%d: PCHS=%u/%d/%d translation=", channel, slave,cylinders, heads, spt);
2608 
2609       translation = inb_cmos(0x39 + channel/2);
2610       for (shift=device%4; shift>0; shift--) translation >>= 2;
2611       translation &= 0x03;
2612 
2613       write_byte(ebda_seg,&EbdaData->ata.devices[device].translation, translation);
2614 
2615       switch (translation) {
2616         case ATA_TRANSLATION_NONE:
2617           BX_INFO("none");
2618           break;
2619         case ATA_TRANSLATION_LBA:
2620           BX_INFO("lba");
2621           break;
2622         case ATA_TRANSLATION_LARGE:
2623           BX_INFO("large");
2624           break;
2625         case ATA_TRANSLATION_RECHS:
2626           BX_INFO("r-echs");
2627           break;
2628         }
2629       switch (translation) {
2630         case ATA_TRANSLATION_NONE:
2631           break;
2632         case ATA_TRANSLATION_LBA:
2633           spt = 63;
2634           sectors_low /= 63;
2635           heads = sectors_low / 1024;
2636           if (heads>128) heads = 255;
2637           else if (heads>64) heads = 128;
2638           else if (heads>32) heads = 64;
2639           else if (heads>16) heads = 32;
2640           else heads=16;
2641           cylinders = sectors_low / heads;
2642           break;
2643         case ATA_TRANSLATION_RECHS:
2644           // Take care not to overflow
2645           if (heads==16) {
2646             if(cylinders>61439) cylinders=61439;
2647             heads=15;
2648             cylinders = (Bit16u)((Bit32u)(cylinders)*16/15);
2649             }
2650           // then go through the large bitshift process
2651         case ATA_TRANSLATION_LARGE:
2652           while(cylinders > 1024) {
2653             cylinders >>= 1;
2654             heads <<= 1;
2655 
2656             // If we max out the head count
2657             if (heads > 127) break;
2658           }
2659           break;
2660         }
2661       // clip to 1024 cylinders in lchs
2662       if (cylinders > 1024) cylinders=1024;
2663       BX_INFO(" LCHS=%d/%d/%d\n", cylinders, heads, spt);
2664 
2665       write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads, heads);
2666       write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders, cylinders);
2667       write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt, spt);
2668 
2669       // fill hdidmap
2670       write_byte(ebda_seg,&EbdaData->ata.hdidmap[hdcount], device);
2671       hdcount++;
2672       }
2673 
2674     // Now we send a IDENTIFY command to ATAPI device
2675     if(type == ATA_TYPE_ATAPI) {
2676 
2677       Bit8u  type, removable, mode;
2678       Bit16u blksize;
2679 
2680       //Temporary values to do the transfer
2681       write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_CDROM);
2682       write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2683 
2684       if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE_PACKET, 1, 0, 0, 0, 0L, 0L, get_SS(),buffer) != 0)
2685         BX_PANIC("ata-detect: Failed to detect ATAPI device\n");
2686 
2687       type      = read_byte(get_SS(),buffer+1) & 0x1f;
2688       removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2689       mode      = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2690       blksize   = 2048;
2691 
2692       write_byte(ebda_seg,&EbdaData->ata.devices[device].device, type);
2693       write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2694       write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2695       write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2696 
2697       // fill cdidmap
2698       write_byte(ebda_seg,&EbdaData->ata.cdidmap[cdcount], device);
2699       cdcount++;
2700       }
2701 
2702       {
2703       Bit32u sizeinmb;
2704       Bit16u ataversion;
2705       Bit8u  c, i, version, model[41];
2706 
2707       switch (type) {
2708         case ATA_TYPE_ATA:
2709           sizeinmb = (read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_high) << 21)
2710             | (read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low) >> 11);
2711         case ATA_TYPE_ATAPI:
2712           // Read ATA/ATAPI version
2713           ataversion=((Bit16u)(read_byte(get_SS(),buffer+161))<<8)|read_byte(get_SS(),buffer+160);
2714           for(version=15;version>0;version--) {
2715             if((ataversion&(1<<version))!=0)
2716             break;
2717             }
2718 
2719           // Read model name
2720           for(i=0;i<20;i++){
2721             write_byte(get_SS(),model+(i*2),read_byte(get_SS(),buffer+(i*2)+54+1));
2722             write_byte(get_SS(),model+(i*2)+1,read_byte(get_SS(),buffer+(i*2)+54));
2723           }
2724 
2725           // Reformat
2726           write_byte(get_SS(),model+40,0x00);
2727           for(i=39;i>0;i--){
2728             if(read_byte(get_SS(),model+i)==0x20)
2729               write_byte(get_SS(),model+i,0x00);
2730             else break;
2731           }
2732           if (i>36) {
2733             write_byte(get_SS(),model+36,0x00);
2734             for(i=35;i>32;i--){
2735               write_byte(get_SS(),model+i,0x2E);
2736             }
2737           }
2738           break;
2739         }
2740 
2741       switch (type) {
2742         case ATA_TYPE_ATA:
2743           printf("ata%d %s: ",channel,slave?" slave":"master");
2744           i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
2745 	  if (sizeinmb < (1UL<<16))
2746             printf(" ATA-%d Hard-Disk (%4u MBytes)\n", version, (Bit16u)sizeinmb);
2747 	  else
2748             printf(" ATA-%d Hard-Disk (%4u GBytes)\n", version, (Bit16u)(sizeinmb>>10));
2749           break;
2750         case ATA_TYPE_ATAPI:
2751           printf("ata%d %s: ",channel,slave?" slave":"master");
2752           i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
2753           if(read_byte(ebda_seg,&EbdaData->ata.devices[device].device)==ATA_DEVICE_CDROM)
2754             printf(" ATAPI-%d CD-Rom/DVD-Rom\n",version);
2755           else
2756             printf(" ATAPI-%d Device\n",version);
2757           break;
2758         case ATA_TYPE_UNKNOWN:
2759           printf("ata%d %s: Unknown device\n",channel,slave?" slave":"master");
2760           break;
2761         }
2762       }
2763     }
2764 
2765   // Store the devices counts
2766   write_byte(ebda_seg,&EbdaData->ata.hdcount, hdcount);
2767   write_byte(ebda_seg,&EbdaData->ata.cdcount, cdcount);
2768   write_byte(0x40,0x75, hdcount);
2769 
2770   printf("\n");
2771 
2772   // FIXME : should use bios=cmos|auto|disable bits
2773   // FIXME : should know about translation bits
2774   // FIXME : move hard_drive_post here
2775 
2776 }
2777 
2778 // ---------------------------------------------------------------------------
2779 // ATA/ATAPI driver : software reset
2780 // ---------------------------------------------------------------------------
2781 // ATA-3
2782 // 8.2.1 Software reset - Device 0
2783 
2784 void   ata_reset(device)
2785 Bit16u device;
2786 {
2787   Bit16u ebda_seg=read_word(0x0040,0x000E);
2788   Bit16u iobase1, iobase2;
2789   Bit8u  channel, slave, sn, sc;
2790   Bit8u  type;
2791   Bit16u max;
2792 
2793   channel = device / 2;
2794   slave = device % 2;
2795 
2796   iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2797   iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2798 
2799   // Reset
2800 
2801 // 8.2.1 (a) -- set SRST in DC
2802   outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST);
2803 
2804 // 8.2.1 (b) -- wait for BSY
2805   await_ide(BSY, iobase1, 20);
2806 
2807 // 8.2.1 (f) -- clear SRST
2808   outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2809 
2810   type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type);
2811   if (type != ATA_TYPE_NONE) {
2812 
2813 // 8.2.1 (g) -- check for sc==sn==0x01
2814     // select device
2815     outb(iobase1+ATA_CB_DH, slave?ATA_CB_DH_DEV1:ATA_CB_DH_DEV0);
2816     sc = inb(iobase1+ATA_CB_SC);
2817     sn = inb(iobase1+ATA_CB_SN);
2818 
2819     if ( (sc==0x01) && (sn==0x01) ) {
2820       if (type == ATA_TYPE_ATA) //ATA
2821         await_ide(NOT_BSY_RDY, iobase1, IDE_TIMEOUT);
2822       else //ATAPI
2823         await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
2824     }
2825 
2826 // 8.2.1 (h) -- wait for not BSY
2827     await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
2828   }
2829 
2830   // Enable interrupts
2831   outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2832 }
2833 
2834 // ---------------------------------------------------------------------------
2835 // ATA/ATAPI driver : execute a non data command
2836 // ---------------------------------------------------------------------------
2837 
2838 Bit16u ata_cmd_non_data()
2839 {return 0;}
2840 
2841 // ---------------------------------------------------------------------------
2842 // ATA/ATAPI driver : execute a data-in command
2843 // ---------------------------------------------------------------------------
2844       // returns
2845       // 0 : no error
2846       // 1 : BUSY bit set
2847       // 2 : read error
2848       // 3 : expected DRQ=1
2849       // 4 : no sectors left to read/verify
2850       // 5 : more sectors to read/verify
2851       // 6 : no sectors left to write
2852       // 7 : more sectors to write
2853 Bit16u ata_cmd_data_in(device, command, count, cylinder, head, sector, lba_low, lba_high, segment, offset)
2854 Bit16u device, command, count, cylinder, head, sector, segment, offset;
2855 Bit32u lba_low, lba_high;
2856 {
2857   Bit16u ebda_seg=read_word(0x0040,0x000E);
2858   Bit16u iobase1, iobase2, blksize;
2859   Bit8u  channel, slave;
2860   Bit8u  status, current, mode;
2861 
2862   channel = device / 2;
2863   slave   = device % 2;
2864 
2865   iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2866   iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2867   mode    = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2868   blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
2869   if (mode == ATA_MODE_PIO32) blksize>>=2;
2870   else blksize>>=1;
2871 
2872   // Reset count of transferred data
2873   write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2874   write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2875   current = 0;
2876 
2877   status = inb(iobase1 + ATA_CB_STAT);
2878   if (status & ATA_CB_STAT_BSY) return 1;
2879 
2880   outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2881 
2882   // sector will be 0 only on lba access. Convert to lba-chs
2883   if (sector == 0) {
2884     if ((count >= 1 << 8) || lba_high || (lba_low + count >= 1UL << 28)) {
2885       outb(iobase1 + ATA_CB_FR, 0x00);
2886       outb(iobase1 + ATA_CB_SC, (count >> 8) & 0xff);
2887       outb(iobase1 + ATA_CB_SN, lba_low >> 24);
2888       outb(iobase1 + ATA_CB_CL, lba_high & 0xff);
2889       outb(iobase1 + ATA_CB_CH, lba_high >> 8);
2890       command |= 0x04;
2891       count &= (1UL << 8) - 1;
2892       lba_low &= (1UL << 24) - 1;
2893       }
2894     sector = (Bit16u) (lba_low & 0x000000ffL);
2895     cylinder = (Bit16u) ((lba_low>>8) & 0x0000ffffL);
2896     head = ((Bit16u) ((lba_low>>24) & 0x0000000fL)) | ATA_CB_DH_LBA;
2897   }
2898 
2899   outb(iobase1 + ATA_CB_FR, 0x00);
2900   outb(iobase1 + ATA_CB_SC, count);
2901   outb(iobase1 + ATA_CB_SN, sector);
2902   outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
2903   outb(iobase1 + ATA_CB_CH, cylinder >> 8);
2904   outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
2905   outb(iobase1 + ATA_CB_CMD, command);
2906 
2907   await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
2908   status = inb(iobase1 + ATA_CB_STAT);
2909 
2910   if (status & ATA_CB_STAT_ERR) {
2911     BX_DEBUG_ATA("ata_cmd_data_in : read error\n");
2912     return 2;
2913     } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2914     BX_DEBUG_ATA("ata_cmd_data_in : DRQ not set (status %02x)\n", (unsigned) status);
2915     return 3;
2916   }
2917 
2918   // FIXME : move seg/off translation here
2919 
2920 ASM_START
2921         sti  ;; enable higher priority interrupts
2922 ASM_END
2923 
2924   while (1) {
2925 
2926 ASM_START
2927         push bp
2928         mov  bp, sp
2929         mov  di, _ata_cmd_data_in.offset + 2[bp]
2930         mov  ax, _ata_cmd_data_in.segment + 2[bp]
2931         mov  cx, _ata_cmd_data_in.blksize + 2[bp]
2932 
2933         ;; adjust if there will be an overrun. 2K max sector size
2934         cmp   di, #0xf800 ;;
2935         jbe   ata_in_no_adjust
2936 
2937 ata_in_adjust:
2938         sub   di, #0x0800 ;; sub 2 kbytes from offset
2939         add   ax, #0x0080 ;; add 2 Kbytes to segment
2940 
2941 ata_in_no_adjust:
2942         mov   es, ax      ;; segment in es
2943 
2944         mov   dx, _ata_cmd_data_in.iobase1 + 2[bp] ;; ATA data read port
2945 
2946         mov  ah, _ata_cmd_data_in.mode + 2[bp]
2947         cmp  ah, #ATA_MODE_PIO32
2948         je   ata_in_32
2949 
2950 ata_in_16:
2951         rep
2952           insw ;; CX words transfered from port(DX) to ES:[DI]
2953         jmp ata_in_done
2954 
2955 ata_in_32:
2956         rep
2957           insd ;; CX dwords transfered from port(DX) to ES:[DI]
2958 
2959 ata_in_done:
2960         mov  _ata_cmd_data_in.offset + 2[bp], di
2961         mov  _ata_cmd_data_in.segment + 2[bp], es
2962         pop  bp
2963 ASM_END
2964 
2965     current++;
2966     write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
2967     count--;
2968     await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
2969     status = inb(iobase1 + ATA_CB_STAT);
2970     if (count == 0) {
2971       if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2972           != ATA_CB_STAT_RDY ) {
2973         BX_DEBUG_ATA("ata_cmd_data_in : no sectors left (status %02x)\n", (unsigned) status);
2974         return 4;
2975         }
2976       break;
2977       }
2978     else {
2979       if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2980           != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2981         BX_DEBUG_ATA("ata_cmd_data_in : more sectors left (status %02x)\n", (unsigned) status);
2982         return 5;
2983       }
2984       continue;
2985     }
2986   }
2987   // Enable interrupts
2988   outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2989   return 0;
2990 }
2991 
2992 // ---------------------------------------------------------------------------
2993 // ATA/ATAPI driver : execute a data-out command
2994 // ---------------------------------------------------------------------------
2995       // returns
2996       // 0 : no error
2997       // 1 : BUSY bit set
2998       // 2 : read error
2999       // 3 : expected DRQ=1
3000       // 4 : no sectors left to read/verify
3001       // 5 : more sectors to read/verify
3002       // 6 : no sectors left to write
3003       // 7 : more sectors to write
3004 Bit16u ata_cmd_data_out(device, command, count, cylinder, head, sector, lba_low, lba_high, segment, offset)
3005 Bit16u device, command, count, cylinder, head, sector, segment, offset;
3006 Bit32u lba_low, lba_high;
3007 {
3008   Bit16u ebda_seg=read_word(0x0040,0x000E);
3009   Bit16u iobase1, iobase2, blksize;
3010   Bit8u  channel, slave;
3011   Bit8u  status, current, mode;
3012 
3013   channel = device / 2;
3014   slave   = device % 2;
3015 
3016   iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
3017   iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
3018   mode    = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
3019   blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
3020   if (mode == ATA_MODE_PIO32) blksize>>=2;
3021   else blksize>>=1;
3022 
3023   // Reset count of transferred data
3024   write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
3025   write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
3026   current = 0;
3027 
3028   status = inb(iobase1 + ATA_CB_STAT);
3029   if (status & ATA_CB_STAT_BSY) return 1;
3030 
3031   outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
3032 
3033   // sector will be 0 only on lba access. Convert to lba-chs
3034   if (sector == 0) {
3035     if ((count >= 1 << 8) || lba_high || (lba_low + count >= 1UL << 28)) {
3036       outb(iobase1 + ATA_CB_FR, 0x00);
3037       outb(iobase1 + ATA_CB_SC, (count >> 8) & 0xff);
3038       outb(iobase1 + ATA_CB_SN, lba_low >> 24);
3039       outb(iobase1 + ATA_CB_CL, lba_high & 0xff);
3040       outb(iobase1 + ATA_CB_CH, lba_high >> 8);
3041       command |= 0x04;
3042       count &= (1UL << 8) - 1;
3043       lba_low &= (1UL << 24) - 1;
3044       }
3045     sector = (Bit16u) (lba_low & 0x000000ffL);
3046     cylinder = (Bit16u) ((lba_low>>8) & 0x0000ffffL);
3047     head = ((Bit16u) ((lba_low>>24) & 0x0000000fL)) | ATA_CB_DH_LBA;
3048   }
3049 
3050   outb(iobase1 + ATA_CB_FR, 0x00);
3051   outb(iobase1 + ATA_CB_SC, count);
3052   outb(iobase1 + ATA_CB_SN, sector);
3053   outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
3054   outb(iobase1 + ATA_CB_CH, cylinder >> 8);
3055   outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
3056   outb(iobase1 + ATA_CB_CMD, command);
3057 
3058   await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
3059   status = inb(iobase1 + ATA_CB_STAT);
3060 
3061   if (status & ATA_CB_STAT_ERR) {
3062     BX_DEBUG_ATA("ata_cmd_data_out : read error\n");
3063     return 2;
3064     } else if ( !(status & ATA_CB_STAT_DRQ) ) {
3065     BX_DEBUG_ATA("ata_cmd_data_out : DRQ not set (status %02x)\n", (unsigned) status);
3066     return 3;
3067     }
3068 
3069   // FIXME : move seg/off translation here
3070 
3071 ASM_START
3072         sti  ;; enable higher priority interrupts
3073 ASM_END
3074 
3075   while (1) {
3076 
3077 ASM_START
3078         push bp
3079         mov  bp, sp
3080         mov  si, _ata_cmd_data_out.offset + 2[bp]
3081         mov  ax, _ata_cmd_data_out.segment + 2[bp]
3082         mov  cx, _ata_cmd_data_out.blksize + 2[bp]
3083 
3084         ;; adjust if there will be an overrun. 2K max sector size
3085         cmp   si, #0xf800 ;;
3086         jbe   ata_out_no_adjust
3087 
3088 ata_out_adjust:
3089         sub   si, #0x0800 ;; sub 2 kbytes from offset
3090         add   ax, #0x0080 ;; add 2 Kbytes to segment
3091 
3092 ata_out_no_adjust:
3093         mov   es, ax      ;; segment in es
3094 
3095         mov   dx, _ata_cmd_data_out.iobase1 + 2[bp] ;; ATA data write port
3096 
3097         mov  ah, _ata_cmd_data_out.mode + 2[bp]
3098         cmp  ah, #ATA_MODE_PIO32
3099         je   ata_out_32
3100 
3101 ata_out_16:
3102         seg ES
3103         rep
3104           outsw ;; CX words transfered from port(DX) to ES:[SI]
3105         jmp ata_out_done
3106 
3107 ata_out_32:
3108         seg ES
3109         rep
3110           outsd ;; CX dwords transfered from port(DX) to ES:[SI]
3111 
3112 ata_out_done:
3113         mov  _ata_cmd_data_out.offset + 2[bp], si
3114         mov  _ata_cmd_data_out.segment + 2[bp], es
3115         pop  bp
3116 ASM_END
3117 
3118     current++;
3119     write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
3120     count--;
3121     status = inb(iobase1 + ATA_CB_STAT);
3122     if (count == 0) {
3123       if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
3124           != ATA_CB_STAT_RDY ) {
3125         BX_DEBUG_ATA("ata_cmd_data_out : no sectors left (status %02x)\n", (unsigned) status);
3126         return 6;
3127         }
3128       break;
3129       }
3130     else {
3131       if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
3132           != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
3133         BX_DEBUG_ATA("ata_cmd_data_out : more sectors left (status %02x)\n", (unsigned) status);
3134         return 7;
3135       }
3136       continue;
3137     }
3138   }
3139   // Enable interrupts
3140   outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
3141   return 0;
3142 }
3143 
3144 // ---------------------------------------------------------------------------
3145 // ATA/ATAPI driver : execute a packet command
3146 // ---------------------------------------------------------------------------
3147       // returns
3148       // 0 : no error
3149       // 1 : error in parameters
3150       // 2 : BUSY bit set
3151       // 3 : error
3152       // 4 : not ready
3153 Bit16u ata_cmd_packet(device, cmdlen, cmdseg, cmdoff, header, length, inout, bufseg, bufoff)
3154 Bit8u  cmdlen,inout;
3155 Bit16u device,cmdseg, cmdoff, bufseg, bufoff;
3156 Bit16u header;
3157 Bit32u length;
3158 {
3159   Bit16u ebda_seg=read_word(0x0040,0x000E);
3160   Bit16u iobase1, iobase2;
3161   Bit16u lcount, lbefore, lafter, count;
3162   Bit8u  channel, slave;
3163   Bit8u  status, mode, lmode;
3164   Bit32u total, transfer;
3165 
3166   channel = device / 2;
3167   slave = device % 2;
3168 
3169   // Data out is not supported yet
3170   if (inout == ATA_DATA_OUT) {
3171     BX_INFO("ata_cmd_packet: DATA_OUT not supported yet\n");
3172     return 1;
3173     }
3174 
3175   // The header length must be even
3176   if (header & 1) {
3177     BX_DEBUG_ATA("ata_cmd_packet : header must be even (%04x)\n",header);
3178     return 1;
3179     }
3180 
3181   iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
3182   iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
3183   mode    = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
3184   transfer= 0L;
3185 
3186   if (cmdlen < 12) cmdlen=12;
3187   if (cmdlen > 12) cmdlen=16;
3188   cmdlen>>=1;
3189 
3190   // Reset count of transferred data
3191   write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
3192   write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
3193 
3194   status = inb(iobase1 + ATA_CB_STAT);
3195   if (status & ATA_CB_STAT_BSY) return 2;
3196 
3197   outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
3198   outb(iobase1 + ATA_CB_FR, 0x00);
3199   outb(iobase1 + ATA_CB_SC, 0x00);
3200   outb(iobase1 + ATA_CB_SN, 0x00);
3201   outb(iobase1 + ATA_CB_CL, 0xfff0 & 0x00ff);
3202   outb(iobase1 + ATA_CB_CH, 0xfff0 >> 8);
3203   outb(iobase1 + ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
3204   outb(iobase1 + ATA_CB_CMD, ATA_CMD_PACKET);
3205 
3206   // Device should ok to receive command
3207   await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
3208   status = inb(iobase1 + ATA_CB_STAT);
3209 
3210   if (status & ATA_CB_STAT_ERR) {
3211     BX_DEBUG_ATA("ata_cmd_packet : error, status is %02x\n",status);
3212     return 3;
3213     } else if ( !(status & ATA_CB_STAT_DRQ) ) {
3214     BX_DEBUG_ATA("ata_cmd_packet : DRQ not set (status %02x)\n", (unsigned) status);
3215     return 4;
3216     }
3217 
3218   // Normalize address
3219   cmdseg += (cmdoff / 16);
3220   cmdoff %= 16;
3221 
3222   // Send command to device
3223 ASM_START
3224       sti  ;; enable higher priority interrupts
3225 
3226       push bp
3227       mov  bp, sp
3228 
3229       mov  si, _ata_cmd_packet.cmdoff + 2[bp]
3230       mov  ax, _ata_cmd_packet.cmdseg + 2[bp]
3231       mov  cx, _ata_cmd_packet.cmdlen + 2[bp]
3232       mov  es, ax      ;; segment in es
3233 
3234       mov  dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data write port
3235 
3236       seg ES
3237       rep
3238         outsw ;; CX words transfered from port(DX) to ES:[SI]
3239 
3240       pop  bp
3241 ASM_END
3242 
3243   if (inout == ATA_DATA_NO) {
3244     await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
3245     status = inb(iobase1 + ATA_CB_STAT);
3246     }
3247   else {
3248         Bit16u loops = 0;
3249         Bit8u sc;
3250   while (1) {
3251 
3252       if (loops == 0) {//first time through
3253         status = inb(iobase2 + ATA_CB_ASTAT);
3254         await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
3255       }
3256       else
3257         await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
3258       loops++;
3259 
3260       status = inb(iobase1 + ATA_CB_STAT);
3261       sc = inb(iobase1 + ATA_CB_SC);
3262 
3263       // Check if command completed
3264       if(((inb(iobase1 + ATA_CB_SC)&0x7)==0x3) &&
3265          ((status & (ATA_CB_STAT_RDY | ATA_CB_STAT_ERR)) == ATA_CB_STAT_RDY)) break;
3266 
3267       if (status & ATA_CB_STAT_ERR) {
3268         BX_DEBUG_ATA("ata_cmd_packet : error (status %02x)\n",status);
3269         return 3;
3270       }
3271 
3272       // Normalize address
3273       bufseg += (bufoff / 16);
3274       bufoff %= 16;
3275 
3276       // Get the byte count
3277       lcount =  ((Bit16u)(inb(iobase1 + ATA_CB_CH))<<8)+inb(iobase1 + ATA_CB_CL);
3278 
3279       // adjust to read what we want
3280       if(header>lcount) {
3281          lbefore=lcount;
3282          header-=lcount;
3283          lcount=0;
3284          }
3285       else {
3286         lbefore=header;
3287         header=0;
3288         lcount-=lbefore;
3289         }
3290 
3291       if(lcount>length) {
3292         lafter=lcount-length;
3293         lcount=length;
3294         length=0;
3295         }
3296       else {
3297         lafter=0;
3298         length-=lcount;
3299         }
3300 
3301       // Save byte count
3302       count = lcount;
3303 
3304       BX_DEBUG_ATA("Trying to read %04x bytes (%04x %04x %04x) ",lbefore+lcount+lafter,lbefore,lcount,lafter);
3305       BX_DEBUG_ATA("to 0x%04x:0x%04x\n",bufseg,bufoff);
3306 
3307       // If counts not dividable by 4, use 16bits mode
3308       lmode = mode;
3309       if (lbefore & 0x03) lmode=ATA_MODE_PIO16;
3310       if (lcount  & 0x03) lmode=ATA_MODE_PIO16;
3311       if (lafter  & 0x03) lmode=ATA_MODE_PIO16;
3312 
3313       // adds an extra byte if count are odd. before is always even
3314       if (lcount & 0x01) {
3315         lcount+=1;
3316         if ((lafter > 0) && (lafter & 0x01)) {
3317           lafter-=1;
3318           }
3319         }
3320 
3321       if (lmode == ATA_MODE_PIO32) {
3322         lcount>>=2; lbefore>>=2; lafter>>=2;
3323         }
3324       else {
3325         lcount>>=1; lbefore>>=1; lafter>>=1;
3326         }
3327 
3328        ;  // FIXME bcc bug
3329 
3330 ASM_START
3331         push bp
3332         mov  bp, sp
3333 
3334         mov  dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data read port
3335 
3336         mov  cx, _ata_cmd_packet.lbefore + 2[bp]
3337         jcxz ata_packet_no_before
3338 
3339         mov  ah, _ata_cmd_packet.lmode + 2[bp]
3340         cmp  ah, #ATA_MODE_PIO32
3341         je   ata_packet_in_before_32
3342 
3343 ata_packet_in_before_16:
3344         in   ax, dx
3345         loop ata_packet_in_before_16
3346         jmp  ata_packet_no_before
3347 
3348 ata_packet_in_before_32:
3349         push eax
3350 ata_packet_in_before_32_loop:
3351         in   eax, dx
3352         loop ata_packet_in_before_32_loop
3353         pop  eax
3354 
3355 ata_packet_no_before:
3356         mov  cx, _ata_cmd_packet.lcount + 2[bp]
3357         jcxz ata_packet_after
3358 
3359         mov  di, _ata_cmd_packet.bufoff + 2[bp]
3360         mov  ax, _ata_cmd_packet.bufseg + 2[bp]
3361         mov  es, ax
3362 
3363         mov  ah, _ata_cmd_packet.lmode + 2[bp]
3364         cmp  ah, #ATA_MODE_PIO32
3365         je   ata_packet_in_32
3366 
3367 ata_packet_in_16:
3368         rep
3369           insw ;; CX words transfered tp port(DX) to ES:[DI]
3370         jmp ata_packet_after
3371 
3372 ata_packet_in_32:
3373         rep
3374           insd ;; CX dwords transfered to port(DX) to ES:[DI]
3375 
3376 ata_packet_after:
3377         mov  cx, _ata_cmd_packet.lafter + 2[bp]
3378         jcxz ata_packet_done
3379 
3380         mov  ah, _ata_cmd_packet.lmode + 2[bp]
3381         cmp  ah, #ATA_MODE_PIO32
3382         je   ata_packet_in_after_32
3383 
3384 ata_packet_in_after_16:
3385         in   ax, dx
3386         loop ata_packet_in_after_16
3387         jmp  ata_packet_done
3388 
3389 ata_packet_in_after_32:
3390         push eax
3391 ata_packet_in_after_32_loop:
3392         in   eax, dx
3393         loop ata_packet_in_after_32_loop
3394         pop  eax
3395 
3396 ata_packet_done:
3397         pop  bp
3398 ASM_END
3399 
3400       // Compute new buffer address
3401       bufoff += count;
3402 
3403       // Save transferred bytes count
3404       transfer += count;
3405       write_dword(ebda_seg, &EbdaData->ata.trsfbytes,transfer);
3406       }
3407     }
3408 
3409   // Final check, device must be ready
3410   if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
3411          != ATA_CB_STAT_RDY ) {
3412     BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", (unsigned) status);
3413     return 4;
3414     }
3415 
3416   // Enable interrupts
3417   outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
3418   return 0;
3419 }
3420 
3421 // ---------------------------------------------------------------------------
3422 // End of ATA/ATAPI Driver
3423 // ---------------------------------------------------------------------------
3424 
3425 // ---------------------------------------------------------------------------
3426 // Start of ATA/ATAPI generic functions
3427 // ---------------------------------------------------------------------------
3428 
3429   Bit16u
3430 atapi_get_sense(device, seg, asc, ascq)
3431   Bit16u device;
3432 {
3433   Bit8u  atacmd[12];
3434   Bit8u  buffer[18];
3435   Bit8u i;
3436 
3437   memsetb(get_SS(),atacmd,0,12);
3438 
3439   // Request SENSE
3440   atacmd[0]=ATA_CMD_REQUEST_SENSE;
3441   atacmd[4]=sizeof(buffer);
3442   if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 18L, ATA_DATA_IN, get_SS(), buffer) != 0)
3443     return 0x0002;
3444 
3445   write_byte(seg,asc,buffer[12]);
3446   write_byte(seg,ascq,buffer[13]);
3447 
3448   return 0;
3449 }
3450 
3451   Bit16u
3452 atapi_is_ready(device)
3453   Bit16u device;
3454 {
3455   Bit8u packet[12];
3456   Bit8u buf[8];
3457   Bit32u block_len;
3458   Bit32u sectors;
3459   Bit32u timeout; //measured in ms
3460   Bit32u time;
3461   Bit8u asc, ascq;
3462   Bit8u in_progress;
3463   Bit16u ebda_seg = read_word(0x0040,0x000E);
3464   if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI) {
3465     printf("not implemented for non-ATAPI device\n");
3466     return -1;
3467   }
3468 
3469   BX_DEBUG_ATA("ata_detect_medium: begin\n");
3470   memsetb(get_SS(),packet, 0, sizeof packet);
3471   packet[0] = 0x25; /* READ CAPACITY */
3472 
3473   /* Retry READ CAPACITY 50 times unless MEDIUM NOT PRESENT
3474    * is reported by the device. If the device reports "IN PROGRESS",
3475    * 30 seconds is added. */
3476   timeout = 5000;
3477   time = 0;
3478   in_progress = 0;
3479   while (time < timeout) {
3480     if (ata_cmd_packet(device, sizeof(packet), get_SS(), packet, 0, 8L, ATA_DATA_IN, get_SS(), buf) == 0)
3481       goto ok;
3482 
3483     if (atapi_get_sense(device, get_SS(), &asc, &ascq) == 0) {
3484       if (asc == 0x3a) { /* MEDIUM NOT PRESENT */
3485         BX_DEBUG_ATA("Device reports MEDIUM NOT PRESENT\n");
3486         return -1;
3487       }
3488 
3489       if (asc == 0x04 && ascq == 0x01 && !in_progress) {
3490         /* IN PROGRESS OF BECOMING READY */
3491         printf("Waiting for device to detect medium... ");
3492         /* Allow 30 seconds more */
3493         timeout = 30000;
3494         in_progress = 1;
3495       }
3496     }
3497     time += 100;
3498   }
3499   BX_DEBUG_ATA("read capacity failed\n");
3500   return -1;
3501 ok:
3502 
3503   block_len = (Bit32u) buf[4] << 24
3504     | (Bit32u) buf[5] << 16
3505     | (Bit32u) buf[6] << 8
3506     | (Bit32u) buf[7] << 0;
3507   BX_DEBUG_ATA("block_len=%u\n", block_len);
3508 
3509   if (block_len!= 2048 && block_len!= 512)
3510   {
3511     printf("Unsupported sector size %u\n", block_len);
3512     return -1;
3513   }
3514   write_dword(ebda_seg,&EbdaData->ata.devices[device].blksize, block_len);
3515 
3516   sectors = (Bit32u) buf[0] << 24
3517     | (Bit32u) buf[1] << 16
3518     | (Bit32u) buf[2] << 8
3519     | (Bit32u) buf[3] << 0;
3520 
3521   BX_DEBUG_ATA("sectors=%u\n", sectors);
3522   if (block_len == 2048)
3523     sectors <<= 2; /* # of sectors in 512-byte "soft" sector */
3524   if (sectors != read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low))
3525     printf("%dMB medium detected\n", sectors>>(20-9));
3526   write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low, sectors);
3527   return 0;
3528 }
3529 
3530   Bit16u
3531 atapi_is_cdrom(device)
3532   Bit8u device;
3533 {
3534   Bit16u ebda_seg=read_word(0x0040,0x000E);
3535 
3536   if (device >= BX_MAX_ATA_DEVICES)
3537     return 0;
3538 
3539   if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI)
3540     return 0;
3541 
3542   if (read_byte(ebda_seg,&EbdaData->ata.devices[device].device) != ATA_DEVICE_CDROM)
3543     return 0;
3544 
3545   return 1;
3546 }
3547 
3548 // ---------------------------------------------------------------------------
3549 // End of ATA/ATAPI generic functions
3550 // ---------------------------------------------------------------------------
3551 
3552 #endif // BX_USE_ATADRV
3553 
3554 #if BX_ELTORITO_BOOT
3555 
3556 // ---------------------------------------------------------------------------
3557 // Start of El-Torito boot functions
3558 // ---------------------------------------------------------------------------
3559 
3560   void
3561 cdemu_init()
3562 {
3563   Bit16u ebda_seg=read_word(0x0040,0x000E);
3564 
3565   // the only important data is this one for now
3566   write_byte(ebda_seg,&EbdaData->cdemu.active,0x00);
3567 }
3568 
3569   Bit8u
3570 cdemu_isactive()
3571 {
3572   Bit16u ebda_seg=read_word(0x0040,0x000E);
3573 
3574   return(read_byte(ebda_seg,&EbdaData->cdemu.active));
3575 }
3576 
3577   Bit8u
3578 cdemu_emulated_drive()
3579 {
3580   Bit16u ebda_seg=read_word(0x0040,0x000E);
3581 
3582   return(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
3583 }
3584 
3585 static char isotag[6]="CD001";
3586 static char eltorito[24]="EL TORITO SPECIFICATION";
3587 //
3588 // Returns ah: emulated drive, al: error code
3589 //
3590   Bit16u
3591 cdrom_boot()
3592 {
3593   Bit16u ebda_seg=read_word(0x0040,0x000E);
3594   Bit8u  atacmd[12], buffer[2048];
3595   Bit32u lba;
3596   Bit16u boot_segment, nbsectors, i, error;
3597   Bit8u  device;
3598 
3599   // Find out the first cdrom
3600   for (device=0; device<BX_MAX_ATA_DEVICES;device++) {
3601     if (atapi_is_cdrom(device)) break;
3602     }
3603 
3604   // if not found
3605   if(device >= BX_MAX_ATA_DEVICES) return 2;
3606 
3607   if(error = atapi_is_ready(device) != 0)
3608     BX_INFO("ata_is_ready returned %d\n",error);
3609 
3610   // Read the Boot Record Volume Descriptor
3611   memsetb(get_SS(),atacmd,0,12);
3612   atacmd[0]=0x28;                      // READ command
3613   atacmd[7]=(0x01 & 0xff00) >> 8;      // Sectors
3614   atacmd[8]=(0x01 & 0x00ff);           // Sectors
3615   atacmd[2]=(0x11 & 0xff000000) >> 24; // LBA
3616   atacmd[3]=(0x11 & 0x00ff0000) >> 16;
3617   atacmd[4]=(0x11 & 0x0000ff00) >> 8;
3618   atacmd[5]=(0x11 & 0x000000ff);
3619   if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3620     return 3;
3621 
3622   // Validity checks
3623   if(buffer[0]!=0)return 4;
3624   for(i=0;i<5;i++){
3625     if(buffer[1+i]!=read_byte(0xf000,&isotag[i]))return 5;
3626    }
3627   for(i=0;i<23;i++)
3628     if(buffer[7+i]!=read_byte(0xf000,&eltorito[i]))return 6;
3629 
3630   // ok, now we calculate the Boot catalog address
3631   lba=buffer[0x4A]*0x1000000+buffer[0x49]*0x10000+buffer[0x48]*0x100+buffer[0x47];
3632 
3633   // And we read the Boot Catalog
3634   memsetb(get_SS(),atacmd,0,12);
3635   atacmd[0]=0x28;                      // READ command
3636   atacmd[7]=(0x01 & 0xff00) >> 8;      // Sectors
3637   atacmd[8]=(0x01 & 0x00ff);           // Sectors
3638   atacmd[2]=(lba & 0xff000000) >> 24;  // LBA
3639   atacmd[3]=(lba & 0x00ff0000) >> 16;
3640   atacmd[4]=(lba & 0x0000ff00) >> 8;
3641   atacmd[5]=(lba & 0x000000ff);
3642   if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3643     return 7;
3644 
3645   // Validation entry
3646   if(buffer[0x00]!=0x01)return 8;   // Header
3647   if(buffer[0x01]!=0x00)return 9;   // Platform
3648   if(buffer[0x1E]!=0x55)return 10;  // key 1
3649   if(buffer[0x1F]!=0xAA)return 10;  // key 2
3650 
3651   // Initial/Default Entry
3652   if(buffer[0x20]!=0x88)return 11; // Bootable
3653 
3654   write_byte(ebda_seg,&EbdaData->cdemu.media,buffer[0x21]);
3655   if(buffer[0x21]==0){
3656     // FIXME ElTorito Hardcoded. cdrom is hardcoded as device 0xE0.
3657     // Win2000 cd boot needs to know it booted from cd
3658     write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0xE0);
3659     }
3660   else if(buffer[0x21]<4)
3661     write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x00);
3662   else
3663     write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x80);
3664 
3665   write_byte(ebda_seg,&EbdaData->cdemu.controller_index,device/2);
3666   write_byte(ebda_seg,&EbdaData->cdemu.device_spec,device%2);
3667 
3668   boot_segment=buffer[0x23]*0x100+buffer[0x22];
3669   if(boot_segment==0x0000)boot_segment=0x07C0;
3670 
3671   write_word(ebda_seg,&EbdaData->cdemu.load_segment,boot_segment);
3672   write_word(ebda_seg,&EbdaData->cdemu.buffer_segment,0x0000);
3673 
3674   nbsectors=buffer[0x27]*0x100+buffer[0x26];
3675   write_word(ebda_seg,&EbdaData->cdemu.sector_count,nbsectors);
3676 
3677   lba=buffer[0x2B]*0x1000000+buffer[0x2A]*0x10000+buffer[0x29]*0x100+buffer[0x28];
3678   write_dword(ebda_seg,&EbdaData->cdemu.ilba,lba);
3679 
3680   // And we read the image in memory
3681   memsetb(get_SS(),atacmd,0,12);
3682   atacmd[0]=0x28;                      // READ command
3683   atacmd[7]=((1+(nbsectors-1)/4) & 0xff00) >> 8;      // Sectors
3684   atacmd[8]=((1+(nbsectors-1)/4) & 0x00ff);           // Sectors
3685   atacmd[2]=(lba & 0xff000000) >> 24;  // LBA
3686   atacmd[3]=(lba & 0x00ff0000) >> 16;
3687   atacmd[4]=(lba & 0x0000ff00) >> 8;
3688   atacmd[5]=(lba & 0x000000ff);
3689   if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, nbsectors*512L, ATA_DATA_IN, boot_segment,0)) != 0)
3690     return 12;
3691 
3692   // Remember the media type
3693   switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
3694     case 0x01:  // 1.2M floppy
3695       write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,15);
3696       write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3697       write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3698       break;
3699     case 0x02:  // 1.44M floppy
3700       write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,18);
3701       write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3702       write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3703       break;
3704     case 0x03:  // 2.88M floppy
3705       write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,36);
3706       write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3707       write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3708       break;
3709     case 0x04:  // Harddrive
3710       write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,read_byte(boot_segment,446+6)&0x3f);
3711       write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,
3712               (read_byte(boot_segment,446+6)<<2) + read_byte(boot_segment,446+7) + 1);
3713       write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,read_byte(boot_segment,446+5) + 1);
3714       break;
3715    }
3716 
3717   if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0) {
3718     // Increase bios installed hardware number of devices
3719     if(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)==0x00)
3720       write_byte(0x40,0x10,read_byte(0x40,0x10)|0x41);
3721     else
3722       write_byte(ebda_seg, &EbdaData->ata.hdcount, read_byte(ebda_seg, &EbdaData->ata.hdcount) + 1);
3723    }
3724 
3725 
3726   // everything is ok, so from now on, the emulation is active
3727   if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0)
3728     write_byte(ebda_seg,&EbdaData->cdemu.active,0x01);
3729 
3730   // return the boot drive + no error
3731   return (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)*0x100)+0;
3732 }
3733 
3734 // ---------------------------------------------------------------------------
3735 // End of El-Torito boot functions
3736 // ---------------------------------------------------------------------------
3737 #endif // BX_ELTORITO_BOOT
3738 
3739   void
3740 int14_function(regs, ds, iret_addr)
3741   pusha_regs_t regs; // regs pushed from PUSHA instruction
3742   Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
3743   iret_addr_t  iret_addr; // CS,IP,Flags pushed from original INT call
3744 {
3745   Bit16u addr,timer,val16;
3746   Bit8u timeout;
3747 
3748   ASM_START
3749   sti
3750   ASM_END
3751 
3752   addr = read_word(0x0040, (regs.u.r16.dx << 1));
3753   timeout = read_byte(0x0040, 0x007C + regs.u.r16.dx);
3754   if ((regs.u.r16.dx < 4) && (addr > 0)) {
3755     switch (regs.u.r8.ah) {
3756       case 0:
3757         outb(addr+3, inb(addr+3) | 0x80);
3758         if (regs.u.r8.al & 0xE0 == 0) {
3759           outb(addr, 0x17);
3760           outb(addr+1, 0x04);
3761         } else {
3762           val16 = 0x600 >> ((regs.u.r8.al & 0xE0) >> 5);
3763           outb(addr, val16 & 0xFF);
3764           outb(addr+1, val16 >> 8);
3765         }
3766         outb(addr+3, regs.u.r8.al & 0x1F);
3767         regs.u.r8.ah = inb(addr+5);
3768         regs.u.r8.al = inb(addr+6);
3769         ClearCF(iret_addr.flags);
3770         break;
3771       case 1:
3772         timer = read_word(0x0040, 0x006C);
3773         while (((inb(addr+5) & 0x60) != 0x60) && (timeout)) {
3774           val16 = read_word(0x0040, 0x006C);
3775           if (val16 != timer) {
3776             timer = val16;
3777             timeout--;
3778             }
3779           }
3780         if (timeout) outb(addr, regs.u.r8.al);
3781         regs.u.r8.ah = inb(addr+5);
3782         if (!timeout) regs.u.r8.ah |= 0x80;
3783         ClearCF(iret_addr.flags);
3784         break;
3785       case 2:
3786         timer = read_word(0x0040, 0x006C);
3787         while (((inb(addr+5) & 0x01) == 0) && (timeout)) {
3788           val16 = read_word(0x0040, 0x006C);
3789           if (val16 != timer) {
3790             timer = val16;
3791             timeout--;
3792             }
3793           }
3794         if (timeout) {
3795           regs.u.r8.ah = 0;
3796           regs.u.r8.al = inb(addr);
3797         } else {
3798           regs.u.r8.ah = inb(addr+5);
3799           }
3800         ClearCF(iret_addr.flags);
3801         break;
3802       case 3:
3803         regs.u.r8.ah = inb(addr+5);
3804         regs.u.r8.al = inb(addr+6);
3805         ClearCF(iret_addr.flags);
3806         break;
3807       default:
3808         SetCF(iret_addr.flags); // Unsupported
3809       }
3810   } else {
3811     SetCF(iret_addr.flags); // Unsupported
3812     }
3813 }
3814 
3815   void
3816 int15_function(regs, ES, DS, FLAGS)
3817   pusha_regs_t regs; // REGS pushed via pusha
3818   Bit16u ES, DS, FLAGS;
3819 {
3820   Bit16u ebda_seg=read_word(0x0040,0x000E);
3821   bx_bool prev_a20_enable;
3822   Bit16u  base15_00;
3823   Bit8u   base23_16;
3824   Bit16u  ss;
3825   Bit16u  CX,DX;
3826 
3827   Bit16u bRegister;
3828   Bit8u irqDisable;
3829 
3830 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3831 
3832   switch (regs.u.r8.ah) {
3833     case 0x24: /* A20 Control */
3834       switch (regs.u.r8.al) {
3835         case 0x00:
3836           set_enable_a20(0);
3837           CLEAR_CF();
3838           regs.u.r8.ah = 0;
3839           break;
3840         case 0x01:
3841           set_enable_a20(1);
3842           CLEAR_CF();
3843           regs.u.r8.ah = 0;
3844           break;
3845         case 0x02:
3846           regs.u.r8.al = (inb(0x92) >> 1) & 0x01;
3847           CLEAR_CF();
3848           regs.u.r8.ah = 0;
3849           break;
3850         case 0x03:
3851           CLEAR_CF();
3852           regs.u.r8.ah = 0;
3853           regs.u.r16.bx = 3;
3854           break;
3855         default:
3856           BX_INFO("int15: Func 24h, subfunc %02xh, A20 gate control not supported\n", (unsigned) regs.u.r8.al);
3857           SET_CF();
3858           regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3859       }
3860       break;
3861 
3862     case 0x41:
3863       SET_CF();
3864       regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3865       break;
3866 
3867     case 0x4f:
3868       /* keyboard intercept */
3869 #if BX_CPU < 2
3870       regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3871 #else
3872       // nop
3873 #endif
3874       SET_CF();
3875       break;
3876 
3877     case 0x52:    // removable media eject
3878       CLEAR_CF();
3879       regs.u.r8.ah = 0;  // "ok ejection may proceed"
3880       break;
3881 
3882     case 0x83: {
3883       if( regs.u.r8.al == 0 ) {
3884         // Set Interval requested.
3885         if( ( read_byte( 0x40, 0xA0 ) & 1 ) == 0 ) {
3886           // Interval not already set.
3887           write_byte( 0x40, 0xA0, 1 );  // Set status byte.
3888           write_word( 0x40, 0x98, ES ); // Byte location, segment
3889           write_word( 0x40, 0x9A, regs.u.r16.bx ); // Byte location, offset
3890           write_word( 0x40, 0x9C, regs.u.r16.dx ); // Low word, delay
3891           write_word( 0x40, 0x9E, regs.u.r16.cx ); // High word, delay.
3892           CLEAR_CF( );
3893           irqDisable = inb( 0xA1 );
3894           outb( 0xA1, irqDisable & 0xFE );
3895           bRegister = inb_cmos( 0xB );  // Unmask IRQ8 so INT70 will get through.
3896           outb_cmos( 0xB, bRegister | 0x40 ); // Turn on the Periodic Interrupt timer
3897         } else {
3898           // Interval already set.
3899           BX_DEBUG_INT15("int15: Func 83h, failed, already waiting.\n" );
3900           SET_CF();
3901           regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3902         }
3903       } else if( regs.u.r8.al == 1 ) {
3904         // Clear Interval requested
3905         write_byte( 0x40, 0xA0, 0 );  // Clear status byte
3906         CLEAR_CF( );
3907         bRegister = inb_cmos( 0xB );
3908         outb_cmos( 0xB, bRegister & ~0x40 );  // Turn off the Periodic Interrupt timer
3909       } else {
3910         BX_DEBUG_INT15("int15: Func 83h, failed.\n" );
3911         SET_CF();
3912         regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3913         regs.u.r8.al--;
3914       }
3915 
3916       break;
3917     }
3918 
3919     case 0x87:
3920 #if BX_CPU < 3
3921 #  error "Int15 function 87h not supported on < 80386"
3922 #endif
3923       // +++ should probably have descriptor checks
3924       // +++ should have exception handlers
3925 
3926  // turn off interrupts
3927 ASM_START
3928   cli
3929 ASM_END
3930 
3931       prev_a20_enable = set_enable_a20(1); // enable A20 line
3932 
3933       // 128K max of transfer on 386+ ???
3934       // source == destination ???
3935 
3936       // ES:SI points to descriptor table
3937       // offset   use     initially  comments
3938       // ==============================================
3939       // 00..07   Unused  zeros      Null descriptor
3940       // 08..0f   GDT     zeros      filled in by BIOS
3941       // 10..17   source  ssssssss   source of data
3942       // 18..1f   dest    dddddddd   destination of data
3943       // 20..27   CS      zeros      filled in by BIOS
3944       // 28..2f   SS      zeros      filled in by BIOS
3945 
3946       //es:si
3947       //eeee0
3948       //0ssss
3949       //-----
3950 
3951 // check for access rights of source & dest here
3952 
3953       // Initialize GDT descriptor
3954       base15_00 = (ES << 4) + regs.u.r16.si;
3955       base23_16 = ES >> 12;
3956       if (base15_00 < (ES<<4))
3957         base23_16++;
3958       write_word(ES, regs.u.r16.si+0x08+0, 47);       // limit 15:00 = 6 * 8bytes/descriptor
3959       write_word(ES, regs.u.r16.si+0x08+2, base15_00);// base 15:00
3960       write_byte(ES, regs.u.r16.si+0x08+4, base23_16);// base 23:16
3961       write_byte(ES, regs.u.r16.si+0x08+5, 0x93);     // access
3962       write_word(ES, regs.u.r16.si+0x08+6, 0x0000);   // base 31:24/reserved/limit 19:16
3963 
3964       // Initialize CS descriptor
3965       write_word(ES, regs.u.r16.si+0x20+0, 0xffff);// limit 15:00 = normal 64K limit
3966       write_word(ES, regs.u.r16.si+0x20+2, 0x0000);// base 15:00
3967       write_byte(ES, regs.u.r16.si+0x20+4, 0x000f);// base 23:16
3968       write_byte(ES, regs.u.r16.si+0x20+5, 0x9b);  // access
3969       write_word(ES, regs.u.r16.si+0x20+6, 0x0000);// base 31:24/reserved/limit 19:16
3970 
3971       // Initialize SS descriptor
3972       ss = get_SS();
3973       base15_00 = ss << 4;
3974       base23_16 = ss >> 12;
3975       write_word(ES, regs.u.r16.si+0x28+0, 0xffff);   // limit 15:00 = normal 64K limit
3976       write_word(ES, regs.u.r16.si+0x28+2, base15_00);// base 15:00
3977       write_byte(ES, regs.u.r16.si+0x28+4, base23_16);// base 23:16
3978       write_byte(ES, regs.u.r16.si+0x28+5, 0x93);     // access
3979       write_word(ES, regs.u.r16.si+0x28+6, 0x0000);   // base 31:24/reserved/limit 19:16
3980 
3981       CX = regs.u.r16.cx;
3982 ASM_START
3983       // Compile generates locals offset info relative to SP.
3984       // Get CX (word count) from stack.
3985       mov  bx, sp
3986       SEG SS
3987         mov  cx, _int15_function.CX [bx]
3988 
3989       // since we need to set SS:SP, save them to the BDA
3990       // for future restore
3991       push eax
3992       xor eax, eax
3993       mov ds, ax
3994       mov 0x0469, ss
3995       mov 0x0467, sp
3996 
3997       SEG ES
3998         lgdt [si + 0x08]
3999       SEG CS
4000         lidt [pmode_IDT_info]
4001       ;;  perhaps do something with IDT here
4002 
4003       ;; set PE bit in CR0
4004       mov  eax, cr0
4005       or   al, #0x01
4006       mov  cr0, eax
4007       ;; far jump to flush CPU queue after transition to protected mode
4008       JMP_AP(0x0020, protected_mode)
4009 
4010 protected_mode:
4011       ;; GDT points to valid descriptor table, now load SS, DS, ES
4012       mov  ax, #0x28 ;; 101 000 = 5th descriptor in table, TI=GDT, RPL=00
4013       mov  ss, ax
4014       mov  ax, #0x10 ;; 010 000 = 2nd descriptor in table, TI=GDT, RPL=00
4015       mov  ds, ax
4016       mov  ax, #0x18 ;; 011 000 = 3rd descriptor in table, TI=GDT, RPL=00
4017       mov  es, ax
4018       xor  si, si
4019       xor  di, di
4020       cld
4021       rep
4022         movsw  ;; move CX words from DS:SI to ES:DI
4023 
4024       ;; make sure DS and ES limits are 64KB
4025       mov ax, #0x28
4026       mov ds, ax
4027       mov es, ax
4028 
4029       ;; reset PG bit in CR0 ???
4030       mov  eax, cr0
4031       and  al, #0xFE
4032       mov  cr0, eax
4033 
4034       ;; far jump to flush CPU queue after transition to real mode
4035       JMP_AP(0xf000, real_mode)
4036 
4037 real_mode:
4038       ;; restore IDT to normal real-mode defaults
4039       SEG CS
4040         lidt [rmode_IDT_info]
4041 
4042       // restore SS:SP from the BDA
4043       xor ax, ax
4044       mov ds, ax
4045       mov ss, 0x0469
4046       mov sp, 0x0467
4047       pop eax
4048 ASM_END
4049 
4050       set_enable_a20(prev_a20_enable);
4051 
4052  // turn back on interrupts
4053 ASM_START
4054   sti
4055 ASM_END
4056 
4057       regs.u.r8.ah = 0;
4058       CLEAR_CF();
4059       break;
4060 
4061 
4062     case 0x88:
4063       // Get the amount of extended memory (above 1M)
4064 #if BX_CPU < 2
4065       regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4066       SET_CF();
4067 #else
4068       regs.u.r8.al = inb_cmos(0x30);
4069       regs.u.r8.ah = inb_cmos(0x31);
4070 
4071       // According to Ralf Brown's interrupt the limit should be 15M,
4072       // but real machines mostly return max. 63M.
4073       if(regs.u.r16.ax > 0xffc0)
4074         regs.u.r16.ax = 0xffc0;
4075 
4076       CLEAR_CF();
4077 #endif
4078       break;
4079 
4080     case 0x90:
4081       /* Device busy interrupt.  Called by Int 16h when no key available */
4082       break;
4083 
4084     case 0x91:
4085       /* Interrupt complete.  Called by Int 16h when key becomes available */
4086       break;
4087 
4088     case 0xbf:
4089       BX_INFO("*** int 15h function AH=bf not yet supported!\n");
4090       SET_CF();
4091       regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4092       break;
4093 
4094     case 0xC0:
4095 #if 0
4096       SET_CF();
4097       regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4098       break;
4099 #endif
4100       CLEAR_CF();
4101       regs.u.r8.ah = 0;
4102       regs.u.r16.bx =  BIOS_CONFIG_TABLE;
4103       ES = 0xF000;
4104       break;
4105 
4106     case 0xc1:
4107       ES = ebda_seg;
4108       CLEAR_CF();
4109       break;
4110 
4111     case 0xd8:
4112       bios_printf(BIOS_PRINTF_DEBUG, "EISA BIOS not present\n");
4113       SET_CF();
4114       regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4115       break;
4116 
4117     default:
4118       BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4119         (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4120       SET_CF();
4121       regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4122       break;
4123     }
4124 }
4125 
4126 #if BX_USE_PS2_MOUSE
4127   void
4128 int15_function_mouse(regs, ES, DS, FLAGS)
4129   pusha_regs_t regs; // REGS pushed via pusha
4130   Bit16u ES, DS, FLAGS;
4131 {
4132   Bit16u ebda_seg=read_word(0x0040,0x000E);
4133   Bit8u  mouse_flags_1, mouse_flags_2;
4134   Bit16u mouse_driver_seg;
4135   Bit16u mouse_driver_offset;
4136   Bit8u  comm_byte, prev_command_byte;
4137   Bit8u  ret, mouse_data1, mouse_data2, mouse_data3;
4138 
4139 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
4140 
4141   switch (regs.u.r8.ah) {
4142     case 0xC2:
4143       // Return Codes status in AH
4144       // =========================
4145       // 00: success
4146       // 01: invalid subfunction (AL > 7)
4147       // 02: invalid input value (out of allowable range)
4148       // 03: interface error
4149       // 04: resend command received from mouse controller,
4150       //     device driver should attempt command again
4151       // 05: cannot enable mouse, since no far call has been installed
4152       // 80/86: mouse service not implemented
4153 
4154       switch (regs.u.r8.al) {
4155         case 0: // Disable/Enable Mouse
4156 BX_DEBUG_INT15("case 0:\n");
4157           switch (regs.u.r8.bh) {
4158             case 0: // Disable Mouse
4159 BX_DEBUG_INT15("case 0: disable mouse\n");
4160               inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4161               ret = send_to_mouse_ctrl(0xF5); // disable mouse command
4162               if (ret == 0) {
4163                 ret = get_mouse_data(&mouse_data1);
4164                 if ( (ret == 0) || (mouse_data1 == 0xFA) ) {
4165                   CLEAR_CF();
4166                   regs.u.r8.ah = 0;
4167                   return;
4168                   }
4169                 }
4170 
4171               // error
4172               SET_CF();
4173               regs.u.r8.ah = ret;
4174               return;
4175               break;
4176 
4177             case 1: // Enable Mouse
4178 BX_DEBUG_INT15("case 1: enable mouse\n");
4179               mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4180               if ( (mouse_flags_2 & 0x80) == 0 ) {
4181                 BX_DEBUG_INT15("INT 15h C2 Enable Mouse, no far call handler\n");
4182                 SET_CF();  // error
4183                 regs.u.r8.ah = 5; // no far call installed
4184                 return;
4185                 }
4186               inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4187               ret = send_to_mouse_ctrl(0xF4); // enable mouse command
4188               if (ret == 0) {
4189                 ret = get_mouse_data(&mouse_data1);
4190                 if ( (ret == 0) && (mouse_data1 == 0xFA) ) {
4191                   enable_mouse_int_and_events(); // turn IRQ12 and packet generation on
4192                   CLEAR_CF();
4193                   regs.u.r8.ah = 0;
4194                   return;
4195                   }
4196                 }
4197               SET_CF();
4198               regs.u.r8.ah = ret;
4199               return;
4200 
4201             default: // invalid subfunction
4202               BX_DEBUG_INT15("INT 15h C2 AL=0, BH=%02x\n", (unsigned) regs.u.r8.bh);
4203               SET_CF();  // error
4204               regs.u.r8.ah = 1; // invalid subfunction
4205               return;
4206             }
4207           break;
4208 
4209         case 1: // Reset Mouse
4210         case 5: // Initialize Mouse
4211 BX_DEBUG_INT15("case 1 or 5:\n");
4212           if (regs.u.r8.al == 5) {
4213             if (regs.u.r8.bh != 3) {
4214               SET_CF();
4215               regs.u.r8.ah = 0x02; // invalid input
4216               return;
4217             }
4218             mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4219             mouse_flags_2 = (mouse_flags_2 & 0x00) | regs.u.r8.bh;
4220             mouse_flags_1 = 0x00;
4221             write_byte(ebda_seg, 0x0026, mouse_flags_1);
4222             write_byte(ebda_seg, 0x0027, mouse_flags_2);
4223           }
4224 
4225           inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4226           ret = send_to_mouse_ctrl(0xFF); // reset mouse command
4227           if (ret == 0) {
4228             ret = get_mouse_data(&mouse_data3);
4229             // if no mouse attached, it will return RESEND
4230             if (mouse_data3 == 0xfe) {
4231               SET_CF();
4232               return;
4233             }
4234             if (mouse_data3 != 0xfa)
4235               BX_PANIC("Mouse reset returned %02x (should be ack)\n", (unsigned)mouse_data3);
4236             if ( ret == 0 ) {
4237               ret = get_mouse_data(&mouse_data1);
4238               if ( ret == 0 ) {
4239                 ret = get_mouse_data(&mouse_data2);
4240                 if ( ret == 0 ) {
4241                   // turn IRQ12 and packet generation on
4242                   enable_mouse_int_and_events();
4243                   CLEAR_CF();
4244                   regs.u.r8.ah = 0;
4245                   regs.u.r8.bl = mouse_data1;
4246                   regs.u.r8.bh = mouse_data2;
4247                   return;
4248                   }
4249                 }
4250               }
4251             }
4252 
4253           // error
4254           SET_CF();
4255           regs.u.r8.ah = ret;
4256           return;
4257 
4258         case 2: // Set Sample Rate
4259 BX_DEBUG_INT15("case 2:\n");
4260           switch (regs.u.r8.bh) {
4261             case 0: mouse_data1 = 10; break; //  10 reports/sec
4262             case 1: mouse_data1 = 20; break; //  20 reports/sec
4263             case 2: mouse_data1 = 40; break; //  40 reports/sec
4264             case 3: mouse_data1 = 60; break; //  60 reports/sec
4265             case 4: mouse_data1 = 80; break; //  80 reports/sec
4266             case 5: mouse_data1 = 100; break; // 100 reports/sec (default)
4267             case 6: mouse_data1 = 200; break; // 200 reports/sec
4268             default: mouse_data1 = 0;
4269           }
4270           if (mouse_data1 > 0) {
4271             ret = send_to_mouse_ctrl(0xF3); // set sample rate command
4272             if (ret == 0) {
4273               ret = get_mouse_data(&mouse_data2);
4274               ret = send_to_mouse_ctrl(mouse_data1);
4275               ret = get_mouse_data(&mouse_data2);
4276               CLEAR_CF();
4277               regs.u.r8.ah = 0;
4278             } else {
4279               // error
4280               SET_CF();
4281               regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4282             }
4283           } else {
4284             // error
4285             SET_CF();
4286             regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4287           }
4288           break;
4289 
4290         case 3: // Set Resolution
4291 BX_DEBUG_INT15("case 3:\n");
4292           // BH:
4293           //      0 =  25 dpi, 1 count  per millimeter
4294           //      1 =  50 dpi, 2 counts per millimeter
4295           //      2 = 100 dpi, 4 counts per millimeter
4296           //      3 = 200 dpi, 8 counts per millimeter
4297           comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4298           if (regs.u.r8.bh < 4) {
4299             ret = send_to_mouse_ctrl(0xE8); // set resolution command
4300             if (ret == 0) {
4301               ret = get_mouse_data(&mouse_data1);
4302               if (mouse_data1 != 0xfa)
4303                 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
4304               ret = send_to_mouse_ctrl(regs.u.r8.bh);
4305               ret = get_mouse_data(&mouse_data1);
4306               if (mouse_data1 != 0xfa)
4307                 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
4308               CLEAR_CF();
4309               regs.u.r8.ah = 0;
4310             } else {
4311               // error
4312               SET_CF();
4313               regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4314             }
4315           } else {
4316             // error
4317             SET_CF();
4318             regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4319           }
4320           set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4321           break;
4322 
4323         case 4: // Get Device ID
4324 BX_DEBUG_INT15("case 4:\n");
4325           inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4326           ret = send_to_mouse_ctrl(0xF2); // get mouse ID command
4327           if (ret == 0) {
4328             ret = get_mouse_data(&mouse_data1);
4329             ret = get_mouse_data(&mouse_data2);
4330             CLEAR_CF();
4331             regs.u.r8.ah = 0;
4332             regs.u.r8.bh = mouse_data2;
4333           } else {
4334             // error
4335             SET_CF();
4336             regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4337           }
4338           break;
4339 
4340         case 6: // Return Status & Set Scaling Factor...
4341 BX_DEBUG_INT15("case 6:\n");
4342           switch (regs.u.r8.bh) {
4343             case 0: // Return Status
4344               comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4345               ret = send_to_mouse_ctrl(0xE9); // get mouse info command
4346               if (ret == 0) {
4347                 ret = get_mouse_data(&mouse_data1);
4348                 if (mouse_data1 != 0xfa)
4349                   BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
4350                 if (ret == 0) {
4351                   ret = get_mouse_data(&mouse_data1);
4352                   if ( ret == 0 ) {
4353                     ret = get_mouse_data(&mouse_data2);
4354                     if ( ret == 0 ) {
4355                       ret = get_mouse_data(&mouse_data3);
4356                       if ( ret == 0 ) {
4357                         CLEAR_CF();
4358                         regs.u.r8.ah = 0;
4359                         regs.u.r8.bl = mouse_data1;
4360                         regs.u.r8.cl = mouse_data2;
4361                         regs.u.r8.dl = mouse_data3;
4362                         set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4363                         return;
4364                         }
4365                       }
4366                     }
4367                   }
4368                 }
4369 
4370               // error
4371               SET_CF();
4372               regs.u.r8.ah = ret;
4373               set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4374               return;
4375 
4376             case 1: // Set Scaling Factor to 1:1
4377             case 2: // Set Scaling Factor to 2:1
4378               comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4379               if (regs.u.r8.bh == 1) {
4380                 ret = send_to_mouse_ctrl(0xE6);
4381               } else {
4382                 ret = send_to_mouse_ctrl(0xE7);
4383               }
4384               if (ret == 0) {
4385                 get_mouse_data(&mouse_data1);
4386                 ret = (mouse_data1 != 0xFA);
4387               }
4388               if (ret == 0) {
4389                 CLEAR_CF();
4390                 regs.u.r8.ah = 0;
4391               } else {
4392                 // error
4393                 SET_CF();
4394                 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4395               }
4396               set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4397               break;
4398 
4399             default:
4400               BX_PANIC("INT 15h C2 AL=6, BH=%02x\n", (unsigned) regs.u.r8.bh);
4401             }
4402           break;
4403 
4404         case 7: // Set Mouse Handler Address
4405 BX_DEBUG_INT15("case 7:\n");
4406           mouse_driver_seg = ES;
4407           mouse_driver_offset = regs.u.r16.bx;
4408           write_word(ebda_seg, 0x0022, mouse_driver_offset);
4409           write_word(ebda_seg, 0x0024, mouse_driver_seg);
4410           mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4411           if (mouse_driver_offset == 0 && mouse_driver_seg == 0) {
4412             /* remove handler */
4413             if ( (mouse_flags_2 & 0x80) != 0 ) {
4414               mouse_flags_2 &= ~0x80;
4415               inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4416               }
4417             }
4418           else {
4419             /* install handler */
4420             mouse_flags_2 |= 0x80;
4421             }
4422           write_byte(ebda_seg, 0x0027, mouse_flags_2);
4423           CLEAR_CF();
4424           regs.u.r8.ah = 0;
4425           break;
4426 
4427         default:
4428 BX_DEBUG_INT15("case default:\n");
4429           regs.u.r8.ah = 1; // invalid function
4430           SET_CF();
4431         }
4432       break;
4433 
4434     default:
4435       BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4436         (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4437       SET_CF();
4438       regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4439       break;
4440     }
4441 }
4442 #endif // BX_USE_PS2_MOUSE
4443 
4444 
4445 void set_e820_range(ES, DI, start, end, extra_start, extra_end, type)
4446      Bit16u ES;
4447      Bit16u DI;
4448      Bit32u start;
4449      Bit32u end;
4450      Bit8u extra_start;
4451      Bit8u extra_end;
4452      Bit16u type;
4453 {
4454     write_word(ES, DI, start);
4455     write_word(ES, DI+2, start >> 16);
4456     write_word(ES, DI+4, extra_start);
4457     write_word(ES, DI+6, 0x00);
4458 
4459     end -= start;
4460     extra_end -= extra_start;
4461     write_word(ES, DI+8, end);
4462     write_word(ES, DI+10, end >> 16);
4463     write_word(ES, DI+12, extra_end);
4464     write_word(ES, DI+14, 0x0000);
4465 
4466     write_word(ES, DI+16, type);
4467     write_word(ES, DI+18, 0x0);
4468 }
4469 
4470   void
4471 int15_function32(regs, ES, DS, FLAGS)
4472   pushad_regs_t regs; // REGS pushed via pushad
4473   Bit16u ES, DS, FLAGS;
4474 {
4475   Bit32u  extended_memory_size=0; // 64bits long
4476   Bit32u  extra_lowbits_memory_size=0;
4477   Bit16u  CX,DX;
4478   Bit8u   extra_highbits_memory_size=0;
4479 
4480 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
4481 
4482   switch (regs.u.r8.ah) {
4483     case 0x86:
4484       // Wait for CX:DX microseconds. currently using the
4485       // refresh request port 0x61 bit4, toggling every 15usec
4486 
4487       CX = regs.u.r16.cx;
4488       DX = regs.u.r16.dx;
4489 
4490 ASM_START
4491       sti
4492 
4493       ;; Get the count in eax
4494       mov  bx, sp
4495       SEG SS
4496         mov  ax, _int15_function32.CX [bx]
4497       shl  eax, #16
4498       SEG SS
4499         mov  ax, _int15_function32.DX [bx]
4500 
4501       ;; convert to numbers of 15usec ticks
4502       mov ebx, #15
4503       xor edx, edx
4504       div eax, ebx
4505       mov ecx, eax
4506 
4507       ;; wait for ecx number of refresh requests
4508       in al, #0x61
4509       and al,#0x10
4510       mov ah, al
4511 
4512       or ecx, ecx
4513       je int1586_tick_end
4514 int1586_tick:
4515       in al, #0x61
4516       and al,#0x10
4517       cmp al, ah
4518       je  int1586_tick
4519       mov ah, al
4520       dec ecx
4521       jnz int1586_tick
4522 int1586_tick_end:
4523 ASM_END
4524 
4525       break;
4526 
4527     case 0xe8:
4528         switch(regs.u.r8.al)
4529         {
4530          case 0x20: // coded by osmaker aka K.J.
4531             if(regs.u.r32.edx == 0x534D4150)
4532             {
4533                 extended_memory_size = inb_cmos(0x35);
4534                 extended_memory_size <<= 8;
4535                 extended_memory_size |= inb_cmos(0x34);
4536                 extended_memory_size *= 64;
4537                 // greater than EFF00000???
4538                 if(extended_memory_size > 0x3bc000) {
4539                     extended_memory_size = 0x3bc000; // everything after this is reserved memory until we get to 0x100000000
4540                 }
4541                 extended_memory_size *= 1024;
4542                 extended_memory_size += (16L * 1024 * 1024);
4543 
4544                 if(extended_memory_size <= (16L * 1024 * 1024)) {
4545                     extended_memory_size = inb_cmos(0x31);
4546                     extended_memory_size <<= 8;
4547                     extended_memory_size |= inb_cmos(0x30);
4548                     extended_memory_size *= 1024;
4549                     extended_memory_size += (1L * 1024 * 1024);
4550                 }
4551 
4552                 extra_lowbits_memory_size = inb_cmos(0x5c);
4553                 extra_lowbits_memory_size <<= 8;
4554                 extra_lowbits_memory_size |= inb_cmos(0x5b);
4555                 extra_lowbits_memory_size *= 64;
4556                 extra_lowbits_memory_size *= 1024;
4557                 extra_highbits_memory_size = inb_cmos(0x5d);
4558 
4559                 switch(regs.u.r16.bx)
4560                 {
4561                     case 0:
4562                         set_e820_range(ES, regs.u.r16.di,
4563                                        0x0000000L, 0x0009f000L, 0, 0, 1);
4564                         regs.u.r32.ebx = 1;
4565                         break;
4566                     case 1:
4567                         set_e820_range(ES, regs.u.r16.di,
4568                                        0x0009f000L, 0x000a0000L, 0, 0, 2);
4569                         regs.u.r32.ebx = 2;
4570                         break;
4571                     case 2:
4572                         set_e820_range(ES, regs.u.r16.di,
4573                                        0x000e8000L, 0x00100000L, 0, 0, 2);
4574                         regs.u.r32.ebx = 3;
4575                         break;
4576                     case 3:
4577 #if BX_ROMBIOS32
4578                         set_e820_range(ES, regs.u.r16.di,
4579                                        0x00100000L,
4580                                        extended_memory_size - ACPI_DATA_SIZE ,0, 0, 1);
4581                         regs.u.r32.ebx = 4;
4582 #else
4583                         set_e820_range(ES, regs.u.r16.di,
4584                                        0x00100000L,
4585                                        extended_memory_size, 1);
4586                         regs.u.r32.ebx = 5;
4587 #endif
4588                         break;
4589                     case 4:
4590                         set_e820_range(ES, regs.u.r16.di,
4591                                        extended_memory_size - ACPI_DATA_SIZE,
4592                                        extended_memory_size ,0, 0, 3); // ACPI RAM
4593                         regs.u.r32.ebx = 5;
4594                         break;
4595                     case 5:
4596                         /* 256KB BIOS area at the end of 4 GB */
4597                         set_e820_range(ES, regs.u.r16.di,
4598                                        0xfffc0000L, 0x00000000L ,0, 0, 2);
4599                         if (extra_highbits_memory_size || extra_lowbits_memory_size)
4600                             regs.u.r32.ebx = 6;
4601                         else
4602                             regs.u.r32.ebx = 0;
4603                         break;
4604                     case 6:
4605                         /* Maping of memory above 4 GB */
4606                         set_e820_range(ES, regs.u.r16.di, 0x00000000L,
4607                         extra_lowbits_memory_size, 1, extra_highbits_memory_size
4608                                        + 1, 1);
4609                         regs.u.r32.ebx = 0;
4610                         break;
4611                     default:  /* AX=E820, DX=534D4150, BX unrecognized */
4612                         goto int15_unimplemented;
4613                         break;
4614                 }
4615                 regs.u.r32.eax = 0x534D4150;
4616                 regs.u.r32.ecx = 0x14;
4617                 CLEAR_CF();
4618             } else {
4619               // if DX != 0x534D4150)
4620               goto int15_unimplemented;
4621             }
4622             break;
4623 
4624         case 0x01:
4625           // do we have any reason to fail here ?
4626           CLEAR_CF();
4627 
4628           // my real system sets ax and bx to 0
4629           // this is confirmed by Ralph Brown list
4630           // but syslinux v1.48 is known to behave
4631           // strangely if ax is set to 0
4632           // regs.u.r16.ax = 0;
4633           // regs.u.r16.bx = 0;
4634 
4635           // Get the amount of extended memory (above 1M)
4636           regs.u.r8.cl = inb_cmos(0x30);
4637           regs.u.r8.ch = inb_cmos(0x31);
4638 
4639           // limit to 15M
4640           if(regs.u.r16.cx > 0x3c00)
4641           {
4642             regs.u.r16.cx = 0x3c00;
4643           }
4644 
4645           // Get the amount of extended memory above 16M in 64k blocs
4646           regs.u.r8.dl = inb_cmos(0x34);
4647           regs.u.r8.dh = inb_cmos(0x35);
4648 
4649           // Set configured memory equal to extended memory
4650           regs.u.r16.ax = regs.u.r16.cx;
4651           regs.u.r16.bx = regs.u.r16.dx;
4652           break;
4653         default:  /* AH=0xE8?? but not implemented */
4654           goto int15_unimplemented;
4655        }
4656        break;
4657     int15_unimplemented:
4658        // fall into the default
4659     default:
4660       BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4661         (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4662       SET_CF();
4663       regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4664       break;
4665     }
4666 }
4667 
4668   void
4669 int16_function(DI, SI, BP, SP, BX, DX, CX, AX, FLAGS)
4670   Bit16u DI, SI, BP, SP, BX, DX, CX, AX, FLAGS;
4671 {
4672   Bit8u scan_code, ascii_code, shift_flags, led_flags, count;
4673   Bit16u kbd_code, max;
4674 
4675   BX_DEBUG_INT16("int16: AX=%04x BX=%04x CX=%04x DX=%04x \n", AX, BX, CX, DX);
4676 
4677   shift_flags = read_byte(0x0040, 0x17);
4678   led_flags = read_byte(0x0040, 0x97);
4679   if ((((shift_flags >> 4) & 0x07) ^ (led_flags & 0x07)) != 0) {
4680 ASM_START
4681     cli
4682 ASM_END
4683     outb(0x60, 0xed);
4684     while ((inb(0x64) & 0x01) == 0) outb(0x80, 0x21);
4685     if ((inb(0x60) == 0xfa)) {
4686       led_flags &= 0xf8;
4687       led_flags |= ((shift_flags >> 4) & 0x07);
4688       outb(0x60, led_flags & 0x07);
4689       while ((inb(0x64) & 0x01) == 0) outb(0x80, 0x21);
4690       inb(0x60);
4691       write_byte(0x0040, 0x97, led_flags);
4692     }
4693 ASM_START
4694     sti
4695 ASM_END
4696   }
4697 
4698   switch (GET_AH()) {
4699     case 0x00: /* read keyboard input */
4700 
4701       if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4702         BX_PANIC("KBD: int16h: out of keyboard input\n");
4703         }
4704       if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4705       else if (ascii_code == 0xE0) ascii_code = 0;
4706       AX = (scan_code << 8) | ascii_code;
4707       break;
4708 
4709     case 0x01: /* check keyboard status */
4710       if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4711         SET_ZF();
4712         return;
4713         }
4714       if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4715       else if (ascii_code == 0xE0) ascii_code = 0;
4716       AX = (scan_code << 8) | ascii_code;
4717       CLEAR_ZF();
4718       break;
4719 
4720     case 0x02: /* get shift flag status */
4721       shift_flags = read_byte(0x0040, 0x17);
4722       SET_AL(shift_flags);
4723       break;
4724 
4725     case 0x05: /* store key-stroke into buffer */
4726       if ( !enqueue_key(GET_CH(), GET_CL()) ) {
4727         SET_AL(1);
4728         }
4729       else {
4730         SET_AL(0);
4731         }
4732       break;
4733 
4734     case 0x09: /* GET KEYBOARD FUNCTIONALITY */
4735       // bit Bochs Description
4736       //  7    0   reserved
4737       //  6    0   INT 16/AH=20h-22h supported (122-key keyboard support)
4738       //  5    1   INT 16/AH=10h-12h supported (enhanced keyboard support)
4739       //  4    1   INT 16/AH=0Ah supported
4740       //  3    0   INT 16/AX=0306h supported
4741       //  2    0   INT 16/AX=0305h supported
4742       //  1    0   INT 16/AX=0304h supported
4743       //  0    0   INT 16/AX=0300h supported
4744       //
4745       SET_AL(0x30);
4746       break;
4747 
4748     case 0x0A: /* GET KEYBOARD ID */
4749       count = 2;
4750       kbd_code = 0x0;
4751       outb(0x60, 0xf2);
4752       /* Wait for data */
4753       max=0xffff;
4754       while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4755       if (max>0x0) {
4756         if ((inb(0x60) == 0xfa)) {
4757           do {
4758             max=0xffff;
4759             while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4760             if (max>0x0) {
4761               kbd_code >>= 8;
4762               kbd_code |= (inb(0x60) << 8);
4763             }
4764           } while (--count>0);
4765         }
4766       }
4767       BX=kbd_code;
4768       break;
4769 
4770     case 0x10: /* read MF-II keyboard input */
4771 
4772       if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4773         BX_PANIC("KBD: int16h: out of keyboard input\n");
4774         }
4775       if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4776       AX = (scan_code << 8) | ascii_code;
4777       break;
4778 
4779     case 0x11: /* check MF-II keyboard status */
4780       if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4781         SET_ZF();
4782         return;
4783         }
4784       if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4785       AX = (scan_code << 8) | ascii_code;
4786       CLEAR_ZF();
4787       break;
4788 
4789     case 0x12: /* get extended keyboard status */
4790       shift_flags = read_byte(0x0040, 0x17);
4791       SET_AL(shift_flags);
4792       shift_flags = read_byte(0x0040, 0x18) & 0x73;
4793       shift_flags |= read_byte(0x0040, 0x96) & 0x0c;
4794       SET_AH(shift_flags);
4795       BX_DEBUG_INT16("int16: func 12 sending %04x\n",AX);
4796       break;
4797 
4798     case 0x92: /* keyboard capability check called by DOS 5.0+ keyb */
4799       SET_AH(0x80); // function int16 ah=0x10-0x12 supported
4800       break;
4801 
4802     case 0xA2: /* 122 keys capability check called by DOS 5.0+ keyb */
4803       // don't change AH : function int16 ah=0x20-0x22 NOT supported
4804       break;
4805 
4806     case 0x6F:
4807       if (GET_AL() == 0x08)
4808         SET_AH(0x02); // unsupported, aka normal keyboard
4809 
4810     default:
4811       BX_INFO("KBD: unsupported int 16h function %02x\n", GET_AH());
4812     }
4813 }
4814 
4815   unsigned int
4816 dequeue_key(scan_code, ascii_code, incr)
4817   Bit8u *scan_code;
4818   Bit8u *ascii_code;
4819   unsigned int incr;
4820 {
4821   Bit16u buffer_start, buffer_end, buffer_head, buffer_tail;
4822   Bit16u ss;
4823   Bit8u  acode, scode;
4824 
4825 #if BX_CPU < 2
4826   buffer_start = 0x001E;
4827   buffer_end   = 0x003E;
4828 #else
4829   buffer_start = read_word(0x0040, 0x0080);
4830   buffer_end   = read_word(0x0040, 0x0082);
4831 #endif
4832 
4833   buffer_head = read_word(0x0040, 0x001a);
4834   buffer_tail = read_word(0x0040, 0x001c);
4835 
4836   if (buffer_head != buffer_tail) {
4837     ss = get_SS();
4838     acode = read_byte(0x0040, buffer_head);
4839     scode = read_byte(0x0040, buffer_head+1);
4840     write_byte(ss, ascii_code, acode);
4841     write_byte(ss, scan_code, scode);
4842 
4843     if (incr) {
4844       buffer_head += 2;
4845       if (buffer_head >= buffer_end)
4846         buffer_head = buffer_start;
4847       write_word(0x0040, 0x001a, buffer_head);
4848       }
4849     return(1);
4850     }
4851   else {
4852     return(0);
4853     }
4854 }
4855 
4856 static char panic_msg_keyb_buffer_full[] = "%s: keyboard input buffer full\n";
4857 
4858   Bit8u
4859 inhibit_mouse_int_and_events()
4860 {
4861   Bit8u command_byte, prev_command_byte;
4862 
4863   // Turn off IRQ generation and aux data line
4864   if ( inb(0x64) & 0x02 )
4865     BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4866   outb(0x64, 0x20); // get command byte
4867   while ( (inb(0x64) & 0x01) != 0x01 );
4868   prev_command_byte = inb(0x60);
4869   command_byte = prev_command_byte;
4870   //while ( (inb(0x64) & 0x02) );
4871   if ( inb(0x64) & 0x02 )
4872     BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4873   command_byte &= 0xfd; // turn off IRQ 12 generation
4874   command_byte |= 0x20; // disable mouse serial clock line
4875   outb(0x64, 0x60); // write command byte
4876   outb(0x60, command_byte);
4877   return(prev_command_byte);
4878 }
4879 
4880   void
4881 enable_mouse_int_and_events()
4882 {
4883   Bit8u command_byte;
4884 
4885   // Turn on IRQ generation and aux data line
4886   if ( inb(0x64) & 0x02 )
4887     BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4888   outb(0x64, 0x20); // get command byte
4889   while ( (inb(0x64) & 0x01) != 0x01 );
4890   command_byte = inb(0x60);
4891   //while ( (inb(0x64) & 0x02) );
4892   if ( inb(0x64) & 0x02 )
4893     BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4894   command_byte |= 0x02; // turn on IRQ 12 generation
4895   command_byte &= 0xdf; // enable mouse serial clock line
4896   outb(0x64, 0x60); // write command byte
4897   outb(0x60, command_byte);
4898 }
4899 
4900   Bit8u
4901 send_to_mouse_ctrl(sendbyte)
4902   Bit8u sendbyte;
4903 {
4904   Bit8u response;
4905 
4906   // wait for chance to write to ctrl
4907   if ( inb(0x64) & 0x02 )
4908     BX_PANIC(panic_msg_keyb_buffer_full,"sendmouse");
4909   outb(0x64, 0xD4);
4910   outb(0x60, sendbyte);
4911   return(0);
4912 }
4913 
4914 
4915   Bit8u
4916 get_mouse_data(data)
4917   Bit8u *data;
4918 {
4919   Bit8u response;
4920   Bit16u ss;
4921 
4922   while ( (inb(0x64) & 0x21) != 0x21 ) {
4923     }
4924 
4925   response = inb(0x60);
4926 
4927   ss = get_SS();
4928   write_byte(ss, data, response);
4929   return(0);
4930 }
4931 
4932   void
4933 set_kbd_command_byte(command_byte)
4934   Bit8u command_byte;
4935 {
4936   if ( inb(0x64) & 0x02 )
4937     BX_PANIC(panic_msg_keyb_buffer_full,"setkbdcomm");
4938   outb(0x64, 0xD4);
4939 
4940   outb(0x64, 0x60); // write command byte
4941   outb(0x60, command_byte);
4942 }
4943 
4944   void
4945 int09_function(DI, SI, BP, SP, BX, DX, CX, AX)
4946   Bit16u DI, SI, BP, SP, BX, DX, CX, AX;
4947 {
4948   Bit8u scancode, asciicode, shift_flags;
4949   Bit8u mf2_flags, mf2_state;
4950 
4951   //
4952   // DS has been set to F000 before call
4953   //
4954 
4955 
4956   scancode = GET_AL();
4957 
4958   if (scancode == 0) {
4959     BX_INFO("KBD: int09 handler: AL=0\n");
4960     return;
4961     }
4962 
4963 
4964   shift_flags = read_byte(0x0040, 0x17);
4965   mf2_flags = read_byte(0x0040, 0x18);
4966   mf2_state = read_byte(0x0040, 0x96);
4967   asciicode = 0;
4968 
4969   switch (scancode) {
4970     case 0x3a: /* Caps Lock press */
4971       shift_flags ^= 0x40;
4972       write_byte(0x0040, 0x17, shift_flags);
4973       mf2_flags |= 0x40;
4974       write_byte(0x0040, 0x18, mf2_flags);
4975       break;
4976     case 0xba: /* Caps Lock release */
4977       mf2_flags &= ~0x40;
4978       write_byte(0x0040, 0x18, mf2_flags);
4979       break;
4980 
4981     case 0x2a: /* L Shift press */
4982       shift_flags |= 0x02;
4983       write_byte(0x0040, 0x17, shift_flags);
4984       break;
4985     case 0xaa: /* L Shift release */
4986       shift_flags &= ~0x02;
4987       write_byte(0x0040, 0x17, shift_flags);
4988       break;
4989 
4990     case 0x36: /* R Shift press */
4991       shift_flags |= 0x01;
4992       write_byte(0x0040, 0x17, shift_flags);
4993       break;
4994     case 0xb6: /* R Shift release */
4995       shift_flags &= ~0x01;
4996       write_byte(0x0040, 0x17, shift_flags);
4997       break;
4998 
4999     case 0x1d: /* Ctrl press */
5000       if ((mf2_state & 0x01) == 0) {
5001         shift_flags |= 0x04;
5002         write_byte(0x0040, 0x17, shift_flags);
5003         if (mf2_state & 0x02) {
5004           mf2_state |= 0x04;
5005           write_byte(0x0040, 0x96, mf2_state);
5006         } else {
5007           mf2_flags |= 0x01;
5008           write_byte(0x0040, 0x18, mf2_flags);
5009         }
5010       }
5011       break;
5012     case 0x9d: /* Ctrl release */
5013       if ((mf2_state & 0x01) == 0) {
5014         shift_flags &= ~0x04;
5015         write_byte(0x0040, 0x17, shift_flags);
5016         if (mf2_state & 0x02) {
5017           mf2_state &= ~0x04;
5018           write_byte(0x0040, 0x96, mf2_state);
5019         } else {
5020           mf2_flags &= ~0x01;
5021           write_byte(0x0040, 0x18, mf2_flags);
5022         }
5023       }
5024       break;
5025 
5026     case 0x38: /* Alt press */
5027       shift_flags |= 0x08;
5028       write_byte(0x0040, 0x17, shift_flags);
5029       if (mf2_state & 0x02) {
5030         mf2_state |= 0x08;
5031         write_byte(0x0040, 0x96, mf2_state);
5032       } else {
5033         mf2_flags |= 0x02;
5034         write_byte(0x0040, 0x18, mf2_flags);
5035       }
5036       break;
5037     case 0xb8: /* Alt release */
5038       shift_flags &= ~0x08;
5039       write_byte(0x0040, 0x17, shift_flags);
5040       if (mf2_state & 0x02) {
5041         mf2_state &= ~0x08;
5042         write_byte(0x0040, 0x96, mf2_state);
5043       } else {
5044         mf2_flags &= ~0x02;
5045         write_byte(0x0040, 0x18, mf2_flags);
5046       }
5047       break;
5048 
5049     case 0x45: /* Num Lock press */
5050       if ((mf2_state & 0x03) == 0) {
5051         mf2_flags |= 0x20;
5052         write_byte(0x0040, 0x18, mf2_flags);
5053         shift_flags ^= 0x20;
5054         write_byte(0x0040, 0x17, shift_flags);
5055       }
5056       break;
5057     case 0xc5: /* Num Lock release */
5058       if ((mf2_state & 0x03) == 0) {
5059         mf2_flags &= ~0x20;
5060         write_byte(0x0040, 0x18, mf2_flags);
5061       }
5062       break;
5063 
5064     case 0x46: /* Scroll Lock press */
5065       mf2_flags |= 0x10;
5066       write_byte(0x0040, 0x18, mf2_flags);
5067       shift_flags ^= 0x10;
5068       write_byte(0x0040, 0x17, shift_flags);
5069       break;
5070 
5071     case 0xc6: /* Scroll Lock release */
5072       mf2_flags &= ~0x10;
5073       write_byte(0x0040, 0x18, mf2_flags);
5074       break;
5075 
5076     default:
5077       if (scancode & 0x80) {
5078         break; /* toss key releases ... */
5079       }
5080       if (scancode > MAX_SCAN_CODE) {
5081         BX_INFO("KBD: int09h_handler(): unknown scancode read: 0x%02x!\n", scancode);
5082         return;
5083       }
5084       if (shift_flags & 0x08) { /* ALT */
5085         asciicode = scan_to_scanascii[scancode].alt;
5086         scancode = scan_to_scanascii[scancode].alt >> 8;
5087       } else if (shift_flags & 0x04) { /* CONTROL */
5088         asciicode = scan_to_scanascii[scancode].control;
5089         scancode = scan_to_scanascii[scancode].control >> 8;
5090       } else if (((mf2_state & 0x02) > 0) && ((scancode >= 0x47) && (scancode <= 0x53))) {
5091         /* extended keys handling */
5092         asciicode = 0xe0;
5093         scancode = scan_to_scanascii[scancode].normal >> 8;
5094       } else if (shift_flags & 0x03) { /* LSHIFT + RSHIFT */
5095         /* check if lock state should be ignored
5096          * because a SHIFT key are pressed */
5097 
5098         if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
5099           asciicode = scan_to_scanascii[scancode].normal;
5100           scancode = scan_to_scanascii[scancode].normal >> 8;
5101         } else {
5102           asciicode = scan_to_scanascii[scancode].shift;
5103           scancode = scan_to_scanascii[scancode].shift >> 8;
5104         }
5105       } else {
5106         /* check if lock is on */
5107         if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
5108           asciicode = scan_to_scanascii[scancode].shift;
5109           scancode = scan_to_scanascii[scancode].shift >> 8;
5110         } else {
5111           asciicode = scan_to_scanascii[scancode].normal;
5112           scancode = scan_to_scanascii[scancode].normal >> 8;
5113         }
5114       }
5115       if (scancode==0 && asciicode==0) {
5116         BX_INFO("KBD: int09h_handler(): scancode & asciicode are zero?\n");
5117       }
5118       enqueue_key(scancode, asciicode);
5119       break;
5120   }
5121   if ((scancode & 0x7f) != 0x1d) {
5122     mf2_state &= ~0x01;
5123   }
5124   mf2_state &= ~0x02;
5125   write_byte(0x0040, 0x96, mf2_state);
5126 }
5127 
5128   unsigned int
5129 enqueue_key(scan_code, ascii_code)
5130   Bit8u scan_code, ascii_code;
5131 {
5132   Bit16u buffer_start, buffer_end, buffer_head, buffer_tail, temp_tail;
5133 
5134 #if BX_CPU < 2
5135   buffer_start = 0x001E;
5136   buffer_end   = 0x003E;
5137 #else
5138   buffer_start = read_word(0x0040, 0x0080);
5139   buffer_end   = read_word(0x0040, 0x0082);
5140 #endif
5141 
5142   buffer_head = read_word(0x0040, 0x001A);
5143   buffer_tail = read_word(0x0040, 0x001C);
5144 
5145   temp_tail = buffer_tail;
5146   buffer_tail += 2;
5147   if (buffer_tail >= buffer_end)
5148     buffer_tail = buffer_start;
5149 
5150   if (buffer_tail == buffer_head) {
5151     return(0);
5152     }
5153 
5154    write_byte(0x0040, temp_tail, ascii_code);
5155    write_byte(0x0040, temp_tail+1, scan_code);
5156    write_word(0x0040, 0x001C, buffer_tail);
5157    return(1);
5158 }
5159 
5160 
5161   void
5162 int74_function(make_farcall, Z, Y, X, status)
5163   Bit16u make_farcall, Z, Y, X, status;
5164 {
5165   Bit16u ebda_seg=read_word(0x0040,0x000E);
5166   Bit8u  in_byte, index, package_count;
5167   Bit8u  mouse_flags_1, mouse_flags_2;
5168 
5169 BX_DEBUG_INT74("entering int74_function\n");
5170   make_farcall = 0;
5171 
5172   in_byte = inb(0x64);
5173   if ( (in_byte & 0x21) != 0x21 ) {
5174     return;
5175     }
5176   in_byte = inb(0x60);
5177 BX_DEBUG_INT74("int74: read byte %02x\n", in_byte);
5178 
5179   mouse_flags_1 = read_byte(ebda_seg, 0x0026);
5180   mouse_flags_2 = read_byte(ebda_seg, 0x0027);
5181 
5182   if ( (mouse_flags_2 & 0x80) != 0x80 ) {
5183       return;
5184   }
5185 
5186   package_count = mouse_flags_2 & 0x07;
5187   index = mouse_flags_1 & 0x07;
5188   write_byte(ebda_seg, 0x28 + index, in_byte);
5189 
5190   if ( (index+1) >= package_count ) {
5191 BX_DEBUG_INT74("int74_function: make_farcall=1\n");
5192     status = read_byte(ebda_seg, 0x0028 + 0);
5193     X      = read_byte(ebda_seg, 0x0028 + 1);
5194     Y      = read_byte(ebda_seg, 0x0028 + 2);
5195     Z      = 0;
5196     mouse_flags_1 = 0;
5197     // check if far call handler installed
5198     if (mouse_flags_2 & 0x80)
5199       make_farcall = 1;
5200     }
5201   else {
5202     mouse_flags_1++;
5203     }
5204   write_byte(ebda_seg, 0x0026, mouse_flags_1);
5205 }
5206 
5207 #define SET_DISK_RET_STATUS(status) write_byte(0x0040, 0x0074, status)
5208 
5209 #if BX_USE_ATADRV
5210 
5211   void
5212 int13_harddisk(EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
5213   Bit16u EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
5214 {
5215   Bit32u lba_low, lba_high;
5216   Bit16u ebda_seg=read_word(0x0040,0x000E);
5217   Bit16u cylinder, head, sector;
5218   Bit16u segment, offset;
5219   Bit16u npc, nph, npspt, nlc, nlh, nlspt;
5220   Bit16u size, count;
5221   Bit8u  device, status;
5222 
5223   BX_DEBUG_INT13_HD("int13_harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5224 
5225   write_byte(0x0040, 0x008e, 0);  // clear completion flag
5226 
5227   // basic check : device has to be defined
5228   if ( (GET_ELDL() < 0x80) || (GET_ELDL() >= 0x80 + BX_MAX_ATA_DEVICES) ) {
5229     BX_INFO("int13_harddisk: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
5230     goto int13_fail;
5231     }
5232 
5233   // Get the ata channel
5234   device=read_byte(ebda_seg,&EbdaData->ata.hdidmap[GET_ELDL()-0x80]);
5235 
5236   // basic check : device has to be valid
5237   if (device >= BX_MAX_ATA_DEVICES) {
5238     BX_INFO("int13_harddisk: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
5239     goto int13_fail;
5240     }
5241 
5242   switch (GET_AH()) {
5243 
5244     case 0x00: /* disk controller reset */
5245       ata_reset (device);
5246       goto int13_success;
5247       break;
5248 
5249     case 0x01: /* read disk status */
5250       status = read_byte(0x0040, 0x0074);
5251       SET_AH(status);
5252       SET_DISK_RET_STATUS(0);
5253       /* set CF if error status read */
5254       if (status) goto int13_fail_nostatus;
5255       else        goto int13_success_noah;
5256       break;
5257 
5258     case 0x02: // read disk sectors
5259     case 0x03: // write disk sectors
5260     case 0x04: // verify disk sectors
5261 
5262       count       = GET_AL();
5263       cylinder    = GET_CH();
5264       cylinder   |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
5265       sector      = (GET_CL() & 0x3f);
5266       head        = GET_DH();
5267 
5268       segment = ES;
5269       offset  = BX;
5270 
5271       if ((count > 128) || (count == 0) || (sector == 0)) {
5272         BX_INFO("int13_harddisk: function %02x, parameter out of range!\n",GET_AH());
5273         goto int13_fail;
5274       }
5275 
5276       nlc   = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
5277       nlh   = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
5278       nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
5279 
5280       // sanity check on cyl heads, sec
5281       if( (cylinder >= nlc) || (head >= nlh) || (sector > nlspt )) {
5282         BX_INFO("int13_harddisk: function %02x, parameters out of range %04x/%04x/%04x!\n", GET_AH(), cylinder, head, sector);
5283         goto int13_fail;
5284         }
5285 
5286       // FIXME verify
5287       if ( GET_AH() == 0x04 ) goto int13_success;
5288 
5289       nph   = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
5290       npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
5291 
5292       // if needed, translate lchs to lba, and execute command
5293       if ( (nph != nlh) || (npspt != nlspt)) {
5294         lba_low = ((((Bit32u)cylinder * (Bit32u)nlh) + (Bit32u)head) * (Bit32u)nlspt) + (Bit32u)sector - 1;
5295         lba_high = 0;
5296         sector = 0; // this forces the command to be lba
5297         }
5298 
5299       if ( GET_AH() == 0x02 )
5300         status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, cylinder, head, sector, lba_low, lba_high, segment, offset);
5301       else
5302         status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, cylinder, head, sector, lba_low, lba_high, segment, offset);
5303 
5304       // Set nb of sector transferred
5305       SET_AL(read_word(ebda_seg, &EbdaData->ata.trsfsectors));
5306 
5307       if (status != 0) {
5308         BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
5309         SET_AH(0x0c);
5310         goto int13_fail_noah;
5311         }
5312 
5313       goto int13_success;
5314       break;
5315 
5316     case 0x05: /* format disk track */
5317       BX_INFO("format disk track called\n");
5318       goto int13_success;
5319       return;
5320       break;
5321 
5322     case 0x08: /* read disk drive parameters */
5323 
5324       // Get logical geometry from table
5325       nlc   = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
5326       nlh   = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
5327       nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
5328       count = read_byte(ebda_seg, &EbdaData->ata.hdcount);
5329 
5330       nlc = nlc - 2; /* 0 based , last sector not used */
5331       SET_AL(0);
5332       SET_CH(nlc & 0xff);
5333       SET_CL(((nlc >> 2) & 0xc0) | (nlspt & 0x3f));
5334       SET_DH(nlh - 1);
5335       SET_DL(count); /* FIXME returns 0, 1, or n hard drives */
5336 
5337       // FIXME should set ES & DI
5338 
5339       goto int13_success;
5340       break;
5341 
5342     case 0x10: /* check drive ready */
5343       // should look at 40:8E also???
5344 
5345       // Read the status from controller
5346       status = inb(read_word(ebda_seg, &EbdaData->ata.channels[device/2].iobase1) + ATA_CB_STAT);
5347       if ( (status & ( ATA_CB_STAT_BSY | ATA_CB_STAT_RDY )) == ATA_CB_STAT_RDY ) {
5348         goto int13_success;
5349         }
5350       else {
5351         SET_AH(0xAA);
5352         goto int13_fail_noah;
5353         }
5354       break;
5355 
5356     case 0x15: /* read disk drive size */
5357 
5358       // Get logical geometry from table
5359       nlc   = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
5360       nlh   = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
5361       nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
5362 
5363       // Compute sector count seen by int13
5364       lba_low = (Bit32u)(nlc - 1) * (Bit32u)nlh * (Bit32u)nlspt;
5365       CX = lba_low >> 16;
5366       DX = lba_low & 0xffff;
5367 
5368       SET_AH(3);  // hard disk accessible
5369       goto int13_success_noah;
5370       break;
5371 
5372     case 0x41: // IBM/MS installation check
5373       BX=0xaa55;     // install check
5374       SET_AH(0x30);  // EDD 3.0
5375       CX=0x0007;     // ext disk access and edd, removable supported
5376       goto int13_success_noah;
5377       break;
5378 
5379     case 0x42: // IBM/MS extended read
5380     case 0x43: // IBM/MS extended write
5381     case 0x44: // IBM/MS verify
5382     case 0x47: // IBM/MS extended seek
5383 
5384       count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
5385       segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
5386       offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
5387 
5388       // Get 32 msb lba and check
5389       lba_high=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
5390       if (lba_high > read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_high) ) {
5391         BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH());
5392         goto int13_fail;
5393         }
5394 
5395       // Get 32 lsb lba and check
5396       lba_low=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5397       if (lba_high == read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_high)
5398           && lba_low >= read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_low) ) {
5399         BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH());
5400         goto int13_fail;
5401         }
5402 
5403       // If verify or seek
5404       if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
5405         goto int13_success;
5406 
5407       // Execute the command
5408       if ( GET_AH() == 0x42 )
5409         status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, 0, 0, 0, lba_low, lba_high, segment, offset);
5410       else
5411         status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, 0, 0, 0, lba_low, lba_high, segment, offset);
5412 
5413       count=read_word(ebda_seg, &EbdaData->ata.trsfsectors);
5414       write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5415 
5416       if (status != 0) {
5417         BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
5418         SET_AH(0x0c);
5419         goto int13_fail_noah;
5420         }
5421 
5422       goto int13_success;
5423       break;
5424 
5425     case 0x45: // IBM/MS lock/unlock drive
5426     case 0x49: // IBM/MS extended media change
5427       goto int13_success;    // Always success for HD
5428       break;
5429 
5430     case 0x46: // IBM/MS eject media
5431       SET_AH(0xb2);          // Volume Not Removable
5432       goto int13_fail_noah;  // Always fail for HD
5433       break;
5434 
5435     case 0x48: // IBM/MS get drive parameters
5436       size=read_word(DS,SI+(Bit16u)&Int13DPT->size);
5437 
5438       // Buffer is too small
5439       if(size < 0x1a)
5440         goto int13_fail;
5441 
5442       // EDD 1.x
5443       if(size >= 0x1a) {
5444         Bit16u   blksize;
5445 
5446         npc     = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
5447         nph     = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
5448         npspt   = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
5449         lba_low = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_low);
5450         lba_high = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_high);
5451         blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5452 
5453         write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5454         if (lba_high || (lba_low/npspt)/nph > 0x3fff)
5455         {
5456           write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x00); // geometry is invalid
5457           write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0x3fff);
5458         }
5459         else
5460         {
5461           write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x02); // geometry is valid
5462           write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, (Bit32u)npc);
5463         }
5464         write_dword(DS, SI+(Bit16u)&Int13DPT->heads, (Bit32u)nph);
5465         write_dword(DS, SI+(Bit16u)&Int13DPT->spt, (Bit32u)npspt);
5466         write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, lba_low);
5467         write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, lba_high);
5468         write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5469         }
5470 
5471       // EDD 2.x
5472       if(size >= 0x1e) {
5473         Bit8u  channel, dev, irq, mode, checksum, i, translation;
5474         Bit16u iobase1, iobase2, options;
5475 
5476         write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5477 
5478         write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5479         write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5480 
5481         // Fill in dpte
5482         channel = device / 2;
5483         iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5484         iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5485         irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5486         mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5487         translation = read_byte(ebda_seg, &EbdaData->ata.devices[device].translation);
5488 
5489         options  = (translation==ATA_TRANSLATION_NONE?0:1)<<3; // chs translation
5490         options |= (1<<4); // lba translation
5491         options |= (mode==ATA_MODE_PIO32?1:0)<<7;
5492         options |= (translation==ATA_TRANSLATION_LBA?1:0)<<9;
5493         options |= (translation==ATA_TRANSLATION_RECHS?3:0)<<9;
5494 
5495         write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5496         write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2 + ATA_CB_DC);
5497         write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5498         write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5499         write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5500         write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5501         write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5502         write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5503         write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5504         write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5505         if (size >=0x42)
5506           write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5507         else
5508           write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x10);
5509 
5510         checksum=0;
5511         for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, ((Bit8u*)(&EbdaData->ata.dpte)) + i);
5512         checksum = ~checksum;
5513         write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5514         }
5515 
5516       // EDD 3.x
5517       if(size >= 0x42) {
5518         Bit8u channel, iface, checksum, i;
5519         Bit16u iobase1;
5520 
5521         channel = device / 2;
5522         iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5523         iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5524 
5525         write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5526         write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5527         write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5528         write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5529         write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5530 
5531         if (iface==ATA_IFACE_ISA) {
5532           write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5533           write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5534           write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5535           write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5536           }
5537         else {
5538           // FIXME PCI
5539           }
5540         write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5541         write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5542         write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5543         write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5544 
5545         if (iface==ATA_IFACE_ISA) {
5546           write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5547           write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5548           write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5549           }
5550         else {
5551           // FIXME PCI
5552           }
5553         write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5554         write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5555         write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5556         write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5557 
5558         checksum=0;
5559         for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5560         checksum = ~checksum;
5561         write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5562         }
5563 
5564       goto int13_success;
5565       break;
5566 
5567     case 0x4e: // // IBM/MS set hardware configuration
5568       // DMA, prefetch, PIO maximum not supported
5569       switch (GET_AL()) {
5570         case 0x01:
5571         case 0x03:
5572         case 0x04:
5573         case 0x06:
5574           goto int13_success;
5575           break;
5576         default :
5577           goto int13_fail;
5578         }
5579       break;
5580 
5581     case 0x09: /* initialize drive parameters */
5582     case 0x0c: /* seek to specified cylinder */
5583     case 0x0d: /* alternate disk reset */
5584     case 0x11: /* recalibrate */
5585     case 0x14: /* controller internal diagnostic */
5586       BX_INFO("int13_harddisk: function %02xh unimplemented, returns success\n", GET_AH());
5587       goto int13_success;
5588       break;
5589 
5590     case 0x0a: /* read disk sectors with ECC */
5591     case 0x0b: /* write disk sectors with ECC */
5592     case 0x18: // set media type for format
5593     case 0x50: // IBM/MS send packet command
5594     default:
5595       BX_INFO("int13_harddisk: function %02xh unsupported, returns fail\n", GET_AH());
5596       goto int13_fail;
5597       break;
5598     }
5599 
5600 int13_fail:
5601     SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5602 int13_fail_noah:
5603     SET_DISK_RET_STATUS(GET_AH());
5604 int13_fail_nostatus:
5605     SET_CF();     // error occurred
5606     return;
5607 
5608 int13_success:
5609     SET_AH(0x00); // no error
5610 int13_success_noah:
5611     SET_DISK_RET_STATUS(0x00);
5612     CLEAR_CF();   // no error
5613     return;
5614 }
5615 
5616 // ---------------------------------------------------------------------------
5617 // Start of int13 for cdrom
5618 // ---------------------------------------------------------------------------
5619 
5620   void
5621 int13_cdrom(EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
5622   Bit16u EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
5623 {
5624   Bit16u ebda_seg=read_word(0x0040,0x000E);
5625   Bit8u  device, status, locks;
5626   Bit8u  atacmd[12];
5627   Bit32u lba;
5628   Bit16u count, segment, offset, i, size;
5629 
5630   BX_DEBUG_INT13_CD("int13_cdrom: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5631 
5632   SET_DISK_RET_STATUS(0x00);
5633 
5634   /* basic check : device should be 0xE0+ */
5635   if( (GET_ELDL() < 0xE0) || (GET_ELDL() >= 0xE0+BX_MAX_ATA_DEVICES) ) {
5636     BX_INFO("int13_cdrom: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
5637     goto int13_fail;
5638     }
5639 
5640   // Get the ata channel
5641   device=read_byte(ebda_seg,&EbdaData->ata.cdidmap[GET_ELDL()-0xE0]);
5642 
5643   /* basic check : device has to be valid  */
5644   if (device >= BX_MAX_ATA_DEVICES) {
5645     BX_INFO("int13_cdrom: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
5646     goto int13_fail;
5647     }
5648 
5649   switch (GET_AH()) {
5650 
5651     // all those functions return SUCCESS
5652     case 0x00: /* disk controller reset */
5653     case 0x09: /* initialize drive parameters */
5654     case 0x0c: /* seek to specified cylinder */
5655     case 0x0d: /* alternate disk reset */
5656     case 0x10: /* check drive ready */
5657     case 0x11: /* recalibrate */
5658     case 0x14: /* controller internal diagnostic */
5659     case 0x16: /* detect disk change */
5660       goto int13_success;
5661       break;
5662 
5663     // all those functions return disk write-protected
5664     case 0x03: /* write disk sectors */
5665     case 0x05: /* format disk track */
5666     case 0x43: // IBM/MS extended write
5667       SET_AH(0x03);
5668       goto int13_fail_noah;
5669       break;
5670 
5671     case 0x01: /* read disk status */
5672       status = read_byte(0x0040, 0x0074);
5673       SET_AH(status);
5674       SET_DISK_RET_STATUS(0);
5675 
5676       /* set CF if error status read */
5677       if (status) goto int13_fail_nostatus;
5678       else        goto int13_success_noah;
5679       break;
5680 
5681     case 0x15: /* read disk drive size */
5682       SET_AH(0x02);
5683       goto int13_fail_noah;
5684       break;
5685 
5686     case 0x41: // IBM/MS installation check
5687       BX=0xaa55;     // install check
5688       SET_AH(0x30);  // EDD 2.1
5689       CX=0x0007;     // ext disk access, removable and edd
5690       goto int13_success_noah;
5691       break;
5692 
5693     case 0x42: // IBM/MS extended read
5694     case 0x44: // IBM/MS verify sectors
5695     case 0x47: // IBM/MS extended seek
5696 
5697       count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
5698       segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
5699       offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
5700 
5701       // Can't use 64 bits lba
5702       lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
5703       if (lba != 0L) {
5704         BX_PANIC("int13_cdrom: function %02x. Can't use 64bits lba\n",GET_AH());
5705         goto int13_fail;
5706         }
5707 
5708       // Get 32 bits lba
5709       lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5710 
5711       // If verify or seek
5712       if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
5713         goto int13_success;
5714 
5715       memsetb(get_SS(),atacmd,0,12);
5716       atacmd[0]=0x28;                      // READ command
5717       atacmd[7]=(count & 0xff00) >> 8;     // Sectors
5718       atacmd[8]=(count & 0x00ff);          // Sectors
5719       atacmd[2]=(lba & 0xff000000) >> 24;  // LBA
5720       atacmd[3]=(lba & 0x00ff0000) >> 16;
5721       atacmd[4]=(lba & 0x0000ff00) >> 8;
5722       atacmd[5]=(lba & 0x000000ff);
5723       status = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, count*2048L, ATA_DATA_IN, segment,offset);
5724 
5725       count = (Bit16u)(read_dword(ebda_seg, &EbdaData->ata.trsfbytes) >> 11);
5726       write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5727 
5728       if (status != 0) {
5729         BX_INFO("int13_cdrom: function %02x, status %02x !\n",GET_AH(),status);
5730         SET_AH(0x0c);
5731         goto int13_fail_noah;
5732         }
5733 
5734       goto int13_success;
5735       break;
5736 
5737     case 0x45: // IBM/MS lock/unlock drive
5738       if (GET_AL() > 2) goto int13_fail;
5739 
5740       locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5741 
5742       switch (GET_AL()) {
5743         case 0 :  // lock
5744           if (locks == 0xff) {
5745             SET_AH(0xb4);
5746             SET_AL(1);
5747             goto int13_fail_noah;
5748             }
5749           write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, ++locks);
5750           SET_AL(1);
5751           break;
5752         case 1 :  // unlock
5753           if (locks == 0x00) {
5754             SET_AH(0xb0);
5755             SET_AL(0);
5756             goto int13_fail_noah;
5757             }
5758           write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, --locks);
5759           SET_AL(locks==0?0:1);
5760           break;
5761         case 2 :  // status
5762           SET_AL(locks==0?0:1);
5763           break;
5764         }
5765       goto int13_success;
5766       break;
5767 
5768     case 0x46: // IBM/MS eject media
5769       locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5770 
5771       if (locks != 0) {
5772         SET_AH(0xb1); // media locked
5773         goto int13_fail_noah;
5774         }
5775       // FIXME should handle 0x31 no media in device
5776       // FIXME should handle 0xb5 valid request failed
5777 
5778       // Call removable media eject
5779       ASM_START
5780         push bp
5781         mov  bp, sp
5782 
5783         mov ah, #0x52
5784         int #0x15
5785         mov _int13_cdrom.status + 2[bp], ah
5786         jnc int13_cdrom_rme_end
5787         mov _int13_cdrom.status, #1
5788 int13_cdrom_rme_end:
5789         pop bp
5790       ASM_END
5791 
5792       if (status != 0) {
5793         SET_AH(0xb1); // media locked
5794         goto int13_fail_noah;
5795       }
5796 
5797       goto int13_success;
5798       break;
5799 
5800     case 0x48: // IBM/MS get drive parameters
5801       size = read_word(DS,SI+(Bit16u)&Int13Ext->size);
5802 
5803       // Buffer is too small
5804       if(size < 0x1a)
5805         goto int13_fail;
5806 
5807       // EDD 1.x
5808       if(size >= 0x1a) {
5809         Bit16u   cylinders, heads, spt, blksize;
5810 
5811         blksize   = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5812 
5813         write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5814         write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x74); // removable, media change, lockable, max values
5815         write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0xffffffff);
5816         write_dword(DS, SI+(Bit16u)&Int13DPT->heads, 0xffffffff);
5817         write_dword(DS, SI+(Bit16u)&Int13DPT->spt, 0xffffffff);
5818         write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, 0xffffffff);  // FIXME should be Bit64
5819         write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0xffffffff);
5820         write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5821         }
5822 
5823       // EDD 2.x
5824       if(size >= 0x1e) {
5825         Bit8u  channel, dev, irq, mode, checksum, i;
5826         Bit16u iobase1, iobase2, options;
5827 
5828         write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5829 
5830         write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5831         write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5832 
5833         // Fill in dpte
5834         channel = device / 2;
5835         iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5836         iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5837         irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5838         mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5839 
5840         // FIXME atapi device
5841         options  = (1<<4); // lba translation
5842         options |= (1<<5); // removable device
5843         options |= (1<<6); // atapi device
5844         options |= (mode==ATA_MODE_PIO32?1:0<<7);
5845 
5846         write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5847         write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2 + ATA_CB_DC);
5848         write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5849         write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5850         write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5851         write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5852         write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5853         write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5854         write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5855         write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5856         write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5857 
5858         checksum=0;
5859         for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, ((Bit8u*)(&EbdaData->ata.dpte)) + i);
5860         checksum = ~checksum;
5861         write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5862         }
5863 
5864       // EDD 3.x
5865       if(size >= 0x42) {
5866         Bit8u channel, iface, checksum, i;
5867         Bit16u iobase1;
5868 
5869         channel = device / 2;
5870         iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5871         iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5872 
5873         write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5874         write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5875         write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5876         write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5877         write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5878 
5879         if (iface==ATA_IFACE_ISA) {
5880           write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5881           write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5882           write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5883           write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5884           }
5885         else {
5886           // FIXME PCI
5887           }
5888         write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5889         write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5890         write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5891         write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5892 
5893         if (iface==ATA_IFACE_ISA) {
5894           write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5895           write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5896           write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5897           }
5898         else {
5899           // FIXME PCI
5900           }
5901         write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5902         write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5903         write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5904         write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5905 
5906         checksum=0;
5907         for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5908         checksum = ~checksum;
5909         write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5910         }
5911 
5912       goto int13_success;
5913       break;
5914 
5915     case 0x49: // IBM/MS extended media change
5916       // always send changed ??
5917       SET_AH(06);
5918       goto int13_fail_nostatus;
5919       break;
5920 
5921     case 0x4e: // // IBM/MS set hardware configuration
5922       // DMA, prefetch, PIO maximum not supported
5923       switch (GET_AL()) {
5924         case 0x01:
5925         case 0x03:
5926         case 0x04:
5927         case 0x06:
5928           goto int13_success;
5929           break;
5930         default :
5931           goto int13_fail;
5932         }
5933       break;
5934 
5935     // all those functions return unimplemented
5936     case 0x02: /* read sectors */
5937     case 0x04: /* verify sectors */
5938     case 0x08: /* read disk drive parameters */
5939     case 0x0a: /* read disk sectors with ECC */
5940     case 0x0b: /* write disk sectors with ECC */
5941     case 0x18: /* set media type for format */
5942     case 0x50: // ? - send packet command
5943     default:
5944       BX_INFO("int13_cdrom: unsupported AH=%02x\n", GET_AH());
5945       goto int13_fail;
5946       break;
5947     }
5948 
5949 int13_fail:
5950     SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5951 int13_fail_noah:
5952     SET_DISK_RET_STATUS(GET_AH());
5953 int13_fail_nostatus:
5954     SET_CF();     // error occurred
5955     return;
5956 
5957 int13_success:
5958     SET_AH(0x00); // no error
5959 int13_success_noah:
5960     SET_DISK_RET_STATUS(0x00);
5961     CLEAR_CF();   // no error
5962     return;
5963 }
5964 
5965 // ---------------------------------------------------------------------------
5966 // End of int13 for cdrom
5967 // ---------------------------------------------------------------------------
5968 
5969 #if BX_ELTORITO_BOOT
5970 // ---------------------------------------------------------------------------
5971 // Start of int13 for eltorito functions
5972 // ---------------------------------------------------------------------------
5973 
5974   void
5975 int13_eltorito(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
5976   Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
5977 {
5978   Bit16u ebda_seg=read_word(0x0040,0x000E);
5979 
5980   BX_DEBUG_INT13_ET("int13_eltorito: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5981   // BX_DEBUG_INT13_ET("int13_eltorito: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
5982 
5983   switch (GET_AH()) {
5984 
5985     // FIXME ElTorito Various. Should be implemented
5986     case 0x4a: // ElTorito - Initiate disk emu
5987     case 0x4c: // ElTorito - Initiate disk emu and boot
5988     case 0x4d: // ElTorito - Return Boot catalog
5989       BX_PANIC("Int13 eltorito call with AX=%04x. Please report\n",AX);
5990       goto int13_fail;
5991       break;
5992 
5993     case 0x4b: // ElTorito - Terminate disk emu
5994       // FIXME ElTorito Hardcoded
5995       write_byte(DS,SI+0x00,0x13);
5996       write_byte(DS,SI+0x01,read_byte(ebda_seg,&EbdaData->cdemu.media));
5997       write_byte(DS,SI+0x02,read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
5998       write_byte(DS,SI+0x03,read_byte(ebda_seg,&EbdaData->cdemu.controller_index));
5999       write_dword(DS,SI+0x04,read_dword(ebda_seg,&EbdaData->cdemu.ilba));
6000       write_word(DS,SI+0x08,read_word(ebda_seg,&EbdaData->cdemu.device_spec));
6001       write_word(DS,SI+0x0a,read_word(ebda_seg,&EbdaData->cdemu.buffer_segment));
6002       write_word(DS,SI+0x0c,read_word(ebda_seg,&EbdaData->cdemu.load_segment));
6003       write_word(DS,SI+0x0e,read_word(ebda_seg,&EbdaData->cdemu.sector_count));
6004       write_byte(DS,SI+0x10,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.cylinders));
6005       write_byte(DS,SI+0x11,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.spt));
6006       write_byte(DS,SI+0x12,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.heads));
6007 
6008       // If we have to terminate emulation
6009       if(GET_AL() == 0x00) {
6010         // FIXME ElTorito Various. Should be handled accordingly to spec
6011         write_byte(ebda_seg,&EbdaData->cdemu.active, 0x00); // bye bye
6012         }
6013 
6014       goto int13_success;
6015       break;
6016 
6017     default:
6018       BX_INFO("int13_eltorito: unsupported AH=%02x\n", GET_AH());
6019       goto int13_fail;
6020       break;
6021     }
6022 
6023 int13_fail:
6024     SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
6025     SET_DISK_RET_STATUS(GET_AH());
6026     SET_CF();     // error occurred
6027     return;
6028 
6029 int13_success:
6030     SET_AH(0x00); // no error
6031     SET_DISK_RET_STATUS(0x00);
6032     CLEAR_CF();   // no error
6033     return;
6034 }
6035 
6036 // ---------------------------------------------------------------------------
6037 // End of int13 for eltorito functions
6038 // ---------------------------------------------------------------------------
6039 
6040 // ---------------------------------------------------------------------------
6041 // Start of int13 when emulating a device from the cd
6042 // ---------------------------------------------------------------------------
6043 
6044   void
6045 int13_cdemu(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
6046   Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
6047 {
6048   Bit16u ebda_seg=read_word(0x0040,0x000E);
6049   Bit8u  device, status;
6050   Bit16u vheads, vspt, vcylinders;
6051   Bit16u head, sector, cylinder, nbsectors;
6052   Bit32u vlba, ilba, slba, elba;
6053   Bit16u before, segment, offset;
6054   Bit8u  atacmd[12];
6055 
6056   BX_DEBUG_INT13_ET("int13_cdemu: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
6057 
6058   /* at this point, we are emulating a floppy/harddisk */
6059 
6060   // Recompute the device number
6061   device  = read_byte(ebda_seg,&EbdaData->cdemu.controller_index) * 2;
6062   device += read_byte(ebda_seg,&EbdaData->cdemu.device_spec);
6063 
6064   SET_DISK_RET_STATUS(0x00);
6065 
6066   /* basic checks : emulation should be active, dl should equal the emulated drive */
6067   if( (read_byte(ebda_seg,&EbdaData->cdemu.active) ==0 )
6068    || (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive ) != GET_DL())) {
6069     BX_INFO("int13_cdemu: function %02x, emulation not active for DL= %02x\n", GET_AH(), GET_DL());
6070     goto int13_fail;
6071     }
6072 
6073   switch (GET_AH()) {
6074 
6075     // all those functions return SUCCESS
6076     case 0x00: /* disk controller reset */
6077     case 0x09: /* initialize drive parameters */
6078     case 0x0c: /* seek to specified cylinder */
6079     case 0x0d: /* alternate disk reset */  // FIXME ElTorito Various. should really reset ?
6080     case 0x10: /* check drive ready */     // FIXME ElTorito Various. should check if ready ?
6081     case 0x11: /* recalibrate */
6082     case 0x14: /* controller internal diagnostic */
6083     case 0x16: /* detect disk change */
6084       goto int13_success;
6085       break;
6086 
6087     // all those functions return disk write-protected
6088     case 0x03: /* write disk sectors */
6089     case 0x05: /* format disk track */
6090       SET_AH(0x03);
6091       goto int13_fail_noah;
6092       break;
6093 
6094     case 0x01: /* read disk status */
6095       status=read_byte(0x0040, 0x0074);
6096       SET_AH(status);
6097       SET_DISK_RET_STATUS(0);
6098 
6099       /* set CF if error status read */
6100       if (status) goto int13_fail_nostatus;
6101       else        goto int13_success_noah;
6102       break;
6103 
6104     case 0x02: // read disk sectors
6105     case 0x04: // verify disk sectors
6106       vspt       = read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
6107       vcylinders = read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders);
6108       vheads     = read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads);
6109 
6110       ilba       = read_dword(ebda_seg,&EbdaData->cdemu.ilba);
6111 
6112       sector    = GET_CL() & 0x003f;
6113       cylinder  = (GET_CL() & 0x00c0) << 2 | GET_CH();
6114       head      = GET_DH();
6115       nbsectors = GET_AL();
6116       segment   = ES;
6117       offset    = BX;
6118 
6119       // no sector to read ?
6120       if(nbsectors==0) goto int13_success;
6121 
6122       // sanity checks sco openserver needs this!
6123       if ((sector   >  vspt)
6124        || (cylinder >= vcylinders)
6125        || (head     >= vheads)) {
6126         goto int13_fail;
6127         }
6128 
6129       // After controls, verify do nothing
6130       if (GET_AH() == 0x04) goto int13_success;
6131 
6132       segment = ES+(BX / 16);
6133       offset  = BX % 16;
6134 
6135       // calculate the virtual lba inside the image
6136       vlba=((((Bit32u)cylinder*(Bit32u)vheads)+(Bit32u)head)*(Bit32u)vspt)+((Bit32u)(sector-1));
6137 
6138       // In advance so we don't loose the count
6139       SET_AL(nbsectors);
6140 
6141       // start lba on cd
6142       slba  = (Bit32u)vlba/4;
6143       before= (Bit16u)vlba%4;
6144 
6145       // end lba on cd
6146       elba = (Bit32u)(vlba+nbsectors-1)/4;
6147 
6148       memsetb(get_SS(),atacmd,0,12);
6149       atacmd[0]=0x28;                      // READ command
6150       atacmd[7]=((Bit16u)(elba-slba+1) & 0xff00) >> 8; // Sectors
6151       atacmd[8]=((Bit16u)(elba-slba+1) & 0x00ff);      // Sectors
6152       atacmd[2]=(ilba+slba & 0xff000000) >> 24;  // LBA
6153       atacmd[3]=(ilba+slba & 0x00ff0000) >> 16;
6154       atacmd[4]=(ilba+slba & 0x0000ff00) >> 8;
6155       atacmd[5]=(ilba+slba & 0x000000ff);
6156       if((status = ata_cmd_packet(device, 12, get_SS(), atacmd, before*512, nbsectors*512L, ATA_DATA_IN, segment,offset)) != 0) {
6157         BX_INFO("int13_cdemu: function %02x, error %02x !\n",GET_AH(),status);
6158         SET_AH(0x02);
6159         SET_AL(0);
6160         goto int13_fail_noah;
6161         }
6162 
6163       goto int13_success;
6164       break;
6165 
6166     case 0x08: /* read disk drive parameters */
6167       vspt=read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
6168       vcylinders=read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders) - 1;
6169       vheads=read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads) - 1;
6170 
6171       SET_AL( 0x00 );
6172       SET_BL( 0x00 );
6173       SET_CH( vcylinders & 0xff );
6174       SET_CL((( vcylinders >> 2) & 0xc0) | ( vspt  & 0x3f ));
6175       SET_DH( vheads );
6176       SET_DL( 0x02 );   // FIXME ElTorito Various. should send the real count of drives 1 or 2
6177                         // FIXME ElTorito Harddisk. should send the HD count
6178 
6179       switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
6180         case 0x01: SET_BL( 0x02 ); break;
6181         case 0x02: SET_BL( 0x04 ); break;
6182         case 0x03: SET_BL( 0x06 ); break;
6183         }
6184 
6185 ASM_START
6186       push bp
6187       mov  bp, sp
6188       mov ax, #diskette_param_table2
6189       mov _int13_cdemu.DI+2[bp], ax
6190       mov _int13_cdemu.ES+2[bp], cs
6191       pop  bp
6192 ASM_END
6193       goto int13_success;
6194       break;
6195 
6196     case 0x15: /* read disk drive size */
6197       // FIXME ElTorito Harddisk. What geometry to send ?
6198       SET_AH(0x03);
6199       goto int13_success_noah;
6200       break;
6201 
6202     // all those functions return unimplemented
6203     case 0x0a: /* read disk sectors with ECC */
6204     case 0x0b: /* write disk sectors with ECC */
6205     case 0x18: /* set media type for format */
6206     case 0x41: // IBM/MS installation check
6207       // FIXME ElTorito Harddisk. Darwin would like to use EDD
6208     case 0x42: // IBM/MS extended read
6209     case 0x43: // IBM/MS extended write
6210     case 0x44: // IBM/MS verify sectors
6211     case 0x45: // IBM/MS lock/unlock drive
6212     case 0x46: // IBM/MS eject media
6213     case 0x47: // IBM/MS extended seek
6214     case 0x48: // IBM/MS get drive parameters
6215     case 0x49: // IBM/MS extended media change
6216     case 0x4e: // ? - set hardware configuration
6217     case 0x50: // ? - send packet command
6218     default:
6219       BX_INFO("int13_cdemu function AH=%02x unsupported, returns fail\n", GET_AH());
6220       goto int13_fail;
6221       break;
6222     }
6223 
6224 int13_fail:
6225     SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
6226 int13_fail_noah:
6227     SET_DISK_RET_STATUS(GET_AH());
6228 int13_fail_nostatus:
6229     SET_CF();     // error occurred
6230     return;
6231 
6232 int13_success:
6233     SET_AH(0x00); // no error
6234 int13_success_noah:
6235     SET_DISK_RET_STATUS(0x00);
6236     CLEAR_CF();   // no error
6237     return;
6238 }
6239 
6240 // ---------------------------------------------------------------------------
6241 // End of int13 when emulating a device from the cd
6242 // ---------------------------------------------------------------------------
6243 
6244 #endif // BX_ELTORITO_BOOT
6245 
6246 #else //BX_USE_ATADRV
6247 
6248   void
6249 outLBA(cylinder,hd_heads,head,hd_sectors,sector,dl)
6250   Bit16u cylinder;
6251   Bit16u hd_heads;
6252   Bit16u head;
6253   Bit16u hd_sectors;
6254   Bit16u sector;
6255   Bit16u dl;
6256 {
6257 ASM_START
6258         push   bp
6259         mov    bp, sp
6260         push   eax
6261         push   ebx
6262         push   edx
6263         xor    eax,eax
6264         mov    ax,4[bp]  // cylinder
6265         xor    ebx,ebx
6266         mov    bl,6[bp]  // hd_heads
6267         imul   ebx
6268 
6269         mov    bl,8[bp]  // head
6270         add    eax,ebx
6271         mov    bl,10[bp] // hd_sectors
6272         imul   ebx
6273         mov    bl,12[bp] // sector
6274         add    eax,ebx
6275 
6276         dec    eax
6277         mov    dx,#0x1f3
6278         out    dx,al
6279         mov    dx,#0x1f4
6280         mov    al,ah
6281         out    dx,al
6282         shr    eax,#16
6283         mov    dx,#0x1f5
6284         out    dx,al
6285         and    ah,#0xf
6286         mov    bl,14[bp] // dl
6287         and    bl,#1
6288         shl    bl,#4
6289         or     ah,bl
6290         or     ah,#0xe0
6291         mov    al,ah
6292         mov    dx,#0x01f6
6293         out    dx,al
6294         pop    edx
6295         pop    ebx
6296         pop    eax
6297         pop    bp
6298 ASM_END
6299 }
6300 
6301   void
6302 int13_harddisk(EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
6303   Bit16u EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
6304 {
6305   Bit8u    drive, num_sectors, sector, head, status, mod;
6306   Bit8u    drive_map;
6307   Bit8u    n_drives;
6308   Bit16u   cyl_mod, ax;
6309   Bit16u   max_cylinder, cylinder, total_sectors;
6310   Bit16u   hd_cylinders;
6311   Bit8u    hd_heads, hd_sectors;
6312   Bit16u   val16;
6313   Bit8u    sector_count;
6314   unsigned int i;
6315   Bit16u   tempbx;
6316   Bit16u   dpsize;
6317 
6318   Bit16u   count, segment, offset;
6319   Bit32u   lba;
6320   Bit16u   error;
6321 
6322   BX_DEBUG_INT13_HD("int13 harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
6323 
6324   write_byte(0x0040, 0x008e, 0);  // clear completion flag
6325 
6326   /* at this point, DL is >= 0x80 to be passed from the floppy int13h
6327      handler code */
6328   /* check how many disks first (cmos reg 0x12), return an error if
6329      drive not present */
6330   drive_map = inb_cmos(0x12);
6331   drive_map = (((drive_map & 0xf0)==0) ? 0 : 1) |
6332               (((drive_map & 0x0f)==0) ? 0 : 2);
6333   n_drives = (drive_map==0) ? 0 :
6334     ((drive_map==3) ? 2 : 1);
6335 
6336   if (!(drive_map & (1<<(GET_ELDL()&0x7f)))) { /* allow 0, 1, or 2 disks */
6337     SET_AH(0x01);
6338     SET_DISK_RET_STATUS(0x01);
6339     SET_CF(); /* error occurred */
6340     return;
6341     }
6342 
6343   switch (GET_AH()) {
6344 
6345     case 0x00: /* disk controller reset */
6346 BX_DEBUG_INT13_HD("int13_f00\n");
6347 
6348       SET_AH(0);
6349       SET_DISK_RET_STATUS(0);
6350       set_diskette_ret_status(0);
6351       set_diskette_current_cyl(0, 0); /* current cylinder, diskette 1 */
6352       set_diskette_current_cyl(1, 0); /* current cylinder, diskette 2 */
6353       CLEAR_CF(); /* successful */
6354       return;
6355       break;
6356 
6357     case 0x01: /* read disk status */
6358 BX_DEBUG_INT13_HD("int13_f01\n");
6359       status = read_byte(0x0040, 0x0074);
6360       SET_AH(status);
6361       SET_DISK_RET_STATUS(0);
6362       /* set CF if error status read */
6363       if (status) SET_CF();
6364       else        CLEAR_CF();
6365       return;
6366       break;
6367 
6368     case 0x04: // verify disk sectors
6369     case 0x02: // read disk sectors
6370       drive = GET_ELDL();
6371       get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6372 
6373       num_sectors = GET_AL();
6374       cylinder    = (GET_CL() & 0x00c0) << 2 | GET_CH();
6375       sector      = (GET_CL() & 0x3f);
6376       head        = GET_DH();
6377 
6378 
6379       if (hd_cylinders > 1024) {
6380         if (hd_cylinders <= 2048) {
6381           cylinder <<= 1;
6382           }
6383         else if (hd_cylinders <= 4096) {
6384           cylinder <<= 2;
6385           }
6386         else if (hd_cylinders <= 8192) {
6387           cylinder <<= 3;
6388           }
6389         else { // hd_cylinders <= 16384
6390           cylinder <<= 4;
6391           }
6392 
6393         ax = head / hd_heads;
6394         cyl_mod = ax & 0xff;
6395         head    = ax >> 8;
6396         cylinder |= cyl_mod;
6397         }
6398 
6399       if ( (cylinder >= hd_cylinders) ||
6400            (sector > hd_sectors) ||
6401            (head >= hd_heads) ) {
6402         SET_AH(1);
6403         SET_DISK_RET_STATUS(1);
6404         SET_CF(); /* error occurred */
6405         return;
6406         }
6407 
6408       if ( (num_sectors > 128) || (num_sectors == 0) )
6409         BX_PANIC("int13_harddisk: num_sectors out of range!\n");
6410 
6411       if (head > 15)
6412         BX_PANIC("hard drive BIOS:(read/verify) head > 15\n");
6413 
6414       if ( GET_AH() == 0x04 ) {
6415         SET_AH(0);
6416         SET_DISK_RET_STATUS(0);
6417         CLEAR_CF();
6418         return;
6419         }
6420 
6421       status = inb(0x1f7);
6422       if (status & 0x80) {
6423         BX_PANIC("hard drive BIOS:(read/verify) BUSY bit set\n");
6424         }
6425       outb(0x01f2, num_sectors);
6426       /* activate LBA? (tomv) */
6427       if (hd_heads > 16) {
6428 BX_DEBUG_INT13_HD("CHS: %x %x %x\n", cylinder, head, sector);
6429         outLBA(cylinder,hd_heads,head,hd_sectors,sector,drive);
6430         }
6431       else {
6432         outb(0x01f3, sector);
6433         outb(0x01f4, cylinder & 0x00ff);
6434         outb(0x01f5, cylinder >> 8);
6435         outb(0x01f6, 0xa0 | ((drive & 0x01)<<4) | (head & 0x0f));
6436         }
6437       outb(0x01f7, 0x20);
6438 
6439       while (1) {
6440         status = inb(0x1f7);
6441         if ( !(status & 0x80) ) break;
6442         }
6443 
6444       if (status & 0x01) {
6445         BX_PANIC("hard drive BIOS:(read/verify) read error\n");
6446       } else if ( !(status & 0x08) ) {
6447         BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
6448         BX_PANIC("hard drive BIOS:(read/verify) expected DRQ=1\n");
6449       }
6450 
6451       sector_count = 0;
6452       tempbx = BX;
6453 
6454 ASM_START
6455   sti  ;; enable higher priority interrupts
6456 ASM_END
6457 
6458       while (1) {
6459 ASM_START
6460         ;; store temp bx in real DI register
6461         push bp
6462         mov  bp, sp
6463         mov  di, _int13_harddisk.tempbx + 2 [bp]
6464         pop  bp
6465 
6466         ;; adjust if there will be an overrun
6467         cmp   di, #0xfe00
6468         jbe   i13_f02_no_adjust
6469 i13_f02_adjust:
6470         sub   di, #0x0200 ; sub 512 bytes from offset
6471         mov   ax, es
6472         add   ax, #0x0020 ; add 512 to segment
6473         mov   es, ax
6474 
6475 i13_f02_no_adjust:
6476         mov  cx, #0x0100   ;; counter (256 words = 512b)
6477         mov  dx, #0x01f0  ;; AT data read port
6478 
6479         rep
6480           insw ;; CX words transfered from port(DX) to ES:[DI]
6481 
6482 i13_f02_done:
6483         ;; store real DI register back to temp bx
6484         push bp
6485         mov  bp, sp
6486         mov  _int13_harddisk.tempbx + 2 [bp], di
6487         pop  bp
6488 ASM_END
6489 
6490         sector_count++;
6491         num_sectors--;
6492         if (num_sectors == 0) {
6493           status = inb(0x1f7);
6494           if ( (status & 0xc9) != 0x40 )
6495             BX_PANIC("no sectors left to read/verify, status is %02x\n", (unsigned) status);
6496           break;
6497           }
6498         else {
6499           status = inb(0x1f7);
6500           if ( (status & 0xc9) != 0x48 )
6501             BX_PANIC("more sectors left to read/verify, status is %02x\n", (unsigned) status);
6502           continue;
6503           }
6504         }
6505 
6506       SET_AH(0);
6507       SET_DISK_RET_STATUS(0);
6508       SET_AL(sector_count);
6509       CLEAR_CF(); /* successful */
6510       return;
6511       break;
6512 
6513 
6514     case 0x03: /* write disk sectors */
6515 BX_DEBUG_INT13_HD("int13_f03\n");
6516       drive = GET_ELDL ();
6517       get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6518 
6519       num_sectors = GET_AL();
6520       cylinder    = GET_CH();
6521       cylinder    |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
6522       sector      = (GET_CL() & 0x3f);
6523       head        = GET_DH();
6524 
6525       if (hd_cylinders > 1024) {
6526         if (hd_cylinders <= 2048) {
6527           cylinder <<= 1;
6528           }
6529         else if (hd_cylinders <= 4096) {
6530           cylinder <<= 2;
6531           }
6532         else if (hd_cylinders <= 8192) {
6533           cylinder <<= 3;
6534           }
6535         else { // hd_cylinders <= 16384
6536           cylinder <<= 4;
6537           }
6538 
6539         ax = head / hd_heads;
6540         cyl_mod = ax & 0xff;
6541         head    = ax >> 8;
6542         cylinder |= cyl_mod;
6543         }
6544 
6545       if ( (cylinder >= hd_cylinders) ||
6546            (sector > hd_sectors) ||
6547            (head >= hd_heads) ) {
6548         SET_AH( 1);
6549         SET_DISK_RET_STATUS(1);
6550         SET_CF(); /* error occurred */
6551         return;
6552         }
6553 
6554       if ( (num_sectors > 128) || (num_sectors == 0) )
6555         BX_PANIC("int13_harddisk: num_sectors out of range!\n");
6556 
6557       if (head > 15)
6558         BX_PANIC("hard drive BIOS:(read) head > 15\n");
6559 
6560       status = inb(0x1f7);
6561       if (status & 0x80) {
6562         BX_PANIC("hard drive BIOS:(read) BUSY bit set\n");
6563         }
6564 // should check for Drive Ready Bit also in status reg
6565       outb(0x01f2, num_sectors);
6566 
6567       /* activate LBA? (tomv) */
6568       if (hd_heads > 16) {
6569 BX_DEBUG_INT13_HD("CHS (write): %x %x %x\n", cylinder, head, sector);
6570         outLBA(cylinder,hd_heads,head,hd_sectors,sector,GET_ELDL());
6571         }
6572       else {
6573         outb(0x01f3, sector);
6574         outb(0x01f4, cylinder & 0x00ff);
6575         outb(0x01f5, cylinder >> 8);
6576         outb(0x01f6, 0xa0 | ((GET_ELDL() & 0x01)<<4) | (head & 0x0f));
6577         }
6578       outb(0x01f7, 0x30);
6579 
6580       // wait for busy bit to turn off after seeking
6581       while (1) {
6582         status = inb(0x1f7);
6583         if ( !(status & 0x80) ) break;
6584         }
6585 
6586       if ( !(status & 0x08) ) {
6587         BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
6588         BX_PANIC("hard drive BIOS:(write) data-request bit not set\n");
6589         }
6590 
6591       sector_count = 0;
6592       tempbx = BX;
6593 
6594 ASM_START
6595   sti  ;; enable higher priority interrupts
6596 ASM_END
6597 
6598       while (1) {
6599 ASM_START
6600         ;; store temp bx in real SI register
6601         push bp
6602         mov  bp, sp
6603         mov  si, _int13_harddisk.tempbx + 2 [bp]
6604         pop  bp
6605 
6606         ;; adjust if there will be an overrun
6607         cmp   si, #0xfe00
6608         jbe   i13_f03_no_adjust
6609 i13_f03_adjust:
6610         sub   si, #0x0200 ; sub 512 bytes from offset
6611         mov   ax, es
6612         add   ax, #0x0020 ; add 512 to segment
6613         mov   es, ax
6614 
6615 i13_f03_no_adjust:
6616         mov  cx, #0x0100   ;; counter (256 words = 512b)
6617         mov  dx, #0x01f0  ;; AT data read port
6618 
6619         seg ES
6620         rep
6621           outsw ;; CX words tranfered from ES:[SI] to port(DX)
6622 
6623         ;; store real SI register back to temp bx
6624         push bp
6625         mov  bp, sp
6626         mov  _int13_harddisk.tempbx + 2 [bp], si
6627         pop  bp
6628 ASM_END
6629 
6630         sector_count++;
6631         num_sectors--;
6632         if (num_sectors == 0) {
6633           status = inb(0x1f7);
6634           if ( (status & 0xe9) != 0x40 )
6635             BX_PANIC("no sectors left to write, status is %02x\n", (unsigned) status);
6636           break;
6637           }
6638         else {
6639           status = inb(0x1f7);
6640           if ( (status & 0xc9) != 0x48 )
6641             BX_PANIC("more sectors left to write, status is %02x\n", (unsigned) status);
6642           continue;
6643           }
6644         }
6645 
6646       SET_AH(0);
6647       SET_DISK_RET_STATUS(0);
6648       SET_AL(sector_count);
6649       CLEAR_CF(); /* successful */
6650       return;
6651       break;
6652 
6653     case 0x05: /* format disk track */
6654 BX_DEBUG_INT13_HD("int13_f05\n");
6655       BX_PANIC("format disk track called\n");
6656       /* nop */
6657       SET_AH(0);
6658       SET_DISK_RET_STATUS(0);
6659       CLEAR_CF(); /* successful */
6660       return;
6661       break;
6662 
6663     case 0x08: /* read disk drive parameters */
6664 BX_DEBUG_INT13_HD("int13_f08\n");
6665 
6666       drive = GET_ELDL ();
6667       get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6668 
6669       // translate CHS
6670       //
6671       if (hd_cylinders <= 1024) {
6672         // hd_cylinders >>= 0;
6673         // hd_heads <<= 0;
6674         }
6675       else if (hd_cylinders <= 2048) {
6676         hd_cylinders >>= 1;
6677         hd_heads <<= 1;
6678         }
6679       else if (hd_cylinders <= 4096) {
6680         hd_cylinders >>= 2;
6681         hd_heads <<= 2;
6682         }
6683       else if (hd_cylinders <= 8192) {
6684         hd_cylinders >>= 3;
6685         hd_heads <<= 3;
6686         }
6687       else { // hd_cylinders <= 16384
6688         hd_cylinders >>= 4;
6689         hd_heads <<= 4;
6690         }
6691 
6692       max_cylinder = hd_cylinders - 2; /* 0 based */
6693       SET_AL(0);
6694       SET_CH(max_cylinder & 0xff);
6695       SET_CL(((max_cylinder >> 2) & 0xc0) | (hd_sectors & 0x3f));
6696       SET_DH(hd_heads - 1);
6697       SET_DL(n_drives); /* returns 0, 1, or 2 hard drives */
6698       SET_AH(0);
6699       SET_DISK_RET_STATUS(0);
6700       CLEAR_CF(); /* successful */
6701 
6702       return;
6703       break;
6704 
6705     case 0x09: /* initialize drive parameters */
6706 BX_DEBUG_INT13_HD("int13_f09\n");
6707       SET_AH(0);
6708       SET_DISK_RET_STATUS(0);
6709       CLEAR_CF(); /* successful */
6710       return;
6711       break;
6712 
6713     case 0x0a: /* read disk sectors with ECC */
6714 BX_DEBUG_INT13_HD("int13_f0a\n");
6715     case 0x0b: /* write disk sectors with ECC */
6716 BX_DEBUG_INT13_HD("int13_f0b\n");
6717       BX_PANIC("int13h Functions 0Ah & 0Bh not implemented!\n");
6718       return;
6719       break;
6720 
6721     case 0x0c: /* seek to specified cylinder */
6722 BX_DEBUG_INT13_HD("int13_f0c\n");
6723       BX_INFO("int13h function 0ch (seek) not implemented!\n");
6724       SET_AH(0);
6725       SET_DISK_RET_STATUS(0);
6726       CLEAR_CF(); /* successful */
6727       return;
6728       break;
6729 
6730     case 0x0d: /* alternate disk reset */
6731 BX_DEBUG_INT13_HD("int13_f0d\n");
6732       SET_AH(0);
6733       SET_DISK_RET_STATUS(0);
6734       CLEAR_CF(); /* successful */
6735       return;
6736       break;
6737 
6738     case 0x10: /* check drive ready */
6739 BX_DEBUG_INT13_HD("int13_f10\n");
6740       //SET_AH(0);
6741       //SET_DISK_RET_STATUS(0);
6742       //CLEAR_CF(); /* successful */
6743       //return;
6744       //break;
6745 
6746       // should look at 40:8E also???
6747       status = inb(0x01f7);
6748       if ( (status & 0xc0) == 0x40 ) {
6749         SET_AH(0);
6750         SET_DISK_RET_STATUS(0);
6751         CLEAR_CF(); // drive ready
6752         return;
6753         }
6754       else {
6755         SET_AH(0xAA);
6756         SET_DISK_RET_STATUS(0xAA);
6757         SET_CF(); // not ready
6758         return;
6759         }
6760       break;
6761 
6762     case 0x11: /* recalibrate */
6763 BX_DEBUG_INT13_HD("int13_f11\n");
6764       SET_AH(0);
6765       SET_DISK_RET_STATUS(0);
6766       CLEAR_CF(); /* successful */
6767       return;
6768       break;
6769 
6770     case 0x14: /* controller internal diagnostic */
6771 BX_DEBUG_INT13_HD("int13_f14\n");
6772       SET_AH(0);
6773       SET_DISK_RET_STATUS(0);
6774       CLEAR_CF(); /* successful */
6775       SET_AL(0);
6776       return;
6777       break;
6778 
6779     case 0x15: /* read disk drive size */
6780       drive = GET_ELDL();
6781       get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6782 ASM_START
6783       push bp
6784       mov  bp, sp
6785       mov  al, _int13_harddisk.hd_heads + 2 [bp]
6786       mov  ah, _int13_harddisk.hd_sectors + 2 [bp]
6787       mul  al, ah ;; ax = heads * sectors
6788       mov  bx, _int13_harddisk.hd_cylinders + 2 [bp]
6789       dec  bx     ;; use (cylinders - 1) ???
6790       mul  ax, bx ;; dx:ax = (cylinders -1) * (heads * sectors)
6791       ;; now we need to move the 32bit result dx:ax to what the
6792       ;; BIOS wants which is cx:dx.
6793       ;; and then into CX:DX on the stack
6794       mov  _int13_harddisk.CX + 2 [bp], dx
6795       mov  _int13_harddisk.DX + 2 [bp], ax
6796       pop  bp
6797 ASM_END
6798       SET_AH(3);  // hard disk accessible
6799       SET_DISK_RET_STATUS(0); // ??? should this be 0
6800       CLEAR_CF(); // successful
6801       return;
6802       break;
6803 
6804     case 0x18: // set media type for format
6805     case 0x41: // IBM/MS
6806     case 0x42: // IBM/MS
6807     case 0x43: // IBM/MS
6808     case 0x44: // IBM/MS
6809     case 0x45: // IBM/MS lock/unlock drive
6810     case 0x46: // IBM/MS eject media
6811     case 0x47: // IBM/MS extended seek
6812     case 0x49: // IBM/MS extended media change
6813     case 0x50: // IBM/MS send packet command
6814     default:
6815       BX_INFO("int13_harddisk: unsupported AH=%02x\n", GET_AH());
6816 
6817       SET_AH(1);  // code=invalid function in AH or invalid parameter
6818       SET_DISK_RET_STATUS(1);
6819       SET_CF(); /* unsuccessful */
6820       return;
6821       break;
6822     }
6823 }
6824 
6825 static char panic_msg_reg12h[] = "HD%d cmos reg 12h not type F\n";
6826 static char panic_msg_reg19h[] = "HD%d cmos reg %02xh not user definable type 47\n";
6827 
6828   void
6829 get_hd_geometry(drive, hd_cylinders, hd_heads, hd_sectors)
6830   Bit8u drive;
6831   Bit16u *hd_cylinders;
6832   Bit8u  *hd_heads;
6833   Bit8u  *hd_sectors;
6834 {
6835   Bit8u hd_type;
6836   Bit16u ss;
6837   Bit16u cylinders;
6838   Bit8u iobase;
6839 
6840   ss = get_SS();
6841   if (drive == 0x80) {
6842     hd_type = inb_cmos(0x12) & 0xf0;
6843     if (hd_type != 0xf0)
6844       BX_INFO(panic_msg_reg12h,0);
6845     hd_type = inb_cmos(0x19); // HD0: extended type
6846     if (hd_type != 47)
6847       BX_INFO(panic_msg_reg19h,0,0x19);
6848     iobase = 0x1b;
6849   } else {
6850     hd_type = inb_cmos(0x12) & 0x0f;
6851     if (hd_type != 0x0f)
6852       BX_INFO(panic_msg_reg12h,1);
6853     hd_type = inb_cmos(0x1a); // HD1: extended type
6854     if (hd_type != 47)
6855       BX_INFO(panic_msg_reg19h,0,0x1a);
6856     iobase = 0x24;
6857   }
6858 
6859   // cylinders
6860   cylinders = inb_cmos(iobase) | (inb_cmos(iobase+1) << 8);
6861   write_word(ss, hd_cylinders, cylinders);
6862 
6863   // heads
6864   write_byte(ss, hd_heads, inb_cmos(iobase+2));
6865 
6866   // sectors per track
6867   write_byte(ss, hd_sectors, inb_cmos(iobase+8));
6868 }
6869 
6870 #endif //else BX_USE_ATADRV
6871 
6872 #if BX_SUPPORT_FLOPPY
6873 
6874 //////////////////////
6875 // FLOPPY functions //
6876 //////////////////////
6877 
6878 void floppy_reset_controller()
6879 {
6880   Bit8u val8;
6881 
6882   // Reset controller
6883   val8 = inb(0x03f2);
6884   outb(0x03f2, val8 & ~0x04);
6885   outb(0x03f2, val8 | 0x04);
6886 
6887   // Wait for controller to come out of reset
6888   do {
6889     val8 = inb(0x3f4);
6890   } while ( (val8 & 0xc0) != 0x80 );
6891 }
6892 
6893 void floppy_prepare_controller(drive)
6894   Bit16u drive;
6895 {
6896   Bit8u  val8, dor, prev_reset;
6897 
6898   // set 40:3e bit 7 to 0
6899   val8 = read_byte(0x0040, 0x003e);
6900   val8 &= 0x7f;
6901   write_byte(0x0040, 0x003e, val8);
6902 
6903   // turn on motor of selected drive, DMA & int enabled, normal operation
6904   prev_reset = inb(0x03f2) & 0x04;
6905   if (drive)
6906     dor = 0x20;
6907   else
6908     dor = 0x10;
6909   dor |= 0x0c;
6910   dor |= drive;
6911   outb(0x03f2, dor);
6912 
6913   // reset the disk motor timeout value of INT 08
6914   write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
6915 
6916   // wait for drive readiness
6917   do {
6918     val8 = inb(0x3f4);
6919   } while ( (val8 & 0xc0) != 0x80 );
6920 
6921   if (prev_reset == 0) {
6922     // turn on interrupts
6923 ASM_START
6924     sti
6925 ASM_END
6926     // wait on 40:3e bit 7 to become 1
6927     do {
6928       val8 = read_byte(0x0040, 0x003e);
6929     } while ( (val8 & 0x80) == 0 );
6930     val8 &= 0x7f;
6931 ASM_START
6932     cli
6933 ASM_END
6934     write_byte(0x0040, 0x003e, val8);
6935   }
6936 }
6937 
6938   bx_bool
6939 floppy_media_known(drive)
6940   Bit16u drive;
6941 {
6942   Bit8u  val8;
6943   Bit16u media_state_offset;
6944 
6945   val8 = read_byte(0x0040, 0x003e); // diskette recal status
6946   if (drive)
6947     val8 >>= 1;
6948   val8 &= 0x01;
6949   if (val8 == 0)
6950     return(0);
6951 
6952   media_state_offset = 0x0090;
6953   if (drive)
6954     media_state_offset += 1;
6955 
6956   val8 = read_byte(0x0040, media_state_offset);
6957   val8 = (val8 >> 4) & 0x01;
6958   if (val8 == 0)
6959     return(0);
6960 
6961   // check pass, return KNOWN
6962   return(1);
6963 }
6964 
6965   bx_bool
6966 floppy_media_sense(drive)
6967   Bit16u drive;
6968 {
6969   bx_bool retval;
6970   Bit16u  media_state_offset;
6971   Bit8u   drive_type, config_data, media_state;
6972 
6973   if (floppy_drive_recal(drive) == 0) {
6974     return(0);
6975     }
6976 
6977   // for now cheat and get drive type from CMOS,
6978   // assume media is same as drive type
6979 
6980   // ** config_data **
6981   // Bitfields for diskette media control:
6982   // Bit(s)  Description (Table M0028)
6983   //  7-6  last data rate set by controller
6984   //        00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6985   //  5-4  last diskette drive step rate selected
6986   //        00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
6987   //  3-2  {data rate at start of operation}
6988   //  1-0  reserved
6989 
6990   // ** media_state **
6991   // Bitfields for diskette drive media state:
6992   // Bit(s)  Description (Table M0030)
6993   //  7-6  data rate
6994   //    00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6995   //  5  double stepping required (e.g. 360kB in 1.2MB)
6996   //  4  media type established
6997   //  3  drive capable of supporting 4MB media
6998   //  2-0  on exit from BIOS, contains
6999   //    000 trying 360kB in 360kB
7000   //    001 trying 360kB in 1.2MB
7001   //    010 trying 1.2MB in 1.2MB
7002   //    011 360kB in 360kB established
7003   //    100 360kB in 1.2MB established
7004   //    101 1.2MB in 1.2MB established
7005   //    110 reserved
7006   //    111 all other formats/drives
7007 
7008   drive_type = inb_cmos(0x10);
7009   if (drive == 0)
7010     drive_type >>= 4;
7011   else
7012     drive_type &= 0x0f;
7013   if ( drive_type == 1 ) {
7014     // 360K 5.25" drive
7015     config_data = 0x00; // 0000 0000
7016     media_state = 0x25; // 0010 0101
7017     retval = 1;
7018     }
7019   else if ( drive_type == 2 ) {
7020     // 1.2 MB 5.25" drive
7021     config_data = 0x00; // 0000 0000
7022     media_state = 0x25; // 0010 0101   // need double stepping??? (bit 5)
7023     retval = 1;
7024     }
7025   else if ( drive_type == 3 ) {
7026     // 720K 3.5" drive
7027     config_data = 0x00; // 0000 0000 ???
7028     media_state = 0x17; // 0001 0111
7029     retval = 1;
7030     }
7031   else if ( drive_type == 4 ) {
7032     // 1.44 MB 3.5" drive
7033     config_data = 0x00; // 0000 0000
7034     media_state = 0x17; // 0001 0111
7035     retval = 1;
7036     }
7037   else if ( drive_type == 5 ) {
7038     // 2.88 MB 3.5" drive
7039     config_data = 0xCC; // 1100 1100
7040     media_state = 0xD7; // 1101 0111
7041     retval = 1;
7042     }
7043   //
7044   // Extended floppy size uses special cmos setting
7045   else if ( drive_type == 6 ) {
7046     // 160k 5.25" drive
7047     config_data = 0x00; // 0000 0000
7048     media_state = 0x27; // 0010 0111
7049     retval = 1;
7050     }
7051   else if ( drive_type == 7 ) {
7052     // 180k 5.25" drive
7053     config_data = 0x00; // 0000 0000
7054     media_state = 0x27; // 0010 0111
7055     retval = 1;
7056     }
7057   else if ( drive_type == 8 ) {
7058     // 320k 5.25" drive
7059     config_data = 0x00; // 0000 0000
7060     media_state = 0x27; // 0010 0111
7061     retval = 1;
7062     }
7063 
7064   else {
7065     // not recognized
7066     config_data = 0x00; // 0000 0000
7067     media_state = 0x00; // 0000 0000
7068     retval = 0;
7069     }
7070 
7071   if (drive == 0)
7072     media_state_offset = 0x90;
7073   else
7074     media_state_offset = 0x91;
7075   write_byte(0x0040, 0x008B, config_data);
7076   write_byte(0x0040, media_state_offset, media_state);
7077 
7078   return(retval);
7079 }
7080 
7081   bx_bool
7082 floppy_drive_recal(drive)
7083   Bit16u drive;
7084 {
7085   Bit8u  val8;
7086   Bit16u curr_cyl_offset;
7087 
7088   floppy_prepare_controller(drive);
7089 
7090   // send Recalibrate command (2 bytes) to controller
7091   outb(0x03f5, 0x07);  // 07: Recalibrate
7092   outb(0x03f5, drive); // 0=drive0, 1=drive1
7093 
7094   // turn on interrupts
7095 ASM_START
7096   sti
7097 ASM_END
7098 
7099   // wait on 40:3e bit 7 to become 1
7100   do {
7101     val8 = (read_byte(0x0040, 0x003e) & 0x80);
7102   } while ( val8 == 0 );
7103 
7104   val8 = 0; // separate asm from while() loop
7105   // turn off interrupts
7106 ASM_START
7107   cli
7108 ASM_END
7109 
7110   // set 40:3e bit 7 to 0, and calibrated bit
7111   val8 = read_byte(0x0040, 0x003e);
7112   val8 &= 0x7f;
7113   if (drive) {
7114     val8 |= 0x02; // Drive 1 calibrated
7115     curr_cyl_offset = 0x0095;
7116   } else {
7117     val8 |= 0x01; // Drive 0 calibrated
7118     curr_cyl_offset = 0x0094;
7119   }
7120   write_byte(0x0040, 0x003e, val8);
7121   write_byte(0x0040, curr_cyl_offset, 0); // current cylinder is 0
7122 
7123   return(1);
7124 }
7125 
7126 
7127 
7128   bx_bool
7129 floppy_drive_exists(drive)
7130   Bit16u drive;
7131 {
7132   Bit8u  drive_type;
7133 
7134   // check CMOS to see if drive exists
7135   drive_type = inb_cmos(0x10);
7136   if (drive == 0)
7137     drive_type >>= 4;
7138   else
7139     drive_type &= 0x0f;
7140   if ( drive_type == 0 )
7141     return(0);
7142   else
7143     return(1);
7144 }
7145 
7146   void
7147 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
7148   Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
7149 {
7150   Bit8u  drive, num_sectors, track, sector, head, status;
7151   Bit16u base_address, base_count, base_es;
7152   Bit8u  page, mode_register, val8, dor;
7153   Bit8u  return_status[7];
7154   Bit8u  drive_type, num_floppies, ah;
7155   Bit16u es, last_addr;
7156 
7157   BX_DEBUG_INT13_FL("int13_diskette: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
7158 
7159   ah = GET_AH();
7160 
7161   switch ( ah ) {
7162     case 0x00: // diskette controller reset
7163 BX_DEBUG_INT13_FL("floppy f00\n");
7164       drive = GET_ELDL();
7165       if (drive > 1) {
7166         SET_AH(1); // invalid param
7167         set_diskette_ret_status(1);
7168         SET_CF();
7169         return;
7170       }
7171       drive_type = inb_cmos(0x10);
7172 
7173       if (drive == 0)
7174         drive_type >>= 4;
7175       else
7176         drive_type &= 0x0f;
7177       if (drive_type == 0) {
7178         SET_AH(0x80); // drive not responding
7179         set_diskette_ret_status(0x80);
7180         SET_CF();
7181         return;
7182       }
7183       SET_AH(0);
7184       set_diskette_ret_status(0);
7185       CLEAR_CF(); // successful
7186       set_diskette_current_cyl(drive, 0); // current cylinder
7187       return;
7188 
7189     case 0x01: // Read Diskette Status
7190       CLEAR_CF();
7191       val8 = read_byte(0x0000, 0x0441);
7192       SET_AH(val8);
7193       if (val8) {
7194         SET_CF();
7195       }
7196       return;
7197 
7198     case 0x02: // Read Diskette Sectors
7199     case 0x03: // Write Diskette Sectors
7200     case 0x04: // Verify Diskette Sectors
7201       num_sectors = GET_AL();
7202       track       = GET_CH();
7203       sector      = GET_CL();
7204       head        = GET_DH();
7205       drive       = GET_ELDL();
7206 
7207       if ((drive > 1) || (head > 1) || (sector == 0) ||
7208           (num_sectors == 0) || (num_sectors > 72)) {
7209         BX_INFO("int13_diskette: read/write/verify: parameter out of range\n");
7210         SET_AH(1);
7211         set_diskette_ret_status(1);
7212         SET_AL(0); // no sectors read
7213         SET_CF(); // error occurred
7214         return;
7215       }
7216 
7217       // see if drive exists
7218       if (floppy_drive_exists(drive) == 0) {
7219         SET_AH(0x80); // not responding
7220         set_diskette_ret_status(0x80);
7221         SET_AL(0); // no sectors read
7222         SET_CF(); // error occurred
7223         return;
7224       }
7225 
7226       // see if media in drive, and type is known
7227       if (floppy_media_known(drive) == 0) {
7228         if (floppy_media_sense(drive) == 0) {
7229           SET_AH(0x0C); // Media type not found
7230           set_diskette_ret_status(0x0C);
7231           SET_AL(0); // no sectors read
7232           SET_CF(); // error occurred
7233           return;
7234         }
7235       }
7236 
7237       if (ah == 0x02) {
7238         // Read Diskette Sectors
7239 
7240         //-----------------------------------
7241         // set up DMA controller for transfer
7242         //-----------------------------------
7243 
7244         // es:bx = pointer to where to place information from diskette
7245         // port 04: DMA-1 base and current address, channel 2
7246         // port 05: DMA-1 base and current count, channel 2
7247         page = (ES >> 12);   // upper 4 bits
7248         base_es = (ES << 4); // lower 16bits contributed by ES
7249         base_address = base_es + BX; // lower 16 bits of address
7250                                      // contributed by ES:BX
7251         if ( base_address < base_es ) {
7252           // in case of carry, adjust page by 1
7253           page++;
7254         }
7255         base_count = (num_sectors * 512) - 1;
7256 
7257         // check for 64K boundary overrun
7258         last_addr = base_address + base_count;
7259         if (last_addr < base_address) {
7260           SET_AH(0x09);
7261           set_diskette_ret_status(0x09);
7262           SET_AL(0); // no sectors read
7263           SET_CF(); // error occurred
7264           return;
7265         }
7266 
7267         BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
7268         outb(0x000a, 0x06);
7269 
7270   BX_DEBUG_INT13_FL("clear flip-flop\n");
7271         outb(0x000c, 0x00); // clear flip-flop
7272         outb(0x0004, base_address);
7273         outb(0x0004, base_address>>8);
7274   BX_DEBUG_INT13_FL("clear flip-flop\n");
7275         outb(0x000c, 0x00); // clear flip-flop
7276         outb(0x0005, base_count);
7277         outb(0x0005, base_count>>8);
7278 
7279         // port 0b: DMA-1 Mode Register
7280         mode_register = 0x46; // single mode, increment, autoinit disable,
7281                               // transfer type=write, channel 2
7282   BX_DEBUG_INT13_FL("setting mode register\n");
7283         outb(0x000b, mode_register);
7284 
7285   BX_DEBUG_INT13_FL("setting page register\n");
7286         // port 81: DMA-1 Page Register, channel 2
7287         outb(0x0081, page);
7288 
7289   BX_DEBUG_INT13_FL("unmask chan 2\n");
7290         outb(0x000a, 0x02); // unmask channel 2
7291 
7292         BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
7293         outb(0x000a, 0x02);
7294 
7295         //--------------------------------------
7296         // set up floppy controller for transfer
7297         //--------------------------------------
7298         floppy_prepare_controller(drive);
7299 
7300         // send read-normal-data command (9 bytes) to controller
7301         outb(0x03f5, 0xe6); // e6: read normal data
7302         outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7303         outb(0x03f5, track);
7304         outb(0x03f5, head);
7305         outb(0x03f5, sector);
7306         outb(0x03f5, 2); // 512 byte sector size
7307         outb(0x03f5, sector + num_sectors - 1); // last sector to read on track
7308         outb(0x03f5, 0); // Gap length
7309         outb(0x03f5, 0xff); // Gap length
7310 
7311         // turn on interrupts
7312   ASM_START
7313         sti
7314   ASM_END
7315 
7316         // wait on 40:3e bit 7 to become 1
7317         do {
7318           val8 = read_byte(0x0040, 0x0040);
7319           if (val8 == 0) {
7320             floppy_reset_controller();
7321             SET_AH(0x80); // drive not ready (timeout)
7322             set_diskette_ret_status(0x80);
7323             SET_AL(0); // no sectors read
7324             SET_CF(); // error occurred
7325             return;
7326           }
7327           val8 = (read_byte(0x0040, 0x003e) & 0x80);
7328         } while ( val8 == 0 );
7329 
7330         val8 = 0; // separate asm from while() loop
7331         // turn off interrupts
7332   ASM_START
7333         cli
7334   ASM_END
7335 
7336         // set 40:3e bit 7 to 0
7337         val8 = read_byte(0x0040, 0x003e);
7338         val8 &= 0x7f;
7339         write_byte(0x0040, 0x003e, val8);
7340 
7341         // check port 3f4 for accessibility to status bytes
7342         val8 = inb(0x3f4);
7343         if ( (val8 & 0xc0) != 0xc0 )
7344           BX_PANIC("int13_diskette: ctrl not ready\n");
7345 
7346         // read 7 return status bytes from controller
7347         // using loop index broken, have to unroll...
7348         return_status[0] = inb(0x3f5);
7349         return_status[1] = inb(0x3f5);
7350         return_status[2] = inb(0x3f5);
7351         return_status[3] = inb(0x3f5);
7352         return_status[4] = inb(0x3f5);
7353         return_status[5] = inb(0x3f5);
7354         return_status[6] = inb(0x3f5);
7355         // record in BIOS Data Area
7356         write_byte(0x0040, 0x0042, return_status[0]);
7357         write_byte(0x0040, 0x0043, return_status[1]);
7358         write_byte(0x0040, 0x0044, return_status[2]);
7359         write_byte(0x0040, 0x0045, return_status[3]);
7360         write_byte(0x0040, 0x0046, return_status[4]);
7361         write_byte(0x0040, 0x0047, return_status[5]);
7362         write_byte(0x0040, 0x0048, return_status[6]);
7363 
7364         if ( (return_status[0] & 0xc0) != 0 ) {
7365           SET_AH(0x20);
7366           set_diskette_ret_status(0x20);
7367           SET_AL(0); // no sectors read
7368           SET_CF(); // error occurred
7369           return;
7370         }
7371 
7372         // ??? should track be new val from return_status[3] ?
7373         set_diskette_current_cyl(drive, track);
7374         // AL = number of sectors read (same value as passed)
7375         SET_AH(0x00); // success
7376         CLEAR_CF();   // success
7377         return;
7378       } else if (ah == 0x03) {
7379         // Write Diskette Sectors
7380 
7381         //-----------------------------------
7382         // set up DMA controller for transfer
7383         //-----------------------------------
7384 
7385         // es:bx = pointer to where to place information from diskette
7386         // port 04: DMA-1 base and current address, channel 2
7387         // port 05: DMA-1 base and current count, channel 2
7388         page = (ES >> 12);   // upper 4 bits
7389         base_es = (ES << 4); // lower 16bits contributed by ES
7390         base_address = base_es + BX; // lower 16 bits of address
7391                                      // contributed by ES:BX
7392         if ( base_address < base_es ) {
7393           // in case of carry, adjust page by 1
7394           page++;
7395         }
7396         base_count = (num_sectors * 512) - 1;
7397 
7398         // check for 64K boundary overrun
7399         last_addr = base_address + base_count;
7400         if (last_addr < base_address) {
7401           SET_AH(0x09);
7402           set_diskette_ret_status(0x09);
7403           SET_AL(0); // no sectors read
7404           SET_CF(); // error occurred
7405           return;
7406         }
7407 
7408         BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
7409         outb(0x000a, 0x06);
7410 
7411         outb(0x000c, 0x00); // clear flip-flop
7412         outb(0x0004, base_address);
7413         outb(0x0004, base_address>>8);
7414         outb(0x000c, 0x00); // clear flip-flop
7415         outb(0x0005, base_count);
7416         outb(0x0005, base_count>>8);
7417 
7418         // port 0b: DMA-1 Mode Register
7419         mode_register = 0x4a; // single mode, increment, autoinit disable,
7420                               // transfer type=read, channel 2
7421         outb(0x000b, mode_register);
7422 
7423         // port 81: DMA-1 Page Register, channel 2
7424         outb(0x0081, page);
7425 
7426         BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
7427         outb(0x000a, 0x02);
7428 
7429         //--------------------------------------
7430         // set up floppy controller for transfer
7431         //--------------------------------------
7432         floppy_prepare_controller(drive);
7433 
7434         // send write-normal-data command (9 bytes) to controller
7435         outb(0x03f5, 0xc5); // c5: write normal data
7436         outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7437         outb(0x03f5, track);
7438         outb(0x03f5, head);
7439         outb(0x03f5, sector);
7440         outb(0x03f5, 2); // 512 byte sector size
7441         outb(0x03f5, sector + num_sectors - 1); // last sector to write on track
7442         outb(0x03f5, 0); // Gap length
7443         outb(0x03f5, 0xff); // Gap length
7444 
7445         // turn on interrupts
7446   ASM_START
7447         sti
7448   ASM_END
7449 
7450         // wait on 40:3e bit 7 to become 1
7451         do {
7452           val8 = read_byte(0x0040, 0x0040);
7453           if (val8 == 0) {
7454             floppy_reset_controller();
7455             SET_AH(0x80); // drive not ready (timeout)
7456             set_diskette_ret_status(0x80);
7457             SET_AL(0); // no sectors written
7458             SET_CF(); // error occurred
7459             return;
7460           }
7461           val8 = (read_byte(0x0040, 0x003e) & 0x80);
7462         } while ( val8 == 0 );
7463 
7464         val8 = 0; // separate asm from while() loop
7465         // turn off interrupts
7466   ASM_START
7467         cli
7468   ASM_END
7469 
7470         // set 40:3e bit 7 to 0
7471         val8 = read_byte(0x0040, 0x003e);
7472         val8 &= 0x7f;
7473         write_byte(0x0040, 0x003e, val8);
7474 
7475         // check port 3f4 for accessibility to status bytes
7476         val8 = inb(0x3f4);
7477         if ( (val8 & 0xc0) != 0xc0 )
7478           BX_PANIC("int13_diskette: ctrl not ready\n");
7479 
7480         // read 7 return status bytes from controller
7481         // using loop index broken, have to unroll...
7482         return_status[0] = inb(0x3f5);
7483         return_status[1] = inb(0x3f5);
7484         return_status[2] = inb(0x3f5);
7485         return_status[3] = inb(0x3f5);
7486         return_status[4] = inb(0x3f5);
7487         return_status[5] = inb(0x3f5);
7488         return_status[6] = inb(0x3f5);
7489         // record in BIOS Data Area
7490         write_byte(0x0040, 0x0042, return_status[0]);
7491         write_byte(0x0040, 0x0043, return_status[1]);
7492         write_byte(0x0040, 0x0044, return_status[2]);
7493         write_byte(0x0040, 0x0045, return_status[3]);
7494         write_byte(0x0040, 0x0046, return_status[4]);
7495         write_byte(0x0040, 0x0047, return_status[5]);
7496         write_byte(0x0040, 0x0048, return_status[6]);
7497 
7498         if ( (return_status[0] & 0xc0) != 0 ) {
7499           if ( (return_status[1] & 0x02) != 0 ) {
7500             // diskette not writable.
7501             // AH=status code=0x03 (tried to write on write-protected disk)
7502             // AL=number of sectors written=0
7503             AX = 0x0300;
7504             SET_CF();
7505             return;
7506           } else {
7507             BX_PANIC("int13_diskette_function: read error\n");
7508           }
7509         }
7510 
7511         // ??? should track be new val from return_status[3] ?
7512         set_diskette_current_cyl(drive, track);
7513         // AL = number of sectors read (same value as passed)
7514         SET_AH(0x00); // success
7515         CLEAR_CF();   // success
7516         return;
7517       } else {  // if (ah == 0x04)
7518         // Verify Diskette Sectors
7519 
7520         // ??? should track be new val from return_status[3] ?
7521         set_diskette_current_cyl(drive, track);
7522         // AL = number of sectors verified (same value as passed)
7523         CLEAR_CF();   // success
7524         SET_AH(0x00); // success
7525         return;
7526       }
7527       break;
7528 
7529     case 0x05: // format diskette track
7530 BX_DEBUG_INT13_FL("floppy f05\n");
7531 
7532       num_sectors = GET_AL();
7533       track       = GET_CH();
7534       head        = GET_DH();
7535       drive       = GET_ELDL();
7536 
7537       if ((drive > 1) || (head > 1) || (track > 79) ||
7538           (num_sectors == 0) || (num_sectors > 18)) {
7539         SET_AH(1);
7540         set_diskette_ret_status(1);
7541         SET_CF(); // error occurred
7542       }
7543 
7544       // see if drive exists
7545       if (floppy_drive_exists(drive) == 0) {
7546         SET_AH(0x80); // drive not responding
7547         set_diskette_ret_status(0x80);
7548         SET_CF(); // error occurred
7549         return;
7550       }
7551 
7552       // see if media in drive, and type is known
7553       if (floppy_media_known(drive) == 0) {
7554         if (floppy_media_sense(drive) == 0) {
7555           SET_AH(0x0C); // Media type not found
7556           set_diskette_ret_status(0x0C);
7557           SET_AL(0); // no sectors read
7558           SET_CF(); // error occurred
7559           return;
7560         }
7561       }
7562 
7563       // set up DMA controller for transfer
7564       page = (ES >> 12);   // upper 4 bits
7565       base_es = (ES << 4); // lower 16bits contributed by ES
7566       base_address = base_es + BX; // lower 16 bits of address
7567                                    // contributed by ES:BX
7568       if ( base_address < base_es ) {
7569         // in case of carry, adjust page by 1
7570         page++;
7571       }
7572       base_count = (num_sectors * 4) - 1;
7573 
7574       // check for 64K boundary overrun
7575       last_addr = base_address + base_count;
7576       if (last_addr < base_address) {
7577         SET_AH(0x09);
7578         set_diskette_ret_status(0x09);
7579         SET_AL(0); // no sectors read
7580         SET_CF(); // error occurred
7581         return;
7582       }
7583 
7584       outb(0x000a, 0x06);
7585       outb(0x000c, 0x00); // clear flip-flop
7586       outb(0x0004, base_address);
7587       outb(0x0004, base_address>>8);
7588       outb(0x000c, 0x00); // clear flip-flop
7589       outb(0x0005, base_count);
7590       outb(0x0005, base_count>>8);
7591       mode_register = 0x4a; // single mode, increment, autoinit disable,
7592                             // transfer type=read, channel 2
7593       outb(0x000b, mode_register);
7594       // port 81: DMA-1 Page Register, channel 2
7595       outb(0x0081, page);
7596       outb(0x000a, 0x02);
7597 
7598       // set up floppy controller for transfer
7599       floppy_prepare_controller(drive);
7600 
7601       // send format-track command (6 bytes) to controller
7602       outb(0x03f5, 0x4d); // 4d: format track
7603       outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7604       outb(0x03f5, 2); // 512 byte sector size
7605       outb(0x03f5, num_sectors); // number of sectors per track
7606       outb(0x03f5, 0); // Gap length
7607       outb(0x03f5, 0xf6); // Fill byte
7608       // turn on interrupts
7609   ASM_START
7610       sti
7611   ASM_END
7612 
7613       // wait on 40:3e bit 7 to become 1
7614       do {
7615         val8 = read_byte(0x0040, 0x0040);
7616         if (val8 == 0) {
7617           floppy_reset_controller();
7618           SET_AH(0x80); // drive not ready (timeout)
7619           set_diskette_ret_status(0x80);
7620           SET_CF(); // error occurred
7621           return;
7622         }
7623         val8 = (read_byte(0x0040, 0x003e) & 0x80);
7624       } while ( val8 == 0 );
7625 
7626       val8 = 0; // separate asm from while() loop
7627       // turn off interrupts
7628   ASM_START
7629       cli
7630   ASM_END
7631       // set 40:3e bit 7 to 0
7632       val8 = read_byte(0x0040, 0x003e);
7633       val8 &= 0x7f;
7634       write_byte(0x0040, 0x003e, val8);
7635       // check port 3f4 for accessibility to status bytes
7636       val8 = inb(0x3f4);
7637       if ( (val8 & 0xc0) != 0xc0 )
7638         BX_PANIC("int13_diskette: ctrl not ready\n");
7639 
7640       // read 7 return status bytes from controller
7641       // using loop index broken, have to unroll...
7642       return_status[0] = inb(0x3f5);
7643       return_status[1] = inb(0x3f5);
7644       return_status[2] = inb(0x3f5);
7645       return_status[3] = inb(0x3f5);
7646       return_status[4] = inb(0x3f5);
7647       return_status[5] = inb(0x3f5);
7648       return_status[6] = inb(0x3f5);
7649       // record in BIOS Data Area
7650       write_byte(0x0040, 0x0042, return_status[0]);
7651       write_byte(0x0040, 0x0043, return_status[1]);
7652       write_byte(0x0040, 0x0044, return_status[2]);
7653       write_byte(0x0040, 0x0045, return_status[3]);
7654       write_byte(0x0040, 0x0046, return_status[4]);
7655       write_byte(0x0040, 0x0047, return_status[5]);
7656       write_byte(0x0040, 0x0048, return_status[6]);
7657 
7658       if ( (return_status[0] & 0xc0) != 0 ) {
7659         if ( (return_status[1] & 0x02) != 0 ) {
7660           // diskette not writable.
7661           // AH=status code=0x03 (tried to write on write-protected disk)
7662           // AL=number of sectors written=0
7663           AX = 0x0300;
7664           SET_CF();
7665           return;
7666         } else {
7667           BX_PANIC("int13_diskette_function: write error\n");
7668         }
7669       }
7670 
7671       SET_AH(0);
7672       set_diskette_ret_status(0);
7673       set_diskette_current_cyl(drive, 0);
7674       CLEAR_CF(); // successful
7675       return;
7676 
7677 
7678     case 0x08: // read diskette drive parameters
7679 BX_DEBUG_INT13_FL("floppy f08\n");
7680       drive = GET_ELDL();
7681 
7682       if (drive > 1) {
7683         AX = 0;
7684         BX = 0;
7685         CX = 0;
7686         DX = 0;
7687         ES = 0;
7688         DI = 0;
7689         SET_DL(num_floppies);
7690         SET_CF();
7691         return;
7692         }
7693 
7694       drive_type = inb_cmos(0x10);
7695       num_floppies = 0;
7696       if (drive_type & 0xf0)
7697         num_floppies++;
7698       if (drive_type & 0x0f)
7699         num_floppies++;
7700 
7701       if (drive == 0)
7702         drive_type >>= 4;
7703       else
7704         drive_type &= 0x0f;
7705 
7706       SET_BH(0);
7707       SET_BL(drive_type);
7708       SET_AH(0);
7709       SET_AL(0);
7710       SET_DL(num_floppies);
7711 
7712       switch (drive_type) {
7713         case 0: // none
7714           CX = 0;
7715           SET_DH(0); // max head #
7716           break;
7717 
7718         case 1: // 360KB, 5.25"
7719           CX = 0x2709; // 40 tracks, 9 sectors
7720           SET_DH(1); // max head #
7721           break;
7722 
7723         case 2: // 1.2MB, 5.25"
7724           CX = 0x4f0f; // 80 tracks, 15 sectors
7725           SET_DH(1); // max head #
7726           break;
7727 
7728         case 3: // 720KB, 3.5"
7729           CX = 0x4f09; // 80 tracks, 9 sectors
7730           SET_DH(1); // max head #
7731           break;
7732 
7733         case 4: // 1.44MB, 3.5"
7734           CX = 0x4f12; // 80 tracks, 18 sectors
7735           SET_DH(1); // max head #
7736           break;
7737 
7738         case 5: // 2.88MB, 3.5"
7739           CX = 0x4f24; // 80 tracks, 36 sectors
7740           SET_DH(1); // max head #
7741           break;
7742 
7743         case 6: // 160k, 5.25"
7744           CX = 0x2708; // 40 tracks, 8 sectors
7745           SET_DH(0); // max head #
7746           break;
7747 
7748         case 7: // 180k, 5.25"
7749           CX = 0x2709; // 40 tracks, 9 sectors
7750           SET_DH(0); // max head #
7751           break;
7752 
7753         case 8: // 320k, 5.25"
7754           CX = 0x2708; // 40 tracks, 8 sectors
7755           SET_DH(1); // max head #
7756           break;
7757 
7758         default: // ?
7759           BX_PANIC("floppy: int13: bad floppy type\n");
7760         }
7761 
7762       /* set es & di to point to 11 byte diskette param table in ROM */
7763 ASM_START
7764       push bp
7765       mov  bp, sp
7766       mov ax, #diskette_param_table2
7767       mov _int13_diskette_function.DI+2[bp], ax
7768       mov _int13_diskette_function.ES+2[bp], cs
7769       pop  bp
7770 ASM_END
7771       CLEAR_CF(); // success
7772       /* disk status not changed upon success */
7773       return;
7774 
7775 
7776     case 0x15: // read diskette drive type
7777 BX_DEBUG_INT13_FL("floppy f15\n");
7778       drive = GET_ELDL();
7779       if (drive > 1) {
7780         SET_AH(0); // only 2 drives supported
7781         // set_diskette_ret_status here ???
7782         SET_CF();
7783         return;
7784         }
7785       drive_type = inb_cmos(0x10);
7786 
7787       if (drive == 0)
7788         drive_type >>= 4;
7789       else
7790         drive_type &= 0x0f;
7791       CLEAR_CF(); // successful, not present
7792       if (drive_type==0) {
7793         SET_AH(0); // drive not present
7794         }
7795       else {
7796         SET_AH(1); // drive present, does not support change line
7797         }
7798 
7799       return;
7800 
7801     case 0x16: // get diskette change line status
7802 BX_DEBUG_INT13_FL("floppy f16\n");
7803       drive = GET_ELDL();
7804       if (drive > 1) {
7805         SET_AH(0x01); // invalid drive
7806         set_diskette_ret_status(0x01);
7807         SET_CF();
7808         return;
7809         }
7810 
7811       SET_AH(0x06); // change line not supported
7812       set_diskette_ret_status(0x06);
7813       SET_CF();
7814       return;
7815 
7816     case 0x17: // set diskette type for format(old)
7817 BX_DEBUG_INT13_FL("floppy f17\n");
7818       /* not used for 1.44M floppies */
7819       SET_AH(0x01); // not supported
7820       set_diskette_ret_status(1); /* not supported */
7821       SET_CF();
7822       return;
7823 
7824     case 0x18: // set diskette type for format(new)
7825 BX_DEBUG_INT13_FL("floppy f18\n");
7826       SET_AH(0x01); // do later
7827       set_diskette_ret_status(1);
7828       SET_CF();
7829       return;
7830 
7831     default:
7832         BX_INFO("int13_diskette: unsupported AH=%02x\n", GET_AH());
7833 
7834       // if ( (ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e) ) {
7835         SET_AH(0x01); // ???
7836         set_diskette_ret_status(1);
7837         SET_CF();
7838         return;
7839       //   }
7840     }
7841 }
7842 #else  // #if BX_SUPPORT_FLOPPY
7843   void
7844 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
7845   Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
7846 {
7847   Bit8u  val8;
7848 
7849   switch ( GET_AH() ) {
7850 
7851     case 0x01: // Read Diskette Status
7852       CLEAR_CF();
7853       val8 = read_byte(0x0000, 0x0441);
7854       SET_AH(val8);
7855       if (val8) {
7856         SET_CF();
7857         }
7858       return;
7859 
7860     default:
7861       SET_CF();
7862       write_byte(0x0000, 0x0441, 0x01);
7863       SET_AH(0x01);
7864     }
7865 }
7866 #endif  // #if BX_SUPPORT_FLOPPY
7867 
7868  void
7869 set_diskette_ret_status(value)
7870   Bit8u value;
7871 {
7872   write_byte(0x0040, 0x0041, value);
7873 }
7874 
7875   void
7876 set_diskette_current_cyl(drive, cyl)
7877   Bit8u drive;
7878   Bit8u cyl;
7879 {
7880   if (drive > 1)
7881     BX_PANIC("set_diskette_current_cyl(): drive > 1\n");
7882   write_byte(0x0040, 0x0094+drive, cyl);
7883 }
7884 
7885   void
7886 determine_floppy_media(drive)
7887   Bit16u drive;
7888 {
7889 #if 0
7890   Bit8u  val8, DOR, ctrl_info;
7891 
7892   ctrl_info = read_byte(0x0040, 0x008F);
7893   if (drive==1)
7894     ctrl_info >>= 4;
7895   else
7896     ctrl_info &= 0x0f;
7897 
7898 #if 0
7899   if (drive == 0) {
7900     DOR = 0x1c; // DOR: drive0 motor on, DMA&int enabled, normal op, drive select 0
7901     }
7902   else {
7903     DOR = 0x2d; // DOR: drive1 motor on, DMA&int enabled, normal op, drive select 1
7904     }
7905 #endif
7906 
7907   if ( (ctrl_info & 0x04) != 0x04 ) {
7908     // Drive not determined means no drive exists, done.
7909     return;
7910     }
7911 
7912 #if 0
7913   // check Main Status Register for readiness
7914   val8 = inb(0x03f4) & 0x80; // Main Status Register
7915   if (val8 != 0x80)
7916     BX_PANIC("d_f_m: MRQ bit not set\n");
7917 
7918   // change line
7919 
7920   // existing BDA values
7921 
7922   // turn on drive motor
7923   outb(0x03f2, DOR); // Digital Output Register
7924   //
7925 #endif
7926   BX_PANIC("d_f_m: OK so far\n");
7927 #endif
7928 }
7929 
7930   void
7931 int17_function(regs, ds, iret_addr)
7932   pusha_regs_t regs; // regs pushed from PUSHA instruction
7933   Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7934   iret_addr_t  iret_addr; // CS,IP,Flags pushed from original INT call
7935 {
7936   Bit16u addr,timeout;
7937   Bit8u val8;
7938 
7939   ASM_START
7940   sti
7941   ASM_END
7942 
7943   addr = read_word(0x0040, (regs.u.r16.dx << 1) + 8);
7944   if ((regs.u.r8.ah < 3) && (regs.u.r16.dx < 3) && (addr > 0)) {
7945     timeout = read_byte(0x0040, 0x0078 + regs.u.r16.dx) << 8;
7946     if (regs.u.r8.ah == 0) {
7947       outb(addr, regs.u.r8.al);
7948       val8 = inb(addr+2);
7949       outb(addr+2, val8 | 0x01); // send strobe
7950       ASM_START
7951       nop
7952       ASM_END
7953       outb(addr+2, val8 & ~0x01);
7954       while (((inb(addr+1) & 0x40) == 0x40) && (timeout)) {
7955         timeout--;
7956       }
7957     }
7958     if (regs.u.r8.ah == 1) {
7959       val8 = inb(addr+2);
7960       outb(addr+2, val8 & ~0x04); // send init
7961       ASM_START
7962       nop
7963       ASM_END
7964       outb(addr+2, val8 | 0x04);
7965     }
7966     val8 = inb(addr+1);
7967     regs.u.r8.ah = (val8 ^ 0x48);
7968     if (!timeout) regs.u.r8.ah |= 0x01;
7969     ClearCF(iret_addr.flags);
7970   } else {
7971     SetCF(iret_addr.flags); // Unsupported
7972   }
7973 }
7974 
7975 void
7976 int19_function(seq_nr)
7977 Bit16u seq_nr;
7978 {
7979   Bit16u ebda_seg=read_word(0x0040,0x000E);
7980   Bit16u bootdev;
7981   Bit8u  bootdrv;
7982   Bit8u  bootchk;
7983   Bit16u bootseg;
7984   Bit16u bootip;
7985   Bit16u status;
7986   Bit16u bootfirst;
7987 
7988   ipl_entry_t e;
7989 
7990   // if BX_ELTORITO_BOOT is not defined, old behavior
7991   //   check bit 5 in CMOS reg 0x2d.  load either 0x00 or 0x80 into DL
7992   //   in preparation for the intial INT 13h (0=floppy A:, 0x80=C:)
7993   //     0: system boot sequence, first drive C: then A:
7994   //     1: system boot sequence, first drive A: then C:
7995   // else BX_ELTORITO_BOOT is defined
7996   //   CMOS regs 0x3D and 0x38 contain the boot sequence:
7997   //     CMOS reg 0x3D & 0x0f : 1st boot device
7998   //     CMOS reg 0x3D & 0xf0 : 2nd boot device
7999   //     CMOS reg 0x38 & 0xf0 : 3rd boot device
8000   //   boot device codes:
8001   //     0x00 : not defined
8002   //     0x01 : first floppy
8003   //     0x02 : first harddrive
8004   //     0x03 : first cdrom
8005   //     0x04 - 0x0f : PnP expansion ROMs (e.g. Etherboot)
8006   //     else : boot failure
8007 
8008   // Get the boot sequence
8009 #if BX_ELTORITO_BOOT
8010   bootdev = inb_cmos(0x3d);
8011   bootdev |= ((inb_cmos(0x38) & 0xf0) << 4);
8012   bootdev >>= 4 * seq_nr;
8013   bootdev &= 0xf;
8014 
8015   /* Read user selected device */
8016   bootfirst = read_word(IPL_SEG, IPL_BOOTFIRST_OFFSET);
8017   if (bootfirst != 0xFFFF) {
8018     bootdev = bootfirst;
8019     /* User selected device not set */
8020     write_word(IPL_SEG, IPL_BOOTFIRST_OFFSET, 0xFFFF);
8021     /* Reset boot sequence */
8022     write_word(IPL_SEG, IPL_SEQUENCE_OFFSET, 0xFFFF);
8023   } else if (bootdev == 0) BX_PANIC("No bootable device.\n");
8024 
8025   /* Translate from CMOS runes to an IPL table offset by subtracting 1 */
8026   bootdev -= 1;
8027 #else
8028   if (seq_nr ==2) BX_PANIC("No more boot devices.");
8029   if (!!(inb_cmos(0x2d) & 0x20) ^ (seq_nr == 1))
8030       /* Boot from floppy if the bit is set or it's the second boot */
8031     bootdev = 0x00;
8032   else
8033     bootdev = 0x01;
8034 #endif
8035 
8036   /* Read the boot device from the IPL table */
8037   if (get_boot_vector(bootdev, &e) == 0) {
8038     BX_INFO("Invalid boot device (0x%x)\n", bootdev);
8039     return;
8040   }
8041 
8042   /* Do the loading, and set up vector as a far pointer to the boot
8043    * address, and bootdrv as the boot drive */
8044   print_boot_device(&e);
8045 
8046   switch(e.type) {
8047   case IPL_TYPE_FLOPPY: /* FDD */
8048   case IPL_TYPE_HARDDISK: /* HDD */
8049 
8050     bootdrv = (e.type == IPL_TYPE_HARDDISK) ? 0x80 : 0x00;
8051     bootseg = 0x07c0;
8052     status = 0;
8053 
8054 ASM_START
8055     push bp
8056     mov  bp, sp
8057     push ax
8058     push bx
8059     push cx
8060     push dx
8061 
8062     mov  dl, _int19_function.bootdrv + 2[bp]
8063     mov  ax, _int19_function.bootseg + 2[bp]
8064     mov  es, ax         ;; segment
8065     xor  bx, bx         ;; offset
8066     mov  ah, #0x02      ;; function 2, read diskette sector
8067     mov  al, #0x01      ;; read 1 sector
8068     mov  ch, #0x00      ;; track 0
8069     mov  cl, #0x01      ;; sector 1
8070     mov  dh, #0x00      ;; head 0
8071     int  #0x13          ;; read sector
8072     jnc  int19_load_done
8073     mov  ax, #0x0001
8074     mov  _int19_function.status + 2[bp], ax
8075 
8076 int19_load_done:
8077     pop  dx
8078     pop  cx
8079     pop  bx
8080     pop  ax
8081     pop  bp
8082 ASM_END
8083 
8084     if (status != 0) {
8085       print_boot_failure(e.type, 1);
8086       return;
8087     }
8088 
8089     /* Always check the signature on a HDD boot sector; on FDD, only do
8090      * the check if the CMOS doesn't tell us to skip it */
8091     if ((e.type != IPL_TYPE_FLOPPY) || !((inb_cmos(0x38) & 0x01))) {
8092       if (read_word(bootseg,0x1fe) != 0xaa55) {
8093         print_boot_failure(e.type, 0);
8094         return;
8095       }
8096     }
8097 
8098     /* Canonicalize bootseg:bootip */
8099     bootip = (bootseg & 0x0fff) << 4;
8100     bootseg &= 0xf000;
8101   break;
8102 
8103 #if BX_ELTORITO_BOOT
8104   case IPL_TYPE_CDROM: /* CD-ROM */
8105     status = cdrom_boot();
8106 
8107     // If failure
8108     if ( (status & 0x00ff) !=0 ) {
8109       print_cdromboot_failure(status);
8110       print_boot_failure(e.type, 1);
8111       return;
8112     }
8113 
8114     bootdrv = (Bit8u)(status>>8);
8115     bootseg = read_word(ebda_seg,&EbdaData->cdemu.load_segment);
8116     bootip = 0;
8117     break;
8118 #endif
8119 
8120   case IPL_TYPE_BEV: /* Expansion ROM with a Bootstrap Entry Vector (a far pointer) */
8121     bootseg = e.vector >> 16;
8122     bootip = e.vector & 0xffff;
8123     break;
8124 
8125   default: return;
8126   }
8127 
8128   /* Debugging info */
8129   BX_INFO("Booting from %x:%x\n", bootseg, bootip);
8130 
8131   /* Jump to the boot vector */
8132 ASM_START
8133     mov  bp, sp
8134     push cs
8135     push #int18_handler
8136     ;; Build an iret stack frame that will take us to the boot vector.
8137     ;; iret pops ip, then cs, then flags, so push them in the opposite order.
8138     pushf
8139     mov  ax, _int19_function.bootseg + 0[bp]
8140     push ax
8141     mov  ax, _int19_function.bootip + 0[bp]
8142     push ax
8143     ;; Set the magic number in ax and the boot drive in dl.
8144     mov  ax, #0xaa55
8145     mov  dl, _int19_function.bootdrv + 0[bp]
8146     ;; Zero some of the other registers.
8147     xor  bx, bx
8148     mov  ds, bx
8149     mov  es, bx
8150     mov  bp, bx
8151     ;; Go!
8152     iret
8153 ASM_END
8154 }
8155 
8156   void
8157 int1a_function(regs, ds, iret_addr)
8158   pusha_regs_t regs; // regs pushed from PUSHA instruction
8159   Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
8160   iret_addr_t  iret_addr; // CS,IP,Flags pushed from original INT call
8161 {
8162   Bit8u val8;
8163 
8164   BX_DEBUG_INT1A("int1a: AX=%04x BX=%04x CX=%04x DX=%04x DS=%04x\n", regs.u.r16.ax, regs.u.r16.bx, regs.u.r16.cx, regs.u.r16.dx, ds);
8165 
8166   ASM_START
8167   sti
8168   ASM_END
8169 
8170   switch (regs.u.r8.ah) {
8171     case 0: // get current clock count
8172       ASM_START
8173       cli
8174       ASM_END
8175       regs.u.r16.cx = BiosData->ticks_high;
8176       regs.u.r16.dx = BiosData->ticks_low;
8177       regs.u.r8.al  = BiosData->midnight_flag;
8178       BiosData->midnight_flag = 0; // reset flag
8179       ASM_START
8180       sti
8181       ASM_END
8182       // AH already 0
8183       ClearCF(iret_addr.flags); // OK
8184       break;
8185 
8186     case 1: // Set Current Clock Count
8187       ASM_START
8188       cli
8189       ASM_END
8190       BiosData->ticks_high = regs.u.r16.cx;
8191       BiosData->ticks_low  = regs.u.r16.dx;
8192       BiosData->midnight_flag = 0; // reset flag
8193       ASM_START
8194       sti
8195       ASM_END
8196       regs.u.r8.ah = 0;
8197       ClearCF(iret_addr.flags); // OK
8198       break;
8199 
8200 
8201     case 2: // Read CMOS Time
8202       if (rtc_updating()) {
8203         SetCF(iret_addr.flags);
8204         break;
8205         }
8206 
8207       regs.u.r8.dh = inb_cmos(0x00); // Seconds
8208       regs.u.r8.cl = inb_cmos(0x02); // Minutes
8209       regs.u.r8.ch = inb_cmos(0x04); // Hours
8210       regs.u.r8.dl = inb_cmos(0x0b) & 0x01; // Stat Reg B
8211       regs.u.r8.ah = 0;
8212       regs.u.r8.al = regs.u.r8.ch;
8213       ClearCF(iret_addr.flags); // OK
8214       break;
8215 
8216     case 3: // Set CMOS Time
8217       // Using a debugger, I notice the following masking/setting
8218       // of bits in Status Register B, by setting Reg B to
8219       // a few values and getting its value after INT 1A was called.
8220       //
8221       //        try#1       try#2       try#3
8222       // before 1111 1101   0111 1101   0000 0000
8223       // after  0110 0010   0110 0010   0000 0010
8224       //
8225       // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8226       // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
8227       if (rtc_updating()) {
8228         init_rtc();
8229         // fall through as if an update were not in progress
8230         }
8231       outb_cmos(0x00, regs.u.r8.dh); // Seconds
8232       outb_cmos(0x02, regs.u.r8.cl); // Minutes
8233       outb_cmos(0x04, regs.u.r8.ch); // Hours
8234       // Set Daylight Savings time enabled bit to requested value
8235       val8 = (inb_cmos(0x0b) & 0x60) | 0x02 | (regs.u.r8.dl & 0x01);
8236       // (reg B already selected)
8237       outb_cmos(0x0b, val8);
8238       regs.u.r8.ah = 0;
8239       regs.u.r8.al = val8; // val last written to Reg B
8240       ClearCF(iret_addr.flags); // OK
8241       break;
8242 
8243     case 4: // Read CMOS Date
8244       regs.u.r8.ah = 0;
8245       if (rtc_updating()) {
8246         SetCF(iret_addr.flags);
8247         break;
8248         }
8249       regs.u.r8.cl = inb_cmos(0x09); // Year
8250       regs.u.r8.dh = inb_cmos(0x08); // Month
8251       regs.u.r8.dl = inb_cmos(0x07); // Day of Month
8252       regs.u.r8.ch = inb_cmos(0x32); // Century
8253       regs.u.r8.al = regs.u.r8.ch;
8254       ClearCF(iret_addr.flags); // OK
8255       break;
8256 
8257     case 5: // Set CMOS Date
8258       // Using a debugger, I notice the following masking/setting
8259       // of bits in Status Register B, by setting Reg B to
8260       // a few values and getting its value after INT 1A was called.
8261       //
8262       //        try#1       try#2       try#3       try#4
8263       // before 1111 1101   0111 1101   0000 0010   0000 0000
8264       // after  0110 1101   0111 1101   0000 0010   0000 0000
8265       //
8266       // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8267       // My assumption: RegB = (RegB & 01111111b)
8268       if (rtc_updating()) {
8269         init_rtc();
8270         SetCF(iret_addr.flags);
8271         break;
8272         }
8273       outb_cmos(0x09, regs.u.r8.cl); // Year
8274       outb_cmos(0x08, regs.u.r8.dh); // Month
8275       outb_cmos(0x07, regs.u.r8.dl); // Day of Month
8276       outb_cmos(0x32, regs.u.r8.ch); // Century
8277       val8 = inb_cmos(0x0b) & 0x7f; // clear halt-clock bit
8278       outb_cmos(0x0b, val8);
8279       regs.u.r8.ah = 0;
8280       regs.u.r8.al = val8; // AL = val last written to Reg B
8281       ClearCF(iret_addr.flags); // OK
8282       break;
8283 
8284     case 6: // Set Alarm Time in CMOS
8285       // Using a debugger, I notice the following masking/setting
8286       // of bits in Status Register B, by setting Reg B to
8287       // a few values and getting its value after INT 1A was called.
8288       //
8289       //        try#1       try#2       try#3
8290       // before 1101 1111   0101 1111   0000 0000
8291       // after  0110 1111   0111 1111   0010 0000
8292       //
8293       // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8294       // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
8295       val8 = inb_cmos(0x0b); // Get Status Reg B
8296       regs.u.r16.ax = 0;
8297       if (val8 & 0x20) {
8298         // Alarm interrupt enabled already
8299         SetCF(iret_addr.flags); // Error: alarm in use
8300         break;
8301         }
8302       if (rtc_updating()) {
8303         init_rtc();
8304         // fall through as if an update were not in progress
8305         }
8306       outb_cmos(0x01, regs.u.r8.dh); // Seconds alarm
8307       outb_cmos(0x03, regs.u.r8.cl); // Minutes alarm
8308       outb_cmos(0x05, regs.u.r8.ch); // Hours alarm
8309       outb(0xa1, inb(0xa1) & 0xfe); // enable IRQ 8
8310       // enable Status Reg B alarm bit, clear halt clock bit
8311       outb_cmos(0x0b, (val8 & 0x7f) | 0x20);
8312       ClearCF(iret_addr.flags); // OK
8313       break;
8314 
8315     case 7: // Turn off Alarm
8316       // Using a debugger, I notice the following masking/setting
8317       // of bits in Status Register B, by setting Reg B to
8318       // a few values and getting its value after INT 1A was called.
8319       //
8320       //        try#1       try#2       try#3       try#4
8321       // before 1111 1101   0111 1101   0010 0000   0010 0010
8322       // after  0100 0101   0101 0101   0000 0000   0000 0010
8323       //
8324       // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8325       // My assumption: RegB = (RegB & 01010111b)
8326       val8 = inb_cmos(0x0b); // Get Status Reg B
8327       // clear clock-halt bit, disable alarm bit
8328       outb_cmos(0x0b, val8 & 0x57); // disable alarm bit
8329       regs.u.r8.ah = 0;
8330       regs.u.r8.al = val8; // val last written to Reg B
8331       ClearCF(iret_addr.flags); // OK
8332       break;
8333 #if BX_PCIBIOS
8334     case 0xb1:
8335       // real mode PCI BIOS functions now handled in assembler code
8336       // this C code handles the error code for information only
8337       if (regs.u.r8.bl == 0xff) {
8338         BX_INFO("PCI BIOS: PCI not present\n");
8339       } else if (regs.u.r8.bl == 0x81) {
8340         BX_INFO("unsupported PCI BIOS function 0x%02x\n", regs.u.r8.al);
8341       } else if (regs.u.r8.bl == 0x83) {
8342         BX_INFO("bad PCI vendor ID %04x\n", regs.u.r16.dx);
8343       } else if (regs.u.r8.bl == 0x86) {
8344         if (regs.u.r8.al == 0x02) {
8345           BX_INFO("PCI device %04x:%04x not found at index %d\n", regs.u.r16.dx, regs.u.r16.cx, regs.u.r16.si);
8346         } else {
8347           BX_INFO("no PCI device with class code 0x%02x%04x found at index %d\n", regs.u.r8.cl, regs.u.r16.dx, regs.u.r16.si);
8348         }
8349       }
8350       regs.u.r8.ah = regs.u.r8.bl;
8351       SetCF(iret_addr.flags);
8352       break;
8353 #endif
8354 
8355     default:
8356       SetCF(iret_addr.flags); // Unsupported
8357     }
8358 }
8359 
8360   void
8361 int70_function(regs, ds, iret_addr)
8362   pusha_regs_t regs; // regs pushed from PUSHA instruction
8363   Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
8364   iret_addr_t  iret_addr; // CS,IP,Flags pushed from original INT call
8365 {
8366   // INT 70h: IRQ 8 - CMOS RTC interrupt from periodic or alarm modes
8367   Bit8u registerB = 0, registerC = 0;
8368 
8369   // Check which modes are enabled and have occurred.
8370   registerB = inb_cmos( 0xB );
8371   registerC = inb_cmos( 0xC );
8372 
8373   if( ( registerB & 0x60 ) != 0 ) {
8374     if( ( registerC & 0x20 ) != 0 ) {
8375       // Handle Alarm Interrupt.
8376 ASM_START
8377       sti
8378       int #0x4a
8379       cli
8380 ASM_END
8381     }
8382     if( ( registerC & 0x40 ) != 0 ) {
8383       // Handle Periodic Interrupt.
8384 
8385       if( read_byte( 0x40, 0xA0 ) != 0 ) {
8386         // Wait Interval (Int 15, AH=83) active.
8387         Bit32u time, toggle;
8388 
8389         time = read_dword( 0x40, 0x9C );  // Time left in microseconds.
8390         if( time < 0x3D1 ) {
8391           // Done waiting.
8392           Bit16u segment, offset;
8393 
8394           segment = read_word( 0x40, 0x98 );
8395           offset = read_word( 0x40, 0x9A );
8396           write_byte( 0x40, 0xA0, 0 );  // Turn of status byte.
8397           outb_cmos( 0xB, registerB & 0x37 ); // Clear the Periodic Interrupt.
8398           write_byte(segment, offset, read_byte(segment, offset) | 0x80 );  // Write to specified flag byte.
8399         } else {
8400           // Continue waiting.
8401           time -= 0x3D1;
8402           write_dword( 0x40, 0x9C, time );
8403         }
8404       }
8405     }
8406   }
8407 
8408 ASM_START
8409   call eoi_both_pics
8410 ASM_END
8411 }
8412 
8413 
8414 ASM_START
8415 ;------------------------------------------
8416 ;- INT74h : PS/2 mouse hardware interrupt -
8417 ;------------------------------------------
8418 int74_handler:
8419   sti
8420   pusha
8421   push ds         ;; save DS
8422   push #0x00 ;; placeholder for status
8423   push #0x00 ;; placeholder for X
8424   push #0x00 ;; placeholder for Y
8425   push #0x00 ;; placeholder for Z
8426   push #0x00 ;; placeholder for make_far_call boolean
8427   call _int74_function
8428   pop  cx      ;; remove make_far_call from stack
8429   jcxz int74_done
8430 
8431   ;; make far call to EBDA:0022
8432   push #0x00
8433   pop ds
8434   push 0x040E     ;; push 0000:040E (opcodes 0xff, 0x36, 0x0E, 0x04)
8435   pop ds
8436   //CALL_EP(0x0022) ;; call far routine (call_Ep DS:0022 :opcodes 0xff, 0x1e, 0x22, 0x00)
8437   call far ptr[0x22]
8438 int74_done:
8439   cli
8440   call eoi_both_pics
8441   add sp, #8     ;; pop status, x, y, z
8442 
8443   pop ds          ;; restore DS
8444   popa
8445   iret
8446 
8447 
8448 ;; This will perform an IRET, but will retain value of current CF
8449 ;; by altering flags on stack.  Better than RETF #02.
8450 iret_modify_cf:
8451   jc   carry_set
8452   push bp
8453   mov  bp, sp
8454   and  BYTE [bp + 0x06], #0xfe
8455   pop  bp
8456   iret
8457 carry_set:
8458   push bp
8459   mov  bp, sp
8460   or   BYTE [bp + 0x06], #0x01
8461   pop  bp
8462   iret
8463 
8464 
8465 ;----------------------
8466 ;- INT13h (relocated) -
8467 ;----------------------
8468 ;
8469 ; int13_relocated is a little bit messed up since I played with it
8470 ; I have to rewrite it:
8471 ;   - call a function that detect which function to call
8472 ;   - make all called C function get the same parameters list
8473 ;
8474 int13_relocated:
8475 
8476 #if BX_ELTORITO_BOOT
8477   ;; check for an eltorito function
8478   cmp   ah,#0x4a
8479   jb    int13_not_eltorito
8480   cmp   ah,#0x4d
8481   ja    int13_not_eltorito
8482 
8483   pusha
8484   push  es
8485   push  ds
8486   push  ss
8487   pop   ds
8488 
8489   push  #int13_out
8490   jmp   _int13_eltorito      ;; ELDX not used
8491 
8492 int13_not_eltorito:
8493   push  ax
8494   push  bx
8495   push  cx
8496   push  dx
8497 
8498   ;; check if emulation active
8499   call  _cdemu_isactive
8500   cmp   al,#0x00
8501   je    int13_cdemu_inactive
8502 
8503   ;; check if access to the emulated drive
8504   call  _cdemu_emulated_drive
8505   pop   dx
8506   push  dx
8507   cmp   al,dl                ;; int13 on emulated drive
8508   jne   int13_nocdemu
8509 
8510   pop   dx
8511   pop   cx
8512   pop   bx
8513   pop   ax
8514 
8515   pusha
8516   push  es
8517   push  ds
8518   push  ss
8519   pop   ds
8520 
8521   push  #int13_out
8522   jmp   _int13_cdemu         ;; ELDX not used
8523 
8524 int13_nocdemu:
8525   and   dl,#0xE0             ;; mask to get device class, including cdroms
8526   cmp   al,dl                ;; al is 0x00 or 0x80
8527   jne   int13_cdemu_inactive ;; inactive for device class
8528 
8529   pop   dx
8530   pop   cx
8531   pop   bx
8532   pop   ax
8533 
8534   push  ax
8535   push  cx
8536   push  dx
8537   push  bx
8538 
8539   dec   dl                   ;; real drive is dl - 1
8540   jmp   int13_legacy
8541 
8542 int13_cdemu_inactive:
8543   pop   dx
8544   pop   cx
8545   pop   bx
8546   pop   ax
8547 
8548 #endif // BX_ELTORITO_BOOT
8549 
8550 int13_noeltorito:
8551 
8552   push  ax
8553   push  cx
8554   push  dx
8555   push  bx
8556 
8557 int13_legacy:
8558 
8559   push  dx                   ;; push eltorito value of dx instead of sp
8560 
8561   push  bp
8562   push  si
8563   push  di
8564 
8565   push  es
8566   push  ds
8567   push  ss
8568   pop   ds
8569 
8570   ;; now the 16-bit registers can be restored with:
8571   ;; pop ds; pop es; popa; iret
8572   ;; arguments passed to functions should be
8573   ;; DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS
8574 
8575   test  dl, #0x80
8576   jnz   int13_notfloppy
8577 
8578   push #int13_out
8579   jmp _int13_diskette_function
8580 
8581 int13_notfloppy:
8582 
8583 #if BX_USE_ATADRV
8584 
8585   cmp   dl, #0xE0
8586   jb    int13_notcdrom
8587 
8588   // ebx is modified: BSD 5.2.1 boot loader problem
8589   // someone should figure out which 32 bit register that actually are used
8590 
8591   shr   ebx, #16
8592   push  bx
8593 
8594   call  _int13_cdrom
8595 
8596   pop   bx
8597   shl   ebx, #16
8598 
8599   jmp int13_out
8600 
8601 int13_notcdrom:
8602 
8603 #endif
8604 
8605 int13_disk:
8606   ;; int13_harddisk modifies high word of EAX
8607   shr   eax, #16
8608   push  ax
8609   call  _int13_harddisk
8610   pop   ax
8611   shl   eax, #16
8612 
8613 int13_out:
8614   pop ds
8615   pop es
8616   popa
8617   iret
8618 
8619 ;----------
8620 ;- INT18h -
8621 ;----------
8622 int18_handler: ;; Boot Failure recovery: try the next device.
8623 
8624   ;; Reset SP and SS
8625   mov  ax, #0xfffe
8626   mov  sp, ax
8627   xor  ax, ax
8628   mov  ss, ax
8629 
8630   ;; Get the boot sequence number out of the IPL memory
8631   mov  bx, #IPL_SEG
8632   mov  ds, bx                     ;; Set segment
8633   mov  bx, IPL_SEQUENCE_OFFSET    ;; BX is now the sequence number
8634   inc  bx                         ;; ++
8635   mov  IPL_SEQUENCE_OFFSET, bx    ;; Write it back
8636   mov  ds, ax                     ;; and reset the segment to zero.
8637 
8638   ;; Carry on in the INT 19h handler, using the new sequence number
8639   push bx
8640 
8641   jmp  int19_next_boot
8642 
8643 ;----------
8644 ;- INT19h -
8645 ;----------
8646 int19_relocated: ;; Boot function, relocated
8647 
8648   ;; int19 was beginning to be really complex, so now it
8649   ;; just calls a C function that does the work
8650 
8651   push bp
8652   mov  bp, sp
8653 
8654   ;; Reset SS and SP
8655   mov  ax, #0xfffe
8656   mov  sp, ax
8657   xor  ax, ax
8658   mov  ss, ax
8659 
8660   ;; Start from the first boot device (0, in AX)
8661   mov  bx, #IPL_SEG
8662   mov  ds, bx                     ;; Set segment to write to the IPL memory
8663   mov  IPL_SEQUENCE_OFFSET, ax    ;; Save the sequence number
8664   mov  ds, ax                     ;; and reset the segment.
8665 
8666   push ax
8667 
8668 int19_next_boot:
8669 
8670   ;; Call the C code for the next boot device
8671   call _int19_function
8672 
8673   ;; Boot failed: invoke the boot recovery function
8674   int  #0x18
8675 
8676 ;----------
8677 ;- INT1Ch -
8678 ;----------
8679 int1c_handler: ;; User Timer Tick
8680   iret
8681 
8682 
8683 ;----------------------
8684 ;- POST: Floppy Drive -
8685 ;----------------------
8686 floppy_drive_post:
8687   xor  ax, ax
8688   mov  ds, ax
8689 
8690   mov  al, #0x00
8691   mov  0x043e, al ;; drive 0 & 1 uncalibrated, no interrupt has occurred
8692 
8693   mov  0x043f, al  ;; diskette motor status: read op, drive0, motors off
8694 
8695   mov  0x0440, al  ;; diskette motor timeout counter: not active
8696   mov  0x0441, al  ;; diskette controller status return code
8697 
8698   mov  0x0442, al  ;; disk & diskette controller status register 0
8699   mov  0x0443, al  ;; diskette controller status register 1
8700   mov  0x0444, al  ;; diskette controller status register 2
8701   mov  0x0445, al  ;; diskette controller cylinder number
8702   mov  0x0446, al  ;; diskette controller head number
8703   mov  0x0447, al  ;; diskette controller sector number
8704   mov  0x0448, al  ;; diskette controller bytes written
8705 
8706   mov  0x048b, al  ;; diskette configuration data
8707 
8708   ;; -----------------------------------------------------------------
8709   ;; (048F) diskette controller information
8710   ;;
8711   mov  al, #0x10   ;; get CMOS diskette drive type
8712   out  0x70, AL
8713   in   AL, 0x71
8714   mov  ah, al      ;; save byte to AH
8715 
8716 look_drive0:
8717   shr  al, #4      ;; look at top 4 bits for drive 0
8718   jz   f0_missing  ;; jump if no drive0
8719   mov  bl, #0x07   ;; drive0 determined, multi-rate, has changed line
8720   jmp  look_drive1
8721 f0_missing:
8722   mov  bl, #0x00   ;; no drive0
8723 
8724 look_drive1:
8725   mov  al, ah      ;; restore from AH
8726   and  al, #0x0f   ;; look at bottom 4 bits for drive 1
8727   jz   f1_missing  ;; jump if no drive1
8728   or   bl, #0x70   ;; drive1 determined, multi-rate, has changed line
8729 f1_missing:
8730                    ;; leave high bits in BL zerod
8731   mov  0x048f, bl  ;; put new val in BDA (diskette controller information)
8732   ;; -----------------------------------------------------------------
8733 
8734   mov  al, #0x00
8735   mov  0x0490, al  ;; diskette 0 media state
8736   mov  0x0491, al  ;; diskette 1 media state
8737 
8738                    ;; diskette 0,1 operational starting state
8739                    ;; drive type has not been determined,
8740                    ;; has no changed detection line
8741   mov  0x0492, al
8742   mov  0x0493, al
8743 
8744   mov  0x0494, al  ;; diskette 0 current cylinder
8745   mov  0x0495, al  ;; diskette 1 current cylinder
8746 
8747   mov  al, #0x02
8748   out  #0x0a, al   ;; clear DMA-1 channel 2 mask bit
8749 
8750   SET_INT_VECTOR(0x1E, #0xF000, #diskette_param_table2)
8751   SET_INT_VECTOR(0x40, #0xF000, #int13_diskette)
8752   SET_INT_VECTOR(0x0E, #0xF000, #int0e_handler) ;; IRQ 6
8753 
8754   ret
8755 
8756 
8757 ;--------------------
8758 ;- POST: HARD DRIVE -
8759 ;--------------------
8760 ; relocated here because the primary POST area isnt big enough.
8761 hard_drive_post:
8762   // IRQ 14 = INT 76h
8763   // INT 76h calls INT 15h function ax=9100
8764 
8765   mov  al, #0x0a   ; 0000 1010 = reserved, disable IRQ 14
8766   mov  dx, #0x03f6
8767   out  dx, al
8768 
8769   xor  ax, ax
8770   mov  ds, ax
8771   mov  0x0474, al /* hard disk status of last operation */
8772   mov  0x0477, al /* hard disk port offset (XT only ???) */
8773   mov  0x048c, al /* hard disk status register */
8774   mov  0x048d, al /* hard disk error register */
8775   mov  0x048e, al /* hard disk task complete flag */
8776   mov  al, #0x01
8777   mov  0x0475, al /* hard disk number attached */
8778   mov  al, #0xc0
8779   mov  0x0476, al /* hard disk control byte */
8780   SET_INT_VECTOR(0x13, #0xF000, #int13_handler)
8781   SET_INT_VECTOR(0x76, #0xF000, #int76_handler)
8782   ;; INT 41h: hard disk 0 configuration pointer
8783   ;; INT 46h: hard disk 1 configuration pointer
8784   SET_INT_VECTOR(0x41, #EBDA_SEG, #0x003D)
8785   SET_INT_VECTOR(0x46, #EBDA_SEG, #0x004D)
8786 
8787   ;; move disk geometry data from CMOS to EBDA disk parameter table(s)
8788   mov  al, #0x12
8789   out  #0x70, al
8790   in   al, #0x71
8791   and  al, #0xf0
8792   cmp  al, #0xf0
8793   je   post_d0_extended
8794   jmp check_for_hd1
8795 post_d0_extended:
8796   mov  al, #0x19
8797   out  #0x70, al
8798   in   al, #0x71
8799   cmp  al, #47  ;; decimal 47 - user definable
8800   je   post_d0_type47
8801   HALT(__LINE__)
8802 post_d0_type47:
8803   ;; CMOS  purpose                  param table offset
8804   ;; 1b    cylinders low            0
8805   ;; 1c    cylinders high           1
8806   ;; 1d    heads                    2
8807   ;; 1e    write pre-comp low       5
8808   ;; 1f    write pre-comp high      6
8809   ;; 20    retries/bad map/heads>8  8
8810   ;; 21    landing zone low         C
8811   ;; 22    landing zone high        D
8812   ;; 23    sectors/track            E
8813 
8814   mov  ax, #EBDA_SEG
8815   mov  ds, ax
8816 
8817   ;;; Filling EBDA table for hard disk 0.
8818   mov  al, #0x1f
8819   out  #0x70, al
8820   in   al, #0x71
8821   mov  ah, al
8822   mov  al, #0x1e
8823   out  #0x70, al
8824   in   al, #0x71
8825   mov   (0x003d + 0x05), ax ;; write precomp word
8826 
8827   mov  al, #0x20
8828   out  #0x70, al
8829   in   al, #0x71
8830   mov   (0x003d + 0x08), al ;; drive control byte
8831 
8832   mov  al, #0x22
8833   out  #0x70, al
8834   in   al, #0x71
8835   mov  ah, al
8836   mov  al, #0x21
8837   out  #0x70, al
8838   in   al, #0x71
8839   mov   (0x003d + 0x0C), ax ;; landing zone word
8840 
8841   mov  al, #0x1c   ;; get cylinders word in AX
8842   out  #0x70, al
8843   in   al, #0x71   ;; high byte
8844   mov  ah, al
8845   mov  al, #0x1b
8846   out  #0x70, al
8847   in   al, #0x71   ;; low byte
8848   mov  bx, ax      ;; BX = cylinders
8849 
8850   mov  al, #0x1d
8851   out  #0x70, al
8852   in   al, #0x71
8853   mov  cl, al      ;; CL = heads
8854 
8855   mov  al, #0x23
8856   out  #0x70, al
8857   in   al, #0x71
8858   mov  dl, al      ;; DL = sectors
8859 
8860   cmp  bx, #1024
8861   jnbe hd0_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8862 
8863 hd0_post_physical_chs:
8864   ;; no logical CHS mapping used, just physical CHS
8865   ;; use Standard Fixed Disk Parameter Table (FDPT)
8866   mov   (0x003d + 0x00), bx ;; number of physical cylinders
8867   mov   (0x003d + 0x02), cl ;; number of physical heads
8868   mov   (0x003d + 0x0E), dl ;; number of physical sectors
8869   jmp check_for_hd1
8870 
8871 hd0_post_logical_chs:
8872   ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8873   mov   (0x003d + 0x09), bx ;; number of physical cylinders
8874   mov   (0x003d + 0x0b), cl ;; number of physical heads
8875   mov   (0x003d + 0x04), dl ;; number of physical sectors
8876   mov   (0x003d + 0x0e), dl ;; number of logical sectors (same)
8877   mov al, #0xa0
8878   mov   (0x003d + 0x03), al ;; A0h signature, indicates translated table
8879 
8880   cmp bx, #2048
8881   jnbe hd0_post_above_2048
8882   ;; 1024 < c <= 2048 cylinders
8883   shr bx, #0x01
8884   shl cl, #0x01
8885   jmp hd0_post_store_logical
8886 
8887 hd0_post_above_2048:
8888   cmp bx, #4096
8889   jnbe hd0_post_above_4096
8890   ;; 2048 < c <= 4096 cylinders
8891   shr bx, #0x02
8892   shl cl, #0x02
8893   jmp hd0_post_store_logical
8894 
8895 hd0_post_above_4096:
8896   cmp bx, #8192
8897   jnbe hd0_post_above_8192
8898   ;; 4096 < c <= 8192 cylinders
8899   shr bx, #0x03
8900   shl cl, #0x03
8901   jmp hd0_post_store_logical
8902 
8903 hd0_post_above_8192:
8904   ;; 8192 < c <= 16384 cylinders
8905   shr bx, #0x04
8906   shl cl, #0x04
8907 
8908 hd0_post_store_logical:
8909   mov   (0x003d + 0x00), bx ;; number of physical cylinders
8910   mov   (0x003d + 0x02), cl ;; number of physical heads
8911   ;; checksum
8912   mov   cl, #0x0f     ;; repeat count
8913   mov   si, #0x003d   ;; offset to disk0 FDPT
8914   mov   al, #0x00     ;; sum
8915 hd0_post_checksum_loop:
8916   add   al, [si]
8917   inc   si
8918   dec   cl
8919   jnz hd0_post_checksum_loop
8920   not   al  ;; now take 2s complement
8921   inc   al
8922   mov   [si], al
8923 ;;; Done filling EBDA table for hard disk 0.
8924 
8925 
8926 check_for_hd1:
8927   ;; is there really a second hard disk?  if not, return now
8928   mov  al, #0x12
8929   out  #0x70, al
8930   in   al, #0x71
8931   and  al, #0x0f
8932   jnz   post_d1_exists
8933   ret
8934 post_d1_exists:
8935   ;; check that the hd type is really 0x0f.
8936   cmp al, #0x0f
8937   jz post_d1_extended
8938   HALT(__LINE__)
8939 post_d1_extended:
8940   ;; check that the extended type is 47 - user definable
8941   mov  al, #0x1a
8942   out  #0x70, al
8943   in   al, #0x71
8944   cmp  al, #47  ;; decimal 47 - user definable
8945   je   post_d1_type47
8946   HALT(__LINE__)
8947 post_d1_type47:
8948   ;; Table for disk1.
8949   ;; CMOS  purpose                  param table offset
8950   ;; 0x24    cylinders low            0
8951   ;; 0x25    cylinders high           1
8952   ;; 0x26    heads                    2
8953   ;; 0x27    write pre-comp low       5
8954   ;; 0x28    write pre-comp high      6
8955   ;; 0x29    heads>8                  8
8956   ;; 0x2a    landing zone low         C
8957   ;; 0x2b    landing zone high        D
8958   ;; 0x2c    sectors/track            E
8959 ;;; Fill EBDA table for hard disk 1.
8960   mov  ax, #EBDA_SEG
8961   mov  ds, ax
8962   mov  al, #0x28
8963   out  #0x70, al
8964   in   al, #0x71
8965   mov  ah, al
8966   mov  al, #0x27
8967   out  #0x70, al
8968   in   al, #0x71
8969   mov   (0x004d + 0x05), ax ;; write precomp word
8970 
8971   mov  al, #0x29
8972   out  #0x70, al
8973   in   al, #0x71
8974   mov   (0x004d + 0x08), al ;; drive control byte
8975 
8976   mov  al, #0x2b
8977   out  #0x70, al
8978   in   al, #0x71
8979   mov  ah, al
8980   mov  al, #0x2a
8981   out  #0x70, al
8982   in   al, #0x71
8983   mov   (0x004d + 0x0C), ax ;; landing zone word
8984 
8985   mov  al, #0x25   ;; get cylinders word in AX
8986   out  #0x70, al
8987   in   al, #0x71   ;; high byte
8988   mov  ah, al
8989   mov  al, #0x24
8990   out  #0x70, al
8991   in   al, #0x71   ;; low byte
8992   mov  bx, ax      ;; BX = cylinders
8993 
8994   mov  al, #0x26
8995   out  #0x70, al
8996   in   al, #0x71
8997   mov  cl, al      ;; CL = heads
8998 
8999   mov  al, #0x2c
9000   out  #0x70, al
9001   in   al, #0x71
9002   mov  dl, al      ;; DL = sectors
9003 
9004   cmp  bx, #1024
9005   jnbe hd1_post_logical_chs ;; if cylinders > 1024, use translated style CHS
9006 
9007 hd1_post_physical_chs:
9008   ;; no logical CHS mapping used, just physical CHS
9009   ;; use Standard Fixed Disk Parameter Table (FDPT)
9010   mov   (0x004d + 0x00), bx ;; number of physical cylinders
9011   mov   (0x004d + 0x02), cl ;; number of physical heads
9012   mov   (0x004d + 0x0E), dl ;; number of physical sectors
9013   ret
9014 
9015 hd1_post_logical_chs:
9016   ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
9017   mov   (0x004d + 0x09), bx ;; number of physical cylinders
9018   mov   (0x004d + 0x0b), cl ;; number of physical heads
9019   mov   (0x004d + 0x04), dl ;; number of physical sectors
9020   mov   (0x004d + 0x0e), dl ;; number of logical sectors (same)
9021   mov al, #0xa0
9022   mov   (0x004d + 0x03), al ;; A0h signature, indicates translated table
9023 
9024   cmp bx, #2048
9025   jnbe hd1_post_above_2048
9026   ;; 1024 < c <= 2048 cylinders
9027   shr bx, #0x01
9028   shl cl, #0x01
9029   jmp hd1_post_store_logical
9030 
9031 hd1_post_above_2048:
9032   cmp bx, #4096
9033   jnbe hd1_post_above_4096
9034   ;; 2048 < c <= 4096 cylinders
9035   shr bx, #0x02
9036   shl cl, #0x02
9037   jmp hd1_post_store_logical
9038 
9039 hd1_post_above_4096:
9040   cmp bx, #8192
9041   jnbe hd1_post_above_8192
9042   ;; 4096 < c <= 8192 cylinders
9043   shr bx, #0x03
9044   shl cl, #0x03
9045   jmp hd1_post_store_logical
9046 
9047 hd1_post_above_8192:
9048   ;; 8192 < c <= 16384 cylinders
9049   shr bx, #0x04
9050   shl cl, #0x04
9051 
9052 hd1_post_store_logical:
9053   mov   (0x004d + 0x00), bx ;; number of physical cylinders
9054   mov   (0x004d + 0x02), cl ;; number of physical heads
9055   ;; checksum
9056   mov   cl, #0x0f     ;; repeat count
9057   mov   si, #0x004d   ;; offset to disk0 FDPT
9058   mov   al, #0x00     ;; sum
9059 hd1_post_checksum_loop:
9060   add   al, [si]
9061   inc   si
9062   dec   cl
9063   jnz hd1_post_checksum_loop
9064   not   al  ;; now take 2s complement
9065   inc   al
9066   mov   [si], al
9067 ;;; Done filling EBDA table for hard disk 1.
9068 
9069   ret
9070 
9071 ;--------------------
9072 ;- POST: EBDA segment
9073 ;--------------------
9074 ; relocated here because the primary POST area isnt big enough.
9075 ebda_post:
9076 #if BX_USE_EBDA
9077   mov ax, #EBDA_SEG
9078   mov ds, ax
9079   mov byte ptr [0x0], #EBDA_SIZE
9080 #endif
9081   xor ax, ax            ; mov EBDA seg into 40E
9082   mov ds, ax
9083   mov word ptr [0x40E], #EBDA_SEG
9084   ret;;
9085 
9086 ;--------------------
9087 ;- POST: EOI + jmp via [0x40:67)
9088 ;--------------------
9089 ; relocated here because the primary POST area isnt big enough.
9090 eoi_jmp_post:
9091   mov   al, #0x20
9092   out   #0xA0, al ;; slave  PIC EOI
9093   mov   al, #0x20
9094   out   #0x20, al ;; master PIC EOI
9095 
9096 jmp_post_0x467:
9097   xor ax, ax
9098   mov ds, ax
9099 
9100   jmp far ptr [0x467]
9101 
9102 iret_post_0x467:
9103   xor ax, ax
9104   mov ds, ax
9105 
9106   mov sp, [0x467]
9107   mov ss, [0x469]
9108   iret
9109 
9110 retf_post_0x467:
9111   xor ax, ax
9112   mov ds, ax
9113 
9114   mov sp, [0x467]
9115   mov ss, [0x469]
9116   retf
9117 
9118 s3_post:
9119   mov sp, #0xffe
9120 #if BX_ROMBIOS32
9121   call rombios32_init
9122 #endif
9123   call _s3_resume
9124   mov bl, #0x00
9125   and ax, ax
9126   jz normal_post
9127   call _s3_resume_panic
9128 
9129 ;--------------------
9130 eoi_both_pics:
9131   mov   al, #0x20
9132   out   #0xA0, al ;; slave  PIC EOI
9133 eoi_master_pic:
9134   mov   al, #0x20
9135   out   #0x20, al ;; master PIC EOI
9136   ret
9137 
9138 ;--------------------
9139 BcdToBin:
9140   ;; in:  AL in BCD format
9141   ;; out: AL in binary format, AH will always be 0
9142   ;; trashes BX
9143   mov  bl, al
9144   and  bl, #0x0f ;; bl has low digit
9145   shr  al, #4    ;; al has high digit
9146   mov  bh, #10
9147   mul  al, bh    ;; multiply high digit by 10 (result in AX)
9148   add  al, bl    ;;   then add low digit
9149   ret
9150 
9151 ;--------------------
9152 timer_tick_post:
9153   ;; Setup the Timer Ticks Count (0x46C:dword) and
9154   ;;   Timer Ticks Roller Flag (0x470:byte)
9155   ;; The Timer Ticks Count needs to be set according to
9156   ;; the current CMOS time, as if ticks have been occurring
9157   ;; at 18.2hz since midnight up to this point.  Calculating
9158   ;; this is a little complicated.  Here are the factors I gather
9159   ;; regarding this.  14,318,180 hz was the original clock speed,
9160   ;; chosen so it could be divided by either 3 to drive the 5Mhz CPU
9161   ;; at the time, or 4 to drive the CGA video adapter.  The div3
9162   ;; source was divided again by 4 to feed a 1.193Mhz signal to
9163   ;; the timer.  With a maximum 16bit timer count, this is again
9164   ;; divided down by 65536 to 18.2hz.
9165   ;;
9166   ;; 14,318,180 Hz clock
9167   ;;   /3 = 4,772,726 Hz fed to orginal 5Mhz CPU
9168   ;;   /4 = 1,193,181 Hz fed to timer
9169   ;;   /65536 (maximum timer count) = 18.20650736 ticks/second
9170   ;; 1 second = 18.20650736 ticks
9171   ;; 1 minute = 1092.390442 ticks
9172   ;; 1 hour   = 65543.42651 ticks
9173   ;;
9174   ;; Given the values in the CMOS clock, one could calculate
9175   ;; the number of ticks by the following:
9176   ;;   ticks = (BcdToBin(seconds) * 18.206507) +
9177   ;;           (BcdToBin(minutes) * 1092.3904)
9178   ;;           (BcdToBin(hours)   * 65543.427)
9179   ;; To get a little more accuracy, since Im using integer
9180   ;; arithmatic, I use:
9181   ;;   ticks = (BcdToBin(seconds) * 18206507) / 1000000 +
9182   ;;           (BcdToBin(minutes) * 10923904) / 10000 +
9183   ;;           (BcdToBin(hours)   * 65543427) / 1000
9184 
9185   ;; assuming DS=0000
9186 
9187   ;; get CMOS seconds
9188   xor  eax, eax ;; clear EAX
9189   mov  al, #0x00
9190   out  #0x70, al
9191   in   al, #0x71 ;; AL has CMOS seconds in BCD
9192   call BcdToBin  ;; EAX now has seconds in binary
9193   mov  edx, #18206507
9194   mul  eax, edx
9195   mov  ebx, #1000000
9196   xor  edx, edx
9197   div  eax, ebx
9198   mov  ecx, eax  ;; ECX will accumulate total ticks
9199 
9200   ;; get CMOS minutes
9201   xor  eax, eax ;; clear EAX
9202   mov  al, #0x02
9203   out  #0x70, al
9204   in   al, #0x71 ;; AL has CMOS minutes in BCD
9205   call BcdToBin  ;; EAX now has minutes in binary
9206   mov  edx, #10923904
9207   mul  eax, edx
9208   mov  ebx, #10000
9209   xor  edx, edx
9210   div  eax, ebx
9211   add  ecx, eax  ;; add to total ticks
9212 
9213   ;; get CMOS hours
9214   xor  eax, eax ;; clear EAX
9215   mov  al, #0x04
9216   out  #0x70, al
9217   in   al, #0x71 ;; AL has CMOS hours in BCD
9218   call BcdToBin  ;; EAX now has hours in binary
9219   mov  edx, #65543427
9220   mul  eax, edx
9221   mov  ebx, #1000
9222   xor  edx, edx
9223   div  eax, ebx
9224   add  ecx, eax  ;; add to total ticks
9225 
9226   mov  0x46C, ecx ;; Timer Ticks Count
9227   xor  al, al
9228   mov  0x470, al  ;; Timer Ticks Rollover Flag
9229   ret
9230 
9231 ;--------------------
9232 int76_handler:
9233   ;; record completion in BIOS task complete flag
9234   push  ax
9235   push  ds
9236   mov   ax, #0x0040
9237   mov   ds, ax
9238   mov   0x008E, #0xff
9239   call  eoi_both_pics
9240   pop   ds
9241   pop   ax
9242   iret
9243 
9244 
9245 ;--------------------
9246 #if BX_APM
9247 
9248 use32 386
9249 #define APM_PROT32
9250 #include "apmbios.S"
9251 
9252 use16 386
9253 #define APM_PROT16
9254 #include "apmbios.S"
9255 
9256 #define APM_REAL
9257 #include "apmbios.S"
9258 
9259 #endif
9260 
9261 ;--------------------
9262 #if BX_PCIBIOS
9263 use32 386
9264 .align 16
9265 bios32_structure:
9266   db 0x5f, 0x33, 0x32, 0x5f  ;; "_32_" signature
9267   dw bios32_entry_point, 0xf ;; 32 bit physical address
9268   db 0             ;; revision level
9269   ;; length in paragraphs and checksum stored in a word to prevent errors
9270   dw (~(((bios32_entry_point >> 8) + (bios32_entry_point & 0xff) + 0x32) \
9271         & 0xff) << 8) + 0x01
9272   db 0,0,0,0,0     ;; reserved
9273 
9274 .align 16
9275 bios32_entry_point:
9276   pushfd
9277   cmp eax, #0x49435024 ;; "$PCI"
9278   jne unknown_service
9279   mov eax, #0x80000000
9280   mov dx, #0x0cf8
9281   out dx, eax
9282   mov dx, #0x0cfc
9283   in  eax, dx
9284 #ifdef PCI_FIXED_HOST_BRIDGE
9285   cmp eax, #PCI_FIXED_HOST_BRIDGE
9286   jne unknown_service
9287 #else
9288   ;; say ok if a device is present
9289   cmp eax, #0xffffffff
9290   je unknown_service
9291 #endif
9292   mov ebx, #0x000f0000
9293   mov ecx, #0
9294   mov edx, #pcibios_protected
9295   xor al, al
9296   jmp bios32_end
9297 unknown_service:
9298   mov al, #0x80
9299 bios32_end:
9300 #ifdef BX_QEMU
9301   and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
9302 #endif
9303   popfd
9304   retf
9305 
9306 .align 16
9307 pcibios_protected:
9308   pushfd
9309   cli
9310   push esi
9311   push edi
9312   cmp al, #0x01 ;; installation check
9313   jne pci_pro_f02
9314   mov bx, #0x0210
9315   mov cx, #0
9316   mov edx, #0x20494350 ;; "PCI "
9317   mov al, #0x01
9318   jmp pci_pro_ok
9319 pci_pro_f02: ;; find pci device
9320   cmp al, #0x02
9321   jne pci_pro_f03
9322   shl ecx, #16
9323   mov cx, dx
9324   xor bx, bx
9325   mov di, #0x00
9326 pci_pro_devloop:
9327   call pci_pro_select_reg
9328   mov dx, #0x0cfc
9329   in  eax, dx
9330   cmp eax, ecx
9331   jne pci_pro_nextdev
9332   cmp si, #0
9333   je  pci_pro_ok
9334   dec si
9335 pci_pro_nextdev:
9336   inc bx
9337   cmp bx, #0x0100
9338   jne pci_pro_devloop
9339   mov ah, #0x86
9340   jmp pci_pro_fail
9341 pci_pro_f03: ;; find class code
9342   cmp al, #0x03
9343   jne pci_pro_f08
9344   xor bx, bx
9345   mov di, #0x08
9346 pci_pro_devloop2:
9347   call pci_pro_select_reg
9348   mov dx, #0x0cfc
9349   in  eax, dx
9350   shr eax, #8
9351   cmp eax, ecx
9352   jne pci_pro_nextdev2
9353   cmp si, #0
9354   je  pci_pro_ok
9355   dec si
9356 pci_pro_nextdev2:
9357   inc bx
9358   cmp bx, #0x0100
9359   jne pci_pro_devloop2
9360   mov ah, #0x86
9361   jmp pci_pro_fail
9362 pci_pro_f08: ;; read configuration byte
9363   cmp al, #0x08
9364   jne pci_pro_f09
9365   call pci_pro_select_reg
9366   push edx
9367   mov dx, di
9368   and dx, #0x03
9369   add dx, #0x0cfc
9370   in  al, dx
9371   pop edx
9372   mov cl, al
9373   jmp pci_pro_ok
9374 pci_pro_f09: ;; read configuration word
9375   cmp al, #0x09
9376   jne pci_pro_f0a
9377   call pci_pro_select_reg
9378   push edx
9379   mov dx, di
9380   and dx, #0x02
9381   add dx, #0x0cfc
9382   in  ax, dx
9383   pop edx
9384   mov cx, ax
9385   jmp pci_pro_ok
9386 pci_pro_f0a: ;; read configuration dword
9387   cmp al, #0x0a
9388   jne pci_pro_f0b
9389   call pci_pro_select_reg
9390   push edx
9391   mov dx, #0x0cfc
9392   in  eax, dx
9393   pop edx
9394   mov ecx, eax
9395   jmp pci_pro_ok
9396 pci_pro_f0b: ;; write configuration byte
9397   cmp al, #0x0b
9398   jne pci_pro_f0c
9399   call pci_pro_select_reg
9400   push edx
9401   mov dx, di
9402   and dx, #0x03
9403   add dx, #0x0cfc
9404   mov al, cl
9405   out dx, al
9406   pop edx
9407   jmp pci_pro_ok
9408 pci_pro_f0c: ;; write configuration word
9409   cmp al, #0x0c
9410   jne pci_pro_f0d
9411   call pci_pro_select_reg
9412   push edx
9413   mov dx, di
9414   and dx, #0x02
9415   add dx, #0x0cfc
9416   mov ax, cx
9417   out dx, ax
9418   pop edx
9419   jmp pci_pro_ok
9420 pci_pro_f0d: ;; write configuration dword
9421   cmp al, #0x0d
9422   jne pci_pro_unknown
9423   call pci_pro_select_reg
9424   push edx
9425   mov dx, #0x0cfc
9426   mov eax, ecx
9427   out dx, eax
9428   pop edx
9429   jmp pci_pro_ok
9430 pci_pro_unknown:
9431   mov ah, #0x81
9432 pci_pro_fail:
9433   pop edi
9434   pop esi
9435 #ifdef BX_QEMU
9436   and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
9437 #endif
9438   popfd
9439   stc
9440   retf
9441 pci_pro_ok:
9442   xor ah, ah
9443   pop edi
9444   pop esi
9445 #ifdef BX_QEMU
9446   and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
9447 #endif
9448   popfd
9449   clc
9450   retf
9451 
9452 pci_pro_select_reg:
9453   push edx
9454   mov eax, #0x800000
9455   mov ax,  bx
9456   shl eax, #8
9457   and di,  #0xff
9458   or  ax,  di
9459   and al,  #0xfc
9460   mov dx, #0x0cf8
9461   out dx,  eax
9462   pop edx
9463   ret
9464 
9465 use16 386
9466 
9467 pcibios_real:
9468   push eax
9469   push dx
9470   mov eax, #0x80000000
9471   mov dx, #0x0cf8
9472   out dx, eax
9473   mov dx, #0x0cfc
9474   in  eax, dx
9475 #ifdef PCI_FIXED_HOST_BRIDGE
9476   cmp eax, #PCI_FIXED_HOST_BRIDGE
9477   je  pci_present
9478 #else
9479   ;; say ok if a device is present
9480   cmp eax, #0xffffffff
9481   jne  pci_present
9482 #endif
9483   pop dx
9484   pop eax
9485   mov ah, #0xff
9486   stc
9487   ret
9488 pci_present:
9489   pop dx
9490   pop eax
9491   cmp al, #0x01 ;; installation check
9492   jne pci_real_f02
9493   mov ax, #0x0001
9494   mov bx, #0x0210
9495   mov cx, #0
9496   mov edx, #0x20494350 ;; "PCI "
9497   mov edi, #0xf0000
9498   mov di, #pcibios_protected
9499   clc
9500   ret
9501 pci_real_f02: ;; find pci device
9502   push esi
9503   push edi
9504   cmp al, #0x02
9505   jne pci_real_f03
9506   shl ecx, #16
9507   mov cx, dx
9508   xor bx, bx
9509   mov di, #0x00
9510 pci_real_devloop:
9511   call pci_real_select_reg
9512   mov dx, #0x0cfc
9513   in  eax, dx
9514   cmp eax, ecx
9515   jne pci_real_nextdev
9516   cmp si, #0
9517   je  pci_real_ok
9518   dec si
9519 pci_real_nextdev:
9520   inc bx
9521   cmp bx, #0x0100
9522   jne pci_real_devloop
9523   mov dx, cx
9524   shr ecx, #16
9525   mov ax, #0x8602
9526   jmp pci_real_fail
9527 pci_real_f03: ;; find class code
9528   cmp al, #0x03
9529   jne pci_real_f08
9530   xor bx, bx
9531   mov di, #0x08
9532 pci_real_devloop2:
9533   call pci_real_select_reg
9534   mov dx, #0x0cfc
9535   in  eax, dx
9536   shr eax, #8
9537   cmp eax, ecx
9538   jne pci_real_nextdev2
9539   cmp si, #0
9540   je  pci_real_ok
9541   dec si
9542 pci_real_nextdev2:
9543   inc bx
9544   cmp bx, #0x0100
9545   jne pci_real_devloop2
9546   mov dx, cx
9547   shr ecx, #16
9548   mov ax, #0x8603
9549   jmp pci_real_fail
9550 pci_real_f08: ;; read configuration byte
9551   cmp al, #0x08
9552   jne pci_real_f09
9553   call pci_real_select_reg
9554   push dx
9555   mov dx, di
9556   and dx, #0x03
9557   add dx, #0x0cfc
9558   in  al, dx
9559   pop dx
9560   mov cl, al
9561   jmp pci_real_ok
9562 pci_real_f09: ;; read configuration word
9563   cmp al, #0x09
9564   jne pci_real_f0a
9565   call pci_real_select_reg
9566   push dx
9567   mov dx, di
9568   and dx, #0x02
9569   add dx, #0x0cfc
9570   in  ax, dx
9571   pop dx
9572   mov cx, ax
9573   jmp pci_real_ok
9574 pci_real_f0a: ;; read configuration dword
9575   cmp al, #0x0a
9576   jne pci_real_f0b
9577   call pci_real_select_reg
9578   push dx
9579   mov dx, #0x0cfc
9580   in  eax, dx
9581   pop dx
9582   mov ecx, eax
9583   jmp pci_real_ok
9584 pci_real_f0b: ;; write configuration byte
9585   cmp al, #0x0b
9586   jne pci_real_f0c
9587   call pci_real_select_reg
9588   push dx
9589   mov dx, di
9590   and dx, #0x03
9591   add dx, #0x0cfc
9592   mov al, cl
9593   out dx, al
9594   pop dx
9595   jmp pci_real_ok
9596 pci_real_f0c: ;; write configuration word
9597   cmp al, #0x0c
9598   jne pci_real_f0d
9599   call pci_real_select_reg
9600   push dx
9601   mov dx, di
9602   and dx, #0x02
9603   add dx, #0x0cfc
9604   mov ax, cx
9605   out dx, ax
9606   pop dx
9607   jmp pci_real_ok
9608 pci_real_f0d: ;; write configuration dword
9609   cmp al, #0x0d
9610   jne pci_real_f0e
9611   call pci_real_select_reg
9612   push dx
9613   mov dx, #0x0cfc
9614   mov eax, ecx
9615   out dx, eax
9616   pop dx
9617   jmp pci_real_ok
9618 pci_real_f0e: ;; get irq routing options
9619   cmp al, #0x0e
9620   jne pci_real_unknown
9621   SEG ES
9622   cmp word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9623   jb pci_real_too_small
9624   SEG ES
9625   mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9626   pushf
9627   push ds
9628   push es
9629   push cx
9630   push si
9631   push di
9632   cld
9633   mov si, #pci_routing_table_structure_start
9634   push cs
9635   pop ds
9636   SEG ES
9637   mov cx, [di+2]
9638   SEG ES
9639   mov es, [di+4]
9640   mov di, cx
9641   mov cx, #pci_routing_table_structure_end - pci_routing_table_structure_start
9642   rep
9643       movsb
9644   pop di
9645   pop si
9646   pop cx
9647   pop es
9648   pop ds
9649   popf
9650   mov bx, #(1 << 9) | (1 << 11)   ;; irq 9 and 11 are used
9651   jmp pci_real_ok
9652 pci_real_too_small:
9653   SEG ES
9654   mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9655   mov ah, #0x89
9656   jmp pci_real_fail
9657 
9658 pci_real_unknown:
9659   mov ah, #0x81
9660 pci_real_fail:
9661   pop edi
9662   pop esi
9663   stc
9664   ret
9665 pci_real_ok:
9666   xor ah, ah
9667   pop edi
9668   pop esi
9669   clc
9670   ret
9671 
9672 pci_real_select_reg:
9673   push dx
9674   mov eax, #0x800000
9675   mov ax,  bx
9676   shl eax, #8
9677   and di,  #0xff
9678   or  ax,  di
9679   and al,  #0xfc
9680   mov dx,  #0x0cf8
9681   out dx,  eax
9682   pop dx
9683   ret
9684 
9685 .align 16
9686 pci_routing_table_structure:
9687   db 0x24, 0x50, 0x49, 0x52  ;; "$PIR" signature
9688   db 0, 1 ;; version
9689   dw 32 + (6 * 16) ;; table size
9690   db 0 ;; PCI interrupt router bus
9691   db 0x08 ;; PCI interrupt router DevFunc
9692   dw 0x0000 ;; PCI exclusive IRQs
9693   dw 0x8086 ;; compatible PCI interrupt router vendor ID
9694   dw 0x122e ;; compatible PCI interrupt router device ID
9695   dw 0,0 ;; Miniport data
9696   db 0,0,0,0,0,0,0,0,0,0,0 ;; reserved
9697   db 0x37 ;; checksum
9698 pci_routing_table_structure_start:
9699   ;; first slot entry PCI-to-ISA (embedded)
9700   db 0 ;; pci bus number
9701   db 0x08 ;; pci device number (bit 7-3)
9702   db 0x60 ;; link value INTA#: pointer into PCI2ISA config space
9703   dw 0xdef8 ;; IRQ bitmap INTA#
9704   db 0x61 ;; link value INTB#
9705   dw 0xdef8 ;; IRQ bitmap INTB#
9706   db 0x62 ;; link value INTC#
9707   dw 0xdef8 ;; IRQ bitmap INTC#
9708   db 0x63 ;; link value INTD#
9709   dw 0xdef8 ;; IRQ bitmap INTD#
9710   db 0 ;; physical slot (0 = embedded)
9711   db 0 ;; reserved
9712   ;; second slot entry: 1st PCI slot
9713   db 0 ;; pci bus number
9714   db 0x10 ;; pci device number (bit 7-3)
9715   db 0x61 ;; link value INTA#
9716   dw 0xdef8 ;; IRQ bitmap INTA#
9717   db 0x62 ;; link value INTB#
9718   dw 0xdef8 ;; IRQ bitmap INTB#
9719   db 0x63 ;; link value INTC#
9720   dw 0xdef8 ;; IRQ bitmap INTC#
9721   db 0x60 ;; link value INTD#
9722   dw 0xdef8 ;; IRQ bitmap INTD#
9723   db 1 ;; physical slot (0 = embedded)
9724   db 0 ;; reserved
9725   ;; third slot entry: 2nd PCI slot
9726   db 0 ;; pci bus number
9727   db 0x18 ;; pci device number (bit 7-3)
9728   db 0x62 ;; link value INTA#
9729   dw 0xdef8 ;; IRQ bitmap INTA#
9730   db 0x63 ;; link value INTB#
9731   dw 0xdef8 ;; IRQ bitmap INTB#
9732   db 0x60 ;; link value INTC#
9733   dw 0xdef8 ;; IRQ bitmap INTC#
9734   db 0x61 ;; link value INTD#
9735   dw 0xdef8 ;; IRQ bitmap INTD#
9736   db 2 ;; physical slot (0 = embedded)
9737   db 0 ;; reserved
9738   ;; 4th slot entry: 3rd PCI slot
9739   db 0 ;; pci bus number
9740   db 0x20 ;; pci device number (bit 7-3)
9741   db 0x63 ;; link value INTA#
9742   dw 0xdef8 ;; IRQ bitmap INTA#
9743   db 0x60 ;; link value INTB#
9744   dw 0xdef8 ;; IRQ bitmap INTB#
9745   db 0x61 ;; link value INTC#
9746   dw 0xdef8 ;; IRQ bitmap INTC#
9747   db 0x62 ;; link value INTD#
9748   dw 0xdef8 ;; IRQ bitmap INTD#
9749   db 3 ;; physical slot (0 = embedded)
9750   db 0 ;; reserved
9751   ;; 5th slot entry: 4rd PCI slot
9752   db 0 ;; pci bus number
9753   db 0x28 ;; pci device number (bit 7-3)
9754   db 0x60 ;; link value INTA#
9755   dw 0xdef8 ;; IRQ bitmap INTA#
9756   db 0x61 ;; link value INTB#
9757   dw 0xdef8 ;; IRQ bitmap INTB#
9758   db 0x62 ;; link value INTC#
9759   dw 0xdef8 ;; IRQ bitmap INTC#
9760   db 0x63 ;; link value INTD#
9761   dw 0xdef8 ;; IRQ bitmap INTD#
9762   db 4 ;; physical slot (0 = embedded)
9763   db 0 ;; reserved
9764   ;; 6th slot entry: 5rd PCI slot
9765   db 0 ;; pci bus number
9766   db 0x30 ;; pci device number (bit 7-3)
9767   db 0x61 ;; link value INTA#
9768   dw 0xdef8 ;; IRQ bitmap INTA#
9769   db 0x62 ;; link value INTB#
9770   dw 0xdef8 ;; IRQ bitmap INTB#
9771   db 0x63 ;; link value INTC#
9772   dw 0xdef8 ;; IRQ bitmap INTC#
9773   db 0x60 ;; link value INTD#
9774   dw 0xdef8 ;; IRQ bitmap INTD#
9775   db 5 ;; physical slot (0 = embedded)
9776   db 0 ;; reserved
9777 pci_routing_table_structure_end:
9778 
9779 #if !BX_ROMBIOS32
9780 pci_irq_list:
9781   db 11, 10, 9, 5;
9782 
9783 pcibios_init_sel_reg:
9784   push eax
9785   mov eax, #0x800000
9786   mov ax,  bx
9787   shl eax, #8
9788   and dl,  #0xfc
9789   or  al,  dl
9790   mov dx,  #0x0cf8
9791   out dx,  eax
9792   pop eax
9793   ret
9794 
9795 pcibios_init_iomem_bases:
9796   push bp
9797   mov  bp, sp
9798   mov  eax, #0xe0000000 ;; base for memory init
9799   push eax
9800   mov  ax, #0xc000 ;; base for i/o init
9801   push ax
9802   mov  ax, #0x0010 ;; start at base address #0
9803   push ax
9804   mov  bx, #0x0008
9805 pci_init_io_loop1:
9806   mov  dl, #0x00
9807   call pcibios_init_sel_reg
9808   mov  dx, #0x0cfc
9809   in   ax, dx
9810   cmp  ax, #0xffff
9811   jz   next_pci_dev
9812   mov  dl, #0x04 ;; disable i/o and memory space access
9813   call pcibios_init_sel_reg
9814   mov  dx, #0x0cfc
9815   in   al, dx
9816   and  al, #0xfc
9817   out  dx, al
9818 pci_init_io_loop2:
9819   mov  dl, [bp-8]
9820   call pcibios_init_sel_reg
9821   mov  dx, #0x0cfc
9822   in   eax, dx
9823   test al, #0x01
9824   jnz  init_io_base
9825   mov  ecx, eax
9826   mov  eax, #0xffffffff
9827   out  dx, eax
9828   in   eax, dx
9829   cmp  eax, ecx
9830   je   next_pci_base
9831   xor  eax, #0xffffffff
9832   mov  ecx, eax
9833   mov  eax, [bp-4]
9834   out  dx, eax
9835   add  eax, ecx ;; calculate next free mem base
9836   add  eax, #0x01000000
9837   and  eax, #0xff000000
9838   mov  [bp-4], eax
9839   jmp  next_pci_base
9840 init_io_base:
9841   mov  cx, ax
9842   mov  ax, #0xffff
9843   out  dx, ax
9844   in   ax, dx
9845   cmp  ax, cx
9846   je   next_pci_base
9847   xor  ax, #0xfffe
9848   mov  cx, ax
9849   mov  ax, [bp-6]
9850   out  dx, ax
9851   add  ax, cx ;; calculate next free i/o base
9852   add  ax, #0x0100
9853   and  ax, #0xff00
9854   mov  [bp-6], ax
9855 next_pci_base:
9856   mov  al, [bp-8]
9857   add  al, #0x04
9858   cmp  al, #0x28
9859   je   enable_iomem_space
9860   mov  byte ptr[bp-8], al
9861   jmp  pci_init_io_loop2
9862 enable_iomem_space:
9863   mov  dl, #0x04 ;; enable i/o and memory space access if available
9864   call pcibios_init_sel_reg
9865   mov  dx, #0x0cfc
9866   in   al, dx
9867   or   al, #0x07
9868   out  dx, al
9869 next_pci_dev:
9870   mov  byte ptr[bp-8], #0x10
9871   inc  bx
9872   cmp  bx, #0x0100
9873   jne  pci_init_io_loop1
9874   mov  sp, bp
9875   pop  bp
9876   ret
9877 
9878 pcibios_init_set_elcr:
9879   push ax
9880   push cx
9881   mov  dx, #0x04d0
9882   test al, #0x08
9883   jz   is_master_pic
9884   inc  dx
9885   and  al, #0x07
9886 is_master_pic:
9887   mov  cl, al
9888   mov  bl, #0x01
9889   shl  bl, cl
9890   in   al, dx
9891   or   al, bl
9892   out  dx, al
9893   pop  cx
9894   pop  ax
9895   ret
9896 
9897 pcibios_init_irqs:
9898   push ds
9899   push bp
9900   mov  ax, #0xf000
9901   mov  ds, ax
9902   mov  dx, #0x04d0 ;; reset ELCR1 + ELCR2
9903   mov  al, #0x00
9904   out  dx, al
9905   inc  dx
9906   out  dx, al
9907   mov  si, #pci_routing_table_structure
9908   mov  bh, [si+8]
9909   mov  bl, [si+9]
9910   mov  dl, #0x00
9911   call pcibios_init_sel_reg
9912   mov  dx, #0x0cfc
9913   in   ax, dx
9914   cmp  ax, [si+12] ;; check irq router
9915   jne  pci_init_end
9916   mov  dl, [si+34]
9917   call pcibios_init_sel_reg
9918   push bx ;; save irq router bus + devfunc
9919   mov  dx, #0x0cfc
9920   mov  ax, #0x8080
9921   out  dx, ax ;; reset PIRQ route control
9922   add  dx, #2
9923   out  dx, ax
9924   mov  ax, [si+6]
9925   sub  ax, #0x20
9926   shr  ax, #4
9927   mov  cx, ax
9928   add  si, #0x20 ;; set pointer to 1st entry
9929   mov  bp, sp
9930   mov  ax, #pci_irq_list
9931   push ax
9932   xor  ax, ax
9933   push ax
9934 pci_init_irq_loop1:
9935   mov  bh, [si]
9936   mov  bl, [si+1]
9937 pci_init_irq_loop2:
9938   mov  dl, #0x00
9939   call pcibios_init_sel_reg
9940   mov  dx, #0x0cfc
9941   in   ax, dx
9942   cmp  ax, #0xffff
9943   jnz  pci_test_int_pin
9944   test bl, #0x07
9945   jz   next_pir_entry
9946   jmp  next_pci_func
9947 pci_test_int_pin:
9948   mov  dl, #0x3c
9949   call pcibios_init_sel_reg
9950   mov  dx, #0x0cfd
9951   in   al, dx
9952   and  al, #0x07
9953   jz   next_pci_func
9954   dec  al ;; determine pirq reg
9955   mov  dl, #0x03
9956   mul  al, dl
9957   add  al, #0x02
9958   xor  ah, ah
9959   mov  bx, ax
9960   mov  al, [si+bx]
9961   mov  dl, al
9962   mov  bx, [bp]
9963   call pcibios_init_sel_reg
9964   mov  dx, #0x0cfc
9965   and  al, #0x03
9966   add  dl, al
9967   in   al, dx
9968   cmp  al, #0x80
9969   jb   pirq_found
9970   mov  bx, [bp-2] ;; pci irq list pointer
9971   mov  al, [bx]
9972   out  dx, al
9973   inc  bx
9974   mov  [bp-2], bx
9975   call pcibios_init_set_elcr
9976 pirq_found:
9977   mov  bh, [si]
9978   mov  bl, [si+1]
9979   add  bl, [bp-3] ;; pci function number
9980   mov  dl, #0x3c
9981   call pcibios_init_sel_reg
9982   mov  dx, #0x0cfc
9983   out  dx, al
9984 next_pci_func:
9985   inc  byte ptr[bp-3]
9986   inc  bl
9987   test bl, #0x07
9988   jnz  pci_init_irq_loop2
9989 next_pir_entry:
9990   add  si, #0x10
9991   mov  byte ptr[bp-3], #0x00
9992   loop pci_init_irq_loop1
9993   mov  sp, bp
9994   pop  bx
9995 pci_init_end:
9996   pop  bp
9997   pop  ds
9998   ret
9999 #endif // !BX_ROMBIOS32
10000 #endif // BX_PCIBIOS
10001 
10002 #if BX_ROMBIOS32
10003 rombios32_init:
10004   ;; save a20 and enable it
10005   in al, 0x92
10006   push ax
10007   or al, #0x02
10008   out 0x92, al
10009 
10010   ;; save SS:SP to the BDA
10011   xor ax, ax
10012   mov ds, ax
10013   mov 0x0469, ss
10014   mov 0x0467, sp
10015 
10016   SEG CS
10017     lidt [pmode_IDT_info]
10018   SEG CS
10019     lgdt [rombios32_gdt_48]
10020   ;; set PE bit in CR0
10021   mov  eax, cr0
10022   or   al, #0x01
10023   mov  cr0, eax
10024   ;; start protected mode code: ljmpl 0x10:rombios32_init1
10025   db 0x66, 0xea
10026   dw rombios32_05
10027   dw 0x000f       ;; high 16 bit address
10028   dw 0x0010
10029 
10030 use32 386
10031 rombios32_05:
10032   ;; init data segments
10033   mov eax, #0x18
10034   mov ds, ax
10035   mov es, ax
10036   mov ss, ax
10037   xor eax, eax
10038   mov fs, ax
10039   mov gs, ax
10040   cld
10041 
10042   ;; init the stack pointer to point below EBDA
10043   mov ax, [0x040e]
10044   shl eax, #4
10045   mov esp, #-0x10
10046   add esp, eax
10047 
10048   ;; pass pointer to s3_resume_flag and s3_resume_vector to rombios32
10049   push #0x04b0
10050   push #0x04b2
10051 
10052   ;; call rombios32 code
10053   mov eax, #0x000e0000
10054   call eax
10055 
10056   ;; return to 16 bit protected mode first
10057   db 0xea
10058   dd rombios32_10
10059   dw 0x20
10060 
10061 use16 386
10062 rombios32_10:
10063   ;; restore data segment limits to 0xffff
10064   mov ax, #0x28
10065   mov ds, ax
10066   mov es, ax
10067   mov ss, ax
10068   mov fs, ax
10069   mov gs, ax
10070 
10071   ;; reset PE bit in CR0
10072   mov  eax, cr0
10073   and  al, #0xFE
10074   mov  cr0, eax
10075 
10076   ;; far jump to flush CPU queue after transition to real mode
10077   JMP_AP(0xf000, rombios32_real_mode)
10078 
10079 rombios32_real_mode:
10080   ;; restore IDT to normal real-mode defaults
10081   SEG CS
10082     lidt [rmode_IDT_info]
10083 
10084   xor ax, ax
10085   mov ds, ax
10086   mov es, ax
10087   mov fs, ax
10088   mov gs, ax
10089 
10090   ;; restore SS:SP from the BDA
10091   mov ss, 0x0469
10092   xor esp, esp
10093   mov sp, 0x0467
10094   ;; restore a20
10095   pop ax
10096   out 0x92, al
10097   ret
10098 
10099 rombios32_gdt_48:
10100   dw 0x30
10101   dw rombios32_gdt
10102   dw 0x000f
10103 
10104 rombios32_gdt:
10105   dw 0, 0, 0, 0
10106   dw 0, 0, 0, 0
10107   dw 0xffff, 0, 0x9b00, 0x00cf ; 32 bit flat code segment (0x10)
10108   dw 0xffff, 0, 0x9300, 0x00cf ; 32 bit flat data segment (0x18)
10109   dw 0xffff, 0, 0x9b0f, 0x0000 ; 16 bit code segment base=0xf0000 limit=0xffff
10110   dw 0xffff, 0, 0x9300, 0x0000 ; 16 bit data segment base=0x0 limit=0xffff
10111 #endif // BX_ROMBIOS32
10112 
10113 
10114 ; parallel port detection: base address in DX, index in BX, timeout in CL
10115 detect_parport:
10116   push dx
10117   add  dx, #2
10118   in   al, dx
10119   and  al, #0xdf ; clear input mode
10120   out  dx, al
10121   pop  dx
10122   mov  al, #0xaa
10123   out  dx, al
10124   in   al, dx
10125   cmp  al, #0xaa
10126   jne  no_parport
10127   push bx
10128   shl  bx, #1
10129   mov  [bx+0x408], dx ; Parallel I/O address
10130   pop  bx
10131   mov  [bx+0x478], cl ; Parallel printer timeout
10132   inc  bx
10133 no_parport:
10134   ret
10135 
10136 ; serial port detection: base address in DX, index in BX, timeout in CL
10137 detect_serial:
10138   push dx
10139   inc  dx
10140   mov  al, #0x02
10141   out  dx, al
10142   in   al, dx
10143   cmp  al, #0x02
10144   jne  no_serial
10145   inc  dx
10146   in   al, dx
10147   cmp  al, #0x02
10148   jne  no_serial
10149   dec  dx
10150   xor  al, al
10151   out  dx, al
10152   pop  dx
10153   push bx
10154   shl  bx, #1
10155   mov  [bx+0x400], dx ; Serial I/O address
10156   pop  bx
10157   mov  [bx+0x47c], cl ; Serial timeout
10158   inc  bx
10159   ret
10160 no_serial:
10161   pop  dx
10162   ret
10163 
10164 rom_checksum:
10165   push ax
10166   push bx
10167   push cx
10168   xor  ax, ax
10169   xor  bx, bx
10170   xor  cx, cx
10171   mov  ch, [2]
10172   shl  cx, #1
10173 checksum_loop:
10174   add  al, [bx]
10175   inc  bx
10176   loop checksum_loop
10177   and  al, #0xff
10178   pop  cx
10179   pop  bx
10180   pop  ax
10181   ret
10182 
10183 
10184 ;; We need a copy of this string, but we are not actually a PnP BIOS,
10185 ;; so make sure it is *not* aligned, so OSes will not see it if they scan.
10186 .align 16
10187   db 0
10188 pnp_string:
10189   .ascii "$PnP"
10190 
10191 
10192 rom_scan:
10193   ;; Scan for existence of valid expansion ROMS.
10194   ;;   Video ROM:   from 0xC0000..0xC7FFF in 2k increments
10195   ;;   General ROM: from 0xC8000..0xDFFFF in 2k increments
10196   ;;   System  ROM: only 0xE0000
10197   ;;
10198   ;; Header:
10199   ;;   Offset    Value
10200   ;;   0         0x55
10201   ;;   1         0xAA
10202   ;;   2         ROM length in 512-byte blocks
10203   ;;   3         ROM initialization entry point (FAR CALL)
10204 
10205 rom_scan_loop:
10206   push ax       ;; Save AX
10207   mov  ds, cx
10208   mov  ax, #0x0004 ;; start with increment of 4 (512-byte) blocks = 2k
10209   cmp [0], #0xAA55 ;; look for signature
10210   jne  rom_scan_increment
10211   call rom_checksum
10212   jnz  rom_scan_increment
10213   mov  al, [2]  ;; change increment to ROM length in 512-byte blocks
10214 
10215   ;; We want our increment in 512-byte quantities, rounded to
10216   ;; the nearest 2k quantity, since we only scan at 2k intervals.
10217   test al, #0x03
10218   jz   block_count_rounded
10219   and  al, #0xfc ;; needs rounding up
10220   add  al, #0x04
10221 block_count_rounded:
10222 
10223   xor  bx, bx   ;; Restore DS back to 0000:
10224   mov  ds, bx
10225   push ax       ;; Save AX
10226   push di       ;; Save DI
10227   ;; Push addr of ROM entry point
10228   push cx       ;; Push seg
10229   push #0x0003  ;; Push offset
10230 
10231   ;; Point ES:DI at "$PnP", which tells the ROM that we are a PnP BIOS.
10232   ;; That should stop it grabbing INT 19h; we will use its BEV instead.
10233   mov  ax, #0xf000
10234   mov  es, ax
10235   lea  di, pnp_string
10236 
10237   mov  bp, sp   ;; Call ROM init routine using seg:off on stack
10238   db   0xff     ;; call_far ss:[bp+0]
10239   db   0x5e
10240   db   0
10241   cli           ;; In case expansion ROM BIOS turns IF on
10242   add  sp, #2   ;; Pop offset value
10243   pop  cx       ;; Pop seg value (restore CX)
10244 
10245   ;; Look at the ROM's PnP Expansion header.  Properly, we're supposed
10246   ;; to init all the ROMs and then go back and build an IPL table of
10247   ;; all the bootable devices, but we can get away with one pass.
10248   mov  ds, cx       ;; ROM base
10249   mov  bx, 0x001a   ;; 0x1A is the offset into ROM header that contains...
10250   mov  ax, [bx]     ;; the offset of PnP expansion header, where...
10251   cmp  ax, #0x5024  ;; we look for signature "$PnP"
10252   jne  no_bev
10253   mov  ax, 2[bx]
10254   cmp  ax, #0x506e
10255   jne  no_bev
10256 
10257   mov  ax, 0x16[bx] ;; 0x16 is the offset of Boot Connection Vector
10258   cmp  ax, #0x0000
10259   je   no_bcv
10260 
10261   ;; Option ROM has BCV. Run it now.
10262   push cx       ;; Push seg
10263   push ax       ;; Push offset
10264 
10265   ;; Point ES:DI at "$PnP", which tells the ROM that we are a PnP BIOS.
10266   mov  bx, #0xf000
10267   mov  es, bx
10268   lea  di, pnp_string
10269   /* jump to BCV function entry pointer */
10270   mov  bp, sp   ;; Call ROM BCV routine using seg:off on stack
10271   db   0xff     ;; call_far ss:[bp+0]
10272   db   0x5e
10273   db   0
10274   cli           ;; In case expansion ROM BIOS turns IF on
10275   add  sp, #2   ;; Pop offset value
10276   pop  cx       ;; Pop seg value (restore CX)
10277   jmp   no_bev
10278 
10279 no_bcv:
10280   mov  ax, 0x1a[bx] ;; 0x1A is also the offset into the expansion header of...
10281   cmp  ax, #0x0000  ;; the Bootstrap Entry Vector, or zero if there is none.
10282   je   no_bev
10283 
10284   ;; Found a device that thinks it can boot the system.  Record its BEV and product name string.
10285   mov  di, 0x10[bx]            ;; Pointer to the product name string or zero if none
10286   mov  bx, #IPL_SEG            ;; Go to the segment where the IPL table lives
10287   mov  ds, bx
10288   mov  bx, IPL_COUNT_OFFSET    ;; Read the number of entries so far
10289   cmp  bx, #IPL_TABLE_ENTRIES
10290   je   no_bev                  ;; Get out if the table is full
10291   shl  bx, #0x4                ;; Turn count into offset (entries are 16 bytes)
10292   mov  0[bx], #IPL_TYPE_BEV    ;; This entry is a BEV device
10293   mov  6[bx], cx               ;; Build a far pointer from the segment...
10294   mov  4[bx], ax               ;; and the offset
10295   cmp  di, #0x0000
10296   je   no_prod_str
10297   mov  0xA[bx], cx             ;; Build a far pointer from the segment...
10298   mov  8[bx], di               ;; and the offset
10299 no_prod_str:
10300   shr  bx, #0x4                ;; Turn the offset back into a count
10301   inc  bx                      ;; We have one more entry now
10302   mov  IPL_COUNT_OFFSET, bx    ;; Remember that.
10303 
10304 no_bev:
10305   pop  di       ;; Restore DI
10306   pop  ax       ;; Restore AX
10307 rom_scan_increment:
10308   shl  ax, #5   ;; convert 512-bytes blocks to 16-byte increments
10309                 ;; because the segment selector is shifted left 4 bits.
10310   add  cx, ax
10311   pop  ax       ;; Restore AX
10312   cmp  cx, ax
10313   jbe  rom_scan_loop
10314 
10315   xor  ax, ax   ;; Restore DS back to 0000:
10316   mov  ds, ax
10317   ret
10318 
10319 post_init_pic:
10320   mov al, #0x11 ; send initialisation commands
10321   out 0x20, al
10322   out 0xa0, al
10323   mov al, #0x08
10324   out 0x21, al
10325   mov al, #0x70
10326   out 0xa1, al
10327   mov al, #0x04
10328   out 0x21, al
10329   mov al, #0x02
10330   out 0xa1, al
10331   mov al, #0x01
10332   out 0x21, al
10333   out 0xa1, al
10334   mov  al, #0xb8
10335   out  0x21, AL ;master pic: unmask IRQ 0, 1, 2, 6
10336 #if BX_USE_PS2_MOUSE
10337   mov  al, #0x8f
10338 #else
10339   mov  al, #0x9f
10340 #endif
10341   out  0xa1, AL ;slave  pic: unmask IRQ 12, 13, 14
10342   ret
10343 
10344 ;; the following area can be used to write dynamically generated tables
10345   .align 16
10346 bios_table_area_start:
10347   dd 0xaafb4442
10348   dd bios_table_area_end - bios_table_area_start - 8;
10349 
10350 ;--------
10351 ;- POST -
10352 ;--------
10353 .org 0xe05b ; POST Entry Point
10354 post:
10355 
10356   xor ax, ax
10357 
10358   ;; first reset the DMA controllers
10359   out 0x0d,al
10360   out 0xda,al
10361 
10362   ;; then initialize the DMA controllers
10363   mov al, #0xC0
10364   out 0xD6, al ; cascade mode of channel 4 enabled
10365   mov al, #0x00
10366   out 0xD4, al ; unmask channel 4
10367 
10368   ;; Examine CMOS shutdown status.
10369   mov AL, #0x0f
10370   out 0x70, AL
10371   in  AL, 0x71
10372 
10373   ;; backup status
10374   mov bl, al
10375 
10376   ;; Reset CMOS shutdown status.
10377   mov AL, #0x0f
10378   out 0x70, AL          ; select CMOS register Fh
10379   mov AL, #0x00
10380   out 0x71, AL          ; set shutdown action to normal
10381 
10382   ;; Examine CMOS shutdown status.
10383   mov al, bl
10384 
10385   ;; 0x00, 0x09, 0x0D+ = normal startup
10386   cmp AL, #0x00
10387   jz normal_post
10388   cmp AL, #0x0d
10389   jae normal_post
10390   cmp AL, #0x09
10391   je normal_post
10392 
10393   ;; 0x05 = eoi + jmp via [0x40:0x67] jump
10394   cmp al, #0x05
10395   je  eoi_jmp_post
10396 
10397   ;; 0x0A = jmp via [0x40:0x67] jump
10398   cmp al, #0x0a
10399   je  jmp_post_0x467
10400 
10401   ;; 0x0B = iret via [0x40:0x67]
10402   cmp al, #0x0b
10403   je  iret_post_0x467
10404 
10405   ;; 0x0C = retf via [0x40:0x67]
10406   cmp al, #0x0c
10407   je  retf_post_0x467
10408 
10409   ;; Examine CMOS shutdown status.
10410   ;;  0x01,0x02,0x03,0x04,0x06,0x07,0x08 = Unimplemented shutdown status.
10411   push bx
10412   call _shutdown_status_panic
10413 
10414 #if 0
10415   HALT(__LINE__)
10416   ;
10417   ;#if 0
10418   ;  0xb0, 0x20,       /* mov al, #0x20 */
10419   ;  0xe6, 0x20,       /* out 0x20, al    ;send EOI to PIC */
10420   ;#endif
10421   ;
10422   pop es
10423   pop ds
10424   popa
10425   iret
10426 #endif
10427 
10428 normal_post:
10429   ; case 0: normal startup
10430 
10431   cli
10432   mov  ax, #0xfffe
10433   mov  sp, ax
10434   xor  ax, ax
10435   mov  ds, ax
10436   mov  ss, ax
10437 
10438   ;; Save shutdown status
10439   mov 0x04b0, bl
10440 
10441   cmp bl, #0xfe
10442   jz s3_post
10443 
10444   ;; zero out BIOS data area (40:00..40:ff)
10445   mov  es, ax
10446   mov  cx, #0x0080 ;; 128 words
10447   mov  di, #0x0400
10448   cld
10449   rep
10450     stosw
10451 
10452   call _log_bios_start
10453 
10454   ;; set all interrupts to default handler
10455   xor  bx, bx         ;; offset index
10456   mov  cx, #0x0100    ;; counter (256 interrupts)
10457   mov  ax, #dummy_iret_handler
10458   mov  dx, #0xF000
10459 
10460 post_default_ints:
10461   mov  [bx], ax
10462   add  bx, #2
10463   mov  [bx], dx
10464   add  bx, #2
10465   loop post_default_ints
10466 
10467   ;; set vector 0x79 to zero
10468   ;; this is used by 'gardian angel' protection system
10469   SET_INT_VECTOR(0x79, #0, #0)
10470 
10471   ;; base memory in K 40:13 (word)
10472   mov  ax, #BASE_MEM_IN_K
10473   mov  0x0413, ax
10474 
10475 
10476   ;; Manufacturing Test 40:12
10477   ;;   zerod out above
10478 
10479   ;; Warm Boot Flag 0040:0072
10480   ;;   value of 1234h = skip memory checks
10481   ;;   zerod out above
10482 
10483 
10484   ;; Printer Services vector
10485   SET_INT_VECTOR(0x17, #0xF000, #int17_handler)
10486 
10487   ;; Bootstrap failure vector
10488   SET_INT_VECTOR(0x18, #0xF000, #int18_handler)
10489 
10490   ;; Bootstrap Loader vector
10491   SET_INT_VECTOR(0x19, #0xF000, #int19_handler)
10492 
10493   ;; User Timer Tick vector
10494   SET_INT_VECTOR(0x1c, #0xF000, #int1c_handler)
10495 
10496   ;; Memory Size Check vector
10497   SET_INT_VECTOR(0x12, #0xF000, #int12_handler)
10498 
10499   ;; Equipment Configuration Check vector
10500   SET_INT_VECTOR(0x11, #0xF000, #int11_handler)
10501 
10502   ;; System Services
10503   SET_INT_VECTOR(0x15, #0xF000, #int15_handler)
10504 
10505   ;; EBDA setup
10506   call ebda_post
10507 
10508   ;; PIT setup
10509   SET_INT_VECTOR(0x08, #0xF000, #int08_handler)
10510   ;; int 1C already points at dummy_iret_handler (above)
10511   mov al, #0x34 ; timer0: binary count, 16bit count, mode 2
10512   out 0x43, al
10513   mov al, #0x00 ; maximum count of 0000H = 18.2Hz
10514   out 0x40, al
10515   out 0x40, al
10516 
10517   ;; Keyboard
10518   SET_INT_VECTOR(0x09, #0xF000, #int09_handler)
10519   SET_INT_VECTOR(0x16, #0xF000, #int16_handler)
10520 
10521   xor  ax, ax
10522   mov  ds, ax
10523   mov  0x0417, al /* keyboard shift flags, set 1 */
10524   mov  0x0418, al /* keyboard shift flags, set 2 */
10525   mov  0x0419, al /* keyboard alt-numpad work area */
10526   mov  0x0471, al /* keyboard ctrl-break flag */
10527   mov  0x0497, al /* keyboard status flags 4 */
10528   mov  al, #0x10
10529   mov  0x0496, al /* keyboard status flags 3 */
10530 
10531 
10532   /* keyboard head of buffer pointer */
10533   mov  bx, #0x001E
10534   mov  0x041A, bx
10535 
10536   /* keyboard end of buffer pointer */
10537   mov  0x041C, bx
10538 
10539   /* keyboard pointer to start of buffer */
10540   mov  bx, #0x001E
10541   mov  0x0480, bx
10542 
10543   /* keyboard pointer to end of buffer */
10544   mov  bx, #0x003E
10545   mov  0x0482, bx
10546 
10547   /* init the keyboard */
10548   call _keyboard_init
10549 
10550   ;; mov CMOS Equipment Byte to BDA Equipment Word
10551   mov  ax, 0x0410
10552   mov  al, #0x14
10553   out  0x70, al
10554   in   al, 0x71
10555   mov  0x0410, ax
10556 
10557 
10558   ;; Parallel setup
10559   SET_INT_VECTOR(0x0F, #0xF000, #dummy_iret_handler)
10560   xor ax, ax
10561   mov ds, ax
10562   xor bx, bx
10563   mov cl, #0x14 ; timeout value
10564   mov dx, #0x378 ; Parallel I/O address, port 1
10565   call detect_parport
10566   mov dx, #0x278 ; Parallel I/O address, port 2
10567   call detect_parport
10568   shl bx, #0x0e
10569   mov ax, 0x410   ; Equipment word bits 14..15 determing # parallel ports
10570   and ax, #0x3fff
10571   or  ax, bx ; set number of parallel ports
10572   mov 0x410, ax
10573 
10574   ;; Serial setup
10575   SET_INT_VECTOR(0x0C, #0xF000, #dummy_iret_handler)
10576   SET_INT_VECTOR(0x14, #0xF000, #int14_handler)
10577   xor bx, bx
10578   mov cl, #0x0a ; timeout value
10579   mov dx, #0x03f8 ; Serial I/O address, port 1
10580   call detect_serial
10581   mov dx, #0x02f8 ; Serial I/O address, port 2
10582   call detect_serial
10583   mov dx, #0x03e8 ; Serial I/O address, port 3
10584   call detect_serial
10585   mov dx, #0x02e8 ; Serial I/O address, port 4
10586   call detect_serial
10587   shl bx, #0x09
10588   mov ax, 0x410   ; Equipment word bits 9..11 determing # serial ports
10589   and ax, #0xf1ff
10590   or  ax, bx ; set number of serial port
10591   mov 0x410, ax
10592 
10593   ;; CMOS RTC
10594   SET_INT_VECTOR(0x1A, #0xF000, #int1a_handler)
10595   SET_INT_VECTOR(0x4A, #0xF000, #dummy_iret_handler)
10596   SET_INT_VECTOR(0x70, #0xF000, #int70_handler)
10597   ;; BIOS DATA AREA 0x4CE ???
10598   call timer_tick_post
10599 
10600   ;; PS/2 mouse setup
10601   SET_INT_VECTOR(0x74, #0xF000, #int74_handler)
10602 
10603   ;; IRQ13 (FPU exception) setup
10604   SET_INT_VECTOR(0x75, #0xF000, #int75_handler)
10605 
10606   ;; Video setup
10607   SET_INT_VECTOR(0x10, #0xF000, #int10_handler)
10608 
10609   ;; PIC
10610   call post_init_pic
10611 
10612   mov  cx, #0xc000  ;; init vga bios
10613   mov  ax, #0xc780
10614   call rom_scan
10615 
10616   call _print_bios_banner
10617 
10618 #if BX_ROMBIOS32
10619   call rombios32_init
10620 #else
10621 #if BX_PCIBIOS
10622   call pcibios_init_iomem_bases
10623   call pcibios_init_irqs
10624 #endif //BX_PCIBIOS
10625 #endif
10626 
10627   ;;
10628   ;; Floppy setup
10629   ;;
10630   call floppy_drive_post
10631 
10632   ;;
10633   ;; Hard Drive setup
10634   ;;
10635   call hard_drive_post
10636 
10637 #if BX_USE_ATADRV
10638 
10639   ;;
10640   ;; ATA/ATAPI driver setup
10641   ;;
10642   call _ata_init
10643   call _ata_detect
10644   ;;
10645 
10646 #endif // BX_USE_ATADRV
10647 
10648 #if BX_ELTORITO_BOOT
10649   ;;
10650   ;; eltorito floppy/harddisk emulation from cd
10651   ;;
10652   call _cdemu_init
10653   ;;
10654 #endif // BX_ELTORITO_BOOT
10655 
10656   call _init_boot_vectors
10657 
10658   mov  cx, #0xc800  ;; init option roms
10659   mov  ax, #0xe000
10660   call rom_scan
10661 
10662 #if BX_ELTORITO_BOOT
10663   call _interactive_bootkey
10664 #endif // BX_ELTORITO_BOOT
10665 
10666   sti        ;; enable interrupts
10667   int  #0x19
10668 
10669 .org 0xe2c3 ; NMI Handler Entry Point
10670 nmi:
10671   ;; FIXME the NMI handler should not panic
10672   ;; but iret when called from int75 (fpu exception)
10673   call _nmi_handler_msg
10674   iret
10675 
10676 int75_handler:
10677   out  0xf0, al         // clear irq13
10678   call eoi_both_pics    // clear interrupt
10679   int  2                // legacy nmi call
10680   iret
10681 
10682 ;-------------------------------------------
10683 ;- INT 13h Fixed Disk Services Entry Point -
10684 ;-------------------------------------------
10685 .org 0xe3fe ; INT 13h Fixed Disk Services Entry Point
10686 int13_handler:
10687   //JMPL(int13_relocated)
10688   jmp int13_relocated
10689 
10690 .org 0xe401 ; Fixed Disk Parameter Table
10691 
10692 ;----------
10693 ;- INT19h -
10694 ;----------
10695 .org 0xe6f2 ; INT 19h Boot Load Service Entry Point
10696 int19_handler:
10697 
10698   jmp int19_relocated
10699 ;-------------------------------------------
10700 ;- System BIOS Configuration Data Table
10701 ;-------------------------------------------
10702 .org BIOS_CONFIG_TABLE
10703 db 0x08                  ; Table size (bytes) -Lo
10704 db 0x00                  ; Table size (bytes) -Hi
10705 db SYS_MODEL_ID
10706 db SYS_SUBMODEL_ID
10707 db BIOS_REVISION
10708 ; Feature byte 1
10709 ; b7: 1=DMA channel 3 used by hard disk
10710 ; b6: 1=2 interrupt controllers present
10711 ; b5: 1=RTC present
10712 ; b4: 1=BIOS calls int 15h/4Fh every key
10713 ; b3: 1=wait for extern event supported (Int 15h/41h)
10714 ; b2: 1=extended BIOS data area used
10715 ; b1: 0=AT or ESDI bus, 1=MicroChannel
10716 ; b0: 1=Dual bus (MicroChannel + ISA)
10717 db (0 << 7) | \
10718    (1 << 6) | \
10719    (1 << 5) | \
10720    (BX_CALL_INT15_4F << 4) | \
10721    (0 << 3) | \
10722    (BX_USE_EBDA << 2) | \
10723    (0 << 1) | \
10724    (0 << 0)
10725 ; Feature byte 2
10726 ; b7: 1=32-bit DMA supported
10727 ; b6: 1=int16h, function 9 supported
10728 ; b5: 1=int15h/C6h (get POS data) supported
10729 ; b4: 1=int15h/C7h (get mem map info) supported
10730 ; b3: 1=int15h/C8h (en/dis CPU) supported
10731 ; b2: 1=non-8042 kb controller
10732 ; b1: 1=data streaming supported
10733 ; b0: reserved
10734 db (0 << 7) | \
10735    (1 << 6) | \
10736    (0 << 5) | \
10737    (0 << 4) | \
10738    (0 << 3) | \
10739    (0 << 2) | \
10740    (0 << 1) | \
10741    (0 << 0)
10742 ; Feature byte 3
10743 ; b7: not used
10744 ; b6: reserved
10745 ; b5: reserved
10746 ; b4: POST supports ROM-to-RAM enable/disable
10747 ; b3: SCSI on system board
10748 ; b2: info panel installed
10749 ; b1: Initial Machine Load (IML) system - BIOS on disk
10750 ; b0: SCSI supported in IML
10751 db 0x00
10752 ; Feature byte 4
10753 ; b7: IBM private
10754 ; b6: EEPROM present
10755 ; b5-3: ABIOS presence (011 = not supported)
10756 ; b2: private
10757 ; b1: memory split above 16Mb supported
10758 ; b0: POSTEXT directly supported by POST
10759 db 0x00
10760 ; Feature byte 5 (IBM)
10761 ; b1: enhanced mouse
10762 ; b0: flash EPROM
10763 db 0x00
10764 
10765 
10766 
10767 .org 0xe729 ; Baud Rate Generator Table
10768 
10769 ;----------
10770 ;- INT14h -
10771 ;----------
10772 .org 0xe739 ; INT 14h Serial Communications Service Entry Point
10773 int14_handler:
10774   push ds
10775   pusha
10776   xor  ax, ax
10777   mov  ds, ax
10778   call _int14_function
10779   popa
10780   pop  ds
10781   iret
10782 
10783 
10784 ;----------------------------------------
10785 ;- INT 16h Keyboard Service Entry Point -
10786 ;----------------------------------------
10787 .org 0xe82e
10788 int16_handler:
10789 
10790   sti
10791   push  ds
10792   pushf
10793   pusha
10794 
10795   cmp   ah, #0x00
10796   je    int16_F00
10797   cmp   ah, #0x10
10798   je    int16_F00
10799 
10800   mov  bx, #0xf000
10801   mov  ds, bx
10802   call _int16_function
10803   popa
10804   popf
10805   pop  ds
10806   jz   int16_zero_set
10807 
10808 int16_zero_clear:
10809   push bp
10810   mov  bp, sp
10811   //SEG SS
10812   and  BYTE [bp + 0x06], #0xbf
10813   pop  bp
10814   iret
10815 
10816 int16_zero_set:
10817   push bp
10818   mov  bp, sp
10819   //SEG SS
10820   or   BYTE [bp + 0x06], #0x40
10821   pop  bp
10822   iret
10823 
10824 int16_F00:
10825   mov  bx, #0x0040
10826   mov  ds, bx
10827 
10828 int16_wait_for_key:
10829   cli
10830   mov  bx, 0x001a
10831   cmp  bx, 0x001c
10832   jne  int16_key_found
10833   sti
10834   nop
10835 #if 0
10836                            /* no key yet, call int 15h, function AX=9002 */
10837   0x50,                    /* push AX */
10838   0xb8, 0x02, 0x90,        /* mov AX, #0x9002 */
10839   0xcd, 0x15,              /* int 15h */
10840   0x58,                    /* pop  AX */
10841   0xeb, 0xea,              /* jmp   WAIT_FOR_KEY */
10842 #endif
10843   jmp  int16_wait_for_key
10844 
10845 int16_key_found:
10846   mov  bx, #0xf000
10847   mov  ds, bx
10848   call _int16_function
10849   popa
10850   popf
10851   pop  ds
10852 #if 0
10853                            /* notify int16 complete w/ int 15h, function AX=9102 */
10854   0x50,                    /* push AX */
10855   0xb8, 0x02, 0x91,        /* mov AX, #0x9102 */
10856   0xcd, 0x15,              /* int 15h */
10857   0x58,                    /* pop  AX */
10858 #endif
10859   iret
10860 
10861 
10862 
10863 ;-------------------------------------------------
10864 ;- INT09h : Keyboard Hardware Service Entry Point -
10865 ;-------------------------------------------------
10866 .org 0xe987
10867 int09_handler:
10868   cli
10869   push ax
10870 
10871   mov al, #0xAD      ;;disable keyboard
10872   out #0x64, al
10873 
10874   mov al, #0x0B
10875   out #0x20, al
10876   in  al, #0x20
10877   and al, #0x02
10878   jz  int09_finish
10879 
10880   in  al, #0x60             ;;read key from keyboard controller
10881   sti
10882   push  ds
10883   pusha
10884 #ifdef BX_CALL_INT15_4F
10885   mov  ah, #0x4f     ;; allow for keyboard intercept
10886   stc
10887   int  #0x15
10888   jnc  int09_done
10889 #endif
10890 
10891   ;; check for extended key
10892   cmp  al, #0xe0
10893   jne int09_check_pause
10894   xor  ax, ax
10895   mov  ds, ax
10896   mov  al, BYTE [0x496]     ;; mf2_state |= 0x02
10897   or   al, #0x02
10898   mov  BYTE [0x496], al
10899   jmp int09_done
10900 
10901 int09_check_pause: ;; check for pause key
10902   cmp  al, #0xe1
10903   jne int09_process_key
10904   xor  ax, ax
10905   mov  ds, ax
10906   mov  al, BYTE [0x496]     ;; mf2_state |= 0x01
10907   or   al, #0x01
10908   mov  BYTE [0x496], al
10909   jmp int09_done
10910 
10911 int09_process_key:
10912   mov   bx, #0xf000
10913   mov   ds, bx
10914   call  _int09_function
10915 
10916 int09_done:
10917   popa
10918   pop   ds
10919   cli
10920   call eoi_master_pic
10921 
10922 int09_finish:
10923   mov al, #0xAE      ;;enable keyboard
10924   out #0x64, al
10925   pop ax
10926   iret
10927 
10928 
10929 ;----------------------------------------
10930 ;- INT 13h Diskette Service Entry Point -
10931 ;----------------------------------------
10932 .org 0xec59
10933 int13_diskette:
10934   jmp int13_noeltorito
10935 
10936 ;---------------------------------------------
10937 ;- INT 0Eh Diskette Hardware ISR Entry Point -
10938 ;---------------------------------------------
10939 .org 0xef57 ; INT 0Eh Diskette Hardware ISR Entry Point
10940 int0e_handler:
10941   push ax
10942   push dx
10943   mov  dx, #0x03f4
10944   in   al, dx
10945   and  al, #0xc0
10946   cmp  al, #0xc0
10947   je   int0e_normal
10948   mov  dx, #0x03f5
10949   mov  al, #0x08 ; sense interrupt status
10950   out  dx, al
10951 int0e_loop1:
10952   mov  dx, #0x03f4
10953   in   al, dx
10954   and  al, #0xc0
10955   cmp  al, #0xc0
10956   jne  int0e_loop1
10957 int0e_loop2:
10958   mov  dx, #0x03f5
10959   in   al, dx
10960   mov  dx, #0x03f4
10961   in   al, dx
10962   and  al, #0xc0
10963   cmp  al, #0xc0
10964   je int0e_loop2
10965 int0e_normal:
10966   push ds
10967   xor  ax, ax ;; segment 0000
10968   mov  ds, ax
10969   call eoi_master_pic
10970   mov  al, 0x043e
10971   or   al, #0x80 ;; diskette interrupt has occurred
10972   mov  0x043e, al
10973   pop  ds
10974   pop  dx
10975   pop  ax
10976   iret
10977 
10978 
10979 .org 0xefc7 ; Diskette Controller Parameter Table
10980 diskette_param_table:
10981 ;;  Since no provisions are made for multiple drive types, most
10982 ;;  values in this table are ignored.  I set parameters for 1.44M
10983 ;;  floppy here
10984 db  0xAF
10985 db  0x02 ;; head load time 0000001, DMA used
10986 db  0x25
10987 db  0x02
10988 db    18
10989 db  0x1B
10990 db  0xFF
10991 db  0x6C
10992 db  0xF6
10993 db  0x0F
10994 db  0x08
10995 
10996 
10997 ;----------------------------------------
10998 ;- INT17h : Printer Service Entry Point -
10999 ;----------------------------------------
11000 .org 0xefd2
11001 int17_handler:
11002   push ds
11003   pusha
11004   xor  ax, ax
11005   mov  ds, ax
11006   call _int17_function
11007   popa
11008   pop  ds
11009   iret
11010 
11011 diskette_param_table2:
11012 ;;  New diskette parameter table adding 3 parameters from IBM
11013 ;;  Since no provisions are made for multiple drive types, most
11014 ;;  values in this table are ignored.  I set parameters for 1.44M
11015 ;;  floppy here
11016 db  0xAF
11017 db  0x02 ;; head load time 0000001, DMA used
11018 db  0x25
11019 db  0x02
11020 db    18
11021 db  0x1B
11022 db  0xFF
11023 db  0x6C
11024 db  0xF6
11025 db  0x0F
11026 db  0x08
11027 db    79 ;; maximum track
11028 db     0 ;; data transfer rate
11029 db     4 ;; drive type in cmos
11030 
11031 .org 0xf045 ; INT 10 Functions 0-Fh Entry Point
11032   HALT(__LINE__)
11033   iret
11034 
11035 ;----------
11036 ;- INT10h -
11037 ;----------
11038 .org 0xf065 ; INT 10h Video Support Service Entry Point
11039 int10_handler:
11040   ;; dont do anything, since the VGA BIOS handles int10h requests
11041   iret
11042 
11043 .org 0xf0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
11044 
11045 ;----------
11046 ;- INT12h -
11047 ;----------
11048 .org 0xf841 ; INT 12h Memory Size Service Entry Point
11049 ; ??? different for Pentium (machine check)?
11050 int12_handler:
11051   push ds
11052   mov  ax, #0x0040
11053   mov  ds, ax
11054   mov  ax, 0x0013
11055   pop  ds
11056   iret
11057 
11058 ;----------
11059 ;- INT11h -
11060 ;----------
11061 .org 0xf84d ; INT 11h Equipment List Service Entry Point
11062 int11_handler:
11063   push ds
11064   mov  ax, #0x0040
11065   mov  ds, ax
11066   mov  ax, 0x0010
11067   pop  ds
11068   iret
11069 
11070 ;----------
11071 ;- INT15h -
11072 ;----------
11073 .org 0xf859 ; INT 15h System Services Entry Point
11074 int15_handler:
11075   pushf
11076 #if BX_APM
11077   cmp ah, #0x53
11078   je apm_call
11079 #endif
11080   push  ds
11081   push  es
11082   cmp  ah, #0x86
11083   je int15_handler32
11084   cmp  ah, #0xE8
11085   je int15_handler32
11086   pusha
11087 #if BX_USE_PS2_MOUSE
11088   cmp  ah, #0xC2
11089   je int15_handler_mouse
11090 #endif
11091   call _int15_function
11092 int15_handler_mouse_ret:
11093   popa
11094 int15_handler32_ret:
11095   pop   es
11096   pop   ds
11097   popf
11098   jmp iret_modify_cf
11099 #if BX_APM
11100 apm_call:
11101   jmp _apmreal_entry
11102 #endif
11103 
11104 #if BX_USE_PS2_MOUSE
11105 int15_handler_mouse:
11106   call _int15_function_mouse
11107   jmp int15_handler_mouse_ret
11108 #endif
11109 
11110 int15_handler32:
11111   pushad
11112   call _int15_function32
11113   popad
11114   jmp int15_handler32_ret
11115 
11116 ;; Protected mode IDT descriptor
11117 ;;
11118 ;; I just make the limit 0, so the machine will shutdown
11119 ;; if an exception occurs during protected mode memory
11120 ;; transfers.
11121 ;;
11122 ;; Set base to f0000 to correspond to beginning of BIOS,
11123 ;; in case I actually define an IDT later
11124 ;; Set limit to 0
11125 
11126 pmode_IDT_info:
11127 dw 0x0000  ;; limit 15:00
11128 dw 0x0000  ;; base  15:00
11129 db 0x0f    ;; base  23:16
11130 
11131 ;; Real mode IDT descriptor
11132 ;;
11133 ;; Set to typical real-mode values.
11134 ;; base  = 000000
11135 ;; limit =   03ff
11136 
11137 rmode_IDT_info:
11138 dw 0x03ff  ;; limit 15:00
11139 dw 0x0000  ;; base  15:00
11140 db 0x00    ;; base  23:16
11141 
11142 
11143 ;----------
11144 ;- INT1Ah -
11145 ;----------
11146 .org 0xfe6e ; INT 1Ah Time-of-day Service Entry Point
11147 int1a_handler:
11148 #if BX_PCIBIOS
11149   cmp  ah, #0xb1
11150   jne  int1a_normal
11151   call pcibios_real
11152   jc   pcibios_error
11153   retf 2
11154 pcibios_error:
11155   mov  bl, ah
11156   mov  ah, #0xb1
11157   push ds
11158   pusha
11159   mov ax, ss  ; set readable descriptor to ds, for calling pcibios
11160   mov ds, ax  ;  on 16bit protected mode.
11161   jmp int1a_callfunction
11162 int1a_normal:
11163 #endif
11164   push ds
11165   pusha
11166   xor  ax, ax
11167   mov  ds, ax
11168 int1a_callfunction:
11169   call _int1a_function
11170   popa
11171   pop  ds
11172   iret
11173 
11174 ;;
11175 ;; int70h: IRQ8 - CMOS RTC
11176 ;;
11177 int70_handler:
11178   push ds
11179   pushad
11180   xor  ax, ax
11181   mov  ds, ax
11182   call _int70_function
11183   popad
11184   pop  ds
11185   iret
11186 
11187 ;---------
11188 ;- INT08 -
11189 ;---------
11190 .org 0xfea5 ; INT 08h System Timer ISR Entry Point
11191 int08_handler:
11192   sti
11193   push eax
11194   push ds
11195   xor ax, ax
11196   mov ds, ax
11197 
11198   ;; time to turn off drive(s)?
11199   mov  al,0x0440
11200   or   al,al
11201   jz   int08_floppy_off
11202   dec  al
11203   mov  0x0440,al
11204   jnz  int08_floppy_off
11205   ;; turn motor(s) off
11206   push dx
11207   mov  dx,#0x03f2
11208   in   al,dx
11209   and  al,#0xcf
11210   out  dx,al
11211   pop  dx
11212 int08_floppy_off:
11213 
11214   mov eax, 0x046c ;; get ticks dword
11215   inc eax
11216 
11217   ;; compare eax to one days worth of timer ticks at 18.2 hz
11218   cmp eax, #0x001800B0
11219   jb  int08_store_ticks
11220   ;; there has been a midnight rollover at this point
11221   xor eax, eax    ;; zero out counter
11222   inc BYTE 0x0470 ;; increment rollover flag
11223 
11224 int08_store_ticks:
11225   mov 0x046c, eax ;; store new ticks dword
11226   ;; chain to user timer tick INT #0x1c
11227   //pushf
11228   //;; call_ep [ds:loc]
11229   //CALL_EP( 0x1c << 2 )
11230   int #0x1c
11231   cli
11232   call eoi_master_pic
11233   pop ds
11234   pop eax
11235   iret
11236 
11237 .org 0xfef3 ; Initial Interrupt Vector Offsets Loaded by POST
11238 
11239 
11240 .org 0xff00
11241 .ascii BIOS_COPYRIGHT_STRING
11242 
11243 ;------------------------------------------------
11244 ;- IRET Instruction for Dummy Interrupt Handler -
11245 ;------------------------------------------------
11246 .org 0xff53 ; IRET Instruction for Dummy Interrupt Handler
11247 dummy_iret_handler:
11248   iret
11249 
11250 .org 0xff54 ; INT 05h Print Screen Service Entry Point
11251   HALT(__LINE__)
11252   iret
11253 
11254 .org 0xfff0 ; Power-up Entry Point
11255   jmp 0xf000:post
11256 
11257 .org 0xfff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
11258 .ascii BIOS_BUILD_DATE
11259 
11260 .org 0xfffe ; System Model ID
11261 db SYS_MODEL_ID
11262 db 0x00   ; filler
11263 
11264 .org 0xfa6e ;; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
11265 ASM_END
11266 /*
11267  * This font comes from the fntcol16.zip package (c) by  Joseph Gil
11268  * found at ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip
11269  * This font is public domain
11270  */
11271 static Bit8u vgafont8[128*8]=
11272 {
11273  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
11274  0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e,
11275  0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e,
11276  0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00,
11277  0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00,
11278  0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c,
11279  0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c,
11280  0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00,
11281  0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff,
11282  0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
11283  0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff,
11284  0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78,
11285  0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18,
11286  0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0,
11287  0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0,
11288  0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99,
11289  0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00,
11290  0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00,
11291  0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18,
11292  0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
11293  0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00,
11294  0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78,
11295  0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00,
11296  0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff,
11297  0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00,
11298  0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
11299  0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00,
11300  0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
11301  0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00,
11302  0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00,
11303  0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00,
11304  0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00,
11305  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
11306  0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00,
11307  0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
11308  0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00,
11309  0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00,
11310  0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00,
11311  0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00,
11312  0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
11313  0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00,
11314  0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
11315  0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
11316  0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00,
11317  0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60,
11318  0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
11319  0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00,
11320  0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00,
11321  0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00,
11322  0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00,
11323  0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00,
11324  0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00,
11325  0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00,
11326  0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00,
11327  0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00,
11328  0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00,
11329  0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00,
11330  0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00,
11331  0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00,
11332  0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60,
11333  0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00,
11334  0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00,
11335  0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00,
11336  0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00,
11337  0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00,
11338  0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00,
11339  0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00,
11340  0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00,
11341  0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00,
11342  0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00,
11343  0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00,
11344  0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00,
11345  0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00,
11346  0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
11347  0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00,
11348  0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00,
11349  0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00,
11350  0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00,
11351  0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00,
11352  0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00,
11353  0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
11354  0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00,
11355  0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00,
11356  0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00,
11357  0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
11358  0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00,
11359  0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
11360  0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00,
11361  0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00,
11362  0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00,
11363  0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00,
11364  0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
11365  0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00,
11366  0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
11367  0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
11368  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
11369  0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
11370  0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00,
11371  0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00,
11372  0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00,
11373  0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00,
11374  0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
11375  0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00,
11376  0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
11377  0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00,
11378  0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
11379  0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78,
11380  0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00,
11381  0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
11382  0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00,
11383  0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
11384  0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
11385  0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0,
11386  0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e,
11387  0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00,
11388  0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00,
11389  0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00,
11390  0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
11391  0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
11392  0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00,
11393  0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00,
11394  0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
11395  0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00,
11396  0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00,
11397  0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
11398  0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00,
11399  0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
11400  0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00,
11401 };
11402 
11403 ASM_START
11404 .org 0xcc00
11405 bios_table_area_end:
11406 // bcc-generated data will be placed here
11407 ASM_END
11408