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