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