Remove devfn from BlockDriverState
[qemu-kvm/fedora.git] / kvm / bios / rombios.c
blob618619978912ce405e27f99c8ad0f66fc0844341
1 /////////////////////////////////////////////////////////////////////////
2 // $Id: rombios.c,v 1.182 2007/08/01 17:09:51 vruppert Exp $
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/
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.
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.
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
27 // ROM BIOS for use with Bochs/Plex86/QEMU emulation environment
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
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
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
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
97 // TODO :
99 // int74
100 // - needs to be reworked. Uses direct [bp] offsets. (?)
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
107 // NOTES:
108 // - NMI access (bit7 of addr written to 70h)
110 // ATA driver
111 // - should handle the "don't detect" bit (cmos regs 0x3b & 0x3c)
112 // - could send the multiple-sector read/write commands
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
123 // BCC Bug: find a generic way to handle the bug of #asm after an "if" (fixed in 0.16.7)
125 #include "rombios.h"
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
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
147 #define BX_USE_ATADRV 1
148 #define BX_ELTORITO_BOOT 1
150 #define BX_MAX_ATA_INTERFACES 4
151 #define BX_MAX_ATA_DEVICES (BX_MAX_ATA_INTERFACES*2)
153 #define BX_VIRTUAL_PORTS 1 /* normal output to Bochs ports */
154 #define BX_DEBUG_SERIAL 0 /* output to COM1 */
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
162 #ifndef BIOS_BUILD_DATE
163 # define BIOS_BUILD_DATE "06/23/99"
164 #endif
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)
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
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
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
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
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
221 // This is for compiling with gcc2 and gcc3
222 #define ASM_START #asm
223 #define ASM_END #endasm
225 ASM_START
226 .rom
228 .org 0x0000
230 #if BX_CPU >= 3
231 use16 386
232 #else
233 use16 286
234 #endif
236 MACRO HALT
237 ;; the HALT macro is called with the line number of the HALT call.
238 ;; The line number is then sent to the PANIC_PORT, causing Bochs/Plex
239 ;; to print a BX_PANIC message. This will normally halt the simulation
240 ;; with a message such as "BIOS panic at rombios.c, line 4091".
241 ;; However, users can choose to make panics non-fatal and continue.
242 #if BX_VIRTUAL_PORTS
243 mov dx,#PANIC_PORT
244 mov ax,#?1
245 out dx,ax
246 #else
247 mov dx,#0x80
248 mov ax,#?1
249 out dx,al
250 #endif
251 MEND
253 MACRO JMP_AP
254 db 0xea
255 dw ?2
256 dw ?1
257 MEND
259 MACRO SET_INT_VECTOR
260 mov ax, ?3
261 mov ?1*4, ax
262 mov ax, ?2
263 mov ?1*4+2, ax
264 MEND
266 ASM_END
268 typedef unsigned char Bit8u;
269 typedef unsigned short Bit16u;
270 typedef unsigned short bx_bool;
271 typedef unsigned long Bit32u;
274 void memsetb(seg,offset,value,count);
275 void memcpyb(dseg,doffset,sseg,soffset,count);
276 void memcpyd(dseg,doffset,sseg,soffset,count);
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;
286 ASM_START
287 push bp
288 mov bp, sp
290 push ax
291 push cx
292 push es
293 push di
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
305 stosb
307 memsetb_end:
308 pop di
309 pop es
310 pop cx
311 pop ax
313 pop bp
314 ASM_END
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;
326 ASM_START
327 push bp
328 mov bp, sp
330 push ax
331 push cx
332 push es
333 push di
334 push ds
335 push si
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
350 movsb
352 memcpyb_end:
353 pop si
354 pop ds
355 pop di
356 pop es
357 pop cx
358 pop ax
360 pop bp
361 ASM_END
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;
373 ASM_START
374 push bp
375 mov bp, sp
377 push ax
378 push cx
379 push es
380 push di
381 push ds
382 push si
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
397 movsd
399 memcpyd_end:
400 pop si
401 pop ds
402 pop di
403 pop es
404 pop cx
405 pop ax
407 pop bp
408 ASM_END
411 // read_dword and write_dword functions
412 static Bit32u read_dword();
413 static void write_dword();
415 Bit32u
416 read_dword(seg, offset)
417 Bit16u seg;
418 Bit16u offset;
420 ASM_START
421 push bp
422 mov bp, sp
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
437 pop bp
438 ASM_END
441 void
442 write_dword(seg, offset, data)
443 Bit16u seg;
444 Bit16u offset;
445 Bit32u data;
447 ASM_START
448 push bp
449 mov bp, sp
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
466 pop bp
467 ASM_END
470 // Bit32u (unsigned long) and long helper functions
471 ASM_START
473 ;; and function
474 landl:
475 landul:
476 SEG SS
477 and ax,[di]
478 SEG SS
479 and bx,2[di]
482 ;; add function
483 laddl:
484 laddul:
485 SEG SS
486 add ax,[di]
487 SEG SS
488 adc bx,2[di]
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]
502 ;; sub function
503 lsubl:
504 lsubul:
505 SEG SS
506 sub ax,[di]
507 SEG SS
508 sbb bx,2[di]
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
523 ;; dec function
524 ldecl:
525 ldecul:
526 SEG SS
527 dec dword ptr [bx]
530 ;; or function
531 lorl:
532 lorul:
533 SEG SS
534 or ax,[di]
535 SEG SS
536 or bx,2[di]
539 ;; inc function
540 lincl:
541 lincul:
542 SEG SS
543 inc dword ptr [bx]
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
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:
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:
587 idiv_:
589 idiv bx
592 idiv_u:
593 xor dx,dx
594 div bx
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
612 ASM_END
614 // for access to RAM area which is used by interrupt vectors
615 // and BIOS Data Area
617 typedef struct {
618 unsigned char filler1[0x400];
619 unsigned char filler2[0x6c];
620 Bit16u ticks_low;
621 Bit16u ticks_high;
622 Bit8u midnight_flag;
623 } bios_data_t;
625 #define BiosData ((bios_data_t *) 0)
627 #if BX_USE_ATADRV
628 typedef struct {
629 Bit16u heads; // # heads
630 Bit16u cylinders; // # cylinders
631 Bit16u spt; // # sectors / track
632 } chs_t;
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;
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;
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
665 Bit8u translation; // type of translation
666 chs_t lchs; // Logical CHS
667 chs_t pchs; // Physical CHS
669 Bit32u sectors_low; // Total sectors count
670 Bit32u sectors_high;
671 } ata_device_t;
673 typedef struct {
674 // ATA channels info
675 ata_channel_t channels[BX_MAX_ATA_INTERFACES];
677 // ATA devices info
678 ata_device_t devices[BX_MAX_ATA_DEVICES];
680 // map between (bios hd id - 0x80) and ata channels
681 Bit8u hdcount, hdidmap[BX_MAX_ATA_DEVICES];
683 // map between (bios cd id - 0xE0) and ata channels
684 Bit8u cdcount, cdidmap[BX_MAX_ATA_DEVICES];
686 // Buffer for DPTE table
687 dpte_t dpte;
689 // Count of transferred sectors and bytes
690 Bit16u trsfsectors;
691 Bit32u trsfbytes;
693 } ata_t;
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;
708 // Virtual device
709 chs_t vdevice;
710 } cdemu_t;
711 #endif // BX_ELTORITO_BOOT
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];
722 // FDPT - Can be splitted in data members if needed
723 unsigned char fdpt0[0x10];
724 unsigned char fdpt1[0x10];
726 unsigned char filler2[0xC4];
728 // ATA Driver data
729 ata_t ata;
731 #if BX_ELTORITO_BOOT
732 // El Torito Emulation data
733 cdemu_t cdemu;
734 #endif // BX_ELTORITO_BOOT
736 } ebda_data_t;
738 #define EbdaData ((ebda_data_t *) 0)
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;
751 #define Int13Ext ((int13ext_t *) 0)
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;
777 #define Int13DPT ((dpt_t *) 0)
779 #endif // BX_USE_ATADRV
781 typedef struct {
782 union {
783 struct {
784 Bit16u di, si, bp, sp;
785 Bit16u bx, dx, cx, ax;
786 } r16;
787 struct {
788 Bit16u filler[4];
789 Bit8u bl, bh, dl, dh, cl, ch, al, ah;
790 } r8;
791 } u;
792 } pusha_regs_t;
794 typedef struct {
795 union {
796 struct {
797 Bit32u edi, esi, ebp, esp;
798 Bit32u ebx, edx, ecx, eax;
799 } r32;
800 struct {
801 Bit16u di, filler1, si, filler2, bp, filler3, sp, filler4;
802 Bit16u bx, filler5, dx, filler6, cx, filler7, ax, filler8;
803 } r16;
804 struct {
805 Bit32u filler[4];
806 Bit8u bl, bh;
807 Bit16u filler1;
808 Bit8u dl, dh;
809 Bit16u filler2;
810 Bit8u cl, ch;
811 Bit16u filler3;
812 Bit8u al, ah;
813 Bit16u filler4;
814 } r8;
815 } u;
816 } pushad_regs_t;
818 typedef struct {
819 union {
820 struct {
821 Bit16u flags;
822 } r16;
823 struct {
824 Bit8u flagsl;
825 Bit8u flagsh;
826 } r8;
827 } u;
828 } flags_t;
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)
836 typedef struct {
837 Bit16u ip;
838 Bit16u cs;
839 flags_t flags;
840 } iret_addr_t;
842 typedef struct {
843 Bit16u type;
844 Bit16u flags;
845 Bit32u vector;
846 Bit32u description;
847 Bit32u reserved;
848 } ipl_entry_t;
852 static Bit8u inb();
853 static Bit8u inb_cmos();
854 static void outb();
855 static void outb_cmos();
856 static Bit16u inw();
857 static void outw();
858 static void init_rtc();
859 static bx_bool rtc_updating();
861 static Bit8u read_byte();
862 static Bit16u read_word();
863 static void write_byte();
864 static void write_word();
865 static void bios_printf();
867 static Bit8u inhibit_mouse_int_and_events();
868 static void enable_mouse_int_and_events();
869 static Bit8u send_to_mouse_ctrl();
870 static Bit8u get_mouse_data();
871 static void set_kbd_command_byte();
873 static void int09_function();
874 static void int13_harddisk();
875 static void int13_cdrom();
876 static void int13_cdemu();
877 static void int13_eltorito();
878 static void int13_diskette_function();
879 static void int14_function();
880 static void int15_function();
881 static void int16_function();
882 static void int17_function();
883 static void int19_function();
884 static void int1a_function();
885 static void int70_function();
886 static void int74_function();
887 static Bit16u get_CS();
888 static Bit16u get_SS();
889 static unsigned int enqueue_key();
890 static unsigned int dequeue_key();
891 static void get_hd_geometry();
892 static void set_diskette_ret_status();
893 static void set_diskette_current_cyl();
894 static void determine_floppy_media();
895 static bx_bool floppy_drive_exists();
896 static bx_bool floppy_drive_recal();
897 static bx_bool floppy_media_known();
898 static bx_bool floppy_media_sense();
899 static bx_bool set_enable_a20();
900 static void debugger_on();
901 static void debugger_off();
902 static void keyboard_init();
903 static void keyboard_panic();
904 static void shutdown_status_panic();
905 static void nmi_handler_msg();
906 static void delay_ticks();
907 static void delay_ticks_and_check_for_keystroke();
909 static void interactive_bootkey();
910 static void print_bios_banner();
911 static void print_boot_device();
912 static void print_boot_failure();
913 static void print_cdromboot_failure();
915 # if BX_USE_ATADRV
917 // ATA / ATAPI driver
918 void ata_init();
919 void ata_detect();
920 void ata_reset();
922 Bit16u ata_cmd_non_data();
923 Bit16u ata_cmd_data_in();
924 Bit16u ata_cmd_data_out();
925 Bit16u ata_cmd_packet();
927 Bit16u atapi_get_sense();
928 Bit16u atapi_is_ready();
929 Bit16u atapi_is_cdrom();
931 #endif // BX_USE_ATADRV
933 #if BX_ELTORITO_BOOT
935 void cdemu_init();
936 Bit8u cdemu_isactive();
937 Bit8u cdemu_emulated_drive();
939 Bit16u cdrom_boot();
941 #endif // BX_ELTORITO_BOOT
943 static char bios_cvs_version_string[] = "$Revision: 1.182 $ $Date: 2007/08/01 17:09:51 $";
945 #define BIOS_COPYRIGHT_STRING "(c) 2002 MandrakeSoft S.A. Written by Kevin Lawton & the Bochs team."
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
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))
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 )
1011 #define GET_ELDL() ( ELDX & 0x00ff )
1012 #define GET_ELDH() ( ELDX >> 8 )
1014 #define SET_CF() FLAGS |= 0x0001
1015 #define CLEAR_CF() FLAGS &= 0xfffe
1016 #define GET_CF() (FLAGS & 0x0001)
1018 #define SET_ZF() FLAGS |= 0x0040
1019 #define CLEAR_ZF() FLAGS &= 0xffbf
1020 #define GET_ZF() (FLAGS & 0x0040)
1022 #define UNSUPPORTED_FUNCTION 0x86
1024 #define none 0
1025 #define MAX_SCAN_CODE 0x58
1027 static struct {
1028 Bit16u normal;
1029 Bit16u shift;
1030 Bit16u control;
1031 Bit16u alt;
1032 Bit8u lock_flags;
1033 } scan_to_scanascii[MAX_SCAN_CODE + 1] = {
1034 { none, none, none, none, none },
1035 { 0x011b, 0x011b, 0x011b, 0x0100, none }, /* escape */
1036 { 0x0231, 0x0221, none, 0x7800, none }, /* 1! */
1037 { 0x0332, 0x0340, 0x0300, 0x7900, none }, /* 2@ */
1038 { 0x0433, 0x0423, none, 0x7a00, none }, /* 3# */
1039 { 0x0534, 0x0524, none, 0x7b00, none }, /* 4$ */
1040 { 0x0635, 0x0625, none, 0x7c00, none }, /* 5% */
1041 { 0x0736, 0x075e, 0x071e, 0x7d00, none }, /* 6^ */
1042 { 0x0837, 0x0826, none, 0x7e00, none }, /* 7& */
1043 { 0x0938, 0x092a, none, 0x7f00, none }, /* 8* */
1044 { 0x0a39, 0x0a28, none, 0x8000, none }, /* 9( */
1045 { 0x0b30, 0x0b29, none, 0x8100, none }, /* 0) */
1046 { 0x0c2d, 0x0c5f, 0x0c1f, 0x8200, none }, /* -_ */
1047 { 0x0d3d, 0x0d2b, none, 0x8300, none }, /* =+ */
1048 { 0x0e08, 0x0e08, 0x0e7f, none, none }, /* backspace */
1049 { 0x0f09, 0x0f00, none, none, none }, /* tab */
1050 { 0x1071, 0x1051, 0x1011, 0x1000, 0x40 }, /* Q */
1051 { 0x1177, 0x1157, 0x1117, 0x1100, 0x40 }, /* W */
1052 { 0x1265, 0x1245, 0x1205, 0x1200, 0x40 }, /* E */
1053 { 0x1372, 0x1352, 0x1312, 0x1300, 0x40 }, /* R */
1054 { 0x1474, 0x1454, 0x1414, 0x1400, 0x40 }, /* T */
1055 { 0x1579, 0x1559, 0x1519, 0x1500, 0x40 }, /* Y */
1056 { 0x1675, 0x1655, 0x1615, 0x1600, 0x40 }, /* U */
1057 { 0x1769, 0x1749, 0x1709, 0x1700, 0x40 }, /* I */
1058 { 0x186f, 0x184f, 0x180f, 0x1800, 0x40 }, /* O */
1059 { 0x1970, 0x1950, 0x1910, 0x1900, 0x40 }, /* P */
1060 { 0x1a5b, 0x1a7b, 0x1a1b, none, none }, /* [{ */
1061 { 0x1b5d, 0x1b7d, 0x1b1d, none, none }, /* ]} */
1062 { 0x1c0d, 0x1c0d, 0x1c0a, none, none }, /* Enter */
1063 { none, none, none, none, none }, /* L Ctrl */
1064 { 0x1e61, 0x1e41, 0x1e01, 0x1e00, 0x40 }, /* A */
1065 { 0x1f73, 0x1f53, 0x1f13, 0x1f00, 0x40 }, /* S */
1066 { 0x2064, 0x2044, 0x2004, 0x2000, 0x40 }, /* D */
1067 { 0x2166, 0x2146, 0x2106, 0x2100, 0x40 }, /* F */
1068 { 0x2267, 0x2247, 0x2207, 0x2200, 0x40 }, /* G */
1069 { 0x2368, 0x2348, 0x2308, 0x2300, 0x40 }, /* H */
1070 { 0x246a, 0x244a, 0x240a, 0x2400, 0x40 }, /* J */
1071 { 0x256b, 0x254b, 0x250b, 0x2500, 0x40 }, /* K */
1072 { 0x266c, 0x264c, 0x260c, 0x2600, 0x40 }, /* L */
1073 { 0x273b, 0x273a, none, none, none }, /* ;: */
1074 { 0x2827, 0x2822, none, none, none }, /* '" */
1075 { 0x2960, 0x297e, none, none, none }, /* `~ */
1076 { none, none, none, none, none }, /* L shift */
1077 { 0x2b5c, 0x2b7c, 0x2b1c, none, none }, /* |\ */
1078 { 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00, 0x40 }, /* Z */
1079 { 0x2d78, 0x2d58, 0x2d18, 0x2d00, 0x40 }, /* X */
1080 { 0x2e63, 0x2e43, 0x2e03, 0x2e00, 0x40 }, /* C */
1081 { 0x2f76, 0x2f56, 0x2f16, 0x2f00, 0x40 }, /* V */
1082 { 0x3062, 0x3042, 0x3002, 0x3000, 0x40 }, /* B */
1083 { 0x316e, 0x314e, 0x310e, 0x3100, 0x40 }, /* N */
1084 { 0x326d, 0x324d, 0x320d, 0x3200, 0x40 }, /* M */
1085 { 0x332c, 0x333c, none, none, none }, /* ,< */
1086 { 0x342e, 0x343e, none, none, none }, /* .> */
1087 { 0x352f, 0x353f, none, none, none }, /* /? */
1088 { none, none, none, none, none }, /* R Shift */
1089 { 0x372a, 0x372a, none, none, none }, /* * */
1090 { none, none, none, none, none }, /* L Alt */
1091 { 0x3920, 0x3920, 0x3920, 0x3920, none }, /* space */
1092 { none, none, none, none, none }, /* caps lock */
1093 { 0x3b00, 0x5400, 0x5e00, 0x6800, none }, /* F1 */
1094 { 0x3c00, 0x5500, 0x5f00, 0x6900, none }, /* F2 */
1095 { 0x3d00, 0x5600, 0x6000, 0x6a00, none }, /* F3 */
1096 { 0x3e00, 0x5700, 0x6100, 0x6b00, none }, /* F4 */
1097 { 0x3f00, 0x5800, 0x6200, 0x6c00, none }, /* F5 */
1098 { 0x4000, 0x5900, 0x6300, 0x6d00, none }, /* F6 */
1099 { 0x4100, 0x5a00, 0x6400, 0x6e00, none }, /* F7 */
1100 { 0x4200, 0x5b00, 0x6500, 0x6f00, none }, /* F8 */
1101 { 0x4300, 0x5c00, 0x6600, 0x7000, none }, /* F9 */
1102 { 0x4400, 0x5d00, 0x6700, 0x7100, none }, /* F10 */
1103 { none, none, none, none, none }, /* Num Lock */
1104 { none, none, none, none, none }, /* Scroll Lock */
1105 { 0x4700, 0x4737, 0x7700, none, 0x20 }, /* 7 Home */
1106 { 0x4800, 0x4838, none, none, 0x20 }, /* 8 UP */
1107 { 0x4900, 0x4939, 0x8400, none, 0x20 }, /* 9 PgUp */
1108 { 0x4a2d, 0x4a2d, none, none, none }, /* - */
1109 { 0x4b00, 0x4b34, 0x7300, none, 0x20 }, /* 4 Left */
1110 { 0x4c00, 0x4c35, none, none, 0x20 }, /* 5 */
1111 { 0x4d00, 0x4d36, 0x7400, none, 0x20 }, /* 6 Right */
1112 { 0x4e2b, 0x4e2b, none, none, none }, /* + */
1113 { 0x4f00, 0x4f31, 0x7500, none, 0x20 }, /* 1 End */
1114 { 0x5000, 0x5032, none, none, 0x20 }, /* 2 Down */
1115 { 0x5100, 0x5133, 0x7600, none, 0x20 }, /* 3 PgDn */
1116 { 0x5200, 0x5230, none, none, 0x20 }, /* 0 Ins */
1117 { 0x5300, 0x532e, none, none, 0x20 }, /* Del */
1118 { none, none, none, none, none },
1119 { none, none, none, none, none },
1120 { 0x565c, 0x567c, none, none, none }, /* \| */
1121 { 0x8500, 0x8700, 0x8900, 0x8b00, none }, /* F11 */
1122 { 0x8600, 0x8800, 0x8a00, 0x8c00, none }, /* F12 */
1125 Bit8u
1126 inb(port)
1127 Bit16u port;
1129 ASM_START
1130 push bp
1131 mov bp, sp
1133 push dx
1134 mov dx, 4[bp]
1135 in al, dx
1136 pop dx
1138 pop bp
1139 ASM_END
1142 #if BX_USE_ATADRV
1143 Bit16u
1144 inw(port)
1145 Bit16u port;
1147 ASM_START
1148 push bp
1149 mov bp, sp
1151 push dx
1152 mov dx, 4[bp]
1153 in ax, dx
1154 pop dx
1156 pop bp
1157 ASM_END
1159 #endif
1161 void
1162 outb(port, val)
1163 Bit16u port;
1164 Bit8u val;
1166 ASM_START
1167 push bp
1168 mov bp, sp
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
1178 pop bp
1179 ASM_END
1182 #if BX_USE_ATADRV
1183 void
1184 outw(port, val)
1185 Bit16u port;
1186 Bit16u val;
1188 ASM_START
1189 push bp
1190 mov bp, sp
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
1200 pop bp
1201 ASM_END
1203 #endif
1205 void
1206 outb_cmos(cmos_reg, val)
1207 Bit8u cmos_reg;
1208 Bit8u val;
1210 ASM_START
1211 push bp
1212 mov bp, sp
1214 mov al, 4[bp] ;; cmos_reg
1215 out 0x70, al
1216 mov al, 6[bp] ;; val
1217 out 0x71, al
1219 pop bp
1220 ASM_END
1223 Bit8u
1224 inb_cmos(cmos_reg)
1225 Bit8u cmos_reg;
1227 ASM_START
1228 push bp
1229 mov bp, sp
1231 mov al, 4[bp] ;; cmos_reg
1232 out 0x70, al
1233 in al, 0x71
1235 pop bp
1236 ASM_END
1239 void
1240 init_rtc()
1242 outb_cmos(0x0a, 0x26);
1243 outb_cmos(0x0b, 0x02);
1244 inb_cmos(0x0c);
1245 inb_cmos(0x0d);
1248 bx_bool
1249 rtc_updating()
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.
1260 Bit16u count;
1262 count = 25000;
1263 while (--count != 0) {
1264 if ( (inb_cmos(0x0a) & 0x80) == 0 )
1265 return(0);
1267 return(1); // update-in-progress never transitioned to 0
1271 Bit8u
1272 read_byte(seg, offset)
1273 Bit16u seg;
1274 Bit16u offset;
1276 ASM_START
1277 push bp
1278 mov bp, sp
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
1290 pop bp
1291 ASM_END
1294 Bit16u
1295 read_word(seg, offset)
1296 Bit16u seg;
1297 Bit16u offset;
1299 ASM_START
1300 push bp
1301 mov bp, sp
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
1313 pop bp
1314 ASM_END
1317 void
1318 write_byte(seg, offset, data)
1319 Bit16u seg;
1320 Bit16u offset;
1321 Bit8u data;
1323 ASM_START
1324 push bp
1325 mov bp, sp
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
1339 pop bp
1340 ASM_END
1343 void
1344 write_word(seg, offset, data)
1345 Bit16u seg;
1346 Bit16u offset;
1347 Bit16u data;
1349 ASM_START
1350 push bp
1351 mov bp, sp
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
1365 pop bp
1366 ASM_END
1369 Bit16u
1370 get_CS()
1372 ASM_START
1373 mov ax, cs
1374 ASM_END
1377 Bit16u
1378 get_SS()
1380 ASM_START
1381 mov ax, ss
1382 ASM_END
1385 #if BX_DEBUG_SERIAL
1386 /* serial debug port*/
1387 #define BX_DEBUG_PORT 0x03f8
1389 /* data */
1390 #define UART_RBR 0x00
1391 #define UART_THR 0x00
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
1402 /* status */
1403 #define UART_LSR 0x05
1404 #define UART_MSR 0x06
1405 #define UART_SCR 0x07
1407 int uart_can_tx_byte(base_port)
1408 Bit16u base_port;
1410 return inb(base_port + UART_LSR) & 0x20;
1413 void uart_wait_to_tx_byte(base_port)
1414 Bit16u base_port;
1416 while (!uart_can_tx_byte(base_port));
1419 void uart_wait_until_sent(base_port)
1420 Bit16u base_port;
1422 while (!(inb(base_port + UART_LSR) & 0x40));
1425 void uart_tx_byte(base_port, data)
1426 Bit16u base_port;
1427 Bit8u data;
1429 uart_wait_to_tx_byte(base_port);
1430 outb(base_port + UART_THR, data);
1431 uart_wait_until_sent(base_port);
1433 #endif
1435 void
1436 wrch(c)
1437 Bit8u c;
1439 ASM_START
1440 push bp
1441 mov bp, sp
1443 push bx
1444 mov ah, #0x0e
1445 mov al, 4[bp]
1446 xor bx,bx
1447 int #0x10
1448 pop bx
1450 pop bp
1451 ASM_END
1454 void
1455 send(action, c)
1456 Bit16u action;
1457 Bit8u c;
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);
1473 void
1474 put_int(action, val, width, neg)
1475 Bit16u action;
1476 short val, width;
1477 bx_bool neg;
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, '-');
1486 send(action, val - (nval * 10) + '0');
1489 void
1490 put_uint(action, val, width, neg)
1491 Bit16u action;
1492 unsigned short val;
1493 short width;
1494 bx_bool neg;
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, '-');
1503 send(action, val - (nval * 10) + '0');
1506 void
1507 put_luint(action, val, width, neg)
1508 Bit16u action;
1509 unsigned long val;
1510 short width;
1511 bx_bool neg;
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, '-');
1520 send(action, val - (nval * 10) + '0');
1523 void put_str(action, segment, offset)
1524 Bit16u action;
1525 Bit16u segment;
1526 Bit16u offset;
1528 Bit8u c;
1530 while (c = read_byte(segment, offset)) {
1531 send(action, c);
1532 offset++;
1536 void
1537 delay_ticks(ticks)
1538 Bit16u ticks;
1540 long ticks_to_wait, delta;
1541 Bit32u prev_ticks, t;
1544 * The 0:046c wraps around at 'midnight' according to a 18.2Hz clock.
1545 * We also have to be careful about interrupt storms.
1547 ASM_START
1548 pushf
1550 ASM_END
1551 ticks_to_wait = ticks;
1552 prev_ticks = read_dword(0x0, 0x46c);
1555 ASM_START
1557 ASM_END
1558 t = read_dword(0x0, 0x46c);
1559 if (t > prev_ticks)
1561 delta = t - prev_ticks; /* The temp var is required or bcc screws up. */
1562 ticks_to_wait -= delta;
1564 else if (t < prev_ticks)
1566 ticks_to_wait -= t; /* wrapped */
1569 prev_ticks = t;
1570 } while (ticks_to_wait > 0);
1571 ASM_START
1573 popf
1574 ASM_END
1577 Bit8u
1578 check_for_keystroke()
1580 ASM_START
1581 mov ax, #0x100
1582 int #0x16
1583 jz no_key
1584 mov al, #1
1585 jmp done
1586 no_key:
1587 xor al, al
1588 done:
1589 ASM_END
1592 Bit8u
1593 get_keystroke()
1595 ASM_START
1596 mov ax, #0x0
1597 int #0x16
1598 xchg ah, al
1599 ASM_END
1602 void
1603 delay_ticks_and_check_for_keystroke(ticks, count)
1604 Bit16u ticks, count;
1606 Bit16u i;
1607 for (i = 1; i <= count; i++) {
1608 delay_ticks(ticks);
1609 if (check_for_keystroke())
1610 break;
1614 //--------------------------------------------------------------------------
1615 // bios_printf()
1616 // A compact variable argument printf function.
1618 // Supports %[format_width][length]format
1619 // where format can be x,X,u,d,s,S,c
1620 // and the optional length modifier is l (ell)
1621 //--------------------------------------------------------------------------
1622 void
1623 bios_printf(action, s)
1624 Bit16u action;
1625 Bit8u *s;
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;
1633 arg_ptr = &s;
1634 arg_seg = get_SS();
1636 in_format = 0;
1637 format_width = 0;
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: ");
1646 while (c = read_byte(get_CS(), s)) {
1647 if ( c == '%' ) {
1648 in_format = 1;
1649 format_width = 0;
1651 else if (in_format) {
1652 if ( (c>='0') && (c<='9') ) {
1653 format_width = (format_width * 10) + (c - '0');
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));
1670 else if (c == 'u') {
1671 put_uint(action, arg, format_width, 0);
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);
1684 else if (c == 'u') {
1685 put_luint(action, ((Bit32u) hibyte << 16) | arg, format_width, 0);
1687 else if (c == 'x' || c == 'X')
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));
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);
1707 else if (c == 's') {
1708 put_str(action, get_CS(), arg);
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);
1716 else if (c == 'c') {
1717 send(action, arg);
1719 else
1720 BX_PANIC("bios_printf: unknown format\n");
1721 in_format = 0;
1724 else {
1725 send(action, c);
1727 s ++;
1730 if (action & BIOS_PRINTF_HALT) {
1731 // freeze in a busy loop.
1732 ASM_START
1734 halt2_loop:
1736 jmp halt2_loop
1737 ASM_END
1741 //--------------------------------------------------------------------------
1742 // keyboard_init
1743 //--------------------------------------------------------------------------
1744 // this file is based on LinuxBIOS implementation of keyboard.c
1745 // could convert to #asm to gain space
1746 void
1747 keyboard_init()
1749 Bit16u max;
1751 /* ------------------- Flush buffers ------------------------*/
1752 /* Wait until buffer is empty */
1753 max=0xffff;
1754 while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
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;
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
1771 /* ------------------- controller side ----------------------*/
1772 /* send cmd = 0xAA, self test 8042 */
1773 outb(0x64, 0xaa);
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);
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);
1785 /* read self-test result, 0x55 should be returned from 0x60 */
1786 if ((inb(0x60) != 0x55)){
1787 keyboard_panic(991);
1790 /* send cmd = 0xAB, keyboard interface test */
1791 outb(0x64,0xab);
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);
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);
1803 /* read keyboard interface test result, */
1804 /* 0x00 should be returned form 0x60 */
1805 if ((inb(0x60) != 0x00)) {
1806 keyboard_panic(992);
1809 /* Enable Keyboard clock */
1810 outb(0x64,0xae);
1811 outb(0x64,0xa8);
1813 /* ------------------- keyboard side ------------------------*/
1814 /* reset kerboard and self test (keyboard side) */
1815 outb(0x60, 0xff);
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);
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);
1827 /* keyboard should return ACK */
1828 if ((inb(0x60) != 0xfa)) {
1829 keyboard_panic(993);
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);
1837 if ((inb(0x60) != 0xaa)) {
1838 keyboard_panic(994);
1841 /* Disable keyboard */
1842 outb(0x60, 0xf5);
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);
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);
1854 /* keyboard should return ACK */
1855 if ((inb(0x60) != 0xfa)) {
1856 keyboard_panic(995);
1859 /* Write Keyboard Mode */
1860 outb(0x64, 0x60);
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);
1867 /* send cmd: scan code convert, disable mouse, enable IRQ 1 */
1868 outb(0x60, 0x61);
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);
1875 /* Enable keyboard */
1876 outb(0x60, 0xf4);
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);
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);
1888 /* keyboard should return ACK */
1889 if ((inb(0x60) != 0xfa)) {
1890 keyboard_panic(996);
1893 outb(0x80, 0x77);
1896 //--------------------------------------------------------------------------
1897 // keyboard_panic
1898 //--------------------------------------------------------------------------
1899 void
1900 keyboard_panic(status)
1901 Bit16u status;
1903 // If you're getting a 993 keyboard panic here,
1904 // please see the comment in keyboard_init
1906 BX_PANIC("Keyboard error:%u\n",status);
1909 //--------------------------------------------------------------------------
1910 // shutdown_status_panic
1911 // called when the shutdown statsu is not implemented, displays the status
1912 //--------------------------------------------------------------------------
1913 void
1914 shutdown_status_panic(status)
1915 Bit16u status;
1917 BX_PANIC("Unimplemented shutdown status: %02x\n",(Bit8u)status);
1920 void s3_resume_panic()
1922 BX_PANIC("Returned from s3_resume.\n");
1925 //--------------------------------------------------------------------------
1926 // print_bios_banner
1927 // displays a the bios version
1928 //--------------------------------------------------------------------------
1929 void
1930 print_bios_banner()
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");
1950 //--------------------------------------------------------------------------
1951 // BIOS Boot Specification 1.0.1 compatibility
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.
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).
1962 // http://www.phoenix.com/en/Customer+Services/White+Papers-Specs/pc+industry+specifications.htm
1963 //--------------------------------------------------------------------------
1965 static char drivetypes[][10]={"", "Floppy","Hard Disk","CD-Rom", "Network"};
1967 static void
1968 init_boot_vectors()
1970 ipl_entry_t e;
1971 Bit16u count = 0;
1972 Bit16u ss = get_SS();
1974 /* Clear out the IPL table. */
1975 memsetb(IPL_SEG, IPL_TABLE_OFFSET, 0, IPL_SIZE);
1977 /* User selected device not set */
1978 write_word(IPL_SEG, IPL_BOOTFIRST_OFFSET, 0xFFFF);
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++;
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++;
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
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);
2003 static Bit8u
2004 get_boot_vector(i, e)
2005 Bit16u i; ipl_entry_t *e;
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;
2017 #if BX_ELTORITO_BOOT
2018 void
2019 interactive_bootkey()
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;
2029 while (check_for_keystroke())
2030 get_keystroke();
2032 printf("Press F12 for boot menu.\n\n");
2034 delay_ticks_and_check_for_keystroke(11, 5); /* ~3 seconds */
2035 if (check_for_keystroke())
2037 scan_code = get_keystroke();
2038 if (scan_code == 0x86) /* F12 */
2040 while (check_for_keystroke())
2041 get_keystroke();
2043 printf("Select boot device:\n\n");
2045 count = read_word(IPL_SEG, IPL_COUNT_OFFSET);
2046 for (i = 0; i < count; i++)
2048 memcpyb(ss, &e, IPL_SEG, IPL_TABLE_OFFSET + i * sizeof (e), sizeof (e));
2049 printf("%d. ", i+1);
2050 switch(e.type)
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)
2061 memcpyb(ss, &description, (Bit16u)(e.description >> 16), (Bit16u)(e.description & 0xffff), 32);
2062 description[32] = 0;
2063 printf(" [%S]", ss, description);
2065 printf("\n");
2066 break;
2070 count++;
2071 while (!valid_choice) {
2072 scan_code = get_keystroke();
2073 if (scan_code == 0x01 || scan_code == 0x58) /* ESC or F12 */
2075 valid_choice = 1;
2077 else if (scan_code <= count)
2079 valid_choice = 1;
2080 scan_code -= 1;
2081 /* Set user selected device */
2082 write_word(IPL_SEG, IPL_BOOTFIRST_OFFSET, scan_code);
2085 printf("\n");
2089 #endif // BX_ELTORITO_BOOT
2091 //--------------------------------------------------------------------------
2092 // print_boot_device
2093 // displays the boot device
2094 //--------------------------------------------------------------------------
2096 void
2097 print_boot_device(e)
2098 ipl_entry_t *e;
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);
2116 printf("...\n");
2119 //--------------------------------------------------------------------------
2120 // print_boot_failure
2121 // displays the reason why boot failed
2122 //--------------------------------------------------------------------------
2123 void
2124 print_boot_failure(type, reason)
2125 Bit16u type; Bit8u reason;
2127 if (type == 0 || type > 0x3) BX_PANIC("Bad drive type\n");
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");
2137 printf("\n\n");
2140 //--------------------------------------------------------------------------
2141 // print_cdromboot_failure
2142 // displays the reason why boot failed
2143 //--------------------------------------------------------------------------
2144 void
2145 print_cdromboot_failure( code )
2146 Bit16u code;
2148 bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, "CDROM boot failure code : %04x\n",code);
2150 return;
2153 void
2154 nmi_handler_msg()
2156 BX_PANIC("NMI Handler called\n");
2159 void
2160 int18_panic_msg()
2162 BX_PANIC("INT18: BOOT FAILURE\n");
2165 void
2166 log_bios_start()
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);
2174 bx_bool
2175 set_enable_a20(val)
2176 bx_bool val;
2178 Bit8u oldval;
2180 // Use PS2 System Control port A to set A20 enable
2182 // get current setting first
2183 oldval = inb(0x92);
2185 // change A20 status
2186 if (val)
2187 outb(0x92, oldval | 0x02);
2188 else
2189 outb(0x92, oldval & 0xfd);
2191 return((oldval & 0x02) != 0);
2194 void
2195 debugger_on()
2197 outb(0xfedc, 0x01);
2200 void
2201 debugger_off()
2203 outb(0xfedc, 0x00);
2207 s3_resume()
2209 Bit32u s3_wakeup_vector;
2210 Bit8u s3_resume_flag;
2212 s3_resume_flag = read_byte(0x40, 0xb0);
2213 s3_wakeup_vector = read_dword(0x40, 0xb2);
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;
2219 write_byte(0x40, 0xb0, 0);
2221 /* setup wakeup vector */
2222 write_word(0x40, 0xb6, (s3_wakeup_vector & 0xF)); /* IP */
2223 write_word(0x40, 0xb8, (s3_wakeup_vector >> 4)); /* CS */
2225 BX_INFO("S3 resume jump to %x:%x\n", (s3_wakeup_vector >> 4),
2226 (s3_wakeup_vector & 0xF));
2227 ASM_START
2228 mov sp, #0 ;; disable tpr patching on boot CPU
2229 jmpf [0x04b6]
2230 ASM_END
2231 return 1;
2234 #if BX_USE_ATADRV
2236 // ---------------------------------------------------------------------------
2237 // Start of ATA/ATAPI Driver
2238 // ---------------------------------------------------------------------------
2240 // Global defines -- ATA register and register bits.
2241 // command block & control block regs
2242 #define ATA_CB_DATA 0 // data reg in/out pio_base_addr1+0
2243 #define ATA_CB_ERR 1 // error in pio_base_addr1+1
2244 #define ATA_CB_FR 1 // feature reg out pio_base_addr1+1
2245 #define ATA_CB_SC 2 // sector count in/out pio_base_addr1+2
2246 #define ATA_CB_SN 3 // sector number in/out pio_base_addr1+3
2247 #define ATA_CB_CL 4 // cylinder low in/out pio_base_addr1+4
2248 #define ATA_CB_CH 5 // cylinder high in/out pio_base_addr1+5
2249 #define ATA_CB_DH 6 // device head in/out pio_base_addr1+6
2250 #define ATA_CB_STAT 7 // primary status in pio_base_addr1+7
2251 #define ATA_CB_CMD 7 // command out pio_base_addr1+7
2252 #define ATA_CB_ASTAT 6 // alternate status in pio_base_addr2+6
2253 #define ATA_CB_DC 6 // device control out pio_base_addr2+6
2254 #define ATA_CB_DA 7 // device address in pio_base_addr2+7
2256 #define ATA_CB_ER_ICRC 0x80 // ATA Ultra DMA bad CRC
2257 #define ATA_CB_ER_BBK 0x80 // ATA bad block
2258 #define ATA_CB_ER_UNC 0x40 // ATA uncorrected error
2259 #define ATA_CB_ER_MC 0x20 // ATA media change
2260 #define ATA_CB_ER_IDNF 0x10 // ATA id not found
2261 #define ATA_CB_ER_MCR 0x08 // ATA media change request
2262 #define ATA_CB_ER_ABRT 0x04 // ATA command aborted
2263 #define ATA_CB_ER_NTK0 0x02 // ATA track 0 not found
2264 #define ATA_CB_ER_NDAM 0x01 // ATA address mark not found
2266 #define ATA_CB_ER_P_SNSKEY 0xf0 // ATAPI sense key (mask)
2267 #define ATA_CB_ER_P_MCR 0x08 // ATAPI Media Change Request
2268 #define ATA_CB_ER_P_ABRT 0x04 // ATAPI command abort
2269 #define ATA_CB_ER_P_EOM 0x02 // ATAPI End of Media
2270 #define ATA_CB_ER_P_ILI 0x01 // ATAPI Illegal Length Indication
2272 // ATAPI Interrupt Reason bits in the Sector Count reg (CB_SC)
2273 #define ATA_CB_SC_P_TAG 0xf8 // ATAPI tag (mask)
2274 #define ATA_CB_SC_P_REL 0x04 // ATAPI release
2275 #define ATA_CB_SC_P_IO 0x02 // ATAPI I/O
2276 #define ATA_CB_SC_P_CD 0x01 // ATAPI C/D
2278 // bits 7-4 of the device/head (CB_DH) reg
2279 #define ATA_CB_DH_DEV0 0xa0 // select device 0
2280 #define ATA_CB_DH_DEV1 0xb0 // select device 1
2281 #define ATA_CB_DH_LBA 0x40 // use LBA
2283 // status reg (CB_STAT and CB_ASTAT) bits
2284 #define ATA_CB_STAT_BSY 0x80 // busy
2285 #define ATA_CB_STAT_RDY 0x40 // ready
2286 #define ATA_CB_STAT_DF 0x20 // device fault
2287 #define ATA_CB_STAT_WFT 0x20 // write fault (old name)
2288 #define ATA_CB_STAT_SKC 0x10 // seek complete
2289 #define ATA_CB_STAT_SERV 0x10 // service
2290 #define ATA_CB_STAT_DRQ 0x08 // data request
2291 #define ATA_CB_STAT_CORR 0x04 // corrected
2292 #define ATA_CB_STAT_IDX 0x02 // index
2293 #define ATA_CB_STAT_ERR 0x01 // error (ATA)
2294 #define ATA_CB_STAT_CHK 0x01 // check (ATAPI)
2296 // device control reg (CB_DC) bits
2297 #define ATA_CB_DC_HD15 0x08 // bit should always be set to one
2298 #define ATA_CB_DC_SRST 0x04 // soft reset
2299 #define ATA_CB_DC_NIEN 0x02 // disable interrupts
2301 // Most mandtory and optional ATA commands (from ATA-3),
2302 #define ATA_CMD_CFA_ERASE_SECTORS 0xC0
2303 #define ATA_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03
2304 #define ATA_CMD_CFA_TRANSLATE_SECTOR 0x87
2305 #define ATA_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD
2306 #define ATA_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38
2307 #define ATA_CMD_CHECK_POWER_MODE1 0xE5
2308 #define ATA_CMD_CHECK_POWER_MODE2 0x98
2309 #define ATA_CMD_DEVICE_RESET 0x08
2310 #define ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90
2311 #define ATA_CMD_FLUSH_CACHE 0xE7
2312 #define ATA_CMD_FORMAT_TRACK 0x50
2313 #define ATA_CMD_IDENTIFY_DEVICE 0xEC
2314 #define ATA_CMD_IDENTIFY_DEVICE_PACKET 0xA1
2315 #define ATA_CMD_IDENTIFY_PACKET_DEVICE 0xA1
2316 #define ATA_CMD_IDLE1 0xE3
2317 #define ATA_CMD_IDLE2 0x97
2318 #define ATA_CMD_IDLE_IMMEDIATE1 0xE1
2319 #define ATA_CMD_IDLE_IMMEDIATE2 0x95
2320 #define ATA_CMD_INITIALIZE_DRIVE_PARAMETERS 0x91
2321 #define ATA_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91
2322 #define ATA_CMD_NOP 0x00
2323 #define ATA_CMD_PACKET 0xA0
2324 #define ATA_CMD_READ_BUFFER 0xE4
2325 #define ATA_CMD_READ_DMA 0xC8
2326 #define ATA_CMD_READ_DMA_QUEUED 0xC7
2327 #define ATA_CMD_READ_MULTIPLE 0xC4
2328 #define ATA_CMD_READ_SECTORS 0x20
2329 #define ATA_CMD_READ_VERIFY_SECTORS 0x40
2330 #define ATA_CMD_RECALIBRATE 0x10
2331 #define ATA_CMD_REQUEST_SENSE 0x03
2332 #define ATA_CMD_SEEK 0x70
2333 #define ATA_CMD_SET_FEATURES 0xEF
2334 #define ATA_CMD_SET_MULTIPLE_MODE 0xC6
2335 #define ATA_CMD_SLEEP1 0xE6
2336 #define ATA_CMD_SLEEP2 0x99
2337 #define ATA_CMD_STANDBY1 0xE2
2338 #define ATA_CMD_STANDBY2 0x96
2339 #define ATA_CMD_STANDBY_IMMEDIATE1 0xE0
2340 #define ATA_CMD_STANDBY_IMMEDIATE2 0x94
2341 #define ATA_CMD_WRITE_BUFFER 0xE8
2342 #define ATA_CMD_WRITE_DMA 0xCA
2343 #define ATA_CMD_WRITE_DMA_QUEUED 0xCC
2344 #define ATA_CMD_WRITE_MULTIPLE 0xC5
2345 #define ATA_CMD_WRITE_SECTORS 0x30
2346 #define ATA_CMD_WRITE_VERIFY 0x3C
2348 #define ATA_IFACE_NONE 0x00
2349 #define ATA_IFACE_ISA 0x00
2350 #define ATA_IFACE_PCI 0x01
2352 #define ATA_TYPE_NONE 0x00
2353 #define ATA_TYPE_UNKNOWN 0x01
2354 #define ATA_TYPE_ATA 0x02
2355 #define ATA_TYPE_ATAPI 0x03
2357 #define ATA_DEVICE_NONE 0x00
2358 #define ATA_DEVICE_HD 0xFF
2359 #define ATA_DEVICE_CDROM 0x05
2361 #define ATA_MODE_NONE 0x00
2362 #define ATA_MODE_PIO16 0x00
2363 #define ATA_MODE_PIO32 0x01
2364 #define ATA_MODE_ISADMA 0x02
2365 #define ATA_MODE_PCIDMA 0x03
2366 #define ATA_MODE_USEIRQ 0x10
2368 #define ATA_TRANSLATION_NONE 0
2369 #define ATA_TRANSLATION_LBA 1
2370 #define ATA_TRANSLATION_LARGE 2
2371 #define ATA_TRANSLATION_RECHS 3
2373 #define ATA_DATA_NO 0x00
2374 #define ATA_DATA_IN 0x01
2375 #define ATA_DATA_OUT 0x02
2377 // ---------------------------------------------------------------------------
2378 // ATA/ATAPI driver : initialization
2379 // ---------------------------------------------------------------------------
2380 void ata_init( )
2382 Bit16u ebda_seg=read_word(0x0040,0x000E);
2383 Bit8u channel, device;
2385 // Channels info init.
2386 for (channel=0; channel<BX_MAX_ATA_INTERFACES; channel++) {
2387 write_byte(ebda_seg,&EbdaData->ata.channels[channel].iface,ATA_IFACE_NONE);
2388 write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1,0x0);
2389 write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2,0x0);
2390 write_byte(ebda_seg,&EbdaData->ata.channels[channel].irq,0);
2393 // Devices info init.
2394 for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2395 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
2396 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_NONE);
2397 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable,0);
2398 write_byte(ebda_seg,&EbdaData->ata.devices[device].lock,0);
2399 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode,ATA_MODE_NONE);
2400 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize,0);
2401 write_byte(ebda_seg,&EbdaData->ata.devices[device].translation,ATA_TRANSLATION_NONE);
2402 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads,0);
2403 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders,0);
2404 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt,0);
2405 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads,0);
2406 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders,0);
2407 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt,0);
2409 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low,0L);
2410 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_high,0L);
2413 // hdidmap and cdidmap init.
2414 for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2415 write_byte(ebda_seg,&EbdaData->ata.hdidmap[device],BX_MAX_ATA_DEVICES);
2416 write_byte(ebda_seg,&EbdaData->ata.cdidmap[device],BX_MAX_ATA_DEVICES);
2419 write_byte(ebda_seg,&EbdaData->ata.hdcount,0);
2420 write_byte(ebda_seg,&EbdaData->ata.cdcount,0);
2423 #define TIMEOUT 0
2424 #define BSY 1
2425 #define NOT_BSY 2
2426 #define NOT_BSY_DRQ 3
2427 #define NOT_BSY_NOT_DRQ 4
2428 #define NOT_BSY_RDY 5
2430 #define IDE_TIMEOUT 32000u //32 seconds max for IDE ops
2432 int await_ide();
2433 static int await_ide(when_done,base,timeout)
2434 Bit8u when_done;
2435 Bit16u base;
2436 Bit16u timeout;
2438 Bit32u time=0,last=0;
2439 Bit16u status;
2440 Bit8u result;
2441 status = inb(base + ATA_CB_STAT); // for the times you're supposed to throw one away
2442 for(;;) {
2443 status = inb(base+ATA_CB_STAT);
2444 time++;
2445 if (when_done == BSY)
2446 result = status & ATA_CB_STAT_BSY;
2447 else if (when_done == NOT_BSY)
2448 result = !(status & ATA_CB_STAT_BSY);
2449 else if (when_done == NOT_BSY_DRQ)
2450 result = !(status & ATA_CB_STAT_BSY) && (status & ATA_CB_STAT_DRQ);
2451 else if (when_done == NOT_BSY_NOT_DRQ)
2452 result = !(status & ATA_CB_STAT_BSY) && !(status & ATA_CB_STAT_DRQ);
2453 else if (when_done == NOT_BSY_RDY)
2454 result = !(status & ATA_CB_STAT_BSY) && (status & ATA_CB_STAT_RDY);
2455 else if (when_done == TIMEOUT)
2456 result = 0;
2458 if (result) return 0;
2459 if (time>>16 != last) // mod 2048 each 16 ms
2461 last = time >>16;
2462 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);
2464 if (status & ATA_CB_STAT_ERR)
2466 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);
2467 return -1;
2469 if ((timeout == 0) || ((time>>11) > timeout)) break;
2471 BX_INFO("IDE time out\n");
2472 return -1;
2475 // ---------------------------------------------------------------------------
2476 // ATA/ATAPI driver : device detection
2477 // ---------------------------------------------------------------------------
2479 void ata_detect( )
2481 Bit16u ebda_seg=read_word(0x0040,0x000E);
2482 Bit8u hdcount, cdcount, device, type;
2483 Bit8u buffer[0x0200];
2485 #if BX_MAX_ATA_INTERFACES > 0
2486 write_byte(ebda_seg,&EbdaData->ata.channels[0].iface,ATA_IFACE_ISA);
2487 write_word(ebda_seg,&EbdaData->ata.channels[0].iobase1,0x1f0);
2488 write_word(ebda_seg,&EbdaData->ata.channels[0].iobase2,0x3f0);
2489 write_byte(ebda_seg,&EbdaData->ata.channels[0].irq,14);
2490 #endif
2491 #if BX_MAX_ATA_INTERFACES > 1
2492 write_byte(ebda_seg,&EbdaData->ata.channels[1].iface,ATA_IFACE_ISA);
2493 write_word(ebda_seg,&EbdaData->ata.channels[1].iobase1,0x170);
2494 write_word(ebda_seg,&EbdaData->ata.channels[1].iobase2,0x370);
2495 write_byte(ebda_seg,&EbdaData->ata.channels[1].irq,15);
2496 #endif
2497 #if BX_MAX_ATA_INTERFACES > 2
2498 write_byte(ebda_seg,&EbdaData->ata.channels[2].iface,ATA_IFACE_ISA);
2499 write_word(ebda_seg,&EbdaData->ata.channels[2].iobase1,0x1e8);
2500 write_word(ebda_seg,&EbdaData->ata.channels[2].iobase2,0x3e0);
2501 write_byte(ebda_seg,&EbdaData->ata.channels[2].irq,12);
2502 #endif
2503 #if BX_MAX_ATA_INTERFACES > 3
2504 write_byte(ebda_seg,&EbdaData->ata.channels[3].iface,ATA_IFACE_ISA);
2505 write_word(ebda_seg,&EbdaData->ata.channels[3].iobase1,0x168);
2506 write_word(ebda_seg,&EbdaData->ata.channels[3].iobase2,0x360);
2507 write_byte(ebda_seg,&EbdaData->ata.channels[3].irq,11);
2508 #endif
2509 #if BX_MAX_ATA_INTERFACES > 4
2510 #error Please fill the ATA interface informations
2511 #endif
2513 // Device detection
2514 hdcount=cdcount=0;
2516 for(device=0; device<BX_MAX_ATA_DEVICES; device++) {
2517 Bit16u iobase1, iobase2;
2518 Bit8u channel, slave, shift;
2519 Bit8u sc, sn, cl, ch, st;
2521 channel = device / 2;
2522 slave = device % 2;
2524 iobase1 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1);
2525 iobase2 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2);
2527 // Disable interrupts
2528 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2530 // Look for device
2531 outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2532 outb(iobase1+ATA_CB_SC, 0x55);
2533 outb(iobase1+ATA_CB_SN, 0xaa);
2534 outb(iobase1+ATA_CB_SC, 0xaa);
2535 outb(iobase1+ATA_CB_SN, 0x55);
2536 outb(iobase1+ATA_CB_SC, 0x55);
2537 outb(iobase1+ATA_CB_SN, 0xaa);
2539 // If we found something
2540 sc = inb(iobase1+ATA_CB_SC);
2541 sn = inb(iobase1+ATA_CB_SN);
2543 if ( (sc == 0x55) && (sn == 0xaa) ) {
2544 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_UNKNOWN);
2546 // reset the channel
2547 ata_reset(device);
2549 // check for ATA or ATAPI
2550 outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2551 sc = inb(iobase1+ATA_CB_SC);
2552 sn = inb(iobase1+ATA_CB_SN);
2553 if ((sc==0x01) && (sn==0x01)) {
2554 cl = inb(iobase1+ATA_CB_CL);
2555 ch = inb(iobase1+ATA_CB_CH);
2556 st = inb(iobase1+ATA_CB_STAT);
2558 if ((cl==0x14) && (ch==0xeb)) {
2559 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATAPI);
2560 } else if ((cl==0x00) && (ch==0x00) && (st!=0x00)) {
2561 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATA);
2562 } else if ((cl==0xff) && (ch==0xff)) {
2563 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
2568 type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type);
2570 // Now we send a IDENTIFY command to ATA device
2571 if(type == ATA_TYPE_ATA) {
2572 Bit32u sectors_low, sectors_high;
2573 Bit16u cylinders, heads, spt, blksize;
2574 Bit8u translation, removable, mode;
2576 //Temporary values to do the transfer
2577 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2578 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2580 if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE, 1, 0, 0, 0, 0L, 0L, get_SS(),buffer) !=0 )
2581 BX_PANIC("ata-detect: Failed to detect ATA device\n");
2583 removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2584 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2585 blksize = read_word(get_SS(),buffer+10);
2587 cylinders = read_word(get_SS(),buffer+(1*2)); // word 1
2588 heads = read_word(get_SS(),buffer+(3*2)); // word 3
2589 spt = read_word(get_SS(),buffer+(6*2)); // word 6
2591 if (read_word(get_SS(),buffer+(83*2)) & (1 << 10)) { // word 83 - lba48 support
2592 sectors_low = read_dword(get_SS(),buffer+(100*2)); // word 100 and word 101
2593 sectors_high = read_dword(get_SS(),buffer+(102*2)); // word 102 and word 103
2594 } else {
2595 sectors_low = read_dword(get_SS(),buffer+(60*2)); // word 60 and word 61
2596 sectors_high = 0;
2599 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2600 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2601 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2602 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2603 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads, heads);
2604 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders, cylinders);
2605 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt, spt);
2606 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low, sectors_low);
2607 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_high, sectors_high);
2608 BX_INFO("ata%d-%d: PCHS=%u/%d/%d translation=", channel, slave,cylinders, heads, spt);
2610 translation = inb_cmos(0x39 + channel/2);
2611 for (shift=device%4; shift>0; shift--) translation >>= 2;
2612 translation &= 0x03;
2614 write_byte(ebda_seg,&EbdaData->ata.devices[device].translation, translation);
2616 switch (translation) {
2617 case ATA_TRANSLATION_NONE:
2618 BX_INFO("none");
2619 break;
2620 case ATA_TRANSLATION_LBA:
2621 BX_INFO("lba");
2622 break;
2623 case ATA_TRANSLATION_LARGE:
2624 BX_INFO("large");
2625 break;
2626 case ATA_TRANSLATION_RECHS:
2627 BX_INFO("r-echs");
2628 break;
2630 switch (translation) {
2631 case ATA_TRANSLATION_NONE:
2632 break;
2633 case ATA_TRANSLATION_LBA:
2634 spt = 63;
2635 sectors_low /= 63;
2636 heads = sectors_low / 1024;
2637 if (heads>128) heads = 255;
2638 else if (heads>64) heads = 128;
2639 else if (heads>32) heads = 64;
2640 else if (heads>16) heads = 32;
2641 else heads=16;
2642 cylinders = sectors_low / heads;
2643 break;
2644 case ATA_TRANSLATION_RECHS:
2645 // Take care not to overflow
2646 if (heads==16) {
2647 if(cylinders>61439) cylinders=61439;
2648 heads=15;
2649 cylinders = (Bit16u)((Bit32u)(cylinders)*16/15);
2651 // then go through the large bitshift process
2652 case ATA_TRANSLATION_LARGE:
2653 while(cylinders > 1024) {
2654 cylinders >>= 1;
2655 heads <<= 1;
2657 // If we max out the head count
2658 if (heads > 127) break;
2660 break;
2662 // clip to 1024 cylinders in lchs
2663 if (cylinders > 1024) cylinders=1024;
2664 BX_INFO(" LCHS=%d/%d/%d\n", cylinders, heads, spt);
2666 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads, heads);
2667 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders, cylinders);
2668 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt, spt);
2670 // fill hdidmap
2671 write_byte(ebda_seg,&EbdaData->ata.hdidmap[hdcount], device);
2672 hdcount++;
2675 // Now we send a IDENTIFY command to ATAPI device
2676 if(type == ATA_TYPE_ATAPI) {
2678 Bit8u type, removable, mode;
2679 Bit16u blksize;
2681 //Temporary values to do the transfer
2682 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_CDROM);
2683 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2685 if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE_PACKET, 1, 0, 0, 0, 0L, 0L, get_SS(),buffer) != 0)
2686 BX_PANIC("ata-detect: Failed to detect ATAPI device\n");
2688 type = read_byte(get_SS(),buffer+1) & 0x1f;
2689 removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2690 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2691 blksize = 2048;
2693 write_byte(ebda_seg,&EbdaData->ata.devices[device].device, type);
2694 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2695 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2696 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2698 // fill cdidmap
2699 write_byte(ebda_seg,&EbdaData->ata.cdidmap[cdcount], device);
2700 cdcount++;
2704 Bit32u sizeinmb;
2705 Bit16u ataversion;
2706 Bit8u c, i, version, model[41];
2708 switch (type) {
2709 case ATA_TYPE_ATA:
2710 sizeinmb = (read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_high) << 21)
2711 | (read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low) >> 11);
2712 case ATA_TYPE_ATAPI:
2713 // Read ATA/ATAPI version
2714 ataversion=((Bit16u)(read_byte(get_SS(),buffer+161))<<8)|read_byte(get_SS(),buffer+160);
2715 for(version=15;version>0;version--) {
2716 if((ataversion&(1<<version))!=0)
2717 break;
2720 // Read model name
2721 for(i=0;i<20;i++){
2722 write_byte(get_SS(),model+(i*2),read_byte(get_SS(),buffer+(i*2)+54+1));
2723 write_byte(get_SS(),model+(i*2)+1,read_byte(get_SS(),buffer+(i*2)+54));
2726 // Reformat
2727 write_byte(get_SS(),model+40,0x00);
2728 for(i=39;i>0;i--){
2729 if(read_byte(get_SS(),model+i)==0x20)
2730 write_byte(get_SS(),model+i,0x00);
2731 else break;
2733 if (i>36) {
2734 write_byte(get_SS(),model+36,0x00);
2735 for(i=35;i>32;i--){
2736 write_byte(get_SS(),model+i,0x2E);
2739 break;
2742 switch (type) {
2743 case ATA_TYPE_ATA:
2744 printf("ata%d %s: ",channel,slave?" slave":"master");
2745 i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
2746 if (sizeinmb < (1UL<<16))
2747 printf(" ATA-%d Hard-Disk (%4u MBytes)\n", version, (Bit16u)sizeinmb);
2748 else
2749 printf(" ATA-%d Hard-Disk (%4u GBytes)\n", version, (Bit16u)(sizeinmb>>10));
2750 break;
2751 case ATA_TYPE_ATAPI:
2752 printf("ata%d %s: ",channel,slave?" slave":"master");
2753 i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
2754 if(read_byte(ebda_seg,&EbdaData->ata.devices[device].device)==ATA_DEVICE_CDROM)
2755 printf(" ATAPI-%d CD-Rom/DVD-Rom\n",version);
2756 else
2757 printf(" ATAPI-%d Device\n",version);
2758 break;
2759 case ATA_TYPE_UNKNOWN:
2760 printf("ata%d %s: Unknown device\n",channel,slave?" slave":"master");
2761 break;
2766 // Store the devices counts
2767 write_byte(ebda_seg,&EbdaData->ata.hdcount, hdcount);
2768 write_byte(ebda_seg,&EbdaData->ata.cdcount, cdcount);
2769 write_byte(0x40,0x75, hdcount);
2771 printf("\n");
2773 // FIXME : should use bios=cmos|auto|disable bits
2774 // FIXME : should know about translation bits
2775 // FIXME : move hard_drive_post here
2779 // ---------------------------------------------------------------------------
2780 // ATA/ATAPI driver : software reset
2781 // ---------------------------------------------------------------------------
2782 // ATA-3
2783 // 8.2.1 Software reset - Device 0
2785 void ata_reset(device)
2786 Bit16u device;
2788 Bit16u ebda_seg=read_word(0x0040,0x000E);
2789 Bit16u iobase1, iobase2;
2790 Bit8u channel, slave, sn, sc;
2791 Bit8u type;
2792 Bit16u max;
2794 channel = device / 2;
2795 slave = device % 2;
2797 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2798 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2800 // Reset
2802 // 8.2.1 (a) -- set SRST in DC
2803 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST);
2805 // 8.2.1 (b) -- wait for BSY
2806 await_ide(BSY, iobase1, 20);
2808 // 8.2.1 (f) -- clear SRST
2809 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2811 type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type);
2812 if (type != ATA_TYPE_NONE) {
2814 // 8.2.1 (g) -- check for sc==sn==0x01
2815 // select device
2816 outb(iobase1+ATA_CB_DH, slave?ATA_CB_DH_DEV1:ATA_CB_DH_DEV0);
2817 sc = inb(iobase1+ATA_CB_SC);
2818 sn = inb(iobase1+ATA_CB_SN);
2820 if ( (sc==0x01) && (sn==0x01) ) {
2821 if (type == ATA_TYPE_ATA) //ATA
2822 await_ide(NOT_BSY_RDY, iobase1, IDE_TIMEOUT);
2823 else //ATAPI
2824 await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
2827 // 8.2.1 (h) -- wait for not BSY
2828 await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
2831 // Enable interrupts
2832 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2835 // ---------------------------------------------------------------------------
2836 // ATA/ATAPI driver : execute a non data command
2837 // ---------------------------------------------------------------------------
2839 Bit16u ata_cmd_non_data()
2840 {return 0;}
2842 // ---------------------------------------------------------------------------
2843 // ATA/ATAPI driver : execute a data-in command
2844 // ---------------------------------------------------------------------------
2845 // returns
2846 // 0 : no error
2847 // 1 : BUSY bit set
2848 // 2 : read error
2849 // 3 : expected DRQ=1
2850 // 4 : no sectors left to read/verify
2851 // 5 : more sectors to read/verify
2852 // 6 : no sectors left to write
2853 // 7 : more sectors to write
2854 Bit16u ata_cmd_data_in(device, command, count, cylinder, head, sector, lba_low, lba_high, segment, offset)
2855 Bit16u device, command, count, cylinder, head, sector, segment, offset;
2856 Bit32u lba_low, lba_high;
2858 Bit16u ebda_seg=read_word(0x0040,0x000E);
2859 Bit16u iobase1, iobase2, blksize;
2860 Bit8u channel, slave;
2861 Bit8u status, current, mode;
2863 channel = device / 2;
2864 slave = device % 2;
2866 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2867 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2868 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2869 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
2870 if (mode == ATA_MODE_PIO32) blksize>>=2;
2871 else blksize>>=1;
2873 // Reset count of transferred data
2874 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2875 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2876 current = 0;
2878 status = inb(iobase1 + ATA_CB_STAT);
2879 if (status & ATA_CB_STAT_BSY) return 1;
2881 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2883 // sector will be 0 only on lba access. Convert to lba-chs
2884 if (sector == 0) {
2885 if ((count >= 1 << 8) || lba_high || (lba_low + count >= 1UL << 28)) {
2886 outb(iobase1 + ATA_CB_FR, 0x00);
2887 outb(iobase1 + ATA_CB_SC, (count >> 8) & 0xff);
2888 outb(iobase1 + ATA_CB_SN, lba_low >> 24);
2889 outb(iobase1 + ATA_CB_CL, lba_high & 0xff);
2890 outb(iobase1 + ATA_CB_CH, lba_high >> 8);
2891 command |= 0x04;
2892 count &= (1UL << 8) - 1;
2893 lba_low &= (1UL << 24) - 1;
2895 sector = (Bit16u) (lba_low & 0x000000ffL);
2896 cylinder = (Bit16u) ((lba_low>>8) & 0x0000ffffL);
2897 head = ((Bit16u) ((lba_low>>24) & 0x0000000fL)) | ATA_CB_DH_LBA;
2900 outb(iobase1 + ATA_CB_FR, 0x00);
2901 outb(iobase1 + ATA_CB_SC, count);
2902 outb(iobase1 + ATA_CB_SN, sector);
2903 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
2904 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
2905 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
2906 outb(iobase1 + ATA_CB_CMD, command);
2908 await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
2909 status = inb(iobase1 + ATA_CB_STAT);
2911 if (status & ATA_CB_STAT_ERR) {
2912 BX_DEBUG_ATA("ata_cmd_data_in : read error\n");
2913 return 2;
2914 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2915 BX_DEBUG_ATA("ata_cmd_data_in : DRQ not set (status %02x)\n", (unsigned) status);
2916 return 3;
2919 // FIXME : move seg/off translation here
2921 ASM_START
2922 sti ;; enable higher priority interrupts
2923 ASM_END
2925 while (1) {
2927 ASM_START
2928 push bp
2929 mov bp, sp
2930 mov di, _ata_cmd_data_in.offset + 2[bp]
2931 mov ax, _ata_cmd_data_in.segment + 2[bp]
2932 mov cx, _ata_cmd_data_in.blksize + 2[bp]
2934 ;; adjust if there will be an overrun. 2K max sector size
2935 cmp di, #0xf800 ;;
2936 jbe ata_in_no_adjust
2938 ata_in_adjust:
2939 sub di, #0x0800 ;; sub 2 kbytes from offset
2940 add ax, #0x0080 ;; add 2 Kbytes to segment
2942 ata_in_no_adjust:
2943 mov es, ax ;; segment in es
2945 mov dx, _ata_cmd_data_in.iobase1 + 2[bp] ;; ATA data read port
2947 mov ah, _ata_cmd_data_in.mode + 2[bp]
2948 cmp ah, #ATA_MODE_PIO32
2949 je ata_in_32
2951 ata_in_16:
2953 insw ;; CX words transfered from port(DX) to ES:[DI]
2954 jmp ata_in_done
2956 ata_in_32:
2958 insd ;; CX dwords transfered from port(DX) to ES:[DI]
2960 ata_in_done:
2961 mov _ata_cmd_data_in.offset + 2[bp], di
2962 mov _ata_cmd_data_in.segment + 2[bp], es
2963 pop bp
2964 ASM_END
2966 current++;
2967 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
2968 count--;
2969 await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
2970 status = inb(iobase1 + ATA_CB_STAT);
2971 if (count == 0) {
2972 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2973 != ATA_CB_STAT_RDY ) {
2974 BX_DEBUG_ATA("ata_cmd_data_in : no sectors left (status %02x)\n", (unsigned) status);
2975 return 4;
2977 break;
2979 else {
2980 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2981 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2982 BX_DEBUG_ATA("ata_cmd_data_in : more sectors left (status %02x)\n", (unsigned) status);
2983 return 5;
2985 continue;
2988 // Enable interrupts
2989 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2990 return 0;
2993 // ---------------------------------------------------------------------------
2994 // ATA/ATAPI driver : execute a data-out command
2995 // ---------------------------------------------------------------------------
2996 // returns
2997 // 0 : no error
2998 // 1 : BUSY bit set
2999 // 2 : read error
3000 // 3 : expected DRQ=1
3001 // 4 : no sectors left to read/verify
3002 // 5 : more sectors to read/verify
3003 // 6 : no sectors left to write
3004 // 7 : more sectors to write
3005 Bit16u ata_cmd_data_out(device, command, count, cylinder, head, sector, lba_low, lba_high, segment, offset)
3006 Bit16u device, command, count, cylinder, head, sector, segment, offset;
3007 Bit32u lba_low, lba_high;
3009 Bit16u ebda_seg=read_word(0x0040,0x000E);
3010 Bit16u iobase1, iobase2, blksize;
3011 Bit8u channel, slave;
3012 Bit8u status, current, mode;
3014 channel = device / 2;
3015 slave = device % 2;
3017 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
3018 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
3019 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
3020 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
3021 if (mode == ATA_MODE_PIO32) blksize>>=2;
3022 else blksize>>=1;
3024 // Reset count of transferred data
3025 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
3026 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
3027 current = 0;
3029 status = inb(iobase1 + ATA_CB_STAT);
3030 if (status & ATA_CB_STAT_BSY) return 1;
3032 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
3034 // sector will be 0 only on lba access. Convert to lba-chs
3035 if (sector == 0) {
3036 if ((count >= 1 << 8) || lba_high || (lba_low + count >= 1UL << 28)) {
3037 outb(iobase1 + ATA_CB_FR, 0x00);
3038 outb(iobase1 + ATA_CB_SC, (count >> 8) & 0xff);
3039 outb(iobase1 + ATA_CB_SN, lba_low >> 24);
3040 outb(iobase1 + ATA_CB_CL, lba_high & 0xff);
3041 outb(iobase1 + ATA_CB_CH, lba_high >> 8);
3042 command |= 0x04;
3043 count &= (1UL << 8) - 1;
3044 lba_low &= (1UL << 24) - 1;
3046 sector = (Bit16u) (lba_low & 0x000000ffL);
3047 cylinder = (Bit16u) ((lba_low>>8) & 0x0000ffffL);
3048 head = ((Bit16u) ((lba_low>>24) & 0x0000000fL)) | ATA_CB_DH_LBA;
3051 outb(iobase1 + ATA_CB_FR, 0x00);
3052 outb(iobase1 + ATA_CB_SC, count);
3053 outb(iobase1 + ATA_CB_SN, sector);
3054 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
3055 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
3056 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
3057 outb(iobase1 + ATA_CB_CMD, command);
3059 await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
3060 status = inb(iobase1 + ATA_CB_STAT);
3062 if (status & ATA_CB_STAT_ERR) {
3063 BX_DEBUG_ATA("ata_cmd_data_out : read error\n");
3064 return 2;
3065 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
3066 BX_DEBUG_ATA("ata_cmd_data_out : DRQ not set (status %02x)\n", (unsigned) status);
3067 return 3;
3070 // FIXME : move seg/off translation here
3072 ASM_START
3073 sti ;; enable higher priority interrupts
3074 ASM_END
3076 while (1) {
3078 ASM_START
3079 push bp
3080 mov bp, sp
3081 mov si, _ata_cmd_data_out.offset + 2[bp]
3082 mov ax, _ata_cmd_data_out.segment + 2[bp]
3083 mov cx, _ata_cmd_data_out.blksize + 2[bp]
3085 ;; adjust if there will be an overrun. 2K max sector size
3086 cmp si, #0xf800 ;;
3087 jbe ata_out_no_adjust
3089 ata_out_adjust:
3090 sub si, #0x0800 ;; sub 2 kbytes from offset
3091 add ax, #0x0080 ;; add 2 Kbytes to segment
3093 ata_out_no_adjust:
3094 mov es, ax ;; segment in es
3096 mov dx, _ata_cmd_data_out.iobase1 + 2[bp] ;; ATA data write port
3098 mov ah, _ata_cmd_data_out.mode + 2[bp]
3099 cmp ah, #ATA_MODE_PIO32
3100 je ata_out_32
3102 ata_out_16:
3103 seg ES
3105 outsw ;; CX words transfered from port(DX) to ES:[SI]
3106 jmp ata_out_done
3108 ata_out_32:
3109 seg ES
3111 outsd ;; CX dwords transfered from port(DX) to ES:[SI]
3113 ata_out_done:
3114 mov _ata_cmd_data_out.offset + 2[bp], si
3115 mov _ata_cmd_data_out.segment + 2[bp], es
3116 pop bp
3117 ASM_END
3119 current++;
3120 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
3121 count--;
3122 status = inb(iobase1 + ATA_CB_STAT);
3123 if (count == 0) {
3124 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
3125 != ATA_CB_STAT_RDY ) {
3126 BX_DEBUG_ATA("ata_cmd_data_out : no sectors left (status %02x)\n", (unsigned) status);
3127 return 6;
3129 break;
3131 else {
3132 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
3133 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
3134 BX_DEBUG_ATA("ata_cmd_data_out : more sectors left (status %02x)\n", (unsigned) status);
3135 return 7;
3137 continue;
3140 // Enable interrupts
3141 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
3142 return 0;
3145 // ---------------------------------------------------------------------------
3146 // ATA/ATAPI driver : execute a packet command
3147 // ---------------------------------------------------------------------------
3148 // returns
3149 // 0 : no error
3150 // 1 : error in parameters
3151 // 2 : BUSY bit set
3152 // 3 : error
3153 // 4 : not ready
3154 Bit16u ata_cmd_packet(device, cmdlen, cmdseg, cmdoff, header, length, inout, bufseg, bufoff)
3155 Bit8u cmdlen,inout;
3156 Bit16u device,cmdseg, cmdoff, bufseg, bufoff;
3157 Bit16u header;
3158 Bit32u length;
3160 Bit16u ebda_seg=read_word(0x0040,0x000E);
3161 Bit16u iobase1, iobase2;
3162 Bit16u lcount, lbefore, lafter, count;
3163 Bit8u channel, slave;
3164 Bit8u status, mode, lmode;
3165 Bit32u total, transfer;
3167 channel = device / 2;
3168 slave = device % 2;
3170 // Data out is not supported yet
3171 if (inout == ATA_DATA_OUT) {
3172 BX_INFO("ata_cmd_packet: DATA_OUT not supported yet\n");
3173 return 1;
3176 // The header length must be even
3177 if (header & 1) {
3178 BX_DEBUG_ATA("ata_cmd_packet : header must be even (%04x)\n",header);
3179 return 1;
3182 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
3183 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
3184 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
3185 transfer= 0L;
3187 if (cmdlen < 12) cmdlen=12;
3188 if (cmdlen > 12) cmdlen=16;
3189 cmdlen>>=1;
3191 // Reset count of transferred data
3192 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
3193 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
3195 status = inb(iobase1 + ATA_CB_STAT);
3196 if (status & ATA_CB_STAT_BSY) return 2;
3198 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
3199 outb(iobase1 + ATA_CB_FR, 0x00);
3200 outb(iobase1 + ATA_CB_SC, 0x00);
3201 outb(iobase1 + ATA_CB_SN, 0x00);
3202 outb(iobase1 + ATA_CB_CL, 0xfff0 & 0x00ff);
3203 outb(iobase1 + ATA_CB_CH, 0xfff0 >> 8);
3204 outb(iobase1 + ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
3205 outb(iobase1 + ATA_CB_CMD, ATA_CMD_PACKET);
3207 // Device should ok to receive command
3208 await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
3209 status = inb(iobase1 + ATA_CB_STAT);
3211 if (status & ATA_CB_STAT_ERR) {
3212 BX_DEBUG_ATA("ata_cmd_packet : error, status is %02x\n",status);
3213 return 3;
3214 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
3215 BX_DEBUG_ATA("ata_cmd_packet : DRQ not set (status %02x)\n", (unsigned) status);
3216 return 4;
3219 // Normalize address
3220 cmdseg += (cmdoff / 16);
3221 cmdoff %= 16;
3223 // Send command to device
3224 ASM_START
3225 sti ;; enable higher priority interrupts
3227 push bp
3228 mov bp, sp
3230 mov si, _ata_cmd_packet.cmdoff + 2[bp]
3231 mov ax, _ata_cmd_packet.cmdseg + 2[bp]
3232 mov cx, _ata_cmd_packet.cmdlen + 2[bp]
3233 mov es, ax ;; segment in es
3235 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data write port
3237 seg ES
3239 outsw ;; CX words transfered from port(DX) to ES:[SI]
3241 pop bp
3242 ASM_END
3244 if (inout == ATA_DATA_NO) {
3245 await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
3246 status = inb(iobase1 + ATA_CB_STAT);
3248 else {
3249 Bit16u loops = 0;
3250 Bit8u sc;
3251 while (1) {
3253 if (loops == 0) {//first time through
3254 status = inb(iobase2 + ATA_CB_ASTAT);
3255 await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
3257 else
3258 await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
3259 loops++;
3261 status = inb(iobase1 + ATA_CB_STAT);
3262 sc = inb(iobase1 + ATA_CB_SC);
3264 // Check if command completed
3265 if(((inb(iobase1 + ATA_CB_SC)&0x7)==0x3) &&
3266 ((status & (ATA_CB_STAT_RDY | ATA_CB_STAT_ERR)) == ATA_CB_STAT_RDY)) break;
3268 if (status & ATA_CB_STAT_ERR) {
3269 BX_DEBUG_ATA("ata_cmd_packet : error (status %02x)\n",status);
3270 return 3;
3273 // Normalize address
3274 bufseg += (bufoff / 16);
3275 bufoff %= 16;
3277 // Get the byte count
3278 lcount = ((Bit16u)(inb(iobase1 + ATA_CB_CH))<<8)+inb(iobase1 + ATA_CB_CL);
3280 // adjust to read what we want
3281 if(header>lcount) {
3282 lbefore=lcount;
3283 header-=lcount;
3284 lcount=0;
3286 else {
3287 lbefore=header;
3288 header=0;
3289 lcount-=lbefore;
3292 if(lcount>length) {
3293 lafter=lcount-length;
3294 lcount=length;
3295 length=0;
3297 else {
3298 lafter=0;
3299 length-=lcount;
3302 // Save byte count
3303 count = lcount;
3305 BX_DEBUG_ATA("Trying to read %04x bytes (%04x %04x %04x) ",lbefore+lcount+lafter,lbefore,lcount,lafter);
3306 BX_DEBUG_ATA("to 0x%04x:0x%04x\n",bufseg,bufoff);
3308 // If counts not dividable by 4, use 16bits mode
3309 lmode = mode;
3310 if (lbefore & 0x03) lmode=ATA_MODE_PIO16;
3311 if (lcount & 0x03) lmode=ATA_MODE_PIO16;
3312 if (lafter & 0x03) lmode=ATA_MODE_PIO16;
3314 // adds an extra byte if count are odd. before is always even
3315 if (lcount & 0x01) {
3316 lcount+=1;
3317 if ((lafter > 0) && (lafter & 0x01)) {
3318 lafter-=1;
3322 if (lmode == ATA_MODE_PIO32) {
3323 lcount>>=2; lbefore>>=2; lafter>>=2;
3325 else {
3326 lcount>>=1; lbefore>>=1; lafter>>=1;
3329 ; // FIXME bcc bug
3331 ASM_START
3332 push bp
3333 mov bp, sp
3335 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data read port
3337 mov cx, _ata_cmd_packet.lbefore + 2[bp]
3338 jcxz ata_packet_no_before
3340 mov ah, _ata_cmd_packet.lmode + 2[bp]
3341 cmp ah, #ATA_MODE_PIO32
3342 je ata_packet_in_before_32
3344 ata_packet_in_before_16:
3345 in ax, dx
3346 loop ata_packet_in_before_16
3347 jmp ata_packet_no_before
3349 ata_packet_in_before_32:
3350 push eax
3351 ata_packet_in_before_32_loop:
3352 in eax, dx
3353 loop ata_packet_in_before_32_loop
3354 pop eax
3356 ata_packet_no_before:
3357 mov cx, _ata_cmd_packet.lcount + 2[bp]
3358 jcxz ata_packet_after
3360 mov di, _ata_cmd_packet.bufoff + 2[bp]
3361 mov ax, _ata_cmd_packet.bufseg + 2[bp]
3362 mov es, ax
3364 mov ah, _ata_cmd_packet.lmode + 2[bp]
3365 cmp ah, #ATA_MODE_PIO32
3366 je ata_packet_in_32
3368 ata_packet_in_16:
3370 insw ;; CX words transfered tp port(DX) to ES:[DI]
3371 jmp ata_packet_after
3373 ata_packet_in_32:
3375 insd ;; CX dwords transfered to port(DX) to ES:[DI]
3377 ata_packet_after:
3378 mov cx, _ata_cmd_packet.lafter + 2[bp]
3379 jcxz ata_packet_done
3381 mov ah, _ata_cmd_packet.lmode + 2[bp]
3382 cmp ah, #ATA_MODE_PIO32
3383 je ata_packet_in_after_32
3385 ata_packet_in_after_16:
3386 in ax, dx
3387 loop ata_packet_in_after_16
3388 jmp ata_packet_done
3390 ata_packet_in_after_32:
3391 push eax
3392 ata_packet_in_after_32_loop:
3393 in eax, dx
3394 loop ata_packet_in_after_32_loop
3395 pop eax
3397 ata_packet_done:
3398 pop bp
3399 ASM_END
3401 // Compute new buffer address
3402 bufoff += count;
3404 // Save transferred bytes count
3405 transfer += count;
3406 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,transfer);
3410 // Final check, device must be ready
3411 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
3412 != ATA_CB_STAT_RDY ) {
3413 BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", (unsigned) status);
3414 return 4;
3417 // Enable interrupts
3418 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
3419 return 0;
3422 // ---------------------------------------------------------------------------
3423 // End of ATA/ATAPI Driver
3424 // ---------------------------------------------------------------------------
3426 // ---------------------------------------------------------------------------
3427 // Start of ATA/ATAPI generic functions
3428 // ---------------------------------------------------------------------------
3430 Bit16u
3431 atapi_get_sense(device, seg, asc, ascq)
3432 Bit16u device;
3434 Bit8u atacmd[12];
3435 Bit8u buffer[18];
3436 Bit8u i;
3438 memsetb(get_SS(),atacmd,0,12);
3440 // Request SENSE
3441 atacmd[0]=ATA_CMD_REQUEST_SENSE;
3442 atacmd[4]=sizeof(buffer);
3443 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 18L, ATA_DATA_IN, get_SS(), buffer) != 0)
3444 return 0x0002;
3446 write_byte(seg,asc,buffer[12]);
3447 write_byte(seg,ascq,buffer[13]);
3449 return 0;
3452 Bit16u
3453 atapi_is_ready(device)
3454 Bit16u device;
3456 Bit8u packet[12];
3457 Bit8u buf[8];
3458 Bit32u block_len;
3459 Bit32u sectors;
3460 Bit32u timeout; //measured in ms
3461 Bit32u time;
3462 Bit8u asc, ascq;
3463 Bit8u in_progress;
3464 Bit16u ebda_seg = read_word(0x0040,0x000E);
3465 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI) {
3466 printf("not implemented for non-ATAPI device\n");
3467 return -1;
3470 BX_DEBUG_ATA("ata_detect_medium: begin\n");
3471 memsetb(get_SS(),packet, 0, sizeof packet);
3472 packet[0] = 0x25; /* READ CAPACITY */
3474 /* Retry READ CAPACITY 50 times unless MEDIUM NOT PRESENT
3475 * is reported by the device. If the device reports "IN PROGRESS",
3476 * 30 seconds is added. */
3477 timeout = 5000;
3478 time = 0;
3479 in_progress = 0;
3480 while (time < timeout) {
3481 if (ata_cmd_packet(device, sizeof(packet), get_SS(), packet, 0, 8L, ATA_DATA_IN, get_SS(), buf) == 0)
3482 goto ok;
3484 if (atapi_get_sense(device, get_SS(), &asc, &ascq) == 0) {
3485 if (asc == 0x3a) { /* MEDIUM NOT PRESENT */
3486 BX_DEBUG_ATA("Device reports MEDIUM NOT PRESENT\n");
3487 return -1;
3490 if (asc == 0x04 && ascq == 0x01 && !in_progress) {
3491 /* IN PROGRESS OF BECOMING READY */
3492 printf("Waiting for device to detect medium... ");
3493 /* Allow 30 seconds more */
3494 timeout = 30000;
3495 in_progress = 1;
3498 time += 100;
3500 BX_DEBUG_ATA("read capacity failed\n");
3501 return -1;
3504 block_len = (Bit32u) buf[4] << 24
3505 | (Bit32u) buf[5] << 16
3506 | (Bit32u) buf[6] << 8
3507 | (Bit32u) buf[7] << 0;
3508 BX_DEBUG_ATA("block_len=%u\n", block_len);
3510 if (block_len!= 2048 && block_len!= 512)
3512 printf("Unsupported sector size %u\n", block_len);
3513 return -1;
3515 write_dword(ebda_seg,&EbdaData->ata.devices[device].blksize, block_len);
3517 sectors = (Bit32u) buf[0] << 24
3518 | (Bit32u) buf[1] << 16
3519 | (Bit32u) buf[2] << 8
3520 | (Bit32u) buf[3] << 0;
3522 BX_DEBUG_ATA("sectors=%u\n", sectors);
3523 if (block_len == 2048)
3524 sectors <<= 2; /* # of sectors in 512-byte "soft" sector */
3525 if (sectors != read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low))
3526 printf("%dMB medium detected\n", sectors>>(20-9));
3527 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low, sectors);
3528 return 0;
3531 Bit16u
3532 atapi_is_cdrom(device)
3533 Bit8u device;
3535 Bit16u ebda_seg=read_word(0x0040,0x000E);
3537 if (device >= BX_MAX_ATA_DEVICES)
3538 return 0;
3540 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI)
3541 return 0;
3543 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].device) != ATA_DEVICE_CDROM)
3544 return 0;
3546 return 1;
3549 // ---------------------------------------------------------------------------
3550 // End of ATA/ATAPI generic functions
3551 // ---------------------------------------------------------------------------
3553 #endif // BX_USE_ATADRV
3555 #if BX_ELTORITO_BOOT
3557 // ---------------------------------------------------------------------------
3558 // Start of El-Torito boot functions
3559 // ---------------------------------------------------------------------------
3561 void
3562 cdemu_init()
3564 Bit16u ebda_seg=read_word(0x0040,0x000E);
3566 // the only important data is this one for now
3567 write_byte(ebda_seg,&EbdaData->cdemu.active,0x00);
3570 Bit8u
3571 cdemu_isactive()
3573 Bit16u ebda_seg=read_word(0x0040,0x000E);
3575 return(read_byte(ebda_seg,&EbdaData->cdemu.active));
3578 Bit8u
3579 cdemu_emulated_drive()
3581 Bit16u ebda_seg=read_word(0x0040,0x000E);
3583 return(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
3586 static char isotag[6]="CD001";
3587 static char eltorito[24]="EL TORITO SPECIFICATION";
3589 // Returns ah: emulated drive, al: error code
3591 Bit16u
3592 cdrom_boot()
3594 Bit16u ebda_seg=read_word(0x0040,0x000E);
3595 Bit8u atacmd[12], buffer[2048];
3596 Bit32u lba;
3597 Bit16u boot_segment, nbsectors, i, error;
3598 Bit8u device;
3600 // Find out the first cdrom
3601 for (device=0; device<BX_MAX_ATA_DEVICES;device++) {
3602 if (atapi_is_cdrom(device)) break;
3605 // if not found
3606 if(device >= BX_MAX_ATA_DEVICES) return 2;
3608 if(error = atapi_is_ready(device) != 0)
3609 BX_INFO("ata_is_ready returned %d\n",error);
3611 // Read the Boot Record Volume Descriptor
3612 memsetb(get_SS(),atacmd,0,12);
3613 atacmd[0]=0x28; // READ command
3614 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3615 atacmd[8]=(0x01 & 0x00ff); // Sectors
3616 atacmd[2]=(0x11 & 0xff000000) >> 24; // LBA
3617 atacmd[3]=(0x11 & 0x00ff0000) >> 16;
3618 atacmd[4]=(0x11 & 0x0000ff00) >> 8;
3619 atacmd[5]=(0x11 & 0x000000ff);
3620 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3621 return 3;
3623 // Validity checks
3624 if(buffer[0]!=0)return 4;
3625 for(i=0;i<5;i++){
3626 if(buffer[1+i]!=read_byte(0xf000,&isotag[i]))return 5;
3628 for(i=0;i<23;i++)
3629 if(buffer[7+i]!=read_byte(0xf000,&eltorito[i]))return 6;
3631 // ok, now we calculate the Boot catalog address
3632 lba=buffer[0x4A]*0x1000000+buffer[0x49]*0x10000+buffer[0x48]*0x100+buffer[0x47];
3634 // And we read the Boot Catalog
3635 memsetb(get_SS(),atacmd,0,12);
3636 atacmd[0]=0x28; // READ command
3637 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3638 atacmd[8]=(0x01 & 0x00ff); // Sectors
3639 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3640 atacmd[3]=(lba & 0x00ff0000) >> 16;
3641 atacmd[4]=(lba & 0x0000ff00) >> 8;
3642 atacmd[5]=(lba & 0x000000ff);
3643 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3644 return 7;
3646 // Validation entry
3647 if(buffer[0x00]!=0x01)return 8; // Header
3648 if(buffer[0x01]!=0x00)return 9; // Platform
3649 if(buffer[0x1E]!=0x55)return 10; // key 1
3650 if(buffer[0x1F]!=0xAA)return 10; // key 2
3652 // Initial/Default Entry
3653 if(buffer[0x20]!=0x88)return 11; // Bootable
3655 write_byte(ebda_seg,&EbdaData->cdemu.media,buffer[0x21]);
3656 if(buffer[0x21]==0){
3657 // FIXME ElTorito Hardcoded. cdrom is hardcoded as device 0xE0.
3658 // Win2000 cd boot needs to know it booted from cd
3659 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0xE0);
3661 else if(buffer[0x21]<4)
3662 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x00);
3663 else
3664 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x80);
3666 write_byte(ebda_seg,&EbdaData->cdemu.controller_index,device/2);
3667 write_byte(ebda_seg,&EbdaData->cdemu.device_spec,device%2);
3669 boot_segment=buffer[0x23]*0x100+buffer[0x22];
3670 if(boot_segment==0x0000)boot_segment=0x07C0;
3672 write_word(ebda_seg,&EbdaData->cdemu.load_segment,boot_segment);
3673 write_word(ebda_seg,&EbdaData->cdemu.buffer_segment,0x0000);
3675 nbsectors=buffer[0x27]*0x100+buffer[0x26];
3676 write_word(ebda_seg,&EbdaData->cdemu.sector_count,nbsectors);
3678 lba=buffer[0x2B]*0x1000000+buffer[0x2A]*0x10000+buffer[0x29]*0x100+buffer[0x28];
3679 write_dword(ebda_seg,&EbdaData->cdemu.ilba,lba);
3681 // And we read the image in memory
3682 memsetb(get_SS(),atacmd,0,12);
3683 atacmd[0]=0x28; // READ command
3684 atacmd[7]=((1+(nbsectors-1)/4) & 0xff00) >> 8; // Sectors
3685 atacmd[8]=((1+(nbsectors-1)/4) & 0x00ff); // Sectors
3686 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3687 atacmd[3]=(lba & 0x00ff0000) >> 16;
3688 atacmd[4]=(lba & 0x0000ff00) >> 8;
3689 atacmd[5]=(lba & 0x000000ff);
3690 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, nbsectors*512L, ATA_DATA_IN, boot_segment,0)) != 0)
3691 return 12;
3693 // Remember the media type
3694 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
3695 case 0x01: // 1.2M floppy
3696 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,15);
3697 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3698 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3699 break;
3700 case 0x02: // 1.44M floppy
3701 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,18);
3702 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3703 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3704 break;
3705 case 0x03: // 2.88M floppy
3706 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,36);
3707 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3708 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3709 break;
3710 case 0x04: // Harddrive
3711 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,read_byte(boot_segment,446+6)&0x3f);
3712 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,
3713 (read_byte(boot_segment,446+6)<<2) + read_byte(boot_segment,446+7) + 1);
3714 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,read_byte(boot_segment,446+5) + 1);
3715 break;
3718 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0) {
3719 // Increase bios installed hardware number of devices
3720 if(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)==0x00)
3721 write_byte(0x40,0x10,read_byte(0x40,0x10)|0x41);
3722 else
3723 write_byte(ebda_seg, &EbdaData->ata.hdcount, read_byte(ebda_seg, &EbdaData->ata.hdcount) + 1);
3727 // everything is ok, so from now on, the emulation is active
3728 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0)
3729 write_byte(ebda_seg,&EbdaData->cdemu.active,0x01);
3731 // return the boot drive + no error
3732 return (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)*0x100)+0;
3735 // ---------------------------------------------------------------------------
3736 // End of El-Torito boot functions
3737 // ---------------------------------------------------------------------------
3738 #endif // BX_ELTORITO_BOOT
3740 void
3741 int14_function(regs, ds, iret_addr)
3742 pusha_regs_t regs; // regs pushed from PUSHA instruction
3743 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
3744 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
3746 Bit16u addr,timer,val16;
3747 Bit8u timeout;
3749 ASM_START
3751 ASM_END
3753 addr = read_word(0x0040, (regs.u.r16.dx << 1));
3754 timeout = read_byte(0x0040, 0x007C + regs.u.r16.dx);
3755 if ((regs.u.r16.dx < 4) && (addr > 0)) {
3756 switch (regs.u.r8.ah) {
3757 case 0:
3758 outb(addr+3, inb(addr+3) | 0x80);
3759 if (regs.u.r8.al & 0xE0 == 0) {
3760 outb(addr, 0x17);
3761 outb(addr+1, 0x04);
3762 } else {
3763 val16 = 0x600 >> ((regs.u.r8.al & 0xE0) >> 5);
3764 outb(addr, val16 & 0xFF);
3765 outb(addr+1, val16 >> 8);
3767 outb(addr+3, regs.u.r8.al & 0x1F);
3768 regs.u.r8.ah = inb(addr+5);
3769 regs.u.r8.al = inb(addr+6);
3770 ClearCF(iret_addr.flags);
3771 break;
3772 case 1:
3773 timer = read_word(0x0040, 0x006C);
3774 while (((inb(addr+5) & 0x60) != 0x60) && (timeout)) {
3775 val16 = read_word(0x0040, 0x006C);
3776 if (val16 != timer) {
3777 timer = val16;
3778 timeout--;
3781 if (timeout) outb(addr, regs.u.r8.al);
3782 regs.u.r8.ah = inb(addr+5);
3783 if (!timeout) regs.u.r8.ah |= 0x80;
3784 ClearCF(iret_addr.flags);
3785 break;
3786 case 2:
3787 timer = read_word(0x0040, 0x006C);
3788 while (((inb(addr+5) & 0x01) == 0) && (timeout)) {
3789 val16 = read_word(0x0040, 0x006C);
3790 if (val16 != timer) {
3791 timer = val16;
3792 timeout--;
3795 if (timeout) {
3796 regs.u.r8.ah = 0;
3797 regs.u.r8.al = inb(addr);
3798 } else {
3799 regs.u.r8.ah = inb(addr+5);
3801 ClearCF(iret_addr.flags);
3802 break;
3803 case 3:
3804 regs.u.r8.ah = inb(addr+5);
3805 regs.u.r8.al = inb(addr+6);
3806 ClearCF(iret_addr.flags);
3807 break;
3808 default:
3809 SetCF(iret_addr.flags); // Unsupported
3811 } else {
3812 SetCF(iret_addr.flags); // Unsupported
3816 void
3817 int15_function(regs, ES, DS, FLAGS)
3818 pusha_regs_t regs; // REGS pushed via pusha
3819 Bit16u ES, DS, FLAGS;
3821 Bit16u ebda_seg=read_word(0x0040,0x000E);
3822 bx_bool prev_a20_enable;
3823 Bit16u base15_00;
3824 Bit8u base23_16;
3825 Bit16u ss;
3826 Bit16u CX,DX;
3828 Bit16u bRegister;
3829 Bit8u irqDisable;
3831 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3833 switch (regs.u.r8.ah) {
3834 case 0x24: /* A20 Control */
3835 switch (regs.u.r8.al) {
3836 case 0x00:
3837 set_enable_a20(0);
3838 CLEAR_CF();
3839 regs.u.r8.ah = 0;
3840 break;
3841 case 0x01:
3842 set_enable_a20(1);
3843 CLEAR_CF();
3844 regs.u.r8.ah = 0;
3845 break;
3846 case 0x02:
3847 regs.u.r8.al = (inb(0x92) >> 1) & 0x01;
3848 CLEAR_CF();
3849 regs.u.r8.ah = 0;
3850 break;
3851 case 0x03:
3852 CLEAR_CF();
3853 regs.u.r8.ah = 0;
3854 regs.u.r16.bx = 3;
3855 break;
3856 default:
3857 BX_INFO("int15: Func 24h, subfunc %02xh, A20 gate control not supported\n", (unsigned) regs.u.r8.al);
3858 SET_CF();
3859 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3861 break;
3863 case 0x41:
3864 SET_CF();
3865 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3866 break;
3868 case 0x4f:
3869 /* keyboard intercept */
3870 #if BX_CPU < 2
3871 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3872 #else
3873 // nop
3874 #endif
3875 SET_CF();
3876 break;
3878 case 0x52: // removable media eject
3879 CLEAR_CF();
3880 regs.u.r8.ah = 0; // "ok ejection may proceed"
3881 break;
3883 case 0x83: {
3884 if( regs.u.r8.al == 0 ) {
3885 // Set Interval requested.
3886 if( ( read_byte( 0x40, 0xA0 ) & 1 ) == 0 ) {
3887 // Interval not already set.
3888 write_byte( 0x40, 0xA0, 1 ); // Set status byte.
3889 write_word( 0x40, 0x98, ES ); // Byte location, segment
3890 write_word( 0x40, 0x9A, regs.u.r16.bx ); // Byte location, offset
3891 write_word( 0x40, 0x9C, regs.u.r16.dx ); // Low word, delay
3892 write_word( 0x40, 0x9E, regs.u.r16.cx ); // High word, delay.
3893 CLEAR_CF( );
3894 irqDisable = inb( 0xA1 );
3895 outb( 0xA1, irqDisable & 0xFE );
3896 bRegister = inb_cmos( 0xB ); // Unmask IRQ8 so INT70 will get through.
3897 outb_cmos( 0xB, bRegister | 0x40 ); // Turn on the Periodic Interrupt timer
3898 } else {
3899 // Interval already set.
3900 BX_DEBUG_INT15("int15: Func 83h, failed, already waiting.\n" );
3901 SET_CF();
3902 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3904 } else if( regs.u.r8.al == 1 ) {
3905 // Clear Interval requested
3906 write_byte( 0x40, 0xA0, 0 ); // Clear status byte
3907 CLEAR_CF( );
3908 bRegister = inb_cmos( 0xB );
3909 outb_cmos( 0xB, bRegister & ~0x40 ); // Turn off the Periodic Interrupt timer
3910 } else {
3911 BX_DEBUG_INT15("int15: Func 83h, failed.\n" );
3912 SET_CF();
3913 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3914 regs.u.r8.al--;
3917 break;
3920 case 0x87:
3921 #if BX_CPU < 3
3922 # error "Int15 function 87h not supported on < 80386"
3923 #endif
3924 // +++ should probably have descriptor checks
3925 // +++ should have exception handlers
3927 // turn off interrupts
3928 ASM_START
3930 ASM_END
3932 prev_a20_enable = set_enable_a20(1); // enable A20 line
3934 // 128K max of transfer on 386+ ???
3935 // source == destination ???
3937 // ES:SI points to descriptor table
3938 // offset use initially comments
3939 // ==============================================
3940 // 00..07 Unused zeros Null descriptor
3941 // 08..0f GDT zeros filled in by BIOS
3942 // 10..17 source ssssssss source of data
3943 // 18..1f dest dddddddd destination of data
3944 // 20..27 CS zeros filled in by BIOS
3945 // 28..2f SS zeros filled in by BIOS
3947 //es:si
3948 //eeee0
3949 //0ssss
3950 //-----
3952 // check for access rights of source & dest here
3954 // Initialize GDT descriptor
3955 base15_00 = (ES << 4) + regs.u.r16.si;
3956 base23_16 = ES >> 12;
3957 if (base15_00 < (ES<<4))
3958 base23_16++;
3959 write_word(ES, regs.u.r16.si+0x08+0, 47); // limit 15:00 = 6 * 8bytes/descriptor
3960 write_word(ES, regs.u.r16.si+0x08+2, base15_00);// base 15:00
3961 write_byte(ES, regs.u.r16.si+0x08+4, base23_16);// base 23:16
3962 write_byte(ES, regs.u.r16.si+0x08+5, 0x93); // access
3963 write_word(ES, regs.u.r16.si+0x08+6, 0x0000); // base 31:24/reserved/limit 19:16
3965 // Initialize CS descriptor
3966 write_word(ES, regs.u.r16.si+0x20+0, 0xffff);// limit 15:00 = normal 64K limit
3967 write_word(ES, regs.u.r16.si+0x20+2, 0x0000);// base 15:00
3968 write_byte(ES, regs.u.r16.si+0x20+4, 0x000f);// base 23:16
3969 write_byte(ES, regs.u.r16.si+0x20+5, 0x9b); // access
3970 write_word(ES, regs.u.r16.si+0x20+6, 0x0000);// base 31:24/reserved/limit 19:16
3972 // Initialize SS descriptor
3973 ss = get_SS();
3974 base15_00 = ss << 4;
3975 base23_16 = ss >> 12;
3976 write_word(ES, regs.u.r16.si+0x28+0, 0xffff); // limit 15:00 = normal 64K limit
3977 write_word(ES, regs.u.r16.si+0x28+2, base15_00);// base 15:00
3978 write_byte(ES, regs.u.r16.si+0x28+4, base23_16);// base 23:16
3979 write_byte(ES, regs.u.r16.si+0x28+5, 0x93); // access
3980 write_word(ES, regs.u.r16.si+0x28+6, 0x0000); // base 31:24/reserved/limit 19:16
3982 CX = regs.u.r16.cx;
3983 ASM_START
3984 // Compile generates locals offset info relative to SP.
3985 // Get CX (word count) from stack.
3986 mov bx, sp
3987 SEG SS
3988 mov cx, _int15_function.CX [bx]
3990 // since we need to set SS:SP, save them to the BDA
3991 // for future restore
3992 push eax
3993 xor eax, eax
3994 mov ds, ax
3995 mov 0x0469, ss
3996 mov 0x0467, sp
3998 SEG ES
3999 lgdt [si + 0x08]
4000 SEG CS
4001 lidt [pmode_IDT_info]
4002 ;; perhaps do something with IDT here
4004 ;; set PE bit in CR0
4005 mov eax, cr0
4006 or al, #0x01
4007 mov cr0, eax
4008 ;; far jump to flush CPU queue after transition to protected mode
4009 JMP_AP(0x0020, protected_mode)
4011 protected_mode:
4012 ;; GDT points to valid descriptor table, now load SS, DS, ES
4013 mov ax, #0x28 ;; 101 000 = 5th descriptor in table, TI=GDT, RPL=00
4014 mov ss, ax
4015 mov ax, #0x10 ;; 010 000 = 2nd descriptor in table, TI=GDT, RPL=00
4016 mov ds, ax
4017 mov ax, #0x18 ;; 011 000 = 3rd descriptor in table, TI=GDT, RPL=00
4018 mov es, ax
4019 xor si, si
4020 xor di, di
4023 movsw ;; move CX words from DS:SI to ES:DI
4025 ;; make sure DS and ES limits are 64KB
4026 mov ax, #0x28
4027 mov ds, ax
4028 mov es, ax
4030 ;; reset PG bit in CR0 ???
4031 mov eax, cr0
4032 and al, #0xFE
4033 mov cr0, eax
4035 ;; far jump to flush CPU queue after transition to real mode
4036 JMP_AP(0xf000, real_mode)
4038 real_mode:
4039 ;; restore IDT to normal real-mode defaults
4040 SEG CS
4041 lidt [rmode_IDT_info]
4043 // restore SS:SP from the BDA
4044 xor ax, ax
4045 mov ds, ax
4046 mov ss, 0x0469
4047 mov sp, 0x0467
4048 pop eax
4049 ASM_END
4051 set_enable_a20(prev_a20_enable);
4053 // turn back on interrupts
4054 ASM_START
4056 ASM_END
4058 regs.u.r8.ah = 0;
4059 CLEAR_CF();
4060 break;
4063 case 0x88:
4064 // Get the amount of extended memory (above 1M)
4065 #if BX_CPU < 2
4066 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4067 SET_CF();
4068 #else
4069 regs.u.r8.al = inb_cmos(0x30);
4070 regs.u.r8.ah = inb_cmos(0x31);
4072 // According to Ralf Brown's interrupt the limit should be 15M,
4073 // but real machines mostly return max. 63M.
4074 if(regs.u.r16.ax > 0xffc0)
4075 regs.u.r16.ax = 0xffc0;
4077 CLEAR_CF();
4078 #endif
4079 break;
4081 case 0x90:
4082 /* Device busy interrupt. Called by Int 16h when no key available */
4083 break;
4085 case 0x91:
4086 /* Interrupt complete. Called by Int 16h when key becomes available */
4087 break;
4089 case 0xbf:
4090 BX_INFO("*** int 15h function AH=bf not yet supported!\n");
4091 SET_CF();
4092 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4093 break;
4095 case 0xC0:
4096 #if 0
4097 SET_CF();
4098 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4099 break;
4100 #endif
4101 CLEAR_CF();
4102 regs.u.r8.ah = 0;
4103 regs.u.r16.bx = BIOS_CONFIG_TABLE;
4104 ES = 0xF000;
4105 break;
4107 case 0xc1:
4108 ES = ebda_seg;
4109 CLEAR_CF();
4110 break;
4112 case 0xd8:
4113 bios_printf(BIOS_PRINTF_DEBUG, "EISA BIOS not present\n");
4114 SET_CF();
4115 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4116 break;
4118 default:
4119 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4120 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4121 SET_CF();
4122 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4123 break;
4127 #if BX_USE_PS2_MOUSE
4128 void
4129 int15_function_mouse(regs, ES, DS, FLAGS)
4130 pusha_regs_t regs; // REGS pushed via pusha
4131 Bit16u ES, DS, FLAGS;
4133 Bit16u ebda_seg=read_word(0x0040,0x000E);
4134 Bit8u mouse_flags_1, mouse_flags_2;
4135 Bit16u mouse_driver_seg;
4136 Bit16u mouse_driver_offset;
4137 Bit8u comm_byte, prev_command_byte;
4138 Bit8u ret, mouse_data1, mouse_data2, mouse_data3;
4140 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
4142 switch (regs.u.r8.ah) {
4143 case 0xC2:
4144 // Return Codes status in AH
4145 // =========================
4146 // 00: success
4147 // 01: invalid subfunction (AL > 7)
4148 // 02: invalid input value (out of allowable range)
4149 // 03: interface error
4150 // 04: resend command received from mouse controller,
4151 // device driver should attempt command again
4152 // 05: cannot enable mouse, since no far call has been installed
4153 // 80/86: mouse service not implemented
4155 switch (regs.u.r8.al) {
4156 case 0: // Disable/Enable Mouse
4157 BX_DEBUG_INT15("case 0:\n");
4158 switch (regs.u.r8.bh) {
4159 case 0: // Disable Mouse
4160 BX_DEBUG_INT15("case 0: disable mouse\n");
4161 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4162 ret = send_to_mouse_ctrl(0xF5); // disable mouse command
4163 if (ret == 0) {
4164 ret = get_mouse_data(&mouse_data1);
4165 if ( (ret == 0) || (mouse_data1 == 0xFA) ) {
4166 CLEAR_CF();
4167 regs.u.r8.ah = 0;
4168 return;
4172 // error
4173 SET_CF();
4174 regs.u.r8.ah = ret;
4175 return;
4176 break;
4178 case 1: // Enable Mouse
4179 BX_DEBUG_INT15("case 1: enable mouse\n");
4180 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4181 if ( (mouse_flags_2 & 0x80) == 0 ) {
4182 BX_DEBUG_INT15("INT 15h C2 Enable Mouse, no far call handler\n");
4183 SET_CF(); // error
4184 regs.u.r8.ah = 5; // no far call installed
4185 return;
4187 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4188 ret = send_to_mouse_ctrl(0xF4); // enable mouse command
4189 if (ret == 0) {
4190 ret = get_mouse_data(&mouse_data1);
4191 if ( (ret == 0) && (mouse_data1 == 0xFA) ) {
4192 enable_mouse_int_and_events(); // turn IRQ12 and packet generation on
4193 CLEAR_CF();
4194 regs.u.r8.ah = 0;
4195 return;
4198 SET_CF();
4199 regs.u.r8.ah = ret;
4200 return;
4202 default: // invalid subfunction
4203 BX_DEBUG_INT15("INT 15h C2 AL=0, BH=%02x\n", (unsigned) regs.u.r8.bh);
4204 SET_CF(); // error
4205 regs.u.r8.ah = 1; // invalid subfunction
4206 return;
4208 break;
4210 case 1: // Reset Mouse
4211 case 5: // Initialize Mouse
4212 BX_DEBUG_INT15("case 1 or 5:\n");
4213 if (regs.u.r8.al == 5) {
4214 if (regs.u.r8.bh != 3) {
4215 SET_CF();
4216 regs.u.r8.ah = 0x02; // invalid input
4217 return;
4219 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4220 mouse_flags_2 = (mouse_flags_2 & 0x00) | regs.u.r8.bh;
4221 mouse_flags_1 = 0x00;
4222 write_byte(ebda_seg, 0x0026, mouse_flags_1);
4223 write_byte(ebda_seg, 0x0027, mouse_flags_2);
4226 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4227 ret = send_to_mouse_ctrl(0xFF); // reset mouse command
4228 if (ret == 0) {
4229 ret = get_mouse_data(&mouse_data3);
4230 // if no mouse attached, it will return RESEND
4231 if (mouse_data3 == 0xfe) {
4232 SET_CF();
4233 return;
4235 if (mouse_data3 != 0xfa)
4236 BX_PANIC("Mouse reset returned %02x (should be ack)\n", (unsigned)mouse_data3);
4237 if ( ret == 0 ) {
4238 ret = get_mouse_data(&mouse_data1);
4239 if ( ret == 0 ) {
4240 ret = get_mouse_data(&mouse_data2);
4241 if ( ret == 0 ) {
4242 // turn IRQ12 and packet generation on
4243 enable_mouse_int_and_events();
4244 CLEAR_CF();
4245 regs.u.r8.ah = 0;
4246 regs.u.r8.bl = mouse_data1;
4247 regs.u.r8.bh = mouse_data2;
4248 return;
4254 // error
4255 SET_CF();
4256 regs.u.r8.ah = ret;
4257 return;
4259 case 2: // Set Sample Rate
4260 BX_DEBUG_INT15("case 2:\n");
4261 switch (regs.u.r8.bh) {
4262 case 0: mouse_data1 = 10; break; // 10 reports/sec
4263 case 1: mouse_data1 = 20; break; // 20 reports/sec
4264 case 2: mouse_data1 = 40; break; // 40 reports/sec
4265 case 3: mouse_data1 = 60; break; // 60 reports/sec
4266 case 4: mouse_data1 = 80; break; // 80 reports/sec
4267 case 5: mouse_data1 = 100; break; // 100 reports/sec (default)
4268 case 6: mouse_data1 = 200; break; // 200 reports/sec
4269 default: mouse_data1 = 0;
4271 if (mouse_data1 > 0) {
4272 ret = send_to_mouse_ctrl(0xF3); // set sample rate command
4273 if (ret == 0) {
4274 ret = get_mouse_data(&mouse_data2);
4275 ret = send_to_mouse_ctrl(mouse_data1);
4276 ret = get_mouse_data(&mouse_data2);
4277 CLEAR_CF();
4278 regs.u.r8.ah = 0;
4279 } else {
4280 // error
4281 SET_CF();
4282 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4284 } else {
4285 // error
4286 SET_CF();
4287 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4289 break;
4291 case 3: // Set Resolution
4292 BX_DEBUG_INT15("case 3:\n");
4293 // BH:
4294 // 0 = 25 dpi, 1 count per millimeter
4295 // 1 = 50 dpi, 2 counts per millimeter
4296 // 2 = 100 dpi, 4 counts per millimeter
4297 // 3 = 200 dpi, 8 counts per millimeter
4298 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4299 if (regs.u.r8.bh < 4) {
4300 ret = send_to_mouse_ctrl(0xE8); // set resolution command
4301 if (ret == 0) {
4302 ret = get_mouse_data(&mouse_data1);
4303 if (mouse_data1 != 0xfa)
4304 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
4305 ret = send_to_mouse_ctrl(regs.u.r8.bh);
4306 ret = get_mouse_data(&mouse_data1);
4307 if (mouse_data1 != 0xfa)
4308 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
4309 CLEAR_CF();
4310 regs.u.r8.ah = 0;
4311 } else {
4312 // error
4313 SET_CF();
4314 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4316 } else {
4317 // error
4318 SET_CF();
4319 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4321 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4322 break;
4324 case 4: // Get Device ID
4325 BX_DEBUG_INT15("case 4:\n");
4326 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4327 ret = send_to_mouse_ctrl(0xF2); // get mouse ID command
4328 if (ret == 0) {
4329 ret = get_mouse_data(&mouse_data1);
4330 ret = get_mouse_data(&mouse_data2);
4331 CLEAR_CF();
4332 regs.u.r8.ah = 0;
4333 regs.u.r8.bh = mouse_data2;
4334 } else {
4335 // error
4336 SET_CF();
4337 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4339 break;
4341 case 6: // Return Status & Set Scaling Factor...
4342 BX_DEBUG_INT15("case 6:\n");
4343 switch (regs.u.r8.bh) {
4344 case 0: // Return Status
4345 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4346 ret = send_to_mouse_ctrl(0xE9); // get mouse info command
4347 if (ret == 0) {
4348 ret = get_mouse_data(&mouse_data1);
4349 if (mouse_data1 != 0xfa)
4350 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
4351 if (ret == 0) {
4352 ret = get_mouse_data(&mouse_data1);
4353 if ( ret == 0 ) {
4354 ret = get_mouse_data(&mouse_data2);
4355 if ( ret == 0 ) {
4356 ret = get_mouse_data(&mouse_data3);
4357 if ( ret == 0 ) {
4358 CLEAR_CF();
4359 regs.u.r8.ah = 0;
4360 regs.u.r8.bl = mouse_data1;
4361 regs.u.r8.cl = mouse_data2;
4362 regs.u.r8.dl = mouse_data3;
4363 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4364 return;
4371 // error
4372 SET_CF();
4373 regs.u.r8.ah = ret;
4374 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4375 return;
4377 case 1: // Set Scaling Factor to 1:1
4378 case 2: // Set Scaling Factor to 2:1
4379 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4380 if (regs.u.r8.bh == 1) {
4381 ret = send_to_mouse_ctrl(0xE6);
4382 } else {
4383 ret = send_to_mouse_ctrl(0xE7);
4385 if (ret == 0) {
4386 get_mouse_data(&mouse_data1);
4387 ret = (mouse_data1 != 0xFA);
4389 if (ret == 0) {
4390 CLEAR_CF();
4391 regs.u.r8.ah = 0;
4392 } else {
4393 // error
4394 SET_CF();
4395 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4397 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4398 break;
4400 default:
4401 BX_PANIC("INT 15h C2 AL=6, BH=%02x\n", (unsigned) regs.u.r8.bh);
4403 break;
4405 case 7: // Set Mouse Handler Address
4406 BX_DEBUG_INT15("case 7:\n");
4407 mouse_driver_seg = ES;
4408 mouse_driver_offset = regs.u.r16.bx;
4409 write_word(ebda_seg, 0x0022, mouse_driver_offset);
4410 write_word(ebda_seg, 0x0024, mouse_driver_seg);
4411 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4412 if (mouse_driver_offset == 0 && mouse_driver_seg == 0) {
4413 /* remove handler */
4414 if ( (mouse_flags_2 & 0x80) != 0 ) {
4415 mouse_flags_2 &= ~0x80;
4416 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4419 else {
4420 /* install handler */
4421 mouse_flags_2 |= 0x80;
4423 write_byte(ebda_seg, 0x0027, mouse_flags_2);
4424 CLEAR_CF();
4425 regs.u.r8.ah = 0;
4426 break;
4428 default:
4429 BX_DEBUG_INT15("case default:\n");
4430 regs.u.r8.ah = 1; // invalid function
4431 SET_CF();
4433 break;
4435 default:
4436 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4437 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4438 SET_CF();
4439 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4440 break;
4443 #endif // BX_USE_PS2_MOUSE
4446 void set_e820_range(ES, DI, start, end, extra_start, extra_end, type)
4447 Bit16u ES;
4448 Bit16u DI;
4449 Bit32u start;
4450 Bit32u end;
4451 Bit8u extra_start;
4452 Bit8u extra_end;
4453 Bit16u type;
4455 write_word(ES, DI, start);
4456 write_word(ES, DI+2, start >> 16);
4457 write_word(ES, DI+4, extra_start);
4458 write_word(ES, DI+6, 0x00);
4460 end -= start;
4461 extra_end -= extra_start;
4462 write_word(ES, DI+8, end);
4463 write_word(ES, DI+10, end >> 16);
4464 write_word(ES, DI+12, extra_end);
4465 write_word(ES, DI+14, 0x0000);
4467 write_word(ES, DI+16, type);
4468 write_word(ES, DI+18, 0x0);
4471 void
4472 int15_function32(regs, ES, DS, FLAGS)
4473 pushad_regs_t regs; // REGS pushed via pushad
4474 Bit16u ES, DS, FLAGS;
4476 Bit32u extended_memory_size=0; // 64bits long
4477 Bit32u extra_lowbits_memory_size=0;
4478 Bit16u CX,DX;
4479 Bit8u extra_highbits_memory_size=0;
4481 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
4483 switch (regs.u.r8.ah) {
4484 case 0x86:
4485 // Wait for CX:DX microseconds. currently using the
4486 // refresh request port 0x61 bit4, toggling every 15usec
4488 CX = regs.u.r16.cx;
4489 DX = regs.u.r16.dx;
4491 ASM_START
4494 ;; Get the count in eax
4495 mov bx, sp
4496 SEG SS
4497 mov ax, _int15_function32.CX [bx]
4498 shl eax, #16
4499 SEG SS
4500 mov ax, _int15_function32.DX [bx]
4502 ;; convert to numbers of 15usec ticks
4503 mov ebx, #15
4504 xor edx, edx
4505 div eax, ebx
4506 mov ecx, eax
4508 ;; wait for ecx number of refresh requests
4509 in al, #0x61
4510 and al,#0x10
4511 mov ah, al
4513 or ecx, ecx
4514 je int1586_tick_end
4515 int1586_tick:
4516 in al, #0x61
4517 and al,#0x10
4518 cmp al, ah
4519 je int1586_tick
4520 mov ah, al
4521 dec ecx
4522 jnz int1586_tick
4523 int1586_tick_end:
4524 ASM_END
4526 break;
4528 case 0xe8:
4529 switch(regs.u.r8.al)
4531 case 0x20: // coded by osmaker aka K.J.
4532 if(regs.u.r32.edx == 0x534D4150)
4534 extended_memory_size = inb_cmos(0x35);
4535 extended_memory_size <<= 8;
4536 extended_memory_size |= inb_cmos(0x34);
4537 extended_memory_size *= 64;
4538 // greater than EFF00000???
4539 if(extended_memory_size > 0x3bc000) {
4540 extended_memory_size = 0x3bc000; // everything after this is reserved memory until we get to 0x100000000
4542 extended_memory_size *= 1024;
4543 extended_memory_size += (16L * 1024 * 1024);
4545 if(extended_memory_size <= (16L * 1024 * 1024)) {
4546 extended_memory_size = inb_cmos(0x31);
4547 extended_memory_size <<= 8;
4548 extended_memory_size |= inb_cmos(0x30);
4549 extended_memory_size *= 1024;
4550 extended_memory_size += (1L * 1024 * 1024);
4553 extra_lowbits_memory_size = inb_cmos(0x5c);
4554 extra_lowbits_memory_size <<= 8;
4555 extra_lowbits_memory_size |= inb_cmos(0x5b);
4556 extra_lowbits_memory_size *= 64;
4557 extra_lowbits_memory_size *= 1024;
4558 extra_highbits_memory_size = inb_cmos(0x5d);
4560 switch(regs.u.r16.bx)
4562 case 0:
4563 set_e820_range(ES, regs.u.r16.di,
4564 0x0000000L, 0x0009f000L, 0, 0, 1);
4565 regs.u.r32.ebx = 1;
4566 break;
4567 case 1:
4568 set_e820_range(ES, regs.u.r16.di,
4569 0x0009f000L, 0x000a0000L, 0, 0, 2);
4570 regs.u.r32.ebx = 2;
4571 break;
4572 case 2:
4573 set_e820_range(ES, regs.u.r16.di,
4574 0x000e8000L, 0x00100000L, 0, 0, 2);
4575 regs.u.r32.ebx = 3;
4576 break;
4577 case 3:
4578 #if BX_ROMBIOS32
4579 set_e820_range(ES, regs.u.r16.di,
4580 0x00100000L,
4581 extended_memory_size - ACPI_DATA_SIZE ,0, 0, 1);
4582 regs.u.r32.ebx = 4;
4583 #else
4584 set_e820_range(ES, regs.u.r16.di,
4585 0x00100000L,
4586 extended_memory_size, 1);
4587 regs.u.r32.ebx = 5;
4588 #endif
4589 break;
4590 case 4:
4591 set_e820_range(ES, regs.u.r16.di,
4592 extended_memory_size - ACPI_DATA_SIZE,
4593 extended_memory_size ,0, 0, 3); // ACPI RAM
4594 regs.u.r32.ebx = 5;
4595 break;
4596 case 5:
4597 /* 4 pages before the bios, 3 pages for vmx tss pages,
4598 * the other page for EPT real mode pagetable */
4599 set_e820_range(ES, regs.u.r16.di, 0xfffbc000L,
4600 0xfffc0000L, 0, 0, 2);
4601 regs.u.r32.ebx = 6;
4602 break;
4603 case 6:
4604 /* 256KB BIOS area at the end of 4 GB */
4605 set_e820_range(ES, regs.u.r16.di,
4606 0xfffc0000L, 0x00000000L ,0, 0, 2);
4607 if (extra_highbits_memory_size || extra_lowbits_memory_size)
4608 regs.u.r32.ebx = 7;
4609 else
4610 regs.u.r32.ebx = 0;
4611 break;
4612 case 7:
4613 /* Maping of memory above 4 GB */
4614 set_e820_range(ES, regs.u.r16.di, 0x00000000L,
4615 extra_lowbits_memory_size, 1, extra_highbits_memory_size
4616 + 1, 1);
4617 regs.u.r32.ebx = 0;
4618 break;
4619 default: /* AX=E820, DX=534D4150, BX unrecognized */
4620 goto int15_unimplemented;
4621 break;
4623 regs.u.r32.eax = 0x534D4150;
4624 regs.u.r32.ecx = 0x14;
4625 CLEAR_CF();
4626 } else {
4627 // if DX != 0x534D4150)
4628 goto int15_unimplemented;
4630 break;
4632 case 0x01:
4633 // do we have any reason to fail here ?
4634 CLEAR_CF();
4636 // my real system sets ax and bx to 0
4637 // this is confirmed by Ralph Brown list
4638 // but syslinux v1.48 is known to behave
4639 // strangely if ax is set to 0
4640 // regs.u.r16.ax = 0;
4641 // regs.u.r16.bx = 0;
4643 // Get the amount of extended memory (above 1M)
4644 regs.u.r8.cl = inb_cmos(0x30);
4645 regs.u.r8.ch = inb_cmos(0x31);
4647 // limit to 15M
4648 if(regs.u.r16.cx > 0x3c00)
4650 regs.u.r16.cx = 0x3c00;
4653 // Get the amount of extended memory above 16M in 64k blocs
4654 regs.u.r8.dl = inb_cmos(0x34);
4655 regs.u.r8.dh = inb_cmos(0x35);
4657 // Set configured memory equal to extended memory
4658 regs.u.r16.ax = regs.u.r16.cx;
4659 regs.u.r16.bx = regs.u.r16.dx;
4660 break;
4661 default: /* AH=0xE8?? but not implemented */
4662 goto int15_unimplemented;
4664 break;
4665 int15_unimplemented:
4666 // fall into the default
4667 default:
4668 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4669 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4670 SET_CF();
4671 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4672 break;
4676 void
4677 int16_function(DI, SI, BP, SP, BX, DX, CX, AX, FLAGS)
4678 Bit16u DI, SI, BP, SP, BX, DX, CX, AX, FLAGS;
4680 Bit8u scan_code, ascii_code, shift_flags, led_flags, count;
4681 Bit16u kbd_code, max;
4683 BX_DEBUG_INT16("int16: AX=%04x BX=%04x CX=%04x DX=%04x \n", AX, BX, CX, DX);
4685 shift_flags = read_byte(0x0040, 0x17);
4686 led_flags = read_byte(0x0040, 0x97);
4687 if ((((shift_flags >> 4) & 0x07) ^ (led_flags & 0x07)) != 0) {
4688 ASM_START
4690 ASM_END
4691 outb(0x60, 0xed);
4692 while ((inb(0x64) & 0x01) == 0) outb(0x80, 0x21);
4693 if ((inb(0x60) == 0xfa)) {
4694 led_flags &= 0xf8;
4695 led_flags |= ((shift_flags >> 4) & 0x07);
4696 outb(0x60, led_flags & 0x07);
4697 while ((inb(0x64) & 0x01) == 0) outb(0x80, 0x21);
4698 inb(0x60);
4699 write_byte(0x0040, 0x97, led_flags);
4701 ASM_START
4703 ASM_END
4706 switch (GET_AH()) {
4707 case 0x00: /* read keyboard input */
4709 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4710 BX_PANIC("KBD: int16h: out of keyboard input\n");
4712 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4713 else if (ascii_code == 0xE0) ascii_code = 0;
4714 AX = (scan_code << 8) | ascii_code;
4715 break;
4717 case 0x01: /* check keyboard status */
4718 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4719 SET_ZF();
4720 return;
4722 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4723 else if (ascii_code == 0xE0) ascii_code = 0;
4724 AX = (scan_code << 8) | ascii_code;
4725 CLEAR_ZF();
4726 break;
4728 case 0x02: /* get shift flag status */
4729 shift_flags = read_byte(0x0040, 0x17);
4730 SET_AL(shift_flags);
4731 break;
4733 case 0x05: /* store key-stroke into buffer */
4734 if ( !enqueue_key(GET_CH(), GET_CL()) ) {
4735 SET_AL(1);
4737 else {
4738 SET_AL(0);
4740 break;
4742 case 0x09: /* GET KEYBOARD FUNCTIONALITY */
4743 // bit Bochs Description
4744 // 7 0 reserved
4745 // 6 0 INT 16/AH=20h-22h supported (122-key keyboard support)
4746 // 5 1 INT 16/AH=10h-12h supported (enhanced keyboard support)
4747 // 4 1 INT 16/AH=0Ah supported
4748 // 3 0 INT 16/AX=0306h supported
4749 // 2 0 INT 16/AX=0305h supported
4750 // 1 0 INT 16/AX=0304h supported
4751 // 0 0 INT 16/AX=0300h supported
4753 SET_AL(0x30);
4754 break;
4756 case 0x0A: /* GET KEYBOARD ID */
4757 count = 2;
4758 kbd_code = 0x0;
4759 outb(0x60, 0xf2);
4760 /* Wait for data */
4761 max=0xffff;
4762 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4763 if (max>0x0) {
4764 if ((inb(0x60) == 0xfa)) {
4765 do {
4766 max=0xffff;
4767 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4768 if (max>0x0) {
4769 kbd_code >>= 8;
4770 kbd_code |= (inb(0x60) << 8);
4772 } while (--count>0);
4775 BX=kbd_code;
4776 break;
4778 case 0x10: /* read MF-II keyboard input */
4780 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4781 BX_PANIC("KBD: int16h: out of keyboard input\n");
4783 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4784 AX = (scan_code << 8) | ascii_code;
4785 break;
4787 case 0x11: /* check MF-II keyboard status */
4788 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4789 SET_ZF();
4790 return;
4792 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4793 AX = (scan_code << 8) | ascii_code;
4794 CLEAR_ZF();
4795 break;
4797 case 0x12: /* get extended keyboard status */
4798 shift_flags = read_byte(0x0040, 0x17);
4799 SET_AL(shift_flags);
4800 shift_flags = read_byte(0x0040, 0x18) & 0x73;
4801 shift_flags |= read_byte(0x0040, 0x96) & 0x0c;
4802 SET_AH(shift_flags);
4803 BX_DEBUG_INT16("int16: func 12 sending %04x\n",AX);
4804 break;
4806 case 0x92: /* keyboard capability check called by DOS 5.0+ keyb */
4807 SET_AH(0x80); // function int16 ah=0x10-0x12 supported
4808 break;
4810 case 0xA2: /* 122 keys capability check called by DOS 5.0+ keyb */
4811 // don't change AH : function int16 ah=0x20-0x22 NOT supported
4812 break;
4814 case 0x6F:
4815 if (GET_AL() == 0x08)
4816 SET_AH(0x02); // unsupported, aka normal keyboard
4818 default:
4819 BX_INFO("KBD: unsupported int 16h function %02x\n", GET_AH());
4823 unsigned int
4824 dequeue_key(scan_code, ascii_code, incr)
4825 Bit8u *scan_code;
4826 Bit8u *ascii_code;
4827 unsigned int incr;
4829 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail;
4830 Bit16u ss;
4831 Bit8u acode, scode;
4833 #if BX_CPU < 2
4834 buffer_start = 0x001E;
4835 buffer_end = 0x003E;
4836 #else
4837 buffer_start = read_word(0x0040, 0x0080);
4838 buffer_end = read_word(0x0040, 0x0082);
4839 #endif
4841 buffer_head = read_word(0x0040, 0x001a);
4842 buffer_tail = read_word(0x0040, 0x001c);
4844 if (buffer_head != buffer_tail) {
4845 ss = get_SS();
4846 acode = read_byte(0x0040, buffer_head);
4847 scode = read_byte(0x0040, buffer_head+1);
4848 write_byte(ss, ascii_code, acode);
4849 write_byte(ss, scan_code, scode);
4851 if (incr) {
4852 buffer_head += 2;
4853 if (buffer_head >= buffer_end)
4854 buffer_head = buffer_start;
4855 write_word(0x0040, 0x001a, buffer_head);
4857 return(1);
4859 else {
4860 return(0);
4864 static char panic_msg_keyb_buffer_full[] = "%s: keyboard input buffer full\n";
4866 Bit8u
4867 inhibit_mouse_int_and_events()
4869 Bit8u command_byte, prev_command_byte;
4871 // Turn off IRQ generation and aux data line
4872 if ( inb(0x64) & 0x02 )
4873 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4874 outb(0x64, 0x20); // get command byte
4875 while ( (inb(0x64) & 0x01) != 0x01 );
4876 prev_command_byte = inb(0x60);
4877 command_byte = prev_command_byte;
4878 //while ( (inb(0x64) & 0x02) );
4879 if ( inb(0x64) & 0x02 )
4880 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4881 command_byte &= 0xfd; // turn off IRQ 12 generation
4882 command_byte |= 0x20; // disable mouse serial clock line
4883 outb(0x64, 0x60); // write command byte
4884 outb(0x60, command_byte);
4885 return(prev_command_byte);
4888 void
4889 enable_mouse_int_and_events()
4891 Bit8u command_byte;
4893 // Turn on IRQ generation and aux data line
4894 if ( inb(0x64) & 0x02 )
4895 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4896 outb(0x64, 0x20); // get command byte
4897 while ( (inb(0x64) & 0x01) != 0x01 );
4898 command_byte = inb(0x60);
4899 //while ( (inb(0x64) & 0x02) );
4900 if ( inb(0x64) & 0x02 )
4901 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4902 command_byte |= 0x02; // turn on IRQ 12 generation
4903 command_byte &= 0xdf; // enable mouse serial clock line
4904 outb(0x64, 0x60); // write command byte
4905 outb(0x60, command_byte);
4908 Bit8u
4909 send_to_mouse_ctrl(sendbyte)
4910 Bit8u sendbyte;
4912 Bit8u response;
4914 // wait for chance to write to ctrl
4915 if ( inb(0x64) & 0x02 )
4916 BX_PANIC(panic_msg_keyb_buffer_full,"sendmouse");
4917 outb(0x64, 0xD4);
4918 outb(0x60, sendbyte);
4919 return(0);
4923 Bit8u
4924 get_mouse_data(data)
4925 Bit8u *data;
4927 Bit8u response;
4928 Bit16u ss;
4930 while ( (inb(0x64) & 0x21) != 0x21 ) {
4933 response = inb(0x60);
4935 ss = get_SS();
4936 write_byte(ss, data, response);
4937 return(0);
4940 void
4941 set_kbd_command_byte(command_byte)
4942 Bit8u command_byte;
4944 if ( inb(0x64) & 0x02 )
4945 BX_PANIC(panic_msg_keyb_buffer_full,"setkbdcomm");
4946 outb(0x64, 0xD4);
4948 outb(0x64, 0x60); // write command byte
4949 outb(0x60, command_byte);
4952 void
4953 int09_function(DI, SI, BP, SP, BX, DX, CX, AX)
4954 Bit16u DI, SI, BP, SP, BX, DX, CX, AX;
4956 Bit8u scancode, asciicode, shift_flags;
4957 Bit8u mf2_flags, mf2_state;
4960 // DS has been set to F000 before call
4964 scancode = GET_AL();
4966 if (scancode == 0) {
4967 BX_INFO("KBD: int09 handler: AL=0\n");
4968 return;
4972 shift_flags = read_byte(0x0040, 0x17);
4973 mf2_flags = read_byte(0x0040, 0x18);
4974 mf2_state = read_byte(0x0040, 0x96);
4975 asciicode = 0;
4977 switch (scancode) {
4978 case 0x3a: /* Caps Lock press */
4979 shift_flags ^= 0x40;
4980 write_byte(0x0040, 0x17, shift_flags);
4981 mf2_flags |= 0x40;
4982 write_byte(0x0040, 0x18, mf2_flags);
4983 break;
4984 case 0xba: /* Caps Lock release */
4985 mf2_flags &= ~0x40;
4986 write_byte(0x0040, 0x18, mf2_flags);
4987 break;
4989 case 0x2a: /* L Shift press */
4990 shift_flags |= 0x02;
4991 write_byte(0x0040, 0x17, shift_flags);
4992 break;
4993 case 0xaa: /* L Shift release */
4994 shift_flags &= ~0x02;
4995 write_byte(0x0040, 0x17, shift_flags);
4996 break;
4998 case 0x36: /* R Shift press */
4999 shift_flags |= 0x01;
5000 write_byte(0x0040, 0x17, shift_flags);
5001 break;
5002 case 0xb6: /* R Shift release */
5003 shift_flags &= ~0x01;
5004 write_byte(0x0040, 0x17, shift_flags);
5005 break;
5007 case 0x1d: /* Ctrl press */
5008 if ((mf2_state & 0x01) == 0) {
5009 shift_flags |= 0x04;
5010 write_byte(0x0040, 0x17, shift_flags);
5011 if (mf2_state & 0x02) {
5012 mf2_state |= 0x04;
5013 write_byte(0x0040, 0x96, mf2_state);
5014 } else {
5015 mf2_flags |= 0x01;
5016 write_byte(0x0040, 0x18, mf2_flags);
5019 break;
5020 case 0x9d: /* Ctrl release */
5021 if ((mf2_state & 0x01) == 0) {
5022 shift_flags &= ~0x04;
5023 write_byte(0x0040, 0x17, shift_flags);
5024 if (mf2_state & 0x02) {
5025 mf2_state &= ~0x04;
5026 write_byte(0x0040, 0x96, mf2_state);
5027 } else {
5028 mf2_flags &= ~0x01;
5029 write_byte(0x0040, 0x18, mf2_flags);
5032 break;
5034 case 0x38: /* Alt press */
5035 shift_flags |= 0x08;
5036 write_byte(0x0040, 0x17, shift_flags);
5037 if (mf2_state & 0x02) {
5038 mf2_state |= 0x08;
5039 write_byte(0x0040, 0x96, mf2_state);
5040 } else {
5041 mf2_flags |= 0x02;
5042 write_byte(0x0040, 0x18, mf2_flags);
5044 break;
5045 case 0xb8: /* Alt release */
5046 shift_flags &= ~0x08;
5047 write_byte(0x0040, 0x17, shift_flags);
5048 if (mf2_state & 0x02) {
5049 mf2_state &= ~0x08;
5050 write_byte(0x0040, 0x96, mf2_state);
5051 } else {
5052 mf2_flags &= ~0x02;
5053 write_byte(0x0040, 0x18, mf2_flags);
5055 break;
5057 case 0x45: /* Num Lock press */
5058 if ((mf2_state & 0x03) == 0) {
5059 mf2_flags |= 0x20;
5060 write_byte(0x0040, 0x18, mf2_flags);
5061 shift_flags ^= 0x20;
5062 write_byte(0x0040, 0x17, shift_flags);
5064 break;
5065 case 0xc5: /* Num Lock release */
5066 if ((mf2_state & 0x03) == 0) {
5067 mf2_flags &= ~0x20;
5068 write_byte(0x0040, 0x18, mf2_flags);
5070 break;
5072 case 0x46: /* Scroll Lock press */
5073 mf2_flags |= 0x10;
5074 write_byte(0x0040, 0x18, mf2_flags);
5075 shift_flags ^= 0x10;
5076 write_byte(0x0040, 0x17, shift_flags);
5077 break;
5079 case 0xc6: /* Scroll Lock release */
5080 mf2_flags &= ~0x10;
5081 write_byte(0x0040, 0x18, mf2_flags);
5082 break;
5084 default:
5085 if (scancode & 0x80) {
5086 break; /* toss key releases ... */
5088 if (scancode > MAX_SCAN_CODE) {
5089 BX_INFO("KBD: int09h_handler(): unknown scancode read: 0x%02x!\n", scancode);
5090 return;
5092 if (shift_flags & 0x08) { /* ALT */
5093 asciicode = scan_to_scanascii[scancode].alt;
5094 scancode = scan_to_scanascii[scancode].alt >> 8;
5095 } else if (shift_flags & 0x04) { /* CONTROL */
5096 asciicode = scan_to_scanascii[scancode].control;
5097 scancode = scan_to_scanascii[scancode].control >> 8;
5098 } else if (((mf2_state & 0x02) > 0) && ((scancode >= 0x47) && (scancode <= 0x53))) {
5099 /* extended keys handling */
5100 asciicode = 0xe0;
5101 scancode = scan_to_scanascii[scancode].normal >> 8;
5102 } else if (shift_flags & 0x03) { /* LSHIFT + RSHIFT */
5103 /* check if lock state should be ignored
5104 * because a SHIFT key are pressed */
5106 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
5107 asciicode = scan_to_scanascii[scancode].normal;
5108 scancode = scan_to_scanascii[scancode].normal >> 8;
5109 } else {
5110 asciicode = scan_to_scanascii[scancode].shift;
5111 scancode = scan_to_scanascii[scancode].shift >> 8;
5113 } else {
5114 /* check if lock is on */
5115 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
5116 asciicode = scan_to_scanascii[scancode].shift;
5117 scancode = scan_to_scanascii[scancode].shift >> 8;
5118 } else {
5119 asciicode = scan_to_scanascii[scancode].normal;
5120 scancode = scan_to_scanascii[scancode].normal >> 8;
5123 if (scancode==0 && asciicode==0) {
5124 BX_INFO("KBD: int09h_handler(): scancode & asciicode are zero?\n");
5126 enqueue_key(scancode, asciicode);
5127 break;
5129 if ((scancode & 0x7f) != 0x1d) {
5130 mf2_state &= ~0x01;
5132 mf2_state &= ~0x02;
5133 write_byte(0x0040, 0x96, mf2_state);
5136 unsigned int
5137 enqueue_key(scan_code, ascii_code)
5138 Bit8u scan_code, ascii_code;
5140 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail, temp_tail;
5142 #if BX_CPU < 2
5143 buffer_start = 0x001E;
5144 buffer_end = 0x003E;
5145 #else
5146 buffer_start = read_word(0x0040, 0x0080);
5147 buffer_end = read_word(0x0040, 0x0082);
5148 #endif
5150 buffer_head = read_word(0x0040, 0x001A);
5151 buffer_tail = read_word(0x0040, 0x001C);
5153 temp_tail = buffer_tail;
5154 buffer_tail += 2;
5155 if (buffer_tail >= buffer_end)
5156 buffer_tail = buffer_start;
5158 if (buffer_tail == buffer_head) {
5159 return(0);
5162 write_byte(0x0040, temp_tail, ascii_code);
5163 write_byte(0x0040, temp_tail+1, scan_code);
5164 write_word(0x0040, 0x001C, buffer_tail);
5165 return(1);
5169 void
5170 int74_function(make_farcall, Z, Y, X, status)
5171 Bit16u make_farcall, Z, Y, X, status;
5173 Bit16u ebda_seg=read_word(0x0040,0x000E);
5174 Bit8u in_byte, index, package_count;
5175 Bit8u mouse_flags_1, mouse_flags_2;
5177 BX_DEBUG_INT74("entering int74_function\n");
5178 make_farcall = 0;
5180 in_byte = inb(0x64);
5181 if ( (in_byte & 0x21) != 0x21 ) {
5182 return;
5184 in_byte = inb(0x60);
5185 BX_DEBUG_INT74("int74: read byte %02x\n", in_byte);
5187 mouse_flags_1 = read_byte(ebda_seg, 0x0026);
5188 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
5190 if ( (mouse_flags_2 & 0x80) != 0x80 ) {
5191 return;
5194 package_count = mouse_flags_2 & 0x07;
5195 index = mouse_flags_1 & 0x07;
5196 write_byte(ebda_seg, 0x28 + index, in_byte);
5198 if ( (index+1) >= package_count ) {
5199 BX_DEBUG_INT74("int74_function: make_farcall=1\n");
5200 status = read_byte(ebda_seg, 0x0028 + 0);
5201 X = read_byte(ebda_seg, 0x0028 + 1);
5202 Y = read_byte(ebda_seg, 0x0028 + 2);
5203 Z = 0;
5204 mouse_flags_1 = 0;
5205 // check if far call handler installed
5206 if (mouse_flags_2 & 0x80)
5207 make_farcall = 1;
5209 else {
5210 mouse_flags_1++;
5212 write_byte(ebda_seg, 0x0026, mouse_flags_1);
5215 #define SET_DISK_RET_STATUS(status) write_byte(0x0040, 0x0074, status)
5217 #if BX_USE_ATADRV
5219 void
5220 int13_harddisk(EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
5221 Bit16u EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
5223 Bit32u lba_low, lba_high;
5224 Bit16u ebda_seg=read_word(0x0040,0x000E);
5225 Bit16u cylinder, head, sector;
5226 Bit16u segment, offset;
5227 Bit16u npc, nph, npspt, nlc, nlh, nlspt;
5228 Bit16u size, count;
5229 Bit8u device, status;
5231 BX_DEBUG_INT13_HD("int13_harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5233 write_byte(0x0040, 0x008e, 0); // clear completion flag
5235 // basic check : device has to be defined
5236 if ( (GET_ELDL() < 0x80) || (GET_ELDL() >= 0x80 + BX_MAX_ATA_DEVICES) ) {
5237 BX_INFO("int13_harddisk: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
5238 goto int13_fail;
5241 // Get the ata channel
5242 device=read_byte(ebda_seg,&EbdaData->ata.hdidmap[GET_ELDL()-0x80]);
5244 // basic check : device has to be valid
5245 if (device >= BX_MAX_ATA_DEVICES) {
5246 BX_INFO("int13_harddisk: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
5247 goto int13_fail;
5250 switch (GET_AH()) {
5252 case 0x00: /* disk controller reset */
5253 ata_reset (device);
5254 goto int13_success;
5255 break;
5257 case 0x01: /* read disk status */
5258 status = read_byte(0x0040, 0x0074);
5259 SET_AH(status);
5260 SET_DISK_RET_STATUS(0);
5261 /* set CF if error status read */
5262 if (status) goto int13_fail_nostatus;
5263 else goto int13_success_noah;
5264 break;
5266 case 0x02: // read disk sectors
5267 case 0x03: // write disk sectors
5268 case 0x04: // verify disk sectors
5270 count = GET_AL();
5271 cylinder = GET_CH();
5272 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
5273 sector = (GET_CL() & 0x3f);
5274 head = GET_DH();
5276 segment = ES;
5277 offset = BX;
5279 if ((count > 128) || (count == 0) || (sector == 0)) {
5280 BX_INFO("int13_harddisk: function %02x, parameter out of range!\n",GET_AH());
5281 goto int13_fail;
5284 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
5285 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
5286 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
5288 // sanity check on cyl heads, sec
5289 if( (cylinder >= nlc) || (head >= nlh) || (sector > nlspt )) {
5290 BX_INFO("int13_harddisk: function %02x, parameters out of range %04x/%04x/%04x!\n", GET_AH(), cylinder, head, sector);
5291 goto int13_fail;
5294 // FIXME verify
5295 if ( GET_AH() == 0x04 ) goto int13_success;
5297 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
5298 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
5300 // if needed, translate lchs to lba, and execute command
5301 if ( (nph != nlh) || (npspt != nlspt)) {
5302 lba_low = ((((Bit32u)cylinder * (Bit32u)nlh) + (Bit32u)head) * (Bit32u)nlspt) + (Bit32u)sector - 1;
5303 lba_high = 0;
5304 sector = 0; // this forces the command to be lba
5307 if ( GET_AH() == 0x02 )
5308 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, cylinder, head, sector, lba_low, lba_high, segment, offset);
5309 else
5310 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, cylinder, head, sector, lba_low, lba_high, segment, offset);
5312 // Set nb of sector transferred
5313 SET_AL(read_word(ebda_seg, &EbdaData->ata.trsfsectors));
5315 if (status != 0) {
5316 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
5317 SET_AH(0x0c);
5318 goto int13_fail_noah;
5321 goto int13_success;
5322 break;
5324 case 0x05: /* format disk track */
5325 BX_INFO("format disk track called\n");
5326 goto int13_success;
5327 return;
5328 break;
5330 case 0x08: /* read disk drive parameters */
5332 // Get logical geometry from table
5333 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
5334 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
5335 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
5336 count = read_byte(ebda_seg, &EbdaData->ata.hdcount);
5338 nlc = nlc - 2; /* 0 based , last sector not used */
5339 SET_AL(0);
5340 SET_CH(nlc & 0xff);
5341 SET_CL(((nlc >> 2) & 0xc0) | (nlspt & 0x3f));
5342 SET_DH(nlh - 1);
5343 SET_DL(count); /* FIXME returns 0, 1, or n hard drives */
5345 // FIXME should set ES & DI
5347 goto int13_success;
5348 break;
5350 case 0x10: /* check drive ready */
5351 // should look at 40:8E also???
5353 // Read the status from controller
5354 status = inb(read_word(ebda_seg, &EbdaData->ata.channels[device/2].iobase1) + ATA_CB_STAT);
5355 if ( (status & ( ATA_CB_STAT_BSY | ATA_CB_STAT_RDY )) == ATA_CB_STAT_RDY ) {
5356 goto int13_success;
5358 else {
5359 SET_AH(0xAA);
5360 goto int13_fail_noah;
5362 break;
5364 case 0x15: /* read disk drive size */
5366 // Get logical geometry from table
5367 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
5368 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
5369 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
5371 // Compute sector count seen by int13
5372 lba_low = (Bit32u)(nlc - 1) * (Bit32u)nlh * (Bit32u)nlspt;
5373 CX = lba_low >> 16;
5374 DX = lba_low & 0xffff;
5376 SET_AH(3); // hard disk accessible
5377 goto int13_success_noah;
5378 break;
5380 case 0x41: // IBM/MS installation check
5381 BX=0xaa55; // install check
5382 SET_AH(0x30); // EDD 3.0
5383 CX=0x0007; // ext disk access and edd, removable supported
5384 goto int13_success_noah;
5385 break;
5387 case 0x42: // IBM/MS extended read
5388 case 0x43: // IBM/MS extended write
5389 case 0x44: // IBM/MS verify
5390 case 0x47: // IBM/MS extended seek
5392 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
5393 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
5394 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
5396 // Get 32 msb lba and check
5397 lba_high=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
5398 if (lba_high > read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_high) ) {
5399 BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH());
5400 goto int13_fail;
5403 // Get 32 lsb lba and check
5404 lba_low=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5405 if (lba_high == read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_high)
5406 && lba_low >= read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_low) ) {
5407 BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH());
5408 goto int13_fail;
5411 // If verify or seek
5412 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
5413 goto int13_success;
5415 // Execute the command
5416 if ( GET_AH() == 0x42 )
5417 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, 0, 0, 0, lba_low, lba_high, segment, offset);
5418 else
5419 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, 0, 0, 0, lba_low, lba_high, segment, offset);
5421 count=read_word(ebda_seg, &EbdaData->ata.trsfsectors);
5422 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5424 if (status != 0) {
5425 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
5426 SET_AH(0x0c);
5427 goto int13_fail_noah;
5430 goto int13_success;
5431 break;
5433 case 0x45: // IBM/MS lock/unlock drive
5434 case 0x49: // IBM/MS extended media change
5435 goto int13_success; // Always success for HD
5436 break;
5438 case 0x46: // IBM/MS eject media
5439 SET_AH(0xb2); // Volume Not Removable
5440 goto int13_fail_noah; // Always fail for HD
5441 break;
5443 case 0x48: // IBM/MS get drive parameters
5444 size=read_word(DS,SI+(Bit16u)&Int13DPT->size);
5446 // Buffer is too small
5447 if(size < 0x1a)
5448 goto int13_fail;
5450 // EDD 1.x
5451 if(size >= 0x1a) {
5452 Bit16u blksize;
5454 npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
5455 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
5456 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
5457 lba_low = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_low);
5458 lba_high = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_high);
5459 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5461 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5462 if (lba_high || (lba_low/npspt)/nph > 0x3fff)
5464 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x00); // geometry is invalid
5465 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0x3fff);
5467 else
5469 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x02); // geometry is valid
5470 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, (Bit32u)npc);
5472 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, (Bit32u)nph);
5473 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, (Bit32u)npspt);
5474 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, lba_low);
5475 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, lba_high);
5476 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5479 // EDD 2.x
5480 if(size >= 0x1e) {
5481 Bit8u channel, dev, irq, mode, checksum, i, translation;
5482 Bit16u iobase1, iobase2, options;
5484 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5486 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5487 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5489 // Fill in dpte
5490 channel = device / 2;
5491 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5492 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5493 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5494 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5495 translation = read_byte(ebda_seg, &EbdaData->ata.devices[device].translation);
5497 options = (translation==ATA_TRANSLATION_NONE?0:1)<<3; // chs translation
5498 options |= (1<<4); // lba translation
5499 options |= (mode==ATA_MODE_PIO32?1:0)<<7;
5500 options |= (translation==ATA_TRANSLATION_LBA?1:0)<<9;
5501 options |= (translation==ATA_TRANSLATION_RECHS?3:0)<<9;
5503 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5504 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2 + ATA_CB_DC);
5505 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5506 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5507 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5508 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5509 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5510 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5511 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5512 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5513 if (size >=0x42)
5514 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5515 else
5516 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x10);
5518 checksum=0;
5519 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, ((Bit8u*)(&EbdaData->ata.dpte)) + i);
5520 checksum = ~checksum;
5521 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5524 // EDD 3.x
5525 if(size >= 0x42) {
5526 Bit8u channel, iface, checksum, i;
5527 Bit16u iobase1;
5529 channel = device / 2;
5530 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5531 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5533 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5534 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5535 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5536 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5537 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5539 if (iface==ATA_IFACE_ISA) {
5540 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5541 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5542 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5543 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5545 else {
5546 // FIXME PCI
5548 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5549 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5550 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5551 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5553 if (iface==ATA_IFACE_ISA) {
5554 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5555 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5556 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5558 else {
5559 // FIXME PCI
5561 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5562 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5563 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5564 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5566 checksum=0;
5567 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5568 checksum = ~checksum;
5569 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5572 goto int13_success;
5573 break;
5575 case 0x4e: // // IBM/MS set hardware configuration
5576 // DMA, prefetch, PIO maximum not supported
5577 switch (GET_AL()) {
5578 case 0x01:
5579 case 0x03:
5580 case 0x04:
5581 case 0x06:
5582 goto int13_success;
5583 break;
5584 default :
5585 goto int13_fail;
5587 break;
5589 case 0x09: /* initialize drive parameters */
5590 case 0x0c: /* seek to specified cylinder */
5591 case 0x0d: /* alternate disk reset */
5592 case 0x11: /* recalibrate */
5593 case 0x14: /* controller internal diagnostic */
5594 BX_INFO("int13_harddisk: function %02xh unimplemented, returns success\n", GET_AH());
5595 goto int13_success;
5596 break;
5598 case 0x0a: /* read disk sectors with ECC */
5599 case 0x0b: /* write disk sectors with ECC */
5600 case 0x18: // set media type for format
5601 case 0x50: // IBM/MS send packet command
5602 default:
5603 BX_INFO("int13_harddisk: function %02xh unsupported, returns fail\n", GET_AH());
5604 goto int13_fail;
5605 break;
5608 int13_fail:
5609 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5610 int13_fail_noah:
5611 SET_DISK_RET_STATUS(GET_AH());
5612 int13_fail_nostatus:
5613 SET_CF(); // error occurred
5614 return;
5616 int13_success:
5617 SET_AH(0x00); // no error
5618 int13_success_noah:
5619 SET_DISK_RET_STATUS(0x00);
5620 CLEAR_CF(); // no error
5621 return;
5624 // ---------------------------------------------------------------------------
5625 // Start of int13 for cdrom
5626 // ---------------------------------------------------------------------------
5628 void
5629 int13_cdrom(EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
5630 Bit16u EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
5632 Bit16u ebda_seg=read_word(0x0040,0x000E);
5633 Bit8u device, status, locks;
5634 Bit8u atacmd[12];
5635 Bit32u lba;
5636 Bit16u count, segment, offset, i, size;
5638 BX_DEBUG_INT13_CD("int13_cdrom: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5640 SET_DISK_RET_STATUS(0x00);
5642 /* basic check : device should be 0xE0+ */
5643 if( (GET_ELDL() < 0xE0) || (GET_ELDL() >= 0xE0+BX_MAX_ATA_DEVICES) ) {
5644 BX_INFO("int13_cdrom: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
5645 goto int13_fail;
5648 // Get the ata channel
5649 device=read_byte(ebda_seg,&EbdaData->ata.cdidmap[GET_ELDL()-0xE0]);
5651 /* basic check : device has to be valid */
5652 if (device >= BX_MAX_ATA_DEVICES) {
5653 BX_INFO("int13_cdrom: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
5654 goto int13_fail;
5657 switch (GET_AH()) {
5659 // all those functions return SUCCESS
5660 case 0x00: /* disk controller reset */
5661 case 0x09: /* initialize drive parameters */
5662 case 0x0c: /* seek to specified cylinder */
5663 case 0x0d: /* alternate disk reset */
5664 case 0x10: /* check drive ready */
5665 case 0x11: /* recalibrate */
5666 case 0x14: /* controller internal diagnostic */
5667 case 0x16: /* detect disk change */
5668 goto int13_success;
5669 break;
5671 // all those functions return disk write-protected
5672 case 0x03: /* write disk sectors */
5673 case 0x05: /* format disk track */
5674 case 0x43: // IBM/MS extended write
5675 SET_AH(0x03);
5676 goto int13_fail_noah;
5677 break;
5679 case 0x01: /* read disk status */
5680 status = read_byte(0x0040, 0x0074);
5681 SET_AH(status);
5682 SET_DISK_RET_STATUS(0);
5684 /* set CF if error status read */
5685 if (status) goto int13_fail_nostatus;
5686 else goto int13_success_noah;
5687 break;
5689 case 0x15: /* read disk drive size */
5690 SET_AH(0x02);
5691 goto int13_fail_noah;
5692 break;
5694 case 0x41: // IBM/MS installation check
5695 BX=0xaa55; // install check
5696 SET_AH(0x30); // EDD 2.1
5697 CX=0x0007; // ext disk access, removable and edd
5698 goto int13_success_noah;
5699 break;
5701 case 0x42: // IBM/MS extended read
5702 case 0x44: // IBM/MS verify sectors
5703 case 0x47: // IBM/MS extended seek
5705 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
5706 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
5707 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
5709 // Can't use 64 bits lba
5710 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
5711 if (lba != 0L) {
5712 BX_PANIC("int13_cdrom: function %02x. Can't use 64bits lba\n",GET_AH());
5713 goto int13_fail;
5716 // Get 32 bits lba
5717 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5719 // If verify or seek
5720 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
5721 goto int13_success;
5723 memsetb(get_SS(),atacmd,0,12);
5724 atacmd[0]=0x28; // READ command
5725 atacmd[7]=(count & 0xff00) >> 8; // Sectors
5726 atacmd[8]=(count & 0x00ff); // Sectors
5727 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
5728 atacmd[3]=(lba & 0x00ff0000) >> 16;
5729 atacmd[4]=(lba & 0x0000ff00) >> 8;
5730 atacmd[5]=(lba & 0x000000ff);
5731 status = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, count*2048L, ATA_DATA_IN, segment,offset);
5733 count = (Bit16u)(read_dword(ebda_seg, &EbdaData->ata.trsfbytes) >> 11);
5734 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5736 if (status != 0) {
5737 BX_INFO("int13_cdrom: function %02x, status %02x !\n",GET_AH(),status);
5738 SET_AH(0x0c);
5739 goto int13_fail_noah;
5742 goto int13_success;
5743 break;
5745 case 0x45: // IBM/MS lock/unlock drive
5746 if (GET_AL() > 2) goto int13_fail;
5748 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5750 switch (GET_AL()) {
5751 case 0 : // lock
5752 if (locks == 0xff) {
5753 SET_AH(0xb4);
5754 SET_AL(1);
5755 goto int13_fail_noah;
5757 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, ++locks);
5758 SET_AL(1);
5759 break;
5760 case 1 : // unlock
5761 if (locks == 0x00) {
5762 SET_AH(0xb0);
5763 SET_AL(0);
5764 goto int13_fail_noah;
5766 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, --locks);
5767 SET_AL(locks==0?0:1);
5768 break;
5769 case 2 : // status
5770 SET_AL(locks==0?0:1);
5771 break;
5773 goto int13_success;
5774 break;
5776 case 0x46: // IBM/MS eject media
5777 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5779 if (locks != 0) {
5780 SET_AH(0xb1); // media locked
5781 goto int13_fail_noah;
5783 // FIXME should handle 0x31 no media in device
5784 // FIXME should handle 0xb5 valid request failed
5786 // Call removable media eject
5787 ASM_START
5788 push bp
5789 mov bp, sp
5791 mov ah, #0x52
5792 int #0x15
5793 mov _int13_cdrom.status + 2[bp], ah
5794 jnc int13_cdrom_rme_end
5795 mov _int13_cdrom.status, #1
5796 int13_cdrom_rme_end:
5797 pop bp
5798 ASM_END
5800 if (status != 0) {
5801 SET_AH(0xb1); // media locked
5802 goto int13_fail_noah;
5805 goto int13_success;
5806 break;
5808 case 0x48: // IBM/MS get drive parameters
5809 size = read_word(DS,SI+(Bit16u)&Int13Ext->size);
5811 // Buffer is too small
5812 if(size < 0x1a)
5813 goto int13_fail;
5815 // EDD 1.x
5816 if(size >= 0x1a) {
5817 Bit16u cylinders, heads, spt, blksize;
5819 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5821 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5822 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x74); // removable, media change, lockable, max values
5823 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0xffffffff);
5824 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, 0xffffffff);
5825 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, 0xffffffff);
5826 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, 0xffffffff); // FIXME should be Bit64
5827 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0xffffffff);
5828 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5831 // EDD 2.x
5832 if(size >= 0x1e) {
5833 Bit8u channel, dev, irq, mode, checksum, i;
5834 Bit16u iobase1, iobase2, options;
5836 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5838 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5839 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5841 // Fill in dpte
5842 channel = device / 2;
5843 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5844 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5845 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5846 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5848 // FIXME atapi device
5849 options = (1<<4); // lba translation
5850 options |= (1<<5); // removable device
5851 options |= (1<<6); // atapi device
5852 options |= (mode==ATA_MODE_PIO32?1:0<<7);
5854 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5855 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2 + ATA_CB_DC);
5856 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5857 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5858 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5859 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5860 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5861 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5862 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5863 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5864 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5866 checksum=0;
5867 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, ((Bit8u*)(&EbdaData->ata.dpte)) + i);
5868 checksum = ~checksum;
5869 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5872 // EDD 3.x
5873 if(size >= 0x42) {
5874 Bit8u channel, iface, checksum, i;
5875 Bit16u iobase1;
5877 channel = device / 2;
5878 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5879 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5881 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5882 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5883 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5884 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5885 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5887 if (iface==ATA_IFACE_ISA) {
5888 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5889 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5890 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5891 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5893 else {
5894 // FIXME PCI
5896 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5897 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5898 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5899 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5901 if (iface==ATA_IFACE_ISA) {
5902 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5903 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5904 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5906 else {
5907 // FIXME PCI
5909 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5910 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5911 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5912 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5914 checksum=0;
5915 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5916 checksum = ~checksum;
5917 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5920 goto int13_success;
5921 break;
5923 case 0x49: // IBM/MS extended media change
5924 // always send changed ??
5925 SET_AH(06);
5926 goto int13_fail_nostatus;
5927 break;
5929 case 0x4e: // // IBM/MS set hardware configuration
5930 // DMA, prefetch, PIO maximum not supported
5931 switch (GET_AL()) {
5932 case 0x01:
5933 case 0x03:
5934 case 0x04:
5935 case 0x06:
5936 goto int13_success;
5937 break;
5938 default :
5939 goto int13_fail;
5941 break;
5943 // all those functions return unimplemented
5944 case 0x02: /* read sectors */
5945 case 0x04: /* verify sectors */
5946 case 0x08: /* read disk drive parameters */
5947 case 0x0a: /* read disk sectors with ECC */
5948 case 0x0b: /* write disk sectors with ECC */
5949 case 0x18: /* set media type for format */
5950 case 0x50: // ? - send packet command
5951 default:
5952 BX_INFO("int13_cdrom: unsupported AH=%02x\n", GET_AH());
5953 goto int13_fail;
5954 break;
5957 int13_fail:
5958 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5959 int13_fail_noah:
5960 SET_DISK_RET_STATUS(GET_AH());
5961 int13_fail_nostatus:
5962 SET_CF(); // error occurred
5963 return;
5965 int13_success:
5966 SET_AH(0x00); // no error
5967 int13_success_noah:
5968 SET_DISK_RET_STATUS(0x00);
5969 CLEAR_CF(); // no error
5970 return;
5973 // ---------------------------------------------------------------------------
5974 // End of int13 for cdrom
5975 // ---------------------------------------------------------------------------
5977 #if BX_ELTORITO_BOOT
5978 // ---------------------------------------------------------------------------
5979 // Start of int13 for eltorito functions
5980 // ---------------------------------------------------------------------------
5982 void
5983 int13_eltorito(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
5984 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
5986 Bit16u ebda_seg=read_word(0x0040,0x000E);
5988 BX_DEBUG_INT13_ET("int13_eltorito: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5989 // BX_DEBUG_INT13_ET("int13_eltorito: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
5991 switch (GET_AH()) {
5993 // FIXME ElTorito Various. Should be implemented
5994 case 0x4a: // ElTorito - Initiate disk emu
5995 case 0x4c: // ElTorito - Initiate disk emu and boot
5996 case 0x4d: // ElTorito - Return Boot catalog
5997 BX_PANIC("Int13 eltorito call with AX=%04x. Please report\n",AX);
5998 goto int13_fail;
5999 break;
6001 case 0x4b: // ElTorito - Terminate disk emu
6002 // FIXME ElTorito Hardcoded
6003 write_byte(DS,SI+0x00,0x13);
6004 write_byte(DS,SI+0x01,read_byte(ebda_seg,&EbdaData->cdemu.media));
6005 write_byte(DS,SI+0x02,read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
6006 write_byte(DS,SI+0x03,read_byte(ebda_seg,&EbdaData->cdemu.controller_index));
6007 write_dword(DS,SI+0x04,read_dword(ebda_seg,&EbdaData->cdemu.ilba));
6008 write_word(DS,SI+0x08,read_word(ebda_seg,&EbdaData->cdemu.device_spec));
6009 write_word(DS,SI+0x0a,read_word(ebda_seg,&EbdaData->cdemu.buffer_segment));
6010 write_word(DS,SI+0x0c,read_word(ebda_seg,&EbdaData->cdemu.load_segment));
6011 write_word(DS,SI+0x0e,read_word(ebda_seg,&EbdaData->cdemu.sector_count));
6012 write_byte(DS,SI+0x10,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.cylinders));
6013 write_byte(DS,SI+0x11,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.spt));
6014 write_byte(DS,SI+0x12,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.heads));
6016 // If we have to terminate emulation
6017 if(GET_AL() == 0x00) {
6018 // FIXME ElTorito Various. Should be handled accordingly to spec
6019 write_byte(ebda_seg,&EbdaData->cdemu.active, 0x00); // bye bye
6022 goto int13_success;
6023 break;
6025 default:
6026 BX_INFO("int13_eltorito: unsupported AH=%02x\n", GET_AH());
6027 goto int13_fail;
6028 break;
6031 int13_fail:
6032 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
6033 SET_DISK_RET_STATUS(GET_AH());
6034 SET_CF(); // error occurred
6035 return;
6037 int13_success:
6038 SET_AH(0x00); // no error
6039 SET_DISK_RET_STATUS(0x00);
6040 CLEAR_CF(); // no error
6041 return;
6044 // ---------------------------------------------------------------------------
6045 // End of int13 for eltorito functions
6046 // ---------------------------------------------------------------------------
6048 // ---------------------------------------------------------------------------
6049 // Start of int13 when emulating a device from the cd
6050 // ---------------------------------------------------------------------------
6052 void
6053 int13_cdemu(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
6054 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
6056 Bit16u ebda_seg=read_word(0x0040,0x000E);
6057 Bit8u device, status;
6058 Bit16u vheads, vspt, vcylinders;
6059 Bit16u head, sector, cylinder, nbsectors;
6060 Bit32u vlba, ilba, slba, elba;
6061 Bit16u before, segment, offset;
6062 Bit8u atacmd[12];
6064 BX_DEBUG_INT13_ET("int13_cdemu: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
6066 /* at this point, we are emulating a floppy/harddisk */
6068 // Recompute the device number
6069 device = read_byte(ebda_seg,&EbdaData->cdemu.controller_index) * 2;
6070 device += read_byte(ebda_seg,&EbdaData->cdemu.device_spec);
6072 SET_DISK_RET_STATUS(0x00);
6074 /* basic checks : emulation should be active, dl should equal the emulated drive */
6075 if( (read_byte(ebda_seg,&EbdaData->cdemu.active) ==0 )
6076 || (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive ) != GET_DL())) {
6077 BX_INFO("int13_cdemu: function %02x, emulation not active for DL= %02x\n", GET_AH(), GET_DL());
6078 goto int13_fail;
6081 switch (GET_AH()) {
6083 // all those functions return SUCCESS
6084 case 0x00: /* disk controller reset */
6085 case 0x09: /* initialize drive parameters */
6086 case 0x0c: /* seek to specified cylinder */
6087 case 0x0d: /* alternate disk reset */ // FIXME ElTorito Various. should really reset ?
6088 case 0x10: /* check drive ready */ // FIXME ElTorito Various. should check if ready ?
6089 case 0x11: /* recalibrate */
6090 case 0x14: /* controller internal diagnostic */
6091 case 0x16: /* detect disk change */
6092 goto int13_success;
6093 break;
6095 // all those functions return disk write-protected
6096 case 0x03: /* write disk sectors */
6097 case 0x05: /* format disk track */
6098 SET_AH(0x03);
6099 goto int13_fail_noah;
6100 break;
6102 case 0x01: /* read disk status */
6103 status=read_byte(0x0040, 0x0074);
6104 SET_AH(status);
6105 SET_DISK_RET_STATUS(0);
6107 /* set CF if error status read */
6108 if (status) goto int13_fail_nostatus;
6109 else goto int13_success_noah;
6110 break;
6112 case 0x02: // read disk sectors
6113 case 0x04: // verify disk sectors
6114 vspt = read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
6115 vcylinders = read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders);
6116 vheads = read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads);
6118 ilba = read_dword(ebda_seg,&EbdaData->cdemu.ilba);
6120 sector = GET_CL() & 0x003f;
6121 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
6122 head = GET_DH();
6123 nbsectors = GET_AL();
6124 segment = ES;
6125 offset = BX;
6127 // no sector to read ?
6128 if(nbsectors==0) goto int13_success;
6130 // sanity checks sco openserver needs this!
6131 if ((sector > vspt)
6132 || (cylinder >= vcylinders)
6133 || (head >= vheads)) {
6134 goto int13_fail;
6137 // After controls, verify do nothing
6138 if (GET_AH() == 0x04) goto int13_success;
6140 segment = ES+(BX / 16);
6141 offset = BX % 16;
6143 // calculate the virtual lba inside the image
6144 vlba=((((Bit32u)cylinder*(Bit32u)vheads)+(Bit32u)head)*(Bit32u)vspt)+((Bit32u)(sector-1));
6146 // In advance so we don't loose the count
6147 SET_AL(nbsectors);
6149 // start lba on cd
6150 slba = (Bit32u)vlba/4;
6151 before= (Bit16u)vlba%4;
6153 // end lba on cd
6154 elba = (Bit32u)(vlba+nbsectors-1)/4;
6156 memsetb(get_SS(),atacmd,0,12);
6157 atacmd[0]=0x28; // READ command
6158 atacmd[7]=((Bit16u)(elba-slba+1) & 0xff00) >> 8; // Sectors
6159 atacmd[8]=((Bit16u)(elba-slba+1) & 0x00ff); // Sectors
6160 atacmd[2]=(ilba+slba & 0xff000000) >> 24; // LBA
6161 atacmd[3]=(ilba+slba & 0x00ff0000) >> 16;
6162 atacmd[4]=(ilba+slba & 0x0000ff00) >> 8;
6163 atacmd[5]=(ilba+slba & 0x000000ff);
6164 if((status = ata_cmd_packet(device, 12, get_SS(), atacmd, before*512, nbsectors*512L, ATA_DATA_IN, segment,offset)) != 0) {
6165 BX_INFO("int13_cdemu: function %02x, error %02x !\n",GET_AH(),status);
6166 SET_AH(0x02);
6167 SET_AL(0);
6168 goto int13_fail_noah;
6171 goto int13_success;
6172 break;
6174 case 0x08: /* read disk drive parameters */
6175 vspt=read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
6176 vcylinders=read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders) - 1;
6177 vheads=read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads) - 1;
6179 SET_AL( 0x00 );
6180 SET_BL( 0x00 );
6181 SET_CH( vcylinders & 0xff );
6182 SET_CL((( vcylinders >> 2) & 0xc0) | ( vspt & 0x3f ));
6183 SET_DH( vheads );
6184 SET_DL( 0x02 ); // FIXME ElTorito Various. should send the real count of drives 1 or 2
6185 // FIXME ElTorito Harddisk. should send the HD count
6187 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
6188 case 0x01: SET_BL( 0x02 ); break;
6189 case 0x02: SET_BL( 0x04 ); break;
6190 case 0x03: SET_BL( 0x06 ); break;
6193 ASM_START
6194 push bp
6195 mov bp, sp
6196 mov ax, #diskette_param_table2
6197 mov _int13_cdemu.DI+2[bp], ax
6198 mov _int13_cdemu.ES+2[bp], cs
6199 pop bp
6200 ASM_END
6201 goto int13_success;
6202 break;
6204 case 0x15: /* read disk drive size */
6205 // FIXME ElTorito Harddisk. What geometry to send ?
6206 SET_AH(0x03);
6207 goto int13_success_noah;
6208 break;
6210 // all those functions return unimplemented
6211 case 0x0a: /* read disk sectors with ECC */
6212 case 0x0b: /* write disk sectors with ECC */
6213 case 0x18: /* set media type for format */
6214 case 0x41: // IBM/MS installation check
6215 // FIXME ElTorito Harddisk. Darwin would like to use EDD
6216 case 0x42: // IBM/MS extended read
6217 case 0x43: // IBM/MS extended write
6218 case 0x44: // IBM/MS verify sectors
6219 case 0x45: // IBM/MS lock/unlock drive
6220 case 0x46: // IBM/MS eject media
6221 case 0x47: // IBM/MS extended seek
6222 case 0x48: // IBM/MS get drive parameters
6223 case 0x49: // IBM/MS extended media change
6224 case 0x4e: // ? - set hardware configuration
6225 case 0x50: // ? - send packet command
6226 default:
6227 BX_INFO("int13_cdemu function AH=%02x unsupported, returns fail\n", GET_AH());
6228 goto int13_fail;
6229 break;
6232 int13_fail:
6233 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
6234 int13_fail_noah:
6235 SET_DISK_RET_STATUS(GET_AH());
6236 int13_fail_nostatus:
6237 SET_CF(); // error occurred
6238 return;
6240 int13_success:
6241 SET_AH(0x00); // no error
6242 int13_success_noah:
6243 SET_DISK_RET_STATUS(0x00);
6244 CLEAR_CF(); // no error
6245 return;
6248 // ---------------------------------------------------------------------------
6249 // End of int13 when emulating a device from the cd
6250 // ---------------------------------------------------------------------------
6252 #endif // BX_ELTORITO_BOOT
6254 #else //BX_USE_ATADRV
6256 void
6257 outLBA(cylinder,hd_heads,head,hd_sectors,sector,dl)
6258 Bit16u cylinder;
6259 Bit16u hd_heads;
6260 Bit16u head;
6261 Bit16u hd_sectors;
6262 Bit16u sector;
6263 Bit16u dl;
6265 ASM_START
6266 push bp
6267 mov bp, sp
6268 push eax
6269 push ebx
6270 push edx
6271 xor eax,eax
6272 mov ax,4[bp] // cylinder
6273 xor ebx,ebx
6274 mov bl,6[bp] // hd_heads
6275 imul ebx
6277 mov bl,8[bp] // head
6278 add eax,ebx
6279 mov bl,10[bp] // hd_sectors
6280 imul ebx
6281 mov bl,12[bp] // sector
6282 add eax,ebx
6284 dec eax
6285 mov dx,#0x1f3
6286 out dx,al
6287 mov dx,#0x1f4
6288 mov al,ah
6289 out dx,al
6290 shr eax,#16
6291 mov dx,#0x1f5
6292 out dx,al
6293 and ah,#0xf
6294 mov bl,14[bp] // dl
6295 and bl,#1
6296 shl bl,#4
6297 or ah,bl
6298 or ah,#0xe0
6299 mov al,ah
6300 mov dx,#0x01f6
6301 out dx,al
6302 pop edx
6303 pop ebx
6304 pop eax
6305 pop bp
6306 ASM_END
6309 void
6310 int13_harddisk(EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
6311 Bit16u EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
6313 Bit8u drive, num_sectors, sector, head, status, mod;
6314 Bit8u drive_map;
6315 Bit8u n_drives;
6316 Bit16u cyl_mod, ax;
6317 Bit16u max_cylinder, cylinder, total_sectors;
6318 Bit16u hd_cylinders;
6319 Bit8u hd_heads, hd_sectors;
6320 Bit16u val16;
6321 Bit8u sector_count;
6322 unsigned int i;
6323 Bit16u tempbx;
6324 Bit16u dpsize;
6326 Bit16u count, segment, offset;
6327 Bit32u lba;
6328 Bit16u error;
6330 BX_DEBUG_INT13_HD("int13 harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
6332 write_byte(0x0040, 0x008e, 0); // clear completion flag
6334 /* at this point, DL is >= 0x80 to be passed from the floppy int13h
6335 handler code */
6336 /* check how many disks first (cmos reg 0x12), return an error if
6337 drive not present */
6338 drive_map = inb_cmos(0x12);
6339 drive_map = (((drive_map & 0xf0)==0) ? 0 : 1) |
6340 (((drive_map & 0x0f)==0) ? 0 : 2);
6341 n_drives = (drive_map==0) ? 0 :
6342 ((drive_map==3) ? 2 : 1);
6344 if (!(drive_map & (1<<(GET_ELDL()&0x7f)))) { /* allow 0, 1, or 2 disks */
6345 SET_AH(0x01);
6346 SET_DISK_RET_STATUS(0x01);
6347 SET_CF(); /* error occurred */
6348 return;
6351 switch (GET_AH()) {
6353 case 0x00: /* disk controller reset */
6354 BX_DEBUG_INT13_HD("int13_f00\n");
6356 SET_AH(0);
6357 SET_DISK_RET_STATUS(0);
6358 set_diskette_ret_status(0);
6359 set_diskette_current_cyl(0, 0); /* current cylinder, diskette 1 */
6360 set_diskette_current_cyl(1, 0); /* current cylinder, diskette 2 */
6361 CLEAR_CF(); /* successful */
6362 return;
6363 break;
6365 case 0x01: /* read disk status */
6366 BX_DEBUG_INT13_HD("int13_f01\n");
6367 status = read_byte(0x0040, 0x0074);
6368 SET_AH(status);
6369 SET_DISK_RET_STATUS(0);
6370 /* set CF if error status read */
6371 if (status) SET_CF();
6372 else CLEAR_CF();
6373 return;
6374 break;
6376 case 0x04: // verify disk sectors
6377 case 0x02: // read disk sectors
6378 drive = GET_ELDL();
6379 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6381 num_sectors = GET_AL();
6382 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
6383 sector = (GET_CL() & 0x3f);
6384 head = GET_DH();
6387 if (hd_cylinders > 1024) {
6388 if (hd_cylinders <= 2048) {
6389 cylinder <<= 1;
6391 else if (hd_cylinders <= 4096) {
6392 cylinder <<= 2;
6394 else if (hd_cylinders <= 8192) {
6395 cylinder <<= 3;
6397 else { // hd_cylinders <= 16384
6398 cylinder <<= 4;
6401 ax = head / hd_heads;
6402 cyl_mod = ax & 0xff;
6403 head = ax >> 8;
6404 cylinder |= cyl_mod;
6407 if ( (cylinder >= hd_cylinders) ||
6408 (sector > hd_sectors) ||
6409 (head >= hd_heads) ) {
6410 SET_AH(1);
6411 SET_DISK_RET_STATUS(1);
6412 SET_CF(); /* error occurred */
6413 return;
6416 if ( (num_sectors > 128) || (num_sectors == 0) )
6417 BX_PANIC("int13_harddisk: num_sectors out of range!\n");
6419 if (head > 15)
6420 BX_PANIC("hard drive BIOS:(read/verify) head > 15\n");
6422 if ( GET_AH() == 0x04 ) {
6423 SET_AH(0);
6424 SET_DISK_RET_STATUS(0);
6425 CLEAR_CF();
6426 return;
6429 status = inb(0x1f7);
6430 if (status & 0x80) {
6431 BX_PANIC("hard drive BIOS:(read/verify) BUSY bit set\n");
6433 outb(0x01f2, num_sectors);
6434 /* activate LBA? (tomv) */
6435 if (hd_heads > 16) {
6436 BX_DEBUG_INT13_HD("CHS: %x %x %x\n", cylinder, head, sector);
6437 outLBA(cylinder,hd_heads,head,hd_sectors,sector,drive);
6439 else {
6440 outb(0x01f3, sector);
6441 outb(0x01f4, cylinder & 0x00ff);
6442 outb(0x01f5, cylinder >> 8);
6443 outb(0x01f6, 0xa0 | ((drive & 0x01)<<4) | (head & 0x0f));
6445 outb(0x01f7, 0x20);
6447 while (1) {
6448 status = inb(0x1f7);
6449 if ( !(status & 0x80) ) break;
6452 if (status & 0x01) {
6453 BX_PANIC("hard drive BIOS:(read/verify) read error\n");
6454 } else if ( !(status & 0x08) ) {
6455 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
6456 BX_PANIC("hard drive BIOS:(read/verify) expected DRQ=1\n");
6459 sector_count = 0;
6460 tempbx = BX;
6462 ASM_START
6463 sti ;; enable higher priority interrupts
6464 ASM_END
6466 while (1) {
6467 ASM_START
6468 ;; store temp bx in real DI register
6469 push bp
6470 mov bp, sp
6471 mov di, _int13_harddisk.tempbx + 2 [bp]
6472 pop bp
6474 ;; adjust if there will be an overrun
6475 cmp di, #0xfe00
6476 jbe i13_f02_no_adjust
6477 i13_f02_adjust:
6478 sub di, #0x0200 ; sub 512 bytes from offset
6479 mov ax, es
6480 add ax, #0x0020 ; add 512 to segment
6481 mov es, ax
6483 i13_f02_no_adjust:
6484 mov cx, #0x0100 ;; counter (256 words = 512b)
6485 mov dx, #0x01f0 ;; AT data read port
6488 insw ;; CX words transfered from port(DX) to ES:[DI]
6490 i13_f02_done:
6491 ;; store real DI register back to temp bx
6492 push bp
6493 mov bp, sp
6494 mov _int13_harddisk.tempbx + 2 [bp], di
6495 pop bp
6496 ASM_END
6498 sector_count++;
6499 num_sectors--;
6500 if (num_sectors == 0) {
6501 status = inb(0x1f7);
6502 if ( (status & 0xc9) != 0x40 )
6503 BX_PANIC("no sectors left to read/verify, status is %02x\n", (unsigned) status);
6504 break;
6506 else {
6507 status = inb(0x1f7);
6508 if ( (status & 0xc9) != 0x48 )
6509 BX_PANIC("more sectors left to read/verify, status is %02x\n", (unsigned) status);
6510 continue;
6514 SET_AH(0);
6515 SET_DISK_RET_STATUS(0);
6516 SET_AL(sector_count);
6517 CLEAR_CF(); /* successful */
6518 return;
6519 break;
6522 case 0x03: /* write disk sectors */
6523 BX_DEBUG_INT13_HD("int13_f03\n");
6524 drive = GET_ELDL ();
6525 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6527 num_sectors = GET_AL();
6528 cylinder = GET_CH();
6529 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
6530 sector = (GET_CL() & 0x3f);
6531 head = GET_DH();
6533 if (hd_cylinders > 1024) {
6534 if (hd_cylinders <= 2048) {
6535 cylinder <<= 1;
6537 else if (hd_cylinders <= 4096) {
6538 cylinder <<= 2;
6540 else if (hd_cylinders <= 8192) {
6541 cylinder <<= 3;
6543 else { // hd_cylinders <= 16384
6544 cylinder <<= 4;
6547 ax = head / hd_heads;
6548 cyl_mod = ax & 0xff;
6549 head = ax >> 8;
6550 cylinder |= cyl_mod;
6553 if ( (cylinder >= hd_cylinders) ||
6554 (sector > hd_sectors) ||
6555 (head >= hd_heads) ) {
6556 SET_AH( 1);
6557 SET_DISK_RET_STATUS(1);
6558 SET_CF(); /* error occurred */
6559 return;
6562 if ( (num_sectors > 128) || (num_sectors == 0) )
6563 BX_PANIC("int13_harddisk: num_sectors out of range!\n");
6565 if (head > 15)
6566 BX_PANIC("hard drive BIOS:(read) head > 15\n");
6568 status = inb(0x1f7);
6569 if (status & 0x80) {
6570 BX_PANIC("hard drive BIOS:(read) BUSY bit set\n");
6572 // should check for Drive Ready Bit also in status reg
6573 outb(0x01f2, num_sectors);
6575 /* activate LBA? (tomv) */
6576 if (hd_heads > 16) {
6577 BX_DEBUG_INT13_HD("CHS (write): %x %x %x\n", cylinder, head, sector);
6578 outLBA(cylinder,hd_heads,head,hd_sectors,sector,GET_ELDL());
6580 else {
6581 outb(0x01f3, sector);
6582 outb(0x01f4, cylinder & 0x00ff);
6583 outb(0x01f5, cylinder >> 8);
6584 outb(0x01f6, 0xa0 | ((GET_ELDL() & 0x01)<<4) | (head & 0x0f));
6586 outb(0x01f7, 0x30);
6588 // wait for busy bit to turn off after seeking
6589 while (1) {
6590 status = inb(0x1f7);
6591 if ( !(status & 0x80) ) break;
6594 if ( !(status & 0x08) ) {
6595 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
6596 BX_PANIC("hard drive BIOS:(write) data-request bit not set\n");
6599 sector_count = 0;
6600 tempbx = BX;
6602 ASM_START
6603 sti ;; enable higher priority interrupts
6604 ASM_END
6606 while (1) {
6607 ASM_START
6608 ;; store temp bx in real SI register
6609 push bp
6610 mov bp, sp
6611 mov si, _int13_harddisk.tempbx + 2 [bp]
6612 pop bp
6614 ;; adjust if there will be an overrun
6615 cmp si, #0xfe00
6616 jbe i13_f03_no_adjust
6617 i13_f03_adjust:
6618 sub si, #0x0200 ; sub 512 bytes from offset
6619 mov ax, es
6620 add ax, #0x0020 ; add 512 to segment
6621 mov es, ax
6623 i13_f03_no_adjust:
6624 mov cx, #0x0100 ;; counter (256 words = 512b)
6625 mov dx, #0x01f0 ;; AT data read port
6627 seg ES
6629 outsw ;; CX words tranfered from ES:[SI] to port(DX)
6631 ;; store real SI register back to temp bx
6632 push bp
6633 mov bp, sp
6634 mov _int13_harddisk.tempbx + 2 [bp], si
6635 pop bp
6636 ASM_END
6638 sector_count++;
6639 num_sectors--;
6640 if (num_sectors == 0) {
6641 status = inb(0x1f7);
6642 if ( (status & 0xe9) != 0x40 )
6643 BX_PANIC("no sectors left to write, status is %02x\n", (unsigned) status);
6644 break;
6646 else {
6647 status = inb(0x1f7);
6648 if ( (status & 0xc9) != 0x48 )
6649 BX_PANIC("more sectors left to write, status is %02x\n", (unsigned) status);
6650 continue;
6654 SET_AH(0);
6655 SET_DISK_RET_STATUS(0);
6656 SET_AL(sector_count);
6657 CLEAR_CF(); /* successful */
6658 return;
6659 break;
6661 case 0x05: /* format disk track */
6662 BX_DEBUG_INT13_HD("int13_f05\n");
6663 BX_PANIC("format disk track called\n");
6664 /* nop */
6665 SET_AH(0);
6666 SET_DISK_RET_STATUS(0);
6667 CLEAR_CF(); /* successful */
6668 return;
6669 break;
6671 case 0x08: /* read disk drive parameters */
6672 BX_DEBUG_INT13_HD("int13_f08\n");
6674 drive = GET_ELDL ();
6675 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6677 // translate CHS
6679 if (hd_cylinders <= 1024) {
6680 // hd_cylinders >>= 0;
6681 // hd_heads <<= 0;
6683 else if (hd_cylinders <= 2048) {
6684 hd_cylinders >>= 1;
6685 hd_heads <<= 1;
6687 else if (hd_cylinders <= 4096) {
6688 hd_cylinders >>= 2;
6689 hd_heads <<= 2;
6691 else if (hd_cylinders <= 8192) {
6692 hd_cylinders >>= 3;
6693 hd_heads <<= 3;
6695 else { // hd_cylinders <= 16384
6696 hd_cylinders >>= 4;
6697 hd_heads <<= 4;
6700 max_cylinder = hd_cylinders - 2; /* 0 based */
6701 SET_AL(0);
6702 SET_CH(max_cylinder & 0xff);
6703 SET_CL(((max_cylinder >> 2) & 0xc0) | (hd_sectors & 0x3f));
6704 SET_DH(hd_heads - 1);
6705 SET_DL(n_drives); /* returns 0, 1, or 2 hard drives */
6706 SET_AH(0);
6707 SET_DISK_RET_STATUS(0);
6708 CLEAR_CF(); /* successful */
6710 return;
6711 break;
6713 case 0x09: /* initialize drive parameters */
6714 BX_DEBUG_INT13_HD("int13_f09\n");
6715 SET_AH(0);
6716 SET_DISK_RET_STATUS(0);
6717 CLEAR_CF(); /* successful */
6718 return;
6719 break;
6721 case 0x0a: /* read disk sectors with ECC */
6722 BX_DEBUG_INT13_HD("int13_f0a\n");
6723 case 0x0b: /* write disk sectors with ECC */
6724 BX_DEBUG_INT13_HD("int13_f0b\n");
6725 BX_PANIC("int13h Functions 0Ah & 0Bh not implemented!\n");
6726 return;
6727 break;
6729 case 0x0c: /* seek to specified cylinder */
6730 BX_DEBUG_INT13_HD("int13_f0c\n");
6731 BX_INFO("int13h function 0ch (seek) not implemented!\n");
6732 SET_AH(0);
6733 SET_DISK_RET_STATUS(0);
6734 CLEAR_CF(); /* successful */
6735 return;
6736 break;
6738 case 0x0d: /* alternate disk reset */
6739 BX_DEBUG_INT13_HD("int13_f0d\n");
6740 SET_AH(0);
6741 SET_DISK_RET_STATUS(0);
6742 CLEAR_CF(); /* successful */
6743 return;
6744 break;
6746 case 0x10: /* check drive ready */
6747 BX_DEBUG_INT13_HD("int13_f10\n");
6748 //SET_AH(0);
6749 //SET_DISK_RET_STATUS(0);
6750 //CLEAR_CF(); /* successful */
6751 //return;
6752 //break;
6754 // should look at 40:8E also???
6755 status = inb(0x01f7);
6756 if ( (status & 0xc0) == 0x40 ) {
6757 SET_AH(0);
6758 SET_DISK_RET_STATUS(0);
6759 CLEAR_CF(); // drive ready
6760 return;
6762 else {
6763 SET_AH(0xAA);
6764 SET_DISK_RET_STATUS(0xAA);
6765 SET_CF(); // not ready
6766 return;
6768 break;
6770 case 0x11: /* recalibrate */
6771 BX_DEBUG_INT13_HD("int13_f11\n");
6772 SET_AH(0);
6773 SET_DISK_RET_STATUS(0);
6774 CLEAR_CF(); /* successful */
6775 return;
6776 break;
6778 case 0x14: /* controller internal diagnostic */
6779 BX_DEBUG_INT13_HD("int13_f14\n");
6780 SET_AH(0);
6781 SET_DISK_RET_STATUS(0);
6782 CLEAR_CF(); /* successful */
6783 SET_AL(0);
6784 return;
6785 break;
6787 case 0x15: /* read disk drive size */
6788 drive = GET_ELDL();
6789 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6790 ASM_START
6791 push bp
6792 mov bp, sp
6793 mov al, _int13_harddisk.hd_heads + 2 [bp]
6794 mov ah, _int13_harddisk.hd_sectors + 2 [bp]
6795 mul al, ah ;; ax = heads * sectors
6796 mov bx, _int13_harddisk.hd_cylinders + 2 [bp]
6797 dec bx ;; use (cylinders - 1) ???
6798 mul ax, bx ;; dx:ax = (cylinders -1) * (heads * sectors)
6799 ;; now we need to move the 32bit result dx:ax to what the
6800 ;; BIOS wants which is cx:dx.
6801 ;; and then into CX:DX on the stack
6802 mov _int13_harddisk.CX + 2 [bp], dx
6803 mov _int13_harddisk.DX + 2 [bp], ax
6804 pop bp
6805 ASM_END
6806 SET_AH(3); // hard disk accessible
6807 SET_DISK_RET_STATUS(0); // ??? should this be 0
6808 CLEAR_CF(); // successful
6809 return;
6810 break;
6812 case 0x18: // set media type for format
6813 case 0x41: // IBM/MS
6814 case 0x42: // IBM/MS
6815 case 0x43: // IBM/MS
6816 case 0x44: // IBM/MS
6817 case 0x45: // IBM/MS lock/unlock drive
6818 case 0x46: // IBM/MS eject media
6819 case 0x47: // IBM/MS extended seek
6820 case 0x49: // IBM/MS extended media change
6821 case 0x50: // IBM/MS send packet command
6822 default:
6823 BX_INFO("int13_harddisk: unsupported AH=%02x\n", GET_AH());
6825 SET_AH(1); // code=invalid function in AH or invalid parameter
6826 SET_DISK_RET_STATUS(1);
6827 SET_CF(); /* unsuccessful */
6828 return;
6829 break;
6833 static char panic_msg_reg12h[] = "HD%d cmos reg 12h not type F\n";
6834 static char panic_msg_reg19h[] = "HD%d cmos reg %02xh not user definable type 47\n";
6836 void
6837 get_hd_geometry(drive, hd_cylinders, hd_heads, hd_sectors)
6838 Bit8u drive;
6839 Bit16u *hd_cylinders;
6840 Bit8u *hd_heads;
6841 Bit8u *hd_sectors;
6843 Bit8u hd_type;
6844 Bit16u ss;
6845 Bit16u cylinders;
6846 Bit8u iobase;
6848 ss = get_SS();
6849 if (drive == 0x80) {
6850 hd_type = inb_cmos(0x12) & 0xf0;
6851 if (hd_type != 0xf0)
6852 BX_INFO(panic_msg_reg12h,0);
6853 hd_type = inb_cmos(0x19); // HD0: extended type
6854 if (hd_type != 47)
6855 BX_INFO(panic_msg_reg19h,0,0x19);
6856 iobase = 0x1b;
6857 } else {
6858 hd_type = inb_cmos(0x12) & 0x0f;
6859 if (hd_type != 0x0f)
6860 BX_INFO(panic_msg_reg12h,1);
6861 hd_type = inb_cmos(0x1a); // HD1: extended type
6862 if (hd_type != 47)
6863 BX_INFO(panic_msg_reg19h,0,0x1a);
6864 iobase = 0x24;
6867 // cylinders
6868 cylinders = inb_cmos(iobase) | (inb_cmos(iobase+1) << 8);
6869 write_word(ss, hd_cylinders, cylinders);
6871 // heads
6872 write_byte(ss, hd_heads, inb_cmos(iobase+2));
6874 // sectors per track
6875 write_byte(ss, hd_sectors, inb_cmos(iobase+8));
6878 #endif //else BX_USE_ATADRV
6880 #if BX_SUPPORT_FLOPPY
6882 //////////////////////
6883 // FLOPPY functions //
6884 //////////////////////
6886 void floppy_reset_controller()
6888 Bit8u val8;
6890 // Reset controller
6891 val8 = inb(0x03f2);
6892 outb(0x03f2, val8 & ~0x04);
6893 outb(0x03f2, val8 | 0x04);
6895 // Wait for controller to come out of reset
6896 do {
6897 val8 = inb(0x3f4);
6898 } while ( (val8 & 0xc0) != 0x80 );
6901 void floppy_prepare_controller(drive)
6902 Bit16u drive;
6904 Bit8u val8, dor, prev_reset;
6906 // set 40:3e bit 7 to 0
6907 val8 = read_byte(0x0040, 0x003e);
6908 val8 &= 0x7f;
6909 write_byte(0x0040, 0x003e, val8);
6911 // turn on motor of selected drive, DMA & int enabled, normal operation
6912 prev_reset = inb(0x03f2) & 0x04;
6913 if (drive)
6914 dor = 0x20;
6915 else
6916 dor = 0x10;
6917 dor |= 0x0c;
6918 dor |= drive;
6919 outb(0x03f2, dor);
6921 // reset the disk motor timeout value of INT 08
6922 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
6924 // wait for drive readiness
6925 do {
6926 val8 = inb(0x3f4);
6927 } while ( (val8 & 0xc0) != 0x80 );
6929 if (prev_reset == 0) {
6930 // turn on interrupts
6931 ASM_START
6933 ASM_END
6934 // wait on 40:3e bit 7 to become 1
6935 do {
6936 val8 = read_byte(0x0040, 0x003e);
6937 } while ( (val8 & 0x80) == 0 );
6938 val8 &= 0x7f;
6939 ASM_START
6941 ASM_END
6942 write_byte(0x0040, 0x003e, val8);
6946 bx_bool
6947 floppy_media_known(drive)
6948 Bit16u drive;
6950 Bit8u val8;
6951 Bit16u media_state_offset;
6953 val8 = read_byte(0x0040, 0x003e); // diskette recal status
6954 if (drive)
6955 val8 >>= 1;
6956 val8 &= 0x01;
6957 if (val8 == 0)
6958 return(0);
6960 media_state_offset = 0x0090;
6961 if (drive)
6962 media_state_offset += 1;
6964 val8 = read_byte(0x0040, media_state_offset);
6965 val8 = (val8 >> 4) & 0x01;
6966 if (val8 == 0)
6967 return(0);
6969 // check pass, return KNOWN
6970 return(1);
6973 bx_bool
6974 floppy_media_sense(drive)
6975 Bit16u drive;
6977 bx_bool retval;
6978 Bit16u media_state_offset;
6979 Bit8u drive_type, config_data, media_state;
6981 if (floppy_drive_recal(drive) == 0) {
6982 return(0);
6985 // for now cheat and get drive type from CMOS,
6986 // assume media is same as drive type
6988 // ** config_data **
6989 // Bitfields for diskette media control:
6990 // Bit(s) Description (Table M0028)
6991 // 7-6 last data rate set by controller
6992 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6993 // 5-4 last diskette drive step rate selected
6994 // 00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
6995 // 3-2 {data rate at start of operation}
6996 // 1-0 reserved
6998 // ** media_state **
6999 // Bitfields for diskette drive media state:
7000 // Bit(s) Description (Table M0030)
7001 // 7-6 data rate
7002 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
7003 // 5 double stepping required (e.g. 360kB in 1.2MB)
7004 // 4 media type established
7005 // 3 drive capable of supporting 4MB media
7006 // 2-0 on exit from BIOS, contains
7007 // 000 trying 360kB in 360kB
7008 // 001 trying 360kB in 1.2MB
7009 // 010 trying 1.2MB in 1.2MB
7010 // 011 360kB in 360kB established
7011 // 100 360kB in 1.2MB established
7012 // 101 1.2MB in 1.2MB established
7013 // 110 reserved
7014 // 111 all other formats/drives
7016 drive_type = inb_cmos(0x10);
7017 if (drive == 0)
7018 drive_type >>= 4;
7019 else
7020 drive_type &= 0x0f;
7021 if ( drive_type == 1 ) {
7022 // 360K 5.25" drive
7023 config_data = 0x00; // 0000 0000
7024 media_state = 0x25; // 0010 0101
7025 retval = 1;
7027 else if ( drive_type == 2 ) {
7028 // 1.2 MB 5.25" drive
7029 config_data = 0x00; // 0000 0000
7030 media_state = 0x25; // 0010 0101 // need double stepping??? (bit 5)
7031 retval = 1;
7033 else if ( drive_type == 3 ) {
7034 // 720K 3.5" drive
7035 config_data = 0x00; // 0000 0000 ???
7036 media_state = 0x17; // 0001 0111
7037 retval = 1;
7039 else if ( drive_type == 4 ) {
7040 // 1.44 MB 3.5" drive
7041 config_data = 0x00; // 0000 0000
7042 media_state = 0x17; // 0001 0111
7043 retval = 1;
7045 else if ( drive_type == 5 ) {
7046 // 2.88 MB 3.5" drive
7047 config_data = 0xCC; // 1100 1100
7048 media_state = 0xD7; // 1101 0111
7049 retval = 1;
7052 // Extended floppy size uses special cmos setting
7053 else if ( drive_type == 6 ) {
7054 // 160k 5.25" drive
7055 config_data = 0x00; // 0000 0000
7056 media_state = 0x27; // 0010 0111
7057 retval = 1;
7059 else if ( drive_type == 7 ) {
7060 // 180k 5.25" drive
7061 config_data = 0x00; // 0000 0000
7062 media_state = 0x27; // 0010 0111
7063 retval = 1;
7065 else if ( drive_type == 8 ) {
7066 // 320k 5.25" drive
7067 config_data = 0x00; // 0000 0000
7068 media_state = 0x27; // 0010 0111
7069 retval = 1;
7072 else {
7073 // not recognized
7074 config_data = 0x00; // 0000 0000
7075 media_state = 0x00; // 0000 0000
7076 retval = 0;
7079 if (drive == 0)
7080 media_state_offset = 0x90;
7081 else
7082 media_state_offset = 0x91;
7083 write_byte(0x0040, 0x008B, config_data);
7084 write_byte(0x0040, media_state_offset, media_state);
7086 return(retval);
7089 bx_bool
7090 floppy_drive_recal(drive)
7091 Bit16u drive;
7093 Bit8u val8;
7094 Bit16u curr_cyl_offset;
7096 floppy_prepare_controller(drive);
7098 // send Recalibrate command (2 bytes) to controller
7099 outb(0x03f5, 0x07); // 07: Recalibrate
7100 outb(0x03f5, drive); // 0=drive0, 1=drive1
7102 // turn on interrupts
7103 ASM_START
7105 ASM_END
7107 // wait on 40:3e bit 7 to become 1
7108 do {
7109 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7110 } while ( val8 == 0 );
7112 val8 = 0; // separate asm from while() loop
7113 // turn off interrupts
7114 ASM_START
7116 ASM_END
7118 // set 40:3e bit 7 to 0, and calibrated bit
7119 val8 = read_byte(0x0040, 0x003e);
7120 val8 &= 0x7f;
7121 if (drive) {
7122 val8 |= 0x02; // Drive 1 calibrated
7123 curr_cyl_offset = 0x0095;
7124 } else {
7125 val8 |= 0x01; // Drive 0 calibrated
7126 curr_cyl_offset = 0x0094;
7128 write_byte(0x0040, 0x003e, val8);
7129 write_byte(0x0040, curr_cyl_offset, 0); // current cylinder is 0
7131 return(1);
7136 bx_bool
7137 floppy_drive_exists(drive)
7138 Bit16u drive;
7140 Bit8u drive_type;
7142 // check CMOS to see if drive exists
7143 drive_type = inb_cmos(0x10);
7144 if (drive == 0)
7145 drive_type >>= 4;
7146 else
7147 drive_type &= 0x0f;
7148 if ( drive_type == 0 )
7149 return(0);
7150 else
7151 return(1);
7154 void
7155 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
7156 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
7158 Bit8u drive, num_sectors, track, sector, head, status;
7159 Bit16u base_address, base_count, base_es;
7160 Bit8u page, mode_register, val8, dor;
7161 Bit8u return_status[7];
7162 Bit8u drive_type, num_floppies, ah;
7163 Bit16u es, last_addr;
7165 BX_DEBUG_INT13_FL("int13_diskette: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
7167 ah = GET_AH();
7169 switch ( ah ) {
7170 case 0x00: // diskette controller reset
7171 BX_DEBUG_INT13_FL("floppy f00\n");
7172 drive = GET_ELDL();
7173 if (drive > 1) {
7174 SET_AH(1); // invalid param
7175 set_diskette_ret_status(1);
7176 SET_CF();
7177 return;
7179 drive_type = inb_cmos(0x10);
7181 if (drive == 0)
7182 drive_type >>= 4;
7183 else
7184 drive_type &= 0x0f;
7185 if (drive_type == 0) {
7186 SET_AH(0x80); // drive not responding
7187 set_diskette_ret_status(0x80);
7188 SET_CF();
7189 return;
7191 SET_AH(0);
7192 set_diskette_ret_status(0);
7193 CLEAR_CF(); // successful
7194 set_diskette_current_cyl(drive, 0); // current cylinder
7195 return;
7197 case 0x01: // Read Diskette Status
7198 CLEAR_CF();
7199 val8 = read_byte(0x0000, 0x0441);
7200 SET_AH(val8);
7201 if (val8) {
7202 SET_CF();
7204 return;
7206 case 0x02: // Read Diskette Sectors
7207 case 0x03: // Write Diskette Sectors
7208 case 0x04: // Verify Diskette Sectors
7209 num_sectors = GET_AL();
7210 track = GET_CH();
7211 sector = GET_CL();
7212 head = GET_DH();
7213 drive = GET_ELDL();
7215 if ((drive > 1) || (head > 1) || (sector == 0) ||
7216 (num_sectors == 0) || (num_sectors > 72)) {
7217 BX_INFO("int13_diskette: read/write/verify: parameter out of range\n");
7218 SET_AH(1);
7219 set_diskette_ret_status(1);
7220 SET_AL(0); // no sectors read
7221 SET_CF(); // error occurred
7222 return;
7225 // see if drive exists
7226 if (floppy_drive_exists(drive) == 0) {
7227 SET_AH(0x80); // not responding
7228 set_diskette_ret_status(0x80);
7229 SET_AL(0); // no sectors read
7230 SET_CF(); // error occurred
7231 return;
7234 // see if media in drive, and type is known
7235 if (floppy_media_known(drive) == 0) {
7236 if (floppy_media_sense(drive) == 0) {
7237 SET_AH(0x0C); // Media type not found
7238 set_diskette_ret_status(0x0C);
7239 SET_AL(0); // no sectors read
7240 SET_CF(); // error occurred
7241 return;
7245 if (ah == 0x02) {
7246 // Read Diskette Sectors
7248 //-----------------------------------
7249 // set up DMA controller for transfer
7250 //-----------------------------------
7252 // es:bx = pointer to where to place information from diskette
7253 // port 04: DMA-1 base and current address, channel 2
7254 // port 05: DMA-1 base and current count, channel 2
7255 page = (ES >> 12); // upper 4 bits
7256 base_es = (ES << 4); // lower 16bits contributed by ES
7257 base_address = base_es + BX; // lower 16 bits of address
7258 // contributed by ES:BX
7259 if ( base_address < base_es ) {
7260 // in case of carry, adjust page by 1
7261 page++;
7263 base_count = (num_sectors * 512) - 1;
7265 // check for 64K boundary overrun
7266 last_addr = base_address + base_count;
7267 if (last_addr < base_address) {
7268 SET_AH(0x09);
7269 set_diskette_ret_status(0x09);
7270 SET_AL(0); // no sectors read
7271 SET_CF(); // error occurred
7272 return;
7275 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
7276 outb(0x000a, 0x06);
7278 BX_DEBUG_INT13_FL("clear flip-flop\n");
7279 outb(0x000c, 0x00); // clear flip-flop
7280 outb(0x0004, base_address);
7281 outb(0x0004, base_address>>8);
7282 BX_DEBUG_INT13_FL("clear flip-flop\n");
7283 outb(0x000c, 0x00); // clear flip-flop
7284 outb(0x0005, base_count);
7285 outb(0x0005, base_count>>8);
7287 // port 0b: DMA-1 Mode Register
7288 mode_register = 0x46; // single mode, increment, autoinit disable,
7289 // transfer type=write, channel 2
7290 BX_DEBUG_INT13_FL("setting mode register\n");
7291 outb(0x000b, mode_register);
7293 BX_DEBUG_INT13_FL("setting page register\n");
7294 // port 81: DMA-1 Page Register, channel 2
7295 outb(0x0081, page);
7297 BX_DEBUG_INT13_FL("unmask chan 2\n");
7298 outb(0x000a, 0x02); // unmask channel 2
7300 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
7301 outb(0x000a, 0x02);
7303 //--------------------------------------
7304 // set up floppy controller for transfer
7305 //--------------------------------------
7306 floppy_prepare_controller(drive);
7308 // send read-normal-data command (9 bytes) to controller
7309 outb(0x03f5, 0xe6); // e6: read normal data
7310 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7311 outb(0x03f5, track);
7312 outb(0x03f5, head);
7313 outb(0x03f5, sector);
7314 outb(0x03f5, 2); // 512 byte sector size
7315 outb(0x03f5, sector + num_sectors - 1); // last sector to read on track
7316 outb(0x03f5, 0); // Gap length
7317 outb(0x03f5, 0xff); // Gap length
7319 // turn on interrupts
7320 ASM_START
7322 ASM_END
7324 // wait on 40:3e bit 7 to become 1
7325 do {
7326 val8 = read_byte(0x0040, 0x0040);
7327 if (val8 == 0) {
7328 floppy_reset_controller();
7329 SET_AH(0x80); // drive not ready (timeout)
7330 set_diskette_ret_status(0x80);
7331 SET_AL(0); // no sectors read
7332 SET_CF(); // error occurred
7333 return;
7335 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7336 } while ( val8 == 0 );
7338 val8 = 0; // separate asm from while() loop
7339 // turn off interrupts
7340 ASM_START
7342 ASM_END
7344 // set 40:3e bit 7 to 0
7345 val8 = read_byte(0x0040, 0x003e);
7346 val8 &= 0x7f;
7347 write_byte(0x0040, 0x003e, val8);
7349 // check port 3f4 for accessibility to status bytes
7350 val8 = inb(0x3f4);
7351 if ( (val8 & 0xc0) != 0xc0 )
7352 BX_PANIC("int13_diskette: ctrl not ready\n");
7354 // read 7 return status bytes from controller
7355 // using loop index broken, have to unroll...
7356 return_status[0] = inb(0x3f5);
7357 return_status[1] = inb(0x3f5);
7358 return_status[2] = inb(0x3f5);
7359 return_status[3] = inb(0x3f5);
7360 return_status[4] = inb(0x3f5);
7361 return_status[5] = inb(0x3f5);
7362 return_status[6] = inb(0x3f5);
7363 // record in BIOS Data Area
7364 write_byte(0x0040, 0x0042, return_status[0]);
7365 write_byte(0x0040, 0x0043, return_status[1]);
7366 write_byte(0x0040, 0x0044, return_status[2]);
7367 write_byte(0x0040, 0x0045, return_status[3]);
7368 write_byte(0x0040, 0x0046, return_status[4]);
7369 write_byte(0x0040, 0x0047, return_status[5]);
7370 write_byte(0x0040, 0x0048, return_status[6]);
7372 if ( (return_status[0] & 0xc0) != 0 ) {
7373 SET_AH(0x20);
7374 set_diskette_ret_status(0x20);
7375 SET_AL(0); // no sectors read
7376 SET_CF(); // error occurred
7377 return;
7380 // ??? should track be new val from return_status[3] ?
7381 set_diskette_current_cyl(drive, track);
7382 // AL = number of sectors read (same value as passed)
7383 SET_AH(0x00); // success
7384 CLEAR_CF(); // success
7385 return;
7386 } else if (ah == 0x03) {
7387 // Write Diskette Sectors
7389 //-----------------------------------
7390 // set up DMA controller for transfer
7391 //-----------------------------------
7393 // es:bx = pointer to where to place information from diskette
7394 // port 04: DMA-1 base and current address, channel 2
7395 // port 05: DMA-1 base and current count, channel 2
7396 page = (ES >> 12); // upper 4 bits
7397 base_es = (ES << 4); // lower 16bits contributed by ES
7398 base_address = base_es + BX; // lower 16 bits of address
7399 // contributed by ES:BX
7400 if ( base_address < base_es ) {
7401 // in case of carry, adjust page by 1
7402 page++;
7404 base_count = (num_sectors * 512) - 1;
7406 // check for 64K boundary overrun
7407 last_addr = base_address + base_count;
7408 if (last_addr < base_address) {
7409 SET_AH(0x09);
7410 set_diskette_ret_status(0x09);
7411 SET_AL(0); // no sectors read
7412 SET_CF(); // error occurred
7413 return;
7416 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
7417 outb(0x000a, 0x06);
7419 outb(0x000c, 0x00); // clear flip-flop
7420 outb(0x0004, base_address);
7421 outb(0x0004, base_address>>8);
7422 outb(0x000c, 0x00); // clear flip-flop
7423 outb(0x0005, base_count);
7424 outb(0x0005, base_count>>8);
7426 // port 0b: DMA-1 Mode Register
7427 mode_register = 0x4a; // single mode, increment, autoinit disable,
7428 // transfer type=read, channel 2
7429 outb(0x000b, mode_register);
7431 // port 81: DMA-1 Page Register, channel 2
7432 outb(0x0081, page);
7434 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
7435 outb(0x000a, 0x02);
7437 //--------------------------------------
7438 // set up floppy controller for transfer
7439 //--------------------------------------
7440 floppy_prepare_controller(drive);
7442 // send write-normal-data command (9 bytes) to controller
7443 outb(0x03f5, 0xc5); // c5: write normal data
7444 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7445 outb(0x03f5, track);
7446 outb(0x03f5, head);
7447 outb(0x03f5, sector);
7448 outb(0x03f5, 2); // 512 byte sector size
7449 outb(0x03f5, sector + num_sectors - 1); // last sector to write on track
7450 outb(0x03f5, 0); // Gap length
7451 outb(0x03f5, 0xff); // Gap length
7453 // turn on interrupts
7454 ASM_START
7456 ASM_END
7458 // wait on 40:3e bit 7 to become 1
7459 do {
7460 val8 = read_byte(0x0040, 0x0040);
7461 if (val8 == 0) {
7462 floppy_reset_controller();
7463 SET_AH(0x80); // drive not ready (timeout)
7464 set_diskette_ret_status(0x80);
7465 SET_AL(0); // no sectors written
7466 SET_CF(); // error occurred
7467 return;
7469 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7470 } while ( val8 == 0 );
7472 val8 = 0; // separate asm from while() loop
7473 // turn off interrupts
7474 ASM_START
7476 ASM_END
7478 // set 40:3e bit 7 to 0
7479 val8 = read_byte(0x0040, 0x003e);
7480 val8 &= 0x7f;
7481 write_byte(0x0040, 0x003e, val8);
7483 // check port 3f4 for accessibility to status bytes
7484 val8 = inb(0x3f4);
7485 if ( (val8 & 0xc0) != 0xc0 )
7486 BX_PANIC("int13_diskette: ctrl not ready\n");
7488 // read 7 return status bytes from controller
7489 // using loop index broken, have to unroll...
7490 return_status[0] = inb(0x3f5);
7491 return_status[1] = inb(0x3f5);
7492 return_status[2] = inb(0x3f5);
7493 return_status[3] = inb(0x3f5);
7494 return_status[4] = inb(0x3f5);
7495 return_status[5] = inb(0x3f5);
7496 return_status[6] = inb(0x3f5);
7497 // record in BIOS Data Area
7498 write_byte(0x0040, 0x0042, return_status[0]);
7499 write_byte(0x0040, 0x0043, return_status[1]);
7500 write_byte(0x0040, 0x0044, return_status[2]);
7501 write_byte(0x0040, 0x0045, return_status[3]);
7502 write_byte(0x0040, 0x0046, return_status[4]);
7503 write_byte(0x0040, 0x0047, return_status[5]);
7504 write_byte(0x0040, 0x0048, return_status[6]);
7506 if ( (return_status[0] & 0xc0) != 0 ) {
7507 if ( (return_status[1] & 0x02) != 0 ) {
7508 // diskette not writable.
7509 // AH=status code=0x03 (tried to write on write-protected disk)
7510 // AL=number of sectors written=0
7511 AX = 0x0300;
7512 SET_CF();
7513 return;
7514 } else {
7515 BX_PANIC("int13_diskette_function: read error\n");
7519 // ??? should track be new val from return_status[3] ?
7520 set_diskette_current_cyl(drive, track);
7521 // AL = number of sectors read (same value as passed)
7522 SET_AH(0x00); // success
7523 CLEAR_CF(); // success
7524 return;
7525 } else { // if (ah == 0x04)
7526 // Verify Diskette Sectors
7528 // ??? should track be new val from return_status[3] ?
7529 set_diskette_current_cyl(drive, track);
7530 // AL = number of sectors verified (same value as passed)
7531 CLEAR_CF(); // success
7532 SET_AH(0x00); // success
7533 return;
7535 break;
7537 case 0x05: // format diskette track
7538 BX_DEBUG_INT13_FL("floppy f05\n");
7540 num_sectors = GET_AL();
7541 track = GET_CH();
7542 head = GET_DH();
7543 drive = GET_ELDL();
7545 if ((drive > 1) || (head > 1) || (track > 79) ||
7546 (num_sectors == 0) || (num_sectors > 18)) {
7547 SET_AH(1);
7548 set_diskette_ret_status(1);
7549 SET_CF(); // error occurred
7552 // see if drive exists
7553 if (floppy_drive_exists(drive) == 0) {
7554 SET_AH(0x80); // drive not responding
7555 set_diskette_ret_status(0x80);
7556 SET_CF(); // error occurred
7557 return;
7560 // see if media in drive, and type is known
7561 if (floppy_media_known(drive) == 0) {
7562 if (floppy_media_sense(drive) == 0) {
7563 SET_AH(0x0C); // Media type not found
7564 set_diskette_ret_status(0x0C);
7565 SET_AL(0); // no sectors read
7566 SET_CF(); // error occurred
7567 return;
7571 // set up DMA controller for transfer
7572 page = (ES >> 12); // upper 4 bits
7573 base_es = (ES << 4); // lower 16bits contributed by ES
7574 base_address = base_es + BX; // lower 16 bits of address
7575 // contributed by ES:BX
7576 if ( base_address < base_es ) {
7577 // in case of carry, adjust page by 1
7578 page++;
7580 base_count = (num_sectors * 4) - 1;
7582 // check for 64K boundary overrun
7583 last_addr = base_address + base_count;
7584 if (last_addr < base_address) {
7585 SET_AH(0x09);
7586 set_diskette_ret_status(0x09);
7587 SET_AL(0); // no sectors read
7588 SET_CF(); // error occurred
7589 return;
7592 outb(0x000a, 0x06);
7593 outb(0x000c, 0x00); // clear flip-flop
7594 outb(0x0004, base_address);
7595 outb(0x0004, base_address>>8);
7596 outb(0x000c, 0x00); // clear flip-flop
7597 outb(0x0005, base_count);
7598 outb(0x0005, base_count>>8);
7599 mode_register = 0x4a; // single mode, increment, autoinit disable,
7600 // transfer type=read, channel 2
7601 outb(0x000b, mode_register);
7602 // port 81: DMA-1 Page Register, channel 2
7603 outb(0x0081, page);
7604 outb(0x000a, 0x02);
7606 // set up floppy controller for transfer
7607 floppy_prepare_controller(drive);
7609 // send format-track command (6 bytes) to controller
7610 outb(0x03f5, 0x4d); // 4d: format track
7611 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7612 outb(0x03f5, 2); // 512 byte sector size
7613 outb(0x03f5, num_sectors); // number of sectors per track
7614 outb(0x03f5, 0); // Gap length
7615 outb(0x03f5, 0xf6); // Fill byte
7616 // turn on interrupts
7617 ASM_START
7619 ASM_END
7621 // wait on 40:3e bit 7 to become 1
7622 do {
7623 val8 = read_byte(0x0040, 0x0040);
7624 if (val8 == 0) {
7625 floppy_reset_controller();
7626 SET_AH(0x80); // drive not ready (timeout)
7627 set_diskette_ret_status(0x80);
7628 SET_CF(); // error occurred
7629 return;
7631 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7632 } while ( val8 == 0 );
7634 val8 = 0; // separate asm from while() loop
7635 // turn off interrupts
7636 ASM_START
7638 ASM_END
7639 // set 40:3e bit 7 to 0
7640 val8 = read_byte(0x0040, 0x003e);
7641 val8 &= 0x7f;
7642 write_byte(0x0040, 0x003e, val8);
7643 // check port 3f4 for accessibility to status bytes
7644 val8 = inb(0x3f4);
7645 if ( (val8 & 0xc0) != 0xc0 )
7646 BX_PANIC("int13_diskette: ctrl not ready\n");
7648 // read 7 return status bytes from controller
7649 // using loop index broken, have to unroll...
7650 return_status[0] = inb(0x3f5);
7651 return_status[1] = inb(0x3f5);
7652 return_status[2] = inb(0x3f5);
7653 return_status[3] = inb(0x3f5);
7654 return_status[4] = inb(0x3f5);
7655 return_status[5] = inb(0x3f5);
7656 return_status[6] = inb(0x3f5);
7657 // record in BIOS Data Area
7658 write_byte(0x0040, 0x0042, return_status[0]);
7659 write_byte(0x0040, 0x0043, return_status[1]);
7660 write_byte(0x0040, 0x0044, return_status[2]);
7661 write_byte(0x0040, 0x0045, return_status[3]);
7662 write_byte(0x0040, 0x0046, return_status[4]);
7663 write_byte(0x0040, 0x0047, return_status[5]);
7664 write_byte(0x0040, 0x0048, return_status[6]);
7666 if ( (return_status[0] & 0xc0) != 0 ) {
7667 if ( (return_status[1] & 0x02) != 0 ) {
7668 // diskette not writable.
7669 // AH=status code=0x03 (tried to write on write-protected disk)
7670 // AL=number of sectors written=0
7671 AX = 0x0300;
7672 SET_CF();
7673 return;
7674 } else {
7675 BX_PANIC("int13_diskette_function: write error\n");
7679 SET_AH(0);
7680 set_diskette_ret_status(0);
7681 set_diskette_current_cyl(drive, 0);
7682 CLEAR_CF(); // successful
7683 return;
7686 case 0x08: // read diskette drive parameters
7687 BX_DEBUG_INT13_FL("floppy f08\n");
7688 drive = GET_ELDL();
7690 if (drive > 1) {
7691 AX = 0;
7692 BX = 0;
7693 CX = 0;
7694 DX = 0;
7695 ES = 0;
7696 DI = 0;
7697 SET_DL(num_floppies);
7698 SET_CF();
7699 return;
7702 drive_type = inb_cmos(0x10);
7703 num_floppies = 0;
7704 if (drive_type & 0xf0)
7705 num_floppies++;
7706 if (drive_type & 0x0f)
7707 num_floppies++;
7709 if (drive == 0)
7710 drive_type >>= 4;
7711 else
7712 drive_type &= 0x0f;
7714 SET_BH(0);
7715 SET_BL(drive_type);
7716 SET_AH(0);
7717 SET_AL(0);
7718 SET_DL(num_floppies);
7720 switch (drive_type) {
7721 case 0: // none
7722 CX = 0;
7723 SET_DH(0); // max head #
7724 break;
7726 case 1: // 360KB, 5.25"
7727 CX = 0x2709; // 40 tracks, 9 sectors
7728 SET_DH(1); // max head #
7729 break;
7731 case 2: // 1.2MB, 5.25"
7732 CX = 0x4f0f; // 80 tracks, 15 sectors
7733 SET_DH(1); // max head #
7734 break;
7736 case 3: // 720KB, 3.5"
7737 CX = 0x4f09; // 80 tracks, 9 sectors
7738 SET_DH(1); // max head #
7739 break;
7741 case 4: // 1.44MB, 3.5"
7742 CX = 0x4f12; // 80 tracks, 18 sectors
7743 SET_DH(1); // max head #
7744 break;
7746 case 5: // 2.88MB, 3.5"
7747 CX = 0x4f24; // 80 tracks, 36 sectors
7748 SET_DH(1); // max head #
7749 break;
7751 case 6: // 160k, 5.25"
7752 CX = 0x2708; // 40 tracks, 8 sectors
7753 SET_DH(0); // max head #
7754 break;
7756 case 7: // 180k, 5.25"
7757 CX = 0x2709; // 40 tracks, 9 sectors
7758 SET_DH(0); // max head #
7759 break;
7761 case 8: // 320k, 5.25"
7762 CX = 0x2708; // 40 tracks, 8 sectors
7763 SET_DH(1); // max head #
7764 break;
7766 default: // ?
7767 BX_PANIC("floppy: int13: bad floppy type\n");
7770 /* set es & di to point to 11 byte diskette param table in ROM */
7771 ASM_START
7772 push bp
7773 mov bp, sp
7774 mov ax, #diskette_param_table2
7775 mov _int13_diskette_function.DI+2[bp], ax
7776 mov _int13_diskette_function.ES+2[bp], cs
7777 pop bp
7778 ASM_END
7779 CLEAR_CF(); // success
7780 /* disk status not changed upon success */
7781 return;
7784 case 0x15: // read diskette drive type
7785 BX_DEBUG_INT13_FL("floppy f15\n");
7786 drive = GET_ELDL();
7787 if (drive > 1) {
7788 SET_AH(0); // only 2 drives supported
7789 // set_diskette_ret_status here ???
7790 SET_CF();
7791 return;
7793 drive_type = inb_cmos(0x10);
7795 if (drive == 0)
7796 drive_type >>= 4;
7797 else
7798 drive_type &= 0x0f;
7799 CLEAR_CF(); // successful, not present
7800 if (drive_type==0) {
7801 SET_AH(0); // drive not present
7803 else {
7804 SET_AH(1); // drive present, does not support change line
7807 return;
7809 case 0x16: // get diskette change line status
7810 BX_DEBUG_INT13_FL("floppy f16\n");
7811 drive = GET_ELDL();
7812 if (drive > 1) {
7813 SET_AH(0x01); // invalid drive
7814 set_diskette_ret_status(0x01);
7815 SET_CF();
7816 return;
7819 SET_AH(0x06); // change line not supported
7820 set_diskette_ret_status(0x06);
7821 SET_CF();
7822 return;
7824 case 0x17: // set diskette type for format(old)
7825 BX_DEBUG_INT13_FL("floppy f17\n");
7826 /* not used for 1.44M floppies */
7827 SET_AH(0x01); // not supported
7828 set_diskette_ret_status(1); /* not supported */
7829 SET_CF();
7830 return;
7832 case 0x18: // set diskette type for format(new)
7833 BX_DEBUG_INT13_FL("floppy f18\n");
7834 SET_AH(0x01); // do later
7835 set_diskette_ret_status(1);
7836 SET_CF();
7837 return;
7839 default:
7840 BX_INFO("int13_diskette: unsupported AH=%02x\n", GET_AH());
7842 // if ( (ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e) ) {
7843 SET_AH(0x01); // ???
7844 set_diskette_ret_status(1);
7845 SET_CF();
7846 return;
7847 // }
7850 #else // #if BX_SUPPORT_FLOPPY
7851 void
7852 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
7853 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
7855 Bit8u val8;
7857 switch ( GET_AH() ) {
7859 case 0x01: // Read Diskette Status
7860 CLEAR_CF();
7861 val8 = read_byte(0x0000, 0x0441);
7862 SET_AH(val8);
7863 if (val8) {
7864 SET_CF();
7866 return;
7868 default:
7869 SET_CF();
7870 write_byte(0x0000, 0x0441, 0x01);
7871 SET_AH(0x01);
7874 #endif // #if BX_SUPPORT_FLOPPY
7876 void
7877 set_diskette_ret_status(value)
7878 Bit8u value;
7880 write_byte(0x0040, 0x0041, value);
7883 void
7884 set_diskette_current_cyl(drive, cyl)
7885 Bit8u drive;
7886 Bit8u cyl;
7888 if (drive > 1)
7889 BX_PANIC("set_diskette_current_cyl(): drive > 1\n");
7890 write_byte(0x0040, 0x0094+drive, cyl);
7893 void
7894 determine_floppy_media(drive)
7895 Bit16u drive;
7897 #if 0
7898 Bit8u val8, DOR, ctrl_info;
7900 ctrl_info = read_byte(0x0040, 0x008F);
7901 if (drive==1)
7902 ctrl_info >>= 4;
7903 else
7904 ctrl_info &= 0x0f;
7906 #if 0
7907 if (drive == 0) {
7908 DOR = 0x1c; // DOR: drive0 motor on, DMA&int enabled, normal op, drive select 0
7910 else {
7911 DOR = 0x2d; // DOR: drive1 motor on, DMA&int enabled, normal op, drive select 1
7913 #endif
7915 if ( (ctrl_info & 0x04) != 0x04 ) {
7916 // Drive not determined means no drive exists, done.
7917 return;
7920 #if 0
7921 // check Main Status Register for readiness
7922 val8 = inb(0x03f4) & 0x80; // Main Status Register
7923 if (val8 != 0x80)
7924 BX_PANIC("d_f_m: MRQ bit not set\n");
7926 // change line
7928 // existing BDA values
7930 // turn on drive motor
7931 outb(0x03f2, DOR); // Digital Output Register
7933 #endif
7934 BX_PANIC("d_f_m: OK so far\n");
7935 #endif
7938 void
7939 int17_function(regs, ds, iret_addr)
7940 pusha_regs_t regs; // regs pushed from PUSHA instruction
7941 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7942 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7944 Bit16u addr,timeout;
7945 Bit8u val8;
7947 ASM_START
7949 ASM_END
7951 addr = read_word(0x0040, (regs.u.r16.dx << 1) + 8);
7952 if ((regs.u.r8.ah < 3) && (regs.u.r16.dx < 3) && (addr > 0)) {
7953 timeout = read_byte(0x0040, 0x0078 + regs.u.r16.dx) << 8;
7954 if (regs.u.r8.ah == 0) {
7955 outb(addr, regs.u.r8.al);
7956 val8 = inb(addr+2);
7957 outb(addr+2, val8 | 0x01); // send strobe
7958 ASM_START
7960 ASM_END
7961 outb(addr+2, val8 & ~0x01);
7962 while (((inb(addr+1) & 0x40) == 0x40) && (timeout)) {
7963 timeout--;
7966 if (regs.u.r8.ah == 1) {
7967 val8 = inb(addr+2);
7968 outb(addr+2, val8 & ~0x04); // send init
7969 ASM_START
7971 ASM_END
7972 outb(addr+2, val8 | 0x04);
7974 val8 = inb(addr+1);
7975 regs.u.r8.ah = (val8 ^ 0x48);
7976 if (!timeout) regs.u.r8.ah |= 0x01;
7977 ClearCF(iret_addr.flags);
7978 } else {
7979 SetCF(iret_addr.flags); // Unsupported
7983 void
7984 int19_function(seq_nr)
7985 Bit16u seq_nr;
7987 Bit16u ebda_seg=read_word(0x0040,0x000E);
7988 Bit16u bootdev;
7989 Bit8u bootdrv;
7990 Bit8u bootchk;
7991 Bit16u bootseg;
7992 Bit16u bootip;
7993 Bit16u status;
7994 Bit16u bootfirst;
7996 ipl_entry_t e;
7998 // if BX_ELTORITO_BOOT is not defined, old behavior
7999 // check bit 5 in CMOS reg 0x2d. load either 0x00 or 0x80 into DL
8000 // in preparation for the intial INT 13h (0=floppy A:, 0x80=C:)
8001 // 0: system boot sequence, first drive C: then A:
8002 // 1: system boot sequence, first drive A: then C:
8003 // else BX_ELTORITO_BOOT is defined
8004 // CMOS regs 0x3D and 0x38 contain the boot sequence:
8005 // CMOS reg 0x3D & 0x0f : 1st boot device
8006 // CMOS reg 0x3D & 0xf0 : 2nd boot device
8007 // CMOS reg 0x38 & 0xf0 : 3rd boot device
8008 // boot device codes:
8009 // 0x00 : not defined
8010 // 0x01 : first floppy
8011 // 0x02 : first harddrive
8012 // 0x03 : first cdrom
8013 // 0x04 - 0x0f : PnP expansion ROMs (e.g. Etherboot)
8014 // else : boot failure
8016 // Get the boot sequence
8017 #if BX_ELTORITO_BOOT
8018 bootdev = inb_cmos(0x3d);
8019 bootdev |= ((inb_cmos(0x38) & 0xf0) << 4);
8020 bootdev >>= 4 * seq_nr;
8021 bootdev &= 0xf;
8023 /* Read user selected device */
8024 bootfirst = read_word(IPL_SEG, IPL_BOOTFIRST_OFFSET);
8025 if (bootfirst != 0xFFFF) {
8026 bootdev = bootfirst;
8027 /* User selected device not set */
8028 write_word(IPL_SEG, IPL_BOOTFIRST_OFFSET, 0xFFFF);
8029 /* Reset boot sequence */
8030 write_word(IPL_SEG, IPL_SEQUENCE_OFFSET, 0xFFFF);
8031 } else if (bootdev == 0) BX_PANIC("No bootable device.\n");
8033 /* Translate from CMOS runes to an IPL table offset by subtracting 1 */
8034 bootdev -= 1;
8035 #else
8036 if (seq_nr ==2) BX_PANIC("No more boot devices.");
8037 if (!!(inb_cmos(0x2d) & 0x20) ^ (seq_nr == 1))
8038 /* Boot from floppy if the bit is set or it's the second boot */
8039 bootdev = 0x00;
8040 else
8041 bootdev = 0x01;
8042 #endif
8044 /* Read the boot device from the IPL table */
8045 if (get_boot_vector(bootdev, &e) == 0) {
8046 BX_INFO("Invalid boot device (0x%x)\n", bootdev);
8047 return;
8050 /* Do the loading, and set up vector as a far pointer to the boot
8051 * address, and bootdrv as the boot drive */
8052 print_boot_device(&e);
8054 switch(e.type) {
8055 case IPL_TYPE_FLOPPY: /* FDD */
8056 case IPL_TYPE_HARDDISK: /* HDD */
8058 bootdrv = (e.type == IPL_TYPE_HARDDISK) ? 0x80 : 0x00;
8059 bootseg = 0x07c0;
8060 status = 0;
8062 ASM_START
8063 push bp
8064 mov bp, sp
8065 push ax
8066 push bx
8067 push cx
8068 push dx
8070 mov dl, _int19_function.bootdrv + 2[bp]
8071 mov ax, _int19_function.bootseg + 2[bp]
8072 mov es, ax ;; segment
8073 xor bx, bx ;; offset
8074 mov ah, #0x02 ;; function 2, read diskette sector
8075 mov al, #0x01 ;; read 1 sector
8076 mov ch, #0x00 ;; track 0
8077 mov cl, #0x01 ;; sector 1
8078 mov dh, #0x00 ;; head 0
8079 int #0x13 ;; read sector
8080 jnc int19_load_done
8081 mov ax, #0x0001
8082 mov _int19_function.status + 2[bp], ax
8084 int19_load_done:
8085 pop dx
8086 pop cx
8087 pop bx
8088 pop ax
8089 pop bp
8090 ASM_END
8092 if (status != 0) {
8093 print_boot_failure(e.type, 1);
8094 return;
8097 /* Always check the signature on a HDD boot sector; on FDD, only do
8098 * the check if the CMOS doesn't tell us to skip it */
8099 if ((e.type != IPL_TYPE_FLOPPY) || !((inb_cmos(0x38) & 0x01))) {
8100 if (read_word(bootseg,0x1fe) != 0xaa55) {
8101 print_boot_failure(e.type, 0);
8102 return;
8106 /* Canonicalize bootseg:bootip */
8107 bootip = (bootseg & 0x0fff) << 4;
8108 bootseg &= 0xf000;
8109 break;
8111 #if BX_ELTORITO_BOOT
8112 case IPL_TYPE_CDROM: /* CD-ROM */
8113 status = cdrom_boot();
8115 // If failure
8116 if ( (status & 0x00ff) !=0 ) {
8117 print_cdromboot_failure(status);
8118 print_boot_failure(e.type, 1);
8119 return;
8122 bootdrv = (Bit8u)(status>>8);
8123 bootseg = read_word(ebda_seg,&EbdaData->cdemu.load_segment);
8124 bootip = 0;
8125 break;
8126 #endif
8128 case IPL_TYPE_BEV: /* Expansion ROM with a Bootstrap Entry Vector (a far pointer) */
8129 bootseg = e.vector >> 16;
8130 bootip = e.vector & 0xffff;
8131 break;
8133 default: return;
8136 /* Debugging info */
8137 BX_INFO("Booting from %x:%x\n", bootseg, bootip);
8139 /* Jump to the boot vector */
8140 ASM_START
8141 mov bp, sp
8142 push cs
8143 push #int18_handler
8144 ;; Build an iret stack frame that will take us to the boot vector.
8145 ;; iret pops ip, then cs, then flags, so push them in the opposite order.
8146 pushf
8147 mov ax, _int19_function.bootseg + 0[bp]
8148 push ax
8149 mov ax, _int19_function.bootip + 0[bp]
8150 push ax
8151 ;; Set the magic number in ax and the boot drive in dl.
8152 mov ax, #0xaa55
8153 mov dl, _int19_function.bootdrv + 0[bp]
8154 ;; Zero some of the other registers.
8155 xor bx, bx
8156 mov ds, bx
8157 mov es, bx
8158 mov bp, bx
8159 ;; Go!
8160 iret
8161 ASM_END
8164 void
8165 int1a_function(regs, ds, iret_addr)
8166 pusha_regs_t regs; // regs pushed from PUSHA instruction
8167 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
8168 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
8170 Bit8u val8;
8172 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);
8174 ASM_START
8176 ASM_END
8178 switch (regs.u.r8.ah) {
8179 case 0: // get current clock count
8180 ASM_START
8182 ASM_END
8183 regs.u.r16.cx = BiosData->ticks_high;
8184 regs.u.r16.dx = BiosData->ticks_low;
8185 regs.u.r8.al = BiosData->midnight_flag;
8186 BiosData->midnight_flag = 0; // reset flag
8187 ASM_START
8189 ASM_END
8190 // AH already 0
8191 ClearCF(iret_addr.flags); // OK
8192 break;
8194 case 1: // Set Current Clock Count
8195 ASM_START
8197 ASM_END
8198 BiosData->ticks_high = regs.u.r16.cx;
8199 BiosData->ticks_low = regs.u.r16.dx;
8200 BiosData->midnight_flag = 0; // reset flag
8201 ASM_START
8203 ASM_END
8204 regs.u.r8.ah = 0;
8205 ClearCF(iret_addr.flags); // OK
8206 break;
8209 case 2: // Read CMOS Time
8210 if (rtc_updating()) {
8211 SetCF(iret_addr.flags);
8212 break;
8215 regs.u.r8.dh = inb_cmos(0x00); // Seconds
8216 regs.u.r8.cl = inb_cmos(0x02); // Minutes
8217 regs.u.r8.ch = inb_cmos(0x04); // Hours
8218 regs.u.r8.dl = inb_cmos(0x0b) & 0x01; // Stat Reg B
8219 regs.u.r8.ah = 0;
8220 regs.u.r8.al = regs.u.r8.ch;
8221 ClearCF(iret_addr.flags); // OK
8222 break;
8224 case 3: // Set CMOS Time
8225 // Using a debugger, I notice the following masking/setting
8226 // of bits in Status Register B, by setting Reg B to
8227 // a few values and getting its value after INT 1A was called.
8229 // try#1 try#2 try#3
8230 // before 1111 1101 0111 1101 0000 0000
8231 // after 0110 0010 0110 0010 0000 0010
8233 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8234 // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
8235 if (rtc_updating()) {
8236 init_rtc();
8237 // fall through as if an update were not in progress
8239 outb_cmos(0x00, regs.u.r8.dh); // Seconds
8240 outb_cmos(0x02, regs.u.r8.cl); // Minutes
8241 outb_cmos(0x04, regs.u.r8.ch); // Hours
8242 // Set Daylight Savings time enabled bit to requested value
8243 val8 = (inb_cmos(0x0b) & 0x60) | 0x02 | (regs.u.r8.dl & 0x01);
8244 // (reg B already selected)
8245 outb_cmos(0x0b, val8);
8246 regs.u.r8.ah = 0;
8247 regs.u.r8.al = val8; // val last written to Reg B
8248 ClearCF(iret_addr.flags); // OK
8249 break;
8251 case 4: // Read CMOS Date
8252 regs.u.r8.ah = 0;
8253 if (rtc_updating()) {
8254 SetCF(iret_addr.flags);
8255 break;
8257 regs.u.r8.cl = inb_cmos(0x09); // Year
8258 regs.u.r8.dh = inb_cmos(0x08); // Month
8259 regs.u.r8.dl = inb_cmos(0x07); // Day of Month
8260 regs.u.r8.ch = inb_cmos(0x32); // Century
8261 regs.u.r8.al = regs.u.r8.ch;
8262 ClearCF(iret_addr.flags); // OK
8263 break;
8265 case 5: // Set CMOS Date
8266 // Using a debugger, I notice the following masking/setting
8267 // of bits in Status Register B, by setting Reg B to
8268 // a few values and getting its value after INT 1A was called.
8270 // try#1 try#2 try#3 try#4
8271 // before 1111 1101 0111 1101 0000 0010 0000 0000
8272 // after 0110 1101 0111 1101 0000 0010 0000 0000
8274 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8275 // My assumption: RegB = (RegB & 01111111b)
8276 if (rtc_updating()) {
8277 init_rtc();
8278 SetCF(iret_addr.flags);
8279 break;
8281 outb_cmos(0x09, regs.u.r8.cl); // Year
8282 outb_cmos(0x08, regs.u.r8.dh); // Month
8283 outb_cmos(0x07, regs.u.r8.dl); // Day of Month
8284 outb_cmos(0x32, regs.u.r8.ch); // Century
8285 val8 = inb_cmos(0x0b) & 0x7f; // clear halt-clock bit
8286 outb_cmos(0x0b, val8);
8287 regs.u.r8.ah = 0;
8288 regs.u.r8.al = val8; // AL = val last written to Reg B
8289 ClearCF(iret_addr.flags); // OK
8290 break;
8292 case 6: // Set Alarm Time in CMOS
8293 // Using a debugger, I notice the following masking/setting
8294 // of bits in Status Register B, by setting Reg B to
8295 // a few values and getting its value after INT 1A was called.
8297 // try#1 try#2 try#3
8298 // before 1101 1111 0101 1111 0000 0000
8299 // after 0110 1111 0111 1111 0010 0000
8301 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8302 // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
8303 val8 = inb_cmos(0x0b); // Get Status Reg B
8304 regs.u.r16.ax = 0;
8305 if (val8 & 0x20) {
8306 // Alarm interrupt enabled already
8307 SetCF(iret_addr.flags); // Error: alarm in use
8308 break;
8310 if (rtc_updating()) {
8311 init_rtc();
8312 // fall through as if an update were not in progress
8314 outb_cmos(0x01, regs.u.r8.dh); // Seconds alarm
8315 outb_cmos(0x03, regs.u.r8.cl); // Minutes alarm
8316 outb_cmos(0x05, regs.u.r8.ch); // Hours alarm
8317 outb(0xa1, inb(0xa1) & 0xfe); // enable IRQ 8
8318 // enable Status Reg B alarm bit, clear halt clock bit
8319 outb_cmos(0x0b, (val8 & 0x7f) | 0x20);
8320 ClearCF(iret_addr.flags); // OK
8321 break;
8323 case 7: // Turn off Alarm
8324 // Using a debugger, I notice the following masking/setting
8325 // of bits in Status Register B, by setting Reg B to
8326 // a few values and getting its value after INT 1A was called.
8328 // try#1 try#2 try#3 try#4
8329 // before 1111 1101 0111 1101 0010 0000 0010 0010
8330 // after 0100 0101 0101 0101 0000 0000 0000 0010
8332 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8333 // My assumption: RegB = (RegB & 01010111b)
8334 val8 = inb_cmos(0x0b); // Get Status Reg B
8335 // clear clock-halt bit, disable alarm bit
8336 outb_cmos(0x0b, val8 & 0x57); // disable alarm bit
8337 regs.u.r8.ah = 0;
8338 regs.u.r8.al = val8; // val last written to Reg B
8339 ClearCF(iret_addr.flags); // OK
8340 break;
8341 #if BX_PCIBIOS
8342 case 0xb1:
8343 // real mode PCI BIOS functions now handled in assembler code
8344 // this C code handles the error code for information only
8345 if (regs.u.r8.bl == 0xff) {
8346 BX_INFO("PCI BIOS: PCI not present\n");
8347 } else if (regs.u.r8.bl == 0x81) {
8348 BX_INFO("unsupported PCI BIOS function 0x%02x\n", regs.u.r8.al);
8349 } else if (regs.u.r8.bl == 0x83) {
8350 BX_INFO("bad PCI vendor ID %04x\n", regs.u.r16.dx);
8351 } else if (regs.u.r8.bl == 0x86) {
8352 if (regs.u.r8.al == 0x02) {
8353 BX_INFO("PCI device %04x:%04x not found at index %d\n", regs.u.r16.dx, regs.u.r16.cx, regs.u.r16.si);
8354 } else {
8355 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);
8358 regs.u.r8.ah = regs.u.r8.bl;
8359 SetCF(iret_addr.flags);
8360 break;
8361 #endif
8363 default:
8364 SetCF(iret_addr.flags); // Unsupported
8368 void
8369 int70_function(regs, ds, iret_addr)
8370 pusha_regs_t regs; // regs pushed from PUSHA instruction
8371 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
8372 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
8374 // INT 70h: IRQ 8 - CMOS RTC interrupt from periodic or alarm modes
8375 Bit8u registerB = 0, registerC = 0;
8377 // Check which modes are enabled and have occurred.
8378 registerB = inb_cmos( 0xB );
8379 registerC = inb_cmos( 0xC );
8381 if( ( registerB & 0x60 ) != 0 ) {
8382 if( ( registerC & 0x20 ) != 0 ) {
8383 // Handle Alarm Interrupt.
8384 ASM_START
8386 int #0x4a
8388 ASM_END
8390 if( ( registerC & 0x40 ) != 0 ) {
8391 // Handle Periodic Interrupt.
8393 if( read_byte( 0x40, 0xA0 ) != 0 ) {
8394 // Wait Interval (Int 15, AH=83) active.
8395 Bit32u time, toggle;
8397 time = read_dword( 0x40, 0x9C ); // Time left in microseconds.
8398 if( time < 0x3D1 ) {
8399 // Done waiting.
8400 Bit16u segment, offset;
8402 segment = read_word( 0x40, 0x98 );
8403 offset = read_word( 0x40, 0x9A );
8404 write_byte( 0x40, 0xA0, 0 ); // Turn of status byte.
8405 outb_cmos( 0xB, registerB & 0x37 ); // Clear the Periodic Interrupt.
8406 write_byte(segment, offset, read_byte(segment, offset) | 0x80 ); // Write to specified flag byte.
8407 } else {
8408 // Continue waiting.
8409 time -= 0x3D1;
8410 write_dword( 0x40, 0x9C, time );
8416 ASM_START
8417 call eoi_both_pics
8418 ASM_END
8422 ASM_START
8423 ;------------------------------------------
8424 ;- INT74h : PS/2 mouse hardware interrupt -
8425 ;------------------------------------------
8426 int74_handler:
8428 pusha
8429 push ds ;; save DS
8430 push #0x00 ;; placeholder for status
8431 push #0x00 ;; placeholder for X
8432 push #0x00 ;; placeholder for Y
8433 push #0x00 ;; placeholder for Z
8434 push #0x00 ;; placeholder for make_far_call boolean
8435 call _int74_function
8436 pop cx ;; remove make_far_call from stack
8437 jcxz int74_done
8439 ;; make far call to EBDA:0022
8440 push #0x00
8441 pop ds
8442 push 0x040E ;; push 0000:040E (opcodes 0xff, 0x36, 0x0E, 0x04)
8443 pop ds
8444 //CALL_EP(0x0022) ;; call far routine (call_Ep DS:0022 :opcodes 0xff, 0x1e, 0x22, 0x00)
8445 call far ptr[0x22]
8446 int74_done:
8448 call eoi_both_pics
8449 add sp, #8 ;; pop status, x, y, z
8451 pop ds ;; restore DS
8452 popa
8453 iret
8456 ;; This will perform an IRET, but will retain value of current CF
8457 ;; by altering flags on stack. Better than RETF #02.
8458 iret_modify_cf:
8459 jc carry_set
8460 push bp
8461 mov bp, sp
8462 and BYTE [bp + 0x06], #0xfe
8463 pop bp
8464 iret
8465 carry_set:
8466 push bp
8467 mov bp, sp
8468 or BYTE [bp + 0x06], #0x01
8469 pop bp
8470 iret
8473 ;----------------------
8474 ;- INT13h (relocated) -
8475 ;----------------------
8477 ; int13_relocated is a little bit messed up since I played with it
8478 ; I have to rewrite it:
8479 ; - call a function that detect which function to call
8480 ; - make all called C function get the same parameters list
8482 int13_relocated:
8484 #if BX_ELTORITO_BOOT
8485 ;; check for an eltorito function
8486 cmp ah,#0x4a
8487 jb int13_not_eltorito
8488 cmp ah,#0x4d
8489 ja int13_not_eltorito
8491 pusha
8492 push es
8493 push ds
8494 push ss
8495 pop ds
8497 push #int13_out
8498 jmp _int13_eltorito ;; ELDX not used
8500 int13_not_eltorito:
8501 push ax
8502 push bx
8503 push cx
8504 push dx
8506 ;; check if emulation active
8507 call _cdemu_isactive
8508 cmp al,#0x00
8509 je int13_cdemu_inactive
8511 ;; check if access to the emulated drive
8512 call _cdemu_emulated_drive
8513 pop dx
8514 push dx
8515 cmp al,dl ;; int13 on emulated drive
8516 jne int13_nocdemu
8518 pop dx
8519 pop cx
8520 pop bx
8521 pop ax
8523 pusha
8524 push es
8525 push ds
8526 push ss
8527 pop ds
8529 push #int13_out
8530 jmp _int13_cdemu ;; ELDX not used
8532 int13_nocdemu:
8533 and dl,#0xE0 ;; mask to get device class, including cdroms
8534 cmp al,dl ;; al is 0x00 or 0x80
8535 jne int13_cdemu_inactive ;; inactive for device class
8537 pop dx
8538 pop cx
8539 pop bx
8540 pop ax
8542 push ax
8543 push cx
8544 push dx
8545 push bx
8547 dec dl ;; real drive is dl - 1
8548 jmp int13_legacy
8550 int13_cdemu_inactive:
8551 pop dx
8552 pop cx
8553 pop bx
8554 pop ax
8556 #endif // BX_ELTORITO_BOOT
8558 int13_noeltorito:
8560 push ax
8561 push cx
8562 push dx
8563 push bx
8565 int13_legacy:
8567 push dx ;; push eltorito value of dx instead of sp
8569 push bp
8570 push si
8571 push di
8573 push es
8574 push ds
8575 push ss
8576 pop ds
8578 ;; now the 16-bit registers can be restored with:
8579 ;; pop ds; pop es; popa; iret
8580 ;; arguments passed to functions should be
8581 ;; DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS
8583 test dl, #0x80
8584 jnz int13_notfloppy
8586 push #int13_out
8587 jmp _int13_diskette_function
8589 int13_notfloppy:
8591 #if BX_USE_ATADRV
8593 cmp dl, #0xE0
8594 jb int13_notcdrom
8596 // ebx is modified: BSD 5.2.1 boot loader problem
8597 // someone should figure out which 32 bit register that actually are used
8599 shr ebx, #16
8600 push bx
8602 call _int13_cdrom
8604 pop bx
8605 shl ebx, #16
8607 jmp int13_out
8609 int13_notcdrom:
8611 #endif
8613 int13_disk:
8614 ;; int13_harddisk modifies high word of EAX
8615 shr eax, #16
8616 push ax
8617 call _int13_harddisk
8618 pop ax
8619 shl eax, #16
8621 int13_out:
8622 pop ds
8623 pop es
8624 popa
8625 iret
8627 ;----------
8628 ;- INT18h -
8629 ;----------
8630 int18_handler: ;; Boot Failure recovery: try the next device.
8632 ;; Reset SP and SS
8633 mov ax, #0xfffe
8634 mov sp, ax
8635 xor ax, ax
8636 mov ss, ax
8638 ;; Get the boot sequence number out of the IPL memory
8639 mov bx, #IPL_SEG
8640 mov ds, bx ;; Set segment
8641 mov bx, IPL_SEQUENCE_OFFSET ;; BX is now the sequence number
8642 inc bx ;; ++
8643 mov IPL_SEQUENCE_OFFSET, bx ;; Write it back
8644 mov ds, ax ;; and reset the segment to zero.
8646 ;; Carry on in the INT 19h handler, using the new sequence number
8647 push bx
8649 jmp int19_next_boot
8651 ;----------
8652 ;- INT19h -
8653 ;----------
8654 int19_relocated: ;; Boot function, relocated
8656 ;; int19 was beginning to be really complex, so now it
8657 ;; just calls a C function that does the work
8659 push bp
8660 mov bp, sp
8662 ;; Reset SS and SP
8663 mov ax, #0xfffe
8664 mov sp, ax
8665 xor ax, ax
8666 mov ss, ax
8668 ;; Start from the first boot device (0, in AX)
8669 mov bx, #IPL_SEG
8670 mov ds, bx ;; Set segment to write to the IPL memory
8671 mov IPL_SEQUENCE_OFFSET, ax ;; Save the sequence number
8672 mov ds, ax ;; and reset the segment.
8674 push ax
8676 int19_next_boot:
8678 ;; Call the C code for the next boot device
8679 call _int19_function
8681 ;; Boot failed: invoke the boot recovery function
8682 int #0x18
8684 ;----------
8685 ;- INT1Ch -
8686 ;----------
8687 int1c_handler: ;; User Timer Tick
8688 iret
8691 ;----------------------
8692 ;- POST: Floppy Drive -
8693 ;----------------------
8694 floppy_drive_post:
8695 xor ax, ax
8696 mov ds, ax
8698 mov al, #0x00
8699 mov 0x043e, al ;; drive 0 & 1 uncalibrated, no interrupt has occurred
8701 mov 0x043f, al ;; diskette motor status: read op, drive0, motors off
8703 mov 0x0440, al ;; diskette motor timeout counter: not active
8704 mov 0x0441, al ;; diskette controller status return code
8706 mov 0x0442, al ;; disk & diskette controller status register 0
8707 mov 0x0443, al ;; diskette controller status register 1
8708 mov 0x0444, al ;; diskette controller status register 2
8709 mov 0x0445, al ;; diskette controller cylinder number
8710 mov 0x0446, al ;; diskette controller head number
8711 mov 0x0447, al ;; diskette controller sector number
8712 mov 0x0448, al ;; diskette controller bytes written
8714 mov 0x048b, al ;; diskette configuration data
8716 ;; -----------------------------------------------------------------
8717 ;; (048F) diskette controller information
8719 mov al, #0x10 ;; get CMOS diskette drive type
8720 out 0x70, AL
8721 in AL, 0x71
8722 mov ah, al ;; save byte to AH
8724 look_drive0:
8725 shr al, #4 ;; look at top 4 bits for drive 0
8726 jz f0_missing ;; jump if no drive0
8727 mov bl, #0x07 ;; drive0 determined, multi-rate, has changed line
8728 jmp look_drive1
8729 f0_missing:
8730 mov bl, #0x00 ;; no drive0
8732 look_drive1:
8733 mov al, ah ;; restore from AH
8734 and al, #0x0f ;; look at bottom 4 bits for drive 1
8735 jz f1_missing ;; jump if no drive1
8736 or bl, #0x70 ;; drive1 determined, multi-rate, has changed line
8737 f1_missing:
8738 ;; leave high bits in BL zerod
8739 mov 0x048f, bl ;; put new val in BDA (diskette controller information)
8740 ;; -----------------------------------------------------------------
8742 mov al, #0x00
8743 mov 0x0490, al ;; diskette 0 media state
8744 mov 0x0491, al ;; diskette 1 media state
8746 ;; diskette 0,1 operational starting state
8747 ;; drive type has not been determined,
8748 ;; has no changed detection line
8749 mov 0x0492, al
8750 mov 0x0493, al
8752 mov 0x0494, al ;; diskette 0 current cylinder
8753 mov 0x0495, al ;; diskette 1 current cylinder
8755 mov al, #0x02
8756 out #0x0a, al ;; clear DMA-1 channel 2 mask bit
8758 SET_INT_VECTOR(0x1E, #0xF000, #diskette_param_table2)
8759 SET_INT_VECTOR(0x40, #0xF000, #int13_diskette)
8760 SET_INT_VECTOR(0x0E, #0xF000, #int0e_handler) ;; IRQ 6
8765 ;--------------------
8766 ;- POST: HARD DRIVE -
8767 ;--------------------
8768 ; relocated here because the primary POST area isnt big enough.
8769 hard_drive_post:
8770 // IRQ 14 = INT 76h
8771 // INT 76h calls INT 15h function ax=9100
8773 mov al, #0x0a ; 0000 1010 = reserved, disable IRQ 14
8774 mov dx, #0x03f6
8775 out dx, al
8777 xor ax, ax
8778 mov ds, ax
8779 mov 0x0474, al /* hard disk status of last operation */
8780 mov 0x0477, al /* hard disk port offset (XT only ???) */
8781 mov 0x048c, al /* hard disk status register */
8782 mov 0x048d, al /* hard disk error register */
8783 mov 0x048e, al /* hard disk task complete flag */
8784 mov al, #0x01
8785 mov 0x0475, al /* hard disk number attached */
8786 mov al, #0xc0
8787 mov 0x0476, al /* hard disk control byte */
8788 SET_INT_VECTOR(0x13, #0xF000, #int13_handler)
8789 SET_INT_VECTOR(0x76, #0xF000, #int76_handler)
8790 ;; INT 41h: hard disk 0 configuration pointer
8791 ;; INT 46h: hard disk 1 configuration pointer
8792 SET_INT_VECTOR(0x41, #EBDA_SEG, #0x003D)
8793 SET_INT_VECTOR(0x46, #EBDA_SEG, #0x004D)
8795 ;; move disk geometry data from CMOS to EBDA disk parameter table(s)
8796 mov al, #0x12
8797 out #0x70, al
8798 in al, #0x71
8799 and al, #0xf0
8800 cmp al, #0xf0
8801 je post_d0_extended
8802 jmp check_for_hd1
8803 post_d0_extended:
8804 mov al, #0x19
8805 out #0x70, al
8806 in al, #0x71
8807 cmp al, #47 ;; decimal 47 - user definable
8808 je post_d0_type47
8809 HALT(__LINE__)
8810 post_d0_type47:
8811 ;; CMOS purpose param table offset
8812 ;; 1b cylinders low 0
8813 ;; 1c cylinders high 1
8814 ;; 1d heads 2
8815 ;; 1e write pre-comp low 5
8816 ;; 1f write pre-comp high 6
8817 ;; 20 retries/bad map/heads>8 8
8818 ;; 21 landing zone low C
8819 ;; 22 landing zone high D
8820 ;; 23 sectors/track E
8822 mov ax, #EBDA_SEG
8823 mov ds, ax
8825 ;;; Filling EBDA table for hard disk 0.
8826 mov al, #0x1f
8827 out #0x70, al
8828 in al, #0x71
8829 mov ah, al
8830 mov al, #0x1e
8831 out #0x70, al
8832 in al, #0x71
8833 mov (0x003d + 0x05), ax ;; write precomp word
8835 mov al, #0x20
8836 out #0x70, al
8837 in al, #0x71
8838 mov (0x003d + 0x08), al ;; drive control byte
8840 mov al, #0x22
8841 out #0x70, al
8842 in al, #0x71
8843 mov ah, al
8844 mov al, #0x21
8845 out #0x70, al
8846 in al, #0x71
8847 mov (0x003d + 0x0C), ax ;; landing zone word
8849 mov al, #0x1c ;; get cylinders word in AX
8850 out #0x70, al
8851 in al, #0x71 ;; high byte
8852 mov ah, al
8853 mov al, #0x1b
8854 out #0x70, al
8855 in al, #0x71 ;; low byte
8856 mov bx, ax ;; BX = cylinders
8858 mov al, #0x1d
8859 out #0x70, al
8860 in al, #0x71
8861 mov cl, al ;; CL = heads
8863 mov al, #0x23
8864 out #0x70, al
8865 in al, #0x71
8866 mov dl, al ;; DL = sectors
8868 cmp bx, #1024
8869 jnbe hd0_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8871 hd0_post_physical_chs:
8872 ;; no logical CHS mapping used, just physical CHS
8873 ;; use Standard Fixed Disk Parameter Table (FDPT)
8874 mov (0x003d + 0x00), bx ;; number of physical cylinders
8875 mov (0x003d + 0x02), cl ;; number of physical heads
8876 mov (0x003d + 0x0E), dl ;; number of physical sectors
8877 jmp check_for_hd1
8879 hd0_post_logical_chs:
8880 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8881 mov (0x003d + 0x09), bx ;; number of physical cylinders
8882 mov (0x003d + 0x0b), cl ;; number of physical heads
8883 mov (0x003d + 0x04), dl ;; number of physical sectors
8884 mov (0x003d + 0x0e), dl ;; number of logical sectors (same)
8885 mov al, #0xa0
8886 mov (0x003d + 0x03), al ;; A0h signature, indicates translated table
8888 cmp bx, #2048
8889 jnbe hd0_post_above_2048
8890 ;; 1024 < c <= 2048 cylinders
8891 shr bx, #0x01
8892 shl cl, #0x01
8893 jmp hd0_post_store_logical
8895 hd0_post_above_2048:
8896 cmp bx, #4096
8897 jnbe hd0_post_above_4096
8898 ;; 2048 < c <= 4096 cylinders
8899 shr bx, #0x02
8900 shl cl, #0x02
8901 jmp hd0_post_store_logical
8903 hd0_post_above_4096:
8904 cmp bx, #8192
8905 jnbe hd0_post_above_8192
8906 ;; 4096 < c <= 8192 cylinders
8907 shr bx, #0x03
8908 shl cl, #0x03
8909 jmp hd0_post_store_logical
8911 hd0_post_above_8192:
8912 ;; 8192 < c <= 16384 cylinders
8913 shr bx, #0x04
8914 shl cl, #0x04
8916 hd0_post_store_logical:
8917 mov (0x003d + 0x00), bx ;; number of physical cylinders
8918 mov (0x003d + 0x02), cl ;; number of physical heads
8919 ;; checksum
8920 mov cl, #0x0f ;; repeat count
8921 mov si, #0x003d ;; offset to disk0 FDPT
8922 mov al, #0x00 ;; sum
8923 hd0_post_checksum_loop:
8924 add al, [si]
8925 inc si
8926 dec cl
8927 jnz hd0_post_checksum_loop
8928 not al ;; now take 2s complement
8929 inc al
8930 mov [si], al
8931 ;;; Done filling EBDA table for hard disk 0.
8934 check_for_hd1:
8935 ;; is there really a second hard disk? if not, return now
8936 mov al, #0x12
8937 out #0x70, al
8938 in al, #0x71
8939 and al, #0x0f
8940 jnz post_d1_exists
8942 post_d1_exists:
8943 ;; check that the hd type is really 0x0f.
8944 cmp al, #0x0f
8945 jz post_d1_extended
8946 HALT(__LINE__)
8947 post_d1_extended:
8948 ;; check that the extended type is 47 - user definable
8949 mov al, #0x1a
8950 out #0x70, al
8951 in al, #0x71
8952 cmp al, #47 ;; decimal 47 - user definable
8953 je post_d1_type47
8954 HALT(__LINE__)
8955 post_d1_type47:
8956 ;; Table for disk1.
8957 ;; CMOS purpose param table offset
8958 ;; 0x24 cylinders low 0
8959 ;; 0x25 cylinders high 1
8960 ;; 0x26 heads 2
8961 ;; 0x27 write pre-comp low 5
8962 ;; 0x28 write pre-comp high 6
8963 ;; 0x29 heads>8 8
8964 ;; 0x2a landing zone low C
8965 ;; 0x2b landing zone high D
8966 ;; 0x2c sectors/track E
8967 ;;; Fill EBDA table for hard disk 1.
8968 mov ax, #EBDA_SEG
8969 mov ds, ax
8970 mov al, #0x28
8971 out #0x70, al
8972 in al, #0x71
8973 mov ah, al
8974 mov al, #0x27
8975 out #0x70, al
8976 in al, #0x71
8977 mov (0x004d + 0x05), ax ;; write precomp word
8979 mov al, #0x29
8980 out #0x70, al
8981 in al, #0x71
8982 mov (0x004d + 0x08), al ;; drive control byte
8984 mov al, #0x2b
8985 out #0x70, al
8986 in al, #0x71
8987 mov ah, al
8988 mov al, #0x2a
8989 out #0x70, al
8990 in al, #0x71
8991 mov (0x004d + 0x0C), ax ;; landing zone word
8993 mov al, #0x25 ;; get cylinders word in AX
8994 out #0x70, al
8995 in al, #0x71 ;; high byte
8996 mov ah, al
8997 mov al, #0x24
8998 out #0x70, al
8999 in al, #0x71 ;; low byte
9000 mov bx, ax ;; BX = cylinders
9002 mov al, #0x26
9003 out #0x70, al
9004 in al, #0x71
9005 mov cl, al ;; CL = heads
9007 mov al, #0x2c
9008 out #0x70, al
9009 in al, #0x71
9010 mov dl, al ;; DL = sectors
9012 cmp bx, #1024
9013 jnbe hd1_post_logical_chs ;; if cylinders > 1024, use translated style CHS
9015 hd1_post_physical_chs:
9016 ;; no logical CHS mapping used, just physical CHS
9017 ;; use Standard Fixed Disk Parameter Table (FDPT)
9018 mov (0x004d + 0x00), bx ;; number of physical cylinders
9019 mov (0x004d + 0x02), cl ;; number of physical heads
9020 mov (0x004d + 0x0E), dl ;; number of physical sectors
9023 hd1_post_logical_chs:
9024 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
9025 mov (0x004d + 0x09), bx ;; number of physical cylinders
9026 mov (0x004d + 0x0b), cl ;; number of physical heads
9027 mov (0x004d + 0x04), dl ;; number of physical sectors
9028 mov (0x004d + 0x0e), dl ;; number of logical sectors (same)
9029 mov al, #0xa0
9030 mov (0x004d + 0x03), al ;; A0h signature, indicates translated table
9032 cmp bx, #2048
9033 jnbe hd1_post_above_2048
9034 ;; 1024 < c <= 2048 cylinders
9035 shr bx, #0x01
9036 shl cl, #0x01
9037 jmp hd1_post_store_logical
9039 hd1_post_above_2048:
9040 cmp bx, #4096
9041 jnbe hd1_post_above_4096
9042 ;; 2048 < c <= 4096 cylinders
9043 shr bx, #0x02
9044 shl cl, #0x02
9045 jmp hd1_post_store_logical
9047 hd1_post_above_4096:
9048 cmp bx, #8192
9049 jnbe hd1_post_above_8192
9050 ;; 4096 < c <= 8192 cylinders
9051 shr bx, #0x03
9052 shl cl, #0x03
9053 jmp hd1_post_store_logical
9055 hd1_post_above_8192:
9056 ;; 8192 < c <= 16384 cylinders
9057 shr bx, #0x04
9058 shl cl, #0x04
9060 hd1_post_store_logical:
9061 mov (0x004d + 0x00), bx ;; number of physical cylinders
9062 mov (0x004d + 0x02), cl ;; number of physical heads
9063 ;; checksum
9064 mov cl, #0x0f ;; repeat count
9065 mov si, #0x004d ;; offset to disk0 FDPT
9066 mov al, #0x00 ;; sum
9067 hd1_post_checksum_loop:
9068 add al, [si]
9069 inc si
9070 dec cl
9071 jnz hd1_post_checksum_loop
9072 not al ;; now take 2s complement
9073 inc al
9074 mov [si], al
9075 ;;; Done filling EBDA table for hard disk 1.
9079 ;--------------------
9080 ;- POST: EBDA segment
9081 ;--------------------
9082 ; relocated here because the primary POST area isnt big enough.
9083 ebda_post:
9084 #if BX_USE_EBDA
9085 mov ax, #EBDA_SEG
9086 mov ds, ax
9087 mov byte ptr [0x0], #EBDA_SIZE
9088 #endif
9089 xor ax, ax ; mov EBDA seg into 40E
9090 mov ds, ax
9091 mov word ptr [0x40E], #EBDA_SEG
9092 ret;;
9094 ;--------------------
9095 ;- POST: EOI + jmp via [0x40:67)
9096 ;--------------------
9097 ; relocated here because the primary POST area isnt big enough.
9098 eoi_jmp_post:
9099 mov al, #0x20
9100 out #0xA0, al ;; slave PIC EOI
9101 mov al, #0x20
9102 out #0x20, al ;; master PIC EOI
9104 jmp_post_0x467:
9105 xor ax, ax
9106 mov ds, ax
9108 jmp far ptr [0x467]
9110 iret_post_0x467:
9111 xor ax, ax
9112 mov ds, ax
9114 mov sp, [0x467]
9115 mov ss, [0x469]
9116 iret
9118 retf_post_0x467:
9119 xor ax, ax
9120 mov ds, ax
9122 mov sp, [0x467]
9123 mov ss, [0x469]
9124 retf
9126 s3_post:
9127 mov sp, #0xffe
9128 #if BX_ROMBIOS32
9129 call rombios32_init
9130 #endif
9131 call _s3_resume
9132 mov bl, #0x00
9133 and ax, ax
9134 jz normal_post
9135 call _s3_resume_panic
9137 ;--------------------
9138 eoi_both_pics:
9139 mov al, #0x20
9140 out #0xA0, al ;; slave PIC EOI
9141 eoi_master_pic:
9142 mov al, #0x20
9143 out #0x20, al ;; master PIC EOI
9146 ;--------------------
9147 BcdToBin:
9148 ;; in: AL in BCD format
9149 ;; out: AL in binary format, AH will always be 0
9150 ;; trashes BX
9151 mov bl, al
9152 and bl, #0x0f ;; bl has low digit
9153 shr al, #4 ;; al has high digit
9154 mov bh, #10
9155 mul al, bh ;; multiply high digit by 10 (result in AX)
9156 add al, bl ;; then add low digit
9159 ;--------------------
9160 timer_tick_post:
9161 ;; Setup the Timer Ticks Count (0x46C:dword) and
9162 ;; Timer Ticks Roller Flag (0x470:byte)
9163 ;; The Timer Ticks Count needs to be set according to
9164 ;; the current CMOS time, as if ticks have been occurring
9165 ;; at 18.2hz since midnight up to this point. Calculating
9166 ;; this is a little complicated. Here are the factors I gather
9167 ;; regarding this. 14,318,180 hz was the original clock speed,
9168 ;; chosen so it could be divided by either 3 to drive the 5Mhz CPU
9169 ;; at the time, or 4 to drive the CGA video adapter. The div3
9170 ;; source was divided again by 4 to feed a 1.193Mhz signal to
9171 ;; the timer. With a maximum 16bit timer count, this is again
9172 ;; divided down by 65536 to 18.2hz.
9174 ;; 14,318,180 Hz clock
9175 ;; /3 = 4,772,726 Hz fed to orginal 5Mhz CPU
9176 ;; /4 = 1,193,181 Hz fed to timer
9177 ;; /65536 (maximum timer count) = 18.20650736 ticks/second
9178 ;; 1 second = 18.20650736 ticks
9179 ;; 1 minute = 1092.390442 ticks
9180 ;; 1 hour = 65543.42651 ticks
9182 ;; Given the values in the CMOS clock, one could calculate
9183 ;; the number of ticks by the following:
9184 ;; ticks = (BcdToBin(seconds) * 18.206507) +
9185 ;; (BcdToBin(minutes) * 1092.3904)
9186 ;; (BcdToBin(hours) * 65543.427)
9187 ;; To get a little more accuracy, since Im using integer
9188 ;; arithmatic, I use:
9189 ;; ticks = (BcdToBin(seconds) * 18206507) / 1000000 +
9190 ;; (BcdToBin(minutes) * 10923904) / 10000 +
9191 ;; (BcdToBin(hours) * 65543427) / 1000
9193 ;; assuming DS=0000
9195 ;; get CMOS seconds
9196 xor eax, eax ;; clear EAX
9197 mov al, #0x00
9198 out #0x70, al
9199 in al, #0x71 ;; AL has CMOS seconds in BCD
9200 call BcdToBin ;; EAX now has seconds in binary
9201 mov edx, #18206507
9202 mul eax, edx
9203 mov ebx, #1000000
9204 xor edx, edx
9205 div eax, ebx
9206 mov ecx, eax ;; ECX will accumulate total ticks
9208 ;; get CMOS minutes
9209 xor eax, eax ;; clear EAX
9210 mov al, #0x02
9211 out #0x70, al
9212 in al, #0x71 ;; AL has CMOS minutes in BCD
9213 call BcdToBin ;; EAX now has minutes in binary
9214 mov edx, #10923904
9215 mul eax, edx
9216 mov ebx, #10000
9217 xor edx, edx
9218 div eax, ebx
9219 add ecx, eax ;; add to total ticks
9221 ;; get CMOS hours
9222 xor eax, eax ;; clear EAX
9223 mov al, #0x04
9224 out #0x70, al
9225 in al, #0x71 ;; AL has CMOS hours in BCD
9226 call BcdToBin ;; EAX now has hours in binary
9227 mov edx, #65543427
9228 mul eax, edx
9229 mov ebx, #1000
9230 xor edx, edx
9231 div eax, ebx
9232 add ecx, eax ;; add to total ticks
9234 mov 0x46C, ecx ;; Timer Ticks Count
9235 xor al, al
9236 mov 0x470, al ;; Timer Ticks Rollover Flag
9239 ;--------------------
9240 int76_handler:
9241 ;; record completion in BIOS task complete flag
9242 push ax
9243 push ds
9244 mov ax, #0x0040
9245 mov ds, ax
9246 mov 0x008E, #0xff
9247 call eoi_both_pics
9248 pop ds
9249 pop ax
9250 iret
9253 ;--------------------
9254 #if BX_APM
9256 use32 386
9257 #define APM_PROT32
9258 #include "apmbios.S"
9260 use16 386
9261 #define APM_PROT16
9262 #include "apmbios.S"
9264 #define APM_REAL
9265 #include "apmbios.S"
9267 #endif
9269 ;--------------------
9270 #if BX_PCIBIOS
9271 use32 386
9272 .align 16
9273 bios32_structure:
9274 db 0x5f, 0x33, 0x32, 0x5f ;; "_32_" signature
9275 dw bios32_entry_point, 0xf ;; 32 bit physical address
9276 db 0 ;; revision level
9277 ;; length in paragraphs and checksum stored in a word to prevent errors
9278 dw (~(((bios32_entry_point >> 8) + (bios32_entry_point & 0xff) + 0x32) \
9279 & 0xff) << 8) + 0x01
9280 db 0,0,0,0,0 ;; reserved
9282 .align 16
9283 bios32_entry_point:
9284 pushfd
9285 cmp eax, #0x49435024 ;; "$PCI"
9286 jne unknown_service
9287 mov eax, #0x80000000
9288 mov dx, #0x0cf8
9289 out dx, eax
9290 mov dx, #0x0cfc
9291 in eax, dx
9292 #ifdef PCI_FIXED_HOST_BRIDGE
9293 cmp eax, #PCI_FIXED_HOST_BRIDGE
9294 jne unknown_service
9295 #else
9296 ;; say ok if a device is present
9297 cmp eax, #0xffffffff
9298 je unknown_service
9299 #endif
9300 mov ebx, #0x000f0000
9301 mov ecx, #0
9302 mov edx, #pcibios_protected
9303 xor al, al
9304 jmp bios32_end
9305 unknown_service:
9306 mov al, #0x80
9307 bios32_end:
9308 #ifdef BX_QEMU
9309 and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
9310 #endif
9311 popfd
9312 retf
9314 .align 16
9315 pcibios_protected:
9316 pushfd
9318 push esi
9319 push edi
9320 cmp al, #0x01 ;; installation check
9321 jne pci_pro_f02
9322 mov bx, #0x0210
9323 mov cx, #0
9324 mov edx, #0x20494350 ;; "PCI "
9325 mov al, #0x01
9326 jmp pci_pro_ok
9327 pci_pro_f02: ;; find pci device
9328 cmp al, #0x02
9329 jne pci_pro_f03
9330 shl ecx, #16
9331 mov cx, dx
9332 xor bx, bx
9333 mov di, #0x00
9334 pci_pro_devloop:
9335 call pci_pro_select_reg
9336 mov dx, #0x0cfc
9337 in eax, dx
9338 cmp eax, ecx
9339 jne pci_pro_nextdev
9340 cmp si, #0
9341 je pci_pro_ok
9342 dec si
9343 pci_pro_nextdev:
9344 inc bx
9345 cmp bx, #0x0100
9346 jne pci_pro_devloop
9347 mov ah, #0x86
9348 jmp pci_pro_fail
9349 pci_pro_f03: ;; find class code
9350 cmp al, #0x03
9351 jne pci_pro_f08
9352 xor bx, bx
9353 mov di, #0x08
9354 pci_pro_devloop2:
9355 call pci_pro_select_reg
9356 mov dx, #0x0cfc
9357 in eax, dx
9358 shr eax, #8
9359 cmp eax, ecx
9360 jne pci_pro_nextdev2
9361 cmp si, #0
9362 je pci_pro_ok
9363 dec si
9364 pci_pro_nextdev2:
9365 inc bx
9366 cmp bx, #0x0100
9367 jne pci_pro_devloop2
9368 mov ah, #0x86
9369 jmp pci_pro_fail
9370 pci_pro_f08: ;; read configuration byte
9371 cmp al, #0x08
9372 jne pci_pro_f09
9373 call pci_pro_select_reg
9374 push edx
9375 mov dx, di
9376 and dx, #0x03
9377 add dx, #0x0cfc
9378 in al, dx
9379 pop edx
9380 mov cl, al
9381 jmp pci_pro_ok
9382 pci_pro_f09: ;; read configuration word
9383 cmp al, #0x09
9384 jne pci_pro_f0a
9385 call pci_pro_select_reg
9386 push edx
9387 mov dx, di
9388 and dx, #0x02
9389 add dx, #0x0cfc
9390 in ax, dx
9391 pop edx
9392 mov cx, ax
9393 jmp pci_pro_ok
9394 pci_pro_f0a: ;; read configuration dword
9395 cmp al, #0x0a
9396 jne pci_pro_f0b
9397 call pci_pro_select_reg
9398 push edx
9399 mov dx, #0x0cfc
9400 in eax, dx
9401 pop edx
9402 mov ecx, eax
9403 jmp pci_pro_ok
9404 pci_pro_f0b: ;; write configuration byte
9405 cmp al, #0x0b
9406 jne pci_pro_f0c
9407 call pci_pro_select_reg
9408 push edx
9409 mov dx, di
9410 and dx, #0x03
9411 add dx, #0x0cfc
9412 mov al, cl
9413 out dx, al
9414 pop edx
9415 jmp pci_pro_ok
9416 pci_pro_f0c: ;; write configuration word
9417 cmp al, #0x0c
9418 jne pci_pro_f0d
9419 call pci_pro_select_reg
9420 push edx
9421 mov dx, di
9422 and dx, #0x02
9423 add dx, #0x0cfc
9424 mov ax, cx
9425 out dx, ax
9426 pop edx
9427 jmp pci_pro_ok
9428 pci_pro_f0d: ;; write configuration dword
9429 cmp al, #0x0d
9430 jne pci_pro_unknown
9431 call pci_pro_select_reg
9432 push edx
9433 mov dx, #0x0cfc
9434 mov eax, ecx
9435 out dx, eax
9436 pop edx
9437 jmp pci_pro_ok
9438 pci_pro_unknown:
9439 mov ah, #0x81
9440 pci_pro_fail:
9441 pop edi
9442 pop esi
9443 #ifdef BX_QEMU
9444 and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
9445 #endif
9446 popfd
9448 retf
9449 pci_pro_ok:
9450 xor ah, ah
9451 pop edi
9452 pop esi
9453 #ifdef BX_QEMU
9454 and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
9455 #endif
9456 popfd
9458 retf
9460 pci_pro_select_reg:
9461 push edx
9462 mov eax, #0x800000
9463 mov ax, bx
9464 shl eax, #8
9465 and di, #0xff
9466 or ax, di
9467 and al, #0xfc
9468 mov dx, #0x0cf8
9469 out dx, eax
9470 pop edx
9473 use16 386
9475 pcibios_real:
9476 push eax
9477 push dx
9478 mov eax, #0x80000000
9479 mov dx, #0x0cf8
9480 out dx, eax
9481 mov dx, #0x0cfc
9482 in eax, dx
9483 #ifdef PCI_FIXED_HOST_BRIDGE
9484 cmp eax, #PCI_FIXED_HOST_BRIDGE
9485 je pci_present
9486 #else
9487 ;; say ok if a device is present
9488 cmp eax, #0xffffffff
9489 jne pci_present
9490 #endif
9491 pop dx
9492 pop eax
9493 mov ah, #0xff
9496 pci_present:
9497 pop dx
9498 pop eax
9499 cmp al, #0x01 ;; installation check
9500 jne pci_real_f02
9501 mov ax, #0x0001
9502 mov bx, #0x0210
9503 mov cx, #0
9504 mov edx, #0x20494350 ;; "PCI "
9505 mov edi, #0xf0000
9506 mov di, #pcibios_protected
9509 pci_real_f02: ;; find pci device
9510 push esi
9511 push edi
9512 cmp al, #0x02
9513 jne pci_real_f03
9514 shl ecx, #16
9515 mov cx, dx
9516 xor bx, bx
9517 mov di, #0x00
9518 pci_real_devloop:
9519 call pci_real_select_reg
9520 mov dx, #0x0cfc
9521 in eax, dx
9522 cmp eax, ecx
9523 jne pci_real_nextdev
9524 cmp si, #0
9525 je pci_real_ok
9526 dec si
9527 pci_real_nextdev:
9528 inc bx
9529 cmp bx, #0x0100
9530 jne pci_real_devloop
9531 mov dx, cx
9532 shr ecx, #16
9533 mov ax, #0x8602
9534 jmp pci_real_fail
9535 pci_real_f03: ;; find class code
9536 cmp al, #0x03
9537 jne pci_real_f08
9538 xor bx, bx
9539 mov di, #0x08
9540 pci_real_devloop2:
9541 call pci_real_select_reg
9542 mov dx, #0x0cfc
9543 in eax, dx
9544 shr eax, #8
9545 cmp eax, ecx
9546 jne pci_real_nextdev2
9547 cmp si, #0
9548 je pci_real_ok
9549 dec si
9550 pci_real_nextdev2:
9551 inc bx
9552 cmp bx, #0x0100
9553 jne pci_real_devloop2
9554 mov dx, cx
9555 shr ecx, #16
9556 mov ax, #0x8603
9557 jmp pci_real_fail
9558 pci_real_f08: ;; read configuration byte
9559 cmp al, #0x08
9560 jne pci_real_f09
9561 call pci_real_select_reg
9562 push dx
9563 mov dx, di
9564 and dx, #0x03
9565 add dx, #0x0cfc
9566 in al, dx
9567 pop dx
9568 mov cl, al
9569 jmp pci_real_ok
9570 pci_real_f09: ;; read configuration word
9571 cmp al, #0x09
9572 jne pci_real_f0a
9573 call pci_real_select_reg
9574 push dx
9575 mov dx, di
9576 and dx, #0x02
9577 add dx, #0x0cfc
9578 in ax, dx
9579 pop dx
9580 mov cx, ax
9581 jmp pci_real_ok
9582 pci_real_f0a: ;; read configuration dword
9583 cmp al, #0x0a
9584 jne pci_real_f0b
9585 call pci_real_select_reg
9586 push dx
9587 mov dx, #0x0cfc
9588 in eax, dx
9589 pop dx
9590 mov ecx, eax
9591 jmp pci_real_ok
9592 pci_real_f0b: ;; write configuration byte
9593 cmp al, #0x0b
9594 jne pci_real_f0c
9595 call pci_real_select_reg
9596 push dx
9597 mov dx, di
9598 and dx, #0x03
9599 add dx, #0x0cfc
9600 mov al, cl
9601 out dx, al
9602 pop dx
9603 jmp pci_real_ok
9604 pci_real_f0c: ;; write configuration word
9605 cmp al, #0x0c
9606 jne pci_real_f0d
9607 call pci_real_select_reg
9608 push dx
9609 mov dx, di
9610 and dx, #0x02
9611 add dx, #0x0cfc
9612 mov ax, cx
9613 out dx, ax
9614 pop dx
9615 jmp pci_real_ok
9616 pci_real_f0d: ;; write configuration dword
9617 cmp al, #0x0d
9618 jne pci_real_f0e
9619 call pci_real_select_reg
9620 push dx
9621 mov dx, #0x0cfc
9622 mov eax, ecx
9623 out dx, eax
9624 pop dx
9625 jmp pci_real_ok
9626 pci_real_f0e: ;; get irq routing options
9627 cmp al, #0x0e
9628 jne pci_real_unknown
9629 SEG ES
9630 cmp word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9631 jb pci_real_too_small
9632 SEG ES
9633 mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9634 pushf
9635 push ds
9636 push es
9637 push cx
9638 push si
9639 push di
9641 mov si, #pci_routing_table_structure_start
9642 push cs
9643 pop ds
9644 SEG ES
9645 mov cx, [di+2]
9646 SEG ES
9647 mov es, [di+4]
9648 mov di, cx
9649 mov cx, #pci_routing_table_structure_end - pci_routing_table_structure_start
9651 movsb
9652 pop di
9653 pop si
9654 pop cx
9655 pop es
9656 pop ds
9657 popf
9658 mov bx, #(1 << 9) | (1 << 11) ;; irq 9 and 11 are used
9659 jmp pci_real_ok
9660 pci_real_too_small:
9661 SEG ES
9662 mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9663 mov ah, #0x89
9664 jmp pci_real_fail
9666 pci_real_unknown:
9667 mov ah, #0x81
9668 pci_real_fail:
9669 pop edi
9670 pop esi
9673 pci_real_ok:
9674 xor ah, ah
9675 pop edi
9676 pop esi
9680 pci_real_select_reg:
9681 push dx
9682 mov eax, #0x800000
9683 mov ax, bx
9684 shl eax, #8
9685 and di, #0xff
9686 or ax, di
9687 and al, #0xfc
9688 mov dx, #0x0cf8
9689 out dx, eax
9690 pop dx
9693 .align 16
9694 pci_routing_table_structure:
9695 db 0x24, 0x50, 0x49, 0x52 ;; "$PIR" signature
9696 db 0, 1 ;; version
9697 dw 32 + (6 * 16) ;; table size
9698 db 0 ;; PCI interrupt router bus
9699 db 0x08 ;; PCI interrupt router DevFunc
9700 dw 0x0000 ;; PCI exclusive IRQs
9701 dw 0x8086 ;; compatible PCI interrupt router vendor ID
9702 dw 0x122e ;; compatible PCI interrupt router device ID
9703 dw 0,0 ;; Miniport data
9704 db 0,0,0,0,0,0,0,0,0,0,0 ;; reserved
9705 db 0x37 ;; checksum
9706 pci_routing_table_structure_start:
9707 ;; first slot entry PCI-to-ISA (embedded)
9708 db 0 ;; pci bus number
9709 db 0x08 ;; pci device number (bit 7-3)
9710 db 0x60 ;; link value INTA#: pointer into PCI2ISA config space
9711 dw 0xdef8 ;; IRQ bitmap INTA#
9712 db 0x61 ;; link value INTB#
9713 dw 0xdef8 ;; IRQ bitmap INTB#
9714 db 0x62 ;; link value INTC#
9715 dw 0xdef8 ;; IRQ bitmap INTC#
9716 db 0x63 ;; link value INTD#
9717 dw 0xdef8 ;; IRQ bitmap INTD#
9718 db 0 ;; physical slot (0 = embedded)
9719 db 0 ;; reserved
9720 ;; second slot entry: 1st PCI slot
9721 db 0 ;; pci bus number
9722 db 0x10 ;; pci device number (bit 7-3)
9723 db 0x61 ;; link value INTA#
9724 dw 0xdef8 ;; IRQ bitmap INTA#
9725 db 0x62 ;; link value INTB#
9726 dw 0xdef8 ;; IRQ bitmap INTB#
9727 db 0x63 ;; link value INTC#
9728 dw 0xdef8 ;; IRQ bitmap INTC#
9729 db 0x60 ;; link value INTD#
9730 dw 0xdef8 ;; IRQ bitmap INTD#
9731 db 1 ;; physical slot (0 = embedded)
9732 db 0 ;; reserved
9733 ;; third slot entry: 2nd PCI slot
9734 db 0 ;; pci bus number
9735 db 0x18 ;; pci device number (bit 7-3)
9736 db 0x62 ;; link value INTA#
9737 dw 0xdef8 ;; IRQ bitmap INTA#
9738 db 0x63 ;; link value INTB#
9739 dw 0xdef8 ;; IRQ bitmap INTB#
9740 db 0x60 ;; link value INTC#
9741 dw 0xdef8 ;; IRQ bitmap INTC#
9742 db 0x61 ;; link value INTD#
9743 dw 0xdef8 ;; IRQ bitmap INTD#
9744 db 2 ;; physical slot (0 = embedded)
9745 db 0 ;; reserved
9746 ;; 4th slot entry: 3rd PCI slot
9747 db 0 ;; pci bus number
9748 db 0x20 ;; pci device number (bit 7-3)
9749 db 0x63 ;; link value INTA#
9750 dw 0xdef8 ;; IRQ bitmap INTA#
9751 db 0x60 ;; link value INTB#
9752 dw 0xdef8 ;; IRQ bitmap INTB#
9753 db 0x61 ;; link value INTC#
9754 dw 0xdef8 ;; IRQ bitmap INTC#
9755 db 0x62 ;; link value INTD#
9756 dw 0xdef8 ;; IRQ bitmap INTD#
9757 db 3 ;; physical slot (0 = embedded)
9758 db 0 ;; reserved
9759 ;; 5th slot entry: 4rd PCI slot
9760 db 0 ;; pci bus number
9761 db 0x28 ;; pci device number (bit 7-3)
9762 db 0x60 ;; link value INTA#
9763 dw 0xdef8 ;; IRQ bitmap INTA#
9764 db 0x61 ;; link value INTB#
9765 dw 0xdef8 ;; IRQ bitmap INTB#
9766 db 0x62 ;; link value INTC#
9767 dw 0xdef8 ;; IRQ bitmap INTC#
9768 db 0x63 ;; link value INTD#
9769 dw 0xdef8 ;; IRQ bitmap INTD#
9770 db 4 ;; physical slot (0 = embedded)
9771 db 0 ;; reserved
9772 ;; 6th slot entry: 5rd PCI slot
9773 db 0 ;; pci bus number
9774 db 0x30 ;; pci device number (bit 7-3)
9775 db 0x61 ;; link value INTA#
9776 dw 0xdef8 ;; IRQ bitmap INTA#
9777 db 0x62 ;; link value INTB#
9778 dw 0xdef8 ;; IRQ bitmap INTB#
9779 db 0x63 ;; link value INTC#
9780 dw 0xdef8 ;; IRQ bitmap INTC#
9781 db 0x60 ;; link value INTD#
9782 dw 0xdef8 ;; IRQ bitmap INTD#
9783 db 5 ;; physical slot (0 = embedded)
9784 db 0 ;; reserved
9785 pci_routing_table_structure_end:
9787 #if !BX_ROMBIOS32
9788 pci_irq_list:
9789 db 11, 10, 9, 5;
9791 pcibios_init_sel_reg:
9792 push eax
9793 mov eax, #0x800000
9794 mov ax, bx
9795 shl eax, #8
9796 and dl, #0xfc
9797 or al, dl
9798 mov dx, #0x0cf8
9799 out dx, eax
9800 pop eax
9803 pcibios_init_iomem_bases:
9804 push bp
9805 mov bp, sp
9806 mov eax, #0xe0000000 ;; base for memory init
9807 push eax
9808 mov ax, #0xc000 ;; base for i/o init
9809 push ax
9810 mov ax, #0x0010 ;; start at base address #0
9811 push ax
9812 mov bx, #0x0008
9813 pci_init_io_loop1:
9814 mov dl, #0x00
9815 call pcibios_init_sel_reg
9816 mov dx, #0x0cfc
9817 in ax, dx
9818 cmp ax, #0xffff
9819 jz next_pci_dev
9820 mov dl, #0x04 ;; disable i/o and memory space access
9821 call pcibios_init_sel_reg
9822 mov dx, #0x0cfc
9823 in al, dx
9824 and al, #0xfc
9825 out dx, al
9826 pci_init_io_loop2:
9827 mov dl, [bp-8]
9828 call pcibios_init_sel_reg
9829 mov dx, #0x0cfc
9830 in eax, dx
9831 test al, #0x01
9832 jnz init_io_base
9833 mov ecx, eax
9834 mov eax, #0xffffffff
9835 out dx, eax
9836 in eax, dx
9837 cmp eax, ecx
9838 je next_pci_base
9839 xor eax, #0xffffffff
9840 mov ecx, eax
9841 mov eax, [bp-4]
9842 out dx, eax
9843 add eax, ecx ;; calculate next free mem base
9844 add eax, #0x01000000
9845 and eax, #0xff000000
9846 mov [bp-4], eax
9847 jmp next_pci_base
9848 init_io_base:
9849 mov cx, ax
9850 mov ax, #0xffff
9851 out dx, ax
9852 in ax, dx
9853 cmp ax, cx
9854 je next_pci_base
9855 xor ax, #0xfffe
9856 mov cx, ax
9857 mov ax, [bp-6]
9858 out dx, ax
9859 add ax, cx ;; calculate next free i/o base
9860 add ax, #0x0100
9861 and ax, #0xff00
9862 mov [bp-6], ax
9863 next_pci_base:
9864 mov al, [bp-8]
9865 add al, #0x04
9866 cmp al, #0x28
9867 je enable_iomem_space
9868 mov byte ptr[bp-8], al
9869 jmp pci_init_io_loop2
9870 enable_iomem_space:
9871 mov dl, #0x04 ;; enable i/o and memory space access if available
9872 call pcibios_init_sel_reg
9873 mov dx, #0x0cfc
9874 in al, dx
9875 or al, #0x07
9876 out dx, al
9877 next_pci_dev:
9878 mov byte ptr[bp-8], #0x10
9879 inc bx
9880 cmp bx, #0x0100
9881 jne pci_init_io_loop1
9882 mov sp, bp
9883 pop bp
9886 pcibios_init_set_elcr:
9887 push ax
9888 push cx
9889 mov dx, #0x04d0
9890 test al, #0x08
9891 jz is_master_pic
9892 inc dx
9893 and al, #0x07
9894 is_master_pic:
9895 mov cl, al
9896 mov bl, #0x01
9897 shl bl, cl
9898 in al, dx
9899 or al, bl
9900 out dx, al
9901 pop cx
9902 pop ax
9905 pcibios_init_irqs:
9906 push ds
9907 push bp
9908 mov ax, #0xf000
9909 mov ds, ax
9910 mov dx, #0x04d0 ;; reset ELCR1 + ELCR2
9911 mov al, #0x00
9912 out dx, al
9913 inc dx
9914 out dx, al
9915 mov si, #pci_routing_table_structure
9916 mov bh, [si+8]
9917 mov bl, [si+9]
9918 mov dl, #0x00
9919 call pcibios_init_sel_reg
9920 mov dx, #0x0cfc
9921 in ax, dx
9922 cmp ax, [si+12] ;; check irq router
9923 jne pci_init_end
9924 mov dl, [si+34]
9925 call pcibios_init_sel_reg
9926 push bx ;; save irq router bus + devfunc
9927 mov dx, #0x0cfc
9928 mov ax, #0x8080
9929 out dx, ax ;; reset PIRQ route control
9930 add dx, #2
9931 out dx, ax
9932 mov ax, [si+6]
9933 sub ax, #0x20
9934 shr ax, #4
9935 mov cx, ax
9936 add si, #0x20 ;; set pointer to 1st entry
9937 mov bp, sp
9938 mov ax, #pci_irq_list
9939 push ax
9940 xor ax, ax
9941 push ax
9942 pci_init_irq_loop1:
9943 mov bh, [si]
9944 mov bl, [si+1]
9945 pci_init_irq_loop2:
9946 mov dl, #0x00
9947 call pcibios_init_sel_reg
9948 mov dx, #0x0cfc
9949 in ax, dx
9950 cmp ax, #0xffff
9951 jnz pci_test_int_pin
9952 test bl, #0x07
9953 jz next_pir_entry
9954 jmp next_pci_func
9955 pci_test_int_pin:
9956 mov dl, #0x3c
9957 call pcibios_init_sel_reg
9958 mov dx, #0x0cfd
9959 in al, dx
9960 and al, #0x07
9961 jz next_pci_func
9962 dec al ;; determine pirq reg
9963 mov dl, #0x03
9964 mul al, dl
9965 add al, #0x02
9966 xor ah, ah
9967 mov bx, ax
9968 mov al, [si+bx]
9969 mov dl, al
9970 mov bx, [bp]
9971 call pcibios_init_sel_reg
9972 mov dx, #0x0cfc
9973 and al, #0x03
9974 add dl, al
9975 in al, dx
9976 cmp al, #0x80
9977 jb pirq_found
9978 mov bx, [bp-2] ;; pci irq list pointer
9979 mov al, [bx]
9980 out dx, al
9981 inc bx
9982 mov [bp-2], bx
9983 call pcibios_init_set_elcr
9984 pirq_found:
9985 mov bh, [si]
9986 mov bl, [si+1]
9987 add bl, [bp-3] ;; pci function number
9988 mov dl, #0x3c
9989 call pcibios_init_sel_reg
9990 mov dx, #0x0cfc
9991 out dx, al
9992 next_pci_func:
9993 inc byte ptr[bp-3]
9994 inc bl
9995 test bl, #0x07
9996 jnz pci_init_irq_loop2
9997 next_pir_entry:
9998 add si, #0x10
9999 mov byte ptr[bp-3], #0x00
10000 loop pci_init_irq_loop1
10001 mov sp, bp
10002 pop bx
10003 pci_init_end:
10004 pop bp
10005 pop ds
10007 #endif // !BX_ROMBIOS32
10008 #endif // BX_PCIBIOS
10010 #if BX_ROMBIOS32
10011 rombios32_init:
10012 ;; save a20 and enable it
10013 in al, 0x92
10014 push ax
10015 or al, #0x02
10016 out 0x92, al
10018 ;; save SS:SP to the BDA
10019 xor ax, ax
10020 mov ds, ax
10021 mov 0x0469, ss
10022 mov 0x0467, sp
10024 SEG CS
10025 lidt [pmode_IDT_info]
10026 SEG CS
10027 lgdt [rombios32_gdt_48]
10028 ;; set PE bit in CR0
10029 mov eax, cr0
10030 or al, #0x01
10031 mov cr0, eax
10032 ;; start protected mode code: ljmpl 0x10:rombios32_init1
10033 db 0x66, 0xea
10034 dw rombios32_05
10035 dw 0x000f ;; high 16 bit address
10036 dw 0x0010
10038 use32 386
10039 rombios32_05:
10040 ;; init data segments
10041 mov eax, #0x18
10042 mov ds, ax
10043 mov es, ax
10044 mov ss, ax
10045 xor eax, eax
10046 mov fs, ax
10047 mov gs, ax
10050 ;; init the stack pointer to point below EBDA
10051 mov ax, [0x040e]
10052 shl eax, #4
10053 mov esp, #-0x10
10054 add esp, eax
10056 ;; pass pointer to s3_resume_flag and s3_resume_vector to rombios32
10057 push #0x04b0
10058 push #0x04b2
10060 ;; call rombios32 code
10061 mov eax, #0x000e0000
10062 call eax
10064 ;; return to 16 bit protected mode first
10065 db 0xea
10066 dd rombios32_10
10067 dw 0x20
10069 use16 386
10070 rombios32_10:
10071 ;; restore data segment limits to 0xffff
10072 mov ax, #0x28
10073 mov ds, ax
10074 mov es, ax
10075 mov ss, ax
10076 mov fs, ax
10077 mov gs, ax
10079 ;; reset PE bit in CR0
10080 mov eax, cr0
10081 and al, #0xFE
10082 mov cr0, eax
10084 ;; far jump to flush CPU queue after transition to real mode
10085 JMP_AP(0xf000, rombios32_real_mode)
10087 rombios32_real_mode:
10088 ;; restore IDT to normal real-mode defaults
10089 SEG CS
10090 lidt [rmode_IDT_info]
10092 xor ax, ax
10093 mov ds, ax
10094 mov es, ax
10095 mov fs, ax
10096 mov gs, ax
10098 ;; restore SS:SP from the BDA
10099 mov ss, 0x0469
10100 xor esp, esp
10101 mov sp, 0x0467
10102 ;; restore a20
10103 pop ax
10104 out 0x92, al
10107 rombios32_gdt_48:
10108 dw 0x30
10109 dw rombios32_gdt
10110 dw 0x000f
10112 rombios32_gdt:
10113 dw 0, 0, 0, 0
10114 dw 0, 0, 0, 0
10115 dw 0xffff, 0, 0x9b00, 0x00cf ; 32 bit flat code segment (0x10)
10116 dw 0xffff, 0, 0x9300, 0x00cf ; 32 bit flat data segment (0x18)
10117 dw 0xffff, 0, 0x9b0f, 0x0000 ; 16 bit code segment base=0xf0000 limit=0xffff
10118 dw 0xffff, 0, 0x9300, 0x0000 ; 16 bit data segment base=0x0 limit=0xffff
10119 #endif // BX_ROMBIOS32
10122 ; parallel port detection: base address in DX, index in BX, timeout in CL
10123 detect_parport:
10124 push dx
10125 add dx, #2
10126 in al, dx
10127 and al, #0xdf ; clear input mode
10128 out dx, al
10129 pop dx
10130 mov al, #0xaa
10131 out dx, al
10132 in al, dx
10133 cmp al, #0xaa
10134 jne no_parport
10135 push bx
10136 shl bx, #1
10137 mov [bx+0x408], dx ; Parallel I/O address
10138 pop bx
10139 mov [bx+0x478], cl ; Parallel printer timeout
10140 inc bx
10141 no_parport:
10144 ; serial port detection: base address in DX, index in BX, timeout in CL
10145 detect_serial:
10146 push dx
10147 inc dx
10148 mov al, #0x02
10149 out dx, al
10150 in al, dx
10151 cmp al, #0x02
10152 jne no_serial
10153 inc dx
10154 in al, dx
10155 cmp al, #0x02
10156 jne no_serial
10157 dec dx
10158 xor al, al
10159 out dx, al
10160 pop dx
10161 push bx
10162 shl bx, #1
10163 mov [bx+0x400], dx ; Serial I/O address
10164 pop bx
10165 mov [bx+0x47c], cl ; Serial timeout
10166 inc bx
10168 no_serial:
10169 pop dx
10172 rom_checksum:
10173 push ax
10174 push bx
10175 push cx
10176 xor ax, ax
10177 xor bx, bx
10178 xor cx, cx
10179 mov ch, [2]
10180 shl cx, #1
10181 checksum_loop:
10182 add al, [bx]
10183 inc bx
10184 loop checksum_loop
10185 and al, #0xff
10186 pop cx
10187 pop bx
10188 pop ax
10192 ;; We need a copy of this string, but we are not actually a PnP BIOS,
10193 ;; so make sure it is *not* aligned, so OSes will not see it if they scan.
10194 .align 16
10195 db 0
10196 pnp_string:
10197 .ascii "$PnP"
10200 rom_scan:
10201 ;; Scan for existence of valid expansion ROMS.
10202 ;; Video ROM: from 0xC0000..0xC7FFF in 2k increments
10203 ;; General ROM: from 0xC8000..0xDFFFF in 2k increments
10204 ;; System ROM: only 0xE0000
10206 ;; Header:
10207 ;; Offset Value
10208 ;; 0 0x55
10209 ;; 1 0xAA
10210 ;; 2 ROM length in 512-byte blocks
10211 ;; 3 ROM initialization entry point (FAR CALL)
10213 rom_scan_loop:
10214 push ax ;; Save AX
10215 mov ds, cx
10216 mov ax, #0x0004 ;; start with increment of 4 (512-byte) blocks = 2k
10217 cmp [0], #0xAA55 ;; look for signature
10218 jne rom_scan_increment
10219 call rom_checksum
10220 jnz rom_scan_increment
10221 mov al, [2] ;; change increment to ROM length in 512-byte blocks
10223 ;; We want our increment in 512-byte quantities, rounded to
10224 ;; the nearest 2k quantity, since we only scan at 2k intervals.
10225 test al, #0x03
10226 jz block_count_rounded
10227 and al, #0xfc ;; needs rounding up
10228 add al, #0x04
10229 block_count_rounded:
10231 push ax ;; Save AX
10232 push di ;; Save DI
10233 ;; Push addr of ROM entry point
10234 push cx ;; Push seg
10235 push #0x0003 ;; Push offset
10237 ;; Get the BDF into ax before invoking the option ROM
10238 mov bl, [2]
10239 mov al, bl
10240 shr al, #7
10241 cmp al, #1
10242 jne fetch_bdf
10243 mov ax, ds ;; Increment the DS since rom size larger than an segment
10244 add ax, #0x1000
10245 mov ds, ax
10246 fetch_bdf:
10247 shl bx, #9
10248 xor ax, ax
10249 mov al, [bx]
10251 ;; Point ES:DI at "$PnP", which tells the ROM that we are a PnP BIOS.
10252 ;; That should stop it grabbing INT 19h; we will use its BEV instead.
10253 mov bx, #0xf000
10254 mov es, bx
10255 lea di, pnp_string
10257 mov bp, sp ;; Call ROM init routine using seg:off on stack
10258 db 0xff ;; call_far ss:[bp+0]
10259 db 0x5e
10260 db 0
10261 cli ;; In case expansion ROM BIOS turns IF on
10262 add sp, #2 ;; Pop offset value
10263 pop cx ;; Pop seg value (restore CX)
10265 ;; Look at the ROM's PnP Expansion header. Properly, we're supposed
10266 ;; to init all the ROMs and then go back and build an IPL table of
10267 ;; all the bootable devices, but we can get away with one pass.
10268 mov ds, cx ;; ROM base
10269 mov bx, 0x001a ;; 0x1A is the offset into ROM header that contains...
10270 mov ax, [bx] ;; the offset of PnP expansion header, where...
10271 cmp ax, #0x5024 ;; we look for signature "$PnP"
10272 jne no_bev
10273 mov ax, 2[bx]
10274 cmp ax, #0x506e
10275 jne no_bev
10277 mov ax, 0x16[bx] ;; 0x16 is the offset of Boot Connection Vector
10278 cmp ax, #0x0000
10279 je no_bcv
10281 ;; Option ROM has BCV. Run it now.
10282 push cx ;; Push seg
10283 push ax ;; Push offset
10285 ;; Point ES:DI at "$PnP", which tells the ROM that we are a PnP BIOS.
10286 mov bx, #0xf000
10287 mov es, bx
10288 lea di, pnp_string
10289 /* jump to BCV function entry pointer */
10290 mov bp, sp ;; Call ROM BCV routine using seg:off on stack
10291 db 0xff ;; call_far ss:[bp+0]
10292 db 0x5e
10293 db 0
10294 cli ;; In case expansion ROM BIOS turns IF on
10295 add sp, #2 ;; Pop offset value
10296 pop cx ;; Pop seg value (restore CX)
10297 jmp no_bev
10299 no_bcv:
10300 mov ax, 0x1a[bx] ;; 0x1A is also the offset into the expansion header of...
10301 cmp ax, #0x0000 ;; the Bootstrap Entry Vector, or zero if there is none.
10302 je no_bev
10304 ;; Found a device that thinks it can boot the system. Record its BEV and product name string.
10305 mov di, 0x10[bx] ;; Pointer to the product name string or zero if none
10306 mov bx, #IPL_SEG ;; Go to the segment where the IPL table lives
10307 mov ds, bx
10308 mov bx, IPL_COUNT_OFFSET ;; Read the number of entries so far
10309 cmp bx, #IPL_TABLE_ENTRIES
10310 je no_bev ;; Get out if the table is full
10311 shl bx, #0x4 ;; Turn count into offset (entries are 16 bytes)
10312 mov 0[bx], #IPL_TYPE_BEV ;; This entry is a BEV device
10313 mov 6[bx], cx ;; Build a far pointer from the segment...
10314 mov 4[bx], ax ;; and the offset
10315 cmp di, #0x0000
10316 je no_prod_str
10317 mov 0xA[bx], cx ;; Build a far pointer from the segment...
10318 mov 8[bx], di ;; and the offset
10319 no_prod_str:
10320 shr bx, #0x4 ;; Turn the offset back into a count
10321 inc bx ;; We have one more entry now
10322 mov IPL_COUNT_OFFSET, bx ;; Remember that.
10324 no_bev:
10325 pop di ;; Restore DI
10326 pop ax ;; Restore AX
10327 rom_scan_increment:
10328 shl ax, #5 ;; convert 512-bytes blocks to 16-byte increments
10329 ;; because the segment selector is shifted left 4 bits.
10330 add cx, ax
10331 pop ax ;; Restore AX
10332 cmp cx, ax
10333 jbe rom_scan_loop
10335 xor ax, ax ;; Restore DS back to 0000:
10336 mov ds, ax
10339 post_enable_cache:
10340 ;; enable cache
10341 mov eax, cr0
10342 and eax, #0x9fffffff
10343 mov cr0, eax
10344 jmp post_enable_cache_done
10346 post_init_pic:
10347 mov al, #0x11 ; send initialisation commands
10348 out 0x20, al
10349 out 0xa0, al
10350 mov al, #0x08
10351 out 0x21, al
10352 mov al, #0x70
10353 out 0xa1, al
10354 mov al, #0x04
10355 out 0x21, al
10356 mov al, #0x02
10357 out 0xa1, al
10358 mov al, #0x01
10359 out 0x21, al
10360 out 0xa1, al
10361 mov al, #0xb8
10362 out 0x21, AL ;master pic: unmask IRQ 0, 1, 2, 6
10363 #if BX_USE_PS2_MOUSE
10364 mov al, #0x8f
10365 #else
10366 mov al, #0x9f
10367 #endif
10368 out 0xa1, AL ;slave pic: unmask IRQ 12, 13, 14
10371 ;; the following area can be used to write dynamically generated tables
10372 .align 16
10373 bios_table_area_start:
10374 dd 0xaafb4442
10375 dd bios_table_area_end - bios_table_area_start - 8;
10378 ;--------
10379 ;- POST -
10380 ;--------
10381 .org 0xe05b ; POST Entry Point
10382 post:
10383 jmp post_enable_cache ; hack: we have limited space before next .org,
10384 ; so take this bit out-of-line
10385 post_enable_cache_done:
10386 xor ax, ax
10388 ;; first reset the DMA controllers
10389 out 0x0d,al
10390 out 0xda,al
10392 ;; then initialize the DMA controllers
10393 mov al, #0xC0
10394 out 0xD6, al ; cascade mode of channel 4 enabled
10395 mov al, #0x00
10396 out 0xD4, al ; unmask channel 4
10398 ;; Examine CMOS shutdown status.
10399 mov AL, #0x0f
10400 out 0x70, AL
10401 in AL, 0x71
10403 ;; backup status
10404 mov bl, al
10406 ;; Reset CMOS shutdown status.
10407 mov AL, #0x0f
10408 out 0x70, AL ; select CMOS register Fh
10409 mov AL, #0x00
10410 out 0x71, AL ; set shutdown action to normal
10412 ;; Examine CMOS shutdown status.
10413 mov al, bl
10415 ;; 0x00, 0x09, 0x0D+ = normal startup
10416 cmp AL, #0x00
10417 jz normal_post
10418 cmp AL, #0x0d
10419 jae normal_post
10420 cmp AL, #0x09
10421 je normal_post
10423 ;; 0x05 = eoi + jmp via [0x40:0x67] jump
10424 cmp al, #0x05
10425 je eoi_jmp_post
10427 ;; 0x0A = jmp via [0x40:0x67] jump
10428 cmp al, #0x0a
10429 je jmp_post_0x467
10431 ;; 0x0B = iret via [0x40:0x67]
10432 cmp al, #0x0b
10433 je iret_post_0x467
10435 ;; 0x0C = retf via [0x40:0x67]
10436 cmp al, #0x0c
10437 je retf_post_0x467
10439 ;; Examine CMOS shutdown status.
10440 ;; 0x01,0x02,0x03,0x04,0x06,0x07,0x08 = Unimplemented shutdown status.
10441 push bx
10442 call _shutdown_status_panic
10444 #if 0
10445 HALT(__LINE__)
10447 ;#if 0
10448 ; 0xb0, 0x20, /* mov al, #0x20 */
10449 ; 0xe6, 0x20, /* out 0x20, al ;send EOI to PIC */
10450 ;#endif
10452 pop es
10453 pop ds
10454 popa
10455 iret
10456 #endif
10458 normal_post:
10459 ; case 0: normal startup
10462 mov ax, #0xfffe
10463 mov sp, ax
10464 xor ax, ax
10465 mov ds, ax
10466 mov ss, ax
10468 ;; Save shutdown status
10469 mov 0x04b0, bl
10471 cmp bl, #0xfe
10472 jz s3_post
10474 ;; zero out BIOS data area (40:00..40:ff)
10475 mov es, ax
10476 mov cx, #0x0080 ;; 128 words
10477 mov di, #0x0400
10480 stosw
10482 call _log_bios_start
10484 ;; set all interrupts to default handler
10485 xor bx, bx ;; offset index
10486 mov cx, #0x0100 ;; counter (256 interrupts)
10487 mov ax, #dummy_iret_handler
10488 mov dx, #0xF000
10490 post_default_ints:
10491 mov [bx], ax
10492 add bx, #2
10493 mov [bx], dx
10494 add bx, #2
10495 loop post_default_ints
10497 ;; set vector 0x79 to zero
10498 ;; this is used by 'gardian angel' protection system
10499 SET_INT_VECTOR(0x79, #0, #0)
10501 ;; base memory in K 40:13 (word)
10502 mov ax, #BASE_MEM_IN_K
10503 mov 0x0413, ax
10506 ;; Manufacturing Test 40:12
10507 ;; zerod out above
10509 ;; Warm Boot Flag 0040:0072
10510 ;; value of 1234h = skip memory checks
10511 ;; zerod out above
10514 ;; Printer Services vector
10515 SET_INT_VECTOR(0x17, #0xF000, #int17_handler)
10517 ;; Bootstrap failure vector
10518 SET_INT_VECTOR(0x18, #0xF000, #int18_handler)
10520 ;; Bootstrap Loader vector
10521 SET_INT_VECTOR(0x19, #0xF000, #int19_handler)
10523 ;; User Timer Tick vector
10524 SET_INT_VECTOR(0x1c, #0xF000, #int1c_handler)
10526 ;; Memory Size Check vector
10527 SET_INT_VECTOR(0x12, #0xF000, #int12_handler)
10529 ;; Equipment Configuration Check vector
10530 SET_INT_VECTOR(0x11, #0xF000, #int11_handler)
10532 ;; System Services
10533 SET_INT_VECTOR(0x15, #0xF000, #int15_handler)
10535 ;; EBDA setup
10536 call ebda_post
10538 ;; PIT setup
10539 SET_INT_VECTOR(0x08, #0xF000, #int08_handler)
10540 ;; int 1C already points at dummy_iret_handler (above)
10541 mov al, #0x34 ; timer0: binary count, 16bit count, mode 2
10542 out 0x43, al
10543 mov al, #0x00 ; maximum count of 0000H = 18.2Hz
10544 out 0x40, al
10545 out 0x40, al
10547 ;; Keyboard
10548 SET_INT_VECTOR(0x09, #0xF000, #int09_handler)
10549 SET_INT_VECTOR(0x16, #0xF000, #int16_handler)
10551 xor ax, ax
10552 mov ds, ax
10553 mov 0x0417, al /* keyboard shift flags, set 1 */
10554 mov 0x0418, al /* keyboard shift flags, set 2 */
10555 mov 0x0419, al /* keyboard alt-numpad work area */
10556 mov 0x0471, al /* keyboard ctrl-break flag */
10557 mov 0x0497, al /* keyboard status flags 4 */
10558 mov al, #0x10
10559 mov 0x0496, al /* keyboard status flags 3 */
10562 /* keyboard head of buffer pointer */
10563 mov bx, #0x001E
10564 mov 0x041A, bx
10566 /* keyboard end of buffer pointer */
10567 mov 0x041C, bx
10569 /* keyboard pointer to start of buffer */
10570 mov bx, #0x001E
10571 mov 0x0480, bx
10573 /* keyboard pointer to end of buffer */
10574 mov bx, #0x003E
10575 mov 0x0482, bx
10577 /* init the keyboard */
10578 call _keyboard_init
10580 ;; mov CMOS Equipment Byte to BDA Equipment Word
10581 mov ax, 0x0410
10582 mov al, #0x14
10583 out 0x70, al
10584 in al, 0x71
10585 mov 0x0410, ax
10588 ;; Parallel setup
10589 SET_INT_VECTOR(0x0F, #0xF000, #dummy_iret_handler)
10590 xor ax, ax
10591 mov ds, ax
10592 xor bx, bx
10593 mov cl, #0x14 ; timeout value
10594 mov dx, #0x378 ; Parallel I/O address, port 1
10595 call detect_parport
10596 mov dx, #0x278 ; Parallel I/O address, port 2
10597 call detect_parport
10598 shl bx, #0x0e
10599 mov ax, 0x410 ; Equipment word bits 14..15 determing # parallel ports
10600 and ax, #0x3fff
10601 or ax, bx ; set number of parallel ports
10602 mov 0x410, ax
10604 ;; Serial setup
10605 SET_INT_VECTOR(0x0C, #0xF000, #dummy_iret_handler)
10606 SET_INT_VECTOR(0x14, #0xF000, #int14_handler)
10607 xor bx, bx
10608 mov cl, #0x0a ; timeout value
10609 mov dx, #0x03f8 ; Serial I/O address, port 1
10610 call detect_serial
10611 mov dx, #0x02f8 ; Serial I/O address, port 2
10612 call detect_serial
10613 mov dx, #0x03e8 ; Serial I/O address, port 3
10614 call detect_serial
10615 mov dx, #0x02e8 ; Serial I/O address, port 4
10616 call detect_serial
10617 shl bx, #0x09
10618 mov ax, 0x410 ; Equipment word bits 9..11 determing # serial ports
10619 and ax, #0xf1ff
10620 or ax, bx ; set number of serial port
10621 mov 0x410, ax
10623 ;; CMOS RTC
10624 SET_INT_VECTOR(0x1A, #0xF000, #int1a_handler)
10625 SET_INT_VECTOR(0x4A, #0xF000, #dummy_iret_handler)
10626 SET_INT_VECTOR(0x70, #0xF000, #int70_handler)
10627 ;; BIOS DATA AREA 0x4CE ???
10628 call timer_tick_post
10630 ;; PS/2 mouse setup
10631 SET_INT_VECTOR(0x74, #0xF000, #int74_handler)
10633 ;; IRQ13 (FPU exception) setup
10634 SET_INT_VECTOR(0x75, #0xF000, #int75_handler)
10636 ;; Video setup
10637 SET_INT_VECTOR(0x10, #0xF000, #int10_handler)
10639 ;; PIC
10640 call post_init_pic
10642 mov cx, #0xc000 ;; init vga bios
10643 mov ax, #0xc780
10644 call rom_scan
10646 call _print_bios_banner
10648 #if BX_ROMBIOS32
10649 call rombios32_init
10650 #else
10651 #if BX_PCIBIOS
10652 call pcibios_init_iomem_bases
10653 call pcibios_init_irqs
10654 #endif //BX_PCIBIOS
10655 #endif
10658 ;; Floppy setup
10660 call floppy_drive_post
10663 ;; Hard Drive setup
10665 call hard_drive_post
10667 #if BX_USE_ATADRV
10670 ;; ATA/ATAPI driver setup
10672 call _ata_init
10673 call _ata_detect
10676 #endif // BX_USE_ATADRV
10678 #if BX_ELTORITO_BOOT
10680 ;; eltorito floppy/harddisk emulation from cd
10682 call _cdemu_init
10684 #endif // BX_ELTORITO_BOOT
10686 call _init_boot_vectors
10688 mov cx, #0xc800 ;; init option roms
10689 mov ax, #0xe000
10690 call rom_scan
10692 #if BX_ELTORITO_BOOT
10693 call _interactive_bootkey
10694 #endif // BX_ELTORITO_BOOT
10696 sti ;; enable interrupts
10697 int #0x19
10699 .org 0xe2c3 ; NMI Handler Entry Point
10700 nmi:
10701 ;; FIXME the NMI handler should not panic
10702 ;; but iret when called from int75 (fpu exception)
10703 call _nmi_handler_msg
10704 iret
10706 int75_handler:
10707 out 0xf0, al // clear irq13
10708 call eoi_both_pics // clear interrupt
10709 int 2 // legacy nmi call
10710 iret
10712 ;-------------------------------------------
10713 ;- INT 13h Fixed Disk Services Entry Point -
10714 ;-------------------------------------------
10715 .org 0xe3fe ; INT 13h Fixed Disk Services Entry Point
10716 int13_handler:
10717 //JMPL(int13_relocated)
10718 jmp int13_relocated
10720 .org 0xe401 ; Fixed Disk Parameter Table
10722 ;----------
10723 ;- INT19h -
10724 ;----------
10725 .org 0xe6f2 ; INT 19h Boot Load Service Entry Point
10726 int19_handler:
10728 jmp int19_relocated
10729 ;-------------------------------------------
10730 ;- System BIOS Configuration Data Table
10731 ;-------------------------------------------
10732 .org BIOS_CONFIG_TABLE
10733 db 0x08 ; Table size (bytes) -Lo
10734 db 0x00 ; Table size (bytes) -Hi
10735 db SYS_MODEL_ID
10736 db SYS_SUBMODEL_ID
10737 db BIOS_REVISION
10738 ; Feature byte 1
10739 ; b7: 1=DMA channel 3 used by hard disk
10740 ; b6: 1=2 interrupt controllers present
10741 ; b5: 1=RTC present
10742 ; b4: 1=BIOS calls int 15h/4Fh every key
10743 ; b3: 1=wait for extern event supported (Int 15h/41h)
10744 ; b2: 1=extended BIOS data area used
10745 ; b1: 0=AT or ESDI bus, 1=MicroChannel
10746 ; b0: 1=Dual bus (MicroChannel + ISA)
10747 db (0 << 7) | \
10748 (1 << 6) | \
10749 (1 << 5) | \
10750 (BX_CALL_INT15_4F << 4) | \
10751 (0 << 3) | \
10752 (BX_USE_EBDA << 2) | \
10753 (0 << 1) | \
10754 (0 << 0)
10755 ; Feature byte 2
10756 ; b7: 1=32-bit DMA supported
10757 ; b6: 1=int16h, function 9 supported
10758 ; b5: 1=int15h/C6h (get POS data) supported
10759 ; b4: 1=int15h/C7h (get mem map info) supported
10760 ; b3: 1=int15h/C8h (en/dis CPU) supported
10761 ; b2: 1=non-8042 kb controller
10762 ; b1: 1=data streaming supported
10763 ; b0: reserved
10764 db (0 << 7) | \
10765 (1 << 6) | \
10766 (0 << 5) | \
10767 (0 << 4) | \
10768 (0 << 3) | \
10769 (0 << 2) | \
10770 (0 << 1) | \
10771 (0 << 0)
10772 ; Feature byte 3
10773 ; b7: not used
10774 ; b6: reserved
10775 ; b5: reserved
10776 ; b4: POST supports ROM-to-RAM enable/disable
10777 ; b3: SCSI on system board
10778 ; b2: info panel installed
10779 ; b1: Initial Machine Load (IML) system - BIOS on disk
10780 ; b0: SCSI supported in IML
10781 db 0x00
10782 ; Feature byte 4
10783 ; b7: IBM private
10784 ; b6: EEPROM present
10785 ; b5-3: ABIOS presence (011 = not supported)
10786 ; b2: private
10787 ; b1: memory split above 16Mb supported
10788 ; b0: POSTEXT directly supported by POST
10789 db 0x00
10790 ; Feature byte 5 (IBM)
10791 ; b1: enhanced mouse
10792 ; b0: flash EPROM
10793 db 0x00
10797 .org 0xe729 ; Baud Rate Generator Table
10799 ;----------
10800 ;- INT14h -
10801 ;----------
10802 .org 0xe739 ; INT 14h Serial Communications Service Entry Point
10803 int14_handler:
10804 push ds
10805 pusha
10806 xor ax, ax
10807 mov ds, ax
10808 call _int14_function
10809 popa
10810 pop ds
10811 iret
10814 ;----------------------------------------
10815 ;- INT 16h Keyboard Service Entry Point -
10816 ;----------------------------------------
10817 .org 0xe82e
10818 int16_handler:
10821 push ds
10822 pushf
10823 pusha
10825 cmp ah, #0x00
10826 je int16_F00
10827 cmp ah, #0x10
10828 je int16_F00
10830 mov bx, #0xf000
10831 mov ds, bx
10832 call _int16_function
10833 popa
10834 popf
10835 pop ds
10836 jz int16_zero_set
10838 int16_zero_clear:
10839 push bp
10840 mov bp, sp
10841 //SEG SS
10842 and BYTE [bp + 0x06], #0xbf
10843 pop bp
10844 iret
10846 int16_zero_set:
10847 push bp
10848 mov bp, sp
10849 //SEG SS
10850 or BYTE [bp + 0x06], #0x40
10851 pop bp
10852 iret
10854 int16_F00:
10855 mov bx, #0x0040
10856 mov ds, bx
10858 int16_wait_for_key:
10860 mov bx, 0x001a
10861 cmp bx, 0x001c
10862 jne int16_key_found
10865 #if 0
10866 /* no key yet, call int 15h, function AX=9002 */
10867 0x50, /* push AX */
10868 0xb8, 0x02, 0x90, /* mov AX, #0x9002 */
10869 0xcd, 0x15, /* int 15h */
10870 0x58, /* pop AX */
10871 0xeb, 0xea, /* jmp WAIT_FOR_KEY */
10872 #endif
10873 jmp int16_wait_for_key
10875 int16_key_found:
10876 mov bx, #0xf000
10877 mov ds, bx
10878 call _int16_function
10879 popa
10880 popf
10881 pop ds
10882 #if 0
10883 /* notify int16 complete w/ int 15h, function AX=9102 */
10884 0x50, /* push AX */
10885 0xb8, 0x02, 0x91, /* mov AX, #0x9102 */
10886 0xcd, 0x15, /* int 15h */
10887 0x58, /* pop AX */
10888 #endif
10889 iret
10893 ;-------------------------------------------------
10894 ;- INT09h : Keyboard Hardware Service Entry Point -
10895 ;-------------------------------------------------
10896 .org 0xe987
10897 int09_handler:
10899 push ax
10901 mov al, #0xAD ;;disable keyboard
10902 out #0x64, al
10904 mov al, #0x0B
10905 out #0x20, al
10906 in al, #0x20
10907 and al, #0x02
10908 jz int09_finish
10910 in al, #0x60 ;;read key from keyboard controller
10912 push ds
10913 pusha
10914 #ifdef BX_CALL_INT15_4F
10915 mov ah, #0x4f ;; allow for keyboard intercept
10917 int #0x15
10918 jnc int09_done
10919 #endif
10921 ;; check for extended key
10922 cmp al, #0xe0
10923 jne int09_check_pause
10924 xor ax, ax
10925 mov ds, ax
10926 mov al, BYTE [0x496] ;; mf2_state |= 0x02
10927 or al, #0x02
10928 mov BYTE [0x496], al
10929 jmp int09_done
10931 int09_check_pause: ;; check for pause key
10932 cmp al, #0xe1
10933 jne int09_process_key
10934 xor ax, ax
10935 mov ds, ax
10936 mov al, BYTE [0x496] ;; mf2_state |= 0x01
10937 or al, #0x01
10938 mov BYTE [0x496], al
10939 jmp int09_done
10941 int09_process_key:
10942 mov bx, #0xf000
10943 mov ds, bx
10944 call _int09_function
10946 int09_done:
10947 popa
10948 pop ds
10950 call eoi_master_pic
10952 int09_finish:
10953 mov al, #0xAE ;;enable keyboard
10954 out #0x64, al
10955 pop ax
10956 iret
10959 ;----------------------------------------
10960 ;- INT 13h Diskette Service Entry Point -
10961 ;----------------------------------------
10962 .org 0xec59
10963 int13_diskette:
10964 jmp int13_noeltorito
10966 ;---------------------------------------------
10967 ;- INT 0Eh Diskette Hardware ISR Entry Point -
10968 ;---------------------------------------------
10969 .org 0xef57 ; INT 0Eh Diskette Hardware ISR Entry Point
10970 int0e_handler:
10971 push ax
10972 push dx
10973 mov dx, #0x03f4
10974 in al, dx
10975 and al, #0xc0
10976 cmp al, #0xc0
10977 je int0e_normal
10978 mov dx, #0x03f5
10979 mov al, #0x08 ; sense interrupt status
10980 out dx, al
10981 int0e_loop1:
10982 mov dx, #0x03f4
10983 in al, dx
10984 and al, #0xc0
10985 cmp al, #0xc0
10986 jne int0e_loop1
10987 int0e_loop2:
10988 mov dx, #0x03f5
10989 in al, dx
10990 mov dx, #0x03f4
10991 in al, dx
10992 and al, #0xc0
10993 cmp al, #0xc0
10994 je int0e_loop2
10995 int0e_normal:
10996 push ds
10997 xor ax, ax ;; segment 0000
10998 mov ds, ax
10999 call eoi_master_pic
11000 mov al, 0x043e
11001 or al, #0x80 ;; diskette interrupt has occurred
11002 mov 0x043e, al
11003 pop ds
11004 pop dx
11005 pop ax
11006 iret
11009 .org 0xefc7 ; Diskette Controller Parameter Table
11010 diskette_param_table:
11011 ;; Since no provisions are made for multiple drive types, most
11012 ;; values in this table are ignored. I set parameters for 1.44M
11013 ;; floppy here
11014 db 0xAF
11015 db 0x02 ;; head load time 0000001, DMA used
11016 db 0x25
11017 db 0x02
11018 db 18
11019 db 0x1B
11020 db 0xFF
11021 db 0x6C
11022 db 0xF6
11023 db 0x0F
11024 db 0x08
11027 ;----------------------------------------
11028 ;- INT17h : Printer Service Entry Point -
11029 ;----------------------------------------
11030 .org 0xefd2
11031 int17_handler:
11032 push ds
11033 pusha
11034 xor ax, ax
11035 mov ds, ax
11036 call _int17_function
11037 popa
11038 pop ds
11039 iret
11041 diskette_param_table2:
11042 ;; New diskette parameter table adding 3 parameters from IBM
11043 ;; Since no provisions are made for multiple drive types, most
11044 ;; values in this table are ignored. I set parameters for 1.44M
11045 ;; floppy here
11046 db 0xAF
11047 db 0x02 ;; head load time 0000001, DMA used
11048 db 0x25
11049 db 0x02
11050 db 18
11051 db 0x1B
11052 db 0xFF
11053 db 0x6C
11054 db 0xF6
11055 db 0x0F
11056 db 0x08
11057 db 79 ;; maximum track
11058 db 0 ;; data transfer rate
11059 db 4 ;; drive type in cmos
11061 .org 0xf045 ; INT 10 Functions 0-Fh Entry Point
11062 HALT(__LINE__)
11063 iret
11065 ;----------
11066 ;- INT10h -
11067 ;----------
11068 .org 0xf065 ; INT 10h Video Support Service Entry Point
11069 int10_handler:
11070 ;; dont do anything, since the VGA BIOS handles int10h requests
11071 iret
11073 .org 0xf0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
11075 ;----------
11076 ;- INT12h -
11077 ;----------
11078 .org 0xf841 ; INT 12h Memory Size Service Entry Point
11079 ; ??? different for Pentium (machine check)?
11080 int12_handler:
11081 push ds
11082 mov ax, #0x0040
11083 mov ds, ax
11084 mov ax, 0x0013
11085 pop ds
11086 iret
11088 ;----------
11089 ;- INT11h -
11090 ;----------
11091 .org 0xf84d ; INT 11h Equipment List Service Entry Point
11092 int11_handler:
11093 push ds
11094 mov ax, #0x0040
11095 mov ds, ax
11096 mov ax, 0x0010
11097 pop ds
11098 iret
11100 ;----------
11101 ;- INT15h -
11102 ;----------
11103 .org 0xf859 ; INT 15h System Services Entry Point
11104 int15_handler:
11105 pushf
11106 #if BX_APM
11107 cmp ah, #0x53
11108 je apm_call
11109 #endif
11110 push ds
11111 push es
11112 cmp ah, #0x86
11113 je int15_handler32
11114 cmp ah, #0xE8
11115 je int15_handler32
11116 pusha
11117 #if BX_USE_PS2_MOUSE
11118 cmp ah, #0xC2
11119 je int15_handler_mouse
11120 #endif
11121 call _int15_function
11122 int15_handler_mouse_ret:
11123 popa
11124 int15_handler32_ret:
11125 pop es
11126 pop ds
11127 popf
11128 jmp iret_modify_cf
11129 #if BX_APM
11130 apm_call:
11131 jmp _apmreal_entry
11132 #endif
11134 #if BX_USE_PS2_MOUSE
11135 int15_handler_mouse:
11136 call _int15_function_mouse
11137 jmp int15_handler_mouse_ret
11138 #endif
11140 int15_handler32:
11141 pushad
11142 call _int15_function32
11143 popad
11144 jmp int15_handler32_ret
11146 ;; Protected mode IDT descriptor
11148 ;; I just make the limit 0, so the machine will shutdown
11149 ;; if an exception occurs during protected mode memory
11150 ;; transfers.
11152 ;; Set base to f0000 to correspond to beginning of BIOS,
11153 ;; in case I actually define an IDT later
11154 ;; Set limit to 0
11156 pmode_IDT_info:
11157 dw 0x0000 ;; limit 15:00
11158 dw 0x0000 ;; base 15:00
11159 db 0x0f ;; base 23:16
11161 ;; Real mode IDT descriptor
11163 ;; Set to typical real-mode values.
11164 ;; base = 000000
11165 ;; limit = 03ff
11167 rmode_IDT_info:
11168 dw 0x03ff ;; limit 15:00
11169 dw 0x0000 ;; base 15:00
11170 db 0x00 ;; base 23:16
11173 ;----------
11174 ;- INT1Ah -
11175 ;----------
11176 .org 0xfe6e ; INT 1Ah Time-of-day Service Entry Point
11177 int1a_handler:
11178 #if BX_PCIBIOS
11179 cmp ah, #0xb1
11180 jne int1a_normal
11181 call pcibios_real
11182 jc pcibios_error
11183 retf 2
11184 pcibios_error:
11185 mov bl, ah
11186 mov ah, #0xb1
11187 push ds
11188 pusha
11189 mov ax, ss ; set readable descriptor to ds, for calling pcibios
11190 mov ds, ax ; on 16bit protected mode.
11191 jmp int1a_callfunction
11192 int1a_normal:
11193 #endif
11194 push ds
11195 pusha
11196 xor ax, ax
11197 mov ds, ax
11198 int1a_callfunction:
11199 call _int1a_function
11200 popa
11201 pop ds
11202 iret
11205 ;; int70h: IRQ8 - CMOS RTC
11207 int70_handler:
11208 push ds
11209 pushad
11210 xor ax, ax
11211 mov ds, ax
11212 call _int70_function
11213 popad
11214 pop ds
11215 iret
11217 ;---------
11218 ;- INT08 -
11219 ;---------
11220 .org 0xfea5 ; INT 08h System Timer ISR Entry Point
11221 int08_handler:
11223 push eax
11224 push ds
11225 xor ax, ax
11226 mov ds, ax
11228 ;; time to turn off drive(s)?
11229 mov al,0x0440
11230 or al,al
11231 jz int08_floppy_off
11232 dec al
11233 mov 0x0440,al
11234 jnz int08_floppy_off
11235 ;; turn motor(s) off
11236 push dx
11237 mov dx,#0x03f2
11238 in al,dx
11239 and al,#0xcf
11240 out dx,al
11241 pop dx
11242 int08_floppy_off:
11244 mov eax, 0x046c ;; get ticks dword
11245 inc eax
11247 ;; compare eax to one days worth of timer ticks at 18.2 hz
11248 cmp eax, #0x001800B0
11249 jb int08_store_ticks
11250 ;; there has been a midnight rollover at this point
11251 xor eax, eax ;; zero out counter
11252 inc BYTE 0x0470 ;; increment rollover flag
11254 int08_store_ticks:
11255 mov 0x046c, eax ;; store new ticks dword
11256 ;; chain to user timer tick INT #0x1c
11257 //pushf
11258 //;; call_ep [ds:loc]
11259 //CALL_EP( 0x1c << 2 )
11260 int #0x1c
11262 call eoi_master_pic
11263 pop ds
11264 pop eax
11265 iret
11267 .org 0xfef3 ; Initial Interrupt Vector Offsets Loaded by POST
11270 .org 0xff00
11271 .ascii BIOS_COPYRIGHT_STRING
11273 ;------------------------------------------------
11274 ;- IRET Instruction for Dummy Interrupt Handler -
11275 ;------------------------------------------------
11276 .org 0xff53 ; IRET Instruction for Dummy Interrupt Handler
11277 dummy_iret_handler:
11278 iret
11280 .org 0xff54 ; INT 05h Print Screen Service Entry Point
11281 HALT(__LINE__)
11282 iret
11284 .org 0xfff0 ; Power-up Entry Point
11285 jmp 0xf000:post
11287 .org 0xfff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
11288 .ascii BIOS_BUILD_DATE
11290 .org 0xfffe ; System Model ID
11291 db SYS_MODEL_ID
11292 db 0x00 ; filler
11294 .org 0xfa6e ;; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
11295 ASM_END
11297 * This font comes from the fntcol16.zip package (c) by Joseph Gil
11298 * found at ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip
11299 * This font is public domain
11301 static Bit8u vgafont8[128*8]=
11303 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
11304 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e,
11305 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e,
11306 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00,
11307 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00,
11308 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c,
11309 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c,
11310 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00,
11311 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff,
11312 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
11313 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff,
11314 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78,
11315 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18,
11316 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0,
11317 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0,
11318 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99,
11319 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00,
11320 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00,
11321 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18,
11322 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
11323 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00,
11324 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78,
11325 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00,
11326 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff,
11327 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00,
11328 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
11329 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00,
11330 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
11331 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00,
11332 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00,
11333 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00,
11334 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00,
11335 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
11336 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00,
11337 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
11338 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00,
11339 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00,
11340 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00,
11341 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00,
11342 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
11343 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00,
11344 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
11345 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
11346 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00,
11347 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60,
11348 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
11349 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00,
11350 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00,
11351 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00,
11352 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00,
11353 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00,
11354 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00,
11355 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00,
11356 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00,
11357 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00,
11358 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00,
11359 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00,
11360 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00,
11361 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00,
11362 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60,
11363 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00,
11364 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00,
11365 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00,
11366 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00,
11367 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00,
11368 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00,
11369 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00,
11370 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00,
11371 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00,
11372 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00,
11373 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00,
11374 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00,
11375 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00,
11376 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
11377 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00,
11378 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00,
11379 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00,
11380 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00,
11381 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00,
11382 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00,
11383 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
11384 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00,
11385 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00,
11386 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00,
11387 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
11388 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00,
11389 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
11390 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00,
11391 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00,
11392 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00,
11393 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00,
11394 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
11395 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00,
11396 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
11397 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
11398 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
11399 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
11400 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00,
11401 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00,
11402 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00,
11403 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00,
11404 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
11405 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00,
11406 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
11407 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00,
11408 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
11409 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78,
11410 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00,
11411 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
11412 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00,
11413 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
11414 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
11415 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0,
11416 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e,
11417 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00,
11418 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00,
11419 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00,
11420 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
11421 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
11422 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00,
11423 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00,
11424 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
11425 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00,
11426 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00,
11427 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
11428 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00,
11429 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
11430 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00,
11433 ASM_START
11434 .org 0xcc00
11435 bios_table_area_end:
11436 // bcc-generated data will be placed here
11437 ASM_END