- implemented INT 15h/89h (patch by Sebastian Herbszt)
[gplbios.git] / rombios.c
blob1a836e73a807ae96e3141acecb9a9b5786c91d7b
1 /////////////////////////////////////////////////////////////////////////
2 // $Id$
3 /////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (C) 2002 MandrakeSoft S.A.
6 //
7 // MandrakeSoft S.A.
8 // 43, rue d'Aboukir
9 // 75002 Paris - France
10 // http://www.linux-mandrake.com/
11 // http://www.mandrakesoft.com/
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;
692 } ata_t;
694 #if BX_ELTORITO_BOOT
695 // ElTorito Device Emulation data
696 typedef struct {
697 Bit8u active;
698 Bit8u media;
699 Bit8u emulated_drive;
700 Bit8u controller_index;
701 Bit16u device_spec;
702 Bit32u ilba;
703 Bit16u buffer_segment;
704 Bit16u load_segment;
705 Bit16u sector_count;
707 // Virtual device
708 chs_t vdevice;
709 } cdemu_t;
710 #endif // BX_ELTORITO_BOOT
712 // for access to EBDA area
713 // The EBDA structure should conform to
714 // http://www.frontiernet.net/~fys/rombios.htm document
715 // I made the ata and cdemu structs begin at 0x121 in the EBDA seg
716 // EBDA must be at most 768 bytes; it lives at EBDA_SEG, and the boot
717 // device tables are at IPL_SEG
718 typedef struct {
719 unsigned char filler1[0x3D];
721 // FDPT - Can be splitted in data members if needed
722 unsigned char fdpt0[0x10];
723 unsigned char fdpt1[0x10];
725 unsigned char filler2[0xC4];
727 // ATA Driver data
728 ata_t ata;
730 #if BX_ELTORITO_BOOT
731 // El Torito Emulation data
732 cdemu_t cdemu;
733 #endif // BX_ELTORITO_BOOT
734 } ebda_data_t;
736 #define EbdaData ((ebda_data_t *) 0)
738 // for access to the int13ext structure
739 typedef struct {
740 Bit8u size;
741 Bit8u reserved;
742 Bit16u count;
743 Bit16u offset;
744 Bit16u segment;
745 Bit32u lba1;
746 Bit32u lba2;
747 } int13ext_t;
749 #define Int13Ext ((int13ext_t *) 0)
751 // Disk Physical Table definition
752 typedef struct {
753 Bit16u size;
754 Bit16u infos;
755 Bit32u cylinders;
756 Bit32u heads;
757 Bit32u spt;
758 Bit32u sector_count1;
759 Bit32u sector_count2;
760 Bit16u blksize;
761 Bit16u dpte_offset;
762 Bit16u dpte_segment;
763 Bit16u key;
764 Bit8u dpi_length;
765 Bit8u reserved1;
766 Bit16u reserved2;
767 Bit8u host_bus[4];
768 Bit8u iface_type[8];
769 Bit8u iface_path[8];
770 Bit8u device_path[8];
771 Bit8u reserved3;
772 Bit8u checksum;
773 } dpt_t;
775 #define Int13DPT ((dpt_t *) 0)
777 #endif // BX_USE_ATADRV
779 typedef struct {
780 union {
781 struct {
782 Bit16u di, si, bp, sp;
783 Bit16u bx, dx, cx, ax;
784 } r16;
785 struct {
786 Bit16u filler[4];
787 Bit8u bl, bh, dl, dh, cl, ch, al, ah;
788 } r8;
789 } u;
790 } pusha_regs_t;
792 typedef struct {
793 union {
794 struct {
795 Bit32u edi, esi, ebp, esp;
796 Bit32u ebx, edx, ecx, eax;
797 } r32;
798 struct {
799 Bit16u di, filler1, si, filler2, bp, filler3, sp, filler4;
800 Bit16u bx, filler5, dx, filler6, cx, filler7, ax, filler8;
801 } r16;
802 struct {
803 Bit32u filler[4];
804 Bit8u bl, bh;
805 Bit16u filler1;
806 Bit8u dl, dh;
807 Bit16u filler2;
808 Bit8u cl, ch;
809 Bit16u filler3;
810 Bit8u al, ah;
811 Bit16u filler4;
812 } r8;
813 } u;
814 } pushad_regs_t;
816 typedef struct {
817 union {
818 struct {
819 Bit16u flags;
820 } r16;
821 struct {
822 Bit8u flagsl;
823 Bit8u flagsh;
824 } r8;
825 } u;
826 } flags_t;
828 #define SetCF(x) x.u.r8.flagsl |= 0x01
829 #define SetZF(x) x.u.r8.flagsl |= 0x40
830 #define ClearCF(x) x.u.r8.flagsl &= 0xfe
831 #define ClearZF(x) x.u.r8.flagsl &= 0xbf
832 #define GetCF(x) (x.u.r8.flagsl & 0x01)
834 typedef struct {
835 Bit16u ip;
836 Bit16u cs;
837 flags_t flags;
838 } iret_addr_t;
840 typedef struct {
841 Bit16u type;
842 Bit16u flags;
843 Bit32u vector;
844 Bit32u description;
845 Bit32u reserved;
846 } ipl_entry_t;
849 static Bit8u inb();
850 static Bit8u inb_cmos();
851 static void outb();
852 static void outb_cmos();
853 static Bit16u inw();
854 static void outw();
855 static void init_rtc();
856 static bx_bool rtc_updating();
858 static Bit8u read_byte();
859 static Bit16u read_word();
860 static void write_byte();
861 static void write_word();
862 static void bios_printf();
864 static Bit8u inhibit_mouse_int_and_events();
865 static void enable_mouse_int_and_events();
866 static Bit8u send_to_mouse_ctrl();
867 static Bit8u get_mouse_data();
868 static void set_kbd_command_byte();
870 static void int09_function();
871 static void int13_harddisk();
872 static void int13_cdrom();
873 static void int13_cdemu();
874 static void int13_eltorito();
875 static void int13_diskette_function();
876 static void int14_function();
877 static void int15_function();
878 static void int16_function();
879 static void int17_function();
880 static void int19_function();
881 static void int1a_function();
882 static void int70_function();
883 static void int74_function();
884 static Bit16u get_CS();
885 static Bit16u get_SS();
886 static unsigned int enqueue_key();
887 static unsigned int dequeue_key();
888 static void get_hd_geometry();
889 static void set_diskette_ret_status();
890 static void set_diskette_current_cyl();
891 static void determine_floppy_media();
892 static bx_bool floppy_drive_exists();
893 static bx_bool floppy_drive_recal();
894 static bx_bool floppy_media_known();
895 static bx_bool floppy_media_sense();
896 static bx_bool set_enable_a20();
897 static void debugger_on();
898 static void debugger_off();
899 static void keyboard_init();
900 static void keyboard_panic();
901 static void shutdown_status_panic();
902 static void nmi_handler_msg();
903 static void delay_ticks();
904 static void delay_ticks_and_check_for_keystroke();
906 static void interactive_bootkey();
907 static void print_bios_banner();
908 static void print_boot_device();
909 static void print_boot_failure();
910 static void print_cdromboot_failure();
912 # if BX_USE_ATADRV
914 // ATA / ATAPI driver
915 void ata_init();
916 void ata_detect();
917 void ata_reset();
919 Bit16u ata_cmd_non_data();
920 Bit16u ata_cmd_data_in();
921 Bit16u ata_cmd_data_out();
922 Bit16u ata_cmd_packet();
924 Bit16u atapi_get_sense();
925 Bit16u atapi_is_ready();
926 Bit16u atapi_is_cdrom();
928 #endif // BX_USE_ATADRV
930 #if BX_ELTORITO_BOOT
932 void cdemu_init();
933 Bit8u cdemu_isactive();
934 Bit8u cdemu_emulated_drive();
936 Bit16u cdrom_boot();
938 #endif // BX_ELTORITO_BOOT
940 static char bios_cvs_version_string[] = "$Revision$ $Date$";
942 #define BIOS_COPYRIGHT_STRING "(c) 2002 MandrakeSoft S.A. Written by Kevin Lawton & the Bochs team."
944 #if DEBUG_ATA
945 # define BX_DEBUG_ATA(a...) BX_DEBUG(a)
946 #else
947 # define BX_DEBUG_ATA(a...)
948 #endif
949 #if DEBUG_INT13_HD
950 # define BX_DEBUG_INT13_HD(a...) BX_DEBUG(a)
951 #else
952 # define BX_DEBUG_INT13_HD(a...)
953 #endif
954 #if DEBUG_INT13_CD
955 # define BX_DEBUG_INT13_CD(a...) BX_DEBUG(a)
956 #else
957 # define BX_DEBUG_INT13_CD(a...)
958 #endif
959 #if DEBUG_INT13_ET
960 # define BX_DEBUG_INT13_ET(a...) BX_DEBUG(a)
961 #else
962 # define BX_DEBUG_INT13_ET(a...)
963 #endif
964 #if DEBUG_INT13_FL
965 # define BX_DEBUG_INT13_FL(a...) BX_DEBUG(a)
966 #else
967 # define BX_DEBUG_INT13_FL(a...)
968 #endif
969 #if DEBUG_INT15
970 # define BX_DEBUG_INT15(a...) BX_DEBUG(a)
971 #else
972 # define BX_DEBUG_INT15(a...)
973 #endif
974 #if DEBUG_INT16
975 # define BX_DEBUG_INT16(a...) BX_DEBUG(a)
976 #else
977 # define BX_DEBUG_INT16(a...)
978 #endif
979 #if DEBUG_INT1A
980 # define BX_DEBUG_INT1A(a...) BX_DEBUG(a)
981 #else
982 # define BX_DEBUG_INT1A(a...)
983 #endif
984 #if DEBUG_INT74
985 # define BX_DEBUG_INT74(a...) BX_DEBUG(a)
986 #else
987 # define BX_DEBUG_INT74(a...)
988 #endif
990 #define SET_AL(val8) AX = ((AX & 0xff00) | (val8))
991 #define SET_BL(val8) BX = ((BX & 0xff00) | (val8))
992 #define SET_CL(val8) CX = ((CX & 0xff00) | (val8))
993 #define SET_DL(val8) DX = ((DX & 0xff00) | (val8))
994 #define SET_AH(val8) AX = ((AX & 0x00ff) | ((val8) << 8))
995 #define SET_BH(val8) BX = ((BX & 0x00ff) | ((val8) << 8))
996 #define SET_CH(val8) CX = ((CX & 0x00ff) | ((val8) << 8))
997 #define SET_DH(val8) DX = ((DX & 0x00ff) | ((val8) << 8))
999 #define GET_AL() ( AX & 0x00ff )
1000 #define GET_BL() ( BX & 0x00ff )
1001 #define GET_CL() ( CX & 0x00ff )
1002 #define GET_DL() ( DX & 0x00ff )
1003 #define GET_AH() ( AX >> 8 )
1004 #define GET_BH() ( BX >> 8 )
1005 #define GET_CH() ( CX >> 8 )
1006 #define GET_DH() ( DX >> 8 )
1008 #define GET_ELDL() ( ELDX & 0x00ff )
1009 #define GET_ELDH() ( ELDX >> 8 )
1011 #define SET_CF() FLAGS |= 0x0001
1012 #define CLEAR_CF() FLAGS &= 0xfffe
1013 #define GET_CF() (FLAGS & 0x0001)
1015 #define SET_ZF() FLAGS |= 0x0040
1016 #define CLEAR_ZF() FLAGS &= 0xffbf
1017 #define GET_ZF() (FLAGS & 0x0040)
1019 #define UNSUPPORTED_FUNCTION 0x86
1021 #define none 0
1022 #define MAX_SCAN_CODE 0x58
1024 static struct {
1025 Bit16u normal;
1026 Bit16u shift;
1027 Bit16u control;
1028 Bit16u alt;
1029 Bit8u lock_flags;
1030 } scan_to_scanascii[MAX_SCAN_CODE + 1] = {
1031 { none, none, none, none, none },
1032 { 0x011b, 0x011b, 0x011b, 0x0100, none }, /* escape */
1033 { 0x0231, 0x0221, none, 0x7800, none }, /* 1! */
1034 { 0x0332, 0x0340, 0x0300, 0x7900, none }, /* 2@ */
1035 { 0x0433, 0x0423, none, 0x7a00, none }, /* 3# */
1036 { 0x0534, 0x0524, none, 0x7b00, none }, /* 4$ */
1037 { 0x0635, 0x0625, none, 0x7c00, none }, /* 5% */
1038 { 0x0736, 0x075e, 0x071e, 0x7d00, none }, /* 6^ */
1039 { 0x0837, 0x0826, none, 0x7e00, none }, /* 7& */
1040 { 0x0938, 0x092a, none, 0x7f00, none }, /* 8* */
1041 { 0x0a39, 0x0a28, none, 0x8000, none }, /* 9( */
1042 { 0x0b30, 0x0b29, none, 0x8100, none }, /* 0) */
1043 { 0x0c2d, 0x0c5f, 0x0c1f, 0x8200, none }, /* -_ */
1044 { 0x0d3d, 0x0d2b, none, 0x8300, none }, /* =+ */
1045 { 0x0e08, 0x0e08, 0x0e7f, none, none }, /* backspace */
1046 { 0x0f09, 0x0f00, none, none, none }, /* tab */
1047 { 0x1071, 0x1051, 0x1011, 0x1000, 0x40 }, /* Q */
1048 { 0x1177, 0x1157, 0x1117, 0x1100, 0x40 }, /* W */
1049 { 0x1265, 0x1245, 0x1205, 0x1200, 0x40 }, /* E */
1050 { 0x1372, 0x1352, 0x1312, 0x1300, 0x40 }, /* R */
1051 { 0x1474, 0x1454, 0x1414, 0x1400, 0x40 }, /* T */
1052 { 0x1579, 0x1559, 0x1519, 0x1500, 0x40 }, /* Y */
1053 { 0x1675, 0x1655, 0x1615, 0x1600, 0x40 }, /* U */
1054 { 0x1769, 0x1749, 0x1709, 0x1700, 0x40 }, /* I */
1055 { 0x186f, 0x184f, 0x180f, 0x1800, 0x40 }, /* O */
1056 { 0x1970, 0x1950, 0x1910, 0x1900, 0x40 }, /* P */
1057 { 0x1a5b, 0x1a7b, 0x1a1b, none, none }, /* [{ */
1058 { 0x1b5d, 0x1b7d, 0x1b1d, none, none }, /* ]} */
1059 { 0x1c0d, 0x1c0d, 0x1c0a, none, none }, /* Enter */
1060 { none, none, none, none, none }, /* L Ctrl */
1061 { 0x1e61, 0x1e41, 0x1e01, 0x1e00, 0x40 }, /* A */
1062 { 0x1f73, 0x1f53, 0x1f13, 0x1f00, 0x40 }, /* S */
1063 { 0x2064, 0x2044, 0x2004, 0x2000, 0x40 }, /* D */
1064 { 0x2166, 0x2146, 0x2106, 0x2100, 0x40 }, /* F */
1065 { 0x2267, 0x2247, 0x2207, 0x2200, 0x40 }, /* G */
1066 { 0x2368, 0x2348, 0x2308, 0x2300, 0x40 }, /* H */
1067 { 0x246a, 0x244a, 0x240a, 0x2400, 0x40 }, /* J */
1068 { 0x256b, 0x254b, 0x250b, 0x2500, 0x40 }, /* K */
1069 { 0x266c, 0x264c, 0x260c, 0x2600, 0x40 }, /* L */
1070 { 0x273b, 0x273a, none, none, none }, /* ;: */
1071 { 0x2827, 0x2822, none, none, none }, /* '" */
1072 { 0x2960, 0x297e, none, none, none }, /* `~ */
1073 { none, none, none, none, none }, /* L shift */
1074 { 0x2b5c, 0x2b7c, 0x2b1c, none, none }, /* |\ */
1075 { 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00, 0x40 }, /* Z */
1076 { 0x2d78, 0x2d58, 0x2d18, 0x2d00, 0x40 }, /* X */
1077 { 0x2e63, 0x2e43, 0x2e03, 0x2e00, 0x40 }, /* C */
1078 { 0x2f76, 0x2f56, 0x2f16, 0x2f00, 0x40 }, /* V */
1079 { 0x3062, 0x3042, 0x3002, 0x3000, 0x40 }, /* B */
1080 { 0x316e, 0x314e, 0x310e, 0x3100, 0x40 }, /* N */
1081 { 0x326d, 0x324d, 0x320d, 0x3200, 0x40 }, /* M */
1082 { 0x332c, 0x333c, none, none, none }, /* ,< */
1083 { 0x342e, 0x343e, none, none, none }, /* .> */
1084 { 0x352f, 0x353f, none, none, none }, /* /? */
1085 { none, none, none, none, none }, /* R Shift */
1086 { 0x372a, 0x372a, none, none, none }, /* * */
1087 { none, none, none, none, none }, /* L Alt */
1088 { 0x3920, 0x3920, 0x3920, 0x3920, none }, /* space */
1089 { none, none, none, none, none }, /* caps lock */
1090 { 0x3b00, 0x5400, 0x5e00, 0x6800, none }, /* F1 */
1091 { 0x3c00, 0x5500, 0x5f00, 0x6900, none }, /* F2 */
1092 { 0x3d00, 0x5600, 0x6000, 0x6a00, none }, /* F3 */
1093 { 0x3e00, 0x5700, 0x6100, 0x6b00, none }, /* F4 */
1094 { 0x3f00, 0x5800, 0x6200, 0x6c00, none }, /* F5 */
1095 { 0x4000, 0x5900, 0x6300, 0x6d00, none }, /* F6 */
1096 { 0x4100, 0x5a00, 0x6400, 0x6e00, none }, /* F7 */
1097 { 0x4200, 0x5b00, 0x6500, 0x6f00, none }, /* F8 */
1098 { 0x4300, 0x5c00, 0x6600, 0x7000, none }, /* F9 */
1099 { 0x4400, 0x5d00, 0x6700, 0x7100, none }, /* F10 */
1100 { none, none, none, none, none }, /* Num Lock */
1101 { none, none, none, none, none }, /* Scroll Lock */
1102 { 0x4700, 0x4737, 0x7700, none, 0x20 }, /* 7 Home */
1103 { 0x4800, 0x4838, none, none, 0x20 }, /* 8 UP */
1104 { 0x4900, 0x4939, 0x8400, none, 0x20 }, /* 9 PgUp */
1105 { 0x4a2d, 0x4a2d, none, none, none }, /* - */
1106 { 0x4b00, 0x4b34, 0x7300, none, 0x20 }, /* 4 Left */
1107 { 0x4c00, 0x4c35, none, none, 0x20 }, /* 5 */
1108 { 0x4d00, 0x4d36, 0x7400, none, 0x20 }, /* 6 Right */
1109 { 0x4e2b, 0x4e2b, none, none, none }, /* + */
1110 { 0x4f00, 0x4f31, 0x7500, none, 0x20 }, /* 1 End */
1111 { 0x5000, 0x5032, none, none, 0x20 }, /* 2 Down */
1112 { 0x5100, 0x5133, 0x7600, none, 0x20 }, /* 3 PgDn */
1113 { 0x5200, 0x5230, none, none, 0x20 }, /* 0 Ins */
1114 { 0x5300, 0x532e, none, none, 0x20 }, /* Del */
1115 { none, none, none, none, none },
1116 { none, none, none, none, none },
1117 { 0x565c, 0x567c, none, none, none }, /* \| */
1118 { 0x8500, 0x8700, 0x8900, 0x8b00, none }, /* F11 */
1119 { 0x8600, 0x8800, 0x8a00, 0x8c00, none }, /* F12 */
1122 Bit8u
1123 inb(port)
1124 Bit16u port;
1126 ASM_START
1127 push bp
1128 mov bp, sp
1130 push dx
1131 mov dx, 4[bp]
1132 in al, dx
1133 pop dx
1135 pop bp
1136 ASM_END
1139 #if BX_USE_ATADRV
1140 Bit16u
1141 inw(port)
1142 Bit16u port;
1144 ASM_START
1145 push bp
1146 mov bp, sp
1148 push dx
1149 mov dx, 4[bp]
1150 in ax, dx
1151 pop dx
1153 pop bp
1154 ASM_END
1156 #endif
1158 void
1159 outb(port, val)
1160 Bit16u port;
1161 Bit8u val;
1163 ASM_START
1164 push bp
1165 mov bp, sp
1167 push ax
1168 push dx
1169 mov dx, 4[bp]
1170 mov al, 6[bp]
1171 out dx, al
1172 pop dx
1173 pop ax
1175 pop bp
1176 ASM_END
1179 #if BX_USE_ATADRV
1180 void
1181 outw(port, val)
1182 Bit16u port;
1183 Bit16u val;
1185 ASM_START
1186 push bp
1187 mov bp, sp
1189 push ax
1190 push dx
1191 mov dx, 4[bp]
1192 mov ax, 6[bp]
1193 out dx, ax
1194 pop dx
1195 pop ax
1197 pop bp
1198 ASM_END
1200 #endif
1202 void
1203 outb_cmos(cmos_reg, val)
1204 Bit8u cmos_reg;
1205 Bit8u val;
1207 ASM_START
1208 push bp
1209 mov bp, sp
1211 mov al, 4[bp] ;; cmos_reg
1212 out 0x70, al
1213 mov al, 6[bp] ;; val
1214 out 0x71, al
1216 pop bp
1217 ASM_END
1220 Bit8u
1221 inb_cmos(cmos_reg)
1222 Bit8u cmos_reg;
1224 ASM_START
1225 push bp
1226 mov bp, sp
1228 mov al, 4[bp] ;; cmos_reg
1229 out 0x70, al
1230 in al, 0x71
1232 pop bp
1233 ASM_END
1236 void
1237 init_rtc()
1239 outb_cmos(0x0a, 0x26);
1240 outb_cmos(0x0b, 0x02);
1241 inb_cmos(0x0c);
1242 inb_cmos(0x0d);
1245 bx_bool
1246 rtc_updating()
1248 // This function checks to see if the update-in-progress bit
1249 // is set in CMOS Status Register A. If not, it returns 0.
1250 // If it is set, it tries to wait until there is a transition
1251 // to 0, and will return 0 if such a transition occurs. A 1
1252 // is returned only after timing out. The maximum period
1253 // that this bit should be set is constrained to 244useconds.
1254 // The count I use below guarantees coverage or more than
1255 // this time, with any reasonable IPS setting.
1257 Bit16u count;
1259 count = 25000;
1260 while (--count != 0) {
1261 if ( (inb_cmos(0x0a) & 0x80) == 0 )
1262 return(0);
1264 return(1); // update-in-progress never transitioned to 0
1268 Bit8u
1269 read_byte(seg, offset)
1270 Bit16u seg;
1271 Bit16u offset;
1273 ASM_START
1274 push bp
1275 mov bp, sp
1277 push bx
1278 push ds
1279 mov ax, 4[bp] ; segment
1280 mov ds, ax
1281 mov bx, 6[bp] ; offset
1282 mov al, [bx]
1283 ;; al = return value (byte)
1284 pop ds
1285 pop bx
1287 pop bp
1288 ASM_END
1291 Bit16u
1292 read_word(seg, offset)
1293 Bit16u seg;
1294 Bit16u offset;
1296 ASM_START
1297 push bp
1298 mov bp, sp
1300 push bx
1301 push ds
1302 mov ax, 4[bp] ; segment
1303 mov ds, ax
1304 mov bx, 6[bp] ; offset
1305 mov ax, [bx]
1306 ;; ax = return value (word)
1307 pop ds
1308 pop bx
1310 pop bp
1311 ASM_END
1314 void
1315 write_byte(seg, offset, data)
1316 Bit16u seg;
1317 Bit16u offset;
1318 Bit8u data;
1320 ASM_START
1321 push bp
1322 mov bp, sp
1324 push ax
1325 push bx
1326 push ds
1327 mov ax, 4[bp] ; segment
1328 mov ds, ax
1329 mov bx, 6[bp] ; offset
1330 mov al, 8[bp] ; data byte
1331 mov [bx], al ; write data byte
1332 pop ds
1333 pop bx
1334 pop ax
1336 pop bp
1337 ASM_END
1340 void
1341 write_word(seg, offset, data)
1342 Bit16u seg;
1343 Bit16u offset;
1344 Bit16u data;
1346 ASM_START
1347 push bp
1348 mov bp, sp
1350 push ax
1351 push bx
1352 push ds
1353 mov ax, 4[bp] ; segment
1354 mov ds, ax
1355 mov bx, 6[bp] ; offset
1356 mov ax, 8[bp] ; data word
1357 mov [bx], ax ; write data word
1358 pop ds
1359 pop bx
1360 pop ax
1362 pop bp
1363 ASM_END
1366 Bit16u
1367 get_CS()
1369 ASM_START
1370 mov ax, cs
1371 ASM_END
1374 Bit16u
1375 get_SS()
1377 ASM_START
1378 mov ax, ss
1379 ASM_END
1382 #if BX_DEBUG_SERIAL
1383 /* serial debug port*/
1384 #define BX_DEBUG_PORT 0x03f8
1386 /* data */
1387 #define UART_RBR 0x00
1388 #define UART_THR 0x00
1390 /* control */
1391 #define UART_IER 0x01
1392 #define UART_IIR 0x02
1393 #define UART_FCR 0x02
1394 #define UART_LCR 0x03
1395 #define UART_MCR 0x04
1396 #define UART_DLL 0x00
1397 #define UART_DLM 0x01
1399 /* status */
1400 #define UART_LSR 0x05
1401 #define UART_MSR 0x06
1402 #define UART_SCR 0x07
1404 int uart_can_tx_byte(base_port)
1405 Bit16u base_port;
1407 return inb(base_port + UART_LSR) & 0x20;
1410 void uart_wait_to_tx_byte(base_port)
1411 Bit16u base_port;
1413 while (!uart_can_tx_byte(base_port));
1416 void uart_wait_until_sent(base_port)
1417 Bit16u base_port;
1419 while (!(inb(base_port + UART_LSR) & 0x40));
1422 void uart_tx_byte(base_port, data)
1423 Bit16u base_port;
1424 Bit8u data;
1426 uart_wait_to_tx_byte(base_port);
1427 outb(base_port + UART_THR, data);
1428 uart_wait_until_sent(base_port);
1430 #endif
1432 void
1433 wrch(c)
1434 Bit8u c;
1436 ASM_START
1437 push bp
1438 mov bp, sp
1440 push bx
1441 mov ah, #0x0e
1442 mov al, 4[bp]
1443 xor bx,bx
1444 int #0x10
1445 pop bx
1447 pop bp
1448 ASM_END
1451 void
1452 send(action, c)
1453 Bit16u action;
1454 Bit8u c;
1456 #if BX_DEBUG_SERIAL
1457 if (c == '\n') uart_tx_byte(BX_DEBUG_PORT, '\r');
1458 uart_tx_byte(BX_DEBUG_PORT, c);
1459 #endif
1460 #if BX_VIRTUAL_PORTS
1461 if (action & BIOS_PRINTF_DEBUG) outb(DEBUG_PORT, c);
1462 if (action & BIOS_PRINTF_INFO) outb(INFO_PORT, c);
1463 #endif
1464 if (action & BIOS_PRINTF_SCREEN) {
1465 if (c == '\n') wrch('\r');
1466 wrch(c);
1470 void
1471 put_int(action, val, width, neg)
1472 Bit16u action;
1473 short val, width;
1474 bx_bool neg;
1476 short nval = val / 10;
1477 if (nval)
1478 put_int(action, nval, width - 1, neg);
1479 else {
1480 while (--width > 0) send(action, ' ');
1481 if (neg) send(action, '-');
1483 send(action, val - (nval * 10) + '0');
1486 void
1487 put_uint(action, val, width, neg)
1488 Bit16u action;
1489 unsigned short val;
1490 short width;
1491 bx_bool neg;
1493 unsigned short nval = val / 10;
1494 if (nval)
1495 put_uint(action, nval, width - 1, neg);
1496 else {
1497 while (--width > 0) send(action, ' ');
1498 if (neg) send(action, '-');
1500 send(action, val - (nval * 10) + '0');
1503 void
1504 put_luint(action, val, width, neg)
1505 Bit16u action;
1506 unsigned long val;
1507 short width;
1508 bx_bool neg;
1510 unsigned long nval = val / 10;
1511 if (nval)
1512 put_luint(action, nval, width - 1, neg);
1513 else {
1514 while (--width > 0) send(action, ' ');
1515 if (neg) send(action, '-');
1517 send(action, val - (nval * 10) + '0');
1520 void put_str(action, segment, offset)
1521 Bit16u action;
1522 Bit16u segment;
1523 Bit16u offset;
1525 Bit8u c;
1527 while (c = read_byte(segment, offset)) {
1528 send(action, c);
1529 offset++;
1533 void
1534 delay_ticks(ticks)
1535 Bit16u ticks;
1537 long ticks_to_wait, delta;
1538 Bit32u prev_ticks, t;
1541 * The 0:046c wraps around at 'midnight' according to a 18.2Hz clock.
1542 * We also have to be careful about interrupt storms.
1544 ASM_START
1545 pushf
1547 ASM_END
1548 ticks_to_wait = ticks;
1549 prev_ticks = read_dword(0x0, 0x46c);
1552 ASM_START
1554 ASM_END
1555 t = read_dword(0x0, 0x46c);
1556 if (t > prev_ticks)
1558 delta = t - prev_ticks; /* The temp var is required or bcc screws up. */
1559 ticks_to_wait -= delta;
1561 else if (t < prev_ticks)
1563 ticks_to_wait -= t; /* wrapped */
1566 prev_ticks = t;
1567 } while (ticks_to_wait > 0);
1568 ASM_START
1570 popf
1571 ASM_END
1574 Bit8u
1575 check_for_keystroke()
1577 ASM_START
1578 mov ax, #0x100
1579 int #0x16
1580 jz no_key
1581 mov al, #1
1582 jmp done
1583 no_key:
1584 xor al, al
1585 done:
1586 ASM_END
1589 Bit8u
1590 get_keystroke()
1592 ASM_START
1593 mov ax, #0x0
1594 int #0x16
1595 xchg ah, al
1596 ASM_END
1599 void
1600 delay_ticks_and_check_for_keystroke(ticks, count)
1601 Bit16u ticks, count;
1603 Bit16u i;
1604 for (i = 1; i <= count; i++) {
1605 delay_ticks(ticks);
1606 if (check_for_keystroke())
1607 break;
1611 //--------------------------------------------------------------------------
1612 // bios_printf()
1613 // A compact variable argument printf function.
1615 // Supports %[format_width][length]format
1616 // where format can be x,X,u,d,s,S,c
1617 // and the optional length modifier is l (ell)
1618 //--------------------------------------------------------------------------
1619 void
1620 bios_printf(action, s)
1621 Bit16u action;
1622 Bit8u *s;
1624 Bit8u c, format_char;
1625 bx_bool in_format;
1626 short i;
1627 Bit16u *arg_ptr;
1628 Bit16u arg_seg, arg, nibble, hibyte, shift_count, format_width, hexadd;
1630 arg_ptr = &s;
1631 arg_seg = get_SS();
1633 in_format = 0;
1634 format_width = 0;
1636 if ((action & BIOS_PRINTF_DEBHALT) == BIOS_PRINTF_DEBHALT) {
1637 #if BX_VIRTUAL_PORTS
1638 outb(PANIC_PORT2, 0x00);
1639 #endif
1640 bios_printf (BIOS_PRINTF_SCREEN, "FATAL: ");
1643 while (c = read_byte(get_CS(), s)) {
1644 if ( c == '%' ) {
1645 in_format = 1;
1646 format_width = 0;
1648 else if (in_format) {
1649 if ( (c>='0') && (c<='9') ) {
1650 format_width = (format_width * 10) + (c - '0');
1652 else {
1653 arg_ptr++; // increment to next arg
1654 arg = read_word(arg_seg, arg_ptr);
1655 if (c == 'x' || c == 'X') {
1656 if (format_width == 0)
1657 format_width = 4;
1658 if (c == 'x')
1659 hexadd = 'a';
1660 else
1661 hexadd = 'A';
1662 for (i=format_width-1; i>=0; i--) {
1663 nibble = (arg >> (4 * i)) & 0x000f;
1664 send (action, (nibble<=9)? (nibble+'0') : (nibble-10+hexadd));
1667 else if (c == 'u') {
1668 put_uint(action, arg, format_width, 0);
1670 else if (c == 'l') {
1671 s++;
1672 c = read_byte(get_CS(), s); /* is it ld,lx,lu? */
1673 arg_ptr++; /* increment to next arg */
1674 hibyte = read_word(arg_seg, arg_ptr);
1675 if (c == 'd') {
1676 if (hibyte & 0x8000)
1677 put_luint(action, 0L-(((Bit32u) hibyte << 16) | arg), format_width-1, 1);
1678 else
1679 put_luint(action, ((Bit32u) hibyte << 16) | arg, format_width, 0);
1681 else if (c == 'u') {
1682 put_luint(action, ((Bit32u) hibyte << 16) | arg, format_width, 0);
1684 else if (c == 'x' || c == 'X')
1686 if (format_width == 0)
1687 format_width = 8;
1688 if (c == 'x')
1689 hexadd = 'a';
1690 else
1691 hexadd = 'A';
1692 for (i=format_width-1; i>=0; i--) {
1693 nibble = ((((Bit32u) hibyte <<16) | arg) >> (4 * i)) & 0x000f;
1694 send (action, (nibble<=9)? (nibble+'0') : (nibble-10+hexadd));
1698 else if (c == 'd') {
1699 if (arg & 0x8000)
1700 put_int(action, -arg, format_width - 1, 1);
1701 else
1702 put_int(action, arg, format_width, 0);
1704 else if (c == 's') {
1705 put_str(action, get_CS(), arg);
1707 else if (c == 'S') {
1708 hibyte = arg;
1709 arg_ptr++;
1710 arg = read_word(arg_seg, arg_ptr);
1711 put_str(action, hibyte, arg);
1713 else if (c == 'c') {
1714 send(action, arg);
1716 else
1717 BX_PANIC("bios_printf: unknown format\n");
1718 in_format = 0;
1721 else {
1722 send(action, c);
1724 s ++;
1727 if (action & BIOS_PRINTF_HALT) {
1728 // freeze in a busy loop.
1729 ASM_START
1731 halt2_loop:
1733 jmp halt2_loop
1734 ASM_END
1738 //--------------------------------------------------------------------------
1739 // keyboard_init
1740 //--------------------------------------------------------------------------
1741 // this file is based on LinuxBIOS implementation of keyboard.c
1742 // could convert to #asm to gain space
1743 void
1744 keyboard_init()
1746 Bit16u max;
1748 /* ------------------- Flush buffers ------------------------*/
1749 /* Wait until buffer is empty */
1750 max=0xffff;
1751 while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
1753 /* flush incoming keys */
1754 max=0x2000;
1755 while (--max > 0) {
1756 outb(0x80, 0x00);
1757 if (inb(0x64) & 0x01) {
1758 inb(0x60);
1759 max = 0x2000;
1763 // Due to timer issues, and if the IPS setting is > 15000000,
1764 // the incoming keys might not be flushed here. That will
1765 // cause a panic a few lines below. See sourceforge bug report :
1766 // [ 642031 ] FATAL: Keyboard RESET error:993
1768 /* ------------------- controller side ----------------------*/
1769 /* send cmd = 0xAA, self test 8042 */
1770 outb(0x64, 0xaa);
1772 /* Wait until buffer is empty */
1773 max=0xffff;
1774 while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
1775 if (max==0x0) keyboard_panic(00);
1777 /* Wait for data */
1778 max=0xffff;
1779 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x01);
1780 if (max==0x0) keyboard_panic(01);
1782 /* read self-test result, 0x55 should be returned from 0x60 */
1783 if ((inb(0x60) != 0x55)){
1784 keyboard_panic(991);
1787 /* send cmd = 0xAB, keyboard interface test */
1788 outb(0x64,0xab);
1790 /* Wait until buffer is empty */
1791 max=0xffff;
1792 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x10);
1793 if (max==0x0) keyboard_panic(10);
1795 /* Wait for data */
1796 max=0xffff;
1797 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x11);
1798 if (max==0x0) keyboard_panic(11);
1800 /* read keyboard interface test result, */
1801 /* 0x00 should be returned form 0x60 */
1802 if ((inb(0x60) != 0x00)) {
1803 keyboard_panic(992);
1806 /* Enable Keyboard clock */
1807 outb(0x64,0xae);
1808 outb(0x64,0xa8);
1810 /* ------------------- keyboard side ------------------------*/
1811 /* reset kerboard and self test (keyboard side) */
1812 outb(0x60, 0xff);
1814 /* Wait until buffer is empty */
1815 max=0xffff;
1816 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x20);
1817 if (max==0x0) keyboard_panic(20);
1819 /* Wait for data */
1820 max=0xffff;
1821 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x21);
1822 if (max==0x0) keyboard_panic(21);
1824 /* keyboard should return ACK */
1825 if ((inb(0x60) != 0xfa)) {
1826 keyboard_panic(993);
1829 /* Wait for data */
1830 max=0xffff;
1831 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x31);
1832 if (max==0x0) keyboard_panic(31);
1834 if ((inb(0x60) != 0xaa)) {
1835 keyboard_panic(994);
1838 /* Disable keyboard */
1839 outb(0x60, 0xf5);
1841 /* Wait until buffer is empty */
1842 max=0xffff;
1843 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x40);
1844 if (max==0x0) keyboard_panic(40);
1846 /* Wait for data */
1847 max=0xffff;
1848 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x41);
1849 if (max==0x0) keyboard_panic(41);
1851 /* keyboard should return ACK */
1852 if ((inb(0x60) != 0xfa)) {
1853 keyboard_panic(995);
1856 /* Write Keyboard Mode */
1857 outb(0x64, 0x60);
1859 /* Wait until buffer is empty */
1860 max=0xffff;
1861 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x50);
1862 if (max==0x0) keyboard_panic(50);
1864 /* send cmd: scan code convert, disable mouse, enable IRQ 1 */
1865 outb(0x60, 0x61);
1867 /* Wait until buffer is empty */
1868 max=0xffff;
1869 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x60);
1870 if (max==0x0) keyboard_panic(60);
1872 /* Enable keyboard */
1873 outb(0x60, 0xf4);
1875 /* Wait until buffer is empty */
1876 max=0xffff;
1877 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x70);
1878 if (max==0x0) keyboard_panic(70);
1880 /* Wait for data */
1881 max=0xffff;
1882 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x71);
1883 if (max==0x0) keyboard_panic(70);
1885 /* keyboard should return ACK */
1886 if ((inb(0x60) != 0xfa)) {
1887 keyboard_panic(996);
1890 outb(0x80, 0x77);
1893 //--------------------------------------------------------------------------
1894 // keyboard_panic
1895 //--------------------------------------------------------------------------
1896 void
1897 keyboard_panic(status)
1898 Bit16u status;
1900 // If you're getting a 993 keyboard panic here,
1901 // please see the comment in keyboard_init
1903 BX_PANIC("Keyboard error:%u\n",status);
1906 //--------------------------------------------------------------------------
1907 // shutdown_status_panic
1908 // called when the shutdown statsu is not implemented, displays the status
1909 //--------------------------------------------------------------------------
1910 void
1911 shutdown_status_panic(status)
1912 Bit16u status;
1914 BX_PANIC("Unimplemented shutdown status: %02x\n",(Bit8u)status);
1917 void s3_resume_panic()
1919 BX_PANIC("Returned from s3_resume.\n");
1922 //--------------------------------------------------------------------------
1923 // print_bios_banner
1924 // displays a the bios version
1925 //--------------------------------------------------------------------------
1926 void
1927 print_bios_banner()
1929 printf(BX_APPNAME" BIOS - build: %s\n%s\nOptions: ",
1930 BIOS_BUILD_DATE, bios_cvs_version_string);
1931 printf(
1932 #if BX_APM
1933 "apmbios "
1934 #endif
1935 #if BX_PCIBIOS
1936 "pcibios "
1937 #endif
1938 #if BX_ELTORITO_BOOT
1939 "eltorito "
1940 #endif
1941 #if BX_ROMBIOS32
1942 "rombios32 "
1943 #endif
1944 "\n\n");
1947 //--------------------------------------------------------------------------
1948 // BIOS Boot Specification 1.0.1 compatibility
1950 // Very basic support for the BIOS Boot Specification, which allows expansion
1951 // ROMs to register themselves as boot devices, instead of just stealing the
1952 // INT 19h boot vector.
1954 // This is a hack: to do it properly requires a proper PnP BIOS and we aren't
1955 // one; we just lie to the option ROMs to make them behave correctly.
1956 // We also don't support letting option ROMs register as bootable disk
1957 // drives (BCVs), only as bootable devices (BEVs).
1959 // http://www.phoenix.com/en/Customer+Services/White+Papers-Specs/pc+industry+specifications.htm
1960 //--------------------------------------------------------------------------
1962 static char drivetypes[][10]={"", "Floppy","Hard Disk","CD-Rom", "Network"};
1964 static void
1965 init_boot_vectors()
1967 ipl_entry_t e;
1968 Bit16u count = 0;
1969 Bit16u ss = get_SS();
1971 /* Clear out the IPL table. */
1972 memsetb(IPL_SEG, IPL_TABLE_OFFSET, 0, IPL_SIZE);
1974 /* User selected device not set */
1975 write_word(IPL_SEG, IPL_BOOTFIRST_OFFSET, 0xFFFF);
1977 /* Floppy drive */
1978 e.type = IPL_TYPE_FLOPPY; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
1979 memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
1980 count++;
1982 /* First HDD */
1983 e.type = IPL_TYPE_HARDDISK; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
1984 memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
1985 count++;
1987 #if BX_ELTORITO_BOOT
1988 /* CDROM */
1989 e.type = IPL_TYPE_CDROM; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
1990 memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
1991 count++;
1992 #endif
1994 /* Remember how many devices we have */
1995 write_word(IPL_SEG, IPL_COUNT_OFFSET, count);
1996 /* Not tried booting anything yet */
1997 write_word(IPL_SEG, IPL_SEQUENCE_OFFSET, 0xffff);
2000 static Bit8u
2001 get_boot_vector(i, e)
2002 Bit16u i; ipl_entry_t *e;
2004 Bit16u count;
2005 Bit16u ss = get_SS();
2006 /* Get the count of boot devices, and refuse to overrun the array */
2007 count = read_word(IPL_SEG, IPL_COUNT_OFFSET);
2008 if (i >= count) return 0;
2009 /* OK to read this device */
2010 memcpyb(ss, e, IPL_SEG, IPL_TABLE_OFFSET + i * sizeof (*e), sizeof (*e));
2011 return 1;
2014 #if BX_ELTORITO_BOOT
2015 void
2016 interactive_bootkey()
2018 ipl_entry_t e;
2019 Bit16u count;
2020 char description[33];
2021 Bit8u scan_code;
2022 Bit8u i;
2023 Bit16u ss = get_SS();
2024 Bit16u valid_choice = 0;
2026 while (check_for_keystroke())
2027 get_keystroke();
2029 printf("Press F12 for boot menu.\n\n");
2031 delay_ticks_and_check_for_keystroke(11, 5); /* ~3 seconds */
2032 if (check_for_keystroke())
2034 scan_code = get_keystroke();
2035 if (scan_code == 0x86) /* F12 */
2037 while (check_for_keystroke())
2038 get_keystroke();
2040 printf("Select boot device:\n\n");
2042 count = read_word(IPL_SEG, IPL_COUNT_OFFSET);
2043 for (i = 0; i < count; i++)
2045 memcpyb(ss, &e, IPL_SEG, IPL_TABLE_OFFSET + i * sizeof (e), sizeof (e));
2046 printf("%d. ", i+1);
2047 switch(e.type)
2049 case IPL_TYPE_FLOPPY:
2050 case IPL_TYPE_HARDDISK:
2051 case IPL_TYPE_CDROM:
2052 printf("%s\n", drivetypes[e.type]);
2053 break;
2054 case IPL_TYPE_BEV:
2055 printf("%s", drivetypes[4]);
2056 if (e.description != 0)
2058 memcpyb(ss, &description, (Bit16u)(e.description >> 16), (Bit16u)(e.description & 0xffff), 32);
2059 description[32] = 0;
2060 printf(" [%S]", ss, description);
2062 printf("\n");
2063 break;
2067 count++;
2068 while (!valid_choice) {
2069 scan_code = get_keystroke();
2070 if (scan_code == 0x01 || scan_code == 0x58) /* ESC or F12 */
2072 valid_choice = 1;
2074 else if (scan_code <= count)
2076 valid_choice = 1;
2077 scan_code -= 1;
2078 /* Set user selected device */
2079 write_word(IPL_SEG, IPL_BOOTFIRST_OFFSET, scan_code);
2083 printf("\n");
2087 #endif // BX_ELTORITO_BOOT
2089 //--------------------------------------------------------------------------
2090 // print_boot_device
2091 // displays the boot device
2092 //--------------------------------------------------------------------------
2094 void
2095 print_boot_device(e)
2096 ipl_entry_t *e;
2098 Bit16u type;
2099 char description[33];
2100 Bit16u ss = get_SS();
2101 type = e->type;
2102 /* NIC appears as type 0x80 */
2103 if (type == IPL_TYPE_BEV) type = 0x4;
2104 if (type == 0 || type > 0x4) BX_PANIC("Bad drive type\n");
2105 printf("Booting from %s", drivetypes[type]);
2106 /* print product string if BEV */
2107 if (type == 4 && e->description != 0) {
2108 /* first 32 bytes are significant */
2109 memcpyb(ss, &description, (Bit16u)(e->description >> 16), (Bit16u)(e->description & 0xffff), 32);
2110 /* terminate string */
2111 description[32] = 0;
2112 printf(" [%S]", ss, description);
2114 printf("...\n");
2117 //--------------------------------------------------------------------------
2118 // print_boot_failure
2119 // displays the reason why boot failed
2120 //--------------------------------------------------------------------------
2121 void
2122 print_boot_failure(type, reason)
2123 Bit16u type; Bit8u reason;
2125 if (type == 0 || type > 0x3) BX_PANIC("Bad drive type\n");
2127 printf("Boot failed");
2128 if (type < 4) {
2129 /* Report the reason too */
2130 if (reason==0)
2131 printf(": not a bootable disk");
2132 else
2133 printf(": could not read the boot disk");
2135 printf("\n\n");
2138 //--------------------------------------------------------------------------
2139 // print_cdromboot_failure
2140 // displays the reason why boot failed
2141 //--------------------------------------------------------------------------
2142 void
2143 print_cdromboot_failure( code )
2144 Bit16u code;
2146 bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, "CDROM boot failure code : %04x\n",code);
2148 return;
2151 void
2152 nmi_handler_msg()
2154 BX_PANIC("NMI Handler called\n");
2157 void
2158 int18_panic_msg()
2160 BX_PANIC("INT18: BOOT FAILURE\n");
2163 void
2164 log_bios_start()
2166 #if BX_DEBUG_SERIAL
2167 outb(BX_DEBUG_PORT+UART_LCR, 0x03); /* setup for serial logging: 8N1 */
2168 #endif
2169 BX_INFO("%s\n", bios_cvs_version_string);
2172 bx_bool
2173 set_enable_a20(val)
2174 bx_bool val;
2176 Bit8u oldval;
2178 // Use PS2 System Control port A to set A20 enable
2180 // get current setting first
2181 oldval = inb(0x92);
2183 // change A20 status
2184 if (val)
2185 outb(0x92, oldval | 0x02);
2186 else
2187 outb(0x92, oldval & 0xfd);
2189 return((oldval & 0x02) != 0);
2192 void
2193 debugger_on()
2195 outb(0xfedc, 0x01);
2198 void
2199 debugger_off()
2201 outb(0xfedc, 0x00);
2205 s3_resume()
2207 Bit32u s3_wakeup_vector;
2208 Bit8u s3_resume_flag;
2210 s3_resume_flag = read_byte(0x40, 0xb0);
2211 s3_wakeup_vector = read_dword(0x40, 0xb2);
2213 BX_INFO("S3 resume called %x 0x%lx\n", s3_resume_flag, s3_wakeup_vector);
2214 if (s3_resume_flag != 0xFE || !s3_wakeup_vector)
2215 return 0;
2217 write_byte(0x40, 0xb0, 0);
2219 /* setup wakeup vector */
2220 write_word(0x40, 0xb6, (s3_wakeup_vector & 0xF)); /* IP */
2221 write_word(0x40, 0xb8, (s3_wakeup_vector >> 4)); /* CS */
2223 BX_INFO("S3 resume jump to %x:%x\n", (s3_wakeup_vector >> 4),
2224 (s3_wakeup_vector & 0xF));
2225 ASM_START
2226 jmpf [0x04b6]
2227 ASM_END
2228 return 1;
2231 #if BX_USE_ATADRV
2233 // ---------------------------------------------------------------------------
2234 // Start of ATA/ATAPI Driver
2235 // ---------------------------------------------------------------------------
2237 // Global defines -- ATA register and register bits.
2238 // command block & control block regs
2239 #define ATA_CB_DATA 0 // data reg in/out pio_base_addr1+0
2240 #define ATA_CB_ERR 1 // error in pio_base_addr1+1
2241 #define ATA_CB_FR 1 // feature reg out pio_base_addr1+1
2242 #define ATA_CB_SC 2 // sector count in/out pio_base_addr1+2
2243 #define ATA_CB_SN 3 // sector number in/out pio_base_addr1+3
2244 #define ATA_CB_CL 4 // cylinder low in/out pio_base_addr1+4
2245 #define ATA_CB_CH 5 // cylinder high in/out pio_base_addr1+5
2246 #define ATA_CB_DH 6 // device head in/out pio_base_addr1+6
2247 #define ATA_CB_STAT 7 // primary status in pio_base_addr1+7
2248 #define ATA_CB_CMD 7 // command out pio_base_addr1+7
2249 #define ATA_CB_ASTAT 6 // alternate status in pio_base_addr2+6
2250 #define ATA_CB_DC 6 // device control out pio_base_addr2+6
2251 #define ATA_CB_DA 7 // device address in pio_base_addr2+7
2253 #define ATA_CB_ER_ICRC 0x80 // ATA Ultra DMA bad CRC
2254 #define ATA_CB_ER_BBK 0x80 // ATA bad block
2255 #define ATA_CB_ER_UNC 0x40 // ATA uncorrected error
2256 #define ATA_CB_ER_MC 0x20 // ATA media change
2257 #define ATA_CB_ER_IDNF 0x10 // ATA id not found
2258 #define ATA_CB_ER_MCR 0x08 // ATA media change request
2259 #define ATA_CB_ER_ABRT 0x04 // ATA command aborted
2260 #define ATA_CB_ER_NTK0 0x02 // ATA track 0 not found
2261 #define ATA_CB_ER_NDAM 0x01 // ATA address mark not found
2263 #define ATA_CB_ER_P_SNSKEY 0xf0 // ATAPI sense key (mask)
2264 #define ATA_CB_ER_P_MCR 0x08 // ATAPI Media Change Request
2265 #define ATA_CB_ER_P_ABRT 0x04 // ATAPI command abort
2266 #define ATA_CB_ER_P_EOM 0x02 // ATAPI End of Media
2267 #define ATA_CB_ER_P_ILI 0x01 // ATAPI Illegal Length Indication
2269 // ATAPI Interrupt Reason bits in the Sector Count reg (CB_SC)
2270 #define ATA_CB_SC_P_TAG 0xf8 // ATAPI tag (mask)
2271 #define ATA_CB_SC_P_REL 0x04 // ATAPI release
2272 #define ATA_CB_SC_P_IO 0x02 // ATAPI I/O
2273 #define ATA_CB_SC_P_CD 0x01 // ATAPI C/D
2275 // bits 7-4 of the device/head (CB_DH) reg
2276 #define ATA_CB_DH_DEV0 0xa0 // select device 0
2277 #define ATA_CB_DH_DEV1 0xb0 // select device 1
2278 #define ATA_CB_DH_LBA 0x40 // use LBA
2280 // status reg (CB_STAT and CB_ASTAT) bits
2281 #define ATA_CB_STAT_BSY 0x80 // busy
2282 #define ATA_CB_STAT_RDY 0x40 // ready
2283 #define ATA_CB_STAT_DF 0x20 // device fault
2284 #define ATA_CB_STAT_WFT 0x20 // write fault (old name)
2285 #define ATA_CB_STAT_SKC 0x10 // seek complete
2286 #define ATA_CB_STAT_SERV 0x10 // service
2287 #define ATA_CB_STAT_DRQ 0x08 // data request
2288 #define ATA_CB_STAT_CORR 0x04 // corrected
2289 #define ATA_CB_STAT_IDX 0x02 // index
2290 #define ATA_CB_STAT_ERR 0x01 // error (ATA)
2291 #define ATA_CB_STAT_CHK 0x01 // check (ATAPI)
2293 // device control reg (CB_DC) bits
2294 #define ATA_CB_DC_HD15 0x08 // bit should always be set to one
2295 #define ATA_CB_DC_SRST 0x04 // soft reset
2296 #define ATA_CB_DC_NIEN 0x02 // disable interrupts
2298 // Most mandtory and optional ATA commands (from ATA-3),
2299 #define ATA_CMD_CFA_ERASE_SECTORS 0xC0
2300 #define ATA_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03
2301 #define ATA_CMD_CFA_TRANSLATE_SECTOR 0x87
2302 #define ATA_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD
2303 #define ATA_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38
2304 #define ATA_CMD_CHECK_POWER_MODE1 0xE5
2305 #define ATA_CMD_CHECK_POWER_MODE2 0x98
2306 #define ATA_CMD_DEVICE_RESET 0x08
2307 #define ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90
2308 #define ATA_CMD_FLUSH_CACHE 0xE7
2309 #define ATA_CMD_FORMAT_TRACK 0x50
2310 #define ATA_CMD_IDENTIFY_DEVICE 0xEC
2311 #define ATA_CMD_IDENTIFY_DEVICE_PACKET 0xA1
2312 #define ATA_CMD_IDENTIFY_PACKET_DEVICE 0xA1
2313 #define ATA_CMD_IDLE1 0xE3
2314 #define ATA_CMD_IDLE2 0x97
2315 #define ATA_CMD_IDLE_IMMEDIATE1 0xE1
2316 #define ATA_CMD_IDLE_IMMEDIATE2 0x95
2317 #define ATA_CMD_INITIALIZE_DRIVE_PARAMETERS 0x91
2318 #define ATA_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91
2319 #define ATA_CMD_NOP 0x00
2320 #define ATA_CMD_PACKET 0xA0
2321 #define ATA_CMD_READ_BUFFER 0xE4
2322 #define ATA_CMD_READ_DMA 0xC8
2323 #define ATA_CMD_READ_DMA_QUEUED 0xC7
2324 #define ATA_CMD_READ_MULTIPLE 0xC4
2325 #define ATA_CMD_READ_SECTORS 0x20
2326 #define ATA_CMD_READ_VERIFY_SECTORS 0x40
2327 #define ATA_CMD_RECALIBRATE 0x10
2328 #define ATA_CMD_REQUEST_SENSE 0x03
2329 #define ATA_CMD_SEEK 0x70
2330 #define ATA_CMD_SET_FEATURES 0xEF
2331 #define ATA_CMD_SET_MULTIPLE_MODE 0xC6
2332 #define ATA_CMD_SLEEP1 0xE6
2333 #define ATA_CMD_SLEEP2 0x99
2334 #define ATA_CMD_STANDBY1 0xE2
2335 #define ATA_CMD_STANDBY2 0x96
2336 #define ATA_CMD_STANDBY_IMMEDIATE1 0xE0
2337 #define ATA_CMD_STANDBY_IMMEDIATE2 0x94
2338 #define ATA_CMD_WRITE_BUFFER 0xE8
2339 #define ATA_CMD_WRITE_DMA 0xCA
2340 #define ATA_CMD_WRITE_DMA_QUEUED 0xCC
2341 #define ATA_CMD_WRITE_MULTIPLE 0xC5
2342 #define ATA_CMD_WRITE_SECTORS 0x30
2343 #define ATA_CMD_WRITE_VERIFY 0x3C
2345 #define ATA_IFACE_NONE 0x00
2346 #define ATA_IFACE_ISA 0x00
2347 #define ATA_IFACE_PCI 0x01
2349 #define ATA_TYPE_NONE 0x00
2350 #define ATA_TYPE_UNKNOWN 0x01
2351 #define ATA_TYPE_ATA 0x02
2352 #define ATA_TYPE_ATAPI 0x03
2354 #define ATA_DEVICE_NONE 0x00
2355 #define ATA_DEVICE_HD 0xFF
2356 #define ATA_DEVICE_CDROM 0x05
2358 #define ATA_MODE_NONE 0x00
2359 #define ATA_MODE_PIO16 0x00
2360 #define ATA_MODE_PIO32 0x01
2361 #define ATA_MODE_ISADMA 0x02
2362 #define ATA_MODE_PCIDMA 0x03
2363 #define ATA_MODE_USEIRQ 0x10
2365 #define ATA_TRANSLATION_NONE 0
2366 #define ATA_TRANSLATION_LBA 1
2367 #define ATA_TRANSLATION_LARGE 2
2368 #define ATA_TRANSLATION_RECHS 3
2370 #define ATA_DATA_NO 0x00
2371 #define ATA_DATA_IN 0x01
2372 #define ATA_DATA_OUT 0x02
2374 // ---------------------------------------------------------------------------
2375 // ATA/ATAPI driver : initialization
2376 // ---------------------------------------------------------------------------
2377 void ata_init( )
2379 Bit16u ebda_seg=read_word(0x0040,0x000E);
2380 Bit8u channel, device;
2382 // Channels info init.
2383 for (channel=0; channel<BX_MAX_ATA_INTERFACES; channel++) {
2384 write_byte(ebda_seg,&EbdaData->ata.channels[channel].iface,ATA_IFACE_NONE);
2385 write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1,0x0);
2386 write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2,0x0);
2387 write_byte(ebda_seg,&EbdaData->ata.channels[channel].irq,0);
2390 // Devices info init.
2391 for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2392 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
2393 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_NONE);
2394 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable,0);
2395 write_byte(ebda_seg,&EbdaData->ata.devices[device].lock,0);
2396 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode,ATA_MODE_NONE);
2397 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize,0);
2398 write_byte(ebda_seg,&EbdaData->ata.devices[device].translation,ATA_TRANSLATION_NONE);
2399 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads,0);
2400 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders,0);
2401 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt,0);
2402 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads,0);
2403 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders,0);
2404 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt,0);
2406 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low,0L);
2407 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_high,0L);
2410 // hdidmap and cdidmap init.
2411 for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2412 write_byte(ebda_seg,&EbdaData->ata.hdidmap[device],BX_MAX_ATA_DEVICES);
2413 write_byte(ebda_seg,&EbdaData->ata.cdidmap[device],BX_MAX_ATA_DEVICES);
2416 write_byte(ebda_seg,&EbdaData->ata.hdcount,0);
2417 write_byte(ebda_seg,&EbdaData->ata.cdcount,0);
2420 #define TIMEOUT 0
2421 #define BSY 1
2422 #define NOT_BSY 2
2423 #define NOT_BSY_DRQ 3
2424 #define NOT_BSY_NOT_DRQ 4
2425 #define NOT_BSY_RDY 5
2427 #define IDE_TIMEOUT 32000u //32 seconds max for IDE ops
2429 int await_ide();
2430 static int await_ide(when_done,base,timeout)
2431 Bit8u when_done;
2432 Bit16u base;
2433 Bit16u timeout;
2435 Bit32u time=0,last=0;
2436 Bit16u status;
2437 Bit8u result;
2438 status = inb(base + ATA_CB_STAT); // for the times you're supposed to throw one away
2439 for(;;) {
2440 status = inb(base+ATA_CB_STAT);
2441 time++;
2442 if (when_done == BSY)
2443 result = status & ATA_CB_STAT_BSY;
2444 else if (when_done == NOT_BSY)
2445 result = !(status & ATA_CB_STAT_BSY);
2446 else if (when_done == NOT_BSY_DRQ)
2447 result = !(status & ATA_CB_STAT_BSY) && (status & ATA_CB_STAT_DRQ);
2448 else if (when_done == NOT_BSY_NOT_DRQ)
2449 result = !(status & ATA_CB_STAT_BSY) && !(status & ATA_CB_STAT_DRQ);
2450 else if (when_done == NOT_BSY_RDY)
2451 result = !(status & ATA_CB_STAT_BSY) && (status & ATA_CB_STAT_RDY);
2452 else if (when_done == TIMEOUT)
2453 result = 0;
2455 if (result) return 0;
2456 if (time>>16 != last) // mod 2048 each 16 ms
2458 last = time >>16;
2459 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);
2461 if (status & ATA_CB_STAT_ERR)
2463 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);
2464 return -1;
2466 if ((timeout == 0) || ((time>>11) > timeout)) break;
2468 BX_INFO("IDE time out\n");
2469 return -1;
2472 // ---------------------------------------------------------------------------
2473 // ATA/ATAPI driver : device detection
2474 // ---------------------------------------------------------------------------
2476 void ata_detect( )
2478 Bit16u ebda_seg=read_word(0x0040,0x000E);
2479 Bit8u hdcount, cdcount, device, type;
2480 Bit8u buffer[0x0200];
2482 #if BX_MAX_ATA_INTERFACES > 0
2483 write_byte(ebda_seg,&EbdaData->ata.channels[0].iface,ATA_IFACE_ISA);
2484 write_word(ebda_seg,&EbdaData->ata.channels[0].iobase1,0x1f0);
2485 write_word(ebda_seg,&EbdaData->ata.channels[0].iobase2,0x3f0);
2486 write_byte(ebda_seg,&EbdaData->ata.channels[0].irq,14);
2487 #endif
2488 #if BX_MAX_ATA_INTERFACES > 1
2489 write_byte(ebda_seg,&EbdaData->ata.channels[1].iface,ATA_IFACE_ISA);
2490 write_word(ebda_seg,&EbdaData->ata.channels[1].iobase1,0x170);
2491 write_word(ebda_seg,&EbdaData->ata.channels[1].iobase2,0x370);
2492 write_byte(ebda_seg,&EbdaData->ata.channels[1].irq,15);
2493 #endif
2494 #if BX_MAX_ATA_INTERFACES > 2
2495 write_byte(ebda_seg,&EbdaData->ata.channels[2].iface,ATA_IFACE_ISA);
2496 write_word(ebda_seg,&EbdaData->ata.channels[2].iobase1,0x1e8);
2497 write_word(ebda_seg,&EbdaData->ata.channels[2].iobase2,0x3e0);
2498 write_byte(ebda_seg,&EbdaData->ata.channels[2].irq,12);
2499 #endif
2500 #if BX_MAX_ATA_INTERFACES > 3
2501 write_byte(ebda_seg,&EbdaData->ata.channels[3].iface,ATA_IFACE_ISA);
2502 write_word(ebda_seg,&EbdaData->ata.channels[3].iobase1,0x168);
2503 write_word(ebda_seg,&EbdaData->ata.channels[3].iobase2,0x360);
2504 write_byte(ebda_seg,&EbdaData->ata.channels[3].irq,11);
2505 #endif
2506 #if BX_MAX_ATA_INTERFACES > 4
2507 #error Please fill the ATA interface informations
2508 #endif
2510 // Device detection
2511 hdcount=cdcount=0;
2513 for(device=0; device<BX_MAX_ATA_DEVICES; device++) {
2514 Bit16u iobase1, iobase2;
2515 Bit8u channel, slave, shift;
2516 Bit8u sc, sn, cl, ch, st;
2518 channel = device / 2;
2519 slave = device % 2;
2521 iobase1 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1);
2522 iobase2 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2);
2524 // Disable interrupts
2525 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2527 // Look for device
2528 outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2529 outb(iobase1+ATA_CB_SC, 0x55);
2530 outb(iobase1+ATA_CB_SN, 0xaa);
2531 outb(iobase1+ATA_CB_SC, 0xaa);
2532 outb(iobase1+ATA_CB_SN, 0x55);
2533 outb(iobase1+ATA_CB_SC, 0x55);
2534 outb(iobase1+ATA_CB_SN, 0xaa);
2536 // If we found something
2537 sc = inb(iobase1+ATA_CB_SC);
2538 sn = inb(iobase1+ATA_CB_SN);
2540 if ( (sc == 0x55) && (sn == 0xaa) ) {
2541 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_UNKNOWN);
2543 // reset the channel
2544 ata_reset(device);
2546 // check for ATA or ATAPI
2547 outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2548 sc = inb(iobase1+ATA_CB_SC);
2549 sn = inb(iobase1+ATA_CB_SN);
2550 if ((sc==0x01) && (sn==0x01)) {
2551 cl = inb(iobase1+ATA_CB_CL);
2552 ch = inb(iobase1+ATA_CB_CH);
2553 st = inb(iobase1+ATA_CB_STAT);
2555 if ((cl==0x14) && (ch==0xeb)) {
2556 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATAPI);
2557 } else if ((cl==0x00) && (ch==0x00) && (st!=0x00)) {
2558 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATA);
2559 } else if ((cl==0xff) && (ch==0xff)) {
2560 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
2565 type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type);
2567 // Now we send a IDENTIFY command to ATA device
2568 if(type == ATA_TYPE_ATA) {
2569 Bit32u sectors_low, sectors_high;
2570 Bit16u cylinders, heads, spt, blksize;
2571 Bit8u translation, removable, mode;
2573 //Temporary values to do the transfer
2574 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2575 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2577 if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE, 1, 0, 0, 0, 0L, 0L, get_SS(),buffer) !=0 )
2578 BX_PANIC("ata-detect: Failed to detect ATA device\n");
2580 removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2581 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2582 blksize = read_word(get_SS(),buffer+10);
2584 cylinders = read_word(get_SS(),buffer+(1*2)); // word 1
2585 heads = read_word(get_SS(),buffer+(3*2)); // word 3
2586 spt = read_word(get_SS(),buffer+(6*2)); // word 6
2588 if (read_word(get_SS(),buffer+(83*2)) & (1 << 10)) { // word 83 - lba48 support
2589 sectors_low = read_dword(get_SS(),buffer+(100*2)); // word 100 and word 101
2590 sectors_high = read_dword(get_SS(),buffer+(102*2)); // word 102 and word 103
2591 } else {
2592 sectors_low = read_dword(get_SS(),buffer+(60*2)); // word 60 and word 61
2593 sectors_high = 0;
2596 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2597 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2598 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2599 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2600 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads, heads);
2601 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders, cylinders);
2602 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt, spt);
2603 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low, sectors_low);
2604 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_high, sectors_high);
2605 BX_INFO("ata%d-%d: PCHS=%u/%d/%d translation=", channel, slave,cylinders, heads, spt);
2607 translation = inb_cmos(0x39 + channel/2);
2608 for (shift=device%4; shift>0; shift--) translation >>= 2;
2609 translation &= 0x03;
2611 write_byte(ebda_seg,&EbdaData->ata.devices[device].translation, translation);
2613 switch (translation) {
2614 case ATA_TRANSLATION_NONE:
2615 BX_INFO("none");
2616 break;
2617 case ATA_TRANSLATION_LBA:
2618 BX_INFO("lba");
2619 break;
2620 case ATA_TRANSLATION_LARGE:
2621 BX_INFO("large");
2622 break;
2623 case ATA_TRANSLATION_RECHS:
2624 BX_INFO("r-echs");
2625 break;
2628 switch (translation) {
2629 case ATA_TRANSLATION_NONE:
2630 break;
2631 case ATA_TRANSLATION_LBA:
2632 spt = 63;
2633 sectors_low /= 63;
2634 heads = sectors_low / 1024;
2635 if (heads>128) heads = 255;
2636 else if (heads>64) heads = 128;
2637 else if (heads>32) heads = 64;
2638 else if (heads>16) heads = 32;
2639 else heads=16;
2640 cylinders = sectors_low / heads;
2641 break;
2642 case ATA_TRANSLATION_RECHS:
2643 // Take care not to overflow
2644 if (heads==16) {
2645 if(cylinders>61439) cylinders=61439;
2646 heads=15;
2647 cylinders = (Bit16u)((Bit32u)(cylinders)*16/15);
2649 // then go through the large bitshift process
2650 case ATA_TRANSLATION_LARGE:
2651 while(cylinders > 1024) {
2652 cylinders >>= 1;
2653 heads <<= 1;
2655 // If we max out the head count
2656 if (heads > 127) break;
2658 break;
2661 // clip to 1024 cylinders in lchs
2662 if (cylinders > 1024) cylinders=1024;
2663 BX_INFO(" LCHS=%d/%d/%d\n", cylinders, heads, spt);
2665 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads, heads);
2666 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders, cylinders);
2667 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt, spt);
2669 // fill hdidmap
2670 write_byte(ebda_seg,&EbdaData->ata.hdidmap[hdcount], device);
2671 hdcount++;
2674 // Now we send a IDENTIFY command to ATAPI device
2675 if(type == ATA_TYPE_ATAPI) {
2677 Bit8u type, removable, mode;
2678 Bit16u blksize;
2680 //Temporary values to do the transfer
2681 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_CDROM);
2682 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2684 if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE_PACKET, 1, 0, 0, 0, 0L, 0L, get_SS(),buffer) != 0)
2685 BX_PANIC("ata-detect: Failed to detect ATAPI device\n");
2687 type = read_byte(get_SS(),buffer+1) & 0x1f;
2688 removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2689 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2690 blksize = 2048;
2692 write_byte(ebda_seg,&EbdaData->ata.devices[device].device, type);
2693 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2694 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2695 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2697 // fill cdidmap
2698 write_byte(ebda_seg,&EbdaData->ata.cdidmap[cdcount], device);
2699 cdcount++;
2703 Bit32u sizeinmb;
2704 Bit16u ataversion;
2705 Bit8u c, i, version, model[41];
2707 switch (type) {
2708 case ATA_TYPE_ATA:
2709 sizeinmb = (read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_high) << 21)
2710 | (read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low) >> 11);
2711 case ATA_TYPE_ATAPI:
2712 // Read ATA/ATAPI version
2713 ataversion=((Bit16u)(read_byte(get_SS(),buffer+161))<<8)|read_byte(get_SS(),buffer+160);
2714 for(version=15;version>0;version--) {
2715 if((ataversion&(1<<version))!=0)
2716 break;
2719 // Read model name
2720 for(i=0;i<20;i++) {
2721 write_byte(get_SS(),model+(i*2),read_byte(get_SS(),buffer+(i*2)+54+1));
2722 write_byte(get_SS(),model+(i*2)+1,read_byte(get_SS(),buffer+(i*2)+54));
2725 // Reformat
2726 write_byte(get_SS(),model+40,0x00);
2727 for(i=39;i>0;i--){
2728 if(read_byte(get_SS(),model+i)==0x20)
2729 write_byte(get_SS(),model+i,0x00);
2730 else break;
2732 if (i>36) {
2733 write_byte(get_SS(),model+36,0x00);
2734 for(i=35;i>32;i--){
2735 write_byte(get_SS(),model+i,0x2E);
2738 break;
2741 switch (type) {
2742 case ATA_TYPE_ATA:
2743 printf("ata%d %s: ",channel,slave?" slave":"master");
2744 i=0;
2745 while(c=read_byte(get_SS(),model+i++))
2746 printf("%c",c);
2747 if (sizeinmb < (1UL<<16))
2748 printf(" ATA-%d Hard-Disk (%4u MBytes)\n", version, (Bit16u)sizeinmb);
2749 else
2750 printf(" ATA-%d Hard-Disk (%4u GBytes)\n", version, (Bit16u)(sizeinmb>>10));
2751 break;
2752 case ATA_TYPE_ATAPI:
2753 printf("ata%d %s: ",channel,slave?" slave":"master");
2754 i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
2755 if(read_byte(ebda_seg,&EbdaData->ata.devices[device].device)==ATA_DEVICE_CDROM)
2756 printf(" ATAPI-%d CD-Rom/DVD-Rom\n",version);
2757 else
2758 printf(" ATAPI-%d Device\n",version);
2759 break;
2760 case ATA_TYPE_UNKNOWN:
2761 printf("ata%d %s: Unknown device\n",channel,slave?" slave":"master");
2762 break;
2767 // Store the devices counts
2768 write_byte(ebda_seg,&EbdaData->ata.hdcount, hdcount);
2769 write_byte(ebda_seg,&EbdaData->ata.cdcount, cdcount);
2770 write_byte(0x40,0x75, hdcount);
2772 printf("\n");
2774 // FIXME : should use bios=cmos|auto|disable bits
2775 // FIXME : should know about translation bits
2776 // FIXME : move hard_drive_post here
2780 // ---------------------------------------------------------------------------
2781 // ATA/ATAPI driver : software reset
2782 // ---------------------------------------------------------------------------
2783 // ATA-3
2784 // 8.2.1 Software reset - Device 0
2786 void ata_reset(device)
2787 Bit16u device;
2789 Bit16u ebda_seg=read_word(0x0040,0x000E);
2790 Bit16u iobase1, iobase2;
2791 Bit8u channel, slave, sn, sc;
2792 Bit8u type;
2793 Bit16u max;
2795 channel = device / 2;
2796 slave = device % 2;
2798 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2799 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2801 // Reset
2803 // 8.2.1 (a) -- set SRST in DC
2804 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST);
2806 // 8.2.1 (b) -- wait for BSY
2807 await_ide(BSY, iobase1, 20);
2809 // 8.2.1 (f) -- clear SRST
2810 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2812 type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type);
2813 if (type != ATA_TYPE_NONE) {
2815 // 8.2.1 (g) -- check for sc==sn==0x01
2816 // select device
2817 outb(iobase1+ATA_CB_DH, slave?ATA_CB_DH_DEV1:ATA_CB_DH_DEV0);
2818 sc = inb(iobase1+ATA_CB_SC);
2819 sn = inb(iobase1+ATA_CB_SN);
2821 if ( (sc==0x01) && (sn==0x01) ) {
2822 if (type == ATA_TYPE_ATA) //ATA
2823 await_ide(NOT_BSY_RDY, iobase1, IDE_TIMEOUT);
2824 else //ATAPI
2825 await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
2828 // 8.2.1 (h) -- wait for not BSY
2829 await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
2832 // Enable interrupts
2833 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2836 // ---------------------------------------------------------------------------
2837 // ATA/ATAPI driver : execute a non data command
2838 // ---------------------------------------------------------------------------
2840 Bit16u ata_cmd_non_data()
2841 {return 0;}
2843 // ---------------------------------------------------------------------------
2844 // ATA/ATAPI driver : execute a data-in command
2845 // ---------------------------------------------------------------------------
2846 // returns
2847 // 0 : no error
2848 // 1 : BUSY bit set
2849 // 2 : read error
2850 // 3 : expected DRQ=1
2851 // 4 : no sectors left to read/verify
2852 // 5 : more sectors to read/verify
2853 // 6 : no sectors left to write
2854 // 7 : more sectors to write
2855 Bit16u ata_cmd_data_in(device, command, count, cylinder, head, sector, lba_low, lba_high, segment, offset)
2856 Bit16u device, command, count, cylinder, head, sector, segment, offset;
2857 Bit32u lba_low, lba_high;
2859 Bit16u ebda_seg=read_word(0x0040,0x000E);
2860 Bit16u iobase1, iobase2, blksize;
2861 Bit8u channel, slave;
2862 Bit8u status, current, mode;
2864 channel = device / 2;
2865 slave = device % 2;
2867 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2868 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2869 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2870 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
2871 if (mode == ATA_MODE_PIO32) blksize>>=2;
2872 else blksize>>=1;
2874 // Reset count of transferred data
2875 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2876 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2877 current = 0;
2879 status = inb(iobase1 + ATA_CB_STAT);
2880 if (status & ATA_CB_STAT_BSY) return 1;
2882 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2884 // sector will be 0 only on lba access. Convert to lba-chs
2885 if (sector == 0) {
2886 if ((count >= 1 << 8) || lba_high || (lba_low + count >= 1UL << 28)) {
2887 outb(iobase1 + ATA_CB_FR, 0x00);
2888 outb(iobase1 + ATA_CB_SC, (count >> 8) & 0xff);
2889 outb(iobase1 + ATA_CB_SN, lba_low >> 24);
2890 outb(iobase1 + ATA_CB_CL, lba_high & 0xff);
2891 outb(iobase1 + ATA_CB_CH, lba_high >> 8);
2892 command |= 0x04;
2893 count &= (1UL << 8) - 1;
2894 lba_low &= (1UL << 24) - 1;
2896 sector = (Bit16u) (lba_low & 0x000000ffL);
2897 cylinder = (Bit16u) ((lba_low>>8) & 0x0000ffffL);
2898 head = ((Bit16u) ((lba_low>>24) & 0x0000000fL)) | ATA_CB_DH_LBA;
2901 outb(iobase1 + ATA_CB_FR, 0x00);
2902 outb(iobase1 + ATA_CB_SC, count);
2903 outb(iobase1 + ATA_CB_SN, sector);
2904 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
2905 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
2906 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
2907 outb(iobase1 + ATA_CB_CMD, command);
2909 await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
2910 status = inb(iobase1 + ATA_CB_STAT);
2912 if (status & ATA_CB_STAT_ERR) {
2913 BX_DEBUG_ATA("ata_cmd_data_in : read error\n");
2914 return 2;
2915 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2916 BX_DEBUG_ATA("ata_cmd_data_in : DRQ not set (status %02x)\n", (unsigned) status);
2917 return 3;
2920 // FIXME : move seg/off translation here
2922 ASM_START
2923 sti ;; enable higher priority interrupts
2924 ASM_END
2926 while (1) {
2928 ASM_START
2929 push bp
2930 mov bp, sp
2931 mov di, _ata_cmd_data_in.offset + 2[bp]
2932 mov ax, _ata_cmd_data_in.segment + 2[bp]
2933 mov cx, _ata_cmd_data_in.blksize + 2[bp]
2935 ;; adjust if there will be an overrun. 2K max sector size
2936 cmp di, #0xf800 ;;
2937 jbe ata_in_no_adjust
2939 ata_in_adjust:
2940 sub di, #0x0800 ;; sub 2 kbytes from offset
2941 add ax, #0x0080 ;; add 2 Kbytes to segment
2943 ata_in_no_adjust:
2944 mov es, ax ;; segment in es
2946 mov dx, _ata_cmd_data_in.iobase1 + 2[bp] ;; ATA data read port
2948 mov ah, _ata_cmd_data_in.mode + 2[bp]
2949 cmp ah, #ATA_MODE_PIO32
2950 je ata_in_32
2952 ata_in_16:
2954 insw ;; CX words transfered from port(DX) to ES:[DI]
2955 jmp ata_in_done
2957 ata_in_32:
2959 insd ;; CX dwords transfered from port(DX) to ES:[DI]
2961 ata_in_done:
2962 mov _ata_cmd_data_in.offset + 2[bp], di
2963 mov _ata_cmd_data_in.segment + 2[bp], es
2964 pop bp
2965 ASM_END
2967 current++;
2968 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
2969 count--;
2970 await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
2971 status = inb(iobase1 + ATA_CB_STAT);
2972 if (count == 0) {
2973 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2974 != ATA_CB_STAT_RDY ) {
2975 BX_DEBUG_ATA("ata_cmd_data_in : no sectors left (status %02x)\n", (unsigned) status);
2976 return 4;
2978 break;
2980 else {
2981 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2982 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2983 BX_DEBUG_ATA("ata_cmd_data_in : more sectors left (status %02x)\n", (unsigned) status);
2984 return 5;
2986 continue;
2989 // Enable interrupts
2990 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2991 return 0;
2994 // ---------------------------------------------------------------------------
2995 // ATA/ATAPI driver : execute a data-out command
2996 // ---------------------------------------------------------------------------
2997 // returns
2998 // 0 : no error
2999 // 1 : BUSY bit set
3000 // 2 : read error
3001 // 3 : expected DRQ=1
3002 // 4 : no sectors left to read/verify
3003 // 5 : more sectors to read/verify
3004 // 6 : no sectors left to write
3005 // 7 : more sectors to write
3006 Bit16u ata_cmd_data_out(device, command, count, cylinder, head, sector, lba_low, lba_high, segment, offset)
3007 Bit16u device, command, count, cylinder, head, sector, segment, offset;
3008 Bit32u lba_low, lba_high;
3010 Bit16u ebda_seg=read_word(0x0040,0x000E);
3011 Bit16u iobase1, iobase2, blksize;
3012 Bit8u channel, slave;
3013 Bit8u status, current, mode;
3015 channel = device / 2;
3016 slave = device % 2;
3018 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
3019 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
3020 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
3021 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
3022 if (mode == ATA_MODE_PIO32) blksize>>=2;
3023 else blksize>>=1;
3025 // Reset count of transferred data
3026 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
3027 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
3028 current = 0;
3030 status = inb(iobase1 + ATA_CB_STAT);
3031 if (status & ATA_CB_STAT_BSY) return 1;
3033 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
3035 // sector will be 0 only on lba access. Convert to lba-chs
3036 if (sector == 0) {
3037 if ((count >= 1 << 8) || lba_high || (lba_low + count >= 1UL << 28)) {
3038 outb(iobase1 + ATA_CB_FR, 0x00);
3039 outb(iobase1 + ATA_CB_SC, (count >> 8) & 0xff);
3040 outb(iobase1 + ATA_CB_SN, lba_low >> 24);
3041 outb(iobase1 + ATA_CB_CL, lba_high & 0xff);
3042 outb(iobase1 + ATA_CB_CH, lba_high >> 8);
3043 command |= 0x04;
3044 count &= (1UL << 8) - 1;
3045 lba_low &= (1UL << 24) - 1;
3047 sector = (Bit16u) (lba_low & 0x000000ffL);
3048 cylinder = (Bit16u) ((lba_low>>8) & 0x0000ffffL);
3049 head = ((Bit16u) ((lba_low>>24) & 0x0000000fL)) | ATA_CB_DH_LBA;
3052 outb(iobase1 + ATA_CB_FR, 0x00);
3053 outb(iobase1 + ATA_CB_SC, count);
3054 outb(iobase1 + ATA_CB_SN, sector);
3055 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
3056 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
3057 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
3058 outb(iobase1 + ATA_CB_CMD, command);
3060 await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
3061 status = inb(iobase1 + ATA_CB_STAT);
3063 if (status & ATA_CB_STAT_ERR) {
3064 BX_DEBUG_ATA("ata_cmd_data_out : read error\n");
3065 return 2;
3066 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
3067 BX_DEBUG_ATA("ata_cmd_data_out : DRQ not set (status %02x)\n", (unsigned) status);
3068 return 3;
3071 // FIXME : move seg/off translation here
3073 ASM_START
3074 sti ;; enable higher priority interrupts
3075 ASM_END
3077 while (1) {
3079 ASM_START
3080 push bp
3081 mov bp, sp
3082 mov si, _ata_cmd_data_out.offset + 2[bp]
3083 mov ax, _ata_cmd_data_out.segment + 2[bp]
3084 mov cx, _ata_cmd_data_out.blksize + 2[bp]
3086 ;; adjust if there will be an overrun. 2K max sector size
3087 cmp si, #0xf800 ;;
3088 jbe ata_out_no_adjust
3090 ata_out_adjust:
3091 sub si, #0x0800 ;; sub 2 kbytes from offset
3092 add ax, #0x0080 ;; add 2 Kbytes to segment
3094 ata_out_no_adjust:
3095 mov es, ax ;; segment in es
3097 mov dx, _ata_cmd_data_out.iobase1 + 2[bp] ;; ATA data write port
3099 mov ah, _ata_cmd_data_out.mode + 2[bp]
3100 cmp ah, #ATA_MODE_PIO32
3101 je ata_out_32
3103 ata_out_16:
3104 seg ES
3106 outsw ;; CX words transfered from port(DX) to ES:[SI]
3107 jmp ata_out_done
3109 ata_out_32:
3110 seg ES
3112 outsd ;; CX dwords transfered from port(DX) to ES:[SI]
3114 ata_out_done:
3115 mov _ata_cmd_data_out.offset + 2[bp], si
3116 mov _ata_cmd_data_out.segment + 2[bp], es
3117 pop bp
3118 ASM_END
3120 current++;
3121 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
3122 count--;
3123 status = inb(iobase1 + ATA_CB_STAT);
3124 if (count == 0) {
3125 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
3126 != ATA_CB_STAT_RDY ) {
3127 BX_DEBUG_ATA("ata_cmd_data_out : no sectors left (status %02x)\n", (unsigned) status);
3128 return 6;
3130 break;
3132 else {
3133 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
3134 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
3135 BX_DEBUG_ATA("ata_cmd_data_out : more sectors left (status %02x)\n", (unsigned) status);
3136 return 7;
3138 continue;
3141 // Enable interrupts
3142 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
3143 return 0;
3146 // ---------------------------------------------------------------------------
3147 // ATA/ATAPI driver : execute a packet command
3148 // ---------------------------------------------------------------------------
3149 // returns
3150 // 0 : no error
3151 // 1 : error in parameters
3152 // 2 : BUSY bit set
3153 // 3 : error
3154 // 4 : not ready
3155 Bit16u ata_cmd_packet(device, cmdlen, cmdseg, cmdoff, header, length, inout, bufseg, bufoff)
3156 Bit8u cmdlen,inout;
3157 Bit16u device,cmdseg, cmdoff, bufseg, bufoff;
3158 Bit16u header;
3159 Bit32u length;
3161 Bit16u ebda_seg=read_word(0x0040,0x000E);
3162 Bit16u iobase1, iobase2;
3163 Bit16u lcount, lbefore, lafter, count;
3164 Bit8u channel, slave;
3165 Bit8u status, mode, lmode;
3166 Bit32u total, transfer;
3168 channel = device / 2;
3169 slave = device % 2;
3171 // Data out is not supported yet
3172 if (inout == ATA_DATA_OUT) {
3173 BX_INFO("ata_cmd_packet: DATA_OUT not supported yet\n");
3174 return 1;
3177 // The header length must be even
3178 if (header & 1) {
3179 BX_DEBUG_ATA("ata_cmd_packet : header must be even (%04x)\n",header);
3180 return 1;
3183 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
3184 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
3185 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
3186 transfer= 0L;
3188 if (cmdlen < 12) cmdlen=12;
3189 if (cmdlen > 12) cmdlen=16;
3190 cmdlen>>=1;
3192 // Reset count of transferred data
3193 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
3194 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
3196 status = inb(iobase1 + ATA_CB_STAT);
3197 if (status & ATA_CB_STAT_BSY) return 2;
3199 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
3200 outb(iobase1 + ATA_CB_FR, 0x00);
3201 outb(iobase1 + ATA_CB_SC, 0x00);
3202 outb(iobase1 + ATA_CB_SN, 0x00);
3203 outb(iobase1 + ATA_CB_CL, 0xfff0 & 0x00ff);
3204 outb(iobase1 + ATA_CB_CH, 0xfff0 >> 8);
3205 outb(iobase1 + ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
3206 outb(iobase1 + ATA_CB_CMD, ATA_CMD_PACKET);
3208 // Device should ok to receive command
3209 await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
3210 status = inb(iobase1 + ATA_CB_STAT);
3212 if (status & ATA_CB_STAT_ERR) {
3213 BX_DEBUG_ATA("ata_cmd_packet : error, status is %02x\n",status);
3214 return 3;
3215 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
3216 BX_DEBUG_ATA("ata_cmd_packet : DRQ not set (status %02x)\n", (unsigned) status);
3217 return 4;
3220 // Normalize address
3221 cmdseg += (cmdoff / 16);
3222 cmdoff %= 16;
3224 // Send command to device
3225 ASM_START
3226 sti ;; enable higher priority interrupts
3228 push bp
3229 mov bp, sp
3231 mov si, _ata_cmd_packet.cmdoff + 2[bp]
3232 mov ax, _ata_cmd_packet.cmdseg + 2[bp]
3233 mov cx, _ata_cmd_packet.cmdlen + 2[bp]
3234 mov es, ax ;; segment in es
3236 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data write port
3238 seg ES
3240 outsw ;; CX words transfered from port(DX) to ES:[SI]
3242 pop bp
3243 ASM_END
3245 if (inout == ATA_DATA_NO) {
3246 await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
3247 status = inb(iobase1 + ATA_CB_STAT);
3249 else {
3250 Bit16u loops = 0;
3251 Bit8u sc;
3253 while (1) {
3255 if (loops == 0) {//first time through
3256 status = inb(iobase2 + ATA_CB_ASTAT);
3257 await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
3259 else
3260 await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
3261 loops++;
3263 status = inb(iobase1 + ATA_CB_STAT);
3264 sc = inb(iobase1 + ATA_CB_SC);
3266 // Check if command completed
3267 if(((inb(iobase1 + ATA_CB_SC)&0x7)==0x3) &&
3268 ((status & (ATA_CB_STAT_RDY | ATA_CB_STAT_ERR)) == ATA_CB_STAT_RDY)) break;
3270 if (status & ATA_CB_STAT_ERR) {
3271 BX_DEBUG_ATA("ata_cmd_packet : error (status %02x)\n",status);
3272 return 3;
3275 // Normalize address
3276 bufseg += (bufoff / 16);
3277 bufoff %= 16;
3279 // Get the byte count
3280 lcount = ((Bit16u)(inb(iobase1 + ATA_CB_CH))<<8)+inb(iobase1 + ATA_CB_CL);
3282 // adjust to read what we want
3283 if(header>lcount) {
3284 lbefore=lcount;
3285 header-=lcount;
3286 lcount=0;
3288 else {
3289 lbefore=header;
3290 header=0;
3291 lcount-=lbefore;
3294 if(lcount>length) {
3295 lafter=lcount-length;
3296 lcount=length;
3297 length=0;
3299 else {
3300 lafter=0;
3301 length-=lcount;
3304 // Save byte count
3305 count = lcount;
3307 BX_DEBUG_ATA("Trying to read %04x bytes (%04x %04x %04x) ",lbefore+lcount+lafter,lbefore,lcount,lafter);
3308 BX_DEBUG_ATA("to 0x%04x:0x%04x\n",bufseg,bufoff);
3310 // If counts not dividable by 4, use 16bits mode
3311 lmode = mode;
3312 if (lbefore & 0x03) lmode=ATA_MODE_PIO16;
3313 if (lcount & 0x03) lmode=ATA_MODE_PIO16;
3314 if (lafter & 0x03) lmode=ATA_MODE_PIO16;
3316 // adds an extra byte if count are odd. before is always even
3317 if (lcount & 0x01) {
3318 lcount+=1;
3319 if ((lafter > 0) && (lafter & 0x01)) {
3320 lafter-=1;
3324 if (lmode == ATA_MODE_PIO32) {
3325 lcount>>=2; lbefore>>=2; lafter>>=2;
3327 else {
3328 lcount>>=1; lbefore>>=1; lafter>>=1;
3331 ; // FIXME bcc bug
3333 ASM_START
3334 push bp
3335 mov bp, sp
3337 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data read port
3339 mov cx, _ata_cmd_packet.lbefore + 2[bp]
3340 jcxz ata_packet_no_before
3342 mov ah, _ata_cmd_packet.lmode + 2[bp]
3343 cmp ah, #ATA_MODE_PIO32
3344 je ata_packet_in_before_32
3346 ata_packet_in_before_16:
3347 in ax, dx
3348 loop ata_packet_in_before_16
3349 jmp ata_packet_no_before
3351 ata_packet_in_before_32:
3352 push eax
3353 ata_packet_in_before_32_loop:
3354 in eax, dx
3355 loop ata_packet_in_before_32_loop
3356 pop eax
3358 ata_packet_no_before:
3359 mov cx, _ata_cmd_packet.lcount + 2[bp]
3360 jcxz ata_packet_after
3362 mov di, _ata_cmd_packet.bufoff + 2[bp]
3363 mov ax, _ata_cmd_packet.bufseg + 2[bp]
3364 mov es, ax
3366 mov ah, _ata_cmd_packet.lmode + 2[bp]
3367 cmp ah, #ATA_MODE_PIO32
3368 je ata_packet_in_32
3370 ata_packet_in_16:
3372 insw ;; CX words transfered tp port(DX) to ES:[DI]
3373 jmp ata_packet_after
3375 ata_packet_in_32:
3377 insd ;; CX dwords transfered to port(DX) to ES:[DI]
3379 ata_packet_after:
3380 mov cx, _ata_cmd_packet.lafter + 2[bp]
3381 jcxz ata_packet_done
3383 mov ah, _ata_cmd_packet.lmode + 2[bp]
3384 cmp ah, #ATA_MODE_PIO32
3385 je ata_packet_in_after_32
3387 ata_packet_in_after_16:
3388 in ax, dx
3389 loop ata_packet_in_after_16
3390 jmp ata_packet_done
3392 ata_packet_in_after_32:
3393 push eax
3394 ata_packet_in_after_32_loop:
3395 in eax, dx
3396 loop ata_packet_in_after_32_loop
3397 pop eax
3399 ata_packet_done:
3400 pop bp
3401 ASM_END
3403 // Compute new buffer address
3404 bufoff += count;
3406 // Save transferred bytes count
3407 transfer += count;
3408 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,transfer);
3412 // Final check, device must be ready
3413 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
3414 != ATA_CB_STAT_RDY ) {
3415 BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", (unsigned) status);
3416 return 4;
3419 // Enable interrupts
3420 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
3421 return 0;
3424 // ---------------------------------------------------------------------------
3425 // End of ATA/ATAPI Driver
3426 // ---------------------------------------------------------------------------
3428 // ---------------------------------------------------------------------------
3429 // Start of ATA/ATAPI generic functions
3430 // ---------------------------------------------------------------------------
3432 Bit16u
3433 atapi_get_sense(device, seg, asc, ascq)
3434 Bit16u device;
3436 Bit8u atacmd[12];
3437 Bit8u buffer[18];
3438 Bit8u i;
3440 memsetb(get_SS(),atacmd,0,12);
3442 // Request SENSE
3443 atacmd[0]=ATA_CMD_REQUEST_SENSE;
3444 atacmd[4]=sizeof(buffer);
3445 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 18L, ATA_DATA_IN, get_SS(), buffer) != 0)
3446 return 0x0002;
3448 write_byte(seg,asc,buffer[12]);
3449 write_byte(seg,ascq,buffer[13]);
3451 return 0;
3454 Bit16u
3455 atapi_is_ready(device)
3456 Bit16u device;
3458 Bit8u packet[12];
3459 Bit8u buf[8];
3460 Bit32u block_len;
3461 Bit32u sectors;
3462 Bit32u timeout; //measured in ms
3463 Bit32u time;
3464 Bit8u asc, ascq;
3465 Bit8u in_progress;
3466 Bit16u ebda_seg = read_word(0x0040,0x000E);
3467 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI) {
3468 printf("not implemented for non-ATAPI device\n");
3469 return -1;
3472 BX_DEBUG_ATA("ata_detect_medium: begin\n");
3473 memsetb(get_SS(),packet, 0, sizeof packet);
3474 packet[0] = 0x25; /* READ CAPACITY */
3476 /* Retry READ CAPACITY 50 times unless MEDIUM NOT PRESENT
3477 * is reported by the device. If the device reports "IN PROGRESS",
3478 * 30 seconds is added. */
3479 timeout = 5000;
3480 time = 0;
3481 in_progress = 0;
3482 while (time < timeout) {
3483 if (ata_cmd_packet(device, sizeof(packet), get_SS(), packet, 0, 8L, ATA_DATA_IN, get_SS(), buf) == 0)
3484 goto ok;
3486 if (atapi_get_sense(device, get_SS(), &asc, &ascq) == 0) {
3487 if (asc == 0x3a) { /* MEDIUM NOT PRESENT */
3488 BX_DEBUG_ATA("Device reports MEDIUM NOT PRESENT\n");
3489 return -1;
3492 if (asc == 0x04 && ascq == 0x01 && !in_progress) {
3493 /* IN PROGRESS OF BECOMING READY */
3494 printf("Waiting for device to detect medium... ");
3495 /* Allow 30 seconds more */
3496 timeout = 30000;
3497 in_progress = 1;
3500 time += 100;
3502 BX_DEBUG_ATA("read capacity failed\n");
3503 return -1;
3506 block_len = (Bit32u) buf[4] << 24
3507 | (Bit32u) buf[5] << 16
3508 | (Bit32u) buf[6] << 8
3509 | (Bit32u) buf[7] << 0;
3510 BX_DEBUG_ATA("block_len=%u\n", block_len);
3512 if (block_len!= 2048 && block_len!= 512)
3514 printf("Unsupported sector size %u\n", block_len);
3515 return -1;
3517 write_dword(ebda_seg,&EbdaData->ata.devices[device].blksize, block_len);
3519 sectors = (Bit32u) buf[0] << 24
3520 | (Bit32u) buf[1] << 16
3521 | (Bit32u) buf[2] << 8
3522 | (Bit32u) buf[3] << 0;
3524 BX_DEBUG_ATA("sectors=%u\n", sectors);
3525 if (block_len == 2048)
3526 sectors <<= 2; /* # of sectors in 512-byte "soft" sector */
3527 if (sectors != read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low))
3528 printf("%dMB medium detected\n", sectors>>(20-9));
3529 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low, sectors);
3530 return 0;
3533 Bit16u
3534 atapi_is_cdrom(device)
3535 Bit8u device;
3537 Bit16u ebda_seg=read_word(0x0040,0x000E);
3539 if (device >= BX_MAX_ATA_DEVICES)
3540 return 0;
3542 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI)
3543 return 0;
3545 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].device) != ATA_DEVICE_CDROM)
3546 return 0;
3548 return 1;
3551 // ---------------------------------------------------------------------------
3552 // End of ATA/ATAPI generic functions
3553 // ---------------------------------------------------------------------------
3555 #endif // BX_USE_ATADRV
3557 #if BX_ELTORITO_BOOT
3559 // ---------------------------------------------------------------------------
3560 // Start of El-Torito boot functions
3561 // ---------------------------------------------------------------------------
3563 void
3564 cdemu_init()
3566 Bit16u ebda_seg=read_word(0x0040,0x000E);
3568 // the only important data is this one for now
3569 write_byte(ebda_seg,&EbdaData->cdemu.active,0x00);
3572 Bit8u
3573 cdemu_isactive()
3575 Bit16u ebda_seg=read_word(0x0040,0x000E);
3577 return(read_byte(ebda_seg,&EbdaData->cdemu.active));
3580 Bit8u
3581 cdemu_emulated_drive()
3583 Bit16u ebda_seg=read_word(0x0040,0x000E);
3585 return(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
3588 static char isotag[6]="CD001";
3589 static char eltorito[24]="EL TORITO SPECIFICATION";
3591 // Returns ah: emulated drive, al: error code
3593 Bit16u
3594 cdrom_boot()
3596 Bit16u ebda_seg=read_word(0x0040,0x000E);
3597 Bit8u atacmd[12], buffer[2048];
3598 Bit32u lba;
3599 Bit16u boot_segment, nbsectors, i, error;
3600 Bit8u device;
3602 // Find out the first cdrom
3603 for (device=0; device<BX_MAX_ATA_DEVICES;device++) {
3604 if (atapi_is_cdrom(device)) break;
3607 // if not found
3608 if(device >= BX_MAX_ATA_DEVICES) return 2;
3610 if(error = atapi_is_ready(device) != 0)
3611 BX_INFO("ata_is_ready returned %d\n",error);
3613 // Read the Boot Record Volume Descriptor
3614 memsetb(get_SS(),atacmd,0,12);
3615 atacmd[0]=0x28; // READ command
3616 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3617 atacmd[8]=(0x01 & 0x00ff); // Sectors
3618 atacmd[2]=(0x11 & 0xff000000) >> 24; // LBA
3619 atacmd[3]=(0x11 & 0x00ff0000) >> 16;
3620 atacmd[4]=(0x11 & 0x0000ff00) >> 8;
3621 atacmd[5]=(0x11 & 0x000000ff);
3622 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3623 return 3;
3625 // Validity checks
3626 if(buffer[0]!=0) return 4;
3627 for(i=0;i<5;i++){
3628 if(buffer[1+i]!=read_byte(0xf000,&isotag[i])) return 5;
3630 for(i=0;i<23;i++)
3631 if(buffer[7+i]!=read_byte(0xf000,&eltorito[i])) return 6;
3633 // ok, now we calculate the Boot catalog address
3634 lba=buffer[0x4A]*0x1000000+buffer[0x49]*0x10000+buffer[0x48]*0x100+buffer[0x47];
3636 // And we read the Boot Catalog
3637 memsetb(get_SS(),atacmd,0,12);
3638 atacmd[0]=0x28; // READ command
3639 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3640 atacmd[8]=(0x01 & 0x00ff); // Sectors
3641 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3642 atacmd[3]=(lba & 0x00ff0000) >> 16;
3643 atacmd[4]=(lba & 0x0000ff00) >> 8;
3644 atacmd[5]=(lba & 0x000000ff);
3645 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3646 return 7;
3648 // Validation entry
3649 if(buffer[0x00]!=0x01)return 8; // Header
3650 if(buffer[0x01]!=0x00)return 9; // Platform
3651 if(buffer[0x1E]!=0x55)return 10; // key 1
3652 if(buffer[0x1F]!=0xAA)return 10; // key 2
3654 // Initial/Default Entry
3655 if(buffer[0x20]!=0x88)return 11; // Bootable
3657 write_byte(ebda_seg,&EbdaData->cdemu.media,buffer[0x21]);
3658 if(buffer[0x21]==0){
3659 // FIXME ElTorito Hardcoded. cdrom is hardcoded as device 0xE0.
3660 // Win2000 cd boot needs to know it booted from cd
3661 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0xE0);
3663 else if(buffer[0x21]<4)
3664 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x00);
3665 else
3666 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x80);
3668 write_byte(ebda_seg,&EbdaData->cdemu.controller_index,device/2);
3669 write_byte(ebda_seg,&EbdaData->cdemu.device_spec,device%2);
3671 boot_segment=buffer[0x23]*0x100+buffer[0x22];
3672 if(boot_segment==0x0000)boot_segment=0x07C0;
3674 write_word(ebda_seg,&EbdaData->cdemu.load_segment,boot_segment);
3675 write_word(ebda_seg,&EbdaData->cdemu.buffer_segment,0x0000);
3677 nbsectors=buffer[0x27]*0x100+buffer[0x26];
3678 write_word(ebda_seg,&EbdaData->cdemu.sector_count,nbsectors);
3680 lba=buffer[0x2B]*0x1000000+buffer[0x2A]*0x10000+buffer[0x29]*0x100+buffer[0x28];
3681 write_dword(ebda_seg,&EbdaData->cdemu.ilba,lba);
3683 // And we read the image in memory
3684 memsetb(get_SS(),atacmd,0,12);
3685 atacmd[0]=0x28; // READ command
3686 atacmd[7]=((1+(nbsectors-1)/4) & 0xff00) >> 8; // Sectors
3687 atacmd[8]=((1+(nbsectors-1)/4) & 0x00ff); // Sectors
3688 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3689 atacmd[3]=(lba & 0x00ff0000) >> 16;
3690 atacmd[4]=(lba & 0x0000ff00) >> 8;
3691 atacmd[5]=(lba & 0x000000ff);
3692 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, nbsectors*512L, ATA_DATA_IN, boot_segment,0)) != 0)
3693 return 12;
3695 // Remember the media type
3696 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
3697 case 0x01: // 1.2M floppy
3698 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,15);
3699 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3700 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3701 break;
3702 case 0x02: // 1.44M floppy
3703 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,18);
3704 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3705 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3706 break;
3707 case 0x03: // 2.88M floppy
3708 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,36);
3709 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3710 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3711 break;
3712 case 0x04: // Harddrive
3713 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,read_byte(boot_segment,446+6)&0x3f);
3714 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,
3715 (read_byte(boot_segment,446+6)<<2) + read_byte(boot_segment,446+7) + 1);
3716 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,read_byte(boot_segment,446+5) + 1);
3717 break;
3720 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0) {
3721 // Increase bios installed hardware number of devices
3722 if(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)==0x00)
3723 write_byte(0x40,0x10,read_byte(0x40,0x10)|0x41);
3724 else
3725 write_byte(ebda_seg, &EbdaData->ata.hdcount, read_byte(ebda_seg, &EbdaData->ata.hdcount) + 1);
3728 // everything is ok, so from now on, the emulation is active
3729 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0)
3730 write_byte(ebda_seg,&EbdaData->cdemu.active,0x01);
3732 // return the boot drive + no error
3733 return (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)*0x100)+0;
3736 // ---------------------------------------------------------------------------
3737 // End of El-Torito boot functions
3738 // ---------------------------------------------------------------------------
3739 #endif // BX_ELTORITO_BOOT
3741 void 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 counter;
3749 ASM_START
3751 ASM_END
3753 addr = read_word(0x0040, (regs.u.r16.dx << 1));
3754 counter = 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) && (counter)) {
3775 val16 = read_word(0x0040, 0x006C);
3776 if (val16 != timer) {
3777 timer = val16;
3778 counter--;
3781 if (counter > 0) {
3782 outb(addr, regs.u.r8.al);
3783 regs.u.r8.ah = inb(addr+5);
3784 } else {
3785 regs.u.r8.ah = 0x80;
3787 ClearCF(iret_addr.flags);
3788 break;
3789 case 2:
3790 timer = read_word(0x0040, 0x006C);
3791 while (((inb(addr+5) & 0x01) == 0) && (counter)) {
3792 val16 = read_word(0x0040, 0x006C);
3793 if (val16 != timer) {
3794 timer = val16;
3795 counter--;
3798 if (counter > 0) {
3799 regs.u.r8.ah = inb(addr+5);
3800 regs.u.r8.al = inb(addr);
3801 } else {
3802 regs.u.r8.ah = 0x80;
3804 ClearCF(iret_addr.flags);
3805 break;
3806 case 3:
3807 regs.u.r8.ah = inb(addr+5);
3808 regs.u.r8.al = inb(addr+6);
3809 ClearCF(iret_addr.flags);
3810 break;
3811 default:
3812 SetCF(iret_addr.flags); // Unsupported
3814 } else {
3815 SetCF(iret_addr.flags); // Unsupported
3819 void
3820 int15_function(regs, ES, DS, FLAGS)
3821 pusha_regs_t regs; // REGS pushed via pusha
3822 Bit16u ES, DS, FLAGS;
3824 Bit16u ebda_seg=read_word(0x0040,0x000E);
3825 bx_bool prev_a20_enable;
3826 Bit16u base15_00;
3827 Bit8u base23_16;
3828 Bit16u ss;
3829 Bit16u BX,CX,DX;
3831 Bit16u bRegister;
3832 Bit8u irqDisable;
3834 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3836 switch (regs.u.r8.ah) {
3837 case 0x24: /* A20 Control */
3838 switch (regs.u.r8.al) {
3839 case 0x00:
3840 set_enable_a20(0);
3841 CLEAR_CF();
3842 regs.u.r8.ah = 0;
3843 break;
3844 case 0x01:
3845 set_enable_a20(1);
3846 CLEAR_CF();
3847 regs.u.r8.ah = 0;
3848 break;
3849 case 0x02:
3850 regs.u.r8.al = (inb(0x92) >> 1) & 0x01;
3851 CLEAR_CF();
3852 regs.u.r8.ah = 0;
3853 break;
3854 case 0x03:
3855 CLEAR_CF();
3856 regs.u.r8.ah = 0;
3857 regs.u.r16.bx = 3;
3858 break;
3859 default:
3860 BX_INFO("int15: Func 24h, subfunc %02xh, A20 gate control not supported\n", (unsigned) regs.u.r8.al);
3861 SET_CF();
3862 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3864 break;
3866 case 0x41:
3867 SET_CF();
3868 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3869 break;
3871 case 0x4f:
3872 /* keyboard intercept */
3873 #if BX_CPU < 2
3874 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3875 #else
3876 // nop
3877 #endif
3878 SET_CF();
3879 break;
3881 case 0x52: // removable media eject
3882 CLEAR_CF();
3883 regs.u.r8.ah = 0; // "ok ejection may proceed"
3884 break;
3886 case 0x83: {
3887 if( regs.u.r8.al == 0 ) {
3888 // Set Interval requested.
3889 if( ( read_byte( 0x40, 0xA0 ) & 1 ) == 0 ) {
3890 // Interval not already set.
3891 write_byte( 0x40, 0xA0, 1 ); // Set status byte.
3892 write_word( 0x40, 0x98, ES ); // Byte location, segment
3893 write_word( 0x40, 0x9A, regs.u.r16.bx ); // Byte location, offset
3894 write_word( 0x40, 0x9C, regs.u.r16.dx ); // Low word, delay
3895 write_word( 0x40, 0x9E, regs.u.r16.cx ); // High word, delay.
3896 CLEAR_CF( );
3897 irqDisable = inb( 0xA1 );
3898 outb( 0xA1, irqDisable & 0xFE );
3899 bRegister = inb_cmos( 0xB ); // Unmask IRQ8 so INT70 will get through.
3900 outb_cmos( 0xB, bRegister | 0x40 ); // Turn on the Periodic Interrupt timer
3901 } else {
3902 // Interval already set.
3903 BX_DEBUG_INT15("int15: Func 83h, failed, already waiting.\n" );
3904 SET_CF();
3905 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3907 } else if( regs.u.r8.al == 1 ) {
3908 // Clear Interval requested
3909 write_byte( 0x40, 0xA0, 0 ); // Clear status byte
3910 CLEAR_CF( );
3911 bRegister = inb_cmos( 0xB );
3912 outb_cmos( 0xB, bRegister & ~0x40 ); // Turn off the Periodic Interrupt timer
3913 } else {
3914 BX_DEBUG_INT15("int15: Func 83h, failed.\n" );
3915 SET_CF();
3916 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3917 regs.u.r8.al--;
3920 break;
3923 case 0x87:
3924 #if BX_CPU < 3
3925 # error "Int15 function 87h not supported on < 80386"
3926 #endif
3927 // +++ should probably have descriptor checks
3928 // +++ should have exception handlers
3930 // turn off interrupts
3931 ASM_START
3933 ASM_END
3935 prev_a20_enable = set_enable_a20(1); // enable A20 line
3937 // 128K max of transfer on 386+ ???
3938 // source == destination ???
3940 // ES:SI points to descriptor table
3941 // offset use initially comments
3942 // ==============================================
3943 // 00..07 Unused zeros Null descriptor
3944 // 08..0f GDT zeros filled in by BIOS
3945 // 10..17 source ssssssss source of data
3946 // 18..1f dest dddddddd destination of data
3947 // 20..27 CS zeros filled in by BIOS
3948 // 28..2f SS zeros filled in by BIOS
3950 //es:si
3951 //eeee0
3952 //0ssss
3953 //-----
3955 // check for access rights of source & dest here
3957 // Initialize GDT descriptor
3958 base15_00 = (ES << 4) + regs.u.r16.si;
3959 base23_16 = ES >> 12;
3960 if (base15_00 < (ES<<4))
3961 base23_16++;
3962 write_word(ES, regs.u.r16.si+0x08+0, 47); // limit 15:00 = 6 * 8bytes/descriptor
3963 write_word(ES, regs.u.r16.si+0x08+2, base15_00);// base 15:00
3964 write_byte(ES, regs.u.r16.si+0x08+4, base23_16);// base 23:16
3965 write_byte(ES, regs.u.r16.si+0x08+5, 0x93); // access
3966 write_word(ES, regs.u.r16.si+0x08+6, 0x0000); // base 31:24/reserved/limit 19:16
3968 // Initialize CS descriptor
3969 write_word(ES, regs.u.r16.si+0x20+0, 0xffff);// limit 15:00 = normal 64K limit
3970 write_word(ES, regs.u.r16.si+0x20+2, 0x0000);// base 15:00
3971 write_byte(ES, regs.u.r16.si+0x20+4, 0x000f);// base 23:16
3972 write_byte(ES, regs.u.r16.si+0x20+5, 0x9b); // access
3973 write_word(ES, regs.u.r16.si+0x20+6, 0x0000);// base 31:24/reserved/limit 19:16
3975 // Initialize SS descriptor
3976 ss = get_SS();
3977 base15_00 = ss << 4;
3978 base23_16 = ss >> 12;
3979 write_word(ES, regs.u.r16.si+0x28+0, 0xffff); // limit 15:00 = normal 64K limit
3980 write_word(ES, regs.u.r16.si+0x28+2, base15_00);// base 15:00
3981 write_byte(ES, regs.u.r16.si+0x28+4, base23_16);// base 23:16
3982 write_byte(ES, regs.u.r16.si+0x28+5, 0x93); // access
3983 write_word(ES, regs.u.r16.si+0x28+6, 0x0000); // base 31:24/reserved/limit 19:16
3985 CX = regs.u.r16.cx;
3986 ASM_START
3987 // Compile generates locals offset info relative to SP.
3988 // Get CX (word count) from stack.
3989 mov bx, sp
3990 SEG SS
3991 mov cx, _int15_function.CX [bx]
3993 // since we need to set SS:SP, save them to the BDA
3994 // for future restore
3995 push eax
3996 xor eax, eax
3997 mov ds, ax
3998 mov 0x0469, ss
3999 mov 0x0467, sp
4001 SEG ES
4002 lgdt [si + 0x08]
4003 SEG CS
4004 lidt [pmode_IDT_info]
4005 ;; perhaps do something with IDT here
4007 ;; set PE bit in CR0
4008 mov eax, cr0
4009 or al, #0x01
4010 mov cr0, eax
4011 ;; far jump to flush CPU queue after transition to protected mode
4012 JMP_AP(0x0020, protected_mode)
4014 protected_mode:
4015 ;; GDT points to valid descriptor table, now load SS, DS, ES
4016 mov ax, #0x28 ;; 101 000 = 5th descriptor in table, TI=GDT, RPL=00
4017 mov ss, ax
4018 mov ax, #0x10 ;; 010 000 = 2nd descriptor in table, TI=GDT, RPL=00
4019 mov ds, ax
4020 mov ax, #0x18 ;; 011 000 = 3rd descriptor in table, TI=GDT, RPL=00
4021 mov es, ax
4022 xor si, si
4023 xor di, di
4026 movsw ;; move CX words from DS:SI to ES:DI
4028 ;; make sure DS and ES limits are 64KB
4029 mov ax, #0x28
4030 mov ds, ax
4031 mov es, ax
4033 ;; reset PG bit in CR0 ???
4034 mov eax, cr0
4035 and al, #0xFE
4036 mov cr0, eax
4038 ;; far jump to flush CPU queue after transition to real mode
4039 JMP_AP(0xf000, real_mode)
4041 real_mode:
4042 ;; restore IDT to normal real-mode defaults
4043 SEG CS
4044 lidt [rmode_IDT_info]
4046 // restore SS:SP from the BDA
4047 xor ax, ax
4048 mov ds, ax
4049 mov ss, 0x0469
4050 mov sp, 0x0467
4051 pop eax
4052 ASM_END
4054 set_enable_a20(prev_a20_enable);
4056 // turn back on interrupts
4057 ASM_START
4059 ASM_END
4061 regs.u.r8.ah = 0;
4062 CLEAR_CF();
4063 break;
4066 case 0x88:
4067 // Get the amount of extended memory (above 1M)
4068 #if BX_CPU < 2
4069 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4070 SET_CF();
4071 #else
4072 regs.u.r8.al = inb_cmos(0x30);
4073 regs.u.r8.ah = inb_cmos(0x31);
4075 // According to Ralf Brown's interrupt the limit should be 15M,
4076 // but real machines mostly return max. 63M.
4077 if(regs.u.r16.ax > 0xffc0)
4078 regs.u.r16.ax = 0xffc0;
4080 CLEAR_CF();
4081 #endif
4082 break;
4084 case 0x89:
4085 // Switch to Protected Mode.
4086 // ES:DI points to user-supplied GDT
4087 // BH/BL contains starting interrupt numbers for PIC0/PIC1
4088 // This subfunction does not return!
4090 // turn off interrupts
4091 ASM_START
4093 ASM_END
4095 set_enable_a20(1); // enable A20 line; we're supposed to fail if that fails
4097 // Initialize CS descriptor for BIOS
4098 write_word(ES, regs.u.r16.si+0x38+0, 0xffff);// limit 15:00 = normal 64K limit
4099 write_word(ES, regs.u.r16.si+0x38+2, 0x0000);// base 15:00
4100 write_byte(ES, regs.u.r16.si+0x38+4, 0x000f);// base 23:16 (hardcoded to f000:0000)
4101 write_byte(ES, regs.u.r16.si+0x38+5, 0x9b); // access
4102 write_word(ES, regs.u.r16.si+0x38+6, 0x0000);// base 31:24/reserved/limit 19:16
4104 BX = regs.u.r16.bx;
4105 ASM_START
4106 // Compiler generates locals offset info relative to SP.
4107 // Get BX (PIC offsets) from stack.
4108 mov bx, sp
4109 SEG SS
4110 mov bx, _int15_function.BX [bx]
4112 // Program PICs
4113 mov al, #0x11 ; send initialisation commands
4114 out 0x20, al
4115 out 0xa0, al
4116 mov al, bh
4117 out 0x21, al
4118 mov al, bl
4119 out 0xa1, al
4120 mov al, #0x04
4121 out 0x21, al
4122 mov al, #0x02
4123 out 0xa1, al
4124 mov al, #0x01
4125 out 0x21, al
4126 out 0xa1, al
4127 mov al, #0xff ; mask all IRQs, user must re-enable
4128 out 0x21, al
4129 out 0xa1, al
4131 // Load GDT and IDT from supplied data
4132 SEG ES
4133 lgdt [si + 0x08]
4134 SEG ES
4135 lidt [si + 0x10]
4137 // set PE bit in CR0
4138 mov eax, cr0
4139 or al, #0x01
4140 mov cr0, eax
4141 // far jump to flush CPU queue after transition to protected mode
4142 JMP_AP(0x0038, protmode_switch)
4144 protmode_switch:
4145 ;; GDT points to valid descriptor table, now load SS, DS, ES
4146 mov ax, #0x28
4147 mov ss, ax
4148 mov ax, #0x18
4149 mov ds, ax
4150 mov ax, #0x20
4151 mov es, ax
4153 // unwind the stack - this will break if calling sequence changes!
4154 mov sp,bp
4155 add sp,#4 ; skip return address
4156 popa ; restore regs
4157 pop ax ; skip saved es
4158 pop ax ; skip saved ds
4159 pop ax ; skip saved flags
4161 // return to caller - note that we do not use IRET because
4162 // we cannot enable interrupts
4163 pop cx ; get return offset
4164 pop ax ; skip return segment
4165 pop ax ; skip flags
4166 mov ax, #0x30 ; ah must be 0 on successful exit
4167 push ax
4168 push cx ; re-create modified ret address on stack
4169 retf
4171 ASM_END
4173 break;
4175 case 0x90:
4176 /* Device busy interrupt. Called by Int 16h when no key available */
4177 break;
4179 case 0x91:
4180 /* Interrupt complete. Called by Int 16h when key becomes available */
4181 break;
4183 case 0xbf:
4184 BX_INFO("*** int 15h function AH=bf not yet supported!\n");
4185 SET_CF();
4186 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4187 break;
4189 case 0xC0:
4190 #if 0
4191 SET_CF();
4192 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4193 break;
4194 #endif
4195 CLEAR_CF();
4196 regs.u.r8.ah = 0;
4197 regs.u.r16.bx = BIOS_CONFIG_TABLE;
4198 ES = 0xF000;
4199 break;
4201 case 0xc1:
4202 ES = ebda_seg;
4203 CLEAR_CF();
4204 break;
4206 case 0xd8:
4207 bios_printf(BIOS_PRINTF_DEBUG, "EISA BIOS not present\n");
4208 SET_CF();
4209 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4210 break;
4212 default:
4213 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4214 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4215 SET_CF();
4216 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4217 break;
4221 #if BX_USE_PS2_MOUSE
4222 void
4223 int15_function_mouse(regs, ES, DS, FLAGS)
4224 pusha_regs_t regs; // REGS pushed via pusha
4225 Bit16u ES, DS, FLAGS;
4227 Bit16u ebda_seg=read_word(0x0040,0x000E);
4228 Bit8u mouse_flags_1, mouse_flags_2;
4229 Bit16u mouse_driver_seg;
4230 Bit16u mouse_driver_offset;
4231 Bit8u comm_byte, prev_command_byte;
4232 Bit8u ret, mouse_data1, mouse_data2, mouse_data3;
4234 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
4236 switch (regs.u.r8.ah) {
4237 case 0xC2:
4238 // Return Codes status in AH
4239 // =========================
4240 // 00: success
4241 // 01: invalid subfunction (AL > 7)
4242 // 02: invalid input value (out of allowable range)
4243 // 03: interface error
4244 // 04: resend command received from mouse controller,
4245 // device driver should attempt command again
4246 // 05: cannot enable mouse, since no far call has been installed
4247 // 80/86: mouse service not implemented
4249 switch (regs.u.r8.al) {
4250 case 0: // Disable/Enable Mouse
4251 BX_DEBUG_INT15("case 0:\n");
4252 switch (regs.u.r8.bh) {
4253 case 0: // Disable Mouse
4254 BX_DEBUG_INT15("case 0: disable mouse\n");
4255 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4256 ret = send_to_mouse_ctrl(0xF5); // disable mouse command
4257 if (ret == 0) {
4258 ret = get_mouse_data(&mouse_data1);
4259 if ( (ret == 0) || (mouse_data1 == 0xFA) ) {
4260 CLEAR_CF();
4261 regs.u.r8.ah = 0;
4262 return;
4266 // error
4267 SET_CF();
4268 regs.u.r8.ah = ret;
4269 return;
4270 break;
4272 case 1: // Enable Mouse
4273 BX_DEBUG_INT15("case 1: enable mouse\n");
4274 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4275 if ( (mouse_flags_2 & 0x80) == 0 ) {
4276 BX_DEBUG_INT15("INT 15h C2 Enable Mouse, no far call handler\n");
4277 SET_CF(); // error
4278 regs.u.r8.ah = 5; // no far call installed
4279 return;
4281 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4282 ret = send_to_mouse_ctrl(0xF4); // enable mouse command
4283 if (ret == 0) {
4284 ret = get_mouse_data(&mouse_data1);
4285 if ( (ret == 0) && (mouse_data1 == 0xFA) ) {
4286 enable_mouse_int_and_events(); // turn IRQ12 and packet generation on
4287 CLEAR_CF();
4288 regs.u.r8.ah = 0;
4289 return;
4292 SET_CF();
4293 regs.u.r8.ah = ret;
4294 return;
4296 default: // invalid subfunction
4297 BX_DEBUG_INT15("INT 15h C2 AL=0, BH=%02x\n", (unsigned) regs.u.r8.bh);
4298 SET_CF(); // error
4299 regs.u.r8.ah = 1; // invalid subfunction
4300 return;
4302 break;
4304 case 1: // Reset Mouse
4305 case 5: // Initialize Mouse
4306 BX_DEBUG_INT15("case 1 or 5:\n");
4307 if (regs.u.r8.al == 5) {
4308 if (regs.u.r8.bh != 3) {
4309 SET_CF();
4310 regs.u.r8.ah = 0x02; // invalid input
4311 return;
4313 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4314 mouse_flags_2 = (mouse_flags_2 & 0x00) | regs.u.r8.bh;
4315 mouse_flags_1 = 0x00;
4316 write_byte(ebda_seg, 0x0026, mouse_flags_1);
4317 write_byte(ebda_seg, 0x0027, mouse_flags_2);
4320 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4321 ret = send_to_mouse_ctrl(0xFF); // reset mouse command
4322 if (ret == 0) {
4323 ret = get_mouse_data(&mouse_data3);
4324 // if no mouse attached, it will return RESEND
4325 if (mouse_data3 == 0xfe) {
4326 SET_CF();
4327 return;
4329 if (mouse_data3 != 0xfa)
4330 BX_PANIC("Mouse reset returned %02x (should be ack)\n", (unsigned)mouse_data3);
4331 if ( ret == 0 ) {
4332 ret = get_mouse_data(&mouse_data1);
4333 if ( ret == 0 ) {
4334 ret = get_mouse_data(&mouse_data2);
4335 if ( ret == 0 ) {
4336 // turn IRQ12 and packet generation on
4337 enable_mouse_int_and_events();
4338 CLEAR_CF();
4339 regs.u.r8.ah = 0;
4340 regs.u.r8.bl = mouse_data1;
4341 regs.u.r8.bh = mouse_data2;
4342 return;
4348 // error
4349 SET_CF();
4350 regs.u.r8.ah = ret;
4351 return;
4353 case 2: // Set Sample Rate
4354 BX_DEBUG_INT15("case 2:\n");
4355 switch (regs.u.r8.bh) {
4356 case 0: mouse_data1 = 10; break; // 10 reports/sec
4357 case 1: mouse_data1 = 20; break; // 20 reports/sec
4358 case 2: mouse_data1 = 40; break; // 40 reports/sec
4359 case 3: mouse_data1 = 60; break; // 60 reports/sec
4360 case 4: mouse_data1 = 80; break; // 80 reports/sec
4361 case 5: mouse_data1 = 100; break; // 100 reports/sec (default)
4362 case 6: mouse_data1 = 200; break; // 200 reports/sec
4363 default: mouse_data1 = 0;
4365 if (mouse_data1 > 0) {
4366 ret = send_to_mouse_ctrl(0xF3); // set sample rate command
4367 if (ret == 0) {
4368 ret = get_mouse_data(&mouse_data2);
4369 ret = send_to_mouse_ctrl(mouse_data1);
4370 ret = get_mouse_data(&mouse_data2);
4371 CLEAR_CF();
4372 regs.u.r8.ah = 0;
4373 } else {
4374 // error
4375 SET_CF();
4376 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4378 } else {
4379 // error
4380 SET_CF();
4381 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4383 break;
4385 case 3: // Set Resolution
4386 BX_DEBUG_INT15("case 3:\n");
4387 // BH:
4388 // 0 = 25 dpi, 1 count per millimeter
4389 // 1 = 50 dpi, 2 counts per millimeter
4390 // 2 = 100 dpi, 4 counts per millimeter
4391 // 3 = 200 dpi, 8 counts per millimeter
4392 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4393 if (regs.u.r8.bh < 4) {
4394 ret = send_to_mouse_ctrl(0xE8); // set resolution command
4395 if (ret == 0) {
4396 ret = get_mouse_data(&mouse_data1);
4397 if (mouse_data1 != 0xfa)
4398 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
4399 ret = send_to_mouse_ctrl(regs.u.r8.bh);
4400 ret = get_mouse_data(&mouse_data1);
4401 if (mouse_data1 != 0xfa)
4402 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
4403 CLEAR_CF();
4404 regs.u.r8.ah = 0;
4405 } else {
4406 // error
4407 SET_CF();
4408 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4410 } else {
4411 // error
4412 SET_CF();
4413 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4415 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4416 break;
4418 case 4: // Get Device ID
4419 BX_DEBUG_INT15("case 4:\n");
4420 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4421 ret = send_to_mouse_ctrl(0xF2); // get mouse ID command
4422 if (ret == 0) {
4423 ret = get_mouse_data(&mouse_data1);
4424 ret = get_mouse_data(&mouse_data2);
4425 CLEAR_CF();
4426 regs.u.r8.ah = 0;
4427 regs.u.r8.bh = mouse_data2;
4428 } else {
4429 // error
4430 SET_CF();
4431 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4433 break;
4435 case 6: // Return Status & Set Scaling Factor...
4436 BX_DEBUG_INT15("case 6:\n");
4437 switch (regs.u.r8.bh) {
4438 case 0: // Return Status
4439 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4440 ret = send_to_mouse_ctrl(0xE9); // get mouse info command
4441 if (ret == 0) {
4442 ret = get_mouse_data(&mouse_data1);
4443 if (mouse_data1 != 0xfa)
4444 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
4445 if (ret == 0) {
4446 ret = get_mouse_data(&mouse_data1);
4447 if (ret == 0) {
4448 ret = get_mouse_data(&mouse_data2);
4449 if (ret == 0) {
4450 ret = get_mouse_data(&mouse_data3);
4451 if (ret == 0) {
4452 CLEAR_CF();
4453 regs.u.r8.ah = 0;
4454 regs.u.r8.bl = mouse_data1;
4455 regs.u.r8.cl = mouse_data2;
4456 regs.u.r8.dl = mouse_data3;
4457 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4458 return;
4465 // error
4466 SET_CF();
4467 regs.u.r8.ah = ret;
4468 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4469 return;
4471 case 1: // Set Scaling Factor to 1:1
4472 case 2: // Set Scaling Factor to 2:1
4473 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4474 if (regs.u.r8.bh == 1) {
4475 ret = send_to_mouse_ctrl(0xE6);
4476 } else {
4477 ret = send_to_mouse_ctrl(0xE7);
4479 if (ret == 0) {
4480 get_mouse_data(&mouse_data1);
4481 ret = (mouse_data1 != 0xFA);
4483 if (ret == 0) {
4484 CLEAR_CF();
4485 regs.u.r8.ah = 0;
4486 } else {
4487 // error
4488 SET_CF();
4489 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4491 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4492 break;
4494 default:
4495 BX_PANIC("INT 15h C2 AL=6, BH=%02x\n", (unsigned) regs.u.r8.bh);
4497 break;
4499 case 7: // Set Mouse Handler Address
4500 BX_DEBUG_INT15("case 7:\n");
4501 mouse_driver_seg = ES;
4502 mouse_driver_offset = regs.u.r16.bx;
4503 write_word(ebda_seg, 0x0022, mouse_driver_offset);
4504 write_word(ebda_seg, 0x0024, mouse_driver_seg);
4505 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4506 if (mouse_driver_offset == 0 && mouse_driver_seg == 0) {
4507 /* remove handler */
4508 if ( (mouse_flags_2 & 0x80) != 0 ) {
4509 mouse_flags_2 &= ~0x80;
4510 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4513 else {
4514 /* install handler */
4515 mouse_flags_2 |= 0x80;
4517 write_byte(ebda_seg, 0x0027, mouse_flags_2);
4518 CLEAR_CF();
4519 regs.u.r8.ah = 0;
4520 break;
4522 default:
4523 BX_DEBUG_INT15("case default:\n");
4524 regs.u.r8.ah = 1; // invalid function
4525 SET_CF();
4527 break;
4529 default:
4530 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4531 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4532 SET_CF();
4533 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4534 break;
4537 #endif // BX_USE_PS2_MOUSE
4540 void set_e820_range(ES, DI, start, end, extra_start, extra_end, type)
4541 Bit16u ES;
4542 Bit16u DI;
4543 Bit32u start;
4544 Bit32u end;
4545 Bit8u extra_start;
4546 Bit8u extra_end;
4547 Bit16u type;
4549 write_word(ES, DI, start);
4550 write_word(ES, DI+2, start >> 16);
4551 write_word(ES, DI+4, extra_start);
4552 write_word(ES, DI+6, 0x00);
4554 end -= start;
4555 extra_end -= extra_start;
4556 write_word(ES, DI+8, end);
4557 write_word(ES, DI+10, end >> 16);
4558 write_word(ES, DI+12, extra_end);
4559 write_word(ES, DI+14, 0x0000);
4561 write_word(ES, DI+16, type);
4562 write_word(ES, DI+18, 0x0);
4565 void
4566 int15_function32(regs, ES, DS, FLAGS)
4567 pushad_regs_t regs; // REGS pushed via pushad
4568 Bit16u ES, DS, FLAGS;
4570 Bit32u extended_memory_size=0; // 64bits long
4571 Bit32u extra_lowbits_memory_size=0;
4572 Bit16u CX,DX;
4573 Bit8u extra_highbits_memory_size=0;
4575 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
4577 switch (regs.u.r8.ah) {
4578 case 0x86:
4579 // Wait for CX:DX microseconds. currently using the
4580 // refresh request port 0x61 bit4, toggling every 15usec
4582 CX = regs.u.r16.cx;
4583 DX = regs.u.r16.dx;
4585 ASM_START
4588 ;; Get the count in eax
4589 mov bx, sp
4590 SEG SS
4591 mov ax, _int15_function32.CX [bx]
4592 shl eax, #16
4593 SEG SS
4594 mov ax, _int15_function32.DX [bx]
4596 ;; convert to numbers of 15usec ticks
4597 mov ebx, #15
4598 xor edx, edx
4599 div eax, ebx
4600 mov ecx, eax
4602 ;; wait for ecx number of refresh requests
4603 in al, #0x61
4604 and al,#0x10
4605 mov ah, al
4607 or ecx, ecx
4608 je int1586_tick_end
4609 int1586_tick:
4610 in al, #0x61
4611 and al,#0x10
4612 cmp al, ah
4613 je int1586_tick
4614 mov ah, al
4615 dec ecx
4616 jnz int1586_tick
4617 int1586_tick_end:
4618 ASM_END
4620 break;
4622 case 0xe8:
4623 switch(regs.u.r8.al) {
4624 case 0x20: // coded by osmaker aka K.J.
4625 if(regs.u.r32.edx == 0x534D4150)
4627 extended_memory_size = inb_cmos(0x35);
4628 extended_memory_size <<= 8;
4629 extended_memory_size |= inb_cmos(0x34);
4630 extended_memory_size *= 64;
4631 // greater than EFF00000???
4632 if(extended_memory_size > 0x3bc000) {
4633 extended_memory_size = 0x3bc000; // everything after this is reserved memory until we get to 0x100000000
4635 extended_memory_size *= 1024;
4636 extended_memory_size += (16L * 1024 * 1024);
4638 if(extended_memory_size <= (16L * 1024 * 1024)) {
4639 extended_memory_size = inb_cmos(0x31);
4640 extended_memory_size <<= 8;
4641 extended_memory_size |= inb_cmos(0x30);
4642 extended_memory_size *= 1024;
4643 extended_memory_size += (1L * 1024 * 1024);
4646 extra_lowbits_memory_size = inb_cmos(0x5c);
4647 extra_lowbits_memory_size <<= 8;
4648 extra_lowbits_memory_size |= inb_cmos(0x5b);
4649 extra_lowbits_memory_size *= 64;
4650 extra_lowbits_memory_size *= 1024;
4651 extra_highbits_memory_size = inb_cmos(0x5d);
4653 switch(regs.u.r16.bx)
4655 case 0:
4656 set_e820_range(ES, regs.u.r16.di,
4657 0x0000000L, 0x0009f000L, 0, 0, 1);
4658 regs.u.r32.ebx = 1;
4659 break;
4660 case 1:
4661 set_e820_range(ES, regs.u.r16.di,
4662 0x0009f000L, 0x000a0000L, 0, 0, 2);
4663 regs.u.r32.ebx = 2;
4664 break;
4665 case 2:
4666 set_e820_range(ES, regs.u.r16.di,
4667 0x000e8000L, 0x00100000L, 0, 0, 2);
4668 regs.u.r32.ebx = 3;
4669 break;
4670 case 3:
4671 #if BX_ROMBIOS32
4672 set_e820_range(ES, regs.u.r16.di,
4673 0x00100000L,
4674 extended_memory_size - ACPI_DATA_SIZE, 0, 0, 1);
4675 regs.u.r32.ebx = 4;
4676 #else
4677 set_e820_range(ES, regs.u.r16.di,
4678 0x00100000L,
4679 extended_memory_size, 0, 0, 1);
4680 regs.u.r32.ebx = 5;
4681 #endif
4682 break;
4683 case 4:
4684 set_e820_range(ES, regs.u.r16.di,
4685 extended_memory_size - ACPI_DATA_SIZE,
4686 extended_memory_size, 0, 0, 3); // ACPI RAM
4687 regs.u.r32.ebx = 5;
4688 break;
4689 case 5:
4690 /* 256KB BIOS area at the end of 4 GB */
4691 set_e820_range(ES, regs.u.r16.di,
4692 0xfffc0000L, 0x00000000L, 0, 0, 2);
4693 if (extra_highbits_memory_size || extra_lowbits_memory_size)
4694 regs.u.r32.ebx = 6;
4695 else
4696 regs.u.r32.ebx = 0;
4697 break;
4698 case 6:
4699 /* Maping of memory above 4 GB */
4700 set_e820_range(ES, regs.u.r16.di, 0x00000000L,
4701 extra_lowbits_memory_size, 1, extra_highbits_memory_size
4702 + 1, 1);
4703 regs.u.r32.ebx = 0;
4704 break;
4705 default: /* AX=E820, DX=534D4150, BX unrecognized */
4706 goto int15_unimplemented;
4707 break;
4709 regs.u.r32.eax = 0x534D4150;
4710 regs.u.r32.ecx = 0x14;
4711 CLEAR_CF();
4712 } else {
4713 // if DX != 0x534D4150)
4714 goto int15_unimplemented;
4716 break;
4718 case 0x01:
4719 // do we have any reason to fail here ?
4720 CLEAR_CF();
4722 // my real system sets ax and bx to 0
4723 // this is confirmed by Ralph Brown list
4724 // but syslinux v1.48 is known to behave
4725 // strangely if ax is set to 0
4726 // regs.u.r16.ax = 0;
4727 // regs.u.r16.bx = 0;
4729 // Get the amount of extended memory (above 1M)
4730 regs.u.r8.cl = inb_cmos(0x30);
4731 regs.u.r8.ch = inb_cmos(0x31);
4733 // limit to 15M
4734 if(regs.u.r16.cx > 0x3c00)
4736 regs.u.r16.cx = 0x3c00;
4739 // Get the amount of extended memory above 16M in 64k blocs
4740 regs.u.r8.dl = inb_cmos(0x34);
4741 regs.u.r8.dh = inb_cmos(0x35);
4743 // Set configured memory equal to extended memory
4744 regs.u.r16.ax = regs.u.r16.cx;
4745 regs.u.r16.bx = regs.u.r16.dx;
4746 break;
4747 default: /* AH=0xE8?? but not implemented */
4748 goto int15_unimplemented;
4750 break;
4751 int15_unimplemented:
4752 // fall into the default
4753 default:
4754 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4755 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4756 SET_CF();
4757 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4758 break;
4762 void
4763 int16_function(DI, SI, BP, SP, BX, DX, CX, AX, FLAGS)
4764 Bit16u DI, SI, BP, SP, BX, DX, CX, AX, FLAGS;
4766 Bit8u scan_code, ascii_code, shift_flags, led_flags, count;
4767 Bit16u kbd_code, max;
4769 BX_DEBUG_INT16("int16: AX=%04x BX=%04x CX=%04x DX=%04x \n", AX, BX, CX, DX);
4771 shift_flags = read_byte(0x0040, 0x17);
4772 led_flags = read_byte(0x0040, 0x97);
4773 if ((((shift_flags >> 4) & 0x07) ^ (led_flags & 0x07)) != 0) {
4774 ASM_START
4776 ASM_END
4777 outb(0x60, 0xed);
4778 while ((inb(0x64) & 0x01) == 0) outb(0x80, 0x21);
4779 if ((inb(0x60) == 0xfa)) {
4780 led_flags &= 0xf8;
4781 led_flags |= ((shift_flags >> 4) & 0x07);
4782 outb(0x60, led_flags & 0x07);
4783 while ((inb(0x64) & 0x01) == 0) outb(0x80, 0x21);
4784 inb(0x60);
4785 write_byte(0x0040, 0x97, led_flags);
4787 ASM_START
4789 ASM_END
4792 switch (GET_AH()) {
4793 case 0x00: /* read keyboard input */
4795 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4796 BX_PANIC("KBD: int16h: out of keyboard input\n");
4798 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4799 else if (ascii_code == 0xE0) ascii_code = 0;
4800 AX = (scan_code << 8) | ascii_code;
4801 break;
4803 case 0x01: /* check keyboard status */
4804 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4805 SET_ZF();
4806 return;
4808 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4809 else if (ascii_code == 0xE0) ascii_code = 0;
4810 AX = (scan_code << 8) | ascii_code;
4811 CLEAR_ZF();
4812 break;
4814 case 0x02: /* get shift flag status */
4815 shift_flags = read_byte(0x0040, 0x17);
4816 SET_AL(shift_flags);
4817 break;
4819 case 0x05: /* store key-stroke into buffer */
4820 if ( !enqueue_key(GET_CH(), GET_CL()) ) {
4821 SET_AL(1);
4823 else {
4824 SET_AL(0);
4826 break;
4828 case 0x09: /* GET KEYBOARD FUNCTIONALITY */
4829 // bit Bochs Description
4830 // 7 0 reserved
4831 // 6 0 INT 16/AH=20h-22h supported (122-key keyboard support)
4832 // 5 1 INT 16/AH=10h-12h supported (enhanced keyboard support)
4833 // 4 1 INT 16/AH=0Ah supported
4834 // 3 0 INT 16/AX=0306h supported
4835 // 2 0 INT 16/AX=0305h supported
4836 // 1 0 INT 16/AX=0304h supported
4837 // 0 0 INT 16/AX=0300h supported
4839 SET_AL(0x30);
4840 break;
4842 case 0x0A: /* GET KEYBOARD ID */
4843 count = 2;
4844 kbd_code = 0x0;
4845 outb(0x60, 0xf2);
4846 /* Wait for data */
4847 max=0xffff;
4848 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4849 if (max>0x0) {
4850 if ((inb(0x60) == 0xfa)) {
4851 do {
4852 max=0xffff;
4853 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4854 if (max>0x0) {
4855 kbd_code >>= 8;
4856 kbd_code |= (inb(0x60) << 8);
4858 } while (--count>0);
4861 BX=kbd_code;
4862 break;
4864 case 0x10: /* read MF-II keyboard input */
4866 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4867 BX_PANIC("KBD: int16h: out of keyboard input\n");
4869 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4870 AX = (scan_code << 8) | ascii_code;
4871 break;
4873 case 0x11: /* check MF-II keyboard status */
4874 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4875 SET_ZF();
4876 return;
4878 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4879 AX = (scan_code << 8) | ascii_code;
4880 CLEAR_ZF();
4881 break;
4883 case 0x12: /* get extended keyboard status */
4884 shift_flags = read_byte(0x0040, 0x17);
4885 SET_AL(shift_flags);
4886 shift_flags = read_byte(0x0040, 0x18) & 0x73;
4887 shift_flags |= read_byte(0x0040, 0x96) & 0x0c;
4888 SET_AH(shift_flags);
4889 BX_DEBUG_INT16("int16: func 12 sending %04x\n",AX);
4890 break;
4892 case 0x92: /* keyboard capability check called by DOS 5.0+ keyb */
4893 SET_AH(0x80); // function int16 ah=0x10-0x12 supported
4894 break;
4896 case 0xA2: /* 122 keys capability check called by DOS 5.0+ keyb */
4897 // don't change AH : function int16 ah=0x20-0x22 NOT supported
4898 break;
4900 case 0x6F:
4901 if (GET_AL() == 0x08)
4902 SET_AH(0x02); // unsupported, aka normal keyboard
4904 default:
4905 BX_INFO("KBD: unsupported int 16h function %02x\n", GET_AH());
4909 unsigned int
4910 dequeue_key(scan_code, ascii_code, incr)
4911 Bit8u *scan_code;
4912 Bit8u *ascii_code;
4913 unsigned int incr;
4915 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail;
4916 Bit16u ss;
4917 Bit8u acode, scode;
4919 #if BX_CPU < 2
4920 buffer_start = 0x001E;
4921 buffer_end = 0x003E;
4922 #else
4923 buffer_start = read_word(0x0040, 0x0080);
4924 buffer_end = read_word(0x0040, 0x0082);
4925 #endif
4927 buffer_head = read_word(0x0040, 0x001a);
4928 buffer_tail = read_word(0x0040, 0x001c);
4930 if (buffer_head != buffer_tail) {
4931 ss = get_SS();
4932 acode = read_byte(0x0040, buffer_head);
4933 scode = read_byte(0x0040, buffer_head+1);
4934 write_byte(ss, ascii_code, acode);
4935 write_byte(ss, scan_code, scode);
4937 if (incr) {
4938 buffer_head += 2;
4939 if (buffer_head >= buffer_end)
4940 buffer_head = buffer_start;
4941 write_word(0x0040, 0x001a, buffer_head);
4943 return(1);
4945 else {
4946 return(0);
4950 static char panic_msg_keyb_buffer_full[] = "%s: keyboard input buffer full\n";
4952 Bit8u
4953 inhibit_mouse_int_and_events()
4955 Bit8u command_byte, prev_command_byte;
4957 // Turn off IRQ generation and aux data line
4958 if ( inb(0x64) & 0x02 )
4959 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4960 outb(0x64, 0x20); // get command byte
4961 while ( (inb(0x64) & 0x01) != 0x01 );
4962 prev_command_byte = inb(0x60);
4963 command_byte = prev_command_byte;
4964 //while ( (inb(0x64) & 0x02) );
4965 if ( inb(0x64) & 0x02 )
4966 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4967 command_byte &= 0xfd; // turn off IRQ 12 generation
4968 command_byte |= 0x20; // disable mouse serial clock line
4969 outb(0x64, 0x60); // write command byte
4970 outb(0x60, command_byte);
4971 return(prev_command_byte);
4974 void
4975 enable_mouse_int_and_events()
4977 Bit8u command_byte;
4979 // Turn on IRQ generation and aux data line
4980 if ( inb(0x64) & 0x02 )
4981 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4982 outb(0x64, 0x20); // get command byte
4983 while ( (inb(0x64) & 0x01) != 0x01 );
4984 command_byte = inb(0x60);
4985 //while ( (inb(0x64) & 0x02) );
4986 if ( inb(0x64) & 0x02 )
4987 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4988 command_byte |= 0x02; // turn on IRQ 12 generation
4989 command_byte &= 0xdf; // enable mouse serial clock line
4990 outb(0x64, 0x60); // write command byte
4991 outb(0x60, command_byte);
4994 Bit8u
4995 send_to_mouse_ctrl(sendbyte)
4996 Bit8u sendbyte;
4998 Bit8u response;
5000 // wait for chance to write to ctrl
5001 if ( inb(0x64) & 0x02 )
5002 BX_PANIC(panic_msg_keyb_buffer_full,"sendmouse");
5003 outb(0x64, 0xD4);
5004 outb(0x60, sendbyte);
5005 return(0);
5009 Bit8u
5010 get_mouse_data(data)
5011 Bit8u *data;
5013 Bit8u response;
5014 Bit16u ss;
5016 while ((inb(0x64) & 0x21) != 0x21) { }
5018 response = inb(0x60);
5020 ss = get_SS();
5021 write_byte(ss, data, response);
5022 return(0);
5025 void
5026 set_kbd_command_byte(command_byte)
5027 Bit8u command_byte;
5029 if ( inb(0x64) & 0x02 )
5030 BX_PANIC(panic_msg_keyb_buffer_full,"setkbdcomm");
5031 outb(0x64, 0xD4);
5033 outb(0x64, 0x60); // write command byte
5034 outb(0x60, command_byte);
5037 void
5038 int09_function(DI, SI, BP, SP, BX, DX, CX, AX)
5039 Bit16u DI, SI, BP, SP, BX, DX, CX, AX;
5041 Bit8u scancode, asciicode, shift_flags;
5042 Bit8u mf2_flags, mf2_state;
5045 // DS has been set to F000 before call
5049 scancode = GET_AL();
5051 if (scancode == 0) {
5052 BX_INFO("KBD: int09 handler: AL=0\n");
5053 return;
5057 shift_flags = read_byte(0x0040, 0x17);
5058 mf2_flags = read_byte(0x0040, 0x18);
5059 mf2_state = read_byte(0x0040, 0x96);
5060 asciicode = 0;
5062 switch (scancode) {
5063 case 0x3a: /* Caps Lock press */
5064 shift_flags ^= 0x40;
5065 write_byte(0x0040, 0x17, shift_flags);
5066 mf2_flags |= 0x40;
5067 write_byte(0x0040, 0x18, mf2_flags);
5068 break;
5069 case 0xba: /* Caps Lock release */
5070 mf2_flags &= ~0x40;
5071 write_byte(0x0040, 0x18, mf2_flags);
5072 break;
5074 case 0x2a: /* L Shift press */
5075 shift_flags |= 0x02;
5076 write_byte(0x0040, 0x17, shift_flags);
5077 break;
5078 case 0xaa: /* L Shift release */
5079 shift_flags &= ~0x02;
5080 write_byte(0x0040, 0x17, shift_flags);
5081 break;
5083 case 0x36: /* R Shift press */
5084 shift_flags |= 0x01;
5085 write_byte(0x0040, 0x17, shift_flags);
5086 break;
5087 case 0xb6: /* R Shift release */
5088 shift_flags &= ~0x01;
5089 write_byte(0x0040, 0x17, shift_flags);
5090 break;
5092 case 0x1d: /* Ctrl press */
5093 if ((mf2_state & 0x01) == 0) {
5094 shift_flags |= 0x04;
5095 write_byte(0x0040, 0x17, shift_flags);
5096 if (mf2_state & 0x02) {
5097 mf2_state |= 0x04;
5098 write_byte(0x0040, 0x96, mf2_state);
5099 } else {
5100 mf2_flags |= 0x01;
5101 write_byte(0x0040, 0x18, mf2_flags);
5104 break;
5105 case 0x9d: /* Ctrl release */
5106 if ((mf2_state & 0x01) == 0) {
5107 shift_flags &= ~0x04;
5108 write_byte(0x0040, 0x17, shift_flags);
5109 if (mf2_state & 0x02) {
5110 mf2_state &= ~0x04;
5111 write_byte(0x0040, 0x96, mf2_state);
5112 } else {
5113 mf2_flags &= ~0x01;
5114 write_byte(0x0040, 0x18, mf2_flags);
5117 break;
5119 case 0x38: /* Alt press */
5120 shift_flags |= 0x08;
5121 write_byte(0x0040, 0x17, shift_flags);
5122 if (mf2_state & 0x02) {
5123 mf2_state |= 0x08;
5124 write_byte(0x0040, 0x96, mf2_state);
5125 } else {
5126 mf2_flags |= 0x02;
5127 write_byte(0x0040, 0x18, mf2_flags);
5129 break;
5130 case 0xb8: /* Alt release */
5131 shift_flags &= ~0x08;
5132 write_byte(0x0040, 0x17, shift_flags);
5133 if (mf2_state & 0x02) {
5134 mf2_state &= ~0x08;
5135 write_byte(0x0040, 0x96, mf2_state);
5136 } else {
5137 mf2_flags &= ~0x02;
5138 write_byte(0x0040, 0x18, mf2_flags);
5140 break;
5142 case 0x45: /* Num Lock press */
5143 if ((mf2_state & 0x03) == 0) {
5144 mf2_flags |= 0x20;
5145 write_byte(0x0040, 0x18, mf2_flags);
5146 shift_flags ^= 0x20;
5147 write_byte(0x0040, 0x17, shift_flags);
5149 break;
5150 case 0xc5: /* Num Lock release */
5151 if ((mf2_state & 0x03) == 0) {
5152 mf2_flags &= ~0x20;
5153 write_byte(0x0040, 0x18, mf2_flags);
5155 break;
5157 case 0x46: /* Scroll Lock press */
5158 mf2_flags |= 0x10;
5159 write_byte(0x0040, 0x18, mf2_flags);
5160 shift_flags ^= 0x10;
5161 write_byte(0x0040, 0x17, shift_flags);
5162 break;
5164 case 0xc6: /* Scroll Lock release */
5165 mf2_flags &= ~0x10;
5166 write_byte(0x0040, 0x18, mf2_flags);
5167 break;
5169 default:
5170 if (scancode & 0x80) {
5171 break; /* toss key releases ... */
5173 if (scancode > MAX_SCAN_CODE) {
5174 BX_INFO("KBD: int09h_handler(): unknown scancode read: 0x%02x!\n", scancode);
5175 return;
5177 if (shift_flags & 0x08) { /* ALT */
5178 asciicode = scan_to_scanascii[scancode].alt;
5179 scancode = scan_to_scanascii[scancode].alt >> 8;
5180 } else if (shift_flags & 0x04) { /* CONTROL */
5181 asciicode = scan_to_scanascii[scancode].control;
5182 scancode = scan_to_scanascii[scancode].control >> 8;
5183 } else if (((mf2_state & 0x02) > 0) && ((scancode >= 0x47) && (scancode <= 0x53))) {
5184 /* extended keys handling */
5185 asciicode = 0xe0;
5186 scancode = scan_to_scanascii[scancode].normal >> 8;
5187 } else if (shift_flags & 0x03) { /* LSHIFT + RSHIFT */
5188 /* check if lock state should be ignored
5189 * because a SHIFT key are pressed */
5191 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
5192 asciicode = scan_to_scanascii[scancode].normal;
5193 scancode = scan_to_scanascii[scancode].normal >> 8;
5194 } else {
5195 asciicode = scan_to_scanascii[scancode].shift;
5196 scancode = scan_to_scanascii[scancode].shift >> 8;
5198 } else {
5199 /* check if lock is on */
5200 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
5201 asciicode = scan_to_scanascii[scancode].shift;
5202 scancode = scan_to_scanascii[scancode].shift >> 8;
5203 } else {
5204 asciicode = scan_to_scanascii[scancode].normal;
5205 scancode = scan_to_scanascii[scancode].normal >> 8;
5208 if (scancode==0 && asciicode==0) {
5209 BX_INFO("KBD: int09h_handler(): scancode & asciicode are zero?\n");
5211 enqueue_key(scancode, asciicode);
5212 break;
5214 if ((scancode & 0x7f) != 0x1d) {
5215 mf2_state &= ~0x01;
5217 mf2_state &= ~0x02;
5218 write_byte(0x0040, 0x96, mf2_state);
5221 unsigned int
5222 enqueue_key(scan_code, ascii_code)
5223 Bit8u scan_code, ascii_code;
5225 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail, temp_tail;
5227 #if BX_CPU < 2
5228 buffer_start = 0x001E;
5229 buffer_end = 0x003E;
5230 #else
5231 buffer_start = read_word(0x0040, 0x0080);
5232 buffer_end = read_word(0x0040, 0x0082);
5233 #endif
5235 buffer_head = read_word(0x0040, 0x001A);
5236 buffer_tail = read_word(0x0040, 0x001C);
5238 temp_tail = buffer_tail;
5239 buffer_tail += 2;
5240 if (buffer_tail >= buffer_end)
5241 buffer_tail = buffer_start;
5243 if (buffer_tail == buffer_head) {
5244 return(0);
5247 write_byte(0x0040, temp_tail, ascii_code);
5248 write_byte(0x0040, temp_tail+1, scan_code);
5249 write_word(0x0040, 0x001C, buffer_tail);
5250 return(1);
5253 void
5254 int74_function(make_farcall, Z, Y, X, status)
5255 Bit16u make_farcall, Z, Y, X, status;
5257 Bit16u ebda_seg=read_word(0x0040,0x000E);
5258 Bit8u in_byte, index, package_count;
5259 Bit8u mouse_flags_1, mouse_flags_2;
5261 BX_DEBUG_INT74("entering int74_function\n");
5262 make_farcall = 0;
5264 in_byte = inb(0x64);
5265 if ((in_byte & 0x21) != 0x21) {
5266 return;
5269 in_byte = inb(0x60);
5270 BX_DEBUG_INT74("int74: read byte %02x\n", in_byte);
5272 mouse_flags_1 = read_byte(ebda_seg, 0x0026);
5273 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
5275 if ((mouse_flags_2 & 0x80) != 0x80) {
5276 return;
5279 package_count = mouse_flags_2 & 0x07;
5280 index = mouse_flags_1 & 0x07;
5281 write_byte(ebda_seg, 0x28 + index, in_byte);
5283 if ( (index+1) >= package_count ) {
5284 BX_DEBUG_INT74("int74_function: make_farcall=1\n");
5285 status = read_byte(ebda_seg, 0x0028 + 0);
5286 X = read_byte(ebda_seg, 0x0028 + 1);
5287 Y = read_byte(ebda_seg, 0x0028 + 2);
5288 Z = 0;
5289 mouse_flags_1 = 0;
5290 // check if far call handler installed
5291 if (mouse_flags_2 & 0x80)
5292 make_farcall = 1;
5294 else {
5295 mouse_flags_1++;
5297 write_byte(ebda_seg, 0x0026, mouse_flags_1);
5300 #define SET_DISK_RET_STATUS(status) write_byte(0x0040, 0x0074, status)
5302 #if BX_USE_ATADRV
5304 void
5305 int13_harddisk(EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
5306 Bit16u EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
5308 Bit32u lba_low, lba_high;
5309 Bit16u ebda_seg=read_word(0x0040,0x000E);
5310 Bit16u cylinder, head, sector;
5311 Bit16u segment, offset;
5312 Bit16u npc, nph, npspt, nlc, nlh, nlspt;
5313 Bit16u size, count;
5314 Bit8u device, status;
5316 BX_DEBUG_INT13_HD("int13_harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5318 write_byte(0x0040, 0x008e, 0); // clear completion flag
5320 // basic check : device has to be defined
5321 if ( (GET_ELDL() < 0x80) || (GET_ELDL() >= 0x80 + BX_MAX_ATA_DEVICES) ) {
5322 BX_INFO("int13_harddisk: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
5323 goto int13_fail;
5326 // Get the ata channel
5327 device=read_byte(ebda_seg,&EbdaData->ata.hdidmap[GET_ELDL()-0x80]);
5329 // basic check : device has to be valid
5330 if (device >= BX_MAX_ATA_DEVICES) {
5331 BX_INFO("int13_harddisk: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
5332 goto int13_fail;
5335 switch (GET_AH()) {
5337 case 0x00: /* disk controller reset */
5338 ata_reset (device);
5339 goto int13_success;
5340 break;
5342 case 0x01: /* read disk status */
5343 status = read_byte(0x0040, 0x0074);
5344 SET_AH(status);
5345 SET_DISK_RET_STATUS(0);
5346 /* set CF if error status read */
5347 if (status) goto int13_fail_nostatus;
5348 else goto int13_success_noah;
5349 break;
5351 case 0x02: // read disk sectors
5352 case 0x03: // write disk sectors
5353 case 0x04: // verify disk sectors
5355 count = GET_AL();
5356 cylinder = GET_CH();
5357 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
5358 sector = (GET_CL() & 0x3f);
5359 head = GET_DH();
5361 segment = ES;
5362 offset = BX;
5364 if ((count > 128) || (count == 0) || (sector == 0)) {
5365 BX_INFO("int13_harddisk: function %02x, parameter out of range!\n",GET_AH());
5366 goto int13_fail;
5369 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
5370 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
5371 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
5373 // sanity check on cyl heads, sec
5374 if( (cylinder >= nlc) || (head >= nlh) || (sector > nlspt) ) {
5375 BX_INFO("int13_harddisk: function %02x, parameters out of range %04x/%04x/%04x!\n", GET_AH(), cylinder, head, sector);
5376 goto int13_fail;
5379 // FIXME verify
5380 if (GET_AH() == 0x04) goto int13_success;
5382 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
5383 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
5385 // if needed, translate lchs to lba, and execute command
5386 if ( (nph != nlh) || (npspt != nlspt)) {
5387 lba_low = ((((Bit32u)cylinder * (Bit32u)nlh) + (Bit32u)head) * (Bit32u)nlspt) + (Bit32u)sector - 1;
5388 lba_high = 0;
5389 sector = 0; // this forces the command to be lba
5392 if (GET_AH() == 0x02)
5393 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, cylinder, head, sector, lba_low, lba_high, segment, offset);
5394 else
5395 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, cylinder, head, sector, lba_low, lba_high, segment, offset);
5397 // Set nb of sector transferred
5398 SET_AL(read_word(ebda_seg, &EbdaData->ata.trsfsectors));
5400 if (status != 0) {
5401 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
5402 SET_AH(0x0c);
5403 goto int13_fail_noah;
5406 goto int13_success;
5407 break;
5409 case 0x05: /* format disk track */
5410 BX_INFO("format disk track called\n");
5411 goto int13_success;
5412 return;
5413 break;
5415 case 0x08: /* read disk drive parameters */
5417 // Get logical geometry from table
5418 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
5419 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
5420 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
5421 count = read_byte(ebda_seg, &EbdaData->ata.hdcount);
5423 nlc = nlc - 2; /* 0 based, last sector not used */
5424 SET_AL(0);
5425 SET_CH(nlc & 0xff);
5426 SET_CL(((nlc >> 2) & 0xc0) | (nlspt & 0x3f));
5427 SET_DH(nlh - 1);
5428 SET_DL(count); /* FIXME returns 0, 1, or n hard drives */
5430 // FIXME should set ES & DI
5432 goto int13_success;
5433 break;
5435 case 0x10: /* check drive ready */
5436 // should look at 40:8E also???
5438 // Read the status from controller
5439 status = inb(read_word(ebda_seg, &EbdaData->ata.channels[device/2].iobase1) + ATA_CB_STAT);
5440 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY)) == ATA_CB_STAT_RDY ) {
5441 goto int13_success;
5443 else {
5444 SET_AH(0xAA);
5445 goto int13_fail_noah;
5447 break;
5449 case 0x15: /* read disk drive size */
5451 // Get logical geometry from table
5452 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
5453 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
5454 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
5456 // Compute sector count seen by int13
5457 lba_low = (Bit32u)(nlc - 1) * (Bit32u)nlh * (Bit32u)nlspt;
5458 CX = lba_low >> 16;
5459 DX = lba_low & 0xffff;
5461 SET_AH(3); // hard disk accessible
5462 goto int13_success_noah;
5463 break;
5465 case 0x41: // IBM/MS installation check
5466 BX=0xaa55; // install check
5467 SET_AH(0x30); // EDD 3.0
5468 CX=0x0007; // ext disk access and edd, removable supported
5469 goto int13_success_noah;
5470 break;
5472 case 0x42: // IBM/MS extended read
5473 case 0x43: // IBM/MS extended write
5474 case 0x44: // IBM/MS verify
5475 case 0x47: // IBM/MS extended seek
5477 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
5478 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
5479 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
5481 // Get 32 msb lba and check
5482 lba_high=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
5483 if (lba_high > read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_high) ) {
5484 BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH());
5485 goto int13_fail;
5488 // Get 32 lsb lba and check
5489 lba_low=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5490 if (lba_high == read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_high)
5491 && lba_low >= read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_low) ) {
5492 BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH());
5493 goto int13_fail;
5496 // If verify or seek
5497 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
5498 goto int13_success;
5500 // Execute the command
5501 if (GET_AH() == 0x42)
5502 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, 0, 0, 0, lba_low, lba_high, segment, offset);
5503 else
5504 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, 0, 0, 0, lba_low, lba_high, segment, offset);
5506 count=read_word(ebda_seg, &EbdaData->ata.trsfsectors);
5507 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5509 if (status != 0) {
5510 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
5511 SET_AH(0x0c);
5512 goto int13_fail_noah;
5515 goto int13_success;
5516 break;
5518 case 0x45: // IBM/MS lock/unlock drive
5519 case 0x49: // IBM/MS extended media change
5520 goto int13_success; // Always success for HD
5521 break;
5523 case 0x46: // IBM/MS eject media
5524 SET_AH(0xb2); // Volume Not Removable
5525 goto int13_fail_noah; // Always fail for HD
5526 break;
5528 case 0x48: // IBM/MS get drive parameters
5529 size=read_word(DS,SI+(Bit16u)&Int13DPT->size);
5531 // Buffer is too small
5532 if(size < 0x1a)
5533 goto int13_fail;
5535 // EDD 1.x
5536 if(size >= 0x1a) {
5537 Bit16u blksize;
5539 npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
5540 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
5541 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
5542 lba_low = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_low);
5543 lba_high = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_high);
5544 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5546 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5547 if (lba_high || (lba_low/npspt)/nph > 0x3fff)
5549 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x00); // geometry is invalid
5550 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0x3fff);
5552 else
5554 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x02); // geometry is valid
5555 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, (Bit32u)npc);
5557 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, (Bit32u)nph);
5558 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, (Bit32u)npspt);
5559 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, lba_low);
5560 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, lba_high);
5561 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5564 // EDD 2.x
5565 if(size >= 0x1e) {
5566 Bit8u channel, dev, irq, mode, checksum, i, translation;
5567 Bit16u iobase1, iobase2, options;
5569 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5571 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5572 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5574 // Fill in dpte
5575 channel = device / 2;
5576 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5577 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5578 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5579 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5580 translation = read_byte(ebda_seg, &EbdaData->ata.devices[device].translation);
5582 options = (translation==ATA_TRANSLATION_NONE?0:1)<<3; // chs translation
5583 options |= (1<<4); // lba translation
5584 options |= (mode==ATA_MODE_PIO32?1:0)<<7;
5585 options |= (translation==ATA_TRANSLATION_LBA?1:0)<<9;
5586 options |= (translation==ATA_TRANSLATION_RECHS?3:0)<<9;
5588 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5589 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2 + ATA_CB_DC);
5590 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5591 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5592 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5593 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5594 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5595 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5596 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5597 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5598 if (size >=0x42)
5599 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5600 else
5601 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x10);
5603 checksum=0;
5604 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, ((Bit8u*)(&EbdaData->ata.dpte)) + i);
5605 checksum = ~checksum;
5606 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5609 // EDD 3.x
5610 if(size >= 0x42) {
5611 Bit8u channel, iface, checksum, i;
5612 Bit16u iobase1;
5614 channel = device / 2;
5615 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5616 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5618 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5619 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5620 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5621 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5622 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5624 if (iface==ATA_IFACE_ISA) {
5625 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5626 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5627 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5628 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5630 else {
5631 // FIXME PCI
5633 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5634 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5635 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5636 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5638 if (iface==ATA_IFACE_ISA) {
5639 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5640 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5641 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5643 else {
5644 // FIXME PCI
5646 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5647 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5648 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5649 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5651 checksum=0;
5652 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5653 checksum = ~checksum;
5654 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5657 goto int13_success;
5658 break;
5660 case 0x4e: // // IBM/MS set hardware configuration
5661 // DMA, prefetch, PIO maximum not supported
5662 switch (GET_AL()) {
5663 case 0x01:
5664 case 0x03:
5665 case 0x04:
5666 case 0x06:
5667 goto int13_success;
5668 break;
5669 default:
5670 goto int13_fail;
5672 break;
5674 case 0x09: /* initialize drive parameters */
5675 case 0x0c: /* seek to specified cylinder */
5676 case 0x0d: /* alternate disk reset */
5677 case 0x11: /* recalibrate */
5678 case 0x14: /* controller internal diagnostic */
5679 BX_INFO("int13_harddisk: function %02xh unimplemented, returns success\n", GET_AH());
5680 goto int13_success;
5681 break;
5683 case 0x0a: /* read disk sectors with ECC */
5684 case 0x0b: /* write disk sectors with ECC */
5685 case 0x18: // set media type for format
5686 case 0x50: // IBM/MS send packet command
5687 default:
5688 BX_INFO("int13_harddisk: function %02xh unsupported, returns fail\n", GET_AH());
5689 goto int13_fail;
5690 break;
5693 int13_fail:
5694 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5695 int13_fail_noah:
5696 SET_DISK_RET_STATUS(GET_AH());
5697 int13_fail_nostatus:
5698 SET_CF(); // error occurred
5699 return;
5701 int13_success:
5702 SET_AH(0x00); // no error
5703 int13_success_noah:
5704 SET_DISK_RET_STATUS(0x00);
5705 CLEAR_CF(); // no error
5708 // ---------------------------------------------------------------------------
5709 // Start of int13 for cdrom
5710 // ---------------------------------------------------------------------------
5712 void
5713 int13_cdrom(EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
5714 Bit16u EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
5716 Bit16u ebda_seg=read_word(0x0040,0x000E);
5717 Bit8u device, status, locks;
5718 Bit8u atacmd[12];
5719 Bit32u lba;
5720 Bit16u count, segment, offset, i, size;
5722 BX_DEBUG_INT13_CD("int13_cdrom: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5724 SET_DISK_RET_STATUS(0x00);
5726 /* basic check : device should be 0xE0+ */
5727 if( (GET_ELDL() < 0xE0) || (GET_ELDL() >= 0xE0+BX_MAX_ATA_DEVICES) ) {
5728 BX_INFO("int13_cdrom: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
5729 goto int13_fail;
5732 // Get the ata channel
5733 device=read_byte(ebda_seg,&EbdaData->ata.cdidmap[GET_ELDL()-0xE0]);
5735 /* basic check : device has to be valid */
5736 if (device >= BX_MAX_ATA_DEVICES) {
5737 BX_INFO("int13_cdrom: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
5738 goto int13_fail;
5741 switch (GET_AH()) {
5743 // all those functions return SUCCESS
5744 case 0x00: /* disk controller reset */
5745 case 0x09: /* initialize drive parameters */
5746 case 0x0c: /* seek to specified cylinder */
5747 case 0x0d: /* alternate disk reset */
5748 case 0x10: /* check drive ready */
5749 case 0x11: /* recalibrate */
5750 case 0x14: /* controller internal diagnostic */
5751 case 0x16: /* detect disk change */
5752 goto int13_success;
5753 break;
5755 // all those functions return disk write-protected
5756 case 0x03: /* write disk sectors */
5757 case 0x05: /* format disk track */
5758 case 0x43: // IBM/MS extended write
5759 SET_AH(0x03);
5760 goto int13_fail_noah;
5761 break;
5763 case 0x01: /* read disk status */
5764 status = read_byte(0x0040, 0x0074);
5765 SET_AH(status);
5766 SET_DISK_RET_STATUS(0);
5768 /* set CF if error status read */
5769 if (status) goto int13_fail_nostatus;
5770 else goto int13_success_noah;
5771 break;
5773 case 0x15: /* read disk drive size */
5774 SET_AH(0x02);
5775 goto int13_fail_noah;
5776 break;
5778 case 0x41: // IBM/MS installation check
5779 BX=0xaa55; // install check
5780 SET_AH(0x30); // EDD 2.1
5781 CX=0x0007; // ext disk access, removable and edd
5782 goto int13_success_noah;
5783 break;
5785 case 0x42: // IBM/MS extended read
5786 case 0x44: // IBM/MS verify sectors
5787 case 0x47: // IBM/MS extended seek
5789 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
5790 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
5791 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
5793 // Can't use 64 bits lba
5794 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
5795 if (lba != 0L) {
5796 BX_PANIC("int13_cdrom: function %02x. Can't use 64bits lba\n",GET_AH());
5797 goto int13_fail;
5800 // Get 32 bits lba
5801 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5803 // If verify or seek
5804 if ((GET_AH() == 0x44) || (GET_AH() == 0x47))
5805 goto int13_success;
5807 memsetb(get_SS(),atacmd,0,12);
5808 atacmd[0]=0x28; // READ command
5809 atacmd[7]=(count & 0xff00) >> 8; // Sectors
5810 atacmd[8]=(count & 0x00ff); // Sectors
5811 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
5812 atacmd[3]=(lba & 0x00ff0000) >> 16;
5813 atacmd[4]=(lba & 0x0000ff00) >> 8;
5814 atacmd[5]=(lba & 0x000000ff);
5815 status = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, count*2048L, ATA_DATA_IN, segment,offset);
5817 count = (Bit16u)(read_dword(ebda_seg, &EbdaData->ata.trsfbytes) >> 11);
5818 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5820 if (status != 0) {
5821 BX_INFO("int13_cdrom: function %02x, status %02x !\n",GET_AH(),status);
5822 SET_AH(0x0c);
5823 goto int13_fail_noah;
5826 goto int13_success;
5827 break;
5829 case 0x45: // IBM/MS lock/unlock drive
5830 if (GET_AL() > 2) goto int13_fail;
5832 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5834 switch (GET_AL()) {
5835 case 0 : // lock
5836 if (locks == 0xff) {
5837 SET_AH(0xb4);
5838 SET_AL(1);
5839 goto int13_fail_noah;
5841 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, ++locks);
5842 SET_AL(1);
5843 break;
5844 case 1 : // unlock
5845 if (locks == 0x00) {
5846 SET_AH(0xb0);
5847 SET_AL(0);
5848 goto int13_fail_noah;
5850 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, --locks);
5851 SET_AL(locks==0?0:1);
5852 break;
5853 case 2 : // status
5854 SET_AL(locks==0?0:1);
5855 break;
5858 goto int13_success;
5859 break;
5861 case 0x46: // IBM/MS eject media
5862 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5864 if (locks != 0) {
5865 SET_AH(0xb1); // media locked
5866 goto int13_fail_noah;
5868 // FIXME should handle 0x31 no media in device
5869 // FIXME should handle 0xb5 valid request failed
5871 // Call removable media eject
5872 ASM_START
5873 push bp
5874 mov bp, sp
5876 mov ah, #0x52
5877 int #0x15
5878 mov _int13_cdrom.status + 2[bp], ah
5879 jnc int13_cdrom_rme_end
5880 mov _int13_cdrom.status, #1
5881 int13_cdrom_rme_end:
5882 pop bp
5883 ASM_END
5885 if (status != 0) {
5886 SET_AH(0xb1); // media locked
5887 goto int13_fail_noah;
5890 goto int13_success;
5891 break;
5893 case 0x48: // IBM/MS get drive parameters
5894 size = read_word(DS,SI+(Bit16u)&Int13Ext->size);
5896 // Buffer is too small
5897 if(size < 0x1a)
5898 goto int13_fail;
5900 // EDD 1.x
5901 if(size >= 0x1a) {
5902 Bit16u cylinders, heads, spt, blksize;
5904 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5906 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5907 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x74); // removable, media change, lockable, max values
5908 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0xffffffff);
5909 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, 0xffffffff);
5910 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, 0xffffffff);
5911 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, 0xffffffff); // FIXME should be Bit64
5912 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0xffffffff);
5913 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5916 // EDD 2.x
5917 if(size >= 0x1e) {
5918 Bit8u channel, dev, irq, mode, checksum, i;
5919 Bit16u iobase1, iobase2, options;
5921 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5923 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5924 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5926 // Fill in dpte
5927 channel = device / 2;
5928 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5929 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5930 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5931 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5933 // FIXME atapi device
5934 options = (1<<4); // lba translation
5935 options |= (1<<5); // removable device
5936 options |= (1<<6); // atapi device
5937 options |= (mode==ATA_MODE_PIO32?1:0<<7);
5939 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5940 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2 + ATA_CB_DC);
5941 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5942 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5943 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5944 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5945 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5946 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5947 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5948 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5949 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5951 checksum=0;
5952 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, ((Bit8u*)(&EbdaData->ata.dpte)) + i);
5953 checksum = ~checksum;
5954 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5957 // EDD 3.x
5958 if(size >= 0x42) {
5959 Bit8u channel, iface, checksum, i;
5960 Bit16u iobase1;
5962 channel = device / 2;
5963 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5964 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5966 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5967 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5968 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5969 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5970 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5972 if (iface==ATA_IFACE_ISA) {
5973 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5974 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5975 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5976 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5978 else {
5979 // FIXME PCI
5981 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5982 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5983 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5984 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5986 if (iface==ATA_IFACE_ISA) {
5987 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5988 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5989 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5991 else {
5992 // FIXME PCI
5994 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5995 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5996 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5997 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5999 checksum=0;
6000 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
6001 checksum = ~checksum;
6002 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
6005 goto int13_success;
6006 break;
6008 case 0x49: // IBM/MS extended media change
6009 // always send changed ??
6010 SET_AH(06);
6011 goto int13_fail_nostatus;
6012 break;
6014 case 0x4e: // // IBM/MS set hardware configuration
6015 // DMA, prefetch, PIO maximum not supported
6016 switch (GET_AL()) {
6017 case 0x01:
6018 case 0x03:
6019 case 0x04:
6020 case 0x06:
6021 goto int13_success;
6022 break;
6023 default:
6024 goto int13_fail;
6026 break;
6028 // all those functions return unimplemented
6029 case 0x02: /* read sectors */
6030 case 0x04: /* verify sectors */
6031 case 0x08: /* read disk drive parameters */
6032 case 0x0a: /* read disk sectors with ECC */
6033 case 0x0b: /* write disk sectors with ECC */
6034 case 0x18: /* set media type for format */
6035 case 0x50: // ? - send packet command
6036 default:
6037 BX_INFO("int13_cdrom: unsupported AH=%02x\n", GET_AH());
6038 goto int13_fail;
6039 break;
6042 int13_fail:
6043 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
6044 int13_fail_noah:
6045 SET_DISK_RET_STATUS(GET_AH());
6046 int13_fail_nostatus:
6047 SET_CF(); // error occurred
6048 return;
6050 int13_success:
6051 SET_AH(0x00); // no error
6052 int13_success_noah:
6053 SET_DISK_RET_STATUS(0x00);
6054 CLEAR_CF(); // no error
6057 // ---------------------------------------------------------------------------
6058 // End of int13 for cdrom
6059 // ---------------------------------------------------------------------------
6061 #if BX_ELTORITO_BOOT
6062 // ---------------------------------------------------------------------------
6063 // Start of int13 for eltorito functions
6064 // ---------------------------------------------------------------------------
6066 void
6067 int13_eltorito(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
6068 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
6070 Bit16u ebda_seg=read_word(0x0040,0x000E);
6072 BX_DEBUG_INT13_ET("int13_eltorito: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
6073 // BX_DEBUG_INT13_ET("int13_eltorito: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
6075 switch (GET_AH()) {
6077 // FIXME ElTorito Various. Should be implemented
6078 case 0x4a: // ElTorito - Initiate disk emu
6079 case 0x4c: // ElTorito - Initiate disk emu and boot
6080 case 0x4d: // ElTorito - Return Boot catalog
6081 BX_PANIC("Int13 eltorito call with AX=%04x. Please report\n",AX);
6082 goto int13_fail;
6083 break;
6085 case 0x4b: // ElTorito - Terminate disk emu
6086 // FIXME ElTorito Hardcoded
6087 write_byte(DS,SI+0x00,0x13);
6088 write_byte(DS,SI+0x01,read_byte(ebda_seg,&EbdaData->cdemu.media));
6089 write_byte(DS,SI+0x02,read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
6090 write_byte(DS,SI+0x03,read_byte(ebda_seg,&EbdaData->cdemu.controller_index));
6091 write_dword(DS,SI+0x04,read_dword(ebda_seg,&EbdaData->cdemu.ilba));
6092 write_word(DS,SI+0x08,read_word(ebda_seg,&EbdaData->cdemu.device_spec));
6093 write_word(DS,SI+0x0a,read_word(ebda_seg,&EbdaData->cdemu.buffer_segment));
6094 write_word(DS,SI+0x0c,read_word(ebda_seg,&EbdaData->cdemu.load_segment));
6095 write_word(DS,SI+0x0e,read_word(ebda_seg,&EbdaData->cdemu.sector_count));
6096 write_byte(DS,SI+0x10,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.cylinders));
6097 write_byte(DS,SI+0x11,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.spt));
6098 write_byte(DS,SI+0x12,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.heads));
6100 // If we have to terminate emulation
6101 if(GET_AL() == 0x00) {
6102 // FIXME ElTorito Various. Should be handled accordingly to spec
6103 write_byte(ebda_seg,&EbdaData->cdemu.active, 0x00); // bye bye
6106 goto int13_success;
6107 break;
6109 default:
6110 BX_INFO("int13_eltorito: unsupported AH=%02x\n", GET_AH());
6111 goto int13_fail;
6112 break;
6115 int13_fail:
6116 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
6117 SET_DISK_RET_STATUS(GET_AH());
6118 SET_CF(); // error occurred
6119 return;
6121 int13_success:
6122 SET_AH(0x00); // no error
6123 SET_DISK_RET_STATUS(0x00);
6124 CLEAR_CF(); // no error
6127 // ---------------------------------------------------------------------------
6128 // End of int13 for eltorito functions
6129 // ---------------------------------------------------------------------------
6131 // ---------------------------------------------------------------------------
6132 // Start of int13 when emulating a device from the cd
6133 // ---------------------------------------------------------------------------
6135 void
6136 int13_cdemu(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
6137 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
6139 Bit16u ebda_seg=read_word(0x0040,0x000E);
6140 Bit8u device, status;
6141 Bit16u vheads, vspt, vcylinders;
6142 Bit16u head, sector, cylinder, nbsectors;
6143 Bit32u vlba, ilba, slba, elba;
6144 Bit16u before, segment, offset;
6145 Bit8u atacmd[12];
6147 BX_DEBUG_INT13_ET("int13_cdemu: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
6149 /* at this point, we are emulating a floppy/harddisk */
6151 // Recompute the device number
6152 device = read_byte(ebda_seg,&EbdaData->cdemu.controller_index) * 2;
6153 device += read_byte(ebda_seg,&EbdaData->cdemu.device_spec);
6155 SET_DISK_RET_STATUS(0x00);
6157 /* basic checks : emulation should be active, dl should equal the emulated drive */
6158 if( (read_byte(ebda_seg,&EbdaData->cdemu.active) ==0) ||
6159 (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive ) != GET_DL())) {
6160 BX_INFO("int13_cdemu: function %02x, emulation not active for DL= %02x\n", GET_AH(), GET_DL());
6161 goto int13_fail;
6164 switch (GET_AH()) {
6166 // all those functions return SUCCESS
6167 case 0x00: /* disk controller reset */
6168 case 0x09: /* initialize drive parameters */
6169 case 0x0c: /* seek to specified cylinder */
6170 case 0x0d: /* alternate disk reset */ // FIXME ElTorito Various. should really reset ?
6171 case 0x10: /* check drive ready */ // FIXME ElTorito Various. should check if ready ?
6172 case 0x11: /* recalibrate */
6173 case 0x14: /* controller internal diagnostic */
6174 case 0x16: /* detect disk change */
6175 goto int13_success;
6176 break;
6178 // all those functions return disk write-protected
6179 case 0x03: /* write disk sectors */
6180 case 0x05: /* format disk track */
6181 SET_AH(0x03);
6182 goto int13_fail_noah;
6183 break;
6185 case 0x01: /* read disk status */
6186 status=read_byte(0x0040, 0x0074);
6187 SET_AH(status);
6188 SET_DISK_RET_STATUS(0);
6190 /* set CF if error status read */
6191 if (status) goto int13_fail_nostatus;
6192 else goto int13_success_noah;
6193 break;
6195 case 0x02: // read disk sectors
6196 case 0x04: // verify disk sectors
6197 vspt = read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
6198 vcylinders = read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders);
6199 vheads = read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads);
6201 ilba = read_dword(ebda_seg,&EbdaData->cdemu.ilba);
6203 sector = GET_CL() & 0x003f;
6204 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
6205 head = GET_DH();
6206 nbsectors = GET_AL();
6207 segment = ES;
6208 offset = BX;
6210 // no sector to read ?
6211 if(nbsectors==0) goto int13_success;
6213 // sanity checks sco openserver needs this!
6214 if ((sector > vspt)
6215 || (cylinder >= vcylinders)
6216 || (head >= vheads)) {
6217 goto int13_fail;
6220 // After controls, verify do nothing
6221 if (GET_AH() == 0x04) goto int13_success;
6223 segment = ES+(BX / 16);
6224 offset = BX % 16;
6226 // calculate the virtual lba inside the image
6227 vlba=((((Bit32u)cylinder*(Bit32u)vheads)+(Bit32u)head)*(Bit32u)vspt)+((Bit32u)(sector-1));
6229 // In advance so we don't loose the count
6230 SET_AL(nbsectors);
6232 // start lba on cd
6233 slba = (Bit32u)vlba/4;
6234 before= (Bit16u)vlba%4;
6236 // end lba on cd
6237 elba = (Bit32u)(vlba+nbsectors-1)/4;
6239 memsetb(get_SS(),atacmd,0,12);
6240 atacmd[0]=0x28; // READ command
6241 atacmd[7]=((Bit16u)(elba-slba+1) & 0xff00) >> 8; // Sectors
6242 atacmd[8]=((Bit16u)(elba-slba+1) & 0x00ff); // Sectors
6243 atacmd[2]=(ilba+slba & 0xff000000) >> 24; // LBA
6244 atacmd[3]=(ilba+slba & 0x00ff0000) >> 16;
6245 atacmd[4]=(ilba+slba & 0x0000ff00) >> 8;
6246 atacmd[5]=(ilba+slba & 0x000000ff);
6247 if((status = ata_cmd_packet(device, 12, get_SS(), atacmd, before*512, nbsectors*512L, ATA_DATA_IN, segment,offset)) != 0) {
6248 BX_INFO("int13_cdemu: function %02x, error %02x !\n",GET_AH(),status);
6249 SET_AH(0x02);
6250 SET_AL(0);
6251 goto int13_fail_noah;
6254 goto int13_success;
6255 break;
6257 case 0x08: /* read disk drive parameters */
6258 vspt=read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
6259 vcylinders=read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders) - 1;
6260 vheads=read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads) - 1;
6262 SET_AL(0x00);
6263 SET_BL(0x00);
6264 SET_CH(vcylinders & 0xff);
6265 SET_CL(((vcylinders >> 2) & 0xc0) | (vspt & 0x3f));
6266 SET_DH(vheads);
6267 SET_DL(0x02); // FIXME ElTorito Various. should send the real count of drives 1 or 2
6268 // FIXME ElTorito Harddisk. should send the HD count
6270 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
6271 case 0x01: SET_BL( 0x02 ); break;
6272 case 0x02: SET_BL( 0x04 ); break;
6273 case 0x03: SET_BL( 0x06 ); break;
6276 ASM_START
6277 push bp
6278 mov bp, sp
6279 mov ax, #diskette_param_table2
6280 mov _int13_cdemu.DI+2[bp], ax
6281 mov _int13_cdemu.ES+2[bp], cs
6282 pop bp
6283 ASM_END
6284 goto int13_success;
6285 break;
6287 case 0x15: /* read disk drive size */
6288 // FIXME ElTorito Harddisk. What geometry to send ?
6289 SET_AH(0x03);
6290 goto int13_success_noah;
6291 break;
6293 // all those functions return unimplemented
6294 case 0x0a: /* read disk sectors with ECC */
6295 case 0x0b: /* write disk sectors with ECC */
6296 case 0x18: /* set media type for format */
6297 case 0x41: // IBM/MS installation check
6298 // FIXME ElTorito Harddisk. Darwin would like to use EDD
6299 case 0x42: // IBM/MS extended read
6300 case 0x43: // IBM/MS extended write
6301 case 0x44: // IBM/MS verify sectors
6302 case 0x45: // IBM/MS lock/unlock drive
6303 case 0x46: // IBM/MS eject media
6304 case 0x47: // IBM/MS extended seek
6305 case 0x48: // IBM/MS get drive parameters
6306 case 0x49: // IBM/MS extended media change
6307 case 0x4e: // ? - set hardware configuration
6308 case 0x50: // ? - send packet command
6309 default:
6310 BX_INFO("int13_cdemu function AH=%02x unsupported, returns fail\n", GET_AH());
6311 goto int13_fail;
6312 break;
6315 int13_fail:
6316 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
6317 int13_fail_noah:
6318 SET_DISK_RET_STATUS(GET_AH());
6319 int13_fail_nostatus:
6320 SET_CF(); // error occurred
6321 return;
6323 int13_success:
6324 SET_AH(0x00); // no error
6325 int13_success_noah:
6326 SET_DISK_RET_STATUS(0x00);
6327 CLEAR_CF(); // no error
6330 // ---------------------------------------------------------------------------
6331 // End of int13 when emulating a device from the cd
6332 // ---------------------------------------------------------------------------
6334 #endif // BX_ELTORITO_BOOT
6336 #else //BX_USE_ATADRV
6338 void
6339 outLBA(cylinder,hd_heads,head,hd_sectors,sector,dl)
6340 Bit16u cylinder;
6341 Bit16u hd_heads;
6342 Bit16u head;
6343 Bit16u hd_sectors;
6344 Bit16u sector;
6345 Bit16u dl;
6347 ASM_START
6348 push bp
6349 mov bp, sp
6350 push eax
6351 push ebx
6352 push edx
6353 xor eax,eax
6354 mov ax,4[bp] // cylinder
6355 xor ebx,ebx
6356 mov bl,6[bp] // hd_heads
6357 imul ebx
6359 mov bl,8[bp] // head
6360 add eax,ebx
6361 mov bl,10[bp] // hd_sectors
6362 imul ebx
6363 mov bl,12[bp] // sector
6364 add eax,ebx
6366 dec eax
6367 mov dx,#0x1f3
6368 out dx,al
6369 mov dx,#0x1f4
6370 mov al,ah
6371 out dx,al
6372 shr eax,#16
6373 mov dx,#0x1f5
6374 out dx,al
6375 and ah,#0xf
6376 mov bl,14[bp] // dl
6377 and bl,#1
6378 shl bl,#4
6379 or ah,bl
6380 or ah,#0xe0
6381 mov al,ah
6382 mov dx,#0x01f6
6383 out dx,al
6384 pop edx
6385 pop ebx
6386 pop eax
6387 pop bp
6388 ASM_END
6391 void
6392 int13_harddisk(EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
6393 Bit16u EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
6395 Bit8u drive, num_sectors, sector, head, status, mod;
6396 Bit8u drive_map;
6397 Bit8u n_drives;
6398 Bit16u cyl_mod, ax;
6399 Bit16u max_cylinder, cylinder, total_sectors;
6400 Bit16u hd_cylinders;
6401 Bit8u hd_heads, hd_sectors;
6402 Bit16u val16;
6403 Bit8u sector_count;
6404 unsigned int i;
6405 Bit16u tempbx;
6406 Bit16u dpsize;
6408 Bit16u count, segment, offset;
6409 Bit32u lba;
6410 Bit16u error;
6412 BX_DEBUG_INT13_HD("int13 harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
6414 write_byte(0x0040, 0x008e, 0); // clear completion flag
6416 /* at this point, DL is >= 0x80 to be passed from the floppy int13h
6417 handler code */
6418 /* check how many disks first (cmos reg 0x12), return an error if
6419 drive not present */
6420 drive_map = inb_cmos(0x12);
6421 drive_map = (((drive_map & 0xf0)==0) ? 0 : 1) |
6422 (((drive_map & 0x0f)==0) ? 0 : 2);
6423 n_drives = (drive_map==0) ? 0 : ((drive_map==3) ? 2 : 1);
6425 if (!(drive_map & (1<<(GET_ELDL()&0x7f)))) { /* allow 0, 1, or 2 disks */
6426 SET_AH(0x01);
6427 SET_DISK_RET_STATUS(0x01);
6428 SET_CF(); /* error occurred */
6429 return;
6432 switch (GET_AH()) {
6434 case 0x00: /* disk controller reset */
6435 BX_DEBUG_INT13_HD("int13_f00\n");
6437 SET_AH(0);
6438 SET_DISK_RET_STATUS(0);
6439 set_diskette_ret_status(0);
6440 set_diskette_current_cyl(0, 0); /* current cylinder, diskette 1 */
6441 set_diskette_current_cyl(1, 0); /* current cylinder, diskette 2 */
6442 CLEAR_CF(); /* successful */
6443 return;
6444 break;
6446 case 0x01: /* read disk status */
6447 BX_DEBUG_INT13_HD("int13_f01\n");
6448 status = read_byte(0x0040, 0x0074);
6449 SET_AH(status);
6450 SET_DISK_RET_STATUS(0);
6451 /* set CF if error status read */
6452 if (status) SET_CF();
6453 else CLEAR_CF();
6454 return;
6455 break;
6457 case 0x04: // verify disk sectors
6458 case 0x02: // read disk sectors
6459 drive = GET_ELDL();
6460 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6462 num_sectors = GET_AL();
6463 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
6464 sector = (GET_CL() & 0x3f);
6465 head = GET_DH();
6468 if (hd_cylinders > 1024) {
6469 if (hd_cylinders <= 2048) {
6470 cylinder <<= 1;
6472 else if (hd_cylinders <= 4096) {
6473 cylinder <<= 2;
6475 else if (hd_cylinders <= 8192) {
6476 cylinder <<= 3;
6478 else { // hd_cylinders <= 16384
6479 cylinder <<= 4;
6482 ax = head / hd_heads;
6483 cyl_mod = ax & 0xff;
6484 head = ax >> 8;
6485 cylinder |= cyl_mod;
6488 if ( (cylinder >= hd_cylinders) ||
6489 (sector > hd_sectors) ||
6490 (head >= hd_heads) ) {
6491 SET_AH(1);
6492 SET_DISK_RET_STATUS(1);
6493 SET_CF(); /* error occurred */
6494 return;
6497 if ( (num_sectors > 128) || (num_sectors == 0) )
6498 BX_PANIC("int13_harddisk: num_sectors out of range!\n");
6500 if (head > 15)
6501 BX_PANIC("hard drive BIOS:(read/verify) head > 15\n");
6503 if ( GET_AH() == 0x04 ) {
6504 SET_AH(0);
6505 SET_DISK_RET_STATUS(0);
6506 CLEAR_CF();
6507 return;
6510 status = inb(0x1f7);
6511 if (status & 0x80) {
6512 BX_PANIC("hard drive BIOS:(read/verify) BUSY bit set\n");
6514 outb(0x01f2, num_sectors);
6515 /* activate LBA? (tomv) */
6516 if (hd_heads > 16) {
6517 BX_DEBUG_INT13_HD("CHS: %x %x %x\n", cylinder, head, sector);
6518 outLBA(cylinder,hd_heads,head,hd_sectors,sector,drive);
6520 else {
6521 outb(0x01f3, sector);
6522 outb(0x01f4, cylinder & 0x00ff);
6523 outb(0x01f5, cylinder >> 8);
6524 outb(0x01f6, 0xa0 | ((drive & 0x01)<<4) | (head & 0x0f));
6526 outb(0x01f7, 0x20);
6528 while (1) {
6529 status = inb(0x1f7);
6530 if (!(status & 0x80)) break;
6533 if (status & 0x01) {
6534 BX_PANIC("hard drive BIOS:(read/verify) read error\n");
6535 } else if (!(status & 0x08)) {
6536 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
6537 BX_PANIC("hard drive BIOS:(read/verify) expected DRQ=1\n");
6540 sector_count = 0;
6541 tempbx = BX;
6543 ASM_START
6544 sti ;; enable higher priority interrupts
6545 ASM_END
6547 while (1) {
6548 ASM_START
6549 ;; store temp bx in real DI register
6550 push bp
6551 mov bp, sp
6552 mov di, _int13_harddisk.tempbx + 2 [bp]
6553 pop bp
6555 ;; adjust if there will be an overrun
6556 cmp di, #0xfe00
6557 jbe i13_f02_no_adjust
6558 i13_f02_adjust:
6559 sub di, #0x0200 ; sub 512 bytes from offset
6560 mov ax, es
6561 add ax, #0x0020 ; add 512 to segment
6562 mov es, ax
6564 i13_f02_no_adjust:
6565 mov cx, #0x0100 ;; counter (256 words = 512b)
6566 mov dx, #0x01f0 ;; AT data read port
6569 insw ;; CX words transfered from port(DX) to ES:[DI]
6571 i13_f02_done:
6572 ;; store real DI register back to temp bx
6573 push bp
6574 mov bp, sp
6575 mov _int13_harddisk.tempbx + 2 [bp], di
6576 pop bp
6577 ASM_END
6579 sector_count++;
6580 num_sectors--;
6581 if (num_sectors == 0) {
6582 status = inb(0x1f7);
6583 if ((status & 0xc9) != 0x40)
6584 BX_PANIC("no sectors left to read/verify, status is %02x\n", (unsigned) status);
6585 break;
6587 else {
6588 status = inb(0x1f7);
6589 if ((status & 0xc9) != 0x48)
6590 BX_PANIC("more sectors left to read/verify, status is %02x\n", (unsigned) status);
6591 continue;
6595 SET_AH(0);
6596 SET_DISK_RET_STATUS(0);
6597 SET_AL(sector_count);
6598 CLEAR_CF(); /* successful */
6599 return;
6600 break;
6602 case 0x03: /* write disk sectors */
6603 BX_DEBUG_INT13_HD("int13_f03\n");
6604 drive = GET_ELDL ();
6605 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6607 num_sectors = GET_AL();
6608 cylinder = GET_CH();
6609 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
6610 sector = (GET_CL() & 0x3f);
6611 head = GET_DH();
6613 if (hd_cylinders > 1024) {
6614 if (hd_cylinders <= 2048) {
6615 cylinder <<= 1;
6617 else if (hd_cylinders <= 4096) {
6618 cylinder <<= 2;
6620 else if (hd_cylinders <= 8192) {
6621 cylinder <<= 3;
6623 else { // hd_cylinders <= 16384
6624 cylinder <<= 4;
6627 ax = head / hd_heads;
6628 cyl_mod = ax & 0xff;
6629 head = ax >> 8;
6630 cylinder |= cyl_mod;
6633 if ( (cylinder >= hd_cylinders) ||
6634 (sector > hd_sectors) ||
6635 (head >= hd_heads) ) {
6636 SET_AH(1);
6637 SET_DISK_RET_STATUS(1);
6638 SET_CF(); /* error occurred */
6639 return;
6642 if ( (num_sectors > 128) || (num_sectors == 0) )
6643 BX_PANIC("int13_harddisk: num_sectors out of range!\n");
6645 if (head > 15)
6646 BX_PANIC("hard drive BIOS:(read) head > 15\n");
6648 status = inb(0x1f7);
6649 if (status & 0x80) {
6650 BX_PANIC("hard drive BIOS:(read) BUSY bit set\n");
6652 // should check for Drive Ready Bit also in status reg
6653 outb(0x01f2, num_sectors);
6655 /* activate LBA? (tomv) */
6656 if (hd_heads > 16) {
6657 BX_DEBUG_INT13_HD("CHS (write): %x %x %x\n", cylinder, head, sector);
6658 outLBA(cylinder,hd_heads,head,hd_sectors,sector,GET_ELDL());
6660 else {
6661 outb(0x01f3, sector);
6662 outb(0x01f4, cylinder & 0x00ff);
6663 outb(0x01f5, cylinder >> 8);
6664 outb(0x01f6, 0xa0 | ((GET_ELDL() & 0x01)<<4) | (head & 0x0f));
6666 outb(0x01f7, 0x30);
6668 // wait for busy bit to turn off after seeking
6669 while (1) {
6670 status = inb(0x1f7);
6671 if (!(status & 0x80)) break;
6674 if (!(status & 0x08)) {
6675 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
6676 BX_PANIC("hard drive BIOS:(write) data-request bit not set\n");
6679 sector_count = 0;
6680 tempbx = BX;
6682 ASM_START
6683 sti ;; enable higher priority interrupts
6684 ASM_END
6686 while (1) {
6687 ASM_START
6688 ;; store temp bx in real SI register
6689 push bp
6690 mov bp, sp
6691 mov si, _int13_harddisk.tempbx + 2 [bp]
6692 pop bp
6694 ;; adjust if there will be an overrun
6695 cmp si, #0xfe00
6696 jbe i13_f03_no_adjust
6697 i13_f03_adjust:
6698 sub si, #0x0200 ; sub 512 bytes from offset
6699 mov ax, es
6700 add ax, #0x0020 ; add 512 to segment
6701 mov es, ax
6703 i13_f03_no_adjust:
6704 mov cx, #0x0100 ;; counter (256 words = 512b)
6705 mov dx, #0x01f0 ;; AT data read port
6707 seg ES
6709 outsw ;; CX words tranfered from ES:[SI] to port(DX)
6711 ;; store real SI register back to temp bx
6712 push bp
6713 mov bp, sp
6714 mov _int13_harddisk.tempbx + 2 [bp], si
6715 pop bp
6716 ASM_END
6718 sector_count++;
6719 num_sectors--;
6720 if (num_sectors == 0) {
6721 status = inb(0x1f7);
6722 if ((status & 0xe9) != 0x40)
6723 BX_PANIC("no sectors left to write, status is %02x\n", (unsigned) status);
6724 break;
6726 else {
6727 status = inb(0x1f7);
6728 if ((status & 0xc9) != 0x48)
6729 BX_PANIC("more sectors left to write, status is %02x\n", (unsigned) status);
6730 continue;
6734 SET_AH(0);
6735 SET_DISK_RET_STATUS(0);
6736 SET_AL(sector_count);
6737 CLEAR_CF(); /* successful */
6738 return;
6739 break;
6741 case 0x05: /* format disk track */
6742 BX_DEBUG_INT13_HD("int13_f05\n");
6743 BX_PANIC("format disk track called\n");
6744 /* nop */
6745 SET_AH(0);
6746 SET_DISK_RET_STATUS(0);
6747 CLEAR_CF(); /* successful */
6748 return;
6749 break;
6751 case 0x08: /* read disk drive parameters */
6752 BX_DEBUG_INT13_HD("int13_f08\n");
6754 drive = GET_ELDL ();
6755 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6757 // translate CHS
6759 if (hd_cylinders <= 1024) {
6760 // hd_cylinders >>= 0;
6761 // hd_heads <<= 0;
6763 else if (hd_cylinders <= 2048) {
6764 hd_cylinders >>= 1;
6765 hd_heads <<= 1;
6767 else if (hd_cylinders <= 4096) {
6768 hd_cylinders >>= 2;
6769 hd_heads <<= 2;
6771 else if (hd_cylinders <= 8192) {
6772 hd_cylinders >>= 3;
6773 hd_heads <<= 3;
6775 else { // hd_cylinders <= 16384
6776 hd_cylinders >>= 4;
6777 hd_heads <<= 4;
6780 max_cylinder = hd_cylinders - 2; /* 0 based */
6781 SET_AL(0);
6782 SET_CH(max_cylinder & 0xff);
6783 SET_CL(((max_cylinder >> 2) & 0xc0) | (hd_sectors & 0x3f));
6784 SET_DH(hd_heads - 1);
6785 SET_DL(n_drives); /* returns 0, 1, or 2 hard drives */
6786 SET_AH(0);
6787 SET_DISK_RET_STATUS(0);
6788 CLEAR_CF(); /* successful */
6790 return;
6791 break;
6793 case 0x09: /* initialize drive parameters */
6794 BX_DEBUG_INT13_HD("int13_f09\n");
6795 SET_AH(0);
6796 SET_DISK_RET_STATUS(0);
6797 CLEAR_CF(); /* successful */
6798 return;
6799 break;
6801 case 0x0a: /* read disk sectors with ECC */
6802 BX_DEBUG_INT13_HD("int13_f0a\n");
6803 case 0x0b: /* write disk sectors with ECC */
6804 BX_DEBUG_INT13_HD("int13_f0b\n");
6805 BX_PANIC("int13h Functions 0Ah & 0Bh not implemented!\n");
6806 return;
6807 break;
6809 case 0x0c: /* seek to specified cylinder */
6810 BX_DEBUG_INT13_HD("int13_f0c\n");
6811 BX_INFO("int13h function 0ch (seek) not implemented!\n");
6812 SET_AH(0);
6813 SET_DISK_RET_STATUS(0);
6814 CLEAR_CF(); /* successful */
6815 return;
6816 break;
6818 case 0x0d: /* alternate disk reset */
6819 BX_DEBUG_INT13_HD("int13_f0d\n");
6820 SET_AH(0);
6821 SET_DISK_RET_STATUS(0);
6822 CLEAR_CF(); /* successful */
6823 return;
6824 break;
6826 case 0x10: /* check drive ready */
6827 BX_DEBUG_INT13_HD("int13_f10\n");
6828 //SET_AH(0);
6829 //SET_DISK_RET_STATUS(0);
6830 //CLEAR_CF(); /* successful */
6831 //return;
6832 //break;
6834 // should look at 40:8E also???
6835 status = inb(0x01f7);
6836 if ((status & 0xc0) == 0x40) {
6837 SET_AH(0);
6838 SET_DISK_RET_STATUS(0);
6839 CLEAR_CF(); // drive ready
6840 return;
6842 else {
6843 SET_AH(0xAA);
6844 SET_DISK_RET_STATUS(0xAA);
6845 SET_CF(); // not ready
6846 return;
6848 break;
6850 case 0x11: /* recalibrate */
6851 BX_DEBUG_INT13_HD("int13_f11\n");
6852 SET_AH(0);
6853 SET_DISK_RET_STATUS(0);
6854 CLEAR_CF(); /* successful */
6855 return;
6856 break;
6858 case 0x14: /* controller internal diagnostic */
6859 BX_DEBUG_INT13_HD("int13_f14\n");
6860 SET_AH(0);
6861 SET_DISK_RET_STATUS(0);
6862 CLEAR_CF(); /* successful */
6863 SET_AL(0);
6864 return;
6865 break;
6867 case 0x15: /* read disk drive size */
6868 drive = GET_ELDL();
6869 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6870 ASM_START
6871 push bp
6872 mov bp, sp
6873 mov al, _int13_harddisk.hd_heads + 2 [bp]
6874 mov ah, _int13_harddisk.hd_sectors + 2 [bp]
6875 mul al, ah ;; ax = heads * sectors
6876 mov bx, _int13_harddisk.hd_cylinders + 2 [bp]
6877 dec bx ;; use (cylinders - 1) ???
6878 mul ax, bx ;; dx:ax = (cylinders -1) * (heads * sectors)
6879 ;; now we need to move the 32bit result dx:ax to what the
6880 ;; BIOS wants which is cx:dx.
6881 ;; and then into CX:DX on the stack
6882 mov _int13_harddisk.CX + 2 [bp], dx
6883 mov _int13_harddisk.DX + 2 [bp], ax
6884 pop bp
6885 ASM_END
6886 SET_AH(3); // hard disk accessible
6887 SET_DISK_RET_STATUS(0); // ??? should this be 0
6888 CLEAR_CF(); // successful
6889 return;
6890 break;
6892 case 0x18: // set media type for format
6893 case 0x41: // IBM/MS
6894 case 0x42: // IBM/MS
6895 case 0x43: // IBM/MS
6896 case 0x44: // IBM/MS
6897 case 0x45: // IBM/MS lock/unlock drive
6898 case 0x46: // IBM/MS eject media
6899 case 0x47: // IBM/MS extended seek
6900 case 0x49: // IBM/MS extended media change
6901 case 0x50: // IBM/MS send packet command
6902 default:
6903 BX_INFO("int13_harddisk: unsupported AH=%02x\n", GET_AH());
6905 SET_AH(1); // code=invalid function in AH or invalid parameter
6906 SET_DISK_RET_STATUS(1);
6907 SET_CF(); /* unsuccessful */
6908 return;
6912 static char panic_msg_reg12h[] = "HD%d cmos reg 12h not type F\n";
6913 static char panic_msg_reg19h[] = "HD%d cmos reg %02xh not user definable type 47\n";
6915 void
6916 get_hd_geometry(drive, hd_cylinders, hd_heads, hd_sectors)
6917 Bit8u drive;
6918 Bit16u *hd_cylinders;
6919 Bit8u *hd_heads;
6920 Bit8u *hd_sectors;
6922 Bit8u hd_type;
6923 Bit16u ss;
6924 Bit16u cylinders;
6925 Bit8u iobase;
6927 ss = get_SS();
6928 if (drive == 0x80) {
6929 hd_type = inb_cmos(0x12) & 0xf0;
6930 if (hd_type != 0xf0)
6931 BX_INFO(panic_msg_reg12h,0);
6932 hd_type = inb_cmos(0x19); // HD0: extended type
6933 if (hd_type != 47)
6934 BX_INFO(panic_msg_reg19h,0,0x19);
6935 iobase = 0x1b;
6936 } else {
6937 hd_type = inb_cmos(0x12) & 0x0f;
6938 if (hd_type != 0x0f)
6939 BX_INFO(panic_msg_reg12h,1);
6940 hd_type = inb_cmos(0x1a); // HD1: extended type
6941 if (hd_type != 47)
6942 BX_INFO(panic_msg_reg19h,0,0x1a);
6943 iobase = 0x24;
6946 // cylinders
6947 cylinders = inb_cmos(iobase) | (inb_cmos(iobase+1) << 8);
6948 write_word(ss, hd_cylinders, cylinders);
6950 // heads
6951 write_byte(ss, hd_heads, inb_cmos(iobase+2));
6953 // sectors per track
6954 write_byte(ss, hd_sectors, inb_cmos(iobase+8));
6957 #endif //else BX_USE_ATADRV
6959 #if BX_SUPPORT_FLOPPY
6961 //////////////////////
6962 // FLOPPY functions //
6963 //////////////////////
6965 void floppy_reset_controller()
6967 Bit8u val8;
6969 // Reset controller
6970 val8 = inb(0x03f2);
6971 outb(0x03f2, val8 & ~0x04);
6972 outb(0x03f2, val8 | 0x04);
6974 // Wait for controller to come out of reset
6975 do {
6976 val8 = inb(0x3f4);
6977 } while ((val8 & 0xc0) != 0x80);
6980 void floppy_prepare_controller(drive)
6981 Bit16u drive;
6983 Bit8u val8, dor, prev_reset;
6985 // set 40:3e bit 7 to 0
6986 val8 = read_byte(0x0040, 0x003e);
6987 val8 &= 0x7f;
6988 write_byte(0x0040, 0x003e, val8);
6990 // turn on motor of selected drive, DMA & int enabled, normal operation
6991 prev_reset = inb(0x03f2) & 0x04;
6992 if (drive)
6993 dor = 0x20;
6994 else
6995 dor = 0x10;
6996 dor |= 0x0c;
6997 dor |= drive;
6998 outb(0x03f2, dor);
7000 // reset the disk motor timeout value of INT 08
7001 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
7003 // wait for drive readiness
7004 do {
7005 val8 = inb(0x3f4);
7006 } while ( (val8 & 0xc0) != 0x80 );
7008 if (prev_reset == 0) {
7009 // turn on interrupts
7010 ASM_START
7012 ASM_END
7013 // wait on 40:3e bit 7 to become 1
7014 do {
7015 val8 = read_byte(0x0040, 0x003e);
7016 } while ( (val8 & 0x80) == 0 );
7017 val8 &= 0x7f;
7018 ASM_START
7020 ASM_END
7021 write_byte(0x0040, 0x003e, val8);
7025 bx_bool
7026 floppy_media_known(drive)
7027 Bit16u drive;
7029 Bit8u val8;
7030 Bit16u media_state_offset;
7032 val8 = read_byte(0x0040, 0x003e); // diskette recal status
7033 if (drive)
7034 val8 >>= 1;
7035 val8 &= 0x01;
7036 if (val8 == 0)
7037 return(0);
7039 media_state_offset = 0x0090;
7040 if (drive)
7041 media_state_offset += 1;
7043 val8 = read_byte(0x0040, media_state_offset);
7044 val8 = (val8 >> 4) & 0x01;
7045 if (val8 == 0)
7046 return(0);
7048 // check pass, return KNOWN
7049 return(1);
7052 bx_bool
7053 floppy_media_sense(drive)
7054 Bit16u drive;
7056 bx_bool retval;
7057 Bit16u media_state_offset;
7058 Bit8u drive_type, config_data, media_state;
7060 if (floppy_drive_recal(drive) == 0) {
7061 return(0);
7064 // for now cheat and get drive type from CMOS,
7065 // assume media is same as drive type
7067 // ** config_data **
7068 // Bitfields for diskette media control:
7069 // Bit(s) Description (Table M0028)
7070 // 7-6 last data rate set by controller
7071 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
7072 // 5-4 last diskette drive step rate selected
7073 // 00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
7074 // 3-2 {data rate at start of operation}
7075 // 1-0 reserved
7077 // ** media_state **
7078 // Bitfields for diskette drive media state:
7079 // Bit(s) Description (Table M0030)
7080 // 7-6 data rate
7081 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
7082 // 5 double stepping required (e.g. 360kB in 1.2MB)
7083 // 4 media type established
7084 // 3 drive capable of supporting 4MB media
7085 // 2-0 on exit from BIOS, contains
7086 // 000 trying 360kB in 360kB
7087 // 001 trying 360kB in 1.2MB
7088 // 010 trying 1.2MB in 1.2MB
7089 // 011 360kB in 360kB established
7090 // 100 360kB in 1.2MB established
7091 // 101 1.2MB in 1.2MB established
7092 // 110 reserved
7093 // 111 all other formats/drives
7095 drive_type = inb_cmos(0x10);
7097 if (drive == 0)
7098 drive_type >>= 4;
7099 else
7100 drive_type &= 0x0f;
7102 if (drive_type == 1) {
7103 // 360K 5.25" drive
7104 config_data = 0x00; // 0000 0000
7105 media_state = 0x25; // 0010 0101
7106 retval = 1;
7108 else if (drive_type == 2) {
7109 // 1.2 MB 5.25" drive
7110 config_data = 0x00; // 0000 0000
7111 media_state = 0x25; // 0010 0101 // need double stepping??? (bit 5)
7112 retval = 1;
7114 else if (drive_type == 3) {
7115 // 720K 3.5" drive
7116 config_data = 0x00; // 0000 0000 ???
7117 media_state = 0x17; // 0001 0111
7118 retval = 1;
7120 else if (drive_type == 4) {
7121 // 1.44 MB 3.5" drive
7122 config_data = 0x00; // 0000 0000
7123 media_state = 0x17; // 0001 0111
7124 retval = 1;
7126 else if (drive_type == 5) {
7127 // 2.88 MB 3.5" drive
7128 config_data = 0xCC; // 1100 1100
7129 media_state = 0xD7; // 1101 0111
7130 retval = 1;
7132 // Extended floppy size uses special cmos setting
7133 else if (drive_type == 6) {
7134 // 160k 5.25" drive
7135 config_data = 0x00; // 0000 0000
7136 media_state = 0x27; // 0010 0111
7137 retval = 1;
7139 else if (drive_type == 7) {
7140 // 180k 5.25" drive
7141 config_data = 0x00; // 0000 0000
7142 media_state = 0x27; // 0010 0111
7143 retval = 1;
7145 else if (drive_type == 8) {
7146 // 320k 5.25" drive
7147 config_data = 0x00; // 0000 0000
7148 media_state = 0x27; // 0010 0111
7149 retval = 1;
7151 else {
7152 // not recognized
7153 config_data = 0x00; // 0000 0000
7154 media_state = 0x00; // 0000 0000
7155 retval = 0;
7158 if (drive == 0)
7159 media_state_offset = 0x90;
7160 else
7161 media_state_offset = 0x91;
7162 write_byte(0x0040, 0x008B, config_data);
7163 write_byte(0x0040, media_state_offset, media_state);
7165 return(retval);
7168 bx_bool
7169 floppy_drive_recal(drive)
7170 Bit16u drive;
7172 Bit8u val8;
7173 Bit16u curr_cyl_offset;
7175 floppy_prepare_controller(drive);
7177 // send Recalibrate command (2 bytes) to controller
7178 outb(0x03f5, 0x07); // 07: Recalibrate
7179 outb(0x03f5, drive); // 0=drive0, 1=drive1
7181 // turn on interrupts
7182 ASM_START
7184 ASM_END
7186 // wait on 40:3e bit 7 to become 1
7187 do {
7188 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7189 } while ( val8 == 0 );
7191 val8 = 0; // separate asm from while() loop
7192 // turn off interrupts
7193 ASM_START
7195 ASM_END
7197 // set 40:3e bit 7 to 0, and calibrated bit
7198 val8 = read_byte(0x0040, 0x003e);
7199 val8 &= 0x7f;
7200 if (drive) {
7201 val8 |= 0x02; // Drive 1 calibrated
7202 curr_cyl_offset = 0x0095;
7203 } else {
7204 val8 |= 0x01; // Drive 0 calibrated
7205 curr_cyl_offset = 0x0094;
7207 write_byte(0x0040, 0x003e, val8);
7208 write_byte(0x0040, curr_cyl_offset, 0); // current cylinder is 0
7210 return(1);
7213 bx_bool
7214 floppy_drive_exists(drive)
7215 Bit16u drive;
7217 Bit8u drive_type;
7219 // check CMOS to see if drive exists
7220 drive_type = inb_cmos(0x10);
7221 if (drive == 0)
7222 drive_type >>= 4;
7223 else
7224 drive_type &= 0x0f;
7225 if ( drive_type == 0 )
7226 return(0);
7227 else
7228 return(1);
7231 void
7232 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
7233 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
7235 Bit8u drive, num_sectors, track, sector, head, status;
7236 Bit16u base_address, base_count, base_es;
7237 Bit8u page, mode_register, val8, dor;
7238 Bit8u return_status[7];
7239 Bit8u drive_type, num_floppies, ah;
7240 Bit16u es, last_addr;
7242 BX_DEBUG_INT13_FL("int13_diskette: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
7244 ah = GET_AH();
7246 switch ( ah ) {
7247 case 0x00: // diskette controller reset
7248 BX_DEBUG_INT13_FL("floppy f00\n");
7249 drive = GET_ELDL();
7250 if (drive > 1) {
7251 SET_AH(1); // invalid param
7252 set_diskette_ret_status(1);
7253 SET_CF();
7254 return;
7256 drive_type = inb_cmos(0x10);
7258 if (drive == 0)
7259 drive_type >>= 4;
7260 else
7261 drive_type &= 0x0f;
7262 if (drive_type == 0) {
7263 SET_AH(0x80); // drive not responding
7264 set_diskette_ret_status(0x80);
7265 SET_CF();
7266 return;
7268 SET_AH(0);
7269 set_diskette_ret_status(0);
7270 CLEAR_CF(); // successful
7271 set_diskette_current_cyl(drive, 0); // current cylinder
7272 return;
7274 case 0x01: // Read Diskette Status
7275 CLEAR_CF();
7276 val8 = read_byte(0x0000, 0x0441);
7277 SET_AH(val8);
7278 if (val8) {
7279 SET_CF();
7281 return;
7283 case 0x02: // Read Diskette Sectors
7284 case 0x03: // Write Diskette Sectors
7285 case 0x04: // Verify Diskette Sectors
7286 num_sectors = GET_AL();
7287 track = GET_CH();
7288 sector = GET_CL();
7289 head = GET_DH();
7290 drive = GET_ELDL();
7292 if ((drive > 1) || (head > 1) || (sector == 0) ||
7293 (num_sectors == 0) || (num_sectors > 72)) {
7294 BX_INFO("int13_diskette: read/write/verify: parameter out of range\n");
7295 SET_AH(1);
7296 set_diskette_ret_status(1);
7297 SET_AL(0); // no sectors read
7298 SET_CF(); // error occurred
7299 return;
7302 // see if drive exists
7303 if (floppy_drive_exists(drive) == 0) {
7304 SET_AH(0x80); // not responding
7305 set_diskette_ret_status(0x80);
7306 SET_AL(0); // no sectors read
7307 SET_CF(); // error occurred
7308 return;
7311 // see if media in drive, and type is known
7312 if (floppy_media_known(drive) == 0) {
7313 if (floppy_media_sense(drive) == 0) {
7314 SET_AH(0x0C); // Media type not found
7315 set_diskette_ret_status(0x0C);
7316 SET_AL(0); // no sectors read
7317 SET_CF(); // error occurred
7318 return;
7322 if (ah == 0x02) {
7323 // Read Diskette Sectors
7325 //-----------------------------------
7326 // set up DMA controller for transfer
7327 //-----------------------------------
7329 // es:bx = pointer to where to place information from diskette
7330 // port 04: DMA-1 base and current address, channel 2
7331 // port 05: DMA-1 base and current count, channel 2
7332 page = (ES >> 12); // upper 4 bits
7333 base_es = (ES << 4); // lower 16bits contributed by ES
7334 base_address = base_es + BX; // lower 16 bits of address
7335 // contributed by ES:BX
7336 if ( base_address < base_es ) {
7337 // in case of carry, adjust page by 1
7338 page++;
7340 base_count = (num_sectors * 512) - 1;
7342 // check for 64K boundary overrun
7343 last_addr = base_address + base_count;
7344 if (last_addr < base_address) {
7345 SET_AH(0x09);
7346 set_diskette_ret_status(0x09);
7347 SET_AL(0); // no sectors read
7348 SET_CF(); // error occurred
7349 return;
7352 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
7353 outb(0x000a, 0x06);
7355 BX_DEBUG_INT13_FL("clear flip-flop\n");
7356 outb(0x000c, 0x00); // clear flip-flop
7357 outb(0x0004, base_address);
7358 outb(0x0004, base_address>>8);
7359 BX_DEBUG_INT13_FL("clear flip-flop\n");
7360 outb(0x000c, 0x00); // clear flip-flop
7361 outb(0x0005, base_count);
7362 outb(0x0005, base_count>>8);
7364 // port 0b: DMA-1 Mode Register
7365 mode_register = 0x46; // single mode, increment, autoinit disable,
7366 // transfer type=write, channel 2
7367 BX_DEBUG_INT13_FL("setting mode register\n");
7368 outb(0x000b, mode_register);
7370 BX_DEBUG_INT13_FL("setting page register\n");
7371 // port 81: DMA-1 Page Register, channel 2
7372 outb(0x0081, page);
7374 BX_DEBUG_INT13_FL("unmask chan 2\n");
7375 outb(0x000a, 0x02); // unmask channel 2
7377 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
7378 outb(0x000a, 0x02);
7380 //--------------------------------------
7381 // set up floppy controller for transfer
7382 //--------------------------------------
7383 floppy_prepare_controller(drive);
7385 // send read-normal-data command (9 bytes) to controller
7386 outb(0x03f5, 0xe6); // e6: read normal data
7387 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7388 outb(0x03f5, track);
7389 outb(0x03f5, head);
7390 outb(0x03f5, sector);
7391 outb(0x03f5, 2); // 512 byte sector size
7392 outb(0x03f5, sector + num_sectors - 1); // last sector to read on track
7393 outb(0x03f5, 0); // Gap length
7394 outb(0x03f5, 0xff); // Gap length
7396 // turn on interrupts
7397 ASM_START
7399 ASM_END
7401 // wait on 40:3e bit 7 to become 1
7402 do {
7403 val8 = read_byte(0x0040, 0x0040);
7404 if (val8 == 0) {
7405 floppy_reset_controller();
7406 SET_AH(0x80); // drive not ready (timeout)
7407 set_diskette_ret_status(0x80);
7408 SET_AL(0); // no sectors read
7409 SET_CF(); // error occurred
7410 return;
7412 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7413 } while ( val8 == 0 );
7415 val8 = 0; // separate asm from while() loop
7416 // turn off interrupts
7417 ASM_START
7419 ASM_END
7421 // set 40:3e bit 7 to 0
7422 val8 = read_byte(0x0040, 0x003e);
7423 val8 &= 0x7f;
7424 write_byte(0x0040, 0x003e, val8);
7426 // check port 3f4 for accessibility to status bytes
7427 val8 = inb(0x3f4);
7428 if ( (val8 & 0xc0) != 0xc0 )
7429 BX_PANIC("int13_diskette: ctrl not ready\n");
7431 // read 7 return status bytes from controller
7432 // using loop index broken, have to unroll...
7433 return_status[0] = inb(0x3f5);
7434 return_status[1] = inb(0x3f5);
7435 return_status[2] = inb(0x3f5);
7436 return_status[3] = inb(0x3f5);
7437 return_status[4] = inb(0x3f5);
7438 return_status[5] = inb(0x3f5);
7439 return_status[6] = inb(0x3f5);
7440 // record in BIOS Data Area
7441 write_byte(0x0040, 0x0042, return_status[0]);
7442 write_byte(0x0040, 0x0043, return_status[1]);
7443 write_byte(0x0040, 0x0044, return_status[2]);
7444 write_byte(0x0040, 0x0045, return_status[3]);
7445 write_byte(0x0040, 0x0046, return_status[4]);
7446 write_byte(0x0040, 0x0047, return_status[5]);
7447 write_byte(0x0040, 0x0048, return_status[6]);
7449 if ( (return_status[0] & 0xc0) != 0 ) {
7450 SET_AH(0x20);
7451 set_diskette_ret_status(0x20);
7452 SET_AL(0); // no sectors read
7453 SET_CF(); // error occurred
7454 return;
7457 // ??? should track be new val from return_status[3] ?
7458 set_diskette_current_cyl(drive, track);
7459 // AL = number of sectors read (same value as passed)
7460 SET_AH(0x00); // success
7461 CLEAR_CF(); // success
7462 return;
7463 } else if (ah == 0x03) {
7464 // Write Diskette Sectors
7466 //-----------------------------------
7467 // set up DMA controller for transfer
7468 //-----------------------------------
7470 // es:bx = pointer to where to place information from diskette
7471 // port 04: DMA-1 base and current address, channel 2
7472 // port 05: DMA-1 base and current count, channel 2
7473 page = (ES >> 12); // upper 4 bits
7474 base_es = (ES << 4); // lower 16bits contributed by ES
7475 base_address = base_es + BX; // lower 16 bits of address
7476 // contributed by ES:BX
7477 if ( base_address < base_es ) {
7478 // in case of carry, adjust page by 1
7479 page++;
7481 base_count = (num_sectors * 512) - 1;
7483 // check for 64K boundary overrun
7484 last_addr = base_address + base_count;
7485 if (last_addr < base_address) {
7486 SET_AH(0x09);
7487 set_diskette_ret_status(0x09);
7488 SET_AL(0); // no sectors read
7489 SET_CF(); // error occurred
7490 return;
7493 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
7494 outb(0x000a, 0x06);
7496 outb(0x000c, 0x00); // clear flip-flop
7497 outb(0x0004, base_address);
7498 outb(0x0004, base_address>>8);
7499 outb(0x000c, 0x00); // clear flip-flop
7500 outb(0x0005, base_count);
7501 outb(0x0005, base_count>>8);
7503 // port 0b: DMA-1 Mode Register
7504 mode_register = 0x4a; // single mode, increment, autoinit disable,
7505 // transfer type=read, channel 2
7506 outb(0x000b, mode_register);
7508 // port 81: DMA-1 Page Register, channel 2
7509 outb(0x0081, page);
7511 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
7512 outb(0x000a, 0x02);
7514 //--------------------------------------
7515 // set up floppy controller for transfer
7516 //--------------------------------------
7517 floppy_prepare_controller(drive);
7519 // send write-normal-data command (9 bytes) to controller
7520 outb(0x03f5, 0xc5); // c5: write normal data
7521 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7522 outb(0x03f5, track);
7523 outb(0x03f5, head);
7524 outb(0x03f5, sector);
7525 outb(0x03f5, 2); // 512 byte sector size
7526 outb(0x03f5, sector + num_sectors - 1); // last sector to write on track
7527 outb(0x03f5, 0); // Gap length
7528 outb(0x03f5, 0xff); // Gap length
7530 // turn on interrupts
7531 ASM_START
7533 ASM_END
7535 // wait on 40:3e bit 7 to become 1
7536 do {
7537 val8 = read_byte(0x0040, 0x0040);
7538 if (val8 == 0) {
7539 floppy_reset_controller();
7540 SET_AH(0x80); // drive not ready (timeout)
7541 set_diskette_ret_status(0x80);
7542 SET_AL(0); // no sectors written
7543 SET_CF(); // error occurred
7544 return;
7546 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7547 } while ( val8 == 0 );
7549 val8 = 0; // separate asm from while() loop
7550 // turn off interrupts
7551 ASM_START
7553 ASM_END
7555 // set 40:3e bit 7 to 0
7556 val8 = read_byte(0x0040, 0x003e);
7557 val8 &= 0x7f;
7558 write_byte(0x0040, 0x003e, val8);
7560 // check port 3f4 for accessibility to status bytes
7561 val8 = inb(0x3f4);
7562 if ( (val8 & 0xc0) != 0xc0 )
7563 BX_PANIC("int13_diskette: ctrl not ready\n");
7565 // read 7 return status bytes from controller
7566 // using loop index broken, have to unroll...
7567 return_status[0] = inb(0x3f5);
7568 return_status[1] = inb(0x3f5);
7569 return_status[2] = inb(0x3f5);
7570 return_status[3] = inb(0x3f5);
7571 return_status[4] = inb(0x3f5);
7572 return_status[5] = inb(0x3f5);
7573 return_status[6] = inb(0x3f5);
7574 // record in BIOS Data Area
7575 write_byte(0x0040, 0x0042, return_status[0]);
7576 write_byte(0x0040, 0x0043, return_status[1]);
7577 write_byte(0x0040, 0x0044, return_status[2]);
7578 write_byte(0x0040, 0x0045, return_status[3]);
7579 write_byte(0x0040, 0x0046, return_status[4]);
7580 write_byte(0x0040, 0x0047, return_status[5]);
7581 write_byte(0x0040, 0x0048, return_status[6]);
7583 if ( (return_status[0] & 0xc0) != 0 ) {
7584 if ( (return_status[1] & 0x02) != 0 ) {
7585 // diskette not writable.
7586 // AH=status code=0x03 (tried to write on write-protected disk)
7587 // AL=number of sectors written=0
7588 AX = 0x0300;
7589 SET_CF();
7590 return;
7591 } else {
7592 BX_PANIC("int13_diskette_function: read error\n");
7596 // ??? should track be new val from return_status[3] ?
7597 set_diskette_current_cyl(drive, track);
7598 // AL = number of sectors read (same value as passed)
7599 SET_AH(0x00); // success
7600 CLEAR_CF(); // success
7601 return;
7602 } else { // if (ah == 0x04)
7603 // Verify Diskette Sectors
7605 // ??? should track be new val from return_status[3] ?
7606 set_diskette_current_cyl(drive, track);
7607 // AL = number of sectors verified (same value as passed)
7608 CLEAR_CF(); // success
7609 SET_AH(0x00); // success
7610 return;
7612 break;
7614 case 0x05: // format diskette track
7615 BX_DEBUG_INT13_FL("floppy f05\n");
7617 num_sectors = GET_AL();
7618 track = GET_CH();
7619 head = GET_DH();
7620 drive = GET_ELDL();
7622 if ((drive > 1) || (head > 1) || (track > 79) ||
7623 (num_sectors == 0) || (num_sectors > 18)) {
7624 SET_AH(1);
7625 set_diskette_ret_status(1);
7626 SET_CF(); // error occurred
7629 // see if drive exists
7630 if (floppy_drive_exists(drive) == 0) {
7631 SET_AH(0x80); // drive not responding
7632 set_diskette_ret_status(0x80);
7633 SET_CF(); // error occurred
7634 return;
7637 // see if media in drive, and type is known
7638 if (floppy_media_known(drive) == 0) {
7639 if (floppy_media_sense(drive) == 0) {
7640 SET_AH(0x0C); // Media type not found
7641 set_diskette_ret_status(0x0C);
7642 SET_AL(0); // no sectors read
7643 SET_CF(); // error occurred
7644 return;
7648 // set up DMA controller for transfer
7649 page = (ES >> 12); // upper 4 bits
7650 base_es = (ES << 4); // lower 16bits contributed by ES
7651 base_address = base_es + BX; // lower 16 bits of address
7652 // contributed by ES:BX
7653 if ( base_address < base_es ) {
7654 // in case of carry, adjust page by 1
7655 page++;
7657 base_count = (num_sectors * 4) - 1;
7659 // check for 64K boundary overrun
7660 last_addr = base_address + base_count;
7661 if (last_addr < base_address) {
7662 SET_AH(0x09);
7663 set_diskette_ret_status(0x09);
7664 SET_AL(0); // no sectors read
7665 SET_CF(); // error occurred
7666 return;
7669 outb(0x000a, 0x06);
7670 outb(0x000c, 0x00); // clear flip-flop
7671 outb(0x0004, base_address);
7672 outb(0x0004, base_address>>8);
7673 outb(0x000c, 0x00); // clear flip-flop
7674 outb(0x0005, base_count);
7675 outb(0x0005, base_count>>8);
7676 mode_register = 0x4a; // single mode, increment, autoinit disable,
7677 // transfer type=read, channel 2
7678 outb(0x000b, mode_register);
7679 // port 81: DMA-1 Page Register, channel 2
7680 outb(0x0081, page);
7681 outb(0x000a, 0x02);
7683 // set up floppy controller for transfer
7684 floppy_prepare_controller(drive);
7686 // send format-track command (6 bytes) to controller
7687 outb(0x03f5, 0x4d); // 4d: format track
7688 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7689 outb(0x03f5, 2); // 512 byte sector size
7690 outb(0x03f5, num_sectors); // number of sectors per track
7691 outb(0x03f5, 0); // Gap length
7692 outb(0x03f5, 0xf6); // Fill byte
7693 // turn on interrupts
7694 ASM_START
7696 ASM_END
7698 // wait on 40:3e bit 7 to become 1
7699 do {
7700 val8 = read_byte(0x0040, 0x0040);
7701 if (val8 == 0) {
7702 floppy_reset_controller();
7703 SET_AH(0x80); // drive not ready (timeout)
7704 set_diskette_ret_status(0x80);
7705 SET_CF(); // error occurred
7706 return;
7708 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7709 } while ( val8 == 0 );
7711 val8 = 0; // separate asm from while() loop
7712 // turn off interrupts
7713 ASM_START
7715 ASM_END
7716 // set 40:3e bit 7 to 0
7717 val8 = read_byte(0x0040, 0x003e);
7718 val8 &= 0x7f;
7719 write_byte(0x0040, 0x003e, val8);
7720 // check port 3f4 for accessibility to status bytes
7721 val8 = inb(0x3f4);
7722 if ( (val8 & 0xc0) != 0xc0 )
7723 BX_PANIC("int13_diskette: ctrl not ready\n");
7725 // read 7 return status bytes from controller
7726 // using loop index broken, have to unroll...
7727 return_status[0] = inb(0x3f5);
7728 return_status[1] = inb(0x3f5);
7729 return_status[2] = inb(0x3f5);
7730 return_status[3] = inb(0x3f5);
7731 return_status[4] = inb(0x3f5);
7732 return_status[5] = inb(0x3f5);
7733 return_status[6] = inb(0x3f5);
7734 // record in BIOS Data Area
7735 write_byte(0x0040, 0x0042, return_status[0]);
7736 write_byte(0x0040, 0x0043, return_status[1]);
7737 write_byte(0x0040, 0x0044, return_status[2]);
7738 write_byte(0x0040, 0x0045, return_status[3]);
7739 write_byte(0x0040, 0x0046, return_status[4]);
7740 write_byte(0x0040, 0x0047, return_status[5]);
7741 write_byte(0x0040, 0x0048, return_status[6]);
7743 if ( (return_status[0] & 0xc0) != 0 ) {
7744 if ( (return_status[1] & 0x02) != 0 ) {
7745 // diskette not writable.
7746 // AH=status code=0x03 (tried to write on write-protected disk)
7747 // AL=number of sectors written=0
7748 AX = 0x0300;
7749 SET_CF();
7750 return;
7751 } else {
7752 BX_PANIC("int13_diskette_function: write error\n");
7756 SET_AH(0);
7757 set_diskette_ret_status(0);
7758 set_diskette_current_cyl(drive, 0);
7759 CLEAR_CF(); // successful
7760 return;
7763 case 0x08: // read diskette drive parameters
7764 BX_DEBUG_INT13_FL("floppy f08\n");
7765 drive = GET_ELDL();
7767 if (drive > 1) {
7768 AX = 0;
7769 BX = 0;
7770 CX = 0;
7771 DX = 0;
7772 ES = 0;
7773 DI = 0;
7774 SET_DL(num_floppies);
7775 SET_CF();
7776 return;
7779 drive_type = inb_cmos(0x10);
7780 num_floppies = 0;
7781 if (drive_type & 0xf0)
7782 num_floppies++;
7783 if (drive_type & 0x0f)
7784 num_floppies++;
7786 if (drive == 0)
7787 drive_type >>= 4;
7788 else
7789 drive_type &= 0x0f;
7791 SET_BH(0);
7792 SET_BL(drive_type);
7793 SET_AH(0);
7794 SET_AL(0);
7795 SET_DL(num_floppies);
7797 switch (drive_type) {
7798 case 0: // none
7799 CX = 0;
7800 SET_DH(0); // max head #
7801 break;
7803 case 1: // 360KB, 5.25"
7804 CX = 0x2709; // 40 tracks, 9 sectors
7805 SET_DH(1); // max head #
7806 break;
7808 case 2: // 1.2MB, 5.25"
7809 CX = 0x4f0f; // 80 tracks, 15 sectors
7810 SET_DH(1); // max head #
7811 break;
7813 case 3: // 720KB, 3.5"
7814 CX = 0x4f09; // 80 tracks, 9 sectors
7815 SET_DH(1); // max head #
7816 break;
7818 case 4: // 1.44MB, 3.5"
7819 CX = 0x4f12; // 80 tracks, 18 sectors
7820 SET_DH(1); // max head #
7821 break;
7823 case 5: // 2.88MB, 3.5"
7824 CX = 0x4f24; // 80 tracks, 36 sectors
7825 SET_DH(1); // max head #
7826 break;
7828 case 6: // 160k, 5.25"
7829 CX = 0x2708; // 40 tracks, 8 sectors
7830 SET_DH(0); // max head #
7831 break;
7833 case 7: // 180k, 5.25"
7834 CX = 0x2709; // 40 tracks, 9 sectors
7835 SET_DH(0); // max head #
7836 break;
7838 case 8: // 320k, 5.25"
7839 CX = 0x2708; // 40 tracks, 8 sectors
7840 SET_DH(1); // max head #
7841 break;
7843 default: // ?
7844 BX_PANIC("floppy: int13: bad floppy type\n");
7847 /* set es & di to point to 11 byte diskette param table in ROM */
7848 ASM_START
7849 push bp
7850 mov bp, sp
7851 mov ax, #diskette_param_table2
7852 mov _int13_diskette_function.DI+2[bp], ax
7853 mov _int13_diskette_function.ES+2[bp], cs
7854 pop bp
7855 ASM_END
7856 CLEAR_CF(); // success
7857 /* disk status not changed upon success */
7858 return;
7861 case 0x15: // read diskette drive type
7862 BX_DEBUG_INT13_FL("floppy f15\n");
7863 drive = GET_ELDL();
7864 if (drive > 1) {
7865 SET_AH(0); // only 2 drives supported
7866 // set_diskette_ret_status here ???
7867 SET_CF();
7868 return;
7870 drive_type = inb_cmos(0x10);
7872 if (drive == 0)
7873 drive_type >>= 4;
7874 else
7875 drive_type &= 0x0f;
7876 CLEAR_CF(); // successful, not present
7877 if (drive_type==0) {
7878 SET_AH(0); // drive not present
7880 else {
7881 SET_AH(1); // drive present, does not support change line
7884 return;
7886 case 0x16: // get diskette change line status
7887 BX_DEBUG_INT13_FL("floppy f16\n");
7888 drive = GET_ELDL();
7889 if (drive > 1) {
7890 SET_AH(0x01); // invalid drive
7891 set_diskette_ret_status(0x01);
7892 SET_CF();
7893 return;
7896 SET_AH(0x06); // change line not supported
7897 set_diskette_ret_status(0x06);
7898 SET_CF();
7899 return;
7901 case 0x17: // set diskette type for format(old)
7902 BX_DEBUG_INT13_FL("floppy f17\n");
7903 /* not used for 1.44M floppies */
7904 SET_AH(0x01); // not supported
7905 set_diskette_ret_status(1); /* not supported */
7906 SET_CF();
7907 return;
7909 case 0x18: // set diskette type for format(new)
7910 BX_DEBUG_INT13_FL("floppy f18\n");
7911 SET_AH(0x01); // do later
7912 set_diskette_ret_status(1);
7913 SET_CF();
7914 return;
7916 default:
7917 BX_INFO("int13_diskette: unsupported AH=%02x\n", GET_AH());
7919 // if ((ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e)) {
7920 SET_AH(0x01); // ???
7921 set_diskette_ret_status(1);
7922 SET_CF();
7923 return;
7924 // }
7927 #else // #if BX_SUPPORT_FLOPPY
7928 void
7929 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
7930 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
7932 Bit8u val8;
7934 switch (GET_AH()) {
7936 case 0x01: // Read Diskette Status
7937 CLEAR_CF();
7938 val8 = read_byte(0x0000, 0x0441);
7939 SET_AH(val8);
7940 if (val8) {
7941 SET_CF();
7943 return;
7945 default:
7946 SET_CF();
7947 write_byte(0x0000, 0x0441, 0x01);
7948 SET_AH(0x01);
7951 #endif // #if BX_SUPPORT_FLOPPY
7953 void
7954 set_diskette_ret_status(value)
7955 Bit8u value;
7957 write_byte(0x0040, 0x0041, value);
7960 void
7961 set_diskette_current_cyl(drive, cyl)
7962 Bit8u drive;
7963 Bit8u cyl;
7965 if (drive > 1)
7966 BX_PANIC("set_diskette_current_cyl(): drive > 1\n");
7967 write_byte(0x0040, 0x0094+drive, cyl);
7970 void
7971 determine_floppy_media(drive)
7972 Bit16u drive;
7974 #if 0
7975 Bit8u val8, DOR, ctrl_info;
7977 ctrl_info = read_byte(0x0040, 0x008F);
7978 if (drive==1)
7979 ctrl_info >>= 4;
7980 else
7981 ctrl_info &= 0x0f;
7983 #if 0
7984 if (drive == 0) {
7985 DOR = 0x1c; // DOR: drive0 motor on, DMA&int enabled, normal op, drive select 0
7987 else {
7988 DOR = 0x2d; // DOR: drive1 motor on, DMA&int enabled, normal op, drive select 1
7990 #endif
7992 if ((ctrl_info & 0x04) != 0x04) {
7993 // Drive not determined means no drive exists, done.
7994 return;
7997 #if 0
7998 // check Main Status Register for readiness
7999 val8 = inb(0x03f4) & 0x80; // Main Status Register
8000 if (val8 != 0x80)
8001 BX_PANIC("d_f_m: MRQ bit not set\n");
8003 // change line
8005 // existing BDA values
8007 // turn on drive motor
8008 outb(0x03f2, DOR); // Digital Output Register
8010 #endif
8011 BX_PANIC("d_f_m: OK so far\n");
8012 #endif
8015 void
8016 int17_function(regs, ds, iret_addr)
8017 pusha_regs_t regs; // regs pushed from PUSHA instruction
8018 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
8019 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
8021 Bit16u addr,timeout;
8022 Bit8u val8;
8024 ASM_START
8026 ASM_END
8028 addr = read_word(0x0040, (regs.u.r16.dx << 1) + 8);
8029 if ((regs.u.r8.ah < 3) && (regs.u.r16.dx < 3) && (addr > 0)) {
8030 timeout = read_byte(0x0040, 0x0078 + regs.u.r16.dx) << 8;
8031 if (regs.u.r8.ah == 0) {
8032 outb(addr, regs.u.r8.al);
8033 val8 = inb(addr+2);
8034 outb(addr+2, val8 | 0x01); // send strobe
8035 ASM_START
8037 ASM_END
8038 outb(addr+2, val8 & ~0x01);
8039 while (((inb(addr+1) & 0x40) == 0x40) && (timeout)) {
8040 timeout--;
8043 if (regs.u.r8.ah == 1) {
8044 val8 = inb(addr+2);
8045 outb(addr+2, val8 & ~0x04); // send init
8046 ASM_START
8048 ASM_END
8049 outb(addr+2, val8 | 0x04);
8051 val8 = inb(addr+1);
8052 regs.u.r8.ah = (val8 ^ 0x48);
8053 if (!timeout) regs.u.r8.ah |= 0x01;
8054 ClearCF(iret_addr.flags);
8055 } else {
8056 SetCF(iret_addr.flags); // Unsupported
8060 void
8061 int19_function(seq_nr)
8062 Bit16u seq_nr;
8064 Bit16u ebda_seg=read_word(0x0040,0x000E);
8065 Bit16u bootdev;
8066 Bit8u bootdrv;
8067 Bit8u bootchk;
8068 Bit16u bootseg;
8069 Bit16u bootip;
8070 Bit16u status;
8071 Bit16u bootfirst;
8073 ipl_entry_t e;
8075 // if BX_ELTORITO_BOOT is not defined, old behavior
8076 // check bit 5 in CMOS reg 0x2d. load either 0x00 or 0x80 into DL
8077 // in preparation for the intial INT 13h (0=floppy A:, 0x80=C:)
8078 // 0: system boot sequence, first drive C: then A:
8079 // 1: system boot sequence, first drive A: then C:
8080 // else BX_ELTORITO_BOOT is defined
8081 // CMOS regs 0x3D and 0x38 contain the boot sequence:
8082 // CMOS reg 0x3D & 0x0f : 1st boot device
8083 // CMOS reg 0x3D & 0xf0 : 2nd boot device
8084 // CMOS reg 0x38 & 0xf0 : 3rd boot device
8085 // boot device codes:
8086 // 0x00 : not defined
8087 // 0x01 : first floppy
8088 // 0x02 : first harddrive
8089 // 0x03 : first cdrom
8090 // 0x04 - 0x0f : PnP expansion ROMs (e.g. Etherboot)
8091 // else : boot failure
8093 // Get the boot sequence
8094 #if BX_ELTORITO_BOOT
8095 bootdev = inb_cmos(0x3d);
8096 bootdev |= ((inb_cmos(0x38) & 0xf0) << 4);
8097 bootdev >>= 4 * seq_nr;
8098 bootdev &= 0xf;
8100 /* Read user selected device */
8101 bootfirst = read_word(IPL_SEG, IPL_BOOTFIRST_OFFSET);
8102 if (bootfirst != 0xFFFF) {
8103 bootdev = bootfirst;
8104 /* User selected device not set */
8105 write_word(IPL_SEG, IPL_BOOTFIRST_OFFSET, 0xFFFF);
8106 /* Reset boot sequence */
8107 write_word(IPL_SEG, IPL_SEQUENCE_OFFSET, 0xFFFF);
8108 } else if (bootdev == 0) BX_PANIC("No bootable device.\n");
8110 /* Translate from CMOS runes to an IPL table offset by subtracting 1 */
8111 bootdev -= 1;
8112 #else
8113 if (seq_nr ==2) BX_PANIC("No more boot devices.");
8114 if (!!(inb_cmos(0x2d) & 0x20) ^ (seq_nr == 1))
8115 /* Boot from floppy if the bit is set or it's the second boot */
8116 bootdev = 0x00;
8117 else
8118 bootdev = 0x01;
8119 #endif
8121 /* Read the boot device from the IPL table */
8122 if (get_boot_vector(bootdev, &e) == 0) {
8123 BX_INFO("Invalid boot device (0x%x)\n", bootdev);
8124 return;
8127 /* Do the loading, and set up vector as a far pointer to the boot
8128 * address, and bootdrv as the boot drive */
8129 print_boot_device(&e);
8131 switch(e.type) {
8132 case IPL_TYPE_FLOPPY: /* FDD */
8133 case IPL_TYPE_HARDDISK: /* HDD */
8135 bootdrv = (e.type == IPL_TYPE_HARDDISK) ? 0x80 : 0x00;
8136 bootseg = 0x07c0;
8137 status = 0;
8139 ASM_START
8140 push bp
8141 mov bp, sp
8142 push ax
8143 push bx
8144 push cx
8145 push dx
8147 mov dl, _int19_function.bootdrv + 2[bp]
8148 mov ax, _int19_function.bootseg + 2[bp]
8149 mov es, ax ;; segment
8150 xor bx, bx ;; offset
8151 mov ah, #0x02 ;; function 2, read diskette sector
8152 mov al, #0x01 ;; read 1 sector
8153 mov ch, #0x00 ;; track 0
8154 mov cl, #0x01 ;; sector 1
8155 mov dh, #0x00 ;; head 0
8156 int #0x13 ;; read sector
8157 jnc int19_load_done
8158 mov ax, #0x0001
8159 mov _int19_function.status + 2[bp], ax
8161 int19_load_done:
8162 pop dx
8163 pop cx
8164 pop bx
8165 pop ax
8166 pop bp
8167 ASM_END
8169 if (status != 0) {
8170 print_boot_failure(e.type, 1);
8171 return;
8174 /* Always check the signature on a HDD boot sector; on FDD, only do
8175 * the check if the CMOS doesn't tell us to skip it */
8176 if ((e.type != IPL_TYPE_FLOPPY) || !((inb_cmos(0x38) & 0x01))) {
8177 if (read_word(bootseg,0x1fe) != 0xaa55) {
8178 print_boot_failure(e.type, 0);
8179 return;
8183 /* Canonicalize bootseg:bootip */
8184 bootip = (bootseg & 0x0fff) << 4;
8185 bootseg &= 0xf000;
8186 break;
8188 #if BX_ELTORITO_BOOT
8189 case IPL_TYPE_CDROM: /* CD-ROM */
8190 status = cdrom_boot();
8192 // If failure
8193 if ( (status & 0x00ff) !=0 ) {
8194 print_cdromboot_failure(status);
8195 print_boot_failure(e.type, 1);
8196 return;
8199 bootdrv = (Bit8u)(status>>8);
8200 bootseg = read_word(ebda_seg,&EbdaData->cdemu.load_segment);
8201 bootip = 0;
8202 break;
8203 #endif
8205 case IPL_TYPE_BEV: /* Expansion ROM with a Bootstrap Entry Vector (a far pointer) */
8206 bootseg = e.vector >> 16;
8207 bootip = e.vector & 0xffff;
8208 break;
8210 default: return;
8213 /* Debugging info */
8214 BX_INFO("Booting from %x:%x\n", bootseg, bootip);
8216 /* Jump to the boot vector */
8217 ASM_START
8218 mov bp, sp
8219 push cs
8220 push #int18_handler
8221 ;; Build an iret stack frame that will take us to the boot vector.
8222 ;; iret pops ip, then cs, then flags, so push them in the opposite order.
8223 pushf
8224 mov ax, _int19_function.bootseg + 0[bp]
8225 push ax
8226 mov ax, _int19_function.bootip + 0[bp]
8227 push ax
8228 ;; Set the magic number in ax and the boot drive in dl.
8229 mov ax, #0xaa55
8230 mov dl, _int19_function.bootdrv + 0[bp]
8231 ;; Zero some of the other registers.
8232 xor bx, bx
8233 mov ds, bx
8234 mov es, bx
8235 mov bp, bx
8236 ;; Go!
8237 iret
8238 ASM_END
8241 void
8242 int1a_function(regs, ds, iret_addr)
8243 pusha_regs_t regs; // regs pushed from PUSHA instruction
8244 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
8245 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
8247 Bit8u val8;
8249 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);
8251 ASM_START
8253 ASM_END
8255 switch (regs.u.r8.ah) {
8256 case 0: // get current clock count
8257 ASM_START
8259 ASM_END
8260 regs.u.r16.cx = BiosData->ticks_high;
8261 regs.u.r16.dx = BiosData->ticks_low;
8262 regs.u.r8.al = BiosData->midnight_flag;
8263 BiosData->midnight_flag = 0; // reset flag
8264 ASM_START
8266 ASM_END
8267 // AH already 0
8268 ClearCF(iret_addr.flags); // OK
8269 break;
8271 case 1: // Set Current Clock Count
8272 ASM_START
8274 ASM_END
8275 BiosData->ticks_high = regs.u.r16.cx;
8276 BiosData->ticks_low = regs.u.r16.dx;
8277 BiosData->midnight_flag = 0; // reset flag
8278 ASM_START
8280 ASM_END
8281 regs.u.r8.ah = 0;
8282 ClearCF(iret_addr.flags); // OK
8283 break;
8286 case 2: // Read CMOS Time
8287 if (rtc_updating()) {
8288 SetCF(iret_addr.flags);
8289 break;
8292 regs.u.r8.dh = inb_cmos(0x00); // Seconds
8293 regs.u.r8.cl = inb_cmos(0x02); // Minutes
8294 regs.u.r8.ch = inb_cmos(0x04); // Hours
8295 regs.u.r8.dl = inb_cmos(0x0b) & 0x01; // Stat Reg B
8296 regs.u.r8.ah = 0;
8297 regs.u.r8.al = regs.u.r8.ch;
8298 ClearCF(iret_addr.flags); // OK
8299 break;
8301 case 3: // Set CMOS Time
8302 // Using a debugger, I notice the following masking/setting
8303 // of bits in Status Register B, by setting Reg B to
8304 // a few values and getting its value after INT 1A was called.
8306 // try#1 try#2 try#3
8307 // before 1111 1101 0111 1101 0000 0000
8308 // after 0110 0010 0110 0010 0000 0010
8310 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8311 // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
8312 if (rtc_updating()) {
8313 init_rtc();
8314 // fall through as if an update were not in progress
8316 outb_cmos(0x00, regs.u.r8.dh); // Seconds
8317 outb_cmos(0x02, regs.u.r8.cl); // Minutes
8318 outb_cmos(0x04, regs.u.r8.ch); // Hours
8319 // Set Daylight Savings time enabled bit to requested value
8320 val8 = (inb_cmos(0x0b) & 0x60) | 0x02 | (regs.u.r8.dl & 0x01);
8321 // (reg B already selected)
8322 outb_cmos(0x0b, val8);
8323 regs.u.r8.ah = 0;
8324 regs.u.r8.al = val8; // val last written to Reg B
8325 ClearCF(iret_addr.flags); // OK
8326 break;
8328 case 4: // Read CMOS Date
8329 regs.u.r8.ah = 0;
8330 if (rtc_updating()) {
8331 SetCF(iret_addr.flags);
8332 break;
8334 regs.u.r8.cl = inb_cmos(0x09); // Year
8335 regs.u.r8.dh = inb_cmos(0x08); // Month
8336 regs.u.r8.dl = inb_cmos(0x07); // Day of Month
8337 regs.u.r8.ch = inb_cmos(0x32); // Century
8338 regs.u.r8.al = regs.u.r8.ch;
8339 ClearCF(iret_addr.flags); // OK
8340 break;
8342 case 5: // Set CMOS Date
8343 // Using a debugger, I notice the following masking/setting
8344 // of bits in Status Register B, by setting Reg B to
8345 // a few values and getting its value after INT 1A was called.
8347 // try#1 try#2 try#3 try#4
8348 // before 1111 1101 0111 1101 0000 0010 0000 0000
8349 // after 0110 1101 0111 1101 0000 0010 0000 0000
8351 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8352 // My assumption: RegB = (RegB & 01111111b)
8353 if (rtc_updating()) {
8354 init_rtc();
8355 SetCF(iret_addr.flags);
8356 break;
8358 outb_cmos(0x09, regs.u.r8.cl); // Year
8359 outb_cmos(0x08, regs.u.r8.dh); // Month
8360 outb_cmos(0x07, regs.u.r8.dl); // Day of Month
8361 outb_cmos(0x32, regs.u.r8.ch); // Century
8362 val8 = inb_cmos(0x0b) & 0x7f; // clear halt-clock bit
8363 outb_cmos(0x0b, val8);
8364 regs.u.r8.ah = 0;
8365 regs.u.r8.al = val8; // AL = val last written to Reg B
8366 ClearCF(iret_addr.flags); // OK
8367 break;
8369 case 6: // Set Alarm Time in CMOS
8370 // Using a debugger, I notice the following masking/setting
8371 // of bits in Status Register B, by setting Reg B to
8372 // a few values and getting its value after INT 1A was called.
8374 // try#1 try#2 try#3
8375 // before 1101 1111 0101 1111 0000 0000
8376 // after 0110 1111 0111 1111 0010 0000
8378 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8379 // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
8380 val8 = inb_cmos(0x0b); // Get Status Reg B
8381 regs.u.r16.ax = 0;
8382 if (val8 & 0x20) {
8383 // Alarm interrupt enabled already
8384 SetCF(iret_addr.flags); // Error: alarm in use
8385 break;
8387 if (rtc_updating()) {
8388 init_rtc();
8389 // fall through as if an update were not in progress
8391 outb_cmos(0x01, regs.u.r8.dh); // Seconds alarm
8392 outb_cmos(0x03, regs.u.r8.cl); // Minutes alarm
8393 outb_cmos(0x05, regs.u.r8.ch); // Hours alarm
8394 outb(0xa1, inb(0xa1) & 0xfe); // enable IRQ 8
8395 // enable Status Reg B alarm bit, clear halt clock bit
8396 outb_cmos(0x0b, (val8 & 0x7f) | 0x20);
8397 ClearCF(iret_addr.flags); // OK
8398 break;
8400 case 7: // Turn off Alarm
8401 // Using a debugger, I notice the following masking/setting
8402 // of bits in Status Register B, by setting Reg B to
8403 // a few values and getting its value after INT 1A was called.
8405 // try#1 try#2 try#3 try#4
8406 // before 1111 1101 0111 1101 0010 0000 0010 0010
8407 // after 0100 0101 0101 0101 0000 0000 0000 0010
8409 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8410 // My assumption: RegB = (RegB & 01010111b)
8411 val8 = inb_cmos(0x0b); // Get Status Reg B
8412 // clear clock-halt bit, disable alarm bit
8413 outb_cmos(0x0b, val8 & 0x57); // disable alarm bit
8414 regs.u.r8.ah = 0;
8415 regs.u.r8.al = val8; // val last written to Reg B
8416 ClearCF(iret_addr.flags); // OK
8417 break;
8418 #if BX_PCIBIOS
8419 case 0xb1:
8420 // real mode PCI BIOS functions now handled in assembler code
8421 // this C code handles the error code for information only
8422 if (regs.u.r8.bl == 0xff) {
8423 BX_INFO("PCI BIOS: PCI not present\n");
8424 } else if (regs.u.r8.bl == 0x81) {
8425 BX_INFO("unsupported PCI BIOS function 0x%02x\n", regs.u.r8.al);
8426 } else if (regs.u.r8.bl == 0x83) {
8427 BX_INFO("bad PCI vendor ID %04x\n", regs.u.r16.dx);
8428 } else if (regs.u.r8.bl == 0x86) {
8429 if (regs.u.r8.al == 0x02) {
8430 BX_INFO("PCI device %04x:%04x not found at index %d\n", regs.u.r16.dx, regs.u.r16.cx, regs.u.r16.si);
8431 } else {
8432 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);
8435 regs.u.r8.ah = regs.u.r8.bl;
8436 SetCF(iret_addr.flags);
8437 break;
8438 #endif
8440 default:
8441 SetCF(iret_addr.flags); // Unsupported
8445 void
8446 int70_function(regs, ds, iret_addr)
8447 pusha_regs_t regs; // regs pushed from PUSHA instruction
8448 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
8449 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
8451 // INT 70h: IRQ 8 - CMOS RTC interrupt from periodic or alarm modes
8452 Bit8u registerB = 0, registerC = 0;
8454 // Check which modes are enabled and have occurred.
8455 registerB = inb_cmos( 0xB );
8456 registerC = inb_cmos( 0xC );
8458 if( ( registerB & 0x60 ) != 0 ) {
8459 if( ( registerC & 0x20 ) != 0 ) {
8460 // Handle Alarm Interrupt.
8461 ASM_START
8463 int #0x4a
8465 ASM_END
8467 if( ( registerC & 0x40 ) != 0 ) {
8468 // Handle Periodic Interrupt.
8470 if( read_byte( 0x40, 0xA0 ) != 0 ) {
8471 // Wait Interval (Int 15, AH=83) active.
8472 Bit32u time, toggle;
8474 time = read_dword( 0x40, 0x9C ); // Time left in microseconds.
8475 if( time < 0x3D1 ) {
8476 // Done waiting.
8477 Bit16u segment, offset;
8479 segment = read_word( 0x40, 0x98 );
8480 offset = read_word( 0x40, 0x9A );
8481 write_byte( 0x40, 0xA0, 0 ); // Turn of status byte.
8482 outb_cmos( 0xB, registerB & 0x37 ); // Clear the Periodic Interrupt.
8483 write_byte(segment, offset, read_byte(segment, offset) | 0x80 ); // Write to specified flag byte.
8484 } else {
8485 // Continue waiting.
8486 time -= 0x3D1;
8487 write_dword( 0x40, 0x9C, time );
8493 ASM_START
8494 call eoi_both_pics
8495 ASM_END
8499 ASM_START
8500 ;------------------------------------------
8501 ;- INT74h : PS/2 mouse hardware interrupt -
8502 ;------------------------------------------
8503 int74_handler:
8505 pusha
8506 push ds ;; save DS
8507 push #0x00 ;; placeholder for status
8508 push #0x00 ;; placeholder for X
8509 push #0x00 ;; placeholder for Y
8510 push #0x00 ;; placeholder for Z
8511 push #0x00 ;; placeholder for make_far_call boolean
8512 call _int74_function
8513 pop cx ;; remove make_far_call from stack
8514 jcxz int74_done
8516 ;; make far call to EBDA:0022
8517 push #0x00
8518 pop ds
8519 push 0x040E ;; push 0000:040E (opcodes 0xff, 0x36, 0x0E, 0x04)
8520 pop ds
8521 //CALL_EP(0x0022) ;; call far routine (call_Ep DS:0022 :opcodes 0xff, 0x1e, 0x22, 0x00)
8522 call far ptr[0x22]
8523 int74_done:
8525 call eoi_both_pics
8526 add sp, #8 ;; pop status, x, y, z
8528 pop ds ;; restore DS
8529 popa
8530 iret
8533 ;; This will perform an IRET, but will retain value of current CF
8534 ;; by altering flags on stack. Better than RETF #02.
8535 iret_modify_cf:
8536 jc carry_set
8537 push bp
8538 mov bp, sp
8539 and BYTE [bp + 0x06], #0xfe
8540 pop bp
8541 iret
8542 carry_set:
8543 push bp
8544 mov bp, sp
8545 or BYTE [bp + 0x06], #0x01
8546 pop bp
8547 iret
8550 ;----------------------
8551 ;- INT13h (relocated) -
8552 ;----------------------
8554 ; int13_relocated is a little bit messed up since I played with it
8555 ; I have to rewrite it:
8556 ; - call a function that detect which function to call
8557 ; - make all called C function get the same parameters list
8559 int13_relocated:
8561 #if BX_ELTORITO_BOOT
8562 ;; check for an eltorito function
8563 cmp ah,#0x4a
8564 jb int13_not_eltorito
8565 cmp ah,#0x4d
8566 ja int13_not_eltorito
8568 pusha
8569 push es
8570 push ds
8571 push ss
8572 pop ds
8574 push #int13_out
8575 jmp _int13_eltorito ;; ELDX not used
8577 int13_not_eltorito:
8578 push ax
8579 push bx
8580 push cx
8581 push dx
8583 ;; check if emulation active
8584 call _cdemu_isactive
8585 cmp al,#0x00
8586 je int13_cdemu_inactive
8588 ;; check if access to the emulated drive
8589 call _cdemu_emulated_drive
8590 pop dx
8591 push dx
8592 cmp al,dl ;; int13 on emulated drive
8593 jne int13_nocdemu
8595 pop dx
8596 pop cx
8597 pop bx
8598 pop ax
8600 pusha
8601 push es
8602 push ds
8603 push ss
8604 pop ds
8606 push #int13_out
8607 jmp _int13_cdemu ;; ELDX not used
8609 int13_nocdemu:
8610 and dl,#0xE0 ;; mask to get device class, including cdroms
8611 cmp al,dl ;; al is 0x00 or 0x80
8612 jne int13_cdemu_inactive ;; inactive for device class
8614 pop dx
8615 pop cx
8616 pop bx
8617 pop ax
8619 push ax
8620 push cx
8621 push dx
8622 push bx
8624 dec dl ;; real drive is dl - 1
8625 jmp int13_legacy
8627 int13_cdemu_inactive:
8628 pop dx
8629 pop cx
8630 pop bx
8631 pop ax
8633 #endif // BX_ELTORITO_BOOT
8635 int13_noeltorito:
8637 push ax
8638 push cx
8639 push dx
8640 push bx
8642 int13_legacy:
8644 push dx ;; push eltorito value of dx instead of sp
8646 push bp
8647 push si
8648 push di
8650 push es
8651 push ds
8652 push ss
8653 pop ds
8655 ;; now the 16-bit registers can be restored with:
8656 ;; pop ds; pop es; popa; iret
8657 ;; arguments passed to functions should be
8658 ;; DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS
8660 test dl, #0x80
8661 jnz int13_notfloppy
8663 push #int13_out
8664 jmp _int13_diskette_function
8666 int13_notfloppy:
8668 #if BX_USE_ATADRV
8670 cmp dl, #0xE0
8671 jb int13_notcdrom
8673 // ebx is modified: BSD 5.2.1 boot loader problem
8674 // someone should figure out which 32 bit register that actually are used
8676 shr ebx, #16
8677 push bx
8679 call _int13_cdrom
8681 pop bx
8682 shl ebx, #16
8684 jmp int13_out
8686 int13_notcdrom:
8688 #endif
8690 int13_disk:
8691 ;; int13_harddisk modifies high word of EAX
8692 shr eax, #16
8693 push ax
8694 call _int13_harddisk
8695 pop ax
8696 shl eax, #16
8698 int13_out:
8699 pop ds
8700 pop es
8701 popa
8702 iret
8704 ;----------
8705 ;- INT18h -
8706 ;----------
8707 int18_handler: ;; Boot Failure recovery: try the next device.
8709 ;; Reset SP and SS
8710 mov ax, #0xfffe
8711 mov sp, ax
8712 xor ax, ax
8713 mov ss, ax
8715 ;; Get the boot sequence number out of the IPL memory
8716 mov bx, #IPL_SEG
8717 mov ds, bx ;; Set segment
8718 mov bx, IPL_SEQUENCE_OFFSET ;; BX is now the sequence number
8719 inc bx ;; ++
8720 mov IPL_SEQUENCE_OFFSET, bx ;; Write it back
8721 mov ds, ax ;; and reset the segment to zero.
8723 ;; Carry on in the INT 19h handler, using the new sequence number
8724 push bx
8726 jmp int19_next_boot
8728 ;----------
8729 ;- INT19h -
8730 ;----------
8731 int19_relocated: ;; Boot function, relocated
8733 ;; int19 was beginning to be really complex, so now it
8734 ;; just calls a C function that does the work
8736 push bp
8737 mov bp, sp
8739 ;; Reset SS and SP
8740 mov ax, #0xfffe
8741 mov sp, ax
8742 xor ax, ax
8743 mov ss, ax
8745 ;; Start from the first boot device (0, in AX)
8746 mov bx, #IPL_SEG
8747 mov ds, bx ;; Set segment to write to the IPL memory
8748 mov IPL_SEQUENCE_OFFSET, ax ;; Save the sequence number
8749 mov ds, ax ;; and reset the segment.
8751 push ax
8753 int19_next_boot:
8755 ;; Call the C code for the next boot device
8756 call _int19_function
8758 ;; Boot failed: invoke the boot recovery function
8759 int #0x18
8761 ;----------
8762 ;- INT1Ch -
8763 ;----------
8764 int1c_handler: ;; User Timer Tick
8765 iret
8768 ;----------------------
8769 ;- POST: Floppy Drive -
8770 ;----------------------
8771 floppy_drive_post:
8772 xor ax, ax
8773 mov ds, ax
8775 mov al, #0x00
8776 mov 0x043e, al ;; drive 0 & 1 uncalibrated, no interrupt has occurred
8778 mov 0x043f, al ;; diskette motor status: read op, drive0, motors off
8780 mov 0x0440, al ;; diskette motor timeout counter: not active
8781 mov 0x0441, al ;; diskette controller status return code
8783 mov 0x0442, al ;; disk & diskette controller status register 0
8784 mov 0x0443, al ;; diskette controller status register 1
8785 mov 0x0444, al ;; diskette controller status register 2
8786 mov 0x0445, al ;; diskette controller cylinder number
8787 mov 0x0446, al ;; diskette controller head number
8788 mov 0x0447, al ;; diskette controller sector number
8789 mov 0x0448, al ;; diskette controller bytes written
8791 mov 0x048b, al ;; diskette configuration data
8793 ;; -----------------------------------------------------------------
8794 ;; (048F) diskette controller information
8796 mov al, #0x10 ;; get CMOS diskette drive type
8797 out 0x70, AL
8798 in AL, 0x71
8799 mov ah, al ;; save byte to AH
8801 look_drive0:
8802 shr al, #4 ;; look at top 4 bits for drive 0
8803 jz f0_missing ;; jump if no drive0
8804 mov bl, #0x07 ;; drive0 determined, multi-rate, has changed line
8805 jmp look_drive1
8806 f0_missing:
8807 mov bl, #0x00 ;; no drive0
8809 look_drive1:
8810 mov al, ah ;; restore from AH
8811 and al, #0x0f ;; look at bottom 4 bits for drive 1
8812 jz f1_missing ;; jump if no drive1
8813 or bl, #0x70 ;; drive1 determined, multi-rate, has changed line
8814 f1_missing:
8815 ;; leave high bits in BL zerod
8816 mov 0x048f, bl ;; put new val in BDA (diskette controller information)
8817 ;; -----------------------------------------------------------------
8819 mov al, #0x00
8820 mov 0x0490, al ;; diskette 0 media state
8821 mov 0x0491, al ;; diskette 1 media state
8823 ;; diskette 0,1 operational starting state
8824 ;; drive type has not been determined,
8825 ;; has no changed detection line
8826 mov 0x0492, al
8827 mov 0x0493, al
8829 mov 0x0494, al ;; diskette 0 current cylinder
8830 mov 0x0495, al ;; diskette 1 current cylinder
8832 mov al, #0x02
8833 out #0x0a, al ;; clear DMA-1 channel 2 mask bit
8835 SET_INT_VECTOR(0x1E, #0xF000, #diskette_param_table2)
8836 SET_INT_VECTOR(0x40, #0xF000, #int13_diskette)
8837 SET_INT_VECTOR(0x0E, #0xF000, #int0e_handler) ;; IRQ 6
8842 ;--------------------
8843 ;- POST: HARD DRIVE -
8844 ;--------------------
8845 ; relocated here because the primary POST area isnt big enough.
8846 hard_drive_post:
8847 // IRQ 14 = INT 76h
8848 // INT 76h calls INT 15h function ax=9100
8850 mov al, #0x0a ; 0000 1010 = reserved, disable IRQ 14
8851 mov dx, #0x03f6
8852 out dx, al
8854 xor ax, ax
8855 mov ds, ax
8856 mov 0x0474, al /* hard disk status of last operation */
8857 mov 0x0477, al /* hard disk port offset (XT only ???) */
8858 mov 0x048c, al /* hard disk status register */
8859 mov 0x048d, al /* hard disk error register */
8860 mov 0x048e, al /* hard disk task complete flag */
8861 mov al, #0x01
8862 mov 0x0475, al /* hard disk number attached */
8863 mov al, #0xc0
8864 mov 0x0476, al /* hard disk control byte */
8865 SET_INT_VECTOR(0x13, #0xF000, #int13_handler)
8866 SET_INT_VECTOR(0x76, #0xF000, #int76_handler)
8867 ;; INT 41h: hard disk 0 configuration pointer
8868 ;; INT 46h: hard disk 1 configuration pointer
8869 SET_INT_VECTOR(0x41, #EBDA_SEG, #0x003D)
8870 SET_INT_VECTOR(0x46, #EBDA_SEG, #0x004D)
8872 ;; move disk geometry data from CMOS to EBDA disk parameter table(s)
8873 mov al, #0x12
8874 out #0x70, al
8875 in al, #0x71
8876 and al, #0xf0
8877 cmp al, #0xf0
8878 je post_d0_extended
8879 jmp check_for_hd1
8880 post_d0_extended:
8881 mov al, #0x19
8882 out #0x70, al
8883 in al, #0x71
8884 cmp al, #47 ;; decimal 47 - user definable
8885 je post_d0_type47
8886 HALT(__LINE__)
8887 post_d0_type47:
8888 ;; CMOS purpose param table offset
8889 ;; 1b cylinders low 0
8890 ;; 1c cylinders high 1
8891 ;; 1d heads 2
8892 ;; 1e write pre-comp low 5
8893 ;; 1f write pre-comp high 6
8894 ;; 20 retries/bad map/heads>8 8
8895 ;; 21 landing zone low C
8896 ;; 22 landing zone high D
8897 ;; 23 sectors/track E
8899 mov ax, #EBDA_SEG
8900 mov ds, ax
8902 ;;; Filling EBDA table for hard disk 0.
8903 mov al, #0x1f
8904 out #0x70, al
8905 in al, #0x71
8906 mov ah, al
8907 mov al, #0x1e
8908 out #0x70, al
8909 in al, #0x71
8910 mov (0x003d + 0x05), ax ;; write precomp word
8912 mov al, #0x20
8913 out #0x70, al
8914 in al, #0x71
8915 mov (0x003d + 0x08), al ;; drive control byte
8917 mov al, #0x22
8918 out #0x70, al
8919 in al, #0x71
8920 mov ah, al
8921 mov al, #0x21
8922 out #0x70, al
8923 in al, #0x71
8924 mov (0x003d + 0x0C), ax ;; landing zone word
8926 mov al, #0x1c ;; get cylinders word in AX
8927 out #0x70, al
8928 in al, #0x71 ;; high byte
8929 mov ah, al
8930 mov al, #0x1b
8931 out #0x70, al
8932 in al, #0x71 ;; low byte
8933 mov bx, ax ;; BX = cylinders
8935 mov al, #0x1d
8936 out #0x70, al
8937 in al, #0x71
8938 mov cl, al ;; CL = heads
8940 mov al, #0x23
8941 out #0x70, al
8942 in al, #0x71
8943 mov dl, al ;; DL = sectors
8945 cmp bx, #1024
8946 jnbe hd0_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8948 hd0_post_physical_chs:
8949 ;; no logical CHS mapping used, just physical CHS
8950 ;; use Standard Fixed Disk Parameter Table (FDPT)
8951 mov (0x003d + 0x00), bx ;; number of physical cylinders
8952 mov (0x003d + 0x02), cl ;; number of physical heads
8953 mov (0x003d + 0x0E), dl ;; number of physical sectors
8954 jmp check_for_hd1
8956 hd0_post_logical_chs:
8957 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8958 mov (0x003d + 0x09), bx ;; number of physical cylinders
8959 mov (0x003d + 0x0b), cl ;; number of physical heads
8960 mov (0x003d + 0x04), dl ;; number of physical sectors
8961 mov (0x003d + 0x0e), dl ;; number of logical sectors (same)
8962 mov al, #0xa0
8963 mov (0x003d + 0x03), al ;; A0h signature, indicates translated table
8965 cmp bx, #2048
8966 jnbe hd0_post_above_2048
8967 ;; 1024 < c <= 2048 cylinders
8968 shr bx, #0x01
8969 shl cl, #0x01
8970 jmp hd0_post_store_logical
8972 hd0_post_above_2048:
8973 cmp bx, #4096
8974 jnbe hd0_post_above_4096
8975 ;; 2048 < c <= 4096 cylinders
8976 shr bx, #0x02
8977 shl cl, #0x02
8978 jmp hd0_post_store_logical
8980 hd0_post_above_4096:
8981 cmp bx, #8192
8982 jnbe hd0_post_above_8192
8983 ;; 4096 < c <= 8192 cylinders
8984 shr bx, #0x03
8985 shl cl, #0x03
8986 jmp hd0_post_store_logical
8988 hd0_post_above_8192:
8989 ;; 8192 < c <= 16384 cylinders
8990 shr bx, #0x04
8991 shl cl, #0x04
8993 hd0_post_store_logical:
8994 mov (0x003d + 0x00), bx ;; number of physical cylinders
8995 mov (0x003d + 0x02), cl ;; number of physical heads
8996 ;; checksum
8997 mov cl, #0x0f ;; repeat count
8998 mov si, #0x003d ;; offset to disk0 FDPT
8999 mov al, #0x00 ;; sum
9000 hd0_post_checksum_loop:
9001 add al, [si]
9002 inc si
9003 dec cl
9004 jnz hd0_post_checksum_loop
9005 not al ;; now take 2s complement
9006 inc al
9007 mov [si], al
9008 ;;; Done filling EBDA table for hard disk 0.
9011 check_for_hd1:
9012 ;; is there really a second hard disk? if not, return now
9013 mov al, #0x12
9014 out #0x70, al
9015 in al, #0x71
9016 and al, #0x0f
9017 jnz post_d1_exists
9019 post_d1_exists:
9020 ;; check that the hd type is really 0x0f.
9021 cmp al, #0x0f
9022 jz post_d1_extended
9023 HALT(__LINE__)
9024 post_d1_extended:
9025 ;; check that the extended type is 47 - user definable
9026 mov al, #0x1a
9027 out #0x70, al
9028 in al, #0x71
9029 cmp al, #47 ;; decimal 47 - user definable
9030 je post_d1_type47
9031 HALT(__LINE__)
9032 post_d1_type47:
9033 ;; Table for disk1.
9034 ;; CMOS purpose param table offset
9035 ;; 0x24 cylinders low 0
9036 ;; 0x25 cylinders high 1
9037 ;; 0x26 heads 2
9038 ;; 0x27 write pre-comp low 5
9039 ;; 0x28 write pre-comp high 6
9040 ;; 0x29 heads>8 8
9041 ;; 0x2a landing zone low C
9042 ;; 0x2b landing zone high D
9043 ;; 0x2c sectors/track E
9044 ;;; Fill EBDA table for hard disk 1.
9045 mov ax, #EBDA_SEG
9046 mov ds, ax
9047 mov al, #0x28
9048 out #0x70, al
9049 in al, #0x71
9050 mov ah, al
9051 mov al, #0x27
9052 out #0x70, al
9053 in al, #0x71
9054 mov (0x004d + 0x05), ax ;; write precomp word
9056 mov al, #0x29
9057 out #0x70, al
9058 in al, #0x71
9059 mov (0x004d + 0x08), al ;; drive control byte
9061 mov al, #0x2b
9062 out #0x70, al
9063 in al, #0x71
9064 mov ah, al
9065 mov al, #0x2a
9066 out #0x70, al
9067 in al, #0x71
9068 mov (0x004d + 0x0C), ax ;; landing zone word
9070 mov al, #0x25 ;; get cylinders word in AX
9071 out #0x70, al
9072 in al, #0x71 ;; high byte
9073 mov ah, al
9074 mov al, #0x24
9075 out #0x70, al
9076 in al, #0x71 ;; low byte
9077 mov bx, ax ;; BX = cylinders
9079 mov al, #0x26
9080 out #0x70, al
9081 in al, #0x71
9082 mov cl, al ;; CL = heads
9084 mov al, #0x2c
9085 out #0x70, al
9086 in al, #0x71
9087 mov dl, al ;; DL = sectors
9089 cmp bx, #1024
9090 jnbe hd1_post_logical_chs ;; if cylinders > 1024, use translated style CHS
9092 hd1_post_physical_chs:
9093 ;; no logical CHS mapping used, just physical CHS
9094 ;; use Standard Fixed Disk Parameter Table (FDPT)
9095 mov (0x004d + 0x00), bx ;; number of physical cylinders
9096 mov (0x004d + 0x02), cl ;; number of physical heads
9097 mov (0x004d + 0x0E), dl ;; number of physical sectors
9100 hd1_post_logical_chs:
9101 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
9102 mov (0x004d + 0x09), bx ;; number of physical cylinders
9103 mov (0x004d + 0x0b), cl ;; number of physical heads
9104 mov (0x004d + 0x04), dl ;; number of physical sectors
9105 mov (0x004d + 0x0e), dl ;; number of logical sectors (same)
9106 mov al, #0xa0
9107 mov (0x004d + 0x03), al ;; A0h signature, indicates translated table
9109 cmp bx, #2048
9110 jnbe hd1_post_above_2048
9111 ;; 1024 < c <= 2048 cylinders
9112 shr bx, #0x01
9113 shl cl, #0x01
9114 jmp hd1_post_store_logical
9116 hd1_post_above_2048:
9117 cmp bx, #4096
9118 jnbe hd1_post_above_4096
9119 ;; 2048 < c <= 4096 cylinders
9120 shr bx, #0x02
9121 shl cl, #0x02
9122 jmp hd1_post_store_logical
9124 hd1_post_above_4096:
9125 cmp bx, #8192
9126 jnbe hd1_post_above_8192
9127 ;; 4096 < c <= 8192 cylinders
9128 shr bx, #0x03
9129 shl cl, #0x03
9130 jmp hd1_post_store_logical
9132 hd1_post_above_8192:
9133 ;; 8192 < c <= 16384 cylinders
9134 shr bx, #0x04
9135 shl cl, #0x04
9137 hd1_post_store_logical:
9138 mov (0x004d + 0x00), bx ;; number of physical cylinders
9139 mov (0x004d + 0x02), cl ;; number of physical heads
9140 ;; checksum
9141 mov cl, #0x0f ;; repeat count
9142 mov si, #0x004d ;; offset to disk0 FDPT
9143 mov al, #0x00 ;; sum
9144 hd1_post_checksum_loop:
9145 add al, [si]
9146 inc si
9147 dec cl
9148 jnz hd1_post_checksum_loop
9149 not al ;; now take 2s complement
9150 inc al
9151 mov [si], al
9152 ;;; Done filling EBDA table for hard disk 1.
9156 ;--------------------
9157 ;- POST: EBDA segment
9158 ;--------------------
9159 ; relocated here because the primary POST area isnt big enough.
9160 ebda_post:
9161 #if BX_USE_EBDA
9162 mov ax, #EBDA_SEG
9163 mov ds, ax
9164 mov byte ptr [0x0], #EBDA_SIZE
9165 #endif
9166 xor ax, ax ; mov EBDA seg into 40E
9167 mov ds, ax
9168 mov word ptr [0x40E], #EBDA_SEG
9169 ret;;
9171 ;--------------------
9172 ;- POST: EOI + jmp via [0x40:67)
9173 ;--------------------
9174 ; relocated here because the primary POST area isnt big enough.
9175 eoi_jmp_post:
9176 mov al, #0x20
9177 out #0xA0, al ;; slave PIC EOI
9178 mov al, #0x20
9179 out #0x20, al ;; master PIC EOI
9181 jmp_post_0x467:
9182 xor ax, ax
9183 mov ds, ax
9185 jmp far ptr [0x467]
9187 iret_post_0x467:
9188 xor ax, ax
9189 mov ds, ax
9191 mov sp, [0x467]
9192 mov ss, [0x469]
9193 iret
9195 retf_post_0x467:
9196 xor ax, ax
9197 mov ds, ax
9199 mov sp, [0x467]
9200 mov ss, [0x469]
9201 retf
9203 s3_post:
9204 mov sp, #0xffe
9205 #if BX_ROMBIOS32
9206 call rombios32_init
9207 #endif
9208 call _s3_resume
9209 mov bl, #0x00
9210 and ax, ax
9211 jz normal_post
9212 call _s3_resume_panic
9214 ;--------------------
9215 eoi_both_pics:
9216 mov al, #0x20
9217 out #0xA0, al ;; slave PIC EOI
9218 eoi_master_pic:
9219 mov al, #0x20
9220 out #0x20, al ;; master PIC EOI
9223 ;--------------------
9224 BcdToBin:
9225 ;; in: AL in BCD format
9226 ;; out: AL in binary format, AH will always be 0
9227 ;; trashes BX
9228 mov bl, al
9229 and bl, #0x0f ;; bl has low digit
9230 shr al, #4 ;; al has high digit
9231 mov bh, #10
9232 mul al, bh ;; multiply high digit by 10 (result in AX)
9233 add al, bl ;; then add low digit
9236 ;--------------------
9237 timer_tick_post:
9238 ;; Setup the Timer Ticks Count (0x46C:dword) and
9239 ;; Timer Ticks Roller Flag (0x470:byte)
9240 ;; The Timer Ticks Count needs to be set according to
9241 ;; the current CMOS time, as if ticks have been occurring
9242 ;; at 18.2hz since midnight up to this point. Calculating
9243 ;; this is a little complicated. Here are the factors I gather
9244 ;; regarding this. 14,318,180 hz was the original clock speed,
9245 ;; chosen so it could be divided by either 3 to drive the 5Mhz CPU
9246 ;; at the time, or 4 to drive the CGA video adapter. The div3
9247 ;; source was divided again by 4 to feed a 1.193Mhz signal to
9248 ;; the timer. With a maximum 16bit timer count, this is again
9249 ;; divided down by 65536 to 18.2hz.
9251 ;; 14,318,180 Hz clock
9252 ;; /3 = 4,772,726 Hz fed to orginal 5Mhz CPU
9253 ;; /4 = 1,193,181 Hz fed to timer
9254 ;; /65536 (maximum timer count) = 18.20650736 ticks/second
9255 ;; 1 second = 18.20650736 ticks
9256 ;; 1 minute = 1092.390442 ticks
9257 ;; 1 hour = 65543.42651 ticks
9259 ;; Given the values in the CMOS clock, one could calculate
9260 ;; the number of ticks by the following:
9261 ;; ticks = (BcdToBin(seconds) * 18.206507) +
9262 ;; (BcdToBin(minutes) * 1092.3904)
9263 ;; (BcdToBin(hours) * 65543.427)
9264 ;; To get a little more accuracy, since Im using integer
9265 ;; arithmatic, I use:
9266 ;; ticks = (BcdToBin(seconds) * 18206507) / 1000000 +
9267 ;; (BcdToBin(minutes) * 10923904) / 10000 +
9268 ;; (BcdToBin(hours) * 65543427) / 1000
9270 ;; assuming DS=0000
9272 ;; get CMOS seconds
9273 xor eax, eax ;; clear EAX
9274 mov al, #0x00
9275 out #0x70, al
9276 in al, #0x71 ;; AL has CMOS seconds in BCD
9277 call BcdToBin ;; EAX now has seconds in binary
9278 mov edx, #18206507
9279 mul eax, edx
9280 mov ebx, #1000000
9281 xor edx, edx
9282 div eax, ebx
9283 mov ecx, eax ;; ECX will accumulate total ticks
9285 ;; get CMOS minutes
9286 xor eax, eax ;; clear EAX
9287 mov al, #0x02
9288 out #0x70, al
9289 in al, #0x71 ;; AL has CMOS minutes in BCD
9290 call BcdToBin ;; EAX now has minutes in binary
9291 mov edx, #10923904
9292 mul eax, edx
9293 mov ebx, #10000
9294 xor edx, edx
9295 div eax, ebx
9296 add ecx, eax ;; add to total ticks
9298 ;; get CMOS hours
9299 xor eax, eax ;; clear EAX
9300 mov al, #0x04
9301 out #0x70, al
9302 in al, #0x71 ;; AL has CMOS hours in BCD
9303 call BcdToBin ;; EAX now has hours in binary
9304 mov edx, #65543427
9305 mul eax, edx
9306 mov ebx, #1000
9307 xor edx, edx
9308 div eax, ebx
9309 add ecx, eax ;; add to total ticks
9311 mov 0x46C, ecx ;; Timer Ticks Count
9312 xor al, al
9313 mov 0x470, al ;; Timer Ticks Rollover Flag
9316 ;--------------------
9317 int76_handler:
9318 ;; record completion in BIOS task complete flag
9319 push ax
9320 push ds
9321 mov ax, #0x0040
9322 mov ds, ax
9323 mov 0x008E, #0xff
9324 call eoi_both_pics
9325 pop ds
9326 pop ax
9327 iret
9330 ;--------------------
9331 #if BX_APM
9333 use32 386
9334 #define APM_PROT32
9335 #include "apmbios.S"
9337 use16 386
9338 #define APM_PROT16
9339 #include "apmbios.S"
9341 #define APM_REAL
9342 #include "apmbios.S"
9344 #endif
9346 ;--------------------
9347 #if BX_PCIBIOS
9348 use32 386
9349 .align 16
9350 bios32_structure:
9351 db 0x5f, 0x33, 0x32, 0x5f ;; "_32_" signature
9352 dw bios32_entry_point, 0xf ;; 32 bit physical address
9353 db 0 ;; revision level
9354 ;; length in paragraphs and checksum stored in a word to prevent errors
9355 dw (~(((bios32_entry_point >> 8) + (bios32_entry_point & 0xff) + 0x32) \
9356 & 0xff) << 8) + 0x01
9357 db 0,0,0,0,0 ;; reserved
9359 .align 16
9360 bios32_entry_point:
9361 pushfd
9362 cmp eax, #0x49435024 ;; "$PCI"
9363 jne unknown_service
9364 mov eax, #0x80000000
9365 mov dx, #0x0cf8
9366 out dx, eax
9367 mov dx, #0x0cfc
9368 in eax, dx
9369 #ifdef PCI_FIXED_HOST_BRIDGE
9370 cmp eax, #PCI_FIXED_HOST_BRIDGE
9371 jne unknown_service
9372 #else
9373 ;; say ok if a device is present
9374 cmp eax, #0xffffffff
9375 je unknown_service
9376 #endif
9377 mov ebx, #0x000f0000
9378 mov ecx, #0
9379 mov edx, #pcibios_protected
9380 xor al, al
9381 jmp bios32_end
9382 unknown_service:
9383 mov al, #0x80
9384 bios32_end:
9385 #ifdef BX_QEMU
9386 and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
9387 #endif
9388 popfd
9389 retf
9391 .align 16
9392 pcibios_protected:
9393 pushfd
9395 push esi
9396 push edi
9397 cmp al, #0x01 ;; installation check
9398 jne pci_pro_f02
9399 mov bx, #0x0210
9400 mov cx, #0
9401 mov edx, #0x20494350 ;; "PCI "
9402 mov al, #0x01
9403 jmp pci_pro_ok
9404 pci_pro_f02: ;; find pci device
9405 cmp al, #0x02
9406 jne pci_pro_f03
9407 shl ecx, #16
9408 mov cx, dx
9409 xor bx, bx
9410 mov di, #0x00
9411 pci_pro_devloop:
9412 call pci_pro_select_reg
9413 mov dx, #0x0cfc
9414 in eax, dx
9415 cmp eax, ecx
9416 jne pci_pro_nextdev
9417 cmp si, #0
9418 je pci_pro_ok
9419 dec si
9420 pci_pro_nextdev:
9421 inc bx
9422 cmp bx, #0x0100
9423 jne pci_pro_devloop
9424 mov ah, #0x86
9425 jmp pci_pro_fail
9426 pci_pro_f03: ;; find class code
9427 cmp al, #0x03
9428 jne pci_pro_f08
9429 xor bx, bx
9430 mov di, #0x08
9431 pci_pro_devloop2:
9432 call pci_pro_select_reg
9433 mov dx, #0x0cfc
9434 in eax, dx
9435 shr eax, #8
9436 cmp eax, ecx
9437 jne pci_pro_nextdev2
9438 cmp si, #0
9439 je pci_pro_ok
9440 dec si
9441 pci_pro_nextdev2:
9442 inc bx
9443 cmp bx, #0x0100
9444 jne pci_pro_devloop2
9445 mov ah, #0x86
9446 jmp pci_pro_fail
9447 pci_pro_f08: ;; read configuration byte
9448 cmp al, #0x08
9449 jne pci_pro_f09
9450 call pci_pro_select_reg
9451 push edx
9452 mov dx, di
9453 and dx, #0x03
9454 add dx, #0x0cfc
9455 in al, dx
9456 pop edx
9457 mov cl, al
9458 jmp pci_pro_ok
9459 pci_pro_f09: ;; read configuration word
9460 cmp al, #0x09
9461 jne pci_pro_f0a
9462 call pci_pro_select_reg
9463 push edx
9464 mov dx, di
9465 and dx, #0x02
9466 add dx, #0x0cfc
9467 in ax, dx
9468 pop edx
9469 mov cx, ax
9470 jmp pci_pro_ok
9471 pci_pro_f0a: ;; read configuration dword
9472 cmp al, #0x0a
9473 jne pci_pro_f0b
9474 call pci_pro_select_reg
9475 push edx
9476 mov dx, #0x0cfc
9477 in eax, dx
9478 pop edx
9479 mov ecx, eax
9480 jmp pci_pro_ok
9481 pci_pro_f0b: ;; write configuration byte
9482 cmp al, #0x0b
9483 jne pci_pro_f0c
9484 call pci_pro_select_reg
9485 push edx
9486 mov dx, di
9487 and dx, #0x03
9488 add dx, #0x0cfc
9489 mov al, cl
9490 out dx, al
9491 pop edx
9492 jmp pci_pro_ok
9493 pci_pro_f0c: ;; write configuration word
9494 cmp al, #0x0c
9495 jne pci_pro_f0d
9496 call pci_pro_select_reg
9497 push edx
9498 mov dx, di
9499 and dx, #0x02
9500 add dx, #0x0cfc
9501 mov ax, cx
9502 out dx, ax
9503 pop edx
9504 jmp pci_pro_ok
9505 pci_pro_f0d: ;; write configuration dword
9506 cmp al, #0x0d
9507 jne pci_pro_unknown
9508 call pci_pro_select_reg
9509 push edx
9510 mov dx, #0x0cfc
9511 mov eax, ecx
9512 out dx, eax
9513 pop edx
9514 jmp pci_pro_ok
9515 pci_pro_unknown:
9516 mov ah, #0x81
9517 pci_pro_fail:
9518 pop edi
9519 pop esi
9520 #ifdef BX_QEMU
9521 and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
9522 #endif
9523 popfd
9525 retf
9526 pci_pro_ok:
9527 xor ah, ah
9528 pop edi
9529 pop esi
9530 #ifdef BX_QEMU
9531 and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
9532 #endif
9533 popfd
9535 retf
9537 pci_pro_select_reg:
9538 push edx
9539 mov eax, #0x800000
9540 mov ax, bx
9541 shl eax, #8
9542 and di, #0xff
9543 or ax, di
9544 and al, #0xfc
9545 mov dx, #0x0cf8
9546 out dx, eax
9547 pop edx
9550 use16 386
9552 pcibios_real:
9553 push eax
9554 push dx
9555 mov eax, #0x80000000
9556 mov dx, #0x0cf8
9557 out dx, eax
9558 mov dx, #0x0cfc
9559 in eax, dx
9560 #ifdef PCI_FIXED_HOST_BRIDGE
9561 cmp eax, #PCI_FIXED_HOST_BRIDGE
9562 je pci_present
9563 #else
9564 ;; say ok if a device is present
9565 cmp eax, #0xffffffff
9566 jne pci_present
9567 #endif
9568 pop dx
9569 pop eax
9570 mov ah, #0xff
9573 pci_present:
9574 pop dx
9575 pop eax
9576 cmp al, #0x01 ;; installation check
9577 jne pci_real_f02
9578 mov ax, #0x0001
9579 mov bx, #0x0210
9580 mov cx, #0
9581 mov edx, #0x20494350 ;; "PCI "
9582 mov edi, #0xf0000
9583 mov di, #pcibios_protected
9586 pci_real_f02: ;; find pci device
9587 push esi
9588 push edi
9589 cmp al, #0x02
9590 jne pci_real_f03
9591 shl ecx, #16
9592 mov cx, dx
9593 xor bx, bx
9594 mov di, #0x00
9595 pci_real_devloop:
9596 call pci_real_select_reg
9597 mov dx, #0x0cfc
9598 in eax, dx
9599 cmp eax, ecx
9600 jne pci_real_nextdev
9601 cmp si, #0
9602 je pci_real_ok
9603 dec si
9604 pci_real_nextdev:
9605 inc bx
9606 cmp bx, #0x0100
9607 jne pci_real_devloop
9608 mov dx, cx
9609 shr ecx, #16
9610 mov ax, #0x8602
9611 jmp pci_real_fail
9612 pci_real_f03: ;; find class code
9613 cmp al, #0x03
9614 jne pci_real_f08
9615 xor bx, bx
9616 mov di, #0x08
9617 pci_real_devloop2:
9618 call pci_real_select_reg
9619 mov dx, #0x0cfc
9620 in eax, dx
9621 shr eax, #8
9622 cmp eax, ecx
9623 jne pci_real_nextdev2
9624 cmp si, #0
9625 je pci_real_ok
9626 dec si
9627 pci_real_nextdev2:
9628 inc bx
9629 cmp bx, #0x0100
9630 jne pci_real_devloop2
9631 mov dx, cx
9632 shr ecx, #16
9633 mov ax, #0x8603
9634 jmp pci_real_fail
9635 pci_real_f08: ;; read configuration byte
9636 cmp al, #0x08
9637 jne pci_real_f09
9638 call pci_real_select_reg
9639 push dx
9640 mov dx, di
9641 and dx, #0x03
9642 add dx, #0x0cfc
9643 in al, dx
9644 pop dx
9645 mov cl, al
9646 jmp pci_real_ok
9647 pci_real_f09: ;; read configuration word
9648 cmp al, #0x09
9649 jne pci_real_f0a
9650 call pci_real_select_reg
9651 push dx
9652 mov dx, di
9653 and dx, #0x02
9654 add dx, #0x0cfc
9655 in ax, dx
9656 pop dx
9657 mov cx, ax
9658 jmp pci_real_ok
9659 pci_real_f0a: ;; read configuration dword
9660 cmp al, #0x0a
9661 jne pci_real_f0b
9662 call pci_real_select_reg
9663 push dx
9664 mov dx, #0x0cfc
9665 in eax, dx
9666 pop dx
9667 mov ecx, eax
9668 jmp pci_real_ok
9669 pci_real_f0b: ;; write configuration byte
9670 cmp al, #0x0b
9671 jne pci_real_f0c
9672 call pci_real_select_reg
9673 push dx
9674 mov dx, di
9675 and dx, #0x03
9676 add dx, #0x0cfc
9677 mov al, cl
9678 out dx, al
9679 pop dx
9680 jmp pci_real_ok
9681 pci_real_f0c: ;; write configuration word
9682 cmp al, #0x0c
9683 jne pci_real_f0d
9684 call pci_real_select_reg
9685 push dx
9686 mov dx, di
9687 and dx, #0x02
9688 add dx, #0x0cfc
9689 mov ax, cx
9690 out dx, ax
9691 pop dx
9692 jmp pci_real_ok
9693 pci_real_f0d: ;; write configuration dword
9694 cmp al, #0x0d
9695 jne pci_real_f0e
9696 call pci_real_select_reg
9697 push dx
9698 mov dx, #0x0cfc
9699 mov eax, ecx
9700 out dx, eax
9701 pop dx
9702 jmp pci_real_ok
9703 pci_real_f0e: ;; get irq routing options
9704 cmp al, #0x0e
9705 jne pci_real_unknown
9706 SEG ES
9707 cmp word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9708 jb pci_real_too_small
9709 SEG ES
9710 mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9711 pushf
9712 push ds
9713 push es
9714 push cx
9715 push si
9716 push di
9718 mov si, #pci_routing_table_structure_start
9719 push cs
9720 pop ds
9721 SEG ES
9722 mov cx, [di+2]
9723 SEG ES
9724 mov es, [di+4]
9725 mov di, cx
9726 mov cx, #pci_routing_table_structure_end - pci_routing_table_structure_start
9728 movsb
9729 pop di
9730 pop si
9731 pop cx
9732 pop es
9733 pop ds
9734 popf
9735 mov bx, #(1 << 9) | (1 << 11) ;; irq 9 and 11 are used
9736 jmp pci_real_ok
9737 pci_real_too_small:
9738 SEG ES
9739 mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9740 mov ah, #0x89
9741 jmp pci_real_fail
9743 pci_real_unknown:
9744 mov ah, #0x81
9745 pci_real_fail:
9746 pop edi
9747 pop esi
9750 pci_real_ok:
9751 xor ah, ah
9752 pop edi
9753 pop esi
9757 pci_real_select_reg:
9758 push dx
9759 mov eax, #0x800000
9760 mov ax, bx
9761 shl eax, #8
9762 and di, #0xff
9763 or ax, di
9764 and al, #0xfc
9765 mov dx, #0x0cf8
9766 out dx, eax
9767 pop dx
9770 .align 16
9771 pci_routing_table_structure:
9772 db 0x24, 0x50, 0x49, 0x52 ;; "$PIR" signature
9773 db 0, 1 ;; version
9774 dw 32 + (6 * 16) ;; table size
9775 db 0 ;; PCI interrupt router bus
9776 db 0x08 ;; PCI interrupt router DevFunc
9777 dw 0x0000 ;; PCI exclusive IRQs
9778 dw 0x8086 ;; compatible PCI interrupt router vendor ID
9779 dw 0x122e ;; compatible PCI interrupt router device ID
9780 dw 0,0 ;; Miniport data
9781 db 0,0,0,0,0,0,0,0,0,0,0 ;; reserved
9782 db 0x37 ;; checksum
9783 pci_routing_table_structure_start:
9784 ;; first slot entry PCI-to-ISA (embedded)
9785 db 0 ;; pci bus number
9786 db 0x08 ;; pci device number (bit 7-3)
9787 db 0x60 ;; link value INTA#: pointer into PCI2ISA config space
9788 dw 0xdef8 ;; IRQ bitmap INTA#
9789 db 0x61 ;; link value INTB#
9790 dw 0xdef8 ;; IRQ bitmap INTB#
9791 db 0x62 ;; link value INTC#
9792 dw 0xdef8 ;; IRQ bitmap INTC#
9793 db 0x63 ;; link value INTD#
9794 dw 0xdef8 ;; IRQ bitmap INTD#
9795 db 0 ;; physical slot (0 = embedded)
9796 db 0 ;; reserved
9797 ;; second slot entry: 1st PCI slot
9798 db 0 ;; pci bus number
9799 db 0x10 ;; pci device number (bit 7-3)
9800 db 0x61 ;; link value INTA#
9801 dw 0xdef8 ;; IRQ bitmap INTA#
9802 db 0x62 ;; link value INTB#
9803 dw 0xdef8 ;; IRQ bitmap INTB#
9804 db 0x63 ;; link value INTC#
9805 dw 0xdef8 ;; IRQ bitmap INTC#
9806 db 0x60 ;; link value INTD#
9807 dw 0xdef8 ;; IRQ bitmap INTD#
9808 db 1 ;; physical slot (0 = embedded)
9809 db 0 ;; reserved
9810 ;; third slot entry: 2nd PCI slot
9811 db 0 ;; pci bus number
9812 db 0x18 ;; pci device number (bit 7-3)
9813 db 0x62 ;; link value INTA#
9814 dw 0xdef8 ;; IRQ bitmap INTA#
9815 db 0x63 ;; link value INTB#
9816 dw 0xdef8 ;; IRQ bitmap INTB#
9817 db 0x60 ;; link value INTC#
9818 dw 0xdef8 ;; IRQ bitmap INTC#
9819 db 0x61 ;; link value INTD#
9820 dw 0xdef8 ;; IRQ bitmap INTD#
9821 db 2 ;; physical slot (0 = embedded)
9822 db 0 ;; reserved
9823 ;; 4th slot entry: 3rd PCI slot
9824 db 0 ;; pci bus number
9825 db 0x20 ;; pci device number (bit 7-3)
9826 db 0x63 ;; link value INTA#
9827 dw 0xdef8 ;; IRQ bitmap INTA#
9828 db 0x60 ;; link value INTB#
9829 dw 0xdef8 ;; IRQ bitmap INTB#
9830 db 0x61 ;; link value INTC#
9831 dw 0xdef8 ;; IRQ bitmap INTC#
9832 db 0x62 ;; link value INTD#
9833 dw 0xdef8 ;; IRQ bitmap INTD#
9834 db 3 ;; physical slot (0 = embedded)
9835 db 0 ;; reserved
9836 ;; 5th slot entry: 4rd PCI slot
9837 db 0 ;; pci bus number
9838 db 0x28 ;; pci device number (bit 7-3)
9839 db 0x60 ;; link value INTA#
9840 dw 0xdef8 ;; IRQ bitmap INTA#
9841 db 0x61 ;; link value INTB#
9842 dw 0xdef8 ;; IRQ bitmap INTB#
9843 db 0x62 ;; link value INTC#
9844 dw 0xdef8 ;; IRQ bitmap INTC#
9845 db 0x63 ;; link value INTD#
9846 dw 0xdef8 ;; IRQ bitmap INTD#
9847 db 4 ;; physical slot (0 = embedded)
9848 db 0 ;; reserved
9849 ;; 6th slot entry: 5rd PCI slot
9850 db 0 ;; pci bus number
9851 db 0x30 ;; pci device number (bit 7-3)
9852 db 0x61 ;; link value INTA#
9853 dw 0xdef8 ;; IRQ bitmap INTA#
9854 db 0x62 ;; link value INTB#
9855 dw 0xdef8 ;; IRQ bitmap INTB#
9856 db 0x63 ;; link value INTC#
9857 dw 0xdef8 ;; IRQ bitmap INTC#
9858 db 0x60 ;; link value INTD#
9859 dw 0xdef8 ;; IRQ bitmap INTD#
9860 db 5 ;; physical slot (0 = embedded)
9861 db 0 ;; reserved
9862 pci_routing_table_structure_end:
9864 #if !BX_ROMBIOS32
9865 pci_irq_list:
9866 db 11, 10, 9, 5;
9868 pcibios_init_sel_reg:
9869 push eax
9870 mov eax, #0x800000
9871 mov ax, bx
9872 shl eax, #8
9873 and dl, #0xfc
9874 or al, dl
9875 mov dx, #0x0cf8
9876 out dx, eax
9877 pop eax
9880 pcibios_init_iomem_bases:
9881 push bp
9882 mov bp, sp
9883 mov eax, #0xc0000000 ;; base for memory init
9884 push eax
9885 mov ax, #0xc000 ;; base for i/o init
9886 push ax
9887 mov ax, #0x0010 ;; start at base address #0
9888 push ax
9889 mov bx, #0x0008
9890 pci_init_io_loop1:
9891 mov dl, #0x00
9892 call pcibios_init_sel_reg
9893 mov dx, #0x0cfc
9894 in ax, dx
9895 cmp ax, #0xffff
9896 jz next_pci_dev
9897 mov dl, #0x04 ;; disable i/o and memory space access
9898 call pcibios_init_sel_reg
9899 mov dx, #0x0cfc
9900 in al, dx
9901 and al, #0xfc
9902 out dx, al
9903 pci_init_io_loop2:
9904 mov dl, [bp-8]
9905 call pcibios_init_sel_reg
9906 mov dx, #0x0cfc
9907 in eax, dx
9908 test al, #0x01
9909 jnz init_io_base
9910 mov ecx, eax
9911 mov eax, #0xffffffff
9912 out dx, eax
9913 in eax, dx
9914 cmp eax, ecx
9915 je next_pci_base
9916 xor eax, #0xffffffff
9917 mov ecx, eax
9918 mov eax, [bp-4]
9919 out dx, eax
9920 add eax, ecx ;; calculate next free mem base
9921 add eax, #0x01000000
9922 and eax, #0xff000000
9923 mov [bp-4], eax
9924 jmp next_pci_base
9925 init_io_base:
9926 mov cx, ax
9927 mov ax, #0xffff
9928 out dx, ax
9929 in ax, dx
9930 cmp ax, cx
9931 je next_pci_base
9932 xor ax, #0xfffe
9933 mov cx, ax
9934 mov ax, [bp-6]
9935 out dx, ax
9936 add ax, cx ;; calculate next free i/o base
9937 add ax, #0x0100
9938 and ax, #0xff00
9939 mov [bp-6], ax
9940 next_pci_base:
9941 mov al, [bp-8]
9942 add al, #0x04
9943 cmp al, #0x28
9944 je enable_iomem_space
9945 mov byte ptr[bp-8], al
9946 jmp pci_init_io_loop2
9947 enable_iomem_space:
9948 mov dl, #0x04 ;; enable i/o and memory space access if available
9949 call pcibios_init_sel_reg
9950 mov dx, #0x0cfc
9951 in al, dx
9952 or al, #0x07
9953 out dx, al
9954 next_pci_dev:
9955 mov byte ptr[bp-8], #0x10
9956 inc bx
9957 cmp bx, #0x0100
9958 jne pci_init_io_loop1
9959 mov sp, bp
9960 pop bp
9963 pcibios_init_set_elcr:
9964 push ax
9965 push cx
9966 mov dx, #0x04d0
9967 test al, #0x08
9968 jz is_master_pic
9969 inc dx
9970 and al, #0x07
9971 is_master_pic:
9972 mov cl, al
9973 mov bl, #0x01
9974 shl bl, cl
9975 in al, dx
9976 or al, bl
9977 out dx, al
9978 pop cx
9979 pop ax
9982 pcibios_init_irqs:
9983 push ds
9984 push bp
9985 mov ax, #0xf000
9986 mov ds, ax
9987 mov dx, #0x04d0 ;; reset ELCR1 + ELCR2
9988 mov al, #0x00
9989 out dx, al
9990 inc dx
9991 out dx, al
9992 mov si, #pci_routing_table_structure
9993 mov bh, [si+8]
9994 mov bl, [si+9]
9995 mov dl, #0x00
9996 call pcibios_init_sel_reg
9997 mov dx, #0x0cfc
9998 in ax, dx
9999 cmp ax, [si+12] ;; check irq router
10000 jne pci_init_end
10001 mov dl, [si+34]
10002 call pcibios_init_sel_reg
10003 push bx ;; save irq router bus + devfunc
10004 mov dx, #0x0cfc
10005 mov ax, #0x8080
10006 out dx, ax ;; reset PIRQ route control
10007 add dx, #2
10008 out dx, ax
10009 mov ax, [si+6]
10010 sub ax, #0x20
10011 shr ax, #4
10012 mov cx, ax
10013 add si, #0x20 ;; set pointer to 1st entry
10014 mov bp, sp
10015 mov ax, #pci_irq_list
10016 push ax
10017 xor ax, ax
10018 push ax
10019 pci_init_irq_loop1:
10020 mov bh, [si]
10021 mov bl, [si+1]
10022 pci_init_irq_loop2:
10023 mov dl, #0x00
10024 call pcibios_init_sel_reg
10025 mov dx, #0x0cfc
10026 in ax, dx
10027 cmp ax, #0xffff
10028 jnz pci_test_int_pin
10029 test bl, #0x07
10030 jz next_pir_entry
10031 jmp next_pci_func
10032 pci_test_int_pin:
10033 mov dl, #0x3c
10034 call pcibios_init_sel_reg
10035 mov dx, #0x0cfd
10036 in al, dx
10037 and al, #0x07
10038 jz next_pci_func
10039 dec al ;; determine pirq reg
10040 mov dl, #0x03
10041 mul al, dl
10042 add al, #0x02
10043 xor ah, ah
10044 mov bx, ax
10045 mov al, [si+bx]
10046 mov dl, al
10047 mov bx, [bp]
10048 call pcibios_init_sel_reg
10049 mov dx, #0x0cfc
10050 and al, #0x03
10051 add dl, al
10052 in al, dx
10053 cmp al, #0x80
10054 jb pirq_found
10055 mov bx, [bp-2] ;; pci irq list pointer
10056 mov al, [bx]
10057 out dx, al
10058 inc bx
10059 mov [bp-2], bx
10060 call pcibios_init_set_elcr
10061 pirq_found:
10062 mov bh, [si]
10063 mov bl, [si+1]
10064 add bl, [bp-3] ;; pci function number
10065 mov dl, #0x3c
10066 call pcibios_init_sel_reg
10067 mov dx, #0x0cfc
10068 out dx, al
10069 next_pci_func:
10070 inc byte ptr[bp-3]
10071 inc bl
10072 test bl, #0x07
10073 jnz pci_init_irq_loop2
10074 next_pir_entry:
10075 add si, #0x10
10076 mov byte ptr[bp-3], #0x00
10077 loop pci_init_irq_loop1
10078 mov sp, bp
10079 pop bx
10080 pci_init_end:
10081 pop bp
10082 pop ds
10084 #endif // !BX_ROMBIOS32
10085 #endif // BX_PCIBIOS
10087 #if BX_ROMBIOS32
10088 rombios32_init:
10089 ;; save a20 and enable it
10090 in al, 0x92
10091 push ax
10092 or al, #0x02
10093 out 0x92, al
10095 ;; save SS:SP to the BDA
10096 xor ax, ax
10097 mov ds, ax
10098 mov 0x0469, ss
10099 mov 0x0467, sp
10101 SEG CS
10102 lidt [pmode_IDT_info]
10103 SEG CS
10104 lgdt [rombios32_gdt_48]
10105 ;; set PE bit in CR0
10106 mov eax, cr0
10107 or al, #0x01
10108 mov cr0, eax
10109 ;; start protected mode code: ljmpl 0x10:rombios32_init1
10110 db 0x66, 0xea
10111 dw rombios32_05
10112 dw 0x000f ;; high 16 bit address
10113 dw 0x0010
10115 use32 386
10116 rombios32_05:
10117 ;; init data segments
10118 mov eax, #0x18
10119 mov ds, ax
10120 mov es, ax
10121 mov ss, ax
10122 xor eax, eax
10123 mov fs, ax
10124 mov gs, ax
10127 ;; init the stack pointer to point below EBDA
10128 mov ax, [0x040e]
10129 shl eax, #4
10130 mov esp, #-0x10
10131 add esp, eax
10133 ;; pass pointer to s3_resume_flag and s3_resume_vector to rombios32
10134 push #0x04b0
10135 push #0x04b2
10137 ;; call rombios32 code
10138 mov eax, #0x000e0000
10139 call eax
10141 ;; return to 16 bit protected mode first
10142 db 0xea
10143 dd rombios32_10
10144 dw 0x20
10146 use16 386
10147 rombios32_10:
10148 ;; restore data segment limits to 0xffff
10149 mov ax, #0x28
10150 mov ds, ax
10151 mov es, ax
10152 mov ss, ax
10153 mov fs, ax
10154 mov gs, ax
10156 ;; reset PE bit in CR0
10157 mov eax, cr0
10158 and al, #0xFE
10159 mov cr0, eax
10161 ;; far jump to flush CPU queue after transition to real mode
10162 JMP_AP(0xf000, rombios32_real_mode)
10164 rombios32_real_mode:
10165 ;; restore IDT to normal real-mode defaults
10166 SEG CS
10167 lidt [rmode_IDT_info]
10169 xor ax, ax
10170 mov ds, ax
10171 mov es, ax
10172 mov fs, ax
10173 mov gs, ax
10175 ;; restore SS:SP from the BDA
10176 mov ss, 0x0469
10177 xor esp, esp
10178 mov sp, 0x0467
10179 ;; restore a20
10180 pop ax
10181 out 0x92, al
10184 rombios32_gdt_48:
10185 dw 0x30
10186 dw rombios32_gdt
10187 dw 0x000f
10189 rombios32_gdt:
10190 dw 0, 0, 0, 0
10191 dw 0, 0, 0, 0
10192 dw 0xffff, 0, 0x9b00, 0x00cf ; 32 bit flat code segment (0x10)
10193 dw 0xffff, 0, 0x9300, 0x00cf ; 32 bit flat data segment (0x18)
10194 dw 0xffff, 0, 0x9b0f, 0x0000 ; 16 bit code segment base=0xf0000 limit=0xffff
10195 dw 0xffff, 0, 0x9300, 0x0000 ; 16 bit data segment base=0x0 limit=0xffff
10196 #endif // BX_ROMBIOS32
10199 ; parallel port detection: base address in DX, index in BX, timeout in CL
10200 detect_parport:
10201 push dx
10202 add dx, #2
10203 in al, dx
10204 and al, #0xdf ; clear input mode
10205 out dx, al
10206 pop dx
10207 mov al, #0xaa
10208 out dx, al
10209 in al, dx
10210 cmp al, #0xaa
10211 jne no_parport
10212 push bx
10213 shl bx, #1
10214 mov [bx+0x408], dx ; Parallel I/O address
10215 pop bx
10216 mov [bx+0x478], cl ; Parallel printer timeout
10217 inc bx
10218 no_parport:
10221 ; serial port detection: base address in DX, index in BX, timeout in CL
10222 detect_serial:
10223 push dx
10224 inc dx
10225 mov al, #0x02
10226 out dx, al
10227 in al, dx
10228 cmp al, #0x02
10229 jne no_serial
10230 inc dx
10231 in al, dx
10232 cmp al, #0x02
10233 jne no_serial
10234 dec dx
10235 xor al, al
10236 out dx, al
10237 pop dx
10238 push bx
10239 shl bx, #1
10240 mov [bx+0x400], dx ; Serial I/O address
10241 pop bx
10242 mov [bx+0x47c], cl ; Serial timeout
10243 inc bx
10245 no_serial:
10246 pop dx
10249 rom_checksum:
10250 pusha
10251 push ds
10253 xor ax, ax
10254 xor bx, bx
10255 xor cx, cx
10256 xor dx, dx
10258 mov ch, [2]
10259 shl cx, #1
10261 jnc checksum_loop
10262 xchg dx, cx
10263 dec cx
10265 checksum_loop:
10266 add al, [bx]
10267 inc bx
10268 loop checksum_loop
10270 test dx, dx
10271 je checksum_out
10273 add al, [bx]
10274 mov cx, dx
10275 mov dx, ds
10276 add dh, #0x10
10277 mov ds, dx
10278 xor dx, dx
10279 xor bx, bx
10281 jmp checksum_loop
10283 checksum_out:
10284 and al, #0xff
10285 pop ds
10286 popa
10290 ;; We need a copy of this string, but we are not actually a PnP BIOS,
10291 ;; so make sure it is *not* aligned, so OSes will not see it if they scan.
10292 .align 16
10293 db 0
10294 pnp_string:
10295 .ascii "$PnP"
10298 rom_scan:
10299 ;; Scan for existence of valid expansion ROMS.
10300 ;; Video ROM: from 0xC0000..0xC7FFF in 2k increments
10301 ;; General ROM: from 0xC8000..0xDFFFF in 2k increments
10302 ;; System ROM: only 0xE0000
10304 ;; Header:
10305 ;; Offset Value
10306 ;; 0 0x55
10307 ;; 1 0xAA
10308 ;; 2 ROM length in 512-byte blocks
10309 ;; 3 ROM initialization entry point (FAR CALL)
10311 rom_scan_loop:
10312 push ax ;; Save AX
10313 mov ds, cx
10314 mov ax, #0x0004 ;; start with increment of 4 (512-byte) blocks = 2k
10315 cmp [0], #0xAA55 ;; look for signature
10316 jne rom_scan_increment
10317 call rom_checksum
10318 jnz rom_scan_increment
10319 mov al, [2] ;; change increment to ROM length in 512-byte blocks
10321 ;; We want our increment in 512-byte quantities, rounded to
10322 ;; the nearest 2k quantity, since we only scan at 2k intervals.
10323 test al, #0x03
10324 jz block_count_rounded
10325 and al, #0xfc ;; needs rounding up
10326 add al, #0x04
10327 block_count_rounded:
10329 xor bx, bx ;; Restore DS back to 0000:
10330 mov ds, bx
10331 push ax ;; Save AX
10332 push di ;; Save DI
10333 ;; Push addr of ROM entry point
10334 push cx ;; Push seg
10335 push #0x0003 ;; Push offset
10337 ;; Point ES:DI at "$PnP", which tells the ROM that we are a PnP BIOS.
10338 ;; That should stop it grabbing INT 19h; we will use its BEV instead.
10339 mov ax, #0xf000
10340 mov es, ax
10341 lea di, pnp_string
10343 mov bp, sp ;; Call ROM init routine using seg:off on stack
10344 db 0xff ;; call_far ss:[bp+0]
10345 db 0x5e
10346 db 0
10347 cli ;; In case expansion ROM BIOS turns IF on
10348 add sp, #2 ;; Pop offset value
10349 pop cx ;; Pop seg value (restore CX)
10351 ;; Look at the ROM's PnP Expansion header. Properly, we're supposed
10352 ;; to init all the ROMs and then go back and build an IPL table of
10353 ;; all the bootable devices, but we can get away with one pass.
10354 mov ds, cx ;; ROM base
10355 mov bx, 0x001a ;; 0x1A is the offset into ROM header that contains...
10356 mov ax, [bx] ;; the offset of PnP expansion header, where...
10357 cmp ax, #0x5024 ;; we look for signature "$PnP"
10358 jne no_bev
10359 mov ax, 2[bx]
10360 cmp ax, #0x506e
10361 jne no_bev
10363 mov ax, 0x16[bx] ;; 0x16 is the offset of Boot Connection Vector
10364 cmp ax, #0x0000
10365 je no_bcv
10367 ;; Option ROM has BCV. Run it now.
10368 push cx ;; Push seg
10369 push ax ;; Push offset
10371 ;; Point ES:DI at "$PnP", which tells the ROM that we are a PnP BIOS.
10372 mov bx, #0xf000
10373 mov es, bx
10374 lea di, pnp_string
10375 /* jump to BCV function entry pointer */
10376 mov bp, sp ;; Call ROM BCV routine using seg:off on stack
10377 db 0xff ;; call_far ss:[bp+0]
10378 db 0x5e
10379 db 0
10380 cli ;; In case expansion ROM BIOS turns IF on
10381 add sp, #2 ;; Pop offset value
10382 pop cx ;; Pop seg value (restore CX)
10383 jmp no_bev
10385 no_bcv:
10386 mov ax, 0x1a[bx] ;; 0x1A is also the offset into the expansion header of...
10387 cmp ax, #0x0000 ;; the Bootstrap Entry Vector, or zero if there is none.
10388 je no_bev
10390 ;; Found a device that thinks it can boot the system. Record its BEV and product name string.
10391 mov di, 0x10[bx] ;; Pointer to the product name string or zero if none
10392 mov bx, #IPL_SEG ;; Go to the segment where the IPL table lives
10393 mov ds, bx
10394 mov bx, IPL_COUNT_OFFSET ;; Read the number of entries so far
10395 cmp bx, #IPL_TABLE_ENTRIES
10396 je no_bev ;; Get out if the table is full
10397 shl bx, #0x4 ;; Turn count into offset (entries are 16 bytes)
10398 mov 0[bx], #IPL_TYPE_BEV ;; This entry is a BEV device
10399 mov 6[bx], cx ;; Build a far pointer from the segment...
10400 mov 4[bx], ax ;; and the offset
10401 cmp di, #0x0000
10402 je no_prod_str
10403 mov 0xA[bx], cx ;; Build a far pointer from the segment...
10404 mov 8[bx], di ;; and the offset
10405 no_prod_str:
10406 shr bx, #0x4 ;; Turn the offset back into a count
10407 inc bx ;; We have one more entry now
10408 mov IPL_COUNT_OFFSET, bx ;; Remember that.
10410 no_bev:
10411 pop di ;; Restore DI
10412 pop ax ;; Restore AX
10413 rom_scan_increment:
10414 shl ax, #5 ;; convert 512-bytes blocks to 16-byte increments
10415 ;; because the segment selector is shifted left 4 bits.
10416 add cx, ax
10417 pop ax ;; Restore AX
10418 cmp cx, ax
10419 jbe rom_scan_loop
10421 xor ax, ax ;; Restore DS back to 0000:
10422 mov ds, ax
10425 post_init_pic:
10426 mov al, #0x11 ; send initialisation commands
10427 out 0x20, al
10428 out 0xa0, al
10429 mov al, #0x08
10430 out 0x21, al
10431 mov al, #0x70
10432 out 0xa1, al
10433 mov al, #0x04
10434 out 0x21, al
10435 mov al, #0x02
10436 out 0xa1, al
10437 mov al, #0x01
10438 out 0x21, al
10439 out 0xa1, al
10440 mov al, #0xb8
10441 out 0x21, AL ;master pic: unmask IRQ 0, 1, 2, 6
10442 #if BX_USE_PS2_MOUSE
10443 mov al, #0x8f
10444 #else
10445 mov al, #0x9f
10446 #endif
10447 out 0xa1, AL ;slave pic: unmask IRQ 12, 13, 14
10450 ;; the following area can be used to write dynamically generated tables
10451 .align 16
10452 bios_table_area_start:
10453 dd 0xaafb4442
10454 dd bios_table_area_end - bios_table_area_start - 8;
10456 ;--------
10457 ;- POST -
10458 ;--------
10459 .org 0xe05b ; POST Entry Point
10460 post:
10462 xor ax, ax
10464 ;; first reset the DMA controllers
10465 out 0x0d,al
10466 out 0xda,al
10468 ;; then initialize the DMA controllers
10469 mov al, #0xC0
10470 out 0xD6, al ; cascade mode of channel 4 enabled
10471 mov al, #0x00
10472 out 0xD4, al ; unmask channel 4
10474 ;; Examine CMOS shutdown status.
10475 mov AL, #0x0f
10476 out 0x70, AL
10477 in AL, 0x71
10479 ;; backup status
10480 mov bl, al
10482 ;; Reset CMOS shutdown status.
10483 mov AL, #0x0f
10484 out 0x70, AL ; select CMOS register Fh
10485 mov AL, #0x00
10486 out 0x71, AL ; set shutdown action to normal
10488 ;; Examine CMOS shutdown status.
10489 mov al, bl
10491 ;; 0x00, 0x09, 0x0D+ = normal startup
10492 cmp AL, #0x00
10493 jz normal_post
10494 cmp AL, #0x0d
10495 jae normal_post
10497 ;; 0x05 = eoi + jmp via [0x40:0x67] jump
10498 cmp al, #0x05
10499 je eoi_jmp_post
10501 ;; 0x0A = jmp via [0x40:0x67] jump
10502 cmp al, #0x0a
10503 je jmp_post_0x467
10505 ;; 0x0B = iret via [0x40:0x67]
10506 cmp al, #0x0b
10507 je iret_post_0x467
10509 ;; 0x0C = retf via [0x40:0x67]
10510 cmp al, #0x0c
10511 je retf_post_0x467
10513 ;; Examine CMOS shutdown status.
10514 ;; 0x01,0x02,0x03,0x04,0x06,0x07,0x08,0x09 = Unimplemented shutdown status.
10515 push bx
10516 call _shutdown_status_panic
10518 #if 0
10519 HALT(__LINE__)
10521 ;#if 0
10522 ; 0xb0, 0x20, /* mov al, #0x20 */
10523 ; 0xe6, 0x20, /* out 0x20, al ;send EOI to PIC */
10524 ;#endif
10526 pop es
10527 pop ds
10528 popa
10529 iret
10530 #endif
10532 normal_post:
10533 ; case 0: normal startup
10536 mov ax, #0xfffe
10537 mov sp, ax
10538 xor ax, ax
10539 mov ds, ax
10540 mov ss, ax
10542 ;; Save shutdown status
10543 mov 0x04b0, bl
10545 cmp bl, #0xfe
10546 jz s3_post
10548 ;; zero out BIOS data area (40:00..40:ff)
10549 mov es, ax
10550 mov cx, #0x0080 ;; 128 words
10551 mov di, #0x0400
10554 stosw
10556 call _log_bios_start
10558 ;; set all interrupts to default handler
10559 xor bx, bx ;; offset index
10560 mov cx, #0x0100 ;; counter (256 interrupts)
10561 mov ax, #dummy_iret_handler
10562 mov dx, #0xF000
10564 post_default_ints:
10565 mov [bx], ax
10566 add bx, #2
10567 mov [bx], dx
10568 add bx, #2
10569 loop post_default_ints
10571 ;; set vector 0x79 to zero
10572 ;; this is used by 'gardian angel' protection system
10573 SET_INT_VECTOR(0x79, #0, #0)
10575 ;; base memory in K 40:13 (word)
10576 mov ax, #BASE_MEM_IN_K
10577 mov 0x0413, ax
10580 ;; Manufacturing Test 40:12
10581 ;; zerod out above
10583 ;; Warm Boot Flag 0040:0072
10584 ;; value of 1234h = skip memory checks
10585 ;; zerod out above
10588 ;; Printer Services vector
10589 SET_INT_VECTOR(0x17, #0xF000, #int17_handler)
10591 ;; Bootstrap failure vector
10592 SET_INT_VECTOR(0x18, #0xF000, #int18_handler)
10594 ;; Bootstrap Loader vector
10595 SET_INT_VECTOR(0x19, #0xF000, #int19_handler)
10597 ;; User Timer Tick vector
10598 SET_INT_VECTOR(0x1c, #0xF000, #int1c_handler)
10600 ;; Memory Size Check vector
10601 SET_INT_VECTOR(0x12, #0xF000, #int12_handler)
10603 ;; Equipment Configuration Check vector
10604 SET_INT_VECTOR(0x11, #0xF000, #int11_handler)
10606 ;; System Services
10607 SET_INT_VECTOR(0x15, #0xF000, #int15_handler)
10609 ;; EBDA setup
10610 call ebda_post
10612 ;; PIT setup
10613 SET_INT_VECTOR(0x08, #0xF000, #int08_handler)
10614 ;; int 1C already points at dummy_iret_handler (above)
10615 mov al, #0x34 ; timer0: binary count, 16bit count, mode 2
10616 out 0x43, al
10617 mov al, #0x00 ; maximum count of 0000H = 18.2Hz
10618 out 0x40, al
10619 out 0x40, al
10621 ;; Keyboard
10622 SET_INT_VECTOR(0x09, #0xF000, #int09_handler)
10623 SET_INT_VECTOR(0x16, #0xF000, #int16_handler)
10625 xor ax, ax
10626 mov ds, ax
10627 mov 0x0417, al /* keyboard shift flags, set 1 */
10628 mov 0x0418, al /* keyboard shift flags, set 2 */
10629 mov 0x0419, al /* keyboard alt-numpad work area */
10630 mov 0x0471, al /* keyboard ctrl-break flag */
10631 mov 0x0497, al /* keyboard status flags 4 */
10632 mov al, #0x10
10633 mov 0x0496, al /* keyboard status flags 3 */
10636 /* keyboard head of buffer pointer */
10637 mov bx, #0x001E
10638 mov 0x041A, bx
10640 /* keyboard end of buffer pointer */
10641 mov 0x041C, bx
10643 /* keyboard pointer to start of buffer */
10644 mov bx, #0x001E
10645 mov 0x0480, bx
10647 /* keyboard pointer to end of buffer */
10648 mov bx, #0x003E
10649 mov 0x0482, bx
10651 /* init the keyboard */
10652 call _keyboard_init
10654 ;; mov CMOS Equipment Byte to BDA Equipment Word
10655 mov ax, 0x0410
10656 mov al, #0x14
10657 out 0x70, al
10658 in al, 0x71
10659 mov 0x0410, ax
10662 ;; Parallel setup
10663 SET_INT_VECTOR(0x0F, #0xF000, #dummy_iret_handler)
10664 xor ax, ax
10665 mov ds, ax
10666 xor bx, bx
10667 mov cl, #0x14 ; timeout value
10668 mov dx, #0x378 ; Parallel I/O address, port 1
10669 call detect_parport
10670 mov dx, #0x278 ; Parallel I/O address, port 2
10671 call detect_parport
10672 shl bx, #0x0e
10673 mov ax, 0x410 ; Equipment word bits 14..15 determing # parallel ports
10674 and ax, #0x3fff
10675 or ax, bx ; set number of parallel ports
10676 mov 0x410, ax
10678 ;; Serial setup
10679 SET_INT_VECTOR(0x0C, #0xF000, #dummy_iret_handler)
10680 SET_INT_VECTOR(0x14, #0xF000, #int14_handler)
10681 xor bx, bx
10682 mov cl, #0x0a ; timeout value
10683 mov dx, #0x03f8 ; Serial I/O address, port 1
10684 call detect_serial
10685 mov dx, #0x02f8 ; Serial I/O address, port 2
10686 call detect_serial
10687 mov dx, #0x03e8 ; Serial I/O address, port 3
10688 call detect_serial
10689 mov dx, #0x02e8 ; Serial I/O address, port 4
10690 call detect_serial
10691 shl bx, #0x09
10692 mov ax, 0x410 ; Equipment word bits 9..11 determing # serial ports
10693 and ax, #0xf1ff
10694 or ax, bx ; set number of serial port
10695 mov 0x410, ax
10697 ;; CMOS RTC
10698 SET_INT_VECTOR(0x1A, #0xF000, #int1a_handler)
10699 SET_INT_VECTOR(0x4A, #0xF000, #dummy_iret_handler)
10700 SET_INT_VECTOR(0x70, #0xF000, #int70_handler)
10701 ;; BIOS DATA AREA 0x4CE ???
10702 call timer_tick_post
10704 ;; PS/2 mouse setup
10705 SET_INT_VECTOR(0x74, #0xF000, #int74_handler)
10707 ;; IRQ13 (FPU exception) setup
10708 SET_INT_VECTOR(0x75, #0xF000, #int75_handler)
10710 ;; Video setup
10711 SET_INT_VECTOR(0x10, #0xF000, #int10_handler)
10713 ;; PIC
10714 call post_init_pic
10716 mov cx, #0xc000 ;; init vga bios
10717 mov ax, #0xc780
10718 call rom_scan
10720 call _print_bios_banner
10722 #if BX_ROMBIOS32
10723 call rombios32_init
10724 #else
10725 #if BX_PCIBIOS
10726 call pcibios_init_iomem_bases
10727 call pcibios_init_irqs
10728 #endif //BX_PCIBIOS
10729 #endif
10732 ;; Floppy setup
10734 call floppy_drive_post
10737 ;; Hard Drive setup
10739 call hard_drive_post
10741 #if BX_USE_ATADRV
10744 ;; ATA/ATAPI driver setup
10746 call _ata_init
10747 call _ata_detect
10750 #endif // BX_USE_ATADRV
10752 #if BX_ELTORITO_BOOT
10754 ;; eltorito floppy/harddisk emulation from cd
10756 call _cdemu_init
10758 #endif // BX_ELTORITO_BOOT
10760 call _init_boot_vectors
10762 mov cx, #0xc800 ;; init option roms
10763 mov ax, #0xe000
10764 call rom_scan
10766 #if BX_ELTORITO_BOOT
10767 call _interactive_bootkey
10768 #endif // BX_ELTORITO_BOOT
10770 sti ;; enable interrupts
10771 int #0x19
10773 .org 0xe2c3 ; NMI Handler Entry Point
10774 nmi:
10775 ;; FIXME the NMI handler should not panic
10776 ;; but iret when called from int75 (fpu exception)
10777 call _nmi_handler_msg
10778 iret
10780 int75_handler:
10781 out 0xf0, al // clear irq13
10782 call eoi_both_pics // clear interrupt
10783 int 2 // legacy nmi call
10784 iret
10786 ;-------------------------------------------
10787 ;- INT 13h Fixed Disk Services Entry Point -
10788 ;-------------------------------------------
10789 .org 0xe3fe ; INT 13h Fixed Disk Services Entry Point
10790 int13_handler:
10791 //JMPL(int13_relocated)
10792 jmp int13_relocated
10794 .org 0xe401 ; Fixed Disk Parameter Table
10796 ;----------
10797 ;- INT19h -
10798 ;----------
10799 .org 0xe6f2 ; INT 19h Boot Load Service Entry Point
10800 int19_handler:
10802 jmp int19_relocated
10803 ;-------------------------------------------
10804 ;- System BIOS Configuration Data Table
10805 ;-------------------------------------------
10806 .org BIOS_CONFIG_TABLE
10807 db 0x08 ; Table size (bytes) -Lo
10808 db 0x00 ; Table size (bytes) -Hi
10809 db SYS_MODEL_ID
10810 db SYS_SUBMODEL_ID
10811 db BIOS_REVISION
10812 ; Feature byte 1
10813 ; b7: 1=DMA channel 3 used by hard disk
10814 ; b6: 1=2 interrupt controllers present
10815 ; b5: 1=RTC present
10816 ; b4: 1=BIOS calls int 15h/4Fh every key
10817 ; b3: 1=wait for extern event supported (Int 15h/41h)
10818 ; b2: 1=extended BIOS data area used
10819 ; b1: 0=AT or ESDI bus, 1=MicroChannel
10820 ; b0: 1=Dual bus (MicroChannel + ISA)
10821 db (0 << 7) | \
10822 (1 << 6) | \
10823 (1 << 5) | \
10824 (BX_CALL_INT15_4F << 4) | \
10825 (0 << 3) | \
10826 (BX_USE_EBDA << 2) | \
10827 (0 << 1) | \
10828 (0 << 0)
10829 ; Feature byte 2
10830 ; b7: 1=32-bit DMA supported
10831 ; b6: 1=int16h, function 9 supported
10832 ; b5: 1=int15h/C6h (get POS data) supported
10833 ; b4: 1=int15h/C7h (get mem map info) supported
10834 ; b3: 1=int15h/C8h (en/dis CPU) supported
10835 ; b2: 1=non-8042 kb controller
10836 ; b1: 1=data streaming supported
10837 ; b0: reserved
10838 db (0 << 7) | \
10839 (1 << 6) | \
10840 (0 << 5) | \
10841 (0 << 4) | \
10842 (0 << 3) | \
10843 (0 << 2) | \
10844 (0 << 1) | \
10845 (0 << 0)
10846 ; Feature byte 3
10847 ; b7: not used
10848 ; b6: reserved
10849 ; b5: reserved
10850 ; b4: POST supports ROM-to-RAM enable/disable
10851 ; b3: SCSI on system board
10852 ; b2: info panel installed
10853 ; b1: Initial Machine Load (IML) system - BIOS on disk
10854 ; b0: SCSI supported in IML
10855 db 0x00
10856 ; Feature byte 4
10857 ; b7: IBM private
10858 ; b6: EEPROM present
10859 ; b5-3: ABIOS presence (011 = not supported)
10860 ; b2: private
10861 ; b1: memory split above 16Mb supported
10862 ; b0: POSTEXT directly supported by POST
10863 db 0x00
10864 ; Feature byte 5 (IBM)
10865 ; b1: enhanced mouse
10866 ; b0: flash EPROM
10867 db 0x00
10871 .org 0xe729 ; Baud Rate Generator Table
10873 ;----------
10874 ;- INT14h -
10875 ;----------
10876 .org 0xe739 ; INT 14h Serial Communications Service Entry Point
10877 int14_handler:
10878 push ds
10879 pusha
10880 xor ax, ax
10881 mov ds, ax
10882 call _int14_function
10883 popa
10884 pop ds
10885 iret
10888 ;----------------------------------------
10889 ;- INT 16h Keyboard Service Entry Point -
10890 ;----------------------------------------
10891 .org 0xe82e
10892 int16_handler:
10895 push ds
10896 pushf
10897 pusha
10899 cmp ah, #0x00
10900 je int16_F00
10901 cmp ah, #0x10
10902 je int16_F00
10904 mov bx, #0xf000
10905 mov ds, bx
10906 call _int16_function
10907 popa
10908 popf
10909 pop ds
10910 jz int16_zero_set
10912 int16_zero_clear:
10913 push bp
10914 mov bp, sp
10915 //SEG SS
10916 and BYTE [bp + 0x06], #0xbf
10917 pop bp
10918 iret
10920 int16_zero_set:
10921 push bp
10922 mov bp, sp
10923 //SEG SS
10924 or BYTE [bp + 0x06], #0x40
10925 pop bp
10926 iret
10928 int16_F00:
10929 mov bx, #0x0040
10930 mov ds, bx
10932 int16_wait_for_key:
10934 mov bx, 0x001a
10935 cmp bx, 0x001c
10936 jne int16_key_found
10939 #if 0
10940 /* no key yet, call int 15h, function AX=9002 */
10941 0x50, /* push AX */
10942 0xb8, 0x02, 0x90, /* mov AX, #0x9002 */
10943 0xcd, 0x15, /* int 15h */
10944 0x58, /* pop AX */
10945 0xeb, 0xea, /* jmp WAIT_FOR_KEY */
10946 #endif
10947 jmp int16_wait_for_key
10949 int16_key_found:
10950 mov bx, #0xf000
10951 mov ds, bx
10952 call _int16_function
10953 popa
10954 popf
10955 pop ds
10956 #if 0
10957 /* notify int16 complete w/ int 15h, function AX=9102 */
10958 0x50, /* push AX */
10959 0xb8, 0x02, 0x91, /* mov AX, #0x9102 */
10960 0xcd, 0x15, /* int 15h */
10961 0x58, /* pop AX */
10962 #endif
10963 iret
10967 ;-------------------------------------------------
10968 ;- INT09h : Keyboard Hardware Service Entry Point -
10969 ;-------------------------------------------------
10970 .org 0xe987
10971 int09_handler:
10973 push ax
10975 mov al, #0xAD ;;disable keyboard
10976 out #0x64, al
10978 mov al, #0x0B
10979 out #0x20, al
10980 in al, #0x20
10981 and al, #0x02
10982 jz int09_finish
10984 in al, #0x60 ;;read key from keyboard controller
10986 push ds
10987 pusha
10988 #ifdef BX_CALL_INT15_4F
10989 mov ah, #0x4f ;; allow for keyboard intercept
10991 int #0x15
10992 jnc int09_done
10993 #endif
10995 ;; check for extended key
10996 cmp al, #0xe0
10997 jne int09_check_pause
10998 xor ax, ax
10999 mov ds, ax
11000 mov al, BYTE [0x496] ;; mf2_state |= 0x02
11001 or al, #0x02
11002 mov BYTE [0x496], al
11003 jmp int09_done
11005 int09_check_pause: ;; check for pause key
11006 cmp al, #0xe1
11007 jne int09_process_key
11008 xor ax, ax
11009 mov ds, ax
11010 mov al, BYTE [0x496] ;; mf2_state |= 0x01
11011 or al, #0x01
11012 mov BYTE [0x496], al
11013 jmp int09_done
11015 int09_process_key:
11016 mov bx, #0xf000
11017 mov ds, bx
11018 call _int09_function
11020 int09_done:
11021 popa
11022 pop ds
11024 call eoi_master_pic
11026 int09_finish:
11027 mov al, #0xAE ;;enable keyboard
11028 out #0x64, al
11029 pop ax
11030 iret
11033 ;----------------------------------------
11034 ;- INT 13h Diskette Service Entry Point -
11035 ;----------------------------------------
11036 .org 0xec59
11037 int13_diskette:
11038 jmp int13_noeltorito
11040 ;---------------------------------------------
11041 ;- INT 0Eh Diskette Hardware ISR Entry Point -
11042 ;---------------------------------------------
11043 .org 0xef57 ; INT 0Eh Diskette Hardware ISR Entry Point
11044 int0e_handler:
11045 push ax
11046 push dx
11047 mov dx, #0x03f4
11048 in al, dx
11049 and al, #0xc0
11050 cmp al, #0xc0
11051 je int0e_normal
11052 mov dx, #0x03f5
11053 mov al, #0x08 ; sense interrupt status
11054 out dx, al
11055 int0e_loop1:
11056 mov dx, #0x03f4
11057 in al, dx
11058 and al, #0xc0
11059 cmp al, #0xc0
11060 jne int0e_loop1
11061 int0e_loop2:
11062 mov dx, #0x03f5
11063 in al, dx
11064 mov dx, #0x03f4
11065 in al, dx
11066 and al, #0xc0
11067 cmp al, #0xc0
11068 je int0e_loop2
11069 int0e_normal:
11070 push ds
11071 xor ax, ax ;; segment 0000
11072 mov ds, ax
11073 call eoi_master_pic
11074 mov al, 0x043e
11075 or al, #0x80 ;; diskette interrupt has occurred
11076 mov 0x043e, al
11077 pop ds
11078 pop dx
11079 pop ax
11080 iret
11083 .org 0xefc7 ; Diskette Controller Parameter Table
11084 diskette_param_table:
11085 ;; Since no provisions are made for multiple drive types, most
11086 ;; values in this table are ignored. I set parameters for 1.44M
11087 ;; floppy here
11088 db 0xAF
11089 db 0x02 ;; head load time 0000001, DMA used
11090 db 0x25
11091 db 0x02
11092 db 18
11093 db 0x1B
11094 db 0xFF
11095 db 0x6C
11096 db 0xF6
11097 db 0x0F
11098 db 0x08
11101 ;----------------------------------------
11102 ;- INT17h : Printer Service Entry Point -
11103 ;----------------------------------------
11104 .org 0xefd2
11105 int17_handler:
11106 push ds
11107 pusha
11108 xor ax, ax
11109 mov ds, ax
11110 call _int17_function
11111 popa
11112 pop ds
11113 iret
11115 diskette_param_table2:
11116 ;; New diskette parameter table adding 3 parameters from IBM
11117 ;; Since no provisions are made for multiple drive types, most
11118 ;; values in this table are ignored. I set parameters for 1.44M
11119 ;; floppy here
11120 db 0xAF
11121 db 0x02 ;; head load time 0000001, DMA used
11122 db 0x25
11123 db 0x02
11124 db 18
11125 db 0x1B
11126 db 0xFF
11127 db 0x6C
11128 db 0xF6
11129 db 0x0F
11130 db 0x08
11131 db 79 ;; maximum track
11132 db 0 ;; data transfer rate
11133 db 4 ;; drive type in cmos
11135 .org 0xf045 ; INT 10 Functions 0-Fh Entry Point
11136 HALT(__LINE__)
11137 iret
11139 ;----------
11140 ;- INT10h -
11141 ;----------
11142 .org 0xf065 ; INT 10h Video Support Service Entry Point
11143 int10_handler:
11144 ;; dont do anything, since the VGA BIOS handles int10h requests
11145 iret
11147 .org 0xf0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
11149 ;----------
11150 ;- INT12h -
11151 ;----------
11152 .org 0xf841 ; INT 12h Memory Size Service Entry Point
11153 ; ??? different for Pentium (machine check)?
11154 int12_handler:
11155 push ds
11156 mov ax, #0x0040
11157 mov ds, ax
11158 mov ax, 0x0013
11159 pop ds
11160 iret
11162 ;----------
11163 ;- INT11h -
11164 ;----------
11165 .org 0xf84d ; INT 11h Equipment List Service Entry Point
11166 int11_handler:
11167 push ds
11168 mov ax, #0x0040
11169 mov ds, ax
11170 mov ax, 0x0010
11171 pop ds
11172 iret
11174 ;----------
11175 ;- INT15h -
11176 ;----------
11177 .org 0xf859 ; INT 15h System Services Entry Point
11178 int15_handler:
11179 pushf
11180 #if BX_APM
11181 cmp ah, #0x53
11182 je apm_call
11183 #endif
11184 push ds
11185 push es
11186 cmp ah, #0x86
11187 je int15_handler32
11188 cmp ah, #0xE8
11189 je int15_handler32
11190 pusha
11191 #if BX_USE_PS2_MOUSE
11192 cmp ah, #0xC2
11193 je int15_handler_mouse
11194 #endif
11195 call _int15_function
11196 int15_handler_mouse_ret:
11197 popa
11198 int15_handler32_ret:
11199 pop es
11200 pop ds
11201 popf
11202 jmp iret_modify_cf
11203 #if BX_APM
11204 apm_call:
11205 jmp _apmreal_entry
11206 #endif
11208 #if BX_USE_PS2_MOUSE
11209 int15_handler_mouse:
11210 call _int15_function_mouse
11211 jmp int15_handler_mouse_ret
11212 #endif
11214 int15_handler32:
11215 pushad
11216 call _int15_function32
11217 popad
11218 jmp int15_handler32_ret
11220 ;; Protected mode IDT descriptor
11222 ;; I just make the limit 0, so the machine will shutdown
11223 ;; if an exception occurs during protected mode memory
11224 ;; transfers.
11226 ;; Set base to f0000 to correspond to beginning of BIOS,
11227 ;; in case I actually define an IDT later
11228 ;; Set limit to 0
11230 pmode_IDT_info:
11231 dw 0x0000 ;; limit 15:00
11232 dw 0x0000 ;; base 15:00
11233 db 0x0f ;; base 23:16
11235 ;; Real mode IDT descriptor
11237 ;; Set to typical real-mode values.
11238 ;; base = 000000
11239 ;; limit = 03ff
11241 rmode_IDT_info:
11242 dw 0x03ff ;; limit 15:00
11243 dw 0x0000 ;; base 15:00
11244 db 0x00 ;; base 23:16
11247 ;----------
11248 ;- INT1Ah -
11249 ;----------
11250 .org 0xfe6e ; INT 1Ah Time-of-day Service Entry Point
11251 int1a_handler:
11252 #if BX_PCIBIOS
11253 cmp ah, #0xb1
11254 jne int1a_normal
11255 call pcibios_real
11256 jc pcibios_error
11257 retf 2
11258 pcibios_error:
11259 mov bl, ah
11260 mov ah, #0xb1
11261 push ds
11262 pusha
11263 mov ax, ss ; set readable descriptor to ds, for calling pcibios
11264 mov ds, ax ; on 16bit protected mode.
11265 jmp int1a_callfunction
11266 int1a_normal:
11267 #endif
11268 push ds
11269 pusha
11270 xor ax, ax
11271 mov ds, ax
11272 int1a_callfunction:
11273 call _int1a_function
11274 popa
11275 pop ds
11276 iret
11279 ;; int70h: IRQ8 - CMOS RTC
11281 int70_handler:
11282 push ds
11283 pushad
11284 xor ax, ax
11285 mov ds, ax
11286 call _int70_function
11287 popad
11288 pop ds
11289 iret
11291 ;---------
11292 ;- INT08 -
11293 ;---------
11294 .org 0xfea5 ; INT 08h System Timer ISR Entry Point
11295 int08_handler:
11297 push eax
11298 push ds
11299 xor ax, ax
11300 mov ds, ax
11302 ;; time to turn off drive(s)?
11303 mov al,0x0440
11304 or al,al
11305 jz int08_floppy_off
11306 dec al
11307 mov 0x0440,al
11308 jnz int08_floppy_off
11309 ;; turn motor(s) off
11310 push dx
11311 mov dx,#0x03f2
11312 in al,dx
11313 and al,#0xcf
11314 out dx,al
11315 pop dx
11316 int08_floppy_off:
11318 mov eax, 0x046c ;; get ticks dword
11319 inc eax
11321 ;; compare eax to one days worth of timer ticks at 18.2 hz
11322 cmp eax, #0x001800B0
11323 jb int08_store_ticks
11324 ;; there has been a midnight rollover at this point
11325 xor eax, eax ;; zero out counter
11326 inc BYTE 0x0470 ;; increment rollover flag
11328 int08_store_ticks:
11329 mov 0x046c, eax ;; store new ticks dword
11330 ;; chain to user timer tick INT #0x1c
11331 //pushf
11332 //;; call_ep [ds:loc]
11333 //CALL_EP( 0x1c << 2 )
11334 int #0x1c
11336 call eoi_master_pic
11337 pop ds
11338 pop eax
11339 iret
11341 .org 0xfef3 ; Initial Interrupt Vector Offsets Loaded by POST
11344 .org 0xff00
11345 .ascii BIOS_COPYRIGHT_STRING
11347 ;------------------------------------------------
11348 ;- IRET Instruction for Dummy Interrupt Handler -
11349 ;------------------------------------------------
11350 .org 0xff53 ; IRET Instruction for Dummy Interrupt Handler
11351 dummy_iret_handler:
11352 iret
11354 .org 0xff54 ; INT 05h Print Screen Service Entry Point
11355 HALT(__LINE__)
11356 iret
11358 .org 0xfff0 ; Power-up Entry Point
11359 jmp 0xf000:post
11361 .org 0xfff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
11362 .ascii BIOS_BUILD_DATE
11364 .org 0xfffe ; System Model ID
11365 db SYS_MODEL_ID
11366 db 0x00 ; filler
11368 .org 0xfa6e ;; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
11369 ASM_END
11371 * This font comes from the fntcol16.zip package (c) by Joseph Gil
11372 * found at ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip
11373 * This font is public domain
11375 static Bit8u vgafont8[128*8]=
11377 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
11378 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e,
11379 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e,
11380 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00,
11381 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00,
11382 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c,
11383 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c,
11384 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00,
11385 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff,
11386 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
11387 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff,
11388 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78,
11389 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18,
11390 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0,
11391 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0,
11392 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99,
11393 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00,
11394 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00,
11395 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18,
11396 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
11397 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00,
11398 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78,
11399 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00,
11400 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff,
11401 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00,
11402 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
11403 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00,
11404 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
11405 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00,
11406 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00,
11407 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00,
11408 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00,
11409 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
11410 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00,
11411 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
11412 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00,
11413 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00,
11414 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00,
11415 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00,
11416 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
11417 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00,
11418 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
11419 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
11420 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00,
11421 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60,
11422 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
11423 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00,
11424 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00,
11425 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00,
11426 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00,
11427 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00,
11428 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00,
11429 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00,
11430 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00,
11431 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00,
11432 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00,
11433 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00,
11434 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00,
11435 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00,
11436 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60,
11437 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00,
11438 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00,
11439 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00,
11440 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00,
11441 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00,
11442 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00,
11443 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00,
11444 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00,
11445 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00,
11446 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00,
11447 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00,
11448 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00,
11449 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00,
11450 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
11451 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00,
11452 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00,
11453 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00,
11454 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00,
11455 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00,
11456 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00,
11457 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
11458 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00,
11459 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00,
11460 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00,
11461 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
11462 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00,
11463 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
11464 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00,
11465 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00,
11466 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00,
11467 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00,
11468 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
11469 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00,
11470 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
11471 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
11472 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
11473 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
11474 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00,
11475 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00,
11476 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00,
11477 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00,
11478 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
11479 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00,
11480 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
11481 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00,
11482 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
11483 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78,
11484 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00,
11485 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
11486 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00,
11487 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
11488 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
11489 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0,
11490 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e,
11491 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00,
11492 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00,
11493 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00,
11494 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
11495 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
11496 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00,
11497 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00,
11498 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
11499 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00,
11500 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00,
11501 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
11502 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00,
11503 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
11504 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00,
11507 ASM_START
11508 .org 0xcc00
11509 bios_table_area_end:
11510 // bcc-generated data will be placed here
11511 ASM_END