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