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