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