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