kvm: bios: make the bios map the tss pages at unavailble memory to the guest
[qemu-kvm/fedora.git] / kvm / bios / rombios.c
bloba9b565317bcbb358368fc12b20e08f3cd6823965
1 /////////////////////////////////////////////////////////////////////////
2 // $Id: rombios.c,v 1.182 2007/08/01 17:09:51 vruppert Exp $
3 /////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (C) 2002 MandrakeSoft S.A.
6 //
7 // MandrakeSoft S.A.
8 // 43, rue d'Aboukir
9 // 75002 Paris - France
10 // http://www.linux-mandrake.com/
11 // http://www.mandrakesoft.com/
13 // This library is free software; you can redistribute it and/or
14 // modify it under the terms of the GNU Lesser General Public
15 // License as published by the Free Software Foundation; either
16 // version 2 of the License, or (at your option) any later version.
18 // This library is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 // Lesser General Public License for more details.
23 // You should have received a copy of the GNU Lesser General Public
24 // License along with this library; if not, write to the Free Software
25 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27 // ROM BIOS for use with Bochs/Plex86/QEMU emulation environment
30 // ROM BIOS compatability entry points:
31 // ===================================
32 // $e05b ; POST Entry Point
33 // $e2c3 ; NMI Handler Entry Point
34 // $e3fe ; INT 13h Fixed Disk Services Entry Point
35 // $e401 ; Fixed Disk Parameter Table
36 // $e6f2 ; INT 19h Boot Load Service Entry Point
37 // $e6f5 ; Configuration Data Table
38 // $e729 ; Baud Rate Generator Table
39 // $e739 ; INT 14h Serial Communications Service Entry Point
40 // $e82e ; INT 16h Keyboard Service Entry Point
41 // $e987 ; INT 09h Keyboard Service Entry Point
42 // $ec59 ; INT 13h Diskette Service Entry Point
43 // $ef57 ; INT 0Eh Diskette Hardware ISR Entry Point
44 // $efc7 ; Diskette Controller Parameter Table
45 // $efd2 ; INT 17h Printer Service Entry Point
46 // $f045 ; INT 10 Functions 0-Fh Entry Point
47 // $f065 ; INT 10h Video Support Service Entry Point
48 // $f0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
49 // $f841 ; INT 12h Memory Size Service Entry Point
50 // $f84d ; INT 11h Equipment List Service Entry Point
51 // $f859 ; INT 15h System Services Entry Point
52 // $fa6e ; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
53 // $fe6e ; INT 1Ah Time-of-day Service Entry Point
54 // $fea5 ; INT 08h System Timer ISR Entry Point
55 // $fef3 ; Initial Interrupt Vector Offsets Loaded by POST
56 // $ff53 ; IRET Instruction for Dummy Interrupt Handler
57 // $ff54 ; INT 05h Print Screen Service Entry Point
58 // $fff0 ; Power-up Entry Point
59 // $fff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
60 // $fffe ; System Model ID
62 // NOTES for ATA/ATAPI driver (cbbochs@free.fr)
63 // Features
64 // - supports up to 4 ATA interfaces
65 // - device/geometry detection
66 // - 16bits/32bits device access
67 // - pchs/lba access
68 // - datain/dataout/packet command support
70 // NOTES for El-Torito Boot (cbbochs@free.fr)
71 // - CD-ROM booting is only available if ATA/ATAPI Driver is available
72 // - Current code is only able to boot mono-session cds
73 // - Current code can not boot and emulate a hard-disk
74 // the bios will panic otherwise
75 // - Current code also use memory in EBDA segement.
76 // - I used cmos byte 0x3D to store extended information on boot-device
77 // - Code has to be modified modified to handle multiple cdrom drives
78 // - Here are the cdrom boot failure codes:
79 // 1 : no atapi device found
80 // 2 : no atapi cdrom found
81 // 3 : can not read cd - BRVD
82 // 4 : cd is not eltorito (BRVD)
83 // 5 : cd is not eltorito (ISO TAG)
84 // 6 : cd is not eltorito (ELTORITO TAG)
85 // 7 : can not read cd - boot catalog
86 // 8 : boot catalog : bad header
87 // 9 : boot catalog : bad platform
88 // 10 : boot catalog : bad signature
89 // 11 : boot catalog : bootable flag not set
90 // 12 : can not read cd - boot image
92 // ATA driver
93 // - EBDA segment.
94 // I used memory starting at 0x121 in the segment
95 // - the translation policy is defined in cmos regs 0x39 & 0x3a
97 // TODO :
99 // int74
100 // - needs to be reworked. Uses direct [bp] offsets. (?)
102 // int13:
103 // - f04 (verify sectors) isn't complete (?)
104 // - f02/03/04 should set current cyl,etc in BDA (?)
105 // - rewrite int13_relocated & clean up int13 entry code
107 // NOTES:
108 // - NMI access (bit7 of addr written to 70h)
110 // ATA driver
111 // - should handle the "don't detect" bit (cmos regs 0x3b & 0x3c)
112 // - could send the multiple-sector read/write commands
114 // El-Torito
115 // - Emulate a Hard-disk (currently only diskette can be emulated) see "FIXME ElTorito Harddisk"
116 // - Implement remaining int13_cdemu functions (as defined by El-Torito specs)
117 // - cdrom drive is hardcoded to ide 0 device 1 in several places. see "FIXME ElTorito Hardcoded"
118 // - int13 Fix DL when emulating a cd. In that case DL is decremented before calling real int13.
119 // This is ok. But DL should be reincremented afterwards.
120 // - Fix all "FIXME ElTorito Various"
121 // - should be able to boot any cdrom instead of the first one
123 // BCC Bug: find a generic way to handle the bug of #asm after an "if" (fixed in 0.16.7)
125 #include "rombios.h"
127 #define DEBUG_ATA 0
128 #define DEBUG_INT13_HD 0
129 #define DEBUG_INT13_CD 0
130 #define DEBUG_INT13_ET 0
131 #define DEBUG_INT13_FL 0
132 #define DEBUG_INT15 0
133 #define DEBUG_INT16 0
134 #define DEBUG_INT1A 0
135 #define DEBUG_INT74 0
136 #define DEBUG_APM 0
138 #define BX_CPU 3
139 #define BX_USE_PS2_MOUSE 1
140 #define BX_CALL_INT15_4F 1
141 #define BX_USE_EBDA 1
142 #define BX_SUPPORT_FLOPPY 1
143 #define BX_FLOPPY_ON_CNT 37 /* 2 seconds */
144 #define BX_PCIBIOS 1
145 #define BX_APM 1
147 #define BX_USE_ATADRV 1
148 #define BX_ELTORITO_BOOT 1
150 #define BX_MAX_ATA_INTERFACES 4
151 #define BX_MAX_ATA_DEVICES (BX_MAX_ATA_INTERFACES*2)
153 #define BX_VIRTUAL_PORTS 1 /* normal output to Bochs ports */
154 #define BX_DEBUG_SERIAL 0 /* output to COM1 */
156 /* model byte 0xFC = AT */
157 #define SYS_MODEL_ID 0xFC
158 #define SYS_SUBMODEL_ID 0x00
159 #define BIOS_REVISION 1
160 #define BIOS_CONFIG_TABLE 0xe6f5
162 #ifndef BIOS_BUILD_DATE
163 # define BIOS_BUILD_DATE "06/23/99"
164 #endif
166 // 1K of base memory used for Extended Bios Data Area (EBDA)
167 // EBDA is used for PS/2 mouse support, and IDE BIOS, etc.
168 #define EBDA_SEG 0x9FC0
169 #define EBDA_SIZE 1 // In KiB
170 #define BASE_MEM_IN_K (640 - EBDA_SIZE)
172 // Define the application NAME
173 #if defined(BX_QEMU)
174 # define BX_APPNAME "QEMU"
175 #elif defined(PLEX86)
176 # define BX_APPNAME "Plex86"
177 #else
178 # define BX_APPNAME "Bochs"
179 #endif
181 // Sanity Checks
182 #if BX_USE_ATADRV && BX_CPU<3
183 # error The ATA/ATAPI Driver can only to be used with a 386+ cpu
184 #endif
185 #if BX_USE_ATADRV && !BX_USE_EBDA
186 # error ATA/ATAPI Driver can only be used if EBDA is available
187 #endif
188 #if BX_ELTORITO_BOOT && !BX_USE_ATADRV
189 # error El-Torito Boot can only be use if ATA/ATAPI Driver is available
190 #endif
191 #if BX_PCIBIOS && BX_CPU<3
192 # error PCI BIOS can only be used with 386+ cpu
193 #endif
194 #if BX_APM && BX_CPU<3
195 # error APM BIOS can only be used with 386+ cpu
196 #endif
198 // define this if you want to make PCIBIOS working on a specific bridge only
199 // undef enables PCIBIOS when at least one PCI device is found
200 // i440FX is emulated by Bochs and QEMU
201 #define PCI_FIXED_HOST_BRIDGE 0x12378086 ;; i440FX PCI bridge
203 // #20 is dec 20
204 // #$20 is hex 20 = 32
205 // #0x20 is hex 20 = 32
206 // LDA #$20
207 // JSR $E820
208 // LDD .i,S
209 // JSR $C682
210 // mov al, #$20
212 // all hex literals should be prefixed with '0x'
213 // grep "#[0-9a-fA-F][0-9a-fA-F]" rombios.c
214 // no mov SEG-REG, #value, must mov register into seg-reg
215 // grep -i "mov[ ]*.s" rombios.c
217 // This is for compiling with gcc2 and gcc3
218 #define ASM_START #asm
219 #define ASM_END #endasm
221 ASM_START
222 .rom
224 .org 0x0000
226 #if BX_CPU >= 3
227 use16 386
228 #else
229 use16 286
230 #endif
232 MACRO HALT
233 ;; the HALT macro is called with the line number of the HALT call.
234 ;; The line number is then sent to the PANIC_PORT, causing Bochs/Plex
235 ;; to print a BX_PANIC message. This will normally halt the simulation
236 ;; with a message such as "BIOS panic at rombios.c, line 4091".
237 ;; However, users can choose to make panics non-fatal and continue.
238 #if BX_VIRTUAL_PORTS
239 mov dx,#PANIC_PORT
240 mov ax,#?1
241 out dx,ax
242 #else
243 mov dx,#0x80
244 mov ax,#?1
245 out dx,al
246 #endif
247 MEND
249 MACRO JMP_AP
250 db 0xea
251 dw ?2
252 dw ?1
253 MEND
255 MACRO SET_INT_VECTOR
256 mov ax, ?3
257 mov ?1*4, ax
258 mov ax, ?2
259 mov ?1*4+2, ax
260 MEND
262 ASM_END
264 typedef unsigned char Bit8u;
265 typedef unsigned short Bit16u;
266 typedef unsigned short bx_bool;
267 typedef unsigned long Bit32u;
270 void memsetb(seg,offset,value,count);
271 void memcpyb(dseg,doffset,sseg,soffset,count);
272 void memcpyd(dseg,doffset,sseg,soffset,count);
274 // memset of count bytes
275 void
276 memsetb(seg,offset,value,count)
277 Bit16u seg;
278 Bit16u offset;
279 Bit16u value;
280 Bit16u count;
282 ASM_START
283 push bp
284 mov bp, sp
286 push ax
287 push cx
288 push es
289 push di
291 mov cx, 10[bp] ; count
292 cmp cx, #0x00
293 je memsetb_end
294 mov ax, 4[bp] ; segment
295 mov es, ax
296 mov ax, 6[bp] ; offset
297 mov di, ax
298 mov al, 8[bp] ; value
301 stosb
303 memsetb_end:
304 pop di
305 pop es
306 pop cx
307 pop ax
309 pop bp
310 ASM_END
313 // memcpy of count bytes
314 void
315 memcpyb(dseg,doffset,sseg,soffset,count)
316 Bit16u dseg;
317 Bit16u doffset;
318 Bit16u sseg;
319 Bit16u soffset;
320 Bit16u count;
322 ASM_START
323 push bp
324 mov bp, sp
326 push ax
327 push cx
328 push es
329 push di
330 push ds
331 push si
333 mov cx, 12[bp] ; count
334 cmp cx, #0x0000
335 je memcpyb_end
336 mov ax, 4[bp] ; dsegment
337 mov es, ax
338 mov ax, 6[bp] ; doffset
339 mov di, ax
340 mov ax, 8[bp] ; ssegment
341 mov ds, ax
342 mov ax, 10[bp] ; soffset
343 mov si, ax
346 movsb
348 memcpyb_end:
349 pop si
350 pop ds
351 pop di
352 pop es
353 pop cx
354 pop ax
356 pop bp
357 ASM_END
360 // memcpy of count dword
361 void
362 memcpyd(dseg,doffset,sseg,soffset,count)
363 Bit16u dseg;
364 Bit16u doffset;
365 Bit16u sseg;
366 Bit16u soffset;
367 Bit16u count;
369 ASM_START
370 push bp
371 mov bp, sp
373 push ax
374 push cx
375 push es
376 push di
377 push ds
378 push si
380 mov cx, 12[bp] ; count
381 cmp cx, #0x0000
382 je memcpyd_end
383 mov ax, 4[bp] ; dsegment
384 mov es, ax
385 mov ax, 6[bp] ; doffset
386 mov di, ax
387 mov ax, 8[bp] ; ssegment
388 mov ds, ax
389 mov ax, 10[bp] ; soffset
390 mov si, ax
393 movsd
395 memcpyd_end:
396 pop si
397 pop ds
398 pop di
399 pop es
400 pop cx
401 pop ax
403 pop bp
404 ASM_END
407 // read_dword and write_dword functions
408 static Bit32u read_dword();
409 static void write_dword();
411 Bit32u
412 read_dword(seg, offset)
413 Bit16u seg;
414 Bit16u offset;
416 ASM_START
417 push bp
418 mov bp, sp
420 push bx
421 push ds
422 mov ax, 4[bp] ; segment
423 mov ds, ax
424 mov bx, 6[bp] ; offset
425 mov ax, [bx]
426 inc bx
427 inc bx
428 mov dx, [bx]
429 ;; ax = return value (word)
430 ;; dx = return value (word)
431 pop ds
432 pop bx
434 pop bp
435 ASM_END
438 void
439 write_dword(seg, offset, data)
440 Bit16u seg;
441 Bit16u offset;
442 Bit32u data;
444 ASM_START
445 push bp
446 mov bp, sp
448 push ax
449 push bx
450 push ds
451 mov ax, 4[bp] ; segment
452 mov ds, ax
453 mov bx, 6[bp] ; offset
454 mov ax, 8[bp] ; data word
455 mov [bx], ax ; write data word
456 inc bx
457 inc bx
458 mov ax, 10[bp] ; data word
459 mov [bx], ax ; write data word
460 pop ds
461 pop bx
462 pop ax
464 pop bp
465 ASM_END
468 // Bit32u (unsigned long) and long helper functions
469 ASM_START
471 ;; and function
472 landl:
473 landul:
474 SEG SS
475 and ax,[di]
476 SEG SS
477 and bx,2[di]
480 ;; add function
481 laddl:
482 laddul:
483 SEG SS
484 add ax,[di]
485 SEG SS
486 adc bx,2[di]
489 ;; cmp function
490 lcmpl:
491 lcmpul:
492 and eax, #0x0000FFFF
493 shl ebx, #16
494 add eax, ebx
495 shr ebx, #16
496 SEG SS
497 cmp eax, dword ptr [di]
500 ;; sub function
501 lsubl:
502 lsubul:
503 SEG SS
504 sub ax,[di]
505 SEG SS
506 sbb bx,2[di]
509 ;; mul function
510 lmull:
511 lmulul:
512 and eax, #0x0000FFFF
513 shl ebx, #16
514 add eax, ebx
515 SEG SS
516 mul eax, dword ptr [di]
517 mov ebx, eax
518 shr ebx, #16
521 ;; dec function
522 ldecl:
523 ldecul:
524 SEG SS
525 dec dword ptr [bx]
528 ;; or function
529 lorl:
530 lorul:
531 SEG SS
532 or ax,[di]
533 SEG SS
534 or bx,2[di]
537 ;; inc function
538 lincl:
539 lincul:
540 SEG SS
541 inc dword ptr [bx]
544 ;; tst function
545 ltstl:
546 ltstul:
547 and eax, #0x0000FFFF
548 shl ebx, #16
549 add eax, ebx
550 shr ebx, #16
551 test eax, eax
554 ;; sr function
555 lsrul:
556 mov cx,di
557 jcxz lsr_exit
558 and eax, #0x0000FFFF
559 shl ebx, #16
560 add eax, ebx
561 lsr_loop:
562 shr eax, #1
563 loop lsr_loop
564 mov ebx, eax
565 shr ebx, #16
566 lsr_exit:
569 ;; sl function
570 lsll:
571 lslul:
572 mov cx,di
573 jcxz lsl_exit
574 and eax, #0x0000FFFF
575 shl ebx, #16
576 add eax, ebx
577 lsl_loop:
578 shl eax, #1
579 loop lsl_loop
580 mov ebx, eax
581 shr ebx, #16
582 lsl_exit:
585 idiv_:
587 idiv bx
590 idiv_u:
591 xor dx,dx
592 div bx
595 ldivul:
596 and eax, #0x0000FFFF
597 shl ebx, #16
598 add eax, ebx
599 xor edx, edx
600 SEG SS
601 mov bx, 2[di]
602 shl ebx, #16
603 SEG SS
604 mov bx, [di]
605 div ebx
606 mov ebx, eax
607 shr ebx, #16
610 ASM_END
612 // for access to RAM area which is used by interrupt vectors
613 // and BIOS Data Area
615 typedef struct {
616 unsigned char filler1[0x400];
617 unsigned char filler2[0x6c];
618 Bit16u ticks_low;
619 Bit16u ticks_high;
620 Bit8u midnight_flag;
621 } bios_data_t;
623 #define BiosData ((bios_data_t *) 0)
625 #if BX_USE_ATADRV
626 typedef struct {
627 Bit16u heads; // # heads
628 Bit16u cylinders; // # cylinders
629 Bit16u spt; // # sectors / track
630 } chs_t;
632 // DPTE definition
633 typedef struct {
634 Bit16u iobase1;
635 Bit16u iobase2;
636 Bit8u prefix;
637 Bit8u unused;
638 Bit8u irq;
639 Bit8u blkcount;
640 Bit8u dma;
641 Bit8u pio;
642 Bit16u options;
643 Bit16u reserved;
644 Bit8u revision;
645 Bit8u checksum;
646 } dpte_t;
648 typedef struct {
649 Bit8u iface; // ISA or PCI
650 Bit16u iobase1; // IO Base 1
651 Bit16u iobase2; // IO Base 2
652 Bit8u irq; // IRQ
653 } ata_channel_t;
655 typedef struct {
656 Bit8u type; // Detected type of ata (ata/atapi/none/unknown)
657 Bit8u device; // Detected type of attached devices (hd/cd/none)
658 Bit8u removable; // Removable device flag
659 Bit8u lock; // Locks for removable devices
660 // Bit8u lba_capable; // LBA capable flag - always yes for bochs devices
661 Bit8u mode; // transfert mode : PIO 16/32 bits - IRQ - ISADMA - PCIDMA
662 Bit16u blksize; // block size
664 Bit8u translation; // type of translation
665 chs_t lchs; // Logical CHS
666 chs_t pchs; // Physical CHS
668 Bit32u sectors; // Total sectors count
669 } ata_device_t;
671 typedef struct {
672 // ATA channels info
673 ata_channel_t channels[BX_MAX_ATA_INTERFACES];
675 // ATA devices info
676 ata_device_t devices[BX_MAX_ATA_DEVICES];
678 // map between (bios hd id - 0x80) and ata channels
679 Bit8u hdcount, hdidmap[BX_MAX_ATA_DEVICES];
681 // map between (bios cd id - 0xE0) and ata channels
682 Bit8u cdcount, cdidmap[BX_MAX_ATA_DEVICES];
684 // Buffer for DPTE table
685 dpte_t dpte;
687 // Count of transferred sectors and bytes
688 Bit16u trsfsectors;
689 Bit32u trsfbytes;
691 } ata_t;
693 #if BX_ELTORITO_BOOT
694 // ElTorito Device Emulation data
695 typedef struct {
696 Bit8u active;
697 Bit8u media;
698 Bit8u emulated_drive;
699 Bit8u controller_index;
700 Bit16u device_spec;
701 Bit32u ilba;
702 Bit16u buffer_segment;
703 Bit16u load_segment;
704 Bit16u sector_count;
706 // Virtual device
707 chs_t vdevice;
708 } cdemu_t;
709 #endif // BX_ELTORITO_BOOT
711 // for access to EBDA area
712 // The EBDA structure should conform to
713 // http://www.frontiernet.net/~fys/rombios.htm document
714 // I made the ata and cdemu structs begin at 0x121 in the EBDA seg
715 // EBDA must be at most 768 bytes; it lives at 0x9fc00, and the boot
716 // device tables are at 0x9ff00 -- 0x9ffff
717 typedef struct {
718 unsigned char filler1[0x3D];
720 // FDPT - Can be splitted in data members if needed
721 unsigned char fdpt0[0x10];
722 unsigned char fdpt1[0x10];
724 unsigned char filler2[0xC4];
726 // ATA Driver data
727 ata_t ata;
729 #if BX_ELTORITO_BOOT
730 // El Torito Emulation data
731 cdemu_t cdemu;
732 #endif // BX_ELTORITO_BOOT
734 } ebda_data_t;
736 #define EbdaData ((ebda_data_t *) 0)
738 // for access to the int13ext structure
739 typedef struct {
740 Bit8u size;
741 Bit8u reserved;
742 Bit16u count;
743 Bit16u offset;
744 Bit16u segment;
745 Bit32u lba1;
746 Bit32u lba2;
747 } int13ext_t;
749 #define Int13Ext ((int13ext_t *) 0)
751 // Disk Physical Table definition
752 typedef struct {
753 Bit16u size;
754 Bit16u infos;
755 Bit32u cylinders;
756 Bit32u heads;
757 Bit32u spt;
758 Bit32u sector_count1;
759 Bit32u sector_count2;
760 Bit16u blksize;
761 Bit16u dpte_segment;
762 Bit16u dpte_offset;
763 Bit16u key;
764 Bit8u dpi_length;
765 Bit8u reserved1;
766 Bit16u reserved2;
767 Bit8u host_bus[4];
768 Bit8u iface_type[8];
769 Bit8u iface_path[8];
770 Bit8u device_path[8];
771 Bit8u reserved3;
772 Bit8u checksum;
773 } dpt_t;
775 #define Int13DPT ((dpt_t *) 0)
777 #endif // BX_USE_ATADRV
779 typedef struct {
780 union {
781 struct {
782 Bit16u di, si, bp, sp;
783 Bit16u bx, dx, cx, ax;
784 } r16;
785 struct {
786 Bit16u filler[4];
787 Bit8u bl, bh, dl, dh, cl, ch, al, ah;
788 } r8;
789 } u;
790 } pusha_regs_t;
792 typedef struct {
793 union {
794 struct {
795 Bit32u edi, esi, ebp, esp;
796 Bit32u ebx, edx, ecx, eax;
797 } r32;
798 struct {
799 Bit16u di, filler1, si, filler2, bp, filler3, sp, filler4;
800 Bit16u bx, filler5, dx, filler6, cx, filler7, ax, filler8;
801 } r16;
802 struct {
803 Bit32u filler[4];
804 Bit8u bl, bh;
805 Bit16u filler1;
806 Bit8u dl, dh;
807 Bit16u filler2;
808 Bit8u cl, ch;
809 Bit16u filler3;
810 Bit8u al, ah;
811 Bit16u filler4;
812 } r8;
813 } u;
814 } pushad_regs_t;
816 typedef struct {
817 union {
818 struct {
819 Bit16u flags;
820 } r16;
821 struct {
822 Bit8u flagsl;
823 Bit8u flagsh;
824 } r8;
825 } u;
826 } flags_t;
828 #define SetCF(x) x.u.r8.flagsl |= 0x01
829 #define SetZF(x) x.u.r8.flagsl |= 0x40
830 #define ClearCF(x) x.u.r8.flagsl &= 0xfe
831 #define ClearZF(x) x.u.r8.flagsl &= 0xbf
832 #define GetCF(x) (x.u.r8.flagsl & 0x01)
834 typedef struct {
835 Bit16u ip;
836 Bit16u cs;
837 flags_t flags;
838 } iret_addr_t;
842 static Bit8u inb();
843 static Bit8u inb_cmos();
844 static void outb();
845 static void outb_cmos();
846 static Bit16u inw();
847 static void outw();
848 static void init_rtc();
849 static bx_bool rtc_updating();
851 static Bit8u read_byte();
852 static Bit16u read_word();
853 static void write_byte();
854 static void write_word();
855 static void bios_printf();
857 static Bit8u inhibit_mouse_int_and_events();
858 static void enable_mouse_int_and_events();
859 static Bit8u send_to_mouse_ctrl();
860 static Bit8u get_mouse_data();
861 static void set_kbd_command_byte();
863 static void int09_function();
864 static void int13_harddisk();
865 static void int13_cdrom();
866 static void int13_cdemu();
867 static void int13_eltorito();
868 static void int13_diskette_function();
869 static void int14_function();
870 static void int15_function();
871 static void int16_function();
872 static void int17_function();
873 static void int19_function();
874 static void int1a_function();
875 static void int70_function();
876 static void int74_function();
877 static Bit16u get_CS();
878 static Bit16u get_SS();
879 static unsigned int enqueue_key();
880 static unsigned int dequeue_key();
881 static void get_hd_geometry();
882 static void set_diskette_ret_status();
883 static void set_diskette_current_cyl();
884 static void determine_floppy_media();
885 static bx_bool floppy_drive_exists();
886 static bx_bool floppy_drive_recal();
887 static bx_bool floppy_media_known();
888 static bx_bool floppy_media_sense();
889 static bx_bool set_enable_a20();
890 static void debugger_on();
891 static void debugger_off();
892 static void keyboard_init();
893 static void keyboard_panic();
894 static void shutdown_status_panic();
895 static void nmi_handler_msg();
897 static void print_bios_banner();
898 static void print_boot_device();
899 static void print_boot_failure();
900 static void print_cdromboot_failure();
902 # if BX_USE_ATADRV
904 // ATA / ATAPI driver
905 void ata_init();
906 void ata_detect();
907 void ata_reset();
909 Bit16u ata_cmd_non_data();
910 Bit16u ata_cmd_data_in();
911 Bit16u ata_cmd_data_out();
912 Bit16u ata_cmd_packet();
914 Bit16u atapi_get_sense();
915 Bit16u atapi_is_ready();
916 Bit16u atapi_is_cdrom();
918 #endif // BX_USE_ATADRV
920 #if BX_ELTORITO_BOOT
922 void cdemu_init();
923 Bit8u cdemu_isactive();
924 Bit8u cdemu_emulated_drive();
926 Bit16u cdrom_boot();
928 #endif // BX_ELTORITO_BOOT
930 static char bios_cvs_version_string[] = "$Revision: 1.182 $ $Date: 2007/08/01 17:09:51 $";
932 #define BIOS_COPYRIGHT_STRING "(c) 2002 MandrakeSoft S.A. Written by Kevin Lawton & the Bochs team."
934 #if DEBUG_ATA
935 # define BX_DEBUG_ATA(a...) BX_DEBUG(a)
936 #else
937 # define BX_DEBUG_ATA(a...)
938 #endif
939 #if DEBUG_INT13_HD
940 # define BX_DEBUG_INT13_HD(a...) BX_DEBUG(a)
941 #else
942 # define BX_DEBUG_INT13_HD(a...)
943 #endif
944 #if DEBUG_INT13_CD
945 # define BX_DEBUG_INT13_CD(a...) BX_DEBUG(a)
946 #else
947 # define BX_DEBUG_INT13_CD(a...)
948 #endif
949 #if DEBUG_INT13_ET
950 # define BX_DEBUG_INT13_ET(a...) BX_DEBUG(a)
951 #else
952 # define BX_DEBUG_INT13_ET(a...)
953 #endif
954 #if DEBUG_INT13_FL
955 # define BX_DEBUG_INT13_FL(a...) BX_DEBUG(a)
956 #else
957 # define BX_DEBUG_INT13_FL(a...)
958 #endif
959 #if DEBUG_INT15
960 # define BX_DEBUG_INT15(a...) BX_DEBUG(a)
961 #else
962 # define BX_DEBUG_INT15(a...)
963 #endif
964 #if DEBUG_INT16
965 # define BX_DEBUG_INT16(a...) BX_DEBUG(a)
966 #else
967 # define BX_DEBUG_INT16(a...)
968 #endif
969 #if DEBUG_INT1A
970 # define BX_DEBUG_INT1A(a...) BX_DEBUG(a)
971 #else
972 # define BX_DEBUG_INT1A(a...)
973 #endif
974 #if DEBUG_INT74
975 # define BX_DEBUG_INT74(a...) BX_DEBUG(a)
976 #else
977 # define BX_DEBUG_INT74(a...)
978 #endif
980 #define SET_AL(val8) AX = ((AX & 0xff00) | (val8))
981 #define SET_BL(val8) BX = ((BX & 0xff00) | (val8))
982 #define SET_CL(val8) CX = ((CX & 0xff00) | (val8))
983 #define SET_DL(val8) DX = ((DX & 0xff00) | (val8))
984 #define SET_AH(val8) AX = ((AX & 0x00ff) | ((val8) << 8))
985 #define SET_BH(val8) BX = ((BX & 0x00ff) | ((val8) << 8))
986 #define SET_CH(val8) CX = ((CX & 0x00ff) | ((val8) << 8))
987 #define SET_DH(val8) DX = ((DX & 0x00ff) | ((val8) << 8))
989 #define GET_AL() ( AX & 0x00ff )
990 #define GET_BL() ( BX & 0x00ff )
991 #define GET_CL() ( CX & 0x00ff )
992 #define GET_DL() ( DX & 0x00ff )
993 #define GET_AH() ( AX >> 8 )
994 #define GET_BH() ( BX >> 8 )
995 #define GET_CH() ( CX >> 8 )
996 #define GET_DH() ( DX >> 8 )
998 #define GET_ELDL() ( ELDX & 0x00ff )
999 #define GET_ELDH() ( ELDX >> 8 )
1001 #define SET_CF() FLAGS |= 0x0001
1002 #define CLEAR_CF() FLAGS &= 0xfffe
1003 #define GET_CF() (FLAGS & 0x0001)
1005 #define SET_ZF() FLAGS |= 0x0040
1006 #define CLEAR_ZF() FLAGS &= 0xffbf
1007 #define GET_ZF() (FLAGS & 0x0040)
1009 #define UNSUPPORTED_FUNCTION 0x86
1011 #define none 0
1012 #define MAX_SCAN_CODE 0x58
1014 static struct {
1015 Bit16u normal;
1016 Bit16u shift;
1017 Bit16u control;
1018 Bit16u alt;
1019 Bit8u lock_flags;
1020 } scan_to_scanascii[MAX_SCAN_CODE + 1] = {
1021 { none, none, none, none, none },
1022 { 0x011b, 0x011b, 0x011b, 0x0100, none }, /* escape */
1023 { 0x0231, 0x0221, none, 0x7800, none }, /* 1! */
1024 { 0x0332, 0x0340, 0x0300, 0x7900, none }, /* 2@ */
1025 { 0x0433, 0x0423, none, 0x7a00, none }, /* 3# */
1026 { 0x0534, 0x0524, none, 0x7b00, none }, /* 4$ */
1027 { 0x0635, 0x0625, none, 0x7c00, none }, /* 5% */
1028 { 0x0736, 0x075e, 0x071e, 0x7d00, none }, /* 6^ */
1029 { 0x0837, 0x0826, none, 0x7e00, none }, /* 7& */
1030 { 0x0938, 0x092a, none, 0x7f00, none }, /* 8* */
1031 { 0x0a39, 0x0a28, none, 0x8000, none }, /* 9( */
1032 { 0x0b30, 0x0b29, none, 0x8100, none }, /* 0) */
1033 { 0x0c2d, 0x0c5f, 0x0c1f, 0x8200, none }, /* -_ */
1034 { 0x0d3d, 0x0d2b, none, 0x8300, none }, /* =+ */
1035 { 0x0e08, 0x0e08, 0x0e7f, none, none }, /* backspace */
1036 { 0x0f09, 0x0f00, none, none, none }, /* tab */
1037 { 0x1071, 0x1051, 0x1011, 0x1000, 0x40 }, /* Q */
1038 { 0x1177, 0x1157, 0x1117, 0x1100, 0x40 }, /* W */
1039 { 0x1265, 0x1245, 0x1205, 0x1200, 0x40 }, /* E */
1040 { 0x1372, 0x1352, 0x1312, 0x1300, 0x40 }, /* R */
1041 { 0x1474, 0x1454, 0x1414, 0x1400, 0x40 }, /* T */
1042 { 0x1579, 0x1559, 0x1519, 0x1500, 0x40 }, /* Y */
1043 { 0x1675, 0x1655, 0x1615, 0x1600, 0x40 }, /* U */
1044 { 0x1769, 0x1749, 0x1709, 0x1700, 0x40 }, /* I */
1045 { 0x186f, 0x184f, 0x180f, 0x1800, 0x40 }, /* O */
1046 { 0x1970, 0x1950, 0x1910, 0x1900, 0x40 }, /* P */
1047 { 0x1a5b, 0x1a7b, 0x1a1b, none, none }, /* [{ */
1048 { 0x1b5d, 0x1b7d, 0x1b1d, none, none }, /* ]} */
1049 { 0x1c0d, 0x1c0d, 0x1c0a, none, none }, /* Enter */
1050 { none, none, none, none, none }, /* L Ctrl */
1051 { 0x1e61, 0x1e41, 0x1e01, 0x1e00, 0x40 }, /* A */
1052 { 0x1f73, 0x1f53, 0x1f13, 0x1f00, 0x40 }, /* S */
1053 { 0x2064, 0x2044, 0x2004, 0x2000, 0x40 }, /* D */
1054 { 0x2166, 0x2146, 0x2106, 0x2100, 0x40 }, /* F */
1055 { 0x2267, 0x2247, 0x2207, 0x2200, 0x40 }, /* G */
1056 { 0x2368, 0x2348, 0x2308, 0x2300, 0x40 }, /* H */
1057 { 0x246a, 0x244a, 0x240a, 0x2400, 0x40 }, /* J */
1058 { 0x256b, 0x254b, 0x250b, 0x2500, 0x40 }, /* K */
1059 { 0x266c, 0x264c, 0x260c, 0x2600, 0x40 }, /* L */
1060 { 0x273b, 0x273a, none, none, none }, /* ;: */
1061 { 0x2827, 0x2822, none, none, none }, /* '" */
1062 { 0x2960, 0x297e, none, none, none }, /* `~ */
1063 { none, none, none, none, none }, /* L shift */
1064 { 0x2b5c, 0x2b7c, 0x2b1c, none, none }, /* |\ */
1065 { 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00, 0x40 }, /* Z */
1066 { 0x2d78, 0x2d58, 0x2d18, 0x2d00, 0x40 }, /* X */
1067 { 0x2e63, 0x2e43, 0x2e03, 0x2e00, 0x40 }, /* C */
1068 { 0x2f76, 0x2f56, 0x2f16, 0x2f00, 0x40 }, /* V */
1069 { 0x3062, 0x3042, 0x3002, 0x3000, 0x40 }, /* B */
1070 { 0x316e, 0x314e, 0x310e, 0x3100, 0x40 }, /* N */
1071 { 0x326d, 0x324d, 0x320d, 0x3200, 0x40 }, /* M */
1072 { 0x332c, 0x333c, none, none, none }, /* ,< */
1073 { 0x342e, 0x343e, none, none, none }, /* .> */
1074 { 0x352f, 0x353f, none, none, none }, /* /? */
1075 { none, none, none, none, none }, /* R Shift */
1076 { 0x372a, 0x372a, none, none, none }, /* * */
1077 { none, none, none, none, none }, /* L Alt */
1078 { 0x3920, 0x3920, 0x3920, 0x3920, none }, /* space */
1079 { none, none, none, none, none }, /* caps lock */
1080 { 0x3b00, 0x5400, 0x5e00, 0x6800, none }, /* F1 */
1081 { 0x3c00, 0x5500, 0x5f00, 0x6900, none }, /* F2 */
1082 { 0x3d00, 0x5600, 0x6000, 0x6a00, none }, /* F3 */
1083 { 0x3e00, 0x5700, 0x6100, 0x6b00, none }, /* F4 */
1084 { 0x3f00, 0x5800, 0x6200, 0x6c00, none }, /* F5 */
1085 { 0x4000, 0x5900, 0x6300, 0x6d00, none }, /* F6 */
1086 { 0x4100, 0x5a00, 0x6400, 0x6e00, none }, /* F7 */
1087 { 0x4200, 0x5b00, 0x6500, 0x6f00, none }, /* F8 */
1088 { 0x4300, 0x5c00, 0x6600, 0x7000, none }, /* F9 */
1089 { 0x4400, 0x5d00, 0x6700, 0x7100, none }, /* F10 */
1090 { none, none, none, none, none }, /* Num Lock */
1091 { none, none, none, none, none }, /* Scroll Lock */
1092 { 0x4700, 0x4737, 0x7700, none, 0x20 }, /* 7 Home */
1093 { 0x4800, 0x4838, none, none, 0x20 }, /* 8 UP */
1094 { 0x4900, 0x4939, 0x8400, none, 0x20 }, /* 9 PgUp */
1095 { 0x4a2d, 0x4a2d, none, none, none }, /* - */
1096 { 0x4b00, 0x4b34, 0x7300, none, 0x20 }, /* 4 Left */
1097 { 0x4c00, 0x4c35, none, none, 0x20 }, /* 5 */
1098 { 0x4d00, 0x4d36, 0x7400, none, 0x20 }, /* 6 Right */
1099 { 0x4e2b, 0x4e2b, none, none, none }, /* + */
1100 { 0x4f00, 0x4f31, 0x7500, none, 0x20 }, /* 1 End */
1101 { 0x5000, 0x5032, none, none, 0x20 }, /* 2 Down */
1102 { 0x5100, 0x5133, 0x7600, none, 0x20 }, /* 3 PgDn */
1103 { 0x5200, 0x5230, none, none, 0x20 }, /* 0 Ins */
1104 { 0x5300, 0x532e, none, none, 0x20 }, /* Del */
1105 { none, none, none, none, none },
1106 { none, none, none, none, none },
1107 { 0x565c, 0x567c, none, none, none }, /* \| */
1108 { 0x5700, 0x5700, none, none, none }, /* F11 */
1109 { 0x5800, 0x5800, none, none, none } /* F12 */
1112 Bit8u
1113 inb(port)
1114 Bit16u port;
1116 ASM_START
1117 push bp
1118 mov bp, sp
1120 push dx
1121 mov dx, 4[bp]
1122 in al, dx
1123 pop dx
1125 pop bp
1126 ASM_END
1129 #if BX_USE_ATADRV
1130 Bit16u
1131 inw(port)
1132 Bit16u port;
1134 ASM_START
1135 push bp
1136 mov bp, sp
1138 push dx
1139 mov dx, 4[bp]
1140 in ax, dx
1141 pop dx
1143 pop bp
1144 ASM_END
1146 #endif
1148 void
1149 outb(port, val)
1150 Bit16u port;
1151 Bit8u val;
1153 ASM_START
1154 push bp
1155 mov bp, sp
1157 push ax
1158 push dx
1159 mov dx, 4[bp]
1160 mov al, 6[bp]
1161 out dx, al
1162 pop dx
1163 pop ax
1165 pop bp
1166 ASM_END
1169 #if BX_USE_ATADRV
1170 void
1171 outw(port, val)
1172 Bit16u port;
1173 Bit16u val;
1175 ASM_START
1176 push bp
1177 mov bp, sp
1179 push ax
1180 push dx
1181 mov dx, 4[bp]
1182 mov ax, 6[bp]
1183 out dx, ax
1184 pop dx
1185 pop ax
1187 pop bp
1188 ASM_END
1190 #endif
1192 void
1193 outb_cmos(cmos_reg, val)
1194 Bit8u cmos_reg;
1195 Bit8u val;
1197 ASM_START
1198 push bp
1199 mov bp, sp
1201 mov al, 4[bp] ;; cmos_reg
1202 out 0x70, al
1203 mov al, 6[bp] ;; val
1204 out 0x71, al
1206 pop bp
1207 ASM_END
1210 Bit8u
1211 inb_cmos(cmos_reg)
1212 Bit8u cmos_reg;
1214 ASM_START
1215 push bp
1216 mov bp, sp
1218 mov al, 4[bp] ;; cmos_reg
1219 out 0x70, al
1220 in al, 0x71
1222 pop bp
1223 ASM_END
1226 void
1227 init_rtc()
1229 outb_cmos(0x0a, 0x26);
1230 outb_cmos(0x0b, 0x02);
1231 inb_cmos(0x0c);
1232 inb_cmos(0x0d);
1235 bx_bool
1236 rtc_updating()
1238 // This function checks to see if the update-in-progress bit
1239 // is set in CMOS Status Register A. If not, it returns 0.
1240 // If it is set, it tries to wait until there is a transition
1241 // to 0, and will return 0 if such a transition occurs. A 1
1242 // is returned only after timing out. The maximum period
1243 // that this bit should be set is constrained to 244useconds.
1244 // The count I use below guarantees coverage or more than
1245 // this time, with any reasonable IPS setting.
1247 Bit16u count;
1249 count = 25000;
1250 while (--count != 0) {
1251 if ( (inb_cmos(0x0a) & 0x80) == 0 )
1252 return(0);
1254 return(1); // update-in-progress never transitioned to 0
1258 Bit8u
1259 read_byte(seg, offset)
1260 Bit16u seg;
1261 Bit16u offset;
1263 ASM_START
1264 push bp
1265 mov bp, sp
1267 push bx
1268 push ds
1269 mov ax, 4[bp] ; segment
1270 mov ds, ax
1271 mov bx, 6[bp] ; offset
1272 mov al, [bx]
1273 ;; al = return value (byte)
1274 pop ds
1275 pop bx
1277 pop bp
1278 ASM_END
1281 Bit16u
1282 read_word(seg, offset)
1283 Bit16u seg;
1284 Bit16u offset;
1286 ASM_START
1287 push bp
1288 mov bp, sp
1290 push bx
1291 push ds
1292 mov ax, 4[bp] ; segment
1293 mov ds, ax
1294 mov bx, 6[bp] ; offset
1295 mov ax, [bx]
1296 ;; ax = return value (word)
1297 pop ds
1298 pop bx
1300 pop bp
1301 ASM_END
1304 void
1305 write_byte(seg, offset, data)
1306 Bit16u seg;
1307 Bit16u offset;
1308 Bit8u data;
1310 ASM_START
1311 push bp
1312 mov bp, sp
1314 push ax
1315 push bx
1316 push ds
1317 mov ax, 4[bp] ; segment
1318 mov ds, ax
1319 mov bx, 6[bp] ; offset
1320 mov al, 8[bp] ; data byte
1321 mov [bx], al ; write data byte
1322 pop ds
1323 pop bx
1324 pop ax
1326 pop bp
1327 ASM_END
1330 void
1331 write_word(seg, offset, data)
1332 Bit16u seg;
1333 Bit16u offset;
1334 Bit16u data;
1336 ASM_START
1337 push bp
1338 mov bp, sp
1340 push ax
1341 push bx
1342 push ds
1343 mov ax, 4[bp] ; segment
1344 mov ds, ax
1345 mov bx, 6[bp] ; offset
1346 mov ax, 8[bp] ; data word
1347 mov [bx], ax ; write data word
1348 pop ds
1349 pop bx
1350 pop ax
1352 pop bp
1353 ASM_END
1356 Bit16u
1357 get_CS()
1359 ASM_START
1360 mov ax, cs
1361 ASM_END
1364 Bit16u
1365 get_SS()
1367 ASM_START
1368 mov ax, ss
1369 ASM_END
1372 #if BX_DEBUG_SERIAL
1373 /* serial debug port*/
1374 #define BX_DEBUG_PORT 0x03f8
1376 /* data */
1377 #define UART_RBR 0x00
1378 #define UART_THR 0x00
1380 /* control */
1381 #define UART_IER 0x01
1382 #define UART_IIR 0x02
1383 #define UART_FCR 0x02
1384 #define UART_LCR 0x03
1385 #define UART_MCR 0x04
1386 #define UART_DLL 0x00
1387 #define UART_DLM 0x01
1389 /* status */
1390 #define UART_LSR 0x05
1391 #define UART_MSR 0x06
1392 #define UART_SCR 0x07
1394 int uart_can_tx_byte(base_port)
1395 Bit16u base_port;
1397 return inb(base_port + UART_LSR) & 0x20;
1400 void uart_wait_to_tx_byte(base_port)
1401 Bit16u base_port;
1403 while (!uart_can_tx_byte(base_port));
1406 void uart_wait_until_sent(base_port)
1407 Bit16u base_port;
1409 while (!(inb(base_port + UART_LSR) & 0x40));
1412 void uart_tx_byte(base_port, data)
1413 Bit16u base_port;
1414 Bit8u data;
1416 uart_wait_to_tx_byte(base_port);
1417 outb(base_port + UART_THR, data);
1418 uart_wait_until_sent(base_port);
1420 #endif
1422 void
1423 wrch(c)
1424 Bit8u c;
1426 ASM_START
1427 push bp
1428 mov bp, sp
1430 push bx
1431 mov ah, #0x0e
1432 mov al, 4[bp]
1433 xor bx,bx
1434 int #0x10
1435 pop bx
1437 pop bp
1438 ASM_END
1441 void
1442 send(action, c)
1443 Bit16u action;
1444 Bit8u c;
1446 #if BX_DEBUG_SERIAL
1447 if (c == '\n') uart_tx_byte(BX_DEBUG_PORT, '\r');
1448 uart_tx_byte(BX_DEBUG_PORT, c);
1449 #endif
1450 #if BX_VIRTUAL_PORTS
1451 if (action & BIOS_PRINTF_DEBUG) outb(DEBUG_PORT, c);
1452 if (action & BIOS_PRINTF_INFO) outb(INFO_PORT, c);
1453 #endif
1454 if (action & BIOS_PRINTF_SCREEN) {
1455 if (c == '\n') wrch('\r');
1456 wrch(c);
1460 void
1461 put_int(action, val, width, neg)
1462 Bit16u action;
1463 short val, width;
1464 bx_bool neg;
1466 short nval = val / 10;
1467 if (nval)
1468 put_int(action, nval, width - 1, neg);
1469 else {
1470 while (--width > 0) send(action, ' ');
1471 if (neg) send(action, '-');
1473 send(action, val - (nval * 10) + '0');
1476 void
1477 put_uint(action, val, width, neg)
1478 Bit16u action;
1479 unsigned short val;
1480 short width;
1481 bx_bool neg;
1483 unsigned short nval = val / 10;
1484 if (nval)
1485 put_uint(action, nval, width - 1, neg);
1486 else {
1487 while (--width > 0) send(action, ' ');
1488 if (neg) send(action, '-');
1490 send(action, val - (nval * 10) + '0');
1493 void
1494 put_luint(action, val, width, neg)
1495 Bit16u action;
1496 unsigned long val;
1497 short width;
1498 bx_bool neg;
1500 unsigned long nval = val / 10;
1501 if (nval)
1502 put_luint(action, nval, width - 1, neg);
1503 else {
1504 while (--width > 0) send(action, ' ');
1505 if (neg) send(action, '-');
1507 send(action, val - (nval * 10) + '0');
1510 void put_str(action, s)
1511 Bit16u action;
1512 Bit8u *s;
1514 Bit8u c;
1515 if (!s)
1516 s = "<NULL>";
1518 while (c = read_byte(get_CS(), s)) {
1519 send(action, c);
1520 s++;
1524 //--------------------------------------------------------------------------
1525 // bios_printf()
1526 // A compact variable argument printf function which prints its output via
1527 // an I/O port so that it can be logged by Bochs/Plex.
1528 // Currently, only %x is supported (or %02x, %04x, etc).
1530 // Supports %[format_width][format]
1531 // where format can be d,x,c,s
1532 //--------------------------------------------------------------------------
1533 void
1534 bios_printf(action, s)
1535 Bit16u action;
1536 Bit8u *s;
1538 Bit8u c, format_char;
1539 bx_bool in_format;
1540 short i;
1541 Bit16u *arg_ptr;
1542 Bit16u arg_seg, arg, nibble, hibyte, shift_count, format_width;
1544 arg_ptr = &s;
1545 arg_seg = get_SS();
1547 in_format = 0;
1548 format_width = 0;
1550 if ((action & BIOS_PRINTF_DEBHALT) == BIOS_PRINTF_DEBHALT) {
1551 #if BX_VIRTUAL_PORTS
1552 outb(PANIC_PORT2, 0x00);
1553 #endif
1554 bios_printf (BIOS_PRINTF_SCREEN, "FATAL: ");
1557 while (c = read_byte(get_CS(), s)) {
1558 if ( c == '%' ) {
1559 in_format = 1;
1560 format_width = 0;
1562 else if (in_format) {
1563 if ( (c>='0') && (c<='9') ) {
1564 format_width = (format_width * 10) + (c - '0');
1566 else {
1567 arg_ptr++; // increment to next arg
1568 arg = read_word(arg_seg, arg_ptr);
1569 if (c == 'x') {
1570 if (format_width == 0)
1571 format_width = 4;
1572 for (i=format_width-1; i>=0; i--) {
1573 nibble = (arg >> (4 * i)) & 0x000f;
1574 send (action, (nibble<=9)? (nibble+'0') : (nibble-10+'A'));
1577 else if (c == 'u') {
1578 put_uint(action, arg, format_width, 0);
1580 else if (c == 'l') {
1581 s++;
1582 arg_ptr++; /* increment to next arg */
1583 hibyte = read_word(arg_seg, arg_ptr);
1584 put_luint(action, ((Bit32u) hibyte << 16) | arg, format_width, 0);
1586 else if (c == 'd') {
1587 if (arg & 0x8000)
1588 put_int(action, -arg, format_width - 1, 1);
1589 else
1590 put_int(action, arg, format_width, 0);
1592 else if (c == 's') {
1593 put_str(action, arg);
1595 else if (c == 'c') {
1596 send(action, arg);
1598 else
1599 BX_PANIC("bios_printf: unknown format\n");
1600 in_format = 0;
1603 else {
1604 send(action, c);
1606 s ++;
1609 if (action & BIOS_PRINTF_HALT) {
1610 // freeze in a busy loop.
1611 ASM_START
1613 halt2_loop:
1615 jmp halt2_loop
1616 ASM_END
1620 //--------------------------------------------------------------------------
1621 // keyboard_init
1622 //--------------------------------------------------------------------------
1623 // this file is based on LinuxBIOS implementation of keyboard.c
1624 // could convert to #asm to gain space
1625 void
1626 keyboard_init()
1628 Bit16u max;
1630 /* ------------------- Flush buffers ------------------------*/
1631 /* Wait until buffer is empty */
1632 max=0xffff;
1633 while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
1635 /* flush incoming keys */
1636 max=0x2000;
1637 while (--max > 0) {
1638 outb(0x80, 0x00);
1639 if (inb(0x64) & 0x01) {
1640 inb(0x60);
1641 max = 0x2000;
1645 // Due to timer issues, and if the IPS setting is > 15000000,
1646 // the incoming keys might not be flushed here. That will
1647 // cause a panic a few lines below. See sourceforge bug report :
1648 // [ 642031 ] FATAL: Keyboard RESET error:993
1650 /* ------------------- controller side ----------------------*/
1651 /* send cmd = 0xAA, self test 8042 */
1652 outb(0x64, 0xaa);
1654 /* Wait until buffer is empty */
1655 max=0xffff;
1656 while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
1657 if (max==0x0) keyboard_panic(00);
1659 /* Wait for data */
1660 max=0xffff;
1661 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x01);
1662 if (max==0x0) keyboard_panic(01);
1664 /* read self-test result, 0x55 should be returned from 0x60 */
1665 if ((inb(0x60) != 0x55)){
1666 keyboard_panic(991);
1669 /* send cmd = 0xAB, keyboard interface test */
1670 outb(0x64,0xab);
1672 /* Wait until buffer is empty */
1673 max=0xffff;
1674 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x10);
1675 if (max==0x0) keyboard_panic(10);
1677 /* Wait for data */
1678 max=0xffff;
1679 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x11);
1680 if (max==0x0) keyboard_panic(11);
1682 /* read keyboard interface test result, */
1683 /* 0x00 should be returned form 0x60 */
1684 if ((inb(0x60) != 0x00)) {
1685 keyboard_panic(992);
1688 /* Enable Keyboard clock */
1689 outb(0x64,0xae);
1690 outb(0x64,0xa8);
1692 /* ------------------- keyboard side ------------------------*/
1693 /* reset kerboard and self test (keyboard side) */
1694 outb(0x60, 0xff);
1696 /* Wait until buffer is empty */
1697 max=0xffff;
1698 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x20);
1699 if (max==0x0) keyboard_panic(20);
1701 /* Wait for data */
1702 max=0xffff;
1703 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x21);
1704 if (max==0x0) keyboard_panic(21);
1706 /* keyboard should return ACK */
1707 if ((inb(0x60) != 0xfa)) {
1708 keyboard_panic(993);
1711 /* Wait for data */
1712 max=0xffff;
1713 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x31);
1714 if (max==0x0) keyboard_panic(31);
1716 if ((inb(0x60) != 0xaa)) {
1717 keyboard_panic(994);
1720 /* Disable keyboard */
1721 outb(0x60, 0xf5);
1723 /* Wait until buffer is empty */
1724 max=0xffff;
1725 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x40);
1726 if (max==0x0) keyboard_panic(40);
1728 /* Wait for data */
1729 max=0xffff;
1730 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x41);
1731 if (max==0x0) keyboard_panic(41);
1733 /* keyboard should return ACK */
1734 if ((inb(0x60) != 0xfa)) {
1735 keyboard_panic(995);
1738 /* Write Keyboard Mode */
1739 outb(0x64, 0x60);
1741 /* Wait until buffer is empty */
1742 max=0xffff;
1743 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x50);
1744 if (max==0x0) keyboard_panic(50);
1746 /* send cmd: scan code convert, disable mouse, enable IRQ 1 */
1747 outb(0x60, 0x61);
1749 /* Wait until buffer is empty */
1750 max=0xffff;
1751 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x60);
1752 if (max==0x0) keyboard_panic(60);
1754 /* Enable keyboard */
1755 outb(0x60, 0xf4);
1757 /* Wait until buffer is empty */
1758 max=0xffff;
1759 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x70);
1760 if (max==0x0) keyboard_panic(70);
1762 /* Wait for data */
1763 max=0xffff;
1764 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x71);
1765 if (max==0x0) keyboard_panic(70);
1767 /* keyboard should return ACK */
1768 if ((inb(0x60) != 0xfa)) {
1769 keyboard_panic(996);
1772 outb(0x80, 0x77);
1775 //--------------------------------------------------------------------------
1776 // keyboard_panic
1777 //--------------------------------------------------------------------------
1778 void
1779 keyboard_panic(status)
1780 Bit16u status;
1782 // If you're getting a 993 keyboard panic here,
1783 // please see the comment in keyboard_init
1785 BX_PANIC("Keyboard error:%u\n",status);
1788 //--------------------------------------------------------------------------
1789 // shutdown_status_panic
1790 // called when the shutdown statsu is not implemented, displays the status
1791 //--------------------------------------------------------------------------
1792 void
1793 shutdown_status_panic(status)
1794 Bit16u status;
1796 BX_PANIC("Unimplemented shutdown status: %02x\n",(Bit8u)status);
1799 //--------------------------------------------------------------------------
1800 // print_bios_banner
1801 // displays a the bios version
1802 //--------------------------------------------------------------------------
1803 void
1804 print_bios_banner()
1806 printf(BX_APPNAME" BIOS - build: %s\n%s\nOptions: ",
1807 BIOS_BUILD_DATE, bios_cvs_version_string);
1808 printf(
1809 #if BX_APM
1810 "apmbios "
1811 #endif
1812 #if BX_PCIBIOS
1813 "pcibios "
1814 #endif
1815 #if BX_ELTORITO_BOOT
1816 "eltorito "
1817 #endif
1818 #if BX_ROMBIOS32
1819 "rombios32 "
1820 #endif
1821 "\n\n");
1824 //--------------------------------------------------------------------------
1825 // BIOS Boot Specification 1.0.1 compatibility
1827 // Very basic support for the BIOS Boot Specification, which allows expansion
1828 // ROMs to register themselves as boot devices, instead of just stealing the
1829 // INT 19h boot vector.
1831 // This is a hack: to do it properly requires a proper PnP BIOS and we aren't
1832 // one; we just lie to the option ROMs to make them behave correctly.
1833 // We also don't support letting option ROMs register as bootable disk
1834 // drives (BCVs), only as bootable devices (BEVs).
1836 // http://www.phoenix.com/en/Customer+Services/White+Papers-Specs/pc+industry+specifications.htm
1837 //--------------------------------------------------------------------------
1839 /* 256 bytes at 0x9ff00 -- 0x9ffff is used for the IPL boot table. */
1840 #define IPL_SEG 0x9ff0
1841 #define IPL_TABLE_OFFSET 0x0000
1842 #define IPL_TABLE_ENTRIES 8
1843 #define IPL_COUNT_OFFSET 0x0080 /* u16: number of valid table entries */
1844 #define IPL_SEQUENCE_OFFSET 0x0082 /* u16: next boot device */
1846 struct ipl_entry {
1847 Bit16u type;
1848 Bit16u flags;
1849 Bit32u vector;
1850 Bit32u description;
1851 Bit32u reserved;
1854 static void
1855 init_boot_vectors()
1857 struct ipl_entry e;
1858 Bit16u count = 0;
1859 Bit16u ss = get_SS();
1861 /* Clear out the IPL table. */
1862 memsetb(IPL_SEG, IPL_TABLE_OFFSET, 0, 0xff);
1864 /* Floppy drive */
1865 e.type = 1; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
1866 memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
1867 count++;
1869 /* First HDD */
1870 e.type = 2; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
1871 memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
1872 count++;
1874 #if BX_ELTORITO_BOOT
1875 /* CDROM */
1876 e.type = 3; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
1877 memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
1878 count++;
1879 #endif
1881 /* Remember how many devices we have */
1882 write_word(IPL_SEG, IPL_COUNT_OFFSET, count);
1883 /* Not tried booting anything yet */
1884 write_word(IPL_SEG, IPL_SEQUENCE_OFFSET, 0xffff);
1887 static Bit8u
1888 get_boot_vector(i, e)
1889 Bit16u i; struct ipl_entry *e;
1891 Bit16u count;
1892 Bit16u ss = get_SS();
1893 /* Get the count of boot devices, and refuse to overrun the array */
1894 count = read_word(IPL_SEG, IPL_COUNT_OFFSET);
1895 if (i >= count) return 0;
1896 /* OK to read this device */
1897 memcpyb(ss, e, IPL_SEG, IPL_TABLE_OFFSET + i * sizeof (*e), sizeof (*e));
1898 return 1;
1902 //--------------------------------------------------------------------------
1903 // print_boot_device
1904 // displays the boot device
1905 //--------------------------------------------------------------------------
1907 static char drivetypes[][10]={"", "Floppy","Hard Disk","CD-Rom", "Network"};
1909 void
1910 print_boot_device(type)
1911 Bit16u type;
1913 /* NIC appears as type 0x80 */
1914 if (type == 0x80 ) type = 0x4;
1915 if (type == 0 || type > 0x4) BX_PANIC("Bad drive type\n");
1916 printf("Booting from %s...\n", drivetypes[type]);
1919 //--------------------------------------------------------------------------
1920 // print_boot_failure
1921 // displays the reason why boot failed
1922 //--------------------------------------------------------------------------
1923 void
1924 print_boot_failure(type, reason)
1925 Bit16u type; Bit8u reason;
1927 if (type == 0 || type > 0x3) BX_PANIC("Bad drive type\n");
1929 printf("Boot from %s failed", drivetypes[type]);
1930 if (type < 4) {
1931 /* Report the reason too */
1932 if (reason==0)
1933 printf(": not a bootable disk");
1934 else
1935 printf(": could not read the boot disk");
1937 printf("\n");
1940 //--------------------------------------------------------------------------
1941 // print_cdromboot_failure
1942 // displays the reason why boot failed
1943 //--------------------------------------------------------------------------
1944 void
1945 print_cdromboot_failure( code )
1946 Bit16u code;
1948 bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, "CDROM boot failure code : %04x\n",code);
1950 return;
1953 void
1954 nmi_handler_msg()
1956 BX_PANIC("NMI Handler called\n");
1959 void
1960 int18_panic_msg()
1962 BX_PANIC("INT18: BOOT FAILURE\n");
1965 void
1966 log_bios_start()
1968 #if BX_DEBUG_SERIAL
1969 outb(BX_DEBUG_PORT+UART_LCR, 0x03); /* setup for serial logging: 8N1 */
1970 #endif
1971 BX_INFO("%s\n", bios_cvs_version_string);
1974 bx_bool
1975 set_enable_a20(val)
1976 bx_bool val;
1978 Bit8u oldval;
1980 // Use PS2 System Control port A to set A20 enable
1982 // get current setting first
1983 oldval = inb(0x92);
1985 // change A20 status
1986 if (val)
1987 outb(0x92, oldval | 0x02);
1988 else
1989 outb(0x92, oldval & 0xfd);
1991 return((oldval & 0x02) != 0);
1994 void
1995 debugger_on()
1997 outb(0xfedc, 0x01);
2000 void
2001 debugger_off()
2003 outb(0xfedc, 0x00);
2006 #if BX_USE_ATADRV
2008 // ---------------------------------------------------------------------------
2009 // Start of ATA/ATAPI Driver
2010 // ---------------------------------------------------------------------------
2012 // Global defines -- ATA register and register bits.
2013 // command block & control block regs
2014 #define ATA_CB_DATA 0 // data reg in/out pio_base_addr1+0
2015 #define ATA_CB_ERR 1 // error in pio_base_addr1+1
2016 #define ATA_CB_FR 1 // feature reg out pio_base_addr1+1
2017 #define ATA_CB_SC 2 // sector count in/out pio_base_addr1+2
2018 #define ATA_CB_SN 3 // sector number in/out pio_base_addr1+3
2019 #define ATA_CB_CL 4 // cylinder low in/out pio_base_addr1+4
2020 #define ATA_CB_CH 5 // cylinder high in/out pio_base_addr1+5
2021 #define ATA_CB_DH 6 // device head in/out pio_base_addr1+6
2022 #define ATA_CB_STAT 7 // primary status in pio_base_addr1+7
2023 #define ATA_CB_CMD 7 // command out pio_base_addr1+7
2024 #define ATA_CB_ASTAT 6 // alternate status in pio_base_addr2+6
2025 #define ATA_CB_DC 6 // device control out pio_base_addr2+6
2026 #define ATA_CB_DA 7 // device address in pio_base_addr2+7
2028 #define ATA_CB_ER_ICRC 0x80 // ATA Ultra DMA bad CRC
2029 #define ATA_CB_ER_BBK 0x80 // ATA bad block
2030 #define ATA_CB_ER_UNC 0x40 // ATA uncorrected error
2031 #define ATA_CB_ER_MC 0x20 // ATA media change
2032 #define ATA_CB_ER_IDNF 0x10 // ATA id not found
2033 #define ATA_CB_ER_MCR 0x08 // ATA media change request
2034 #define ATA_CB_ER_ABRT 0x04 // ATA command aborted
2035 #define ATA_CB_ER_NTK0 0x02 // ATA track 0 not found
2036 #define ATA_CB_ER_NDAM 0x01 // ATA address mark not found
2038 #define ATA_CB_ER_P_SNSKEY 0xf0 // ATAPI sense key (mask)
2039 #define ATA_CB_ER_P_MCR 0x08 // ATAPI Media Change Request
2040 #define ATA_CB_ER_P_ABRT 0x04 // ATAPI command abort
2041 #define ATA_CB_ER_P_EOM 0x02 // ATAPI End of Media
2042 #define ATA_CB_ER_P_ILI 0x01 // ATAPI Illegal Length Indication
2044 // ATAPI Interrupt Reason bits in the Sector Count reg (CB_SC)
2045 #define ATA_CB_SC_P_TAG 0xf8 // ATAPI tag (mask)
2046 #define ATA_CB_SC_P_REL 0x04 // ATAPI release
2047 #define ATA_CB_SC_P_IO 0x02 // ATAPI I/O
2048 #define ATA_CB_SC_P_CD 0x01 // ATAPI C/D
2050 // bits 7-4 of the device/head (CB_DH) reg
2051 #define ATA_CB_DH_DEV0 0xa0 // select device 0
2052 #define ATA_CB_DH_DEV1 0xb0 // select device 1
2054 // status reg (CB_STAT and CB_ASTAT) bits
2055 #define ATA_CB_STAT_BSY 0x80 // busy
2056 #define ATA_CB_STAT_RDY 0x40 // ready
2057 #define ATA_CB_STAT_DF 0x20 // device fault
2058 #define ATA_CB_STAT_WFT 0x20 // write fault (old name)
2059 #define ATA_CB_STAT_SKC 0x10 // seek complete
2060 #define ATA_CB_STAT_SERV 0x10 // service
2061 #define ATA_CB_STAT_DRQ 0x08 // data request
2062 #define ATA_CB_STAT_CORR 0x04 // corrected
2063 #define ATA_CB_STAT_IDX 0x02 // index
2064 #define ATA_CB_STAT_ERR 0x01 // error (ATA)
2065 #define ATA_CB_STAT_CHK 0x01 // check (ATAPI)
2067 // device control reg (CB_DC) bits
2068 #define ATA_CB_DC_HD15 0x08 // bit should always be set to one
2069 #define ATA_CB_DC_SRST 0x04 // soft reset
2070 #define ATA_CB_DC_NIEN 0x02 // disable interrupts
2072 // Most mandtory and optional ATA commands (from ATA-3),
2073 #define ATA_CMD_CFA_ERASE_SECTORS 0xC0
2074 #define ATA_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03
2075 #define ATA_CMD_CFA_TRANSLATE_SECTOR 0x87
2076 #define ATA_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD
2077 #define ATA_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38
2078 #define ATA_CMD_CHECK_POWER_MODE1 0xE5
2079 #define ATA_CMD_CHECK_POWER_MODE2 0x98
2080 #define ATA_CMD_DEVICE_RESET 0x08
2081 #define ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90
2082 #define ATA_CMD_FLUSH_CACHE 0xE7
2083 #define ATA_CMD_FORMAT_TRACK 0x50
2084 #define ATA_CMD_IDENTIFY_DEVICE 0xEC
2085 #define ATA_CMD_IDENTIFY_DEVICE_PACKET 0xA1
2086 #define ATA_CMD_IDENTIFY_PACKET_DEVICE 0xA1
2087 #define ATA_CMD_IDLE1 0xE3
2088 #define ATA_CMD_IDLE2 0x97
2089 #define ATA_CMD_IDLE_IMMEDIATE1 0xE1
2090 #define ATA_CMD_IDLE_IMMEDIATE2 0x95
2091 #define ATA_CMD_INITIALIZE_DRIVE_PARAMETERS 0x91
2092 #define ATA_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91
2093 #define ATA_CMD_NOP 0x00
2094 #define ATA_CMD_PACKET 0xA0
2095 #define ATA_CMD_READ_BUFFER 0xE4
2096 #define ATA_CMD_READ_DMA 0xC8
2097 #define ATA_CMD_READ_DMA_QUEUED 0xC7
2098 #define ATA_CMD_READ_MULTIPLE 0xC4
2099 #define ATA_CMD_READ_SECTORS 0x20
2100 #define ATA_CMD_READ_VERIFY_SECTORS 0x40
2101 #define ATA_CMD_RECALIBRATE 0x10
2102 #define ATA_CMD_SEEK 0x70
2103 #define ATA_CMD_SET_FEATURES 0xEF
2104 #define ATA_CMD_SET_MULTIPLE_MODE 0xC6
2105 #define ATA_CMD_SLEEP1 0xE6
2106 #define ATA_CMD_SLEEP2 0x99
2107 #define ATA_CMD_STANDBY1 0xE2
2108 #define ATA_CMD_STANDBY2 0x96
2109 #define ATA_CMD_STANDBY_IMMEDIATE1 0xE0
2110 #define ATA_CMD_STANDBY_IMMEDIATE2 0x94
2111 #define ATA_CMD_WRITE_BUFFER 0xE8
2112 #define ATA_CMD_WRITE_DMA 0xCA
2113 #define ATA_CMD_WRITE_DMA_QUEUED 0xCC
2114 #define ATA_CMD_WRITE_MULTIPLE 0xC5
2115 #define ATA_CMD_WRITE_SECTORS 0x30
2116 #define ATA_CMD_WRITE_VERIFY 0x3C
2118 #define ATA_IFACE_NONE 0x00
2119 #define ATA_IFACE_ISA 0x00
2120 #define ATA_IFACE_PCI 0x01
2122 #define ATA_TYPE_NONE 0x00
2123 #define ATA_TYPE_UNKNOWN 0x01
2124 #define ATA_TYPE_ATA 0x02
2125 #define ATA_TYPE_ATAPI 0x03
2127 #define ATA_DEVICE_NONE 0x00
2128 #define ATA_DEVICE_HD 0xFF
2129 #define ATA_DEVICE_CDROM 0x05
2131 #define ATA_MODE_NONE 0x00
2132 #define ATA_MODE_PIO16 0x00
2133 #define ATA_MODE_PIO32 0x01
2134 #define ATA_MODE_ISADMA 0x02
2135 #define ATA_MODE_PCIDMA 0x03
2136 #define ATA_MODE_USEIRQ 0x10
2138 #define ATA_TRANSLATION_NONE 0
2139 #define ATA_TRANSLATION_LBA 1
2140 #define ATA_TRANSLATION_LARGE 2
2141 #define ATA_TRANSLATION_RECHS 3
2143 #define ATA_DATA_NO 0x00
2144 #define ATA_DATA_IN 0x01
2145 #define ATA_DATA_OUT 0x02
2147 // ---------------------------------------------------------------------------
2148 // ATA/ATAPI driver : initialization
2149 // ---------------------------------------------------------------------------
2150 void ata_init( )
2152 Bit16u ebda_seg=read_word(0x0040,0x000E);
2153 Bit8u channel, device;
2155 // Channels info init.
2156 for (channel=0; channel<BX_MAX_ATA_INTERFACES; channel++) {
2157 write_byte(ebda_seg,&EbdaData->ata.channels[channel].iface,ATA_IFACE_NONE);
2158 write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1,0x0);
2159 write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2,0x0);
2160 write_byte(ebda_seg,&EbdaData->ata.channels[channel].irq,0);
2163 // Devices info init.
2164 for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2165 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
2166 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_NONE);
2167 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable,0);
2168 write_byte(ebda_seg,&EbdaData->ata.devices[device].lock,0);
2169 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode,ATA_MODE_NONE);
2170 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize,0);
2171 write_byte(ebda_seg,&EbdaData->ata.devices[device].translation,ATA_TRANSLATION_NONE);
2172 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads,0);
2173 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders,0);
2174 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt,0);
2175 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads,0);
2176 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders,0);
2177 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt,0);
2179 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors,0L);
2182 // hdidmap and cdidmap init.
2183 for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2184 write_byte(ebda_seg,&EbdaData->ata.hdidmap[device],BX_MAX_ATA_DEVICES);
2185 write_byte(ebda_seg,&EbdaData->ata.cdidmap[device],BX_MAX_ATA_DEVICES);
2188 write_byte(ebda_seg,&EbdaData->ata.hdcount,0);
2189 write_byte(ebda_seg,&EbdaData->ata.cdcount,0);
2192 // ---------------------------------------------------------------------------
2193 // ATA/ATAPI driver : device detection
2194 // ---------------------------------------------------------------------------
2196 void ata_detect( )
2198 Bit16u ebda_seg=read_word(0x0040,0x000E);
2199 Bit8u hdcount, cdcount, device, type;
2200 Bit8u buffer[0x0200];
2202 #if BX_MAX_ATA_INTERFACES > 0
2203 write_byte(ebda_seg,&EbdaData->ata.channels[0].iface,ATA_IFACE_ISA);
2204 write_word(ebda_seg,&EbdaData->ata.channels[0].iobase1,0x1f0);
2205 write_word(ebda_seg,&EbdaData->ata.channels[0].iobase2,0x3f0);
2206 write_byte(ebda_seg,&EbdaData->ata.channels[0].irq,14);
2207 #endif
2208 #if BX_MAX_ATA_INTERFACES > 1
2209 write_byte(ebda_seg,&EbdaData->ata.channels[1].iface,ATA_IFACE_ISA);
2210 write_word(ebda_seg,&EbdaData->ata.channels[1].iobase1,0x170);
2211 write_word(ebda_seg,&EbdaData->ata.channels[1].iobase2,0x370);
2212 write_byte(ebda_seg,&EbdaData->ata.channels[1].irq,15);
2213 #endif
2214 #if BX_MAX_ATA_INTERFACES > 2
2215 write_byte(ebda_seg,&EbdaData->ata.channels[2].iface,ATA_IFACE_ISA);
2216 write_word(ebda_seg,&EbdaData->ata.channels[2].iobase1,0x1e8);
2217 write_word(ebda_seg,&EbdaData->ata.channels[2].iobase2,0x3e0);
2218 write_byte(ebda_seg,&EbdaData->ata.channels[2].irq,12);
2219 #endif
2220 #if BX_MAX_ATA_INTERFACES > 3
2221 write_byte(ebda_seg,&EbdaData->ata.channels[3].iface,ATA_IFACE_ISA);
2222 write_word(ebda_seg,&EbdaData->ata.channels[3].iobase1,0x168);
2223 write_word(ebda_seg,&EbdaData->ata.channels[3].iobase2,0x360);
2224 write_byte(ebda_seg,&EbdaData->ata.channels[3].irq,11);
2225 #endif
2226 #if BX_MAX_ATA_INTERFACES > 4
2227 #error Please fill the ATA interface informations
2228 #endif
2230 // Device detection
2231 hdcount=cdcount=0;
2233 for(device=0; device<BX_MAX_ATA_DEVICES; device++) {
2234 Bit16u iobase1, iobase2;
2235 Bit8u channel, slave, shift;
2236 Bit8u sc, sn, cl, ch, st;
2238 channel = device / 2;
2239 slave = device % 2;
2241 iobase1 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1);
2242 iobase2 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2);
2244 // Disable interrupts
2245 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2247 // Look for device
2248 outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2249 outb(iobase1+ATA_CB_SC, 0x55);
2250 outb(iobase1+ATA_CB_SN, 0xaa);
2251 outb(iobase1+ATA_CB_SC, 0xaa);
2252 outb(iobase1+ATA_CB_SN, 0x55);
2253 outb(iobase1+ATA_CB_SC, 0x55);
2254 outb(iobase1+ATA_CB_SN, 0xaa);
2256 // If we found something
2257 sc = inb(iobase1+ATA_CB_SC);
2258 sn = inb(iobase1+ATA_CB_SN);
2260 if ( (sc == 0x55) && (sn == 0xaa) ) {
2261 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_UNKNOWN);
2263 // reset the channel
2264 ata_reset(device);
2266 // check for ATA or ATAPI
2267 outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2268 sc = inb(iobase1+ATA_CB_SC);
2269 sn = inb(iobase1+ATA_CB_SN);
2270 if ((sc==0x01) && (sn==0x01)) {
2271 cl = inb(iobase1+ATA_CB_CL);
2272 ch = inb(iobase1+ATA_CB_CH);
2273 st = inb(iobase1+ATA_CB_STAT);
2275 if ((cl==0x14) && (ch==0xeb)) {
2276 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATAPI);
2277 } else if ((cl==0x00) && (ch==0x00) && (st!=0x00)) {
2278 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATA);
2279 } else if ((cl==0xff) && (ch==0xff)) {
2280 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
2285 type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type);
2287 // Now we send a IDENTIFY command to ATA device
2288 if(type == ATA_TYPE_ATA) {
2289 Bit32u sectors;
2290 Bit16u cylinders, heads, spt, blksize;
2291 Bit8u translation, removable, mode;
2293 //Temporary values to do the transfer
2294 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2295 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2297 if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE, 1, 0, 0, 0, 0L, get_SS(),buffer) !=0 )
2298 BX_PANIC("ata-detect: Failed to detect ATA device\n");
2300 removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2301 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2302 blksize = read_word(get_SS(),buffer+10);
2304 cylinders = read_word(get_SS(),buffer+(1*2)); // word 1
2305 heads = read_word(get_SS(),buffer+(3*2)); // word 3
2306 spt = read_word(get_SS(),buffer+(6*2)); // word 6
2308 sectors = read_dword(get_SS(),buffer+(60*2)); // word 60 and word 61
2310 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2311 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2312 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2313 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2314 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads, heads);
2315 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders, cylinders);
2316 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt, spt);
2317 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors, sectors);
2318 BX_INFO("ata%d-%d: PCHS=%u/%d/%d translation=", channel, slave,cylinders, heads, spt);
2320 translation = inb_cmos(0x39 + channel/2);
2321 for (shift=device%4; shift>0; shift--) translation >>= 2;
2322 translation &= 0x03;
2324 write_byte(ebda_seg,&EbdaData->ata.devices[device].translation, translation);
2326 switch (translation) {
2327 case ATA_TRANSLATION_NONE:
2328 BX_INFO("none");
2329 break;
2330 case ATA_TRANSLATION_LBA:
2331 BX_INFO("lba");
2332 break;
2333 case ATA_TRANSLATION_LARGE:
2334 BX_INFO("large");
2335 break;
2336 case ATA_TRANSLATION_RECHS:
2337 BX_INFO("r-echs");
2338 break;
2340 switch (translation) {
2341 case ATA_TRANSLATION_NONE:
2342 break;
2343 case ATA_TRANSLATION_LBA:
2344 spt = 63;
2345 sectors /= 63;
2346 heads = sectors / 1024;
2347 if (heads>128) heads = 255;
2348 else if (heads>64) heads = 128;
2349 else if (heads>32) heads = 64;
2350 else if (heads>16) heads = 32;
2351 else heads=16;
2352 cylinders = sectors / heads;
2353 break;
2354 case ATA_TRANSLATION_RECHS:
2355 // Take care not to overflow
2356 if (heads==16) {
2357 if(cylinders>61439) cylinders=61439;
2358 heads=15;
2359 cylinders = (Bit16u)((Bit32u)(cylinders)*16/15);
2361 // then go through the large bitshift process
2362 case ATA_TRANSLATION_LARGE:
2363 while(cylinders > 1024) {
2364 cylinders >>= 1;
2365 heads <<= 1;
2367 // If we max out the head count
2368 if (heads > 127) break;
2370 break;
2372 // clip to 1024 cylinders in lchs
2373 if (cylinders > 1024) cylinders=1024;
2374 BX_INFO(" LCHS=%d/%d/%d\n", cylinders, heads, spt);
2376 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads, heads);
2377 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders, cylinders);
2378 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt, spt);
2380 // fill hdidmap
2381 write_byte(ebda_seg,&EbdaData->ata.hdidmap[hdcount], device);
2382 hdcount++;
2385 // Now we send a IDENTIFY command to ATAPI device
2386 if(type == ATA_TYPE_ATAPI) {
2388 Bit8u type, removable, mode;
2389 Bit16u blksize;
2391 //Temporary values to do the transfer
2392 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_CDROM);
2393 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2395 if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE_PACKET, 1, 0, 0, 0, 0L, get_SS(),buffer) != 0)
2396 BX_PANIC("ata-detect: Failed to detect ATAPI device\n");
2398 type = read_byte(get_SS(),buffer+1) & 0x1f;
2399 removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2400 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2401 blksize = 2048;
2403 write_byte(ebda_seg,&EbdaData->ata.devices[device].device, type);
2404 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2405 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2406 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2408 // fill cdidmap
2409 write_byte(ebda_seg,&EbdaData->ata.cdidmap[cdcount], device);
2410 cdcount++;
2414 Bit32u sizeinmb;
2415 Bit16u ataversion;
2416 Bit8u c, i, version, model[41];
2418 switch (type) {
2419 case ATA_TYPE_ATA:
2420 sizeinmb = read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors);
2421 sizeinmb >>= 11;
2422 case ATA_TYPE_ATAPI:
2423 // Read ATA/ATAPI version
2424 ataversion=((Bit16u)(read_byte(get_SS(),buffer+161))<<8)|read_byte(get_SS(),buffer+160);
2425 for(version=15;version>0;version--) {
2426 if((ataversion&(1<<version))!=0)
2427 break;
2430 // Read model name
2431 for(i=0;i<20;i++){
2432 write_byte(get_SS(),model+(i*2),read_byte(get_SS(),buffer+(i*2)+54+1));
2433 write_byte(get_SS(),model+(i*2)+1,read_byte(get_SS(),buffer+(i*2)+54));
2436 // Reformat
2437 write_byte(get_SS(),model+40,0x00);
2438 for(i=39;i>0;i--){
2439 if(read_byte(get_SS(),model+i)==0x20)
2440 write_byte(get_SS(),model+i,0x00);
2441 else break;
2443 break;
2446 switch (type) {
2447 case ATA_TYPE_ATA:
2448 printf("ata%d %s: ",channel,slave?" slave":"master");
2449 i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
2450 printf(" ATA-%d Hard-Disk (%lu MBytes)\n", version, sizeinmb);
2451 break;
2452 case ATA_TYPE_ATAPI:
2453 printf("ata%d %s: ",channel,slave?" slave":"master");
2454 i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
2455 if(read_byte(ebda_seg,&EbdaData->ata.devices[device].device)==ATA_DEVICE_CDROM)
2456 printf(" ATAPI-%d CD-Rom/DVD-Rom\n",version);
2457 else
2458 printf(" ATAPI-%d Device\n",version);
2459 break;
2460 case ATA_TYPE_UNKNOWN:
2461 printf("ata%d %s: Unknown device\n",channel,slave?" slave":"master");
2462 break;
2467 // Store the devices counts
2468 write_byte(ebda_seg,&EbdaData->ata.hdcount, hdcount);
2469 write_byte(ebda_seg,&EbdaData->ata.cdcount, cdcount);
2470 write_byte(0x40,0x75, hdcount);
2472 printf("\n");
2474 // FIXME : should use bios=cmos|auto|disable bits
2475 // FIXME : should know about translation bits
2476 // FIXME : move hard_drive_post here
2480 // ---------------------------------------------------------------------------
2481 // ATA/ATAPI driver : software reset
2482 // ---------------------------------------------------------------------------
2483 // ATA-3
2484 // 8.2.1 Software reset - Device 0
2486 void ata_reset(device)
2487 Bit16u device;
2489 Bit16u ebda_seg=read_word(0x0040,0x000E);
2490 Bit16u iobase1, iobase2;
2491 Bit8u channel, slave, sn, sc;
2492 Bit16u max;
2494 channel = device / 2;
2495 slave = device % 2;
2497 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2498 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2500 // Reset
2502 // 8.2.1 (a) -- set SRST in DC
2503 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST);
2505 // 8.2.1 (b) -- wait for BSY
2506 max=0xff;
2507 while(--max>0) {
2508 Bit8u status = inb(iobase1+ATA_CB_STAT);
2509 if ((status & ATA_CB_STAT_BSY) != 0) break;
2512 // 8.2.1 (f) -- clear SRST
2513 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2515 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_NONE) {
2517 // 8.2.1 (g) -- check for sc==sn==0x01
2518 // select device
2519 outb(iobase1+ATA_CB_DH, slave?ATA_CB_DH_DEV1:ATA_CB_DH_DEV0);
2520 sc = inb(iobase1+ATA_CB_SC);
2521 sn = inb(iobase1+ATA_CB_SN);
2523 if ( (sc==0x01) && (sn==0x01) ) {
2525 // 8.2.1 (h) -- wait for not BSY
2526 max=0xff;
2527 while(--max>0) {
2528 Bit8u status = inb(iobase1+ATA_CB_STAT);
2529 if ((status & ATA_CB_STAT_BSY) == 0) break;
2534 // 8.2.1 (i) -- wait for DRDY
2535 max=0xfff;
2536 while(--max>0) {
2537 Bit8u status = inb(iobase1+ATA_CB_STAT);
2538 if ((status & ATA_CB_STAT_RDY) != 0) break;
2541 // Enable interrupts
2542 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2545 // ---------------------------------------------------------------------------
2546 // ATA/ATAPI driver : execute a non data command
2547 // ---------------------------------------------------------------------------
2549 Bit16u ata_cmd_non_data()
2550 {return 0;}
2552 // ---------------------------------------------------------------------------
2553 // ATA/ATAPI driver : execute a data-in command
2554 // ---------------------------------------------------------------------------
2555 // returns
2556 // 0 : no error
2557 // 1 : BUSY bit set
2558 // 2 : read error
2559 // 3 : expected DRQ=1
2560 // 4 : no sectors left to read/verify
2561 // 5 : more sectors to read/verify
2562 // 6 : no sectors left to write
2563 // 7 : more sectors to write
2564 Bit16u ata_cmd_data_in(device, command, count, cylinder, head, sector, lba, segment, offset)
2565 Bit16u device, command, count, cylinder, head, sector, segment, offset;
2566 Bit32u lba;
2568 Bit16u ebda_seg=read_word(0x0040,0x000E);
2569 Bit16u iobase1, iobase2, blksize;
2570 Bit8u channel, slave;
2571 Bit8u status, current, mode;
2573 channel = device / 2;
2574 slave = device % 2;
2576 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2577 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2578 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2579 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
2580 if (mode == ATA_MODE_PIO32) blksize>>=2;
2581 else blksize>>=1;
2583 // sector will be 0 only on lba access. Convert to lba-chs
2584 if (sector == 0) {
2585 sector = (Bit16u) (lba & 0x000000ffL);
2586 lba >>= 8;
2587 cylinder = (Bit16u) (lba & 0x0000ffffL);
2588 lba >>= 16;
2589 head = ((Bit16u) (lba & 0x0000000fL)) | 0x40;
2592 // Reset count of transferred data
2593 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2594 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2595 current = 0;
2597 status = inb(iobase1 + ATA_CB_STAT);
2598 if (status & ATA_CB_STAT_BSY) return 1;
2600 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2601 outb(iobase1 + ATA_CB_FR, 0x00);
2602 outb(iobase1 + ATA_CB_SC, count);
2603 outb(iobase1 + ATA_CB_SN, sector);
2604 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
2605 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
2606 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
2607 outb(iobase1 + ATA_CB_CMD, command);
2609 while (1) {
2610 status = inb(iobase1 + ATA_CB_STAT);
2611 if ( !(status & ATA_CB_STAT_BSY) ) break;
2614 if (status & ATA_CB_STAT_ERR) {
2615 BX_DEBUG_ATA("ata_cmd_data_in : read error\n");
2616 return 2;
2617 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2618 BX_DEBUG_ATA("ata_cmd_data_in : DRQ not set (status %02x)\n", (unsigned) status);
2619 return 3;
2622 // FIXME : move seg/off translation here
2624 ASM_START
2625 sti ;; enable higher priority interrupts
2626 ASM_END
2628 while (1) {
2630 ASM_START
2631 push bp
2632 mov bp, sp
2633 mov di, _ata_cmd_data_in.offset + 2[bp]
2634 mov ax, _ata_cmd_data_in.segment + 2[bp]
2635 mov cx, _ata_cmd_data_in.blksize + 2[bp]
2637 ;; adjust if there will be an overrun. 2K max sector size
2638 cmp di, #0xf800 ;;
2639 jbe ata_in_no_adjust
2641 ata_in_adjust:
2642 sub di, #0x0800 ;; sub 2 kbytes from offset
2643 add ax, #0x0080 ;; add 2 Kbytes to segment
2645 ata_in_no_adjust:
2646 mov es, ax ;; segment in es
2648 mov dx, _ata_cmd_data_in.iobase1 + 2[bp] ;; ATA data read port
2650 mov ah, _ata_cmd_data_in.mode + 2[bp]
2651 cmp ah, #ATA_MODE_PIO32
2652 je ata_in_32
2654 ata_in_16:
2656 insw ;; CX words transfered from port(DX) to ES:[DI]
2657 jmp ata_in_done
2659 ata_in_32:
2661 insd ;; CX dwords transfered from port(DX) to ES:[DI]
2663 ata_in_done:
2664 mov _ata_cmd_data_in.offset + 2[bp], di
2665 mov _ata_cmd_data_in.segment + 2[bp], es
2666 pop bp
2667 ASM_END
2669 current++;
2670 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
2671 count--;
2672 status = inb(iobase1 + ATA_CB_STAT);
2673 if (count == 0) {
2674 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2675 != ATA_CB_STAT_RDY ) {
2676 BX_DEBUG_ATA("ata_cmd_data_in : no sectors left (status %02x)\n", (unsigned) status);
2677 return 4;
2679 break;
2681 else {
2682 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2683 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2684 BX_DEBUG_ATA("ata_cmd_data_in : more sectors left (status %02x)\n", (unsigned) status);
2685 return 5;
2687 continue;
2690 // Enable interrupts
2691 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2692 return 0;
2695 // ---------------------------------------------------------------------------
2696 // ATA/ATAPI driver : execute a data-out command
2697 // ---------------------------------------------------------------------------
2698 // returns
2699 // 0 : no error
2700 // 1 : BUSY bit set
2701 // 2 : read error
2702 // 3 : expected DRQ=1
2703 // 4 : no sectors left to read/verify
2704 // 5 : more sectors to read/verify
2705 // 6 : no sectors left to write
2706 // 7 : more sectors to write
2707 Bit16u ata_cmd_data_out(device, command, count, cylinder, head, sector, lba, segment, offset)
2708 Bit16u device, command, count, cylinder, head, sector, segment, offset;
2709 Bit32u lba;
2711 Bit16u ebda_seg=read_word(0x0040,0x000E);
2712 Bit16u iobase1, iobase2, blksize;
2713 Bit8u channel, slave;
2714 Bit8u status, current, mode;
2716 channel = device / 2;
2717 slave = device % 2;
2719 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2720 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2721 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2722 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
2723 if (mode == ATA_MODE_PIO32) blksize>>=2;
2724 else blksize>>=1;
2726 // sector will be 0 only on lba access. Convert to lba-chs
2727 if (sector == 0) {
2728 sector = (Bit16u) (lba & 0x000000ffL);
2729 lba >>= 8;
2730 cylinder = (Bit16u) (lba & 0x0000ffffL);
2731 lba >>= 16;
2732 head = ((Bit16u) (lba & 0x0000000fL)) | 0x40;
2735 // Reset count of transferred data
2736 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2737 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2738 current = 0;
2740 status = inb(iobase1 + ATA_CB_STAT);
2741 if (status & ATA_CB_STAT_BSY) return 1;
2743 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2744 outb(iobase1 + ATA_CB_FR, 0x00);
2745 outb(iobase1 + ATA_CB_SC, count);
2746 outb(iobase1 + ATA_CB_SN, sector);
2747 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
2748 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
2749 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
2750 outb(iobase1 + ATA_CB_CMD, command);
2752 while (1) {
2753 status = inb(iobase1 + ATA_CB_STAT);
2754 if ( !(status & ATA_CB_STAT_BSY) ) break;
2757 if (status & ATA_CB_STAT_ERR) {
2758 BX_DEBUG_ATA("ata_cmd_data_out : read error\n");
2759 return 2;
2760 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2761 BX_DEBUG_ATA("ata_cmd_data_out : DRQ not set (status %02x)\n", (unsigned) status);
2762 return 3;
2765 // FIXME : move seg/off translation here
2767 ASM_START
2768 sti ;; enable higher priority interrupts
2769 ASM_END
2771 while (1) {
2773 ASM_START
2774 push bp
2775 mov bp, sp
2776 mov si, _ata_cmd_data_out.offset + 2[bp]
2777 mov ax, _ata_cmd_data_out.segment + 2[bp]
2778 mov cx, _ata_cmd_data_out.blksize + 2[bp]
2780 ;; adjust if there will be an overrun. 2K max sector size
2781 cmp si, #0xf800 ;;
2782 jbe ata_out_no_adjust
2784 ata_out_adjust:
2785 sub si, #0x0800 ;; sub 2 kbytes from offset
2786 add ax, #0x0080 ;; add 2 Kbytes to segment
2788 ata_out_no_adjust:
2789 mov es, ax ;; segment in es
2791 mov dx, _ata_cmd_data_out.iobase1 + 2[bp] ;; ATA data write port
2793 mov ah, _ata_cmd_data_out.mode + 2[bp]
2794 cmp ah, #ATA_MODE_PIO32
2795 je ata_out_32
2797 ata_out_16:
2798 seg ES
2800 outsw ;; CX words transfered from port(DX) to ES:[SI]
2801 jmp ata_out_done
2803 ata_out_32:
2804 seg ES
2806 outsd ;; CX dwords transfered from port(DX) to ES:[SI]
2808 ata_out_done:
2809 mov _ata_cmd_data_out.offset + 2[bp], si
2810 mov _ata_cmd_data_out.segment + 2[bp], es
2811 pop bp
2812 ASM_END
2814 current++;
2815 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
2816 count--;
2817 status = inb(iobase1 + ATA_CB_STAT);
2818 if (count == 0) {
2819 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2820 != ATA_CB_STAT_RDY ) {
2821 BX_DEBUG_ATA("ata_cmd_data_out : no sectors left (status %02x)\n", (unsigned) status);
2822 return 6;
2824 break;
2826 else {
2827 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2828 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2829 BX_DEBUG_ATA("ata_cmd_data_out : more sectors left (status %02x)\n", (unsigned) status);
2830 return 7;
2832 continue;
2835 // Enable interrupts
2836 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2837 return 0;
2840 // ---------------------------------------------------------------------------
2841 // ATA/ATAPI driver : execute a packet command
2842 // ---------------------------------------------------------------------------
2843 // returns
2844 // 0 : no error
2845 // 1 : error in parameters
2846 // 2 : BUSY bit set
2847 // 3 : error
2848 // 4 : not ready
2849 Bit16u ata_cmd_packet(device, cmdlen, cmdseg, cmdoff, header, length, inout, bufseg, bufoff)
2850 Bit8u cmdlen,inout;
2851 Bit16u device,cmdseg, cmdoff, bufseg, bufoff;
2852 Bit16u header;
2853 Bit32u length;
2855 Bit16u ebda_seg=read_word(0x0040,0x000E);
2856 Bit16u iobase1, iobase2;
2857 Bit16u lcount, lbefore, lafter, count;
2858 Bit8u channel, slave;
2859 Bit8u status, mode, lmode;
2860 Bit32u total, transfer;
2862 channel = device / 2;
2863 slave = device % 2;
2865 // Data out is not supported yet
2866 if (inout == ATA_DATA_OUT) {
2867 BX_INFO("ata_cmd_packet: DATA_OUT not supported yet\n");
2868 return 1;
2871 // The header length must be even
2872 if (header & 1) {
2873 BX_DEBUG_ATA("ata_cmd_packet : header must be even (%04x)\n",header);
2874 return 1;
2877 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2878 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2879 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2880 transfer= 0L;
2882 if (cmdlen < 12) cmdlen=12;
2883 if (cmdlen > 12) cmdlen=16;
2884 cmdlen>>=1;
2886 // Reset count of transferred data
2887 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2888 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2890 status = inb(iobase1 + ATA_CB_STAT);
2891 if (status & ATA_CB_STAT_BSY) return 2;
2893 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2894 // outb(iobase1 + ATA_CB_FR, 0x00);
2895 // outb(iobase1 + ATA_CB_SC, 0x00);
2896 // outb(iobase1 + ATA_CB_SN, 0x00);
2897 outb(iobase1 + ATA_CB_CL, 0xfff0 & 0x00ff);
2898 outb(iobase1 + ATA_CB_CH, 0xfff0 >> 8);
2899 outb(iobase1 + ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2900 outb(iobase1 + ATA_CB_CMD, ATA_CMD_PACKET);
2902 // Device should ok to receive command
2903 while (1) {
2904 status = inb(iobase1 + ATA_CB_STAT);
2905 if ( !(status & ATA_CB_STAT_BSY) ) break;
2908 if (status & ATA_CB_STAT_ERR) {
2909 BX_DEBUG_ATA("ata_cmd_packet : error, status is %02x\n",status);
2910 return 3;
2911 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2912 BX_DEBUG_ATA("ata_cmd_packet : DRQ not set (status %02x)\n", (unsigned) status);
2913 return 4;
2916 // Normalize address
2917 cmdseg += (cmdoff / 16);
2918 cmdoff %= 16;
2920 // Send command to device
2921 ASM_START
2922 sti ;; enable higher priority interrupts
2924 push bp
2925 mov bp, sp
2927 mov si, _ata_cmd_packet.cmdoff + 2[bp]
2928 mov ax, _ata_cmd_packet.cmdseg + 2[bp]
2929 mov cx, _ata_cmd_packet.cmdlen + 2[bp]
2930 mov es, ax ;; segment in es
2932 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data write port
2934 seg ES
2936 outsw ;; CX words transfered from port(DX) to ES:[SI]
2938 pop bp
2939 ASM_END
2941 if (inout == ATA_DATA_NO) {
2942 status = inb(iobase1 + ATA_CB_STAT);
2944 else {
2945 while (1) {
2947 status = inb(iobase1 + ATA_CB_STAT);
2949 // Check if command completed
2950 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ) ) ==0 ) break;
2952 if (status & ATA_CB_STAT_ERR) {
2953 BX_DEBUG_ATA("ata_cmd_packet : error (status %02x)\n",status);
2954 return 3;
2957 // Device must be ready to send data
2958 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2959 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2960 BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", status);
2961 return 4;
2964 // Normalize address
2965 bufseg += (bufoff / 16);
2966 bufoff %= 16;
2968 // Get the byte count
2969 lcount = ((Bit16u)(inb(iobase1 + ATA_CB_CH))<<8)+inb(iobase1 + ATA_CB_CL);
2971 // adjust to read what we want
2972 if(header>lcount) {
2973 lbefore=lcount;
2974 header-=lcount;
2975 lcount=0;
2977 else {
2978 lbefore=header;
2979 header=0;
2980 lcount-=lbefore;
2983 if(lcount>length) {
2984 lafter=lcount-length;
2985 lcount=length;
2986 length=0;
2988 else {
2989 lafter=0;
2990 length-=lcount;
2993 // Save byte count
2994 count = lcount;
2996 BX_DEBUG_ATA("Trying to read %04x bytes (%04x %04x %04x) ",lbefore+lcount+lafter,lbefore,lcount,lafter);
2997 BX_DEBUG_ATA("to 0x%04x:0x%04x\n",bufseg,bufoff);
2999 // If counts not dividable by 4, use 16bits mode
3000 lmode = mode;
3001 if (lbefore & 0x03) lmode=ATA_MODE_PIO16;
3002 if (lcount & 0x03) lmode=ATA_MODE_PIO16;
3003 if (lafter & 0x03) lmode=ATA_MODE_PIO16;
3005 // adds an extra byte if count are odd. before is always even
3006 if (lcount & 0x01) {
3007 lcount+=1;
3008 if ((lafter > 0) && (lafter & 0x01)) {
3009 lafter-=1;
3013 if (lmode == ATA_MODE_PIO32) {
3014 lcount>>=2; lbefore>>=2; lafter>>=2;
3016 else {
3017 lcount>>=1; lbefore>>=1; lafter>>=1;
3020 ; // FIXME bcc bug
3022 ASM_START
3023 push bp
3024 mov bp, sp
3026 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data read port
3028 mov cx, _ata_cmd_packet.lbefore + 2[bp]
3029 jcxz ata_packet_no_before
3031 mov ah, _ata_cmd_packet.lmode + 2[bp]
3032 cmp ah, #ATA_MODE_PIO32
3033 je ata_packet_in_before_32
3035 ata_packet_in_before_16:
3036 in ax, dx
3037 loop ata_packet_in_before_16
3038 jmp ata_packet_no_before
3040 ata_packet_in_before_32:
3041 push eax
3042 ata_packet_in_before_32_loop:
3043 in eax, dx
3044 loop ata_packet_in_before_32_loop
3045 pop eax
3047 ata_packet_no_before:
3048 mov cx, _ata_cmd_packet.lcount + 2[bp]
3049 jcxz ata_packet_after
3051 mov di, _ata_cmd_packet.bufoff + 2[bp]
3052 mov ax, _ata_cmd_packet.bufseg + 2[bp]
3053 mov es, ax
3055 mov ah, _ata_cmd_packet.lmode + 2[bp]
3056 cmp ah, #ATA_MODE_PIO32
3057 je ata_packet_in_32
3059 ata_packet_in_16:
3061 insw ;; CX words transfered tp port(DX) to ES:[DI]
3062 jmp ata_packet_after
3064 ata_packet_in_32:
3066 insd ;; CX dwords transfered to port(DX) to ES:[DI]
3068 ata_packet_after:
3069 mov cx, _ata_cmd_packet.lafter + 2[bp]
3070 jcxz ata_packet_done
3072 mov ah, _ata_cmd_packet.lmode + 2[bp]
3073 cmp ah, #ATA_MODE_PIO32
3074 je ata_packet_in_after_32
3076 ata_packet_in_after_16:
3077 in ax, dx
3078 loop ata_packet_in_after_16
3079 jmp ata_packet_done
3081 ata_packet_in_after_32:
3082 push eax
3083 ata_packet_in_after_32_loop:
3084 in eax, dx
3085 loop ata_packet_in_after_32_loop
3086 pop eax
3088 ata_packet_done:
3089 pop bp
3090 ASM_END
3092 // Compute new buffer address
3093 bufoff += count;
3095 // Save transferred bytes count
3096 transfer += count;
3097 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,transfer);
3101 // Final check, device must be ready
3102 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
3103 != ATA_CB_STAT_RDY ) {
3104 BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", (unsigned) status);
3105 return 4;
3108 // Enable interrupts
3109 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
3110 return 0;
3113 // ---------------------------------------------------------------------------
3114 // End of ATA/ATAPI Driver
3115 // ---------------------------------------------------------------------------
3117 // ---------------------------------------------------------------------------
3118 // Start of ATA/ATAPI generic functions
3119 // ---------------------------------------------------------------------------
3121 Bit16u
3122 atapi_get_sense(device)
3123 Bit16u device;
3125 Bit8u atacmd[12];
3126 Bit8u buffer[16];
3127 Bit8u i;
3129 memsetb(get_SS(),atacmd,0,12);
3131 // Request SENSE
3132 atacmd[0]=0x03;
3133 atacmd[4]=0x20;
3134 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 16L, ATA_DATA_IN, get_SS(), buffer) != 0)
3135 return 0x0002;
3137 if ((buffer[0] & 0x7e) == 0x70) {
3138 return (((Bit16u)buffer[2]&0x0f)*0x100)+buffer[12];
3141 return 0;
3144 Bit16u
3145 atapi_is_ready(device)
3146 Bit16u device;
3148 Bit8u atacmd[12];
3149 Bit8u buffer[];
3151 memsetb(get_SS(),atacmd,0,12);
3153 // Test Unit Ready
3154 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0)
3155 return 0x000f;
3157 if (atapi_get_sense(device) !=0 ) {
3158 memsetb(get_SS(),atacmd,0,12);
3160 // try to send Test Unit Ready again
3161 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0)
3162 return 0x000f;
3164 return atapi_get_sense(device);
3166 return 0;
3169 Bit16u
3170 atapi_is_cdrom(device)
3171 Bit8u device;
3173 Bit16u ebda_seg=read_word(0x0040,0x000E);
3175 if (device >= BX_MAX_ATA_DEVICES)
3176 return 0;
3178 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI)
3179 return 0;
3181 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].device) != ATA_DEVICE_CDROM)
3182 return 0;
3184 return 1;
3187 // ---------------------------------------------------------------------------
3188 // End of ATA/ATAPI generic functions
3189 // ---------------------------------------------------------------------------
3191 #endif // BX_USE_ATADRV
3193 #if BX_ELTORITO_BOOT
3195 // ---------------------------------------------------------------------------
3196 // Start of El-Torito boot functions
3197 // ---------------------------------------------------------------------------
3199 void
3200 cdemu_init()
3202 Bit16u ebda_seg=read_word(0x0040,0x000E);
3204 // the only important data is this one for now
3205 write_byte(ebda_seg,&EbdaData->cdemu.active,0x00);
3208 Bit8u
3209 cdemu_isactive()
3211 Bit16u ebda_seg=read_word(0x0040,0x000E);
3213 return(read_byte(ebda_seg,&EbdaData->cdemu.active));
3216 Bit8u
3217 cdemu_emulated_drive()
3219 Bit16u ebda_seg=read_word(0x0040,0x000E);
3221 return(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
3224 static char isotag[6]="CD001";
3225 static char eltorito[24]="EL TORITO SPECIFICATION";
3227 // Returns ah: emulated drive, al: error code
3229 Bit16u
3230 cdrom_boot()
3232 Bit16u ebda_seg=read_word(0x0040,0x000E);
3233 Bit8u atacmd[12], buffer[2048];
3234 Bit32u lba;
3235 Bit16u boot_segment, nbsectors, i, error;
3236 Bit8u device;
3238 // Find out the first cdrom
3239 for (device=0; device<BX_MAX_ATA_DEVICES;device++) {
3240 if (atapi_is_cdrom(device)) break;
3243 // if not found
3244 if(device >= BX_MAX_ATA_DEVICES) return 2;
3246 // Read the Boot Record Volume Descriptor
3247 memsetb(get_SS(),atacmd,0,12);
3248 atacmd[0]=0x28; // READ command
3249 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3250 atacmd[8]=(0x01 & 0x00ff); // Sectors
3251 atacmd[2]=(0x11 & 0xff000000) >> 24; // LBA
3252 atacmd[3]=(0x11 & 0x00ff0000) >> 16;
3253 atacmd[4]=(0x11 & 0x0000ff00) >> 8;
3254 atacmd[5]=(0x11 & 0x000000ff);
3255 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3256 return 3;
3258 // Validity checks
3259 if(buffer[0]!=0)return 4;
3260 for(i=0;i<5;i++){
3261 if(buffer[1+i]!=read_byte(0xf000,&isotag[i]))return 5;
3263 for(i=0;i<23;i++)
3264 if(buffer[7+i]!=read_byte(0xf000,&eltorito[i]))return 6;
3266 // ok, now we calculate the Boot catalog address
3267 lba=buffer[0x4A]*0x1000000+buffer[0x49]*0x10000+buffer[0x48]*0x100+buffer[0x47];
3269 // And we read the Boot Catalog
3270 memsetb(get_SS(),atacmd,0,12);
3271 atacmd[0]=0x28; // READ command
3272 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3273 atacmd[8]=(0x01 & 0x00ff); // Sectors
3274 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3275 atacmd[3]=(lba & 0x00ff0000) >> 16;
3276 atacmd[4]=(lba & 0x0000ff00) >> 8;
3277 atacmd[5]=(lba & 0x000000ff);
3278 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3279 return 7;
3281 // Validation entry
3282 if(buffer[0x00]!=0x01)return 8; // Header
3283 if(buffer[0x01]!=0x00)return 9; // Platform
3284 if(buffer[0x1E]!=0x55)return 10; // key 1
3285 if(buffer[0x1F]!=0xAA)return 10; // key 2
3287 // Initial/Default Entry
3288 if(buffer[0x20]!=0x88)return 11; // Bootable
3290 write_byte(ebda_seg,&EbdaData->cdemu.media,buffer[0x21]);
3291 if(buffer[0x21]==0){
3292 // FIXME ElTorito Hardcoded. cdrom is hardcoded as device 0xE0.
3293 // Win2000 cd boot needs to know it booted from cd
3294 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0xE0);
3296 else if(buffer[0x21]<4)
3297 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x00);
3298 else
3299 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x80);
3301 write_byte(ebda_seg,&EbdaData->cdemu.controller_index,device/2);
3302 write_byte(ebda_seg,&EbdaData->cdemu.device_spec,device%2);
3304 boot_segment=buffer[0x23]*0x100+buffer[0x22];
3305 if(boot_segment==0x0000)boot_segment=0x07C0;
3307 write_word(ebda_seg,&EbdaData->cdemu.load_segment,boot_segment);
3308 write_word(ebda_seg,&EbdaData->cdemu.buffer_segment,0x0000);
3310 nbsectors=buffer[0x27]*0x100+buffer[0x26];
3311 write_word(ebda_seg,&EbdaData->cdemu.sector_count,nbsectors);
3313 lba=buffer[0x2B]*0x1000000+buffer[0x2A]*0x10000+buffer[0x29]*0x100+buffer[0x28];
3314 write_dword(ebda_seg,&EbdaData->cdemu.ilba,lba);
3316 // And we read the image in memory
3317 memsetb(get_SS(),atacmd,0,12);
3318 atacmd[0]=0x28; // READ command
3319 atacmd[7]=((1+(nbsectors-1)/4) & 0xff00) >> 8; // Sectors
3320 atacmd[8]=((1+(nbsectors-1)/4) & 0x00ff); // Sectors
3321 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3322 atacmd[3]=(lba & 0x00ff0000) >> 16;
3323 atacmd[4]=(lba & 0x0000ff00) >> 8;
3324 atacmd[5]=(lba & 0x000000ff);
3325 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, nbsectors*512L, ATA_DATA_IN, boot_segment,0)) != 0)
3326 return 12;
3328 // Remember the media type
3329 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
3330 case 0x01: // 1.2M floppy
3331 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,15);
3332 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3333 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3334 break;
3335 case 0x02: // 1.44M floppy
3336 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,18);
3337 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3338 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3339 break;
3340 case 0x03: // 2.88M floppy
3341 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,36);
3342 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3343 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3344 break;
3345 case 0x04: // Harddrive
3346 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,read_byte(boot_segment,446+6)&0x3f);
3347 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,
3348 (read_byte(boot_segment,446+6)<<2) + read_byte(boot_segment,446+7) + 1);
3349 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,read_byte(boot_segment,446+5) + 1);
3350 break;
3353 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0) {
3354 // Increase bios installed hardware number of devices
3355 if(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)==0x00)
3356 write_byte(0x40,0x10,read_byte(0x40,0x10)|0x41);
3357 else
3358 write_byte(ebda_seg, &EbdaData->ata.hdcount, read_byte(ebda_seg, &EbdaData->ata.hdcount) + 1);
3362 // everything is ok, so from now on, the emulation is active
3363 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0)
3364 write_byte(ebda_seg,&EbdaData->cdemu.active,0x01);
3366 // return the boot drive + no error
3367 return (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)*0x100)+0;
3370 // ---------------------------------------------------------------------------
3371 // End of El-Torito boot functions
3372 // ---------------------------------------------------------------------------
3373 #endif // BX_ELTORITO_BOOT
3375 void
3376 int14_function(regs, ds, iret_addr)
3377 pusha_regs_t regs; // regs pushed from PUSHA instruction
3378 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
3379 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
3381 Bit16u addr,timer,val16;
3382 Bit8u timeout;
3384 ASM_START
3386 ASM_END
3388 addr = read_word(0x0040, (regs.u.r16.dx << 1));
3389 timeout = read_byte(0x0040, 0x007C + regs.u.r16.dx);
3390 if ((regs.u.r16.dx < 4) && (addr > 0)) {
3391 switch (regs.u.r8.ah) {
3392 case 0:
3393 outb(addr+3, inb(addr+3) | 0x80);
3394 if (regs.u.r8.al & 0xE0 == 0) {
3395 outb(addr, 0x17);
3396 outb(addr+1, 0x04);
3397 } else {
3398 val16 = 0x600 >> ((regs.u.r8.al & 0xE0) >> 5);
3399 outb(addr, val16 & 0xFF);
3400 outb(addr+1, val16 >> 8);
3402 outb(addr+3, regs.u.r8.al & 0x1F);
3403 regs.u.r8.ah = inb(addr+5);
3404 regs.u.r8.al = inb(addr+6);
3405 ClearCF(iret_addr.flags);
3406 break;
3407 case 1:
3408 timer = read_word(0x0040, 0x006C);
3409 while (((inb(addr+5) & 0x60) != 0x60) && (timeout)) {
3410 val16 = read_word(0x0040, 0x006C);
3411 if (val16 != timer) {
3412 timer = val16;
3413 timeout--;
3416 if (timeout) outb(addr, regs.u.r8.al);
3417 regs.u.r8.ah = inb(addr+5);
3418 if (!timeout) regs.u.r8.ah |= 0x80;
3419 ClearCF(iret_addr.flags);
3420 break;
3421 case 2:
3422 timer = read_word(0x0040, 0x006C);
3423 while (((inb(addr+5) & 0x01) == 0) && (timeout)) {
3424 val16 = read_word(0x0040, 0x006C);
3425 if (val16 != timer) {
3426 timer = val16;
3427 timeout--;
3430 if (timeout) {
3431 regs.u.r8.ah = 0;
3432 regs.u.r8.al = inb(addr);
3433 } else {
3434 regs.u.r8.ah = inb(addr+5);
3436 ClearCF(iret_addr.flags);
3437 break;
3438 case 3:
3439 regs.u.r8.ah = inb(addr+5);
3440 regs.u.r8.al = inb(addr+6);
3441 ClearCF(iret_addr.flags);
3442 break;
3443 default:
3444 SetCF(iret_addr.flags); // Unsupported
3446 } else {
3447 SetCF(iret_addr.flags); // Unsupported
3451 void
3452 int15_function(regs, ES, DS, FLAGS)
3453 pusha_regs_t regs; // REGS pushed via pusha
3454 Bit16u ES, DS, FLAGS;
3456 Bit16u ebda_seg=read_word(0x0040,0x000E);
3457 bx_bool prev_a20_enable;
3458 Bit16u base15_00;
3459 Bit8u base23_16;
3460 Bit16u ss;
3461 Bit16u CX,DX;
3463 Bit16u bRegister;
3464 Bit8u irqDisable;
3466 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3468 switch (regs.u.r8.ah) {
3469 case 0x24: /* A20 Control */
3470 switch (regs.u.r8.al) {
3471 case 0x00:
3472 set_enable_a20(0);
3473 CLEAR_CF();
3474 regs.u.r8.ah = 0;
3475 break;
3476 case 0x01:
3477 set_enable_a20(1);
3478 CLEAR_CF();
3479 regs.u.r8.ah = 0;
3480 break;
3481 case 0x02:
3482 regs.u.r8.al = (inb(0x92) >> 1) & 0x01;
3483 CLEAR_CF();
3484 regs.u.r8.ah = 0;
3485 break;
3486 case 0x03:
3487 CLEAR_CF();
3488 regs.u.r8.ah = 0;
3489 regs.u.r16.bx = 3;
3490 break;
3491 default:
3492 BX_INFO("int15: Func 24h, subfunc %02xh, A20 gate control not supported\n", (unsigned) regs.u.r8.al);
3493 SET_CF();
3494 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3496 break;
3498 case 0x41:
3499 SET_CF();
3500 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3501 break;
3503 case 0x4f:
3504 /* keyboard intercept */
3505 #if BX_CPU < 2
3506 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3507 #else
3508 // nop
3509 #endif
3510 SET_CF();
3511 break;
3513 case 0x52: // removable media eject
3514 CLEAR_CF();
3515 regs.u.r8.ah = 0; // "ok ejection may proceed"
3516 break;
3518 case 0x83: {
3519 if( regs.u.r8.al == 0 ) {
3520 // Set Interval requested.
3521 if( ( read_byte( 0x40, 0xA0 ) & 1 ) == 0 ) {
3522 // Interval not already set.
3523 write_byte( 0x40, 0xA0, 1 ); // Set status byte.
3524 write_word( 0x40, 0x98, ES ); // Byte location, segment
3525 write_word( 0x40, 0x9A, regs.u.r16.bx ); // Byte location, offset
3526 write_word( 0x40, 0x9C, regs.u.r16.dx ); // Low word, delay
3527 write_word( 0x40, 0x9E, regs.u.r16.cx ); // High word, delay.
3528 CLEAR_CF( );
3529 irqDisable = inb( 0xA1 );
3530 outb( 0xA1, irqDisable & 0xFE );
3531 bRegister = inb_cmos( 0xB ); // Unmask IRQ8 so INT70 will get through.
3532 outb_cmos( 0xB, bRegister | 0x40 ); // Turn on the Periodic Interrupt timer
3533 } else {
3534 // Interval already set.
3535 BX_DEBUG_INT15("int15: Func 83h, failed, already waiting.\n" );
3536 SET_CF();
3537 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3539 } else if( regs.u.r8.al == 1 ) {
3540 // Clear Interval requested
3541 write_byte( 0x40, 0xA0, 0 ); // Clear status byte
3542 CLEAR_CF( );
3543 bRegister = inb_cmos( 0xB );
3544 outb_cmos( 0xB, bRegister & ~0x40 ); // Turn off the Periodic Interrupt timer
3545 } else {
3546 BX_DEBUG_INT15("int15: Func 83h, failed.\n" );
3547 SET_CF();
3548 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3549 regs.u.r8.al--;
3552 break;
3555 case 0x87:
3556 #if BX_CPU < 3
3557 # error "Int15 function 87h not supported on < 80386"
3558 #endif
3559 // +++ should probably have descriptor checks
3560 // +++ should have exception handlers
3562 // turn off interrupts
3563 ASM_START
3565 ASM_END
3567 prev_a20_enable = set_enable_a20(1); // enable A20 line
3569 // 128K max of transfer on 386+ ???
3570 // source == destination ???
3572 // ES:SI points to descriptor table
3573 // offset use initially comments
3574 // ==============================================
3575 // 00..07 Unused zeros Null descriptor
3576 // 08..0f GDT zeros filled in by BIOS
3577 // 10..17 source ssssssss source of data
3578 // 18..1f dest dddddddd destination of data
3579 // 20..27 CS zeros filled in by BIOS
3580 // 28..2f SS zeros filled in by BIOS
3582 //es:si
3583 //eeee0
3584 //0ssss
3585 //-----
3587 // check for access rights of source & dest here
3589 // Initialize GDT descriptor
3590 base15_00 = (ES << 4) + regs.u.r16.si;
3591 base23_16 = ES >> 12;
3592 if (base15_00 < (ES<<4))
3593 base23_16++;
3594 write_word(ES, regs.u.r16.si+0x08+0, 47); // limit 15:00 = 6 * 8bytes/descriptor
3595 write_word(ES, regs.u.r16.si+0x08+2, base15_00);// base 15:00
3596 write_byte(ES, regs.u.r16.si+0x08+4, base23_16);// base 23:16
3597 write_byte(ES, regs.u.r16.si+0x08+5, 0x93); // access
3598 write_word(ES, regs.u.r16.si+0x08+6, 0x0000); // base 31:24/reserved/limit 19:16
3600 // Initialize CS descriptor
3601 write_word(ES, regs.u.r16.si+0x20+0, 0xffff);// limit 15:00 = normal 64K limit
3602 write_word(ES, regs.u.r16.si+0x20+2, 0x0000);// base 15:00
3603 write_byte(ES, regs.u.r16.si+0x20+4, 0x000f);// base 23:16
3604 write_byte(ES, regs.u.r16.si+0x20+5, 0x9b); // access
3605 write_word(ES, regs.u.r16.si+0x20+6, 0x0000);// base 31:24/reserved/limit 19:16
3607 // Initialize SS descriptor
3608 ss = get_SS();
3609 base15_00 = ss << 4;
3610 base23_16 = ss >> 12;
3611 write_word(ES, regs.u.r16.si+0x28+0, 0xffff); // limit 15:00 = normal 64K limit
3612 write_word(ES, regs.u.r16.si+0x28+2, base15_00);// base 15:00
3613 write_byte(ES, regs.u.r16.si+0x28+4, base23_16);// base 23:16
3614 write_byte(ES, regs.u.r16.si+0x28+5, 0x93); // access
3615 write_word(ES, regs.u.r16.si+0x28+6, 0x0000); // base 31:24/reserved/limit 19:16
3617 CX = regs.u.r16.cx;
3618 ASM_START
3619 // Compile generates locals offset info relative to SP.
3620 // Get CX (word count) from stack.
3621 mov bx, sp
3622 SEG SS
3623 mov cx, _int15_function.CX [bx]
3625 // since we need to set SS:SP, save them to the BDA
3626 // for future restore
3627 push eax
3628 xor eax, eax
3629 mov ds, ax
3630 mov 0x0469, ss
3631 mov 0x0467, sp
3633 SEG ES
3634 lgdt [si + 0x08]
3635 SEG CS
3636 lidt [pmode_IDT_info]
3637 ;; perhaps do something with IDT here
3639 ;; set PE bit in CR0
3640 mov eax, cr0
3641 or al, #0x01
3642 mov cr0, eax
3643 ;; far jump to flush CPU queue after transition to protected mode
3644 JMP_AP(0x0020, protected_mode)
3646 protected_mode:
3647 ;; GDT points to valid descriptor table, now load SS, DS, ES
3648 mov ax, #0x28 ;; 101 000 = 5th descriptor in table, TI=GDT, RPL=00
3649 mov ss, ax
3650 mov ax, #0x10 ;; 010 000 = 2nd descriptor in table, TI=GDT, RPL=00
3651 mov ds, ax
3652 mov ax, #0x18 ;; 011 000 = 3rd descriptor in table, TI=GDT, RPL=00
3653 mov es, ax
3654 xor si, si
3655 xor di, di
3658 movsw ;; move CX words from DS:SI to ES:DI
3660 ;; make sure DS and ES limits are 64KB
3661 mov ax, #0x28
3662 mov ds, ax
3663 mov es, ax
3665 ;; reset PG bit in CR0 ???
3666 mov eax, cr0
3667 and al, #0xFE
3668 mov cr0, eax
3670 ;; far jump to flush CPU queue after transition to real mode
3671 JMP_AP(0xf000, real_mode)
3673 real_mode:
3674 ;; restore IDT to normal real-mode defaults
3675 SEG CS
3676 lidt [rmode_IDT_info]
3678 // restore SS:SP from the BDA
3679 xor ax, ax
3680 mov ds, ax
3681 mov ss, 0x0469
3682 mov sp, 0x0467
3683 pop eax
3684 ASM_END
3686 set_enable_a20(prev_a20_enable);
3688 // turn back on interrupts
3689 ASM_START
3691 ASM_END
3693 regs.u.r8.ah = 0;
3694 CLEAR_CF();
3695 break;
3698 case 0x88:
3699 // Get the amount of extended memory (above 1M)
3700 #if BX_CPU < 2
3701 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3702 SET_CF();
3703 #else
3704 regs.u.r8.al = inb_cmos(0x30);
3705 regs.u.r8.ah = inb_cmos(0x31);
3707 // According to Ralf Brown's interrupt the limit should be 15M,
3708 // but real machines mostly return max. 63M.
3709 if(regs.u.r16.ax > 0xffc0)
3710 regs.u.r16.ax = 0xffc0;
3712 CLEAR_CF();
3713 #endif
3714 break;
3716 case 0x90:
3717 /* Device busy interrupt. Called by Int 16h when no key available */
3718 break;
3720 case 0x91:
3721 /* Interrupt complete. Called by Int 16h when key becomes available */
3722 break;
3724 case 0xbf:
3725 BX_INFO("*** int 15h function AH=bf not yet supported!\n");
3726 SET_CF();
3727 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3728 break;
3730 case 0xC0:
3731 #if 0
3732 SET_CF();
3733 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3734 break;
3735 #endif
3736 CLEAR_CF();
3737 regs.u.r8.ah = 0;
3738 regs.u.r16.bx = BIOS_CONFIG_TABLE;
3739 ES = 0xF000;
3740 break;
3742 case 0xc1:
3743 ES = ebda_seg;
3744 CLEAR_CF();
3745 break;
3747 case 0xd8:
3748 bios_printf(BIOS_PRINTF_DEBUG, "EISA BIOS not present\n");
3749 SET_CF();
3750 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3751 break;
3753 default:
3754 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
3755 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
3756 SET_CF();
3757 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3758 break;
3762 #if BX_USE_PS2_MOUSE
3763 void
3764 int15_function_mouse(regs, ES, DS, FLAGS)
3765 pusha_regs_t regs; // REGS pushed via pusha
3766 Bit16u ES, DS, FLAGS;
3768 Bit16u ebda_seg=read_word(0x0040,0x000E);
3769 Bit8u mouse_flags_1, mouse_flags_2;
3770 Bit16u mouse_driver_seg;
3771 Bit16u mouse_driver_offset;
3772 Bit8u comm_byte, prev_command_byte;
3773 Bit8u ret, mouse_data1, mouse_data2, mouse_data3;
3775 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3777 switch (regs.u.r8.ah) {
3778 case 0xC2:
3779 // Return Codes status in AH
3780 // =========================
3781 // 00: success
3782 // 01: invalid subfunction (AL > 7)
3783 // 02: invalid input value (out of allowable range)
3784 // 03: interface error
3785 // 04: resend command received from mouse controller,
3786 // device driver should attempt command again
3787 // 05: cannot enable mouse, since no far call has been installed
3788 // 80/86: mouse service not implemented
3790 switch (regs.u.r8.al) {
3791 case 0: // Disable/Enable Mouse
3792 BX_DEBUG_INT15("case 0:\n");
3793 switch (regs.u.r8.bh) {
3794 case 0: // Disable Mouse
3795 BX_DEBUG_INT15("case 0: disable mouse\n");
3796 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3797 ret = send_to_mouse_ctrl(0xF5); // disable mouse command
3798 if (ret == 0) {
3799 ret = get_mouse_data(&mouse_data1);
3800 if ( (ret == 0) || (mouse_data1 == 0xFA) ) {
3801 CLEAR_CF();
3802 regs.u.r8.ah = 0;
3803 return;
3807 // error
3808 SET_CF();
3809 regs.u.r8.ah = ret;
3810 return;
3811 break;
3813 case 1: // Enable Mouse
3814 BX_DEBUG_INT15("case 1: enable mouse\n");
3815 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
3816 if ( (mouse_flags_2 & 0x80) == 0 ) {
3817 BX_DEBUG_INT15("INT 15h C2 Enable Mouse, no far call handler\n");
3818 SET_CF(); // error
3819 regs.u.r8.ah = 5; // no far call installed
3820 return;
3822 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3823 ret = send_to_mouse_ctrl(0xF4); // enable mouse command
3824 if (ret == 0) {
3825 ret = get_mouse_data(&mouse_data1);
3826 if ( (ret == 0) && (mouse_data1 == 0xFA) ) {
3827 enable_mouse_int_and_events(); // turn IRQ12 and packet generation on
3828 CLEAR_CF();
3829 regs.u.r8.ah = 0;
3830 return;
3833 SET_CF();
3834 regs.u.r8.ah = ret;
3835 return;
3837 default: // invalid subfunction
3838 BX_DEBUG_INT15("INT 15h C2 AL=0, BH=%02x\n", (unsigned) regs.u.r8.bh);
3839 SET_CF(); // error
3840 regs.u.r8.ah = 1; // invalid subfunction
3841 return;
3843 break;
3845 case 1: // Reset Mouse
3846 case 5: // Initialize Mouse
3847 BX_DEBUG_INT15("case 1 or 5:\n");
3848 if (regs.u.r8.al == 5) {
3849 if (regs.u.r8.bh != 3) {
3850 SET_CF();
3851 regs.u.r8.ah = 0x02; // invalid input
3852 return;
3854 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
3855 mouse_flags_2 = (mouse_flags_2 & 0x00) | regs.u.r8.bh;
3856 mouse_flags_1 = 0x00;
3857 write_byte(ebda_seg, 0x0026, mouse_flags_1);
3858 write_byte(ebda_seg, 0x0027, mouse_flags_2);
3861 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3862 ret = send_to_mouse_ctrl(0xFF); // reset mouse command
3863 if (ret == 0) {
3864 ret = get_mouse_data(&mouse_data3);
3865 // if no mouse attached, it will return RESEND
3866 if (mouse_data3 == 0xfe) {
3867 SET_CF();
3868 return;
3870 if (mouse_data3 != 0xfa)
3871 BX_PANIC("Mouse reset returned %02x (should be ack)\n", (unsigned)mouse_data3);
3872 if ( ret == 0 ) {
3873 ret = get_mouse_data(&mouse_data1);
3874 if ( ret == 0 ) {
3875 ret = get_mouse_data(&mouse_data2);
3876 if ( ret == 0 ) {
3877 // turn IRQ12 and packet generation on
3878 enable_mouse_int_and_events();
3879 CLEAR_CF();
3880 regs.u.r8.ah = 0;
3881 regs.u.r8.bl = mouse_data1;
3882 regs.u.r8.bh = mouse_data2;
3883 return;
3889 // error
3890 SET_CF();
3891 regs.u.r8.ah = ret;
3892 return;
3894 case 2: // Set Sample Rate
3895 BX_DEBUG_INT15("case 2:\n");
3896 switch (regs.u.r8.bh) {
3897 case 0: mouse_data1 = 10; break; // 10 reports/sec
3898 case 1: mouse_data1 = 20; break; // 20 reports/sec
3899 case 2: mouse_data1 = 40; break; // 40 reports/sec
3900 case 3: mouse_data1 = 60; break; // 60 reports/sec
3901 case 4: mouse_data1 = 80; break; // 80 reports/sec
3902 case 5: mouse_data1 = 100; break; // 100 reports/sec (default)
3903 case 6: mouse_data1 = 200; break; // 200 reports/sec
3904 default: mouse_data1 = 0;
3906 if (mouse_data1 > 0) {
3907 ret = send_to_mouse_ctrl(0xF3); // set sample rate command
3908 if (ret == 0) {
3909 ret = get_mouse_data(&mouse_data2);
3910 ret = send_to_mouse_ctrl(mouse_data1);
3911 ret = get_mouse_data(&mouse_data2);
3912 CLEAR_CF();
3913 regs.u.r8.ah = 0;
3914 } else {
3915 // error
3916 SET_CF();
3917 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3919 } else {
3920 // error
3921 SET_CF();
3922 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3924 break;
3926 case 3: // Set Resolution
3927 BX_DEBUG_INT15("case 3:\n");
3928 // BH:
3929 // 0 = 25 dpi, 1 count per millimeter
3930 // 1 = 50 dpi, 2 counts per millimeter
3931 // 2 = 100 dpi, 4 counts per millimeter
3932 // 3 = 200 dpi, 8 counts per millimeter
3933 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3934 if (regs.u.r8.bh < 4) {
3935 ret = send_to_mouse_ctrl(0xE8); // set resolution command
3936 if (ret == 0) {
3937 ret = get_mouse_data(&mouse_data1);
3938 if (mouse_data1 != 0xfa)
3939 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
3940 ret = send_to_mouse_ctrl(regs.u.r8.bh);
3941 ret = get_mouse_data(&mouse_data1);
3942 if (mouse_data1 != 0xfa)
3943 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
3944 CLEAR_CF();
3945 regs.u.r8.ah = 0;
3946 } else {
3947 // error
3948 SET_CF();
3949 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3951 } else {
3952 // error
3953 SET_CF();
3954 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3956 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
3957 break;
3959 case 4: // Get Device ID
3960 BX_DEBUG_INT15("case 4:\n");
3961 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3962 ret = send_to_mouse_ctrl(0xF2); // get mouse ID command
3963 if (ret == 0) {
3964 ret = get_mouse_data(&mouse_data1);
3965 ret = get_mouse_data(&mouse_data2);
3966 CLEAR_CF();
3967 regs.u.r8.ah = 0;
3968 regs.u.r8.bh = mouse_data2;
3969 } else {
3970 // error
3971 SET_CF();
3972 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3974 break;
3976 case 6: // Return Status & Set Scaling Factor...
3977 BX_DEBUG_INT15("case 6:\n");
3978 switch (regs.u.r8.bh) {
3979 case 0: // Return Status
3980 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3981 ret = send_to_mouse_ctrl(0xE9); // get mouse info command
3982 if (ret == 0) {
3983 ret = get_mouse_data(&mouse_data1);
3984 if (mouse_data1 != 0xfa)
3985 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
3986 if (ret == 0) {
3987 ret = get_mouse_data(&mouse_data1);
3988 if ( ret == 0 ) {
3989 ret = get_mouse_data(&mouse_data2);
3990 if ( ret == 0 ) {
3991 ret = get_mouse_data(&mouse_data3);
3992 if ( ret == 0 ) {
3993 CLEAR_CF();
3994 regs.u.r8.ah = 0;
3995 regs.u.r8.bl = mouse_data1;
3996 regs.u.r8.cl = mouse_data2;
3997 regs.u.r8.dl = mouse_data3;
3998 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
3999 return;
4006 // error
4007 SET_CF();
4008 regs.u.r8.ah = ret;
4009 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4010 return;
4012 case 1: // Set Scaling Factor to 1:1
4013 case 2: // Set Scaling Factor to 2:1
4014 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4015 if (regs.u.r8.bh == 1) {
4016 ret = send_to_mouse_ctrl(0xE6);
4017 } else {
4018 ret = send_to_mouse_ctrl(0xE7);
4020 if (ret == 0) {
4021 get_mouse_data(&mouse_data1);
4022 ret = (mouse_data1 != 0xFA);
4024 if (ret == 0) {
4025 CLEAR_CF();
4026 regs.u.r8.ah = 0;
4027 } else {
4028 // error
4029 SET_CF();
4030 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4032 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4033 break;
4035 default:
4036 BX_PANIC("INT 15h C2 AL=6, BH=%02x\n", (unsigned) regs.u.r8.bh);
4038 break;
4040 case 7: // Set Mouse Handler Address
4041 BX_DEBUG_INT15("case 7:\n");
4042 mouse_driver_seg = ES;
4043 mouse_driver_offset = regs.u.r16.bx;
4044 write_word(ebda_seg, 0x0022, mouse_driver_offset);
4045 write_word(ebda_seg, 0x0024, mouse_driver_seg);
4046 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4047 if (mouse_driver_offset == 0 && mouse_driver_seg == 0) {
4048 /* remove handler */
4049 if ( (mouse_flags_2 & 0x80) != 0 ) {
4050 mouse_flags_2 &= ~0x80;
4051 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4054 else {
4055 /* install handler */
4056 mouse_flags_2 |= 0x80;
4058 write_byte(ebda_seg, 0x0027, mouse_flags_2);
4059 CLEAR_CF();
4060 regs.u.r8.ah = 0;
4061 break;
4063 default:
4064 BX_DEBUG_INT15("case default:\n");
4065 regs.u.r8.ah = 1; // invalid function
4066 SET_CF();
4068 break;
4070 default:
4071 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4072 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4073 SET_CF();
4074 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4075 break;
4078 #endif
4081 void set_e820_range(ES, DI, start, end, extra_start, extra_end, type)
4082 Bit16u ES;
4083 Bit16u DI;
4084 Bit32u start;
4085 Bit32u end;
4086 Bit8u extra_start;
4087 Bit8u extra_end;
4088 Bit16u type;
4090 write_word(ES, DI, start);
4091 write_word(ES, DI+2, start >> 16);
4092 write_word(ES, DI+4, extra_start);
4093 write_word(ES, DI+6, 0x00);
4095 end -= start;
4096 extra_end -= extra_start;
4097 write_word(ES, DI+8, end);
4098 write_word(ES, DI+10, end >> 16);
4099 write_word(ES, DI+12, extra_end);
4100 write_word(ES, DI+14, 0x0000);
4102 write_word(ES, DI+16, type);
4103 write_word(ES, DI+18, 0x0);
4106 void
4107 int15_function32(regs, ES, DS, FLAGS)
4108 pushad_regs_t regs; // REGS pushed via pushad
4109 Bit16u ES, DS, FLAGS;
4111 Bit32u extended_memory_size=0; // 64bits long
4112 Bit32u extra_lowbits_memory_size=0;
4113 Bit16u CX,DX;
4114 Bit8u extra_highbits_memory_size=0;
4116 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
4118 switch (regs.u.r8.ah) {
4119 case 0x86:
4120 // Wait for CX:DX microseconds. currently using the
4121 // refresh request port 0x61 bit4, toggling every 15usec
4123 CX = regs.u.r16.cx;
4124 DX = regs.u.r16.dx;
4126 ASM_START
4129 ;; Get the count in eax
4130 mov bx, sp
4131 SEG SS
4132 mov ax, _int15_function.CX [bx]
4133 shl eax, #16
4134 SEG SS
4135 mov ax, _int15_function.DX [bx]
4137 ;; convert to numbers of 15usec ticks
4138 mov ebx, #15
4139 xor edx, edx
4140 div eax, ebx
4141 mov ecx, eax
4143 ;; wait for ecx number of refresh requests
4144 in al, #0x61
4145 and al,#0x10
4146 mov ah, al
4148 or ecx, ecx
4149 je int1586_tick_end
4150 int1586_tick:
4151 in al, #0x61
4152 and al,#0x10
4153 cmp al, ah
4154 je int1586_tick
4155 mov ah, al
4156 dec ecx
4157 jnz int1586_tick
4158 int1586_tick_end:
4159 ASM_END
4161 break;
4163 case 0xe8:
4164 switch(regs.u.r8.al)
4166 case 0x20: // coded by osmaker aka K.J.
4167 if(regs.u.r32.edx == 0x534D4150)
4169 extended_memory_size = inb_cmos(0x35);
4170 extended_memory_size <<= 8;
4171 extended_memory_size |= inb_cmos(0x34);
4172 extended_memory_size *= 64;
4173 // greater than EFF00000???
4174 if(extended_memory_size > 0x3bc000) {
4175 extended_memory_size = 0x3bc000; // everything after this is reserved memory until we get to 0x100000000
4177 extended_memory_size *= 1024;
4178 extended_memory_size += (16L * 1024 * 1024);
4180 if(extended_memory_size <= (16L * 1024 * 1024)) {
4181 extended_memory_size = inb_cmos(0x31);
4182 extended_memory_size <<= 8;
4183 extended_memory_size |= inb_cmos(0x30);
4184 extended_memory_size *= 1024;
4187 extra_lowbits_memory_size = inb_cmos(0x5c);
4188 extra_lowbits_memory_size <<= 8;
4189 extra_lowbits_memory_size |= inb_cmos(0x5b);
4190 extra_lowbits_memory_size *= 64;
4191 extra_lowbits_memory_size *= 1024;
4192 extra_highbits_memory_size = inb_cmos(0x5d);
4194 switch(regs.u.r16.bx)
4196 case 0:
4197 set_e820_range(ES, regs.u.r16.di,
4198 0x0000000L, 0x0009fc00L, 0, 0, 1);
4199 regs.u.r32.ebx = 1;
4200 regs.u.r32.eax = 0x534D4150;
4201 regs.u.r32.ecx = 0x14;
4202 CLEAR_CF();
4203 return;
4204 break;
4205 case 1:
4206 set_e820_range(ES, regs.u.r16.di,
4207 0x0009fc00L, 0x000a0000L, 0, 0, 2);
4208 regs.u.r32.ebx = 2;
4209 regs.u.r32.eax = 0x534D4150;
4210 regs.u.r32.ecx = 0x14;
4211 CLEAR_CF();
4212 return;
4213 break;
4214 case 2:
4215 set_e820_range(ES, regs.u.r16.di,
4216 0x000e8000L, 0x00100000L, 0, 0, 2);
4217 regs.u.r32.ebx = 3;
4218 regs.u.r32.eax = 0x534D4150;
4219 regs.u.r32.ecx = 0x14;
4220 CLEAR_CF();
4221 return;
4222 break;
4223 case 3:
4224 set_e820_range(ES, regs.u.r16.di,
4225 0x00100000L,
4226 extended_memory_size - ACPI_DATA_SIZE ,0, 0, 1);
4227 regs.u.r32.ebx = 4;
4228 regs.u.r32.eax = 0x534D4150;
4229 regs.u.r32.ecx = 0x14;
4230 CLEAR_CF();
4231 return;
4232 break;
4233 case 4:
4234 set_e820_range(ES, regs.u.r16.di,
4235 extended_memory_size - ACPI_DATA_SIZE,
4236 extended_memory_size ,0, 0, 3); // ACPI RAM
4237 regs.u.r32.ebx = 5;
4238 regs.u.r32.eax = 0x534D4150;
4239 regs.u.r32.ecx = 0x14;
4240 CLEAR_CF();
4241 return;
4242 break;
4243 case 5:
4244 /* 3 pages before the bios, we map the vmx tss pages */
4245 set_e820_range(ES, regs.u.r16.di, 0xfffbd000L,
4246 0xfffc0000L, 0, 0, 2);
4247 regs.u.r32.ebx = 6;
4248 regs.u.r32.eax = 0x534D4150;
4249 regs.u.r32.ecx = 0x14;
4250 CLEAR_CF();
4251 return;
4252 case 6:
4253 /* 256KB BIOS area at the end of 4 GB */
4254 set_e820_range(ES, regs.u.r16.di,
4255 0xfffc0000L, 0x00000000L ,0, 0, 2);
4256 if (extra_highbits_memory_size || extra_lowbits_memory_size)
4257 regs.u.r32.ebx = 7;
4258 else
4259 regs.u.r32.ebx = 0;
4260 regs.u.r32.eax = 0x534D4150;
4261 regs.u.r32.ecx = 0x14;
4262 CLEAR_CF();
4263 return;
4264 case 7:
4265 /* Maping of memory above 4 GB */
4266 set_e820_range(ES, regs.u.r16.di, 0x00000000L,
4267 extra_lowbits_memory_size, 1, extra_highbits_memory_size
4268 + 1, 1);
4269 regs.u.r32.ebx = 0;
4270 regs.u.r32.eax = 0x534D4150;
4271 regs.u.r32.ecx = 0x14;
4272 CLEAR_CF();
4273 return;
4274 default: /* AX=E820, DX=534D4150, BX unrecognized */
4275 goto int15_unimplemented;
4276 break;
4278 } else {
4279 // if DX != 0x534D4150)
4280 goto int15_unimplemented;
4282 break;
4284 case 0x01:
4285 // do we have any reason to fail here ?
4286 CLEAR_CF();
4288 // my real system sets ax and bx to 0
4289 // this is confirmed by Ralph Brown list
4290 // but syslinux v1.48 is known to behave
4291 // strangely if ax is set to 0
4292 // regs.u.r16.ax = 0;
4293 // regs.u.r16.bx = 0;
4295 // Get the amount of extended memory (above 1M)
4296 regs.u.r8.cl = inb_cmos(0x30);
4297 regs.u.r8.ch = inb_cmos(0x31);
4299 // limit to 15M
4300 if(regs.u.r16.cx > 0x3c00)
4302 regs.u.r16.cx = 0x3c00;
4305 // Get the amount of extended memory above 16M in 64k blocs
4306 regs.u.r8.dl = inb_cmos(0x34);
4307 regs.u.r8.dh = inb_cmos(0x35);
4309 // Set configured memory equal to extended memory
4310 regs.u.r16.ax = regs.u.r16.cx;
4311 regs.u.r16.bx = regs.u.r16.dx;
4312 break;
4313 default: /* AH=0xE8?? but not implemented */
4314 goto int15_unimplemented;
4316 break;
4317 int15_unimplemented:
4318 // fall into the default
4319 default:
4320 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4321 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4322 SET_CF();
4323 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4324 break;
4328 void
4329 int16_function(DI, SI, BP, SP, BX, DX, CX, AX, FLAGS)
4330 Bit16u DI, SI, BP, SP, BX, DX, CX, AX, FLAGS;
4332 Bit8u scan_code, ascii_code, shift_flags, led_flags, count;
4333 Bit16u kbd_code, max;
4335 BX_DEBUG_INT16("int16: AX=%04x BX=%04x CX=%04x DX=%04x \n", AX, BX, CX, DX);
4337 shift_flags = read_byte(0x0040, 0x17);
4338 led_flags = read_byte(0x0040, 0x97);
4339 if ((((shift_flags >> 4) & 0x07) ^ (led_flags & 0x07)) != 0) {
4340 ASM_START
4342 ASM_END
4343 outb(0x60, 0xed);
4344 while ((inb(0x64) & 0x01) == 0) outb(0x80, 0x21);
4345 if ((inb(0x60) == 0xfa)) {
4346 led_flags &= 0xf8;
4347 led_flags |= ((shift_flags >> 4) & 0x07);
4348 outb(0x60, led_flags & 0x07);
4349 while ((inb(0x64) & 0x01) == 0) outb(0x80, 0x21);
4350 inb(0x60);
4351 write_byte(0x0040, 0x97, led_flags);
4353 ASM_START
4355 ASM_END
4358 switch (GET_AH()) {
4359 case 0x00: /* read keyboard input */
4361 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4362 BX_PANIC("KBD: int16h: out of keyboard input\n");
4364 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4365 else if (ascii_code == 0xE0) ascii_code = 0;
4366 AX = (scan_code << 8) | ascii_code;
4367 break;
4369 case 0x01: /* check keyboard status */
4370 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4371 SET_ZF();
4372 return;
4374 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4375 else if (ascii_code == 0xE0) ascii_code = 0;
4376 AX = (scan_code << 8) | ascii_code;
4377 CLEAR_ZF();
4378 break;
4380 case 0x02: /* get shift flag status */
4381 shift_flags = read_byte(0x0040, 0x17);
4382 SET_AL(shift_flags);
4383 break;
4385 case 0x05: /* store key-stroke into buffer */
4386 if ( !enqueue_key(GET_CH(), GET_CL()) ) {
4387 SET_AL(1);
4389 else {
4390 SET_AL(0);
4392 break;
4394 case 0x09: /* GET KEYBOARD FUNCTIONALITY */
4395 // bit Bochs Description
4396 // 7 0 reserved
4397 // 6 0 INT 16/AH=20h-22h supported (122-key keyboard support)
4398 // 5 1 INT 16/AH=10h-12h supported (enhanced keyboard support)
4399 // 4 1 INT 16/AH=0Ah supported
4400 // 3 0 INT 16/AX=0306h supported
4401 // 2 0 INT 16/AX=0305h supported
4402 // 1 0 INT 16/AX=0304h supported
4403 // 0 0 INT 16/AX=0300h supported
4405 SET_AL(0x30);
4406 break;
4408 case 0x0A: /* GET KEYBOARD ID */
4409 count = 2;
4410 kbd_code = 0x0;
4411 outb(0x60, 0xf2);
4412 /* Wait for data */
4413 max=0xffff;
4414 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4415 if (max>0x0) {
4416 if ((inb(0x60) == 0xfa)) {
4417 do {
4418 max=0xffff;
4419 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4420 if (max>0x0) {
4421 kbd_code >>= 8;
4422 kbd_code |= (inb(0x60) << 8);
4424 } while (--count>0);
4427 BX=kbd_code;
4428 break;
4430 case 0x10: /* read MF-II keyboard input */
4432 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4433 BX_PANIC("KBD: int16h: out of keyboard input\n");
4435 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4436 AX = (scan_code << 8) | ascii_code;
4437 break;
4439 case 0x11: /* check MF-II keyboard status */
4440 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4441 SET_ZF();
4442 return;
4444 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4445 AX = (scan_code << 8) | ascii_code;
4446 CLEAR_ZF();
4447 break;
4449 case 0x12: /* get extended keyboard status */
4450 shift_flags = read_byte(0x0040, 0x17);
4451 SET_AL(shift_flags);
4452 shift_flags = read_byte(0x0040, 0x18) & 0x73;
4453 shift_flags |= read_byte(0x0040, 0x96) & 0x0c;
4454 SET_AH(shift_flags);
4455 BX_DEBUG_INT16("int16: func 12 sending %04x\n",AX);
4456 break;
4458 case 0x92: /* keyboard capability check called by DOS 5.0+ keyb */
4459 SET_AH(0x80); // function int16 ah=0x10-0x12 supported
4460 break;
4462 case 0xA2: /* 122 keys capability check called by DOS 5.0+ keyb */
4463 // don't change AH : function int16 ah=0x20-0x22 NOT supported
4464 break;
4466 case 0x6F:
4467 if (GET_AL() == 0x08)
4468 SET_AH(0x02); // unsupported, aka normal keyboard
4470 default:
4471 BX_INFO("KBD: unsupported int 16h function %02x\n", GET_AH());
4475 unsigned int
4476 dequeue_key(scan_code, ascii_code, incr)
4477 Bit8u *scan_code;
4478 Bit8u *ascii_code;
4479 unsigned int incr;
4481 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail;
4482 Bit16u ss;
4483 Bit8u acode, scode;
4485 #if BX_CPU < 2
4486 buffer_start = 0x001E;
4487 buffer_end = 0x003E;
4488 #else
4489 buffer_start = read_word(0x0040, 0x0080);
4490 buffer_end = read_word(0x0040, 0x0082);
4491 #endif
4493 buffer_head = read_word(0x0040, 0x001a);
4494 buffer_tail = read_word(0x0040, 0x001c);
4496 if (buffer_head != buffer_tail) {
4497 ss = get_SS();
4498 acode = read_byte(0x0040, buffer_head);
4499 scode = read_byte(0x0040, buffer_head+1);
4500 write_byte(ss, ascii_code, acode);
4501 write_byte(ss, scan_code, scode);
4503 if (incr) {
4504 buffer_head += 2;
4505 if (buffer_head >= buffer_end)
4506 buffer_head = buffer_start;
4507 write_word(0x0040, 0x001a, buffer_head);
4509 return(1);
4511 else {
4512 return(0);
4516 static char panic_msg_keyb_buffer_full[] = "%s: keyboard input buffer full\n";
4518 Bit8u
4519 inhibit_mouse_int_and_events()
4521 Bit8u command_byte, prev_command_byte;
4523 // Turn off IRQ generation and aux data line
4524 if ( inb(0x64) & 0x02 )
4525 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4526 outb(0x64, 0x20); // get command byte
4527 while ( (inb(0x64) & 0x01) != 0x01 );
4528 prev_command_byte = inb(0x60);
4529 command_byte = prev_command_byte;
4530 //while ( (inb(0x64) & 0x02) );
4531 if ( inb(0x64) & 0x02 )
4532 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4533 command_byte &= 0xfd; // turn off IRQ 12 generation
4534 command_byte |= 0x20; // disable mouse serial clock line
4535 outb(0x64, 0x60); // write command byte
4536 outb(0x60, command_byte);
4537 return(prev_command_byte);
4540 void
4541 enable_mouse_int_and_events()
4543 Bit8u command_byte;
4545 // Turn on IRQ generation and aux data line
4546 if ( inb(0x64) & 0x02 )
4547 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4548 outb(0x64, 0x20); // get command byte
4549 while ( (inb(0x64) & 0x01) != 0x01 );
4550 command_byte = inb(0x60);
4551 //while ( (inb(0x64) & 0x02) );
4552 if ( inb(0x64) & 0x02 )
4553 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4554 command_byte |= 0x02; // turn on IRQ 12 generation
4555 command_byte &= 0xdf; // enable mouse serial clock line
4556 outb(0x64, 0x60); // write command byte
4557 outb(0x60, command_byte);
4560 Bit8u
4561 send_to_mouse_ctrl(sendbyte)
4562 Bit8u sendbyte;
4564 Bit8u response;
4566 // wait for chance to write to ctrl
4567 if ( inb(0x64) & 0x02 )
4568 BX_PANIC(panic_msg_keyb_buffer_full,"sendmouse");
4569 outb(0x64, 0xD4);
4570 outb(0x60, sendbyte);
4571 return(0);
4575 Bit8u
4576 get_mouse_data(data)
4577 Bit8u *data;
4579 Bit8u response;
4580 Bit16u ss;
4582 while ( (inb(0x64) & 0x21) != 0x21 ) {
4585 response = inb(0x60);
4587 ss = get_SS();
4588 write_byte(ss, data, response);
4589 return(0);
4592 void
4593 set_kbd_command_byte(command_byte)
4594 Bit8u command_byte;
4596 if ( inb(0x64) & 0x02 )
4597 BX_PANIC(panic_msg_keyb_buffer_full,"setkbdcomm");
4598 outb(0x64, 0xD4);
4600 outb(0x64, 0x60); // write command byte
4601 outb(0x60, command_byte);
4604 void
4605 int09_function(DI, SI, BP, SP, BX, DX, CX, AX)
4606 Bit16u DI, SI, BP, SP, BX, DX, CX, AX;
4608 Bit8u scancode, asciicode, shift_flags;
4609 Bit8u mf2_flags, mf2_state;
4612 // DS has been set to F000 before call
4616 scancode = GET_AL();
4618 if (scancode == 0) {
4619 BX_INFO("KBD: int09 handler: AL=0\n");
4620 return;
4624 shift_flags = read_byte(0x0040, 0x17);
4625 mf2_flags = read_byte(0x0040, 0x18);
4626 mf2_state = read_byte(0x0040, 0x96);
4627 asciicode = 0;
4629 switch (scancode) {
4630 case 0x3a: /* Caps Lock press */
4631 shift_flags ^= 0x40;
4632 write_byte(0x0040, 0x17, shift_flags);
4633 mf2_flags |= 0x40;
4634 write_byte(0x0040, 0x18, mf2_flags);
4635 break;
4636 case 0xba: /* Caps Lock release */
4637 mf2_flags &= ~0x40;
4638 write_byte(0x0040, 0x18, mf2_flags);
4639 break;
4641 case 0x2a: /* L Shift press */
4642 shift_flags |= 0x02;
4643 write_byte(0x0040, 0x17, shift_flags);
4644 break;
4645 case 0xaa: /* L Shift release */
4646 shift_flags &= ~0x02;
4647 write_byte(0x0040, 0x17, shift_flags);
4648 break;
4650 case 0x36: /* R Shift press */
4651 shift_flags |= 0x01;
4652 write_byte(0x0040, 0x17, shift_flags);
4653 break;
4654 case 0xb6: /* R Shift release */
4655 shift_flags &= ~0x01;
4656 write_byte(0x0040, 0x17, shift_flags);
4657 break;
4659 case 0x1d: /* Ctrl press */
4660 if ((mf2_state & 0x01) == 0) {
4661 shift_flags |= 0x04;
4662 write_byte(0x0040, 0x17, shift_flags);
4663 if (mf2_state & 0x02) {
4664 mf2_state |= 0x04;
4665 write_byte(0x0040, 0x96, mf2_state);
4666 } else {
4667 mf2_flags |= 0x01;
4668 write_byte(0x0040, 0x18, mf2_flags);
4671 break;
4672 case 0x9d: /* Ctrl release */
4673 if ((mf2_state & 0x01) == 0) {
4674 shift_flags &= ~0x04;
4675 write_byte(0x0040, 0x17, shift_flags);
4676 if (mf2_state & 0x02) {
4677 mf2_state &= ~0x04;
4678 write_byte(0x0040, 0x96, mf2_state);
4679 } else {
4680 mf2_flags &= ~0x01;
4681 write_byte(0x0040, 0x18, mf2_flags);
4684 break;
4686 case 0x38: /* Alt press */
4687 shift_flags |= 0x08;
4688 write_byte(0x0040, 0x17, shift_flags);
4689 if (mf2_state & 0x02) {
4690 mf2_state |= 0x08;
4691 write_byte(0x0040, 0x96, mf2_state);
4692 } else {
4693 mf2_flags |= 0x02;
4694 write_byte(0x0040, 0x18, mf2_flags);
4696 break;
4697 case 0xb8: /* Alt release */
4698 shift_flags &= ~0x08;
4699 write_byte(0x0040, 0x17, shift_flags);
4700 if (mf2_state & 0x02) {
4701 mf2_state &= ~0x08;
4702 write_byte(0x0040, 0x96, mf2_state);
4703 } else {
4704 mf2_flags &= ~0x02;
4705 write_byte(0x0040, 0x18, mf2_flags);
4707 break;
4709 case 0x45: /* Num Lock press */
4710 if ((mf2_state & 0x03) == 0) {
4711 mf2_flags |= 0x20;
4712 write_byte(0x0040, 0x18, mf2_flags);
4713 shift_flags ^= 0x20;
4714 write_byte(0x0040, 0x17, shift_flags);
4716 break;
4717 case 0xc5: /* Num Lock release */
4718 if ((mf2_state & 0x03) == 0) {
4719 mf2_flags &= ~0x20;
4720 write_byte(0x0040, 0x18, mf2_flags);
4722 break;
4724 case 0x46: /* Scroll Lock press */
4725 mf2_flags |= 0x10;
4726 write_byte(0x0040, 0x18, mf2_flags);
4727 shift_flags ^= 0x10;
4728 write_byte(0x0040, 0x17, shift_flags);
4729 break;
4731 case 0xc6: /* Scroll Lock release */
4732 mf2_flags &= ~0x10;
4733 write_byte(0x0040, 0x18, mf2_flags);
4734 break;
4736 default:
4737 if (scancode & 0x80) {
4738 break; /* toss key releases ... */
4740 if (scancode > MAX_SCAN_CODE) {
4741 BX_INFO("KBD: int09h_handler(): unknown scancode read: 0x%02x!\n", scancode);
4742 return;
4744 if (shift_flags & 0x08) { /* ALT */
4745 asciicode = scan_to_scanascii[scancode].alt;
4746 scancode = scan_to_scanascii[scancode].alt >> 8;
4747 } else if (shift_flags & 0x04) { /* CONTROL */
4748 asciicode = scan_to_scanascii[scancode].control;
4749 scancode = scan_to_scanascii[scancode].control >> 8;
4750 } else if (((mf2_state & 0x02) > 0) && ((scancode >= 0x47) && (scancode <= 0x53))) {
4751 /* extended keys handling */
4752 asciicode = 0xe0;
4753 scancode = scan_to_scanascii[scancode].normal >> 8;
4754 } else if (shift_flags & 0x03) { /* LSHIFT + RSHIFT */
4755 /* check if lock state should be ignored
4756 * because a SHIFT key are pressed */
4758 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
4759 asciicode = scan_to_scanascii[scancode].normal;
4760 scancode = scan_to_scanascii[scancode].normal >> 8;
4761 } else {
4762 asciicode = scan_to_scanascii[scancode].shift;
4763 scancode = scan_to_scanascii[scancode].shift >> 8;
4765 } else {
4766 /* check if lock is on */
4767 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
4768 asciicode = scan_to_scanascii[scancode].shift;
4769 scancode = scan_to_scanascii[scancode].shift >> 8;
4770 } else {
4771 asciicode = scan_to_scanascii[scancode].normal;
4772 scancode = scan_to_scanascii[scancode].normal >> 8;
4775 if (scancode==0 && asciicode==0) {
4776 BX_INFO("KBD: int09h_handler(): scancode & asciicode are zero?\n");
4778 enqueue_key(scancode, asciicode);
4779 break;
4781 if ((scancode & 0x7f) != 0x1d) {
4782 mf2_state &= ~0x01;
4784 mf2_state &= ~0x02;
4785 write_byte(0x0040, 0x96, mf2_state);
4788 unsigned int
4789 enqueue_key(scan_code, ascii_code)
4790 Bit8u scan_code, ascii_code;
4792 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail, temp_tail;
4794 #if BX_CPU < 2
4795 buffer_start = 0x001E;
4796 buffer_end = 0x003E;
4797 #else
4798 buffer_start = read_word(0x0040, 0x0080);
4799 buffer_end = read_word(0x0040, 0x0082);
4800 #endif
4802 buffer_head = read_word(0x0040, 0x001A);
4803 buffer_tail = read_word(0x0040, 0x001C);
4805 temp_tail = buffer_tail;
4806 buffer_tail += 2;
4807 if (buffer_tail >= buffer_end)
4808 buffer_tail = buffer_start;
4810 if (buffer_tail == buffer_head) {
4811 return(0);
4814 write_byte(0x0040, temp_tail, ascii_code);
4815 write_byte(0x0040, temp_tail+1, scan_code);
4816 write_word(0x0040, 0x001C, buffer_tail);
4817 return(1);
4821 void
4822 int74_function(make_farcall, Z, Y, X, status)
4823 Bit16u make_farcall, Z, Y, X, status;
4825 Bit16u ebda_seg=read_word(0x0040,0x000E);
4826 Bit8u in_byte, index, package_count;
4827 Bit8u mouse_flags_1, mouse_flags_2;
4829 BX_DEBUG_INT74("entering int74_function\n");
4830 make_farcall = 0;
4832 in_byte = inb(0x64);
4833 if ( (in_byte & 0x21) != 0x21 ) {
4834 return;
4836 in_byte = inb(0x60);
4837 BX_DEBUG_INT74("int74: read byte %02x\n", in_byte);
4839 mouse_flags_1 = read_byte(ebda_seg, 0x0026);
4840 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4842 if ( (mouse_flags_2 & 0x80) != 0x80 ) {
4843 return;
4846 package_count = mouse_flags_2 & 0x07;
4847 index = mouse_flags_1 & 0x07;
4848 write_byte(ebda_seg, 0x28 + index, in_byte);
4850 if ( (index+1) >= package_count ) {
4851 BX_DEBUG_INT74("int74_function: make_farcall=1\n");
4852 status = read_byte(ebda_seg, 0x0028 + 0);
4853 X = read_byte(ebda_seg, 0x0028 + 1);
4854 Y = read_byte(ebda_seg, 0x0028 + 2);
4855 Z = 0;
4856 mouse_flags_1 = 0;
4857 // check if far call handler installed
4858 if (mouse_flags_2 & 0x80)
4859 make_farcall = 1;
4861 else {
4862 mouse_flags_1++;
4864 write_byte(ebda_seg, 0x0026, mouse_flags_1);
4867 #define SET_DISK_RET_STATUS(status) write_byte(0x0040, 0x0074, status)
4869 #if BX_USE_ATADRV
4871 void
4872 int13_harddisk(EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
4873 Bit16u EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
4875 Bit32u lba;
4876 Bit16u ebda_seg=read_word(0x0040,0x000E);
4877 Bit16u cylinder, head, sector;
4878 Bit16u segment, offset;
4879 Bit16u npc, nph, npspt, nlc, nlh, nlspt;
4880 Bit16u size, count;
4881 Bit8u device, status;
4883 BX_DEBUG_INT13_HD("int13_harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
4885 write_byte(0x0040, 0x008e, 0); // clear completion flag
4887 // basic check : device has to be defined
4888 if ( (GET_ELDL() < 0x80) || (GET_ELDL() >= 0x80 + BX_MAX_ATA_DEVICES) ) {
4889 BX_INFO("int13_harddisk: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
4890 goto int13_fail;
4893 // Get the ata channel
4894 device=read_byte(ebda_seg,&EbdaData->ata.hdidmap[GET_ELDL()-0x80]);
4896 // basic check : device has to be valid
4897 if (device >= BX_MAX_ATA_DEVICES) {
4898 BX_INFO("int13_harddisk: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
4899 goto int13_fail;
4902 switch (GET_AH()) {
4904 case 0x00: /* disk controller reset */
4905 ata_reset (device);
4906 goto int13_success;
4907 break;
4909 case 0x01: /* read disk status */
4910 status = read_byte(0x0040, 0x0074);
4911 SET_AH(status);
4912 SET_DISK_RET_STATUS(0);
4913 /* set CF if error status read */
4914 if (status) goto int13_fail_nostatus;
4915 else goto int13_success_noah;
4916 break;
4918 case 0x02: // read disk sectors
4919 case 0x03: // write disk sectors
4920 case 0x04: // verify disk sectors
4922 count = GET_AL();
4923 cylinder = GET_CH();
4924 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
4925 sector = (GET_CL() & 0x3f);
4926 head = GET_DH();
4928 segment = ES;
4929 offset = BX;
4931 if ((count > 128) || (count == 0) || (sector == 0)) {
4932 BX_INFO("int13_harddisk: function %02x, parameter out of range!\n",GET_AH());
4933 goto int13_fail;
4936 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
4937 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
4938 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
4940 // sanity check on cyl heads, sec
4941 if( (cylinder >= nlc) || (head >= nlh) || (sector > nlspt )) {
4942 BX_INFO("int13_harddisk: function %02x, parameters out of range %04x/%04x/%04x!\n", GET_AH(), cylinder, head, sector);
4943 goto int13_fail;
4946 // FIXME verify
4947 if ( GET_AH() == 0x04 ) goto int13_success;
4949 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
4950 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
4952 // if needed, translate lchs to lba, and execute command
4953 if ( (nph != nlh) || (npspt != nlspt)) {
4954 lba = ((((Bit32u)cylinder * (Bit32u)nlh) + (Bit32u)head) * (Bit32u)nlspt) + (Bit32u)sector - 1;
4955 sector = 0; // this forces the command to be lba
4958 if ( GET_AH() == 0x02 )
4959 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, cylinder, head, sector, lba, segment, offset);
4960 else
4961 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, cylinder, head, sector, lba, segment, offset);
4963 // Set nb of sector transferred
4964 SET_AL(read_word(ebda_seg, &EbdaData->ata.trsfsectors));
4966 if (status != 0) {
4967 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
4968 SET_AH(0x0c);
4969 goto int13_fail_noah;
4972 goto int13_success;
4973 break;
4975 case 0x05: /* format disk track */
4976 BX_INFO("format disk track called\n");
4977 goto int13_success;
4978 return;
4979 break;
4981 case 0x08: /* read disk drive parameters */
4983 // Get logical geometry from table
4984 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
4985 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
4986 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
4987 count = read_byte(ebda_seg, &EbdaData->ata.hdcount);
4989 nlc = nlc - 2; /* 0 based , last sector not used */
4990 SET_AL(0);
4991 SET_CH(nlc & 0xff);
4992 SET_CL(((nlc >> 2) & 0xc0) | (nlspt & 0x3f));
4993 SET_DH(nlh - 1);
4994 SET_DL(count); /* FIXME returns 0, 1, or n hard drives */
4996 // FIXME should set ES & DI
4998 goto int13_success;
4999 break;
5001 case 0x10: /* check drive ready */
5002 // should look at 40:8E also???
5004 // Read the status from controller
5005 status = inb(read_word(ebda_seg, &EbdaData->ata.channels[device/2].iobase1) + ATA_CB_STAT);
5006 if ( (status & ( ATA_CB_STAT_BSY | ATA_CB_STAT_RDY )) == ATA_CB_STAT_RDY ) {
5007 goto int13_success;
5009 else {
5010 SET_AH(0xAA);
5011 goto int13_fail_noah;
5013 break;
5015 case 0x15: /* read disk drive size */
5017 // Get physical geometry from table
5018 npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
5019 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
5020 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
5022 // Compute sector count seen by int13
5023 lba = (Bit32u)(npc - 1) * (Bit32u)nph * (Bit32u)npspt;
5024 CX = lba >> 16;
5025 DX = lba & 0xffff;
5027 SET_AH(3); // hard disk accessible
5028 goto int13_success_noah;
5029 break;
5031 case 0x41: // IBM/MS installation check
5032 BX=0xaa55; // install check
5033 SET_AH(0x30); // EDD 3.0
5034 CX=0x0007; // ext disk access and edd, removable supported
5035 goto int13_success_noah;
5036 break;
5038 case 0x42: // IBM/MS extended read
5039 case 0x43: // IBM/MS extended write
5040 case 0x44: // IBM/MS verify
5041 case 0x47: // IBM/MS extended seek
5043 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
5044 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
5045 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
5047 // Can't use 64 bits lba
5048 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
5049 if (lba != 0L) {
5050 BX_PANIC("int13_harddisk: function %02x. Can't use 64bits lba\n",GET_AH());
5051 goto int13_fail;
5054 // Get 32 bits lba and check
5055 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5056 if (lba >= read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors) ) {
5057 BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH());
5058 goto int13_fail;
5061 // If verify or seek
5062 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
5063 goto int13_success;
5065 // Execute the command
5066 if ( GET_AH() == 0x42 )
5067 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, 0, 0, 0, lba, segment, offset);
5068 else
5069 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, 0, 0, 0, lba, segment, offset);
5071 count=read_word(ebda_seg, &EbdaData->ata.trsfsectors);
5072 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5074 if (status != 0) {
5075 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
5076 SET_AH(0x0c);
5077 goto int13_fail_noah;
5080 goto int13_success;
5081 break;
5083 case 0x45: // IBM/MS lock/unlock drive
5084 case 0x49: // IBM/MS extended media change
5085 goto int13_success; // Always success for HD
5086 break;
5088 case 0x46: // IBM/MS eject media
5089 SET_AH(0xb2); // Volume Not Removable
5090 goto int13_fail_noah; // Always fail for HD
5091 break;
5093 case 0x48: // IBM/MS get drive parameters
5094 size=read_word(DS,SI+(Bit16u)&Int13DPT->size);
5096 // Buffer is too small
5097 if(size < 0x1a)
5098 goto int13_fail;
5100 // EDD 1.x
5101 if(size >= 0x1a) {
5102 Bit16u blksize;
5104 npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
5105 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
5106 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
5107 lba = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors);
5108 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5110 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5111 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x02); // geometry is valid
5112 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, (Bit32u)npc);
5113 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, (Bit32u)nph);
5114 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, (Bit32u)npspt);
5115 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, lba); // FIXME should be Bit64
5116 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0L);
5117 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5120 // EDD 2.x
5121 if(size >= 0x1e) {
5122 Bit8u channel, dev, irq, mode, checksum, i, translation;
5123 Bit16u iobase1, iobase2, options;
5125 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5127 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5128 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5130 // Fill in dpte
5131 channel = device / 2;
5132 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5133 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5134 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5135 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5136 translation = read_byte(ebda_seg, &EbdaData->ata.devices[device].translation);
5138 options = (translation==ATA_TRANSLATION_NONE?0:1<<3); // chs translation
5139 options |= (1<<4); // lba translation
5140 options |= (mode==ATA_MODE_PIO32?1:0<<7);
5141 options |= (translation==ATA_TRANSLATION_LBA?1:0<<9);
5142 options |= (translation==ATA_TRANSLATION_RECHS?3:0<<9);
5144 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5145 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2);
5146 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5147 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5148 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5149 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5150 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5151 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5152 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5153 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5154 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5156 checksum=0;
5157 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, (&EbdaData->ata.dpte) + i);
5158 checksum = ~checksum;
5159 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5162 // EDD 3.x
5163 if(size >= 0x42) {
5164 Bit8u channel, iface, checksum, i;
5165 Bit16u iobase1;
5167 channel = device / 2;
5168 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5169 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5171 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5172 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5173 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5174 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5175 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5177 if (iface==ATA_IFACE_ISA) {
5178 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5179 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5180 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5181 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5183 else {
5184 // FIXME PCI
5186 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5187 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5188 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5189 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5191 if (iface==ATA_IFACE_ISA) {
5192 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5193 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5194 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5196 else {
5197 // FIXME PCI
5199 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5200 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5201 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5202 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5204 checksum=0;
5205 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5206 checksum = ~checksum;
5207 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5210 goto int13_success;
5211 break;
5213 case 0x4e: // // IBM/MS set hardware configuration
5214 // DMA, prefetch, PIO maximum not supported
5215 switch (GET_AL()) {
5216 case 0x01:
5217 case 0x03:
5218 case 0x04:
5219 case 0x06:
5220 goto int13_success;
5221 break;
5222 default :
5223 goto int13_fail;
5225 break;
5227 case 0x09: /* initialize drive parameters */
5228 case 0x0c: /* seek to specified cylinder */
5229 case 0x0d: /* alternate disk reset */
5230 case 0x11: /* recalibrate */
5231 case 0x14: /* controller internal diagnostic */
5232 BX_INFO("int13_harddisk: function %02xh unimplemented, returns success\n", GET_AH());
5233 goto int13_success;
5234 break;
5236 case 0x0a: /* read disk sectors with ECC */
5237 case 0x0b: /* write disk sectors with ECC */
5238 case 0x18: // set media type for format
5239 case 0x50: // IBM/MS send packet command
5240 default:
5241 BX_INFO("int13_harddisk: function %02xh unsupported, returns fail\n", GET_AH());
5242 goto int13_fail;
5243 break;
5246 int13_fail:
5247 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5248 int13_fail_noah:
5249 SET_DISK_RET_STATUS(GET_AH());
5250 int13_fail_nostatus:
5251 SET_CF(); // error occurred
5252 return;
5254 int13_success:
5255 SET_AH(0x00); // no error
5256 int13_success_noah:
5257 SET_DISK_RET_STATUS(0x00);
5258 CLEAR_CF(); // no error
5259 return;
5262 // ---------------------------------------------------------------------------
5263 // Start of int13 for cdrom
5264 // ---------------------------------------------------------------------------
5266 void
5267 int13_cdrom(EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
5268 Bit16u EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
5270 Bit16u ebda_seg=read_word(0x0040,0x000E);
5271 Bit8u device, status, locks;
5272 Bit8u atacmd[12];
5273 Bit32u lba;
5274 Bit16u count, segment, offset, i, size;
5276 BX_DEBUG_INT13_CD("int13_cdrom: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5278 SET_DISK_RET_STATUS(0x00);
5280 /* basic check : device should be 0xE0+ */
5281 if( (GET_ELDL() < 0xE0) || (GET_ELDL() >= 0xE0+BX_MAX_ATA_DEVICES) ) {
5282 BX_INFO("int13_cdrom: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
5283 goto int13_fail;
5286 // Get the ata channel
5287 device=read_byte(ebda_seg,&EbdaData->ata.cdidmap[GET_ELDL()-0xE0]);
5289 /* basic check : device has to be valid */
5290 if (device >= BX_MAX_ATA_DEVICES) {
5291 BX_INFO("int13_cdrom: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
5292 goto int13_fail;
5295 switch (GET_AH()) {
5297 // all those functions return SUCCESS
5298 case 0x00: /* disk controller reset */
5299 case 0x09: /* initialize drive parameters */
5300 case 0x0c: /* seek to specified cylinder */
5301 case 0x0d: /* alternate disk reset */
5302 case 0x10: /* check drive ready */
5303 case 0x11: /* recalibrate */
5304 case 0x14: /* controller internal diagnostic */
5305 case 0x16: /* detect disk change */
5306 goto int13_success;
5307 break;
5309 // all those functions return disk write-protected
5310 case 0x03: /* write disk sectors */
5311 case 0x05: /* format disk track */
5312 case 0x43: // IBM/MS extended write
5313 SET_AH(0x03);
5314 goto int13_fail_noah;
5315 break;
5317 case 0x01: /* read disk status */
5318 status = read_byte(0x0040, 0x0074);
5319 SET_AH(status);
5320 SET_DISK_RET_STATUS(0);
5322 /* set CF if error status read */
5323 if (status) goto int13_fail_nostatus;
5324 else goto int13_success_noah;
5325 break;
5327 case 0x15: /* read disk drive size */
5328 SET_AH(0x02);
5329 goto int13_fail_noah;
5330 break;
5332 case 0x41: // IBM/MS installation check
5333 BX=0xaa55; // install check
5334 SET_AH(0x30); // EDD 2.1
5335 CX=0x0007; // ext disk access, removable and edd
5336 goto int13_success_noah;
5337 break;
5339 case 0x42: // IBM/MS extended read
5340 case 0x44: // IBM/MS verify sectors
5341 case 0x47: // IBM/MS extended seek
5343 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
5344 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
5345 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
5347 // Can't use 64 bits lba
5348 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
5349 if (lba != 0L) {
5350 BX_PANIC("int13_cdrom: function %02x. Can't use 64bits lba\n",GET_AH());
5351 goto int13_fail;
5354 // Get 32 bits lba
5355 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5357 // If verify or seek
5358 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
5359 goto int13_success;
5361 memsetb(get_SS(),atacmd,0,12);
5362 atacmd[0]=0x28; // READ command
5363 atacmd[7]=(count & 0xff00) >> 8; // Sectors
5364 atacmd[8]=(count & 0x00ff); // Sectors
5365 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
5366 atacmd[3]=(lba & 0x00ff0000) >> 16;
5367 atacmd[4]=(lba & 0x0000ff00) >> 8;
5368 atacmd[5]=(lba & 0x000000ff);
5369 status = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, count*2048L, ATA_DATA_IN, segment,offset);
5371 count = (Bit16u)(read_dword(ebda_seg, &EbdaData->ata.trsfbytes) >> 11);
5372 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5374 if (status != 0) {
5375 BX_INFO("int13_cdrom: function %02x, status %02x !\n",GET_AH(),status);
5376 SET_AH(0x0c);
5377 goto int13_fail_noah;
5380 goto int13_success;
5381 break;
5383 case 0x45: // IBM/MS lock/unlock drive
5384 if (GET_AL() > 2) goto int13_fail;
5386 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5388 switch (GET_AL()) {
5389 case 0 : // lock
5390 if (locks == 0xff) {
5391 SET_AH(0xb4);
5392 SET_AL(1);
5393 goto int13_fail_noah;
5395 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, ++locks);
5396 SET_AL(1);
5397 break;
5398 case 1 : // unlock
5399 if (locks == 0x00) {
5400 SET_AH(0xb0);
5401 SET_AL(0);
5402 goto int13_fail_noah;
5404 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, --locks);
5405 SET_AL(locks==0?0:1);
5406 break;
5407 case 2 : // status
5408 SET_AL(locks==0?0:1);
5409 break;
5411 goto int13_success;
5412 break;
5414 case 0x46: // IBM/MS eject media
5415 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5417 if (locks != 0) {
5418 SET_AH(0xb1); // media locked
5419 goto int13_fail_noah;
5421 // FIXME should handle 0x31 no media in device
5422 // FIXME should handle 0xb5 valid request failed
5424 // Call removable media eject
5425 ASM_START
5426 push bp
5427 mov bp, sp
5429 mov ah, #0x52
5430 int 15
5431 mov _int13_cdrom.status + 2[bp], ah
5432 jnc int13_cdrom_rme_end
5433 mov _int13_cdrom.status, #1
5434 int13_cdrom_rme_end:
5435 pop bp
5436 ASM_END
5438 if (status != 0) {
5439 SET_AH(0xb1); // media locked
5440 goto int13_fail_noah;
5443 goto int13_success;
5444 break;
5446 case 0x48: // IBM/MS get drive parameters
5447 size = read_word(DS,SI+(Bit16u)&Int13Ext->size);
5449 // Buffer is too small
5450 if(size < 0x1a)
5451 goto int13_fail;
5453 // EDD 1.x
5454 if(size >= 0x1a) {
5455 Bit16u cylinders, heads, spt, blksize;
5457 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5459 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5460 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x74); // removable, media change, lockable, max values
5461 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0xffffffff);
5462 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, 0xffffffff);
5463 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, 0xffffffff);
5464 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, 0xffffffff); // FIXME should be Bit64
5465 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0xffffffff);
5466 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5469 // EDD 2.x
5470 if(size >= 0x1e) {
5471 Bit8u channel, dev, irq, mode, checksum, i;
5472 Bit16u iobase1, iobase2, options;
5474 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5476 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5477 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5479 // Fill in dpte
5480 channel = device / 2;
5481 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5482 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5483 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5484 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5486 // FIXME atapi device
5487 options = (1<<4); // lba translation
5488 options |= (1<<5); // removable device
5489 options |= (1<<6); // atapi device
5490 options |= (mode==ATA_MODE_PIO32?1:0<<7);
5492 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5493 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2);
5494 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5495 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5496 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5497 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5498 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5499 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5500 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5501 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5502 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5504 checksum=0;
5505 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, (&EbdaData->ata.dpte) + i);
5506 checksum = ~checksum;
5507 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5510 // EDD 3.x
5511 if(size >= 0x42) {
5512 Bit8u channel, iface, checksum, i;
5513 Bit16u iobase1;
5515 channel = device / 2;
5516 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5517 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5519 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5520 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5521 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5522 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5523 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5525 if (iface==ATA_IFACE_ISA) {
5526 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5527 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5528 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5529 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5531 else {
5532 // FIXME PCI
5534 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5535 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5536 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5537 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5539 if (iface==ATA_IFACE_ISA) {
5540 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5541 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5542 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5544 else {
5545 // FIXME PCI
5547 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5548 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5549 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5550 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5552 checksum=0;
5553 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5554 checksum = ~checksum;
5555 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5558 goto int13_success;
5559 break;
5561 case 0x49: // IBM/MS extended media change
5562 // always send changed ??
5563 SET_AH(06);
5564 goto int13_fail_nostatus;
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 // all those functions return unimplemented
5582 case 0x02: /* read sectors */
5583 case 0x04: /* verify sectors */
5584 case 0x08: /* read disk drive parameters */
5585 case 0x0a: /* read disk sectors with ECC */
5586 case 0x0b: /* write disk sectors with ECC */
5587 case 0x18: /* set media type for format */
5588 case 0x50: // ? - send packet command
5589 default:
5590 BX_INFO("int13_cdrom: unsupported AH=%02x\n", GET_AH());
5591 goto int13_fail;
5592 break;
5595 int13_fail:
5596 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5597 int13_fail_noah:
5598 SET_DISK_RET_STATUS(GET_AH());
5599 int13_fail_nostatus:
5600 SET_CF(); // error occurred
5601 return;
5603 int13_success:
5604 SET_AH(0x00); // no error
5605 int13_success_noah:
5606 SET_DISK_RET_STATUS(0x00);
5607 CLEAR_CF(); // no error
5608 return;
5611 // ---------------------------------------------------------------------------
5612 // End of int13 for cdrom
5613 // ---------------------------------------------------------------------------
5615 #if BX_ELTORITO_BOOT
5616 // ---------------------------------------------------------------------------
5617 // Start of int13 for eltorito functions
5618 // ---------------------------------------------------------------------------
5620 void
5621 int13_eltorito(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
5622 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
5624 Bit16u ebda_seg=read_word(0x0040,0x000E);
5626 BX_DEBUG_INT13_ET("int13_eltorito: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5627 // BX_DEBUG_INT13_ET("int13_eltorito: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
5629 switch (GET_AH()) {
5631 // FIXME ElTorito Various. Should be implemented
5632 case 0x4a: // ElTorito - Initiate disk emu
5633 case 0x4c: // ElTorito - Initiate disk emu and boot
5634 case 0x4d: // ElTorito - Return Boot catalog
5635 BX_PANIC("Int13 eltorito call with AX=%04x. Please report\n",AX);
5636 goto int13_fail;
5637 break;
5639 case 0x4b: // ElTorito - Terminate disk emu
5640 // FIXME ElTorito Hardcoded
5641 write_byte(DS,SI+0x00,0x13);
5642 write_byte(DS,SI+0x01,read_byte(ebda_seg,&EbdaData->cdemu.media));
5643 write_byte(DS,SI+0x02,read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
5644 write_byte(DS,SI+0x03,read_byte(ebda_seg,&EbdaData->cdemu.controller_index));
5645 write_dword(DS,SI+0x04,read_dword(ebda_seg,&EbdaData->cdemu.ilba));
5646 write_word(DS,SI+0x08,read_word(ebda_seg,&EbdaData->cdemu.device_spec));
5647 write_word(DS,SI+0x0a,read_word(ebda_seg,&EbdaData->cdemu.buffer_segment));
5648 write_word(DS,SI+0x0c,read_word(ebda_seg,&EbdaData->cdemu.load_segment));
5649 write_word(DS,SI+0x0e,read_word(ebda_seg,&EbdaData->cdemu.sector_count));
5650 write_byte(DS,SI+0x10,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.cylinders));
5651 write_byte(DS,SI+0x11,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.spt));
5652 write_byte(DS,SI+0x12,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.heads));
5654 // If we have to terminate emulation
5655 if(GET_AL() == 0x00) {
5656 // FIXME ElTorito Various. Should be handled accordingly to spec
5657 write_byte(ebda_seg,&EbdaData->cdemu.active, 0x00); // bye bye
5660 goto int13_success;
5661 break;
5663 default:
5664 BX_INFO("int13_eltorito: unsupported AH=%02x\n", GET_AH());
5665 goto int13_fail;
5666 break;
5669 int13_fail:
5670 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5671 SET_DISK_RET_STATUS(GET_AH());
5672 SET_CF(); // error occurred
5673 return;
5675 int13_success:
5676 SET_AH(0x00); // no error
5677 SET_DISK_RET_STATUS(0x00);
5678 CLEAR_CF(); // no error
5679 return;
5682 // ---------------------------------------------------------------------------
5683 // End of int13 for eltorito functions
5684 // ---------------------------------------------------------------------------
5686 // ---------------------------------------------------------------------------
5687 // Start of int13 when emulating a device from the cd
5688 // ---------------------------------------------------------------------------
5690 void
5691 int13_cdemu(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
5692 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
5694 Bit16u ebda_seg=read_word(0x0040,0x000E);
5695 Bit8u device, status;
5696 Bit16u vheads, vspt, vcylinders;
5697 Bit16u head, sector, cylinder, nbsectors;
5698 Bit32u vlba, ilba, slba, elba;
5699 Bit16u before, segment, offset;
5700 Bit8u atacmd[12];
5702 BX_DEBUG_INT13_ET("int13_cdemu: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5704 /* at this point, we are emulating a floppy/harddisk */
5706 // Recompute the device number
5707 device = read_byte(ebda_seg,&EbdaData->cdemu.controller_index) * 2;
5708 device += read_byte(ebda_seg,&EbdaData->cdemu.device_spec);
5710 SET_DISK_RET_STATUS(0x00);
5712 /* basic checks : emulation should be active, dl should equal the emulated drive */
5713 if( (read_byte(ebda_seg,&EbdaData->cdemu.active) ==0 )
5714 || (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive ) != GET_DL())) {
5715 BX_INFO("int13_cdemu: function %02x, emulation not active for DL= %02x\n", GET_AH(), GET_DL());
5716 goto int13_fail;
5719 switch (GET_AH()) {
5721 // all those functions return SUCCESS
5722 case 0x00: /* disk controller reset */
5723 case 0x09: /* initialize drive parameters */
5724 case 0x0c: /* seek to specified cylinder */
5725 case 0x0d: /* alternate disk reset */ // FIXME ElTorito Various. should really reset ?
5726 case 0x10: /* check drive ready */ // FIXME ElTorito Various. should check if ready ?
5727 case 0x11: /* recalibrate */
5728 case 0x14: /* controller internal diagnostic */
5729 case 0x16: /* detect disk change */
5730 goto int13_success;
5731 break;
5733 // all those functions return disk write-protected
5734 case 0x03: /* write disk sectors */
5735 case 0x05: /* format disk track */
5736 SET_AH(0x03);
5737 goto int13_fail_noah;
5738 break;
5740 case 0x01: /* read disk status */
5741 status=read_byte(0x0040, 0x0074);
5742 SET_AH(status);
5743 SET_DISK_RET_STATUS(0);
5745 /* set CF if error status read */
5746 if (status) goto int13_fail_nostatus;
5747 else goto int13_success_noah;
5748 break;
5750 case 0x02: // read disk sectors
5751 case 0x04: // verify disk sectors
5752 vspt = read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
5753 vcylinders = read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders);
5754 vheads = read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads);
5756 ilba = read_dword(ebda_seg,&EbdaData->cdemu.ilba);
5758 sector = GET_CL() & 0x003f;
5759 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
5760 head = GET_DH();
5761 nbsectors = GET_AL();
5762 segment = ES;
5763 offset = BX;
5765 // no sector to read ?
5766 if(nbsectors==0) goto int13_success;
5768 // sanity checks sco openserver needs this!
5769 if ((sector > vspt)
5770 || (cylinder >= vcylinders)
5771 || (head >= vheads)) {
5772 goto int13_fail;
5775 // After controls, verify do nothing
5776 if (GET_AH() == 0x04) goto int13_success;
5778 segment = ES+(BX / 16);
5779 offset = BX % 16;
5781 // calculate the virtual lba inside the image
5782 vlba=((((Bit32u)cylinder*(Bit32u)vheads)+(Bit32u)head)*(Bit32u)vspt)+((Bit32u)(sector-1));
5784 // In advance so we don't loose the count
5785 SET_AL(nbsectors);
5787 // start lba on cd
5788 slba = (Bit32u)vlba/4;
5789 before= (Bit16u)vlba%4;
5791 // end lba on cd
5792 elba = (Bit32u)(vlba+nbsectors-1)/4;
5794 memsetb(get_SS(),atacmd,0,12);
5795 atacmd[0]=0x28; // READ command
5796 atacmd[7]=((Bit16u)(elba-slba+1) & 0xff00) >> 8; // Sectors
5797 atacmd[8]=((Bit16u)(elba-slba+1) & 0x00ff); // Sectors
5798 atacmd[2]=(ilba+slba & 0xff000000) >> 24; // LBA
5799 atacmd[3]=(ilba+slba & 0x00ff0000) >> 16;
5800 atacmd[4]=(ilba+slba & 0x0000ff00) >> 8;
5801 atacmd[5]=(ilba+slba & 0x000000ff);
5802 if((status = ata_cmd_packet(device, 12, get_SS(), atacmd, before*512, nbsectors*512L, ATA_DATA_IN, segment,offset)) != 0) {
5803 BX_INFO("int13_cdemu: function %02x, error %02x !\n",GET_AH(),status);
5804 SET_AH(0x02);
5805 SET_AL(0);
5806 goto int13_fail_noah;
5809 goto int13_success;
5810 break;
5812 case 0x08: /* read disk drive parameters */
5813 vspt=read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
5814 vcylinders=read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders) - 1;
5815 vheads=read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads) - 1;
5817 SET_AL( 0x00 );
5818 SET_BL( 0x00 );
5819 SET_CH( vcylinders & 0xff );
5820 SET_CL((( vcylinders >> 2) & 0xc0) | ( vspt & 0x3f ));
5821 SET_DH( vheads );
5822 SET_DL( 0x02 ); // FIXME ElTorito Various. should send the real count of drives 1 or 2
5823 // FIXME ElTorito Harddisk. should send the HD count
5825 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
5826 case 0x01: SET_BL( 0x02 ); break;
5827 case 0x02: SET_BL( 0x04 ); break;
5828 case 0x03: SET_BL( 0x06 ); break;
5831 ASM_START
5832 push bp
5833 mov bp, sp
5834 mov ax, #diskette_param_table2
5835 mov _int13_cdemu.DI+2[bp], ax
5836 mov _int13_cdemu.ES+2[bp], cs
5837 pop bp
5838 ASM_END
5839 goto int13_success;
5840 break;
5842 case 0x15: /* read disk drive size */
5843 // FIXME ElTorito Harddisk. What geometry to send ?
5844 SET_AH(0x03);
5845 goto int13_success_noah;
5846 break;
5848 // all those functions return unimplemented
5849 case 0x0a: /* read disk sectors with ECC */
5850 case 0x0b: /* write disk sectors with ECC */
5851 case 0x18: /* set media type for format */
5852 case 0x41: // IBM/MS installation check
5853 // FIXME ElTorito Harddisk. Darwin would like to use EDD
5854 case 0x42: // IBM/MS extended read
5855 case 0x43: // IBM/MS extended write
5856 case 0x44: // IBM/MS verify sectors
5857 case 0x45: // IBM/MS lock/unlock drive
5858 case 0x46: // IBM/MS eject media
5859 case 0x47: // IBM/MS extended seek
5860 case 0x48: // IBM/MS get drive parameters
5861 case 0x49: // IBM/MS extended media change
5862 case 0x4e: // ? - set hardware configuration
5863 case 0x50: // ? - send packet command
5864 default:
5865 BX_INFO("int13_cdemu function AH=%02x unsupported, returns fail\n", GET_AH());
5866 goto int13_fail;
5867 break;
5870 int13_fail:
5871 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5872 int13_fail_noah:
5873 SET_DISK_RET_STATUS(GET_AH());
5874 int13_fail_nostatus:
5875 SET_CF(); // error occurred
5876 return;
5878 int13_success:
5879 SET_AH(0x00); // no error
5880 int13_success_noah:
5881 SET_DISK_RET_STATUS(0x00);
5882 CLEAR_CF(); // no error
5883 return;
5886 // ---------------------------------------------------------------------------
5887 // End of int13 when emulating a device from the cd
5888 // ---------------------------------------------------------------------------
5890 #endif // BX_ELTORITO_BOOT
5892 #else //BX_USE_ATADRV
5894 void
5895 outLBA(cylinder,hd_heads,head,hd_sectors,sector,dl)
5896 Bit16u cylinder;
5897 Bit16u hd_heads;
5898 Bit16u head;
5899 Bit16u hd_sectors;
5900 Bit16u sector;
5901 Bit16u dl;
5903 ASM_START
5904 push bp
5905 mov bp, sp
5906 push eax
5907 push ebx
5908 push edx
5909 xor eax,eax
5910 mov ax,4[bp] // cylinder
5911 xor ebx,ebx
5912 mov bl,6[bp] // hd_heads
5913 imul ebx
5915 mov bl,8[bp] // head
5916 add eax,ebx
5917 mov bl,10[bp] // hd_sectors
5918 imul ebx
5919 mov bl,12[bp] // sector
5920 add eax,ebx
5922 dec eax
5923 mov dx,#0x1f3
5924 out dx,al
5925 mov dx,#0x1f4
5926 mov al,ah
5927 out dx,al
5928 shr eax,#16
5929 mov dx,#0x1f5
5930 out dx,al
5931 and ah,#0xf
5932 mov bl,14[bp] // dl
5933 and bl,#1
5934 shl bl,#4
5935 or ah,bl
5936 or ah,#0xe0
5937 mov al,ah
5938 mov dx,#0x01f6
5939 out dx,al
5940 pop edx
5941 pop ebx
5942 pop eax
5943 pop bp
5944 ASM_END
5947 void
5948 int13_harddisk(EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
5949 Bit16u EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
5951 Bit8u drive, num_sectors, sector, head, status, mod;
5952 Bit8u drive_map;
5953 Bit8u n_drives;
5954 Bit16u cyl_mod, ax;
5955 Bit16u max_cylinder, cylinder, total_sectors;
5956 Bit16u hd_cylinders;
5957 Bit8u hd_heads, hd_sectors;
5958 Bit16u val16;
5959 Bit8u sector_count;
5960 unsigned int i;
5961 Bit16u tempbx;
5962 Bit16u dpsize;
5964 Bit16u count, segment, offset;
5965 Bit32u lba;
5966 Bit16u error;
5968 BX_DEBUG_INT13_HD("int13 harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5970 write_byte(0x0040, 0x008e, 0); // clear completion flag
5972 /* at this point, DL is >= 0x80 to be passed from the floppy int13h
5973 handler code */
5974 /* check how many disks first (cmos reg 0x12), return an error if
5975 drive not present */
5976 drive_map = inb_cmos(0x12);
5977 drive_map = (((drive_map & 0xf0)==0) ? 0 : 1) |
5978 (((drive_map & 0x0f)==0) ? 0 : 2);
5979 n_drives = (drive_map==0) ? 0 :
5980 ((drive_map==3) ? 2 : 1);
5982 if (!(drive_map & (1<<(GET_ELDL()&0x7f)))) { /* allow 0, 1, or 2 disks */
5983 SET_AH(0x01);
5984 SET_DISK_RET_STATUS(0x01);
5985 SET_CF(); /* error occurred */
5986 return;
5989 switch (GET_AH()) {
5991 case 0x00: /* disk controller reset */
5992 BX_DEBUG_INT13_HD("int13_f00\n");
5994 SET_AH(0);
5995 SET_DISK_RET_STATUS(0);
5996 set_diskette_ret_status(0);
5997 set_diskette_current_cyl(0, 0); /* current cylinder, diskette 1 */
5998 set_diskette_current_cyl(1, 0); /* current cylinder, diskette 2 */
5999 CLEAR_CF(); /* successful */
6000 return;
6001 break;
6003 case 0x01: /* read disk status */
6004 BX_DEBUG_INT13_HD("int13_f01\n");
6005 status = read_byte(0x0040, 0x0074);
6006 SET_AH(status);
6007 SET_DISK_RET_STATUS(0);
6008 /* set CF if error status read */
6009 if (status) SET_CF();
6010 else CLEAR_CF();
6011 return;
6012 break;
6014 case 0x04: // verify disk sectors
6015 case 0x02: // read disk sectors
6016 drive = GET_ELDL();
6017 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6019 num_sectors = GET_AL();
6020 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
6021 sector = (GET_CL() & 0x3f);
6022 head = GET_DH();
6025 if (hd_cylinders > 1024) {
6026 if (hd_cylinders <= 2048) {
6027 cylinder <<= 1;
6029 else if (hd_cylinders <= 4096) {
6030 cylinder <<= 2;
6032 else if (hd_cylinders <= 8192) {
6033 cylinder <<= 3;
6035 else { // hd_cylinders <= 16384
6036 cylinder <<= 4;
6039 ax = head / hd_heads;
6040 cyl_mod = ax & 0xff;
6041 head = ax >> 8;
6042 cylinder |= cyl_mod;
6045 if ( (cylinder >= hd_cylinders) ||
6046 (sector > hd_sectors) ||
6047 (head >= hd_heads) ) {
6048 SET_AH(1);
6049 SET_DISK_RET_STATUS(1);
6050 SET_CF(); /* error occurred */
6051 return;
6054 if ( (num_sectors > 128) || (num_sectors == 0) )
6055 BX_PANIC("int13_harddisk: num_sectors out of range!\n");
6057 if (head > 15)
6058 BX_PANIC("hard drive BIOS:(read/verify) head > 15\n");
6060 if ( GET_AH() == 0x04 ) {
6061 SET_AH(0);
6062 SET_DISK_RET_STATUS(0);
6063 CLEAR_CF();
6064 return;
6067 status = inb(0x1f7);
6068 if (status & 0x80) {
6069 BX_PANIC("hard drive BIOS:(read/verify) BUSY bit set\n");
6071 outb(0x01f2, num_sectors);
6072 /* activate LBA? (tomv) */
6073 if (hd_heads > 16) {
6074 BX_DEBUG_INT13_HD("CHS: %x %x %x\n", cylinder, head, sector);
6075 outLBA(cylinder,hd_heads,head,hd_sectors,sector,drive);
6077 else {
6078 outb(0x01f3, sector);
6079 outb(0x01f4, cylinder & 0x00ff);
6080 outb(0x01f5, cylinder >> 8);
6081 outb(0x01f6, 0xa0 | ((drive & 0x01)<<4) | (head & 0x0f));
6083 outb(0x01f7, 0x20);
6085 while (1) {
6086 status = inb(0x1f7);
6087 if ( !(status & 0x80) ) break;
6090 if (status & 0x01) {
6091 BX_PANIC("hard drive BIOS:(read/verify) read error\n");
6092 } else if ( !(status & 0x08) ) {
6093 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
6094 BX_PANIC("hard drive BIOS:(read/verify) expected DRQ=1\n");
6097 sector_count = 0;
6098 tempbx = BX;
6100 ASM_START
6101 sti ;; enable higher priority interrupts
6102 ASM_END
6104 while (1) {
6105 ASM_START
6106 ;; store temp bx in real DI register
6107 push bp
6108 mov bp, sp
6109 mov di, _int13_harddisk.tempbx + 2 [bp]
6110 pop bp
6112 ;; adjust if there will be an overrun
6113 cmp di, #0xfe00
6114 jbe i13_f02_no_adjust
6115 i13_f02_adjust:
6116 sub di, #0x0200 ; sub 512 bytes from offset
6117 mov ax, es
6118 add ax, #0x0020 ; add 512 to segment
6119 mov es, ax
6121 i13_f02_no_adjust:
6122 mov cx, #0x0100 ;; counter (256 words = 512b)
6123 mov dx, #0x01f0 ;; AT data read port
6126 insw ;; CX words transfered from port(DX) to ES:[DI]
6128 i13_f02_done:
6129 ;; store real DI register back to temp bx
6130 push bp
6131 mov bp, sp
6132 mov _int13_harddisk.tempbx + 2 [bp], di
6133 pop bp
6134 ASM_END
6136 sector_count++;
6137 num_sectors--;
6138 if (num_sectors == 0) {
6139 status = inb(0x1f7);
6140 if ( (status & 0xc9) != 0x40 )
6141 BX_PANIC("no sectors left to read/verify, status is %02x\n", (unsigned) status);
6142 break;
6144 else {
6145 status = inb(0x1f7);
6146 if ( (status & 0xc9) != 0x48 )
6147 BX_PANIC("more sectors left to read/verify, status is %02x\n", (unsigned) status);
6148 continue;
6152 SET_AH(0);
6153 SET_DISK_RET_STATUS(0);
6154 SET_AL(sector_count);
6155 CLEAR_CF(); /* successful */
6156 return;
6157 break;
6160 case 0x03: /* write disk sectors */
6161 BX_DEBUG_INT13_HD("int13_f03\n");
6162 drive = GET_ELDL ();
6163 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6165 num_sectors = GET_AL();
6166 cylinder = GET_CH();
6167 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
6168 sector = (GET_CL() & 0x3f);
6169 head = GET_DH();
6171 if (hd_cylinders > 1024) {
6172 if (hd_cylinders <= 2048) {
6173 cylinder <<= 1;
6175 else if (hd_cylinders <= 4096) {
6176 cylinder <<= 2;
6178 else if (hd_cylinders <= 8192) {
6179 cylinder <<= 3;
6181 else { // hd_cylinders <= 16384
6182 cylinder <<= 4;
6185 ax = head / hd_heads;
6186 cyl_mod = ax & 0xff;
6187 head = ax >> 8;
6188 cylinder |= cyl_mod;
6191 if ( (cylinder >= hd_cylinders) ||
6192 (sector > hd_sectors) ||
6193 (head >= hd_heads) ) {
6194 SET_AH( 1);
6195 SET_DISK_RET_STATUS(1);
6196 SET_CF(); /* error occurred */
6197 return;
6200 if ( (num_sectors > 128) || (num_sectors == 0) )
6201 BX_PANIC("int13_harddisk: num_sectors out of range!\n");
6203 if (head > 15)
6204 BX_PANIC("hard drive BIOS:(read) head > 15\n");
6206 status = inb(0x1f7);
6207 if (status & 0x80) {
6208 BX_PANIC("hard drive BIOS:(read) BUSY bit set\n");
6210 // should check for Drive Ready Bit also in status reg
6211 outb(0x01f2, num_sectors);
6213 /* activate LBA? (tomv) */
6214 if (hd_heads > 16) {
6215 BX_DEBUG_INT13_HD("CHS (write): %x %x %x\n", cylinder, head, sector);
6216 outLBA(cylinder,hd_heads,head,hd_sectors,sector,GET_ELDL());
6218 else {
6219 outb(0x01f3, sector);
6220 outb(0x01f4, cylinder & 0x00ff);
6221 outb(0x01f5, cylinder >> 8);
6222 outb(0x01f6, 0xa0 | ((GET_ELDL() & 0x01)<<4) | (head & 0x0f));
6224 outb(0x01f7, 0x30);
6226 // wait for busy bit to turn off after seeking
6227 while (1) {
6228 status = inb(0x1f7);
6229 if ( !(status & 0x80) ) break;
6232 if ( !(status & 0x08) ) {
6233 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
6234 BX_PANIC("hard drive BIOS:(write) data-request bit not set\n");
6237 sector_count = 0;
6238 tempbx = BX;
6240 ASM_START
6241 sti ;; enable higher priority interrupts
6242 ASM_END
6244 while (1) {
6245 ASM_START
6246 ;; store temp bx in real SI register
6247 push bp
6248 mov bp, sp
6249 mov si, _int13_harddisk.tempbx + 2 [bp]
6250 pop bp
6252 ;; adjust if there will be an overrun
6253 cmp si, #0xfe00
6254 jbe i13_f03_no_adjust
6255 i13_f03_adjust:
6256 sub si, #0x0200 ; sub 512 bytes from offset
6257 mov ax, es
6258 add ax, #0x0020 ; add 512 to segment
6259 mov es, ax
6261 i13_f03_no_adjust:
6262 mov cx, #0x0100 ;; counter (256 words = 512b)
6263 mov dx, #0x01f0 ;; AT data read port
6265 seg ES
6267 outsw ;; CX words tranfered from ES:[SI] to port(DX)
6269 ;; store real SI register back to temp bx
6270 push bp
6271 mov bp, sp
6272 mov _int13_harddisk.tempbx + 2 [bp], si
6273 pop bp
6274 ASM_END
6276 sector_count++;
6277 num_sectors--;
6278 if (num_sectors == 0) {
6279 status = inb(0x1f7);
6280 if ( (status & 0xe9) != 0x40 )
6281 BX_PANIC("no sectors left to write, status is %02x\n", (unsigned) status);
6282 break;
6284 else {
6285 status = inb(0x1f7);
6286 if ( (status & 0xc9) != 0x48 )
6287 BX_PANIC("more sectors left to write, status is %02x\n", (unsigned) status);
6288 continue;
6292 SET_AH(0);
6293 SET_DISK_RET_STATUS(0);
6294 SET_AL(sector_count);
6295 CLEAR_CF(); /* successful */
6296 return;
6297 break;
6299 case 0x05: /* format disk track */
6300 BX_DEBUG_INT13_HD("int13_f05\n");
6301 BX_PANIC("format disk track called\n");
6302 /* nop */
6303 SET_AH(0);
6304 SET_DISK_RET_STATUS(0);
6305 CLEAR_CF(); /* successful */
6306 return;
6307 break;
6309 case 0x08: /* read disk drive parameters */
6310 BX_DEBUG_INT13_HD("int13_f08\n");
6312 drive = GET_ELDL ();
6313 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6315 // translate CHS
6317 if (hd_cylinders <= 1024) {
6318 // hd_cylinders >>= 0;
6319 // hd_heads <<= 0;
6321 else if (hd_cylinders <= 2048) {
6322 hd_cylinders >>= 1;
6323 hd_heads <<= 1;
6325 else if (hd_cylinders <= 4096) {
6326 hd_cylinders >>= 2;
6327 hd_heads <<= 2;
6329 else if (hd_cylinders <= 8192) {
6330 hd_cylinders >>= 3;
6331 hd_heads <<= 3;
6333 else { // hd_cylinders <= 16384
6334 hd_cylinders >>= 4;
6335 hd_heads <<= 4;
6338 max_cylinder = hd_cylinders - 2; /* 0 based */
6339 SET_AL(0);
6340 SET_CH(max_cylinder & 0xff);
6341 SET_CL(((max_cylinder >> 2) & 0xc0) | (hd_sectors & 0x3f));
6342 SET_DH(hd_heads - 1);
6343 SET_DL(n_drives); /* returns 0, 1, or 2 hard drives */
6344 SET_AH(0);
6345 SET_DISK_RET_STATUS(0);
6346 CLEAR_CF(); /* successful */
6348 return;
6349 break;
6351 case 0x09: /* initialize drive parameters */
6352 BX_DEBUG_INT13_HD("int13_f09\n");
6353 SET_AH(0);
6354 SET_DISK_RET_STATUS(0);
6355 CLEAR_CF(); /* successful */
6356 return;
6357 break;
6359 case 0x0a: /* read disk sectors with ECC */
6360 BX_DEBUG_INT13_HD("int13_f0a\n");
6361 case 0x0b: /* write disk sectors with ECC */
6362 BX_DEBUG_INT13_HD("int13_f0b\n");
6363 BX_PANIC("int13h Functions 0Ah & 0Bh not implemented!\n");
6364 return;
6365 break;
6367 case 0x0c: /* seek to specified cylinder */
6368 BX_DEBUG_INT13_HD("int13_f0c\n");
6369 BX_INFO("int13h function 0ch (seek) not implemented!\n");
6370 SET_AH(0);
6371 SET_DISK_RET_STATUS(0);
6372 CLEAR_CF(); /* successful */
6373 return;
6374 break;
6376 case 0x0d: /* alternate disk reset */
6377 BX_DEBUG_INT13_HD("int13_f0d\n");
6378 SET_AH(0);
6379 SET_DISK_RET_STATUS(0);
6380 CLEAR_CF(); /* successful */
6381 return;
6382 break;
6384 case 0x10: /* check drive ready */
6385 BX_DEBUG_INT13_HD("int13_f10\n");
6386 //SET_AH(0);
6387 //SET_DISK_RET_STATUS(0);
6388 //CLEAR_CF(); /* successful */
6389 //return;
6390 //break;
6392 // should look at 40:8E also???
6393 status = inb(0x01f7);
6394 if ( (status & 0xc0) == 0x40 ) {
6395 SET_AH(0);
6396 SET_DISK_RET_STATUS(0);
6397 CLEAR_CF(); // drive ready
6398 return;
6400 else {
6401 SET_AH(0xAA);
6402 SET_DISK_RET_STATUS(0xAA);
6403 SET_CF(); // not ready
6404 return;
6406 break;
6408 case 0x11: /* recalibrate */
6409 BX_DEBUG_INT13_HD("int13_f11\n");
6410 SET_AH(0);
6411 SET_DISK_RET_STATUS(0);
6412 CLEAR_CF(); /* successful */
6413 return;
6414 break;
6416 case 0x14: /* controller internal diagnostic */
6417 BX_DEBUG_INT13_HD("int13_f14\n");
6418 SET_AH(0);
6419 SET_DISK_RET_STATUS(0);
6420 CLEAR_CF(); /* successful */
6421 SET_AL(0);
6422 return;
6423 break;
6425 case 0x15: /* read disk drive size */
6426 drive = GET_ELDL();
6427 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6428 ASM_START
6429 push bp
6430 mov bp, sp
6431 mov al, _int13_harddisk.hd_heads + 2 [bp]
6432 mov ah, _int13_harddisk.hd_sectors + 2 [bp]
6433 mul al, ah ;; ax = heads * sectors
6434 mov bx, _int13_harddisk.hd_cylinders + 2 [bp]
6435 dec bx ;; use (cylinders - 1) ???
6436 mul ax, bx ;; dx:ax = (cylinders -1) * (heads * sectors)
6437 ;; now we need to move the 32bit result dx:ax to what the
6438 ;; BIOS wants which is cx:dx.
6439 ;; and then into CX:DX on the stack
6440 mov _int13_harddisk.CX + 2 [bp], dx
6441 mov _int13_harddisk.DX + 2 [bp], ax
6442 pop bp
6443 ASM_END
6444 SET_AH(3); // hard disk accessible
6445 SET_DISK_RET_STATUS(0); // ??? should this be 0
6446 CLEAR_CF(); // successful
6447 return;
6448 break;
6450 case 0x18: // set media type for format
6451 case 0x41: // IBM/MS
6452 case 0x42: // IBM/MS
6453 case 0x43: // IBM/MS
6454 case 0x44: // IBM/MS
6455 case 0x45: // IBM/MS lock/unlock drive
6456 case 0x46: // IBM/MS eject media
6457 case 0x47: // IBM/MS extended seek
6458 case 0x49: // IBM/MS extended media change
6459 case 0x50: // IBM/MS send packet command
6460 default:
6461 BX_INFO("int13_harddisk: unsupported AH=%02x\n", GET_AH());
6463 SET_AH(1); // code=invalid function in AH or invalid parameter
6464 SET_DISK_RET_STATUS(1);
6465 SET_CF(); /* unsuccessful */
6466 return;
6467 break;
6471 static char panic_msg_reg12h[] = "HD%d cmos reg 12h not type F\n";
6472 static char panic_msg_reg19h[] = "HD%d cmos reg %02xh not user definable type 47\n";
6474 void
6475 get_hd_geometry(drive, hd_cylinders, hd_heads, hd_sectors)
6476 Bit8u drive;
6477 Bit16u *hd_cylinders;
6478 Bit8u *hd_heads;
6479 Bit8u *hd_sectors;
6481 Bit8u hd_type;
6482 Bit16u ss;
6483 Bit16u cylinders;
6484 Bit8u iobase;
6486 ss = get_SS();
6487 if (drive == 0x80) {
6488 hd_type = inb_cmos(0x12) & 0xf0;
6489 if (hd_type != 0xf0)
6490 BX_INFO(panic_msg_reg12h,0);
6491 hd_type = inb_cmos(0x19); // HD0: extended type
6492 if (hd_type != 47)
6493 BX_INFO(panic_msg_reg19h,0,0x19);
6494 iobase = 0x1b;
6495 } else {
6496 hd_type = inb_cmos(0x12) & 0x0f;
6497 if (hd_type != 0x0f)
6498 BX_INFO(panic_msg_reg12h,1);
6499 hd_type = inb_cmos(0x1a); // HD0: extended type
6500 if (hd_type != 47)
6501 BX_INFO(panic_msg_reg19h,0,0x1a);
6502 iobase = 0x24;
6505 // cylinders
6506 cylinders = inb_cmos(iobase) | (inb_cmos(iobase+1) << 8);
6507 write_word(ss, hd_cylinders, cylinders);
6509 // heads
6510 write_byte(ss, hd_heads, inb_cmos(iobase+2));
6512 // sectors per track
6513 write_byte(ss, hd_sectors, inb_cmos(iobase+8));
6516 #endif //else BX_USE_ATADRV
6519 //////////////////////
6520 // FLOPPY functions //
6521 //////////////////////
6523 void floppy_reset_controller()
6525 Bit8u val8;
6527 // Reset controller
6528 val8 = inb(0x03f2);
6529 outb(0x03f2, val8 & ~0x04);
6530 outb(0x03f2, val8 | 0x04);
6532 // Wait for controller to come out of reset
6533 do {
6534 val8 = inb(0x3f4);
6535 } while ( (val8 & 0xc0) != 0x80 );
6538 void floppy_prepare_controller(drive)
6539 Bit16u drive;
6541 Bit8u val8, dor, prev_reset;
6543 // set 40:3e bit 7 to 0
6544 val8 = read_byte(0x0040, 0x003e);
6545 val8 &= 0x7f;
6546 write_byte(0x0040, 0x003e, val8);
6548 // turn on motor of selected drive, DMA & int enabled, normal operation
6549 prev_reset = inb(0x03f2) & 0x04;
6550 if (drive)
6551 dor = 0x20;
6552 else
6553 dor = 0x10;
6554 dor |= 0x0c;
6555 dor |= drive;
6556 outb(0x03f2, dor);
6558 // reset the disk motor timeout value of INT 08
6559 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
6561 // wait for drive readiness
6562 do {
6563 val8 = inb(0x3f4);
6564 } while ( (val8 & 0xc0) != 0x80 );
6566 if (prev_reset == 0) {
6567 // turn on interrupts
6568 ASM_START
6570 ASM_END
6571 // wait on 40:3e bit 7 to become 1
6572 do {
6573 val8 = read_byte(0x0040, 0x003e);
6574 } while ( (val8 & 0x80) == 0 );
6575 val8 &= 0x7f;
6576 ASM_START
6578 ASM_END
6579 write_byte(0x0040, 0x003e, val8);
6583 bx_bool
6584 floppy_media_known(drive)
6585 Bit16u drive;
6587 Bit8u val8;
6588 Bit16u media_state_offset;
6590 val8 = read_byte(0x0040, 0x003e); // diskette recal status
6591 if (drive)
6592 val8 >>= 1;
6593 val8 &= 0x01;
6594 if (val8 == 0)
6595 return(0);
6597 media_state_offset = 0x0090;
6598 if (drive)
6599 media_state_offset += 1;
6601 val8 = read_byte(0x0040, media_state_offset);
6602 val8 = (val8 >> 4) & 0x01;
6603 if (val8 == 0)
6604 return(0);
6606 // check pass, return KNOWN
6607 return(1);
6610 bx_bool
6611 floppy_media_sense(drive)
6612 Bit16u drive;
6614 bx_bool retval;
6615 Bit16u media_state_offset;
6616 Bit8u drive_type, config_data, media_state;
6618 if (floppy_drive_recal(drive) == 0) {
6619 return(0);
6622 // for now cheat and get drive type from CMOS,
6623 // assume media is same as drive type
6625 // ** config_data **
6626 // Bitfields for diskette media control:
6627 // Bit(s) Description (Table M0028)
6628 // 7-6 last data rate set by controller
6629 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6630 // 5-4 last diskette drive step rate selected
6631 // 00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
6632 // 3-2 {data rate at start of operation}
6633 // 1-0 reserved
6635 // ** media_state **
6636 // Bitfields for diskette drive media state:
6637 // Bit(s) Description (Table M0030)
6638 // 7-6 data rate
6639 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6640 // 5 double stepping required (e.g. 360kB in 1.2MB)
6641 // 4 media type established
6642 // 3 drive capable of supporting 4MB media
6643 // 2-0 on exit from BIOS, contains
6644 // 000 trying 360kB in 360kB
6645 // 001 trying 360kB in 1.2MB
6646 // 010 trying 1.2MB in 1.2MB
6647 // 011 360kB in 360kB established
6648 // 100 360kB in 1.2MB established
6649 // 101 1.2MB in 1.2MB established
6650 // 110 reserved
6651 // 111 all other formats/drives
6653 drive_type = inb_cmos(0x10);
6654 if (drive == 0)
6655 drive_type >>= 4;
6656 else
6657 drive_type &= 0x0f;
6658 if ( drive_type == 1 ) {
6659 // 360K 5.25" drive
6660 config_data = 0x00; // 0000 0000
6661 media_state = 0x25; // 0010 0101
6662 retval = 1;
6664 else if ( drive_type == 2 ) {
6665 // 1.2 MB 5.25" drive
6666 config_data = 0x00; // 0000 0000
6667 media_state = 0x25; // 0010 0101 // need double stepping??? (bit 5)
6668 retval = 1;
6670 else if ( drive_type == 3 ) {
6671 // 720K 3.5" drive
6672 config_data = 0x00; // 0000 0000 ???
6673 media_state = 0x17; // 0001 0111
6674 retval = 1;
6676 else if ( drive_type == 4 ) {
6677 // 1.44 MB 3.5" drive
6678 config_data = 0x00; // 0000 0000
6679 media_state = 0x17; // 0001 0111
6680 retval = 1;
6682 else if ( drive_type == 5 ) {
6683 // 2.88 MB 3.5" drive
6684 config_data = 0xCC; // 1100 1100
6685 media_state = 0xD7; // 1101 0111
6686 retval = 1;
6689 // Extended floppy size uses special cmos setting
6690 else if ( drive_type == 6 ) {
6691 // 160k 5.25" drive
6692 config_data = 0x00; // 0000 0000
6693 media_state = 0x27; // 0010 0111
6694 retval = 1;
6696 else if ( drive_type == 7 ) {
6697 // 180k 5.25" drive
6698 config_data = 0x00; // 0000 0000
6699 media_state = 0x27; // 0010 0111
6700 retval = 1;
6702 else if ( drive_type == 8 ) {
6703 // 320k 5.25" drive
6704 config_data = 0x00; // 0000 0000
6705 media_state = 0x27; // 0010 0111
6706 retval = 1;
6709 else {
6710 // not recognized
6711 config_data = 0x00; // 0000 0000
6712 media_state = 0x00; // 0000 0000
6713 retval = 0;
6716 if (drive == 0)
6717 media_state_offset = 0x90;
6718 else
6719 media_state_offset = 0x91;
6720 write_byte(0x0040, 0x008B, config_data);
6721 write_byte(0x0040, media_state_offset, media_state);
6723 return(retval);
6726 bx_bool
6727 floppy_drive_recal(drive)
6728 Bit16u drive;
6730 Bit8u val8;
6731 Bit16u curr_cyl_offset;
6733 floppy_prepare_controller(drive);
6735 // send Recalibrate command (2 bytes) to controller
6736 outb(0x03f5, 0x07); // 07: Recalibrate
6737 outb(0x03f5, drive); // 0=drive0, 1=drive1
6739 // turn on interrupts
6740 ASM_START
6742 ASM_END
6744 // wait on 40:3e bit 7 to become 1
6745 do {
6746 val8 = (read_byte(0x0040, 0x003e) & 0x80);
6747 } while ( val8 == 0 );
6749 val8 = 0; // separate asm from while() loop
6750 // turn off interrupts
6751 ASM_START
6753 ASM_END
6755 // set 40:3e bit 7 to 0, and calibrated bit
6756 val8 = read_byte(0x0040, 0x003e);
6757 val8 &= 0x7f;
6758 if (drive) {
6759 val8 |= 0x02; // Drive 1 calibrated
6760 curr_cyl_offset = 0x0095;
6761 } else {
6762 val8 |= 0x01; // Drive 0 calibrated
6763 curr_cyl_offset = 0x0094;
6765 write_byte(0x0040, 0x003e, val8);
6766 write_byte(0x0040, curr_cyl_offset, 0); // current cylinder is 0
6768 return(1);
6773 bx_bool
6774 floppy_drive_exists(drive)
6775 Bit16u drive;
6777 Bit8u drive_type;
6779 // check CMOS to see if drive exists
6780 drive_type = inb_cmos(0x10);
6781 if (drive == 0)
6782 drive_type >>= 4;
6783 else
6784 drive_type &= 0x0f;
6785 if ( drive_type == 0 )
6786 return(0);
6787 else
6788 return(1);
6791 #if BX_SUPPORT_FLOPPY
6792 void
6793 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
6794 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
6796 Bit8u drive, num_sectors, track, sector, head, status;
6797 Bit16u base_address, base_count, base_es;
6798 Bit8u page, mode_register, val8, dor;
6799 Bit8u return_status[7];
6800 Bit8u drive_type, num_floppies, ah;
6801 Bit16u es, last_addr;
6803 BX_DEBUG_INT13_FL("int13_diskette: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
6805 ah = GET_AH();
6807 switch ( ah ) {
6808 case 0x00: // diskette controller reset
6809 BX_DEBUG_INT13_FL("floppy f00\n");
6810 drive = GET_ELDL();
6811 if (drive > 1) {
6812 SET_AH(1); // invalid param
6813 set_diskette_ret_status(1);
6814 SET_CF();
6815 return;
6817 drive_type = inb_cmos(0x10);
6819 if (drive == 0)
6820 drive_type >>= 4;
6821 else
6822 drive_type &= 0x0f;
6823 if (drive_type == 0) {
6824 SET_AH(0x80); // drive not responding
6825 set_diskette_ret_status(0x80);
6826 SET_CF();
6827 return;
6829 SET_AH(0);
6830 set_diskette_ret_status(0);
6831 CLEAR_CF(); // successful
6832 set_diskette_current_cyl(drive, 0); // current cylinder
6833 return;
6835 case 0x01: // Read Diskette Status
6836 CLEAR_CF();
6837 val8 = read_byte(0x0000, 0x0441);
6838 SET_AH(val8);
6839 if (val8) {
6840 SET_CF();
6842 return;
6844 case 0x02: // Read Diskette Sectors
6845 case 0x03: // Write Diskette Sectors
6846 case 0x04: // Verify Diskette Sectors
6847 num_sectors = GET_AL();
6848 track = GET_CH();
6849 sector = GET_CL();
6850 head = GET_DH();
6851 drive = GET_ELDL();
6853 if ((drive > 1) || (head > 1) || (sector == 0) ||
6854 (num_sectors == 0) || (num_sectors > 72)) {
6855 BX_INFO("int13_diskette: read/write/verify: parameter out of range\n");
6856 SET_AH(1);
6857 set_diskette_ret_status(1);
6858 SET_AL(0); // no sectors read
6859 SET_CF(); // error occurred
6860 return;
6863 // see if drive exists
6864 if (floppy_drive_exists(drive) == 0) {
6865 SET_AH(0x80); // not responding
6866 set_diskette_ret_status(0x80);
6867 SET_AL(0); // no sectors read
6868 SET_CF(); // error occurred
6869 return;
6872 // see if media in drive, and type is known
6873 if (floppy_media_known(drive) == 0) {
6874 if (floppy_media_sense(drive) == 0) {
6875 SET_AH(0x0C); // Media type not found
6876 set_diskette_ret_status(0x0C);
6877 SET_AL(0); // no sectors read
6878 SET_CF(); // error occurred
6879 return;
6883 if (ah == 0x02) {
6884 // Read Diskette Sectors
6886 //-----------------------------------
6887 // set up DMA controller for transfer
6888 //-----------------------------------
6890 // es:bx = pointer to where to place information from diskette
6891 // port 04: DMA-1 base and current address, channel 2
6892 // port 05: DMA-1 base and current count, channel 2
6893 page = (ES >> 12); // upper 4 bits
6894 base_es = (ES << 4); // lower 16bits contributed by ES
6895 base_address = base_es + BX; // lower 16 bits of address
6896 // contributed by ES:BX
6897 if ( base_address < base_es ) {
6898 // in case of carry, adjust page by 1
6899 page++;
6901 base_count = (num_sectors * 512) - 1;
6903 // check for 64K boundary overrun
6904 last_addr = base_address + base_count;
6905 if (last_addr < base_address) {
6906 SET_AH(0x09);
6907 set_diskette_ret_status(0x09);
6908 SET_AL(0); // no sectors read
6909 SET_CF(); // error occurred
6910 return;
6913 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
6914 outb(0x000a, 0x06);
6916 BX_DEBUG_INT13_FL("clear flip-flop\n");
6917 outb(0x000c, 0x00); // clear flip-flop
6918 outb(0x0004, base_address);
6919 outb(0x0004, base_address>>8);
6920 BX_DEBUG_INT13_FL("clear flip-flop\n");
6921 outb(0x000c, 0x00); // clear flip-flop
6922 outb(0x0005, base_count);
6923 outb(0x0005, base_count>>8);
6925 // port 0b: DMA-1 Mode Register
6926 mode_register = 0x46; // single mode, increment, autoinit disable,
6927 // transfer type=write, channel 2
6928 BX_DEBUG_INT13_FL("setting mode register\n");
6929 outb(0x000b, mode_register);
6931 BX_DEBUG_INT13_FL("setting page register\n");
6932 // port 81: DMA-1 Page Register, channel 2
6933 outb(0x0081, page);
6935 BX_DEBUG_INT13_FL("unmask chan 2\n");
6936 outb(0x000a, 0x02); // unmask channel 2
6938 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
6939 outb(0x000a, 0x02);
6941 //--------------------------------------
6942 // set up floppy controller for transfer
6943 //--------------------------------------
6944 floppy_prepare_controller(drive);
6946 // send read-normal-data command (9 bytes) to controller
6947 outb(0x03f5, 0xe6); // e6: read normal data
6948 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
6949 outb(0x03f5, track);
6950 outb(0x03f5, head);
6951 outb(0x03f5, sector);
6952 outb(0x03f5, 2); // 512 byte sector size
6953 outb(0x03f5, sector + num_sectors - 1); // last sector to read on track
6954 outb(0x03f5, 0); // Gap length
6955 outb(0x03f5, 0xff); // Gap length
6957 // turn on interrupts
6958 ASM_START
6960 ASM_END
6962 // wait on 40:3e bit 7 to become 1
6963 do {
6964 val8 = read_byte(0x0040, 0x0040);
6965 if (val8 == 0) {
6966 floppy_reset_controller();
6967 SET_AH(0x80); // drive not ready (timeout)
6968 set_diskette_ret_status(0x80);
6969 SET_AL(0); // no sectors read
6970 SET_CF(); // error occurred
6971 return;
6973 val8 = (read_byte(0x0040, 0x003e) & 0x80);
6974 } while ( val8 == 0 );
6976 val8 = 0; // separate asm from while() loop
6977 // turn off interrupts
6978 ASM_START
6980 ASM_END
6982 // set 40:3e bit 7 to 0
6983 val8 = read_byte(0x0040, 0x003e);
6984 val8 &= 0x7f;
6985 write_byte(0x0040, 0x003e, val8);
6987 // check port 3f4 for accessibility to status bytes
6988 val8 = inb(0x3f4);
6989 if ( (val8 & 0xc0) != 0xc0 )
6990 BX_PANIC("int13_diskette: ctrl not ready\n");
6992 // read 7 return status bytes from controller
6993 // using loop index broken, have to unroll...
6994 return_status[0] = inb(0x3f5);
6995 return_status[1] = inb(0x3f5);
6996 return_status[2] = inb(0x3f5);
6997 return_status[3] = inb(0x3f5);
6998 return_status[4] = inb(0x3f5);
6999 return_status[5] = inb(0x3f5);
7000 return_status[6] = inb(0x3f5);
7001 // record in BIOS Data Area
7002 write_byte(0x0040, 0x0042, return_status[0]);
7003 write_byte(0x0040, 0x0043, return_status[1]);
7004 write_byte(0x0040, 0x0044, return_status[2]);
7005 write_byte(0x0040, 0x0045, return_status[3]);
7006 write_byte(0x0040, 0x0046, return_status[4]);
7007 write_byte(0x0040, 0x0047, return_status[5]);
7008 write_byte(0x0040, 0x0048, return_status[6]);
7010 if ( (return_status[0] & 0xc0) != 0 ) {
7011 SET_AH(0x20);
7012 set_diskette_ret_status(0x20);
7013 SET_AL(0); // no sectors read
7014 SET_CF(); // error occurred
7015 return;
7018 // ??? should track be new val from return_status[3] ?
7019 set_diskette_current_cyl(drive, track);
7020 // AL = number of sectors read (same value as passed)
7021 SET_AH(0x00); // success
7022 CLEAR_CF(); // success
7023 return;
7024 } else if (ah == 0x03) {
7025 // Write Diskette Sectors
7027 //-----------------------------------
7028 // set up DMA controller for transfer
7029 //-----------------------------------
7031 // es:bx = pointer to where to place information from diskette
7032 // port 04: DMA-1 base and current address, channel 2
7033 // port 05: DMA-1 base and current count, channel 2
7034 page = (ES >> 12); // upper 4 bits
7035 base_es = (ES << 4); // lower 16bits contributed by ES
7036 base_address = base_es + BX; // lower 16 bits of address
7037 // contributed by ES:BX
7038 if ( base_address < base_es ) {
7039 // in case of carry, adjust page by 1
7040 page++;
7042 base_count = (num_sectors * 512) - 1;
7044 // check for 64K boundary overrun
7045 last_addr = base_address + base_count;
7046 if (last_addr < base_address) {
7047 SET_AH(0x09);
7048 set_diskette_ret_status(0x09);
7049 SET_AL(0); // no sectors read
7050 SET_CF(); // error occurred
7051 return;
7054 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
7055 outb(0x000a, 0x06);
7057 outb(0x000c, 0x00); // clear flip-flop
7058 outb(0x0004, base_address);
7059 outb(0x0004, base_address>>8);
7060 outb(0x000c, 0x00); // clear flip-flop
7061 outb(0x0005, base_count);
7062 outb(0x0005, base_count>>8);
7064 // port 0b: DMA-1 Mode Register
7065 mode_register = 0x4a; // single mode, increment, autoinit disable,
7066 // transfer type=read, channel 2
7067 outb(0x000b, mode_register);
7069 // port 81: DMA-1 Page Register, channel 2
7070 outb(0x0081, page);
7072 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
7073 outb(0x000a, 0x02);
7075 //--------------------------------------
7076 // set up floppy controller for transfer
7077 //--------------------------------------
7078 floppy_prepare_controller(drive);
7080 // send write-normal-data command (9 bytes) to controller
7081 outb(0x03f5, 0xc5); // c5: write normal data
7082 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7083 outb(0x03f5, track);
7084 outb(0x03f5, head);
7085 outb(0x03f5, sector);
7086 outb(0x03f5, 2); // 512 byte sector size
7087 outb(0x03f5, sector + num_sectors - 1); // last sector to write on track
7088 outb(0x03f5, 0); // Gap length
7089 outb(0x03f5, 0xff); // Gap length
7091 // turn on interrupts
7092 ASM_START
7094 ASM_END
7096 // wait on 40:3e bit 7 to become 1
7097 do {
7098 val8 = read_byte(0x0040, 0x0040);
7099 if (val8 == 0) {
7100 floppy_reset_controller();
7101 SET_AH(0x80); // drive not ready (timeout)
7102 set_diskette_ret_status(0x80);
7103 SET_AL(0); // no sectors written
7104 SET_CF(); // error occurred
7105 return;
7107 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7108 } while ( val8 == 0 );
7110 val8 = 0; // separate asm from while() loop
7111 // turn off interrupts
7112 ASM_START
7114 ASM_END
7116 // set 40:3e bit 7 to 0
7117 val8 = read_byte(0x0040, 0x003e);
7118 val8 &= 0x7f;
7119 write_byte(0x0040, 0x003e, val8);
7121 // check port 3f4 for accessibility to status bytes
7122 val8 = inb(0x3f4);
7123 if ( (val8 & 0xc0) != 0xc0 )
7124 BX_PANIC("int13_diskette: ctrl not ready\n");
7126 // read 7 return status bytes from controller
7127 // using loop index broken, have to unroll...
7128 return_status[0] = inb(0x3f5);
7129 return_status[1] = inb(0x3f5);
7130 return_status[2] = inb(0x3f5);
7131 return_status[3] = inb(0x3f5);
7132 return_status[4] = inb(0x3f5);
7133 return_status[5] = inb(0x3f5);
7134 return_status[6] = inb(0x3f5);
7135 // record in BIOS Data Area
7136 write_byte(0x0040, 0x0042, return_status[0]);
7137 write_byte(0x0040, 0x0043, return_status[1]);
7138 write_byte(0x0040, 0x0044, return_status[2]);
7139 write_byte(0x0040, 0x0045, return_status[3]);
7140 write_byte(0x0040, 0x0046, return_status[4]);
7141 write_byte(0x0040, 0x0047, return_status[5]);
7142 write_byte(0x0040, 0x0048, return_status[6]);
7144 if ( (return_status[0] & 0xc0) != 0 ) {
7145 if ( (return_status[1] & 0x02) != 0 ) {
7146 // diskette not writable.
7147 // AH=status code=0x03 (tried to write on write-protected disk)
7148 // AL=number of sectors written=0
7149 AX = 0x0300;
7150 SET_CF();
7151 return;
7152 } else {
7153 BX_PANIC("int13_diskette_function: read error\n");
7157 // ??? should track be new val from return_status[3] ?
7158 set_diskette_current_cyl(drive, track);
7159 // AL = number of sectors read (same value as passed)
7160 SET_AH(0x00); // success
7161 CLEAR_CF(); // success
7162 return;
7163 } else { // if (ah == 0x04)
7164 // Verify Diskette Sectors
7166 // ??? should track be new val from return_status[3] ?
7167 set_diskette_current_cyl(drive, track);
7168 // AL = number of sectors verified (same value as passed)
7169 CLEAR_CF(); // success
7170 SET_AH(0x00); // success
7171 return;
7173 break;
7175 case 0x05: // format diskette track
7176 BX_DEBUG_INT13_FL("floppy f05\n");
7178 num_sectors = GET_AL();
7179 track = GET_CH();
7180 head = GET_DH();
7181 drive = GET_ELDL();
7183 if ((drive > 1) || (head > 1) || (track > 79) ||
7184 (num_sectors == 0) || (num_sectors > 18)) {
7185 SET_AH(1);
7186 set_diskette_ret_status(1);
7187 SET_CF(); // error occurred
7190 // see if drive exists
7191 if (floppy_drive_exists(drive) == 0) {
7192 SET_AH(0x80); // drive not responding
7193 set_diskette_ret_status(0x80);
7194 SET_CF(); // error occurred
7195 return;
7198 // see if media in drive, and type is known
7199 if (floppy_media_known(drive) == 0) {
7200 if (floppy_media_sense(drive) == 0) {
7201 SET_AH(0x0C); // Media type not found
7202 set_diskette_ret_status(0x0C);
7203 SET_AL(0); // no sectors read
7204 SET_CF(); // error occurred
7205 return;
7209 // set up DMA controller for transfer
7210 page = (ES >> 12); // upper 4 bits
7211 base_es = (ES << 4); // lower 16bits contributed by ES
7212 base_address = base_es + BX; // lower 16 bits of address
7213 // contributed by ES:BX
7214 if ( base_address < base_es ) {
7215 // in case of carry, adjust page by 1
7216 page++;
7218 base_count = (num_sectors * 4) - 1;
7220 // check for 64K boundary overrun
7221 last_addr = base_address + base_count;
7222 if (last_addr < base_address) {
7223 SET_AH(0x09);
7224 set_diskette_ret_status(0x09);
7225 SET_AL(0); // no sectors read
7226 SET_CF(); // error occurred
7227 return;
7230 outb(0x000a, 0x06);
7231 outb(0x000c, 0x00); // clear flip-flop
7232 outb(0x0004, base_address);
7233 outb(0x0004, base_address>>8);
7234 outb(0x000c, 0x00); // clear flip-flop
7235 outb(0x0005, base_count);
7236 outb(0x0005, base_count>>8);
7237 mode_register = 0x4a; // single mode, increment, autoinit disable,
7238 // transfer type=read, channel 2
7239 outb(0x000b, mode_register);
7240 // port 81: DMA-1 Page Register, channel 2
7241 outb(0x0081, page);
7242 outb(0x000a, 0x02);
7244 // set up floppy controller for transfer
7245 floppy_prepare_controller(drive);
7247 // send format-track command (6 bytes) to controller
7248 outb(0x03f5, 0x4d); // 4d: format track
7249 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7250 outb(0x03f5, 2); // 512 byte sector size
7251 outb(0x03f5, num_sectors); // number of sectors per track
7252 outb(0x03f5, 0); // Gap length
7253 outb(0x03f5, 0xf6); // Fill byte
7254 // turn on interrupts
7255 ASM_START
7257 ASM_END
7259 // wait on 40:3e bit 7 to become 1
7260 do {
7261 val8 = read_byte(0x0040, 0x0040);
7262 if (val8 == 0) {
7263 floppy_reset_controller();
7264 SET_AH(0x80); // drive not ready (timeout)
7265 set_diskette_ret_status(0x80);
7266 SET_CF(); // error occurred
7267 return;
7269 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7270 } while ( val8 == 0 );
7272 val8 = 0; // separate asm from while() loop
7273 // turn off interrupts
7274 ASM_START
7276 ASM_END
7277 // set 40:3e bit 7 to 0
7278 val8 = read_byte(0x0040, 0x003e);
7279 val8 &= 0x7f;
7280 write_byte(0x0040, 0x003e, val8);
7281 // check port 3f4 for accessibility to status bytes
7282 val8 = inb(0x3f4);
7283 if ( (val8 & 0xc0) != 0xc0 )
7284 BX_PANIC("int13_diskette: ctrl not ready\n");
7286 // read 7 return status bytes from controller
7287 // using loop index broken, have to unroll...
7288 return_status[0] = inb(0x3f5);
7289 return_status[1] = inb(0x3f5);
7290 return_status[2] = inb(0x3f5);
7291 return_status[3] = inb(0x3f5);
7292 return_status[4] = inb(0x3f5);
7293 return_status[5] = inb(0x3f5);
7294 return_status[6] = inb(0x3f5);
7295 // record in BIOS Data Area
7296 write_byte(0x0040, 0x0042, return_status[0]);
7297 write_byte(0x0040, 0x0043, return_status[1]);
7298 write_byte(0x0040, 0x0044, return_status[2]);
7299 write_byte(0x0040, 0x0045, return_status[3]);
7300 write_byte(0x0040, 0x0046, return_status[4]);
7301 write_byte(0x0040, 0x0047, return_status[5]);
7302 write_byte(0x0040, 0x0048, return_status[6]);
7304 if ( (return_status[0] & 0xc0) != 0 ) {
7305 if ( (return_status[1] & 0x02) != 0 ) {
7306 // diskette not writable.
7307 // AH=status code=0x03 (tried to write on write-protected disk)
7308 // AL=number of sectors written=0
7309 AX = 0x0300;
7310 SET_CF();
7311 return;
7312 } else {
7313 BX_PANIC("int13_diskette_function: write error\n");
7317 SET_AH(0);
7318 set_diskette_ret_status(0);
7319 set_diskette_current_cyl(drive, 0);
7320 CLEAR_CF(); // successful
7321 return;
7324 case 0x08: // read diskette drive parameters
7325 BX_DEBUG_INT13_FL("floppy f08\n");
7326 drive = GET_ELDL();
7328 if (drive > 1) {
7329 AX = 0;
7330 BX = 0;
7331 CX = 0;
7332 DX = 0;
7333 ES = 0;
7334 DI = 0;
7335 SET_DL(num_floppies);
7336 SET_CF();
7337 return;
7340 drive_type = inb_cmos(0x10);
7341 num_floppies = 0;
7342 if (drive_type & 0xf0)
7343 num_floppies++;
7344 if (drive_type & 0x0f)
7345 num_floppies++;
7347 if (drive == 0)
7348 drive_type >>= 4;
7349 else
7350 drive_type &= 0x0f;
7352 SET_BH(0);
7353 SET_BL(drive_type);
7354 SET_AH(0);
7355 SET_AL(0);
7356 SET_DL(num_floppies);
7358 switch (drive_type) {
7359 case 0: // none
7360 CX = 0;
7361 SET_DH(0); // max head #
7362 break;
7364 case 1: // 360KB, 5.25"
7365 CX = 0x2709; // 40 tracks, 9 sectors
7366 SET_DH(1); // max head #
7367 break;
7369 case 2: // 1.2MB, 5.25"
7370 CX = 0x4f0f; // 80 tracks, 15 sectors
7371 SET_DH(1); // max head #
7372 break;
7374 case 3: // 720KB, 3.5"
7375 CX = 0x4f09; // 80 tracks, 9 sectors
7376 SET_DH(1); // max head #
7377 break;
7379 case 4: // 1.44MB, 3.5"
7380 CX = 0x4f12; // 80 tracks, 18 sectors
7381 SET_DH(1); // max head #
7382 break;
7384 case 5: // 2.88MB, 3.5"
7385 CX = 0x4f24; // 80 tracks, 36 sectors
7386 SET_DH(1); // max head #
7387 break;
7389 case 6: // 160k, 5.25"
7390 CX = 0x2708; // 40 tracks, 8 sectors
7391 SET_DH(0); // max head #
7392 break;
7394 case 7: // 180k, 5.25"
7395 CX = 0x2709; // 40 tracks, 9 sectors
7396 SET_DH(0); // max head #
7397 break;
7399 case 8: // 320k, 5.25"
7400 CX = 0x2708; // 40 tracks, 8 sectors
7401 SET_DH(1); // max head #
7402 break;
7404 default: // ?
7405 BX_PANIC("floppy: int13: bad floppy type\n");
7408 /* set es & di to point to 11 byte diskette param table in ROM */
7409 ASM_START
7410 push bp
7411 mov bp, sp
7412 mov ax, #diskette_param_table2
7413 mov _int13_diskette_function.DI+2[bp], ax
7414 mov _int13_diskette_function.ES+2[bp], cs
7415 pop bp
7416 ASM_END
7417 CLEAR_CF(); // success
7418 /* disk status not changed upon success */
7419 return;
7422 case 0x15: // read diskette drive type
7423 BX_DEBUG_INT13_FL("floppy f15\n");
7424 drive = GET_ELDL();
7425 if (drive > 1) {
7426 SET_AH(0); // only 2 drives supported
7427 // set_diskette_ret_status here ???
7428 SET_CF();
7429 return;
7431 drive_type = inb_cmos(0x10);
7433 if (drive == 0)
7434 drive_type >>= 4;
7435 else
7436 drive_type &= 0x0f;
7437 CLEAR_CF(); // successful, not present
7438 if (drive_type==0) {
7439 SET_AH(0); // drive not present
7441 else {
7442 SET_AH(1); // drive present, does not support change line
7445 return;
7447 case 0x16: // get diskette change line status
7448 BX_DEBUG_INT13_FL("floppy f16\n");
7449 drive = GET_ELDL();
7450 if (drive > 1) {
7451 SET_AH(0x01); // invalid drive
7452 set_diskette_ret_status(0x01);
7453 SET_CF();
7454 return;
7457 SET_AH(0x06); // change line not supported
7458 set_diskette_ret_status(0x06);
7459 SET_CF();
7460 return;
7462 case 0x17: // set diskette type for format(old)
7463 BX_DEBUG_INT13_FL("floppy f17\n");
7464 /* not used for 1.44M floppies */
7465 SET_AH(0x01); // not supported
7466 set_diskette_ret_status(1); /* not supported */
7467 SET_CF();
7468 return;
7470 case 0x18: // set diskette type for format(new)
7471 BX_DEBUG_INT13_FL("floppy f18\n");
7472 SET_AH(0x01); // do later
7473 set_diskette_ret_status(1);
7474 SET_CF();
7475 return;
7477 default:
7478 BX_INFO("int13_diskette: unsupported AH=%02x\n", GET_AH());
7480 // if ( (ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e) ) {
7481 SET_AH(0x01); // ???
7482 set_diskette_ret_status(1);
7483 SET_CF();
7484 return;
7485 // }
7488 #else // #if BX_SUPPORT_FLOPPY
7489 void
7490 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
7491 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
7493 Bit8u val8;
7495 switch ( GET_AH() ) {
7497 case 0x01: // Read Diskette Status
7498 CLEAR_CF();
7499 val8 = read_byte(0x0000, 0x0441);
7500 SET_AH(val8);
7501 if (val8) {
7502 SET_CF();
7504 return;
7506 default:
7507 SET_CF();
7508 write_byte(0x0000, 0x0441, 0x01);
7509 SET_AH(0x01);
7512 #endif // #if BX_SUPPORT_FLOPPY
7514 void
7515 set_diskette_ret_status(value)
7516 Bit8u value;
7518 write_byte(0x0040, 0x0041, value);
7521 void
7522 set_diskette_current_cyl(drive, cyl)
7523 Bit8u drive;
7524 Bit8u cyl;
7526 if (drive > 1)
7527 BX_PANIC("set_diskette_current_cyl(): drive > 1\n");
7528 write_byte(0x0040, 0x0094+drive, cyl);
7531 void
7532 determine_floppy_media(drive)
7533 Bit16u drive;
7535 #if 0
7536 Bit8u val8, DOR, ctrl_info;
7538 ctrl_info = read_byte(0x0040, 0x008F);
7539 if (drive==1)
7540 ctrl_info >>= 4;
7541 else
7542 ctrl_info &= 0x0f;
7544 #if 0
7545 if (drive == 0) {
7546 DOR = 0x1c; // DOR: drive0 motor on, DMA&int enabled, normal op, drive select 0
7548 else {
7549 DOR = 0x2d; // DOR: drive1 motor on, DMA&int enabled, normal op, drive select 1
7551 #endif
7553 if ( (ctrl_info & 0x04) != 0x04 ) {
7554 // Drive not determined means no drive exists, done.
7555 return;
7558 #if 0
7559 // check Main Status Register for readiness
7560 val8 = inb(0x03f4) & 0x80; // Main Status Register
7561 if (val8 != 0x80)
7562 BX_PANIC("d_f_m: MRQ bit not set\n");
7564 // change line
7566 // existing BDA values
7568 // turn on drive motor
7569 outb(0x03f2, DOR); // Digital Output Register
7571 #endif
7572 BX_PANIC("d_f_m: OK so far\n");
7573 #endif
7576 void
7577 int17_function(regs, ds, iret_addr)
7578 pusha_regs_t regs; // regs pushed from PUSHA instruction
7579 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7580 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7582 Bit16u addr,timeout;
7583 Bit8u val8;
7585 ASM_START
7587 ASM_END
7589 addr = read_word(0x0040, (regs.u.r16.dx << 1) + 8);
7590 if ((regs.u.r8.ah < 3) && (regs.u.r16.dx < 3) && (addr > 0)) {
7591 timeout = read_byte(0x0040, 0x0078 + regs.u.r16.dx) << 8;
7592 if (regs.u.r8.ah == 0) {
7593 outb(addr, regs.u.r8.al);
7594 val8 = inb(addr+2);
7595 outb(addr+2, val8 | 0x01); // send strobe
7596 ASM_START
7598 ASM_END
7599 outb(addr+2, val8 & ~0x01);
7600 while (((inb(addr+1) & 0x40) == 0x40) && (timeout)) {
7601 timeout--;
7604 if (regs.u.r8.ah == 1) {
7605 val8 = inb(addr+2);
7606 outb(addr+2, val8 & ~0x04); // send init
7607 ASM_START
7609 ASM_END
7610 outb(addr+2, val8 | 0x04);
7612 val8 = inb(addr+1);
7613 regs.u.r8.ah = (val8 ^ 0x48);
7614 if (!timeout) regs.u.r8.ah |= 0x01;
7615 ClearCF(iret_addr.flags);
7616 } else {
7617 SetCF(iret_addr.flags); // Unsupported
7621 void
7622 int19_function(seq_nr)
7623 Bit16u seq_nr;
7625 Bit16u ebda_seg=read_word(0x0040,0x000E);
7626 Bit16u bootdev;
7627 Bit8u bootdrv;
7628 Bit8u bootchk;
7629 Bit16u bootseg;
7630 Bit16u bootip;
7631 Bit16u status;
7633 struct ipl_entry e;
7635 // if BX_ELTORITO_BOOT is not defined, old behavior
7636 // check bit 5 in CMOS reg 0x2d. load either 0x00 or 0x80 into DL
7637 // in preparation for the intial INT 13h (0=floppy A:, 0x80=C:)
7638 // 0: system boot sequence, first drive C: then A:
7639 // 1: system boot sequence, first drive A: then C:
7640 // else BX_ELTORITO_BOOT is defined
7641 // CMOS regs 0x3D and 0x38 contain the boot sequence:
7642 // CMOS reg 0x3D & 0x0f : 1st boot device
7643 // CMOS reg 0x3D & 0xf0 : 2nd boot device
7644 // CMOS reg 0x38 & 0xf0 : 3rd boot device
7645 // boot device codes:
7646 // 0x00 : not defined
7647 // 0x01 : first floppy
7648 // 0x02 : first harddrive
7649 // 0x03 : first cdrom
7650 // 0x04 - 0x0f : PnP expansion ROMs (e.g. Etherboot)
7651 // else : boot failure
7653 // Get the boot sequence
7654 #if BX_ELTORITO_BOOT
7655 bootdev = inb_cmos(0x3d);
7656 bootdev |= ((inb_cmos(0x38) & 0xf0) << 4);
7657 bootdev >>= 4 * seq_nr;
7658 bootdev &= 0xf;
7659 if (bootdev == 0) BX_PANIC("No bootable device.\n");
7661 /* Translate from CMOS runes to an IPL table offset by subtracting 1 */
7662 bootdev -= 1;
7663 #else
7664 if (seq_nr ==2) BX_PANIC("No more boot devices.");
7665 if (!!(inb_cmos(0x2d) & 0x20) ^ (seq_nr == 1))
7666 /* Boot from floppy if the bit is set or it's the second boot */
7667 bootdev = 0x00;
7668 else
7669 bootdev = 0x01;
7670 #endif
7672 /* Read the boot device from the IPL table */
7673 if (get_boot_vector(bootdev, &e) == 0) {
7674 BX_INFO("Invalid boot device (0x%x)\n", bootdev);
7675 return;
7678 /* Do the loading, and set up vector as a far pointer to the boot
7679 * address, and bootdrv as the boot drive */
7680 print_boot_device(e.type);
7682 switch(e.type) {
7683 case 0x01: /* FDD */
7684 case 0x02: /* HDD */
7686 bootdrv = (e.type == 0x02) ? 0x80 : 0x00;
7687 bootseg = 0x07c0;
7688 status = 0;
7690 ASM_START
7691 push bp
7692 mov bp, sp
7693 push ax
7694 push bx
7695 push cx
7696 push dx
7698 mov dl, _int19_function.bootdrv + 2[bp]
7699 mov ax, _int19_function.bootseg + 2[bp]
7700 mov es, ax ;; segment
7701 xor bx, bx ;; offset
7702 mov ah, #0x02 ;; function 2, read diskette sector
7703 mov al, #0x01 ;; read 1 sector
7704 mov ch, #0x00 ;; track 0
7705 mov cl, #0x01 ;; sector 1
7706 mov dh, #0x00 ;; head 0
7707 int #0x13 ;; read sector
7708 jnc int19_load_done
7709 mov ax, #0x0001
7710 mov _int19_function.status + 2[bp], ax
7712 int19_load_done:
7713 pop dx
7714 pop cx
7715 pop bx
7716 pop ax
7717 pop bp
7718 ASM_END
7720 if (status != 0) {
7721 print_boot_failure(e.type, 1);
7722 return;
7725 /* Always check the signature on a HDD boot sector; on FDD, only do
7726 * the check if the CMOS doesn't tell us to skip it */
7727 if ((e.type != 0x01) || !((inb_cmos(0x38) & 0x01))) {
7728 if (read_word(bootseg,0x1fe) != 0xaa55) {
7729 print_boot_failure(e.type, 0);
7730 return;
7734 /* Canonicalize bootseg:bootip */
7735 bootip = (bootseg & 0x0fff) << 4;
7736 bootseg &= 0xf000;
7737 break;
7739 #if BX_ELTORITO_BOOT
7740 case 0x03: /* CD-ROM */
7741 status = cdrom_boot();
7743 // If failure
7744 if ( (status & 0x00ff) !=0 ) {
7745 print_cdromboot_failure(status);
7746 print_boot_failure(e.type, 1);
7747 return;
7750 bootdrv = (Bit8u)(status>>8);
7751 bootseg = read_word(ebda_seg,&EbdaData->cdemu.load_segment);
7752 /* Canonicalize bootseg:bootip */
7753 bootip = (bootseg & 0x0fff) << 4;
7754 bootseg &= 0xf000;
7755 break;
7756 #endif
7758 case 0x80: /* Expansion ROM with a Bootstrap Entry Vector (a far pointer) */
7759 bootseg = e.vector >> 16;
7760 bootip = e.vector & 0xffff;
7761 break;
7763 default: return;
7766 /* Debugging info */
7767 BX_INFO("Booting from %x:%x\n", bootseg, bootip);
7769 /* Jump to the boot vector */
7770 ASM_START
7771 mov bp, sp
7772 ;; Build an iret stack frame that will take us to the boot vector.
7773 ;; iret pops ip, then cs, then flags, so push them in the opposite order.
7774 pushf
7775 mov ax, _int19_function.bootseg + 0[bp]
7776 push ax
7777 mov ax, _int19_function.bootip + 0[bp]
7778 push ax
7779 ;; Set the magic number in ax and the boot drive in dl.
7780 mov ax, #0xaa55
7781 mov dl, _int19_function.bootdrv + 0[bp]
7782 ;; Zero some of the other registers.
7783 xor bx, bx
7784 mov ds, bx
7785 mov es, bx
7786 mov bp, bx
7787 ;; Go!
7788 iret
7789 ASM_END
7792 void
7793 int1a_function(regs, ds, iret_addr)
7794 pusha_regs_t regs; // regs pushed from PUSHA instruction
7795 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7796 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7798 Bit8u val8;
7800 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);
7802 ASM_START
7804 ASM_END
7806 switch (regs.u.r8.ah) {
7807 case 0: // get current clock count
7808 ASM_START
7810 ASM_END
7811 regs.u.r16.cx = BiosData->ticks_high;
7812 regs.u.r16.dx = BiosData->ticks_low;
7813 regs.u.r8.al = BiosData->midnight_flag;
7814 BiosData->midnight_flag = 0; // reset flag
7815 ASM_START
7817 ASM_END
7818 // AH already 0
7819 ClearCF(iret_addr.flags); // OK
7820 break;
7822 case 1: // Set Current Clock Count
7823 ASM_START
7825 ASM_END
7826 BiosData->ticks_high = regs.u.r16.cx;
7827 BiosData->ticks_low = regs.u.r16.dx;
7828 BiosData->midnight_flag = 0; // reset flag
7829 ASM_START
7831 ASM_END
7832 regs.u.r8.ah = 0;
7833 ClearCF(iret_addr.flags); // OK
7834 break;
7837 case 2: // Read CMOS Time
7838 if (rtc_updating()) {
7839 SetCF(iret_addr.flags);
7840 break;
7843 regs.u.r8.dh = inb_cmos(0x00); // Seconds
7844 regs.u.r8.cl = inb_cmos(0x02); // Minutes
7845 regs.u.r8.ch = inb_cmos(0x04); // Hours
7846 regs.u.r8.dl = inb_cmos(0x0b) & 0x01; // Stat Reg B
7847 regs.u.r8.ah = 0;
7848 regs.u.r8.al = regs.u.r8.ch;
7849 ClearCF(iret_addr.flags); // OK
7850 break;
7852 case 3: // Set CMOS Time
7853 // Using a debugger, I notice the following masking/setting
7854 // of bits in Status Register B, by setting Reg B to
7855 // a few values and getting its value after INT 1A was called.
7857 // try#1 try#2 try#3
7858 // before 1111 1101 0111 1101 0000 0000
7859 // after 0110 0010 0110 0010 0000 0010
7861 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7862 // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
7863 if (rtc_updating()) {
7864 init_rtc();
7865 // fall through as if an update were not in progress
7867 outb_cmos(0x00, regs.u.r8.dh); // Seconds
7868 outb_cmos(0x02, regs.u.r8.cl); // Minutes
7869 outb_cmos(0x04, regs.u.r8.ch); // Hours
7870 // Set Daylight Savings time enabled bit to requested value
7871 val8 = (inb_cmos(0x0b) & 0x60) | 0x02 | (regs.u.r8.dl & 0x01);
7872 // (reg B already selected)
7873 outb_cmos(0x0b, val8);
7874 regs.u.r8.ah = 0;
7875 regs.u.r8.al = val8; // val last written to Reg B
7876 ClearCF(iret_addr.flags); // OK
7877 break;
7879 case 4: // Read CMOS Date
7880 regs.u.r8.ah = 0;
7881 if (rtc_updating()) {
7882 SetCF(iret_addr.flags);
7883 break;
7885 regs.u.r8.cl = inb_cmos(0x09); // Year
7886 regs.u.r8.dh = inb_cmos(0x08); // Month
7887 regs.u.r8.dl = inb_cmos(0x07); // Day of Month
7888 regs.u.r8.ch = inb_cmos(0x32); // Century
7889 regs.u.r8.al = regs.u.r8.ch;
7890 ClearCF(iret_addr.flags); // OK
7891 break;
7893 case 5: // Set CMOS Date
7894 // Using a debugger, I notice the following masking/setting
7895 // of bits in Status Register B, by setting Reg B to
7896 // a few values and getting its value after INT 1A was called.
7898 // try#1 try#2 try#3 try#4
7899 // before 1111 1101 0111 1101 0000 0010 0000 0000
7900 // after 0110 1101 0111 1101 0000 0010 0000 0000
7902 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7903 // My assumption: RegB = (RegB & 01111111b)
7904 if (rtc_updating()) {
7905 init_rtc();
7906 SetCF(iret_addr.flags);
7907 break;
7909 outb_cmos(0x09, regs.u.r8.cl); // Year
7910 outb_cmos(0x08, regs.u.r8.dh); // Month
7911 outb_cmos(0x07, regs.u.r8.dl); // Day of Month
7912 outb_cmos(0x32, regs.u.r8.ch); // Century
7913 val8 = inb_cmos(0x0b) & 0x7f; // clear halt-clock bit
7914 outb_cmos(0x0b, val8);
7915 regs.u.r8.ah = 0;
7916 regs.u.r8.al = val8; // AL = val last written to Reg B
7917 ClearCF(iret_addr.flags); // OK
7918 break;
7920 case 6: // Set Alarm Time in CMOS
7921 // Using a debugger, I notice the following masking/setting
7922 // of bits in Status Register B, by setting Reg B to
7923 // a few values and getting its value after INT 1A was called.
7925 // try#1 try#2 try#3
7926 // before 1101 1111 0101 1111 0000 0000
7927 // after 0110 1111 0111 1111 0010 0000
7929 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7930 // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
7931 val8 = inb_cmos(0x0b); // Get Status Reg B
7932 regs.u.r16.ax = 0;
7933 if (val8 & 0x20) {
7934 // Alarm interrupt enabled already
7935 SetCF(iret_addr.flags); // Error: alarm in use
7936 break;
7938 if (rtc_updating()) {
7939 init_rtc();
7940 // fall through as if an update were not in progress
7942 outb_cmos(0x01, regs.u.r8.dh); // Seconds alarm
7943 outb_cmos(0x03, regs.u.r8.cl); // Minutes alarm
7944 outb_cmos(0x05, regs.u.r8.ch); // Hours alarm
7945 outb(0xa1, inb(0xa1) & 0xfe); // enable IRQ 8
7946 // enable Status Reg B alarm bit, clear halt clock bit
7947 outb_cmos(0x0b, (val8 & 0x7f) | 0x20);
7948 ClearCF(iret_addr.flags); // OK
7949 break;
7951 case 7: // Turn off Alarm
7952 // Using a debugger, I notice the following masking/setting
7953 // of bits in Status Register B, by setting Reg B to
7954 // a few values and getting its value after INT 1A was called.
7956 // try#1 try#2 try#3 try#4
7957 // before 1111 1101 0111 1101 0010 0000 0010 0010
7958 // after 0100 0101 0101 0101 0000 0000 0000 0010
7960 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7961 // My assumption: RegB = (RegB & 01010111b)
7962 val8 = inb_cmos(0x0b); // Get Status Reg B
7963 // clear clock-halt bit, disable alarm bit
7964 outb_cmos(0x0b, val8 & 0x57); // disable alarm bit
7965 regs.u.r8.ah = 0;
7966 regs.u.r8.al = val8; // val last written to Reg B
7967 ClearCF(iret_addr.flags); // OK
7968 break;
7969 #if BX_PCIBIOS
7970 case 0xb1:
7971 // real mode PCI BIOS functions now handled in assembler code
7972 // this C code handles the error code for information only
7973 if (regs.u.r8.bl == 0xff) {
7974 BX_INFO("PCI BIOS: PCI not present\n");
7975 } else if (regs.u.r8.bl == 0x81) {
7976 BX_INFO("unsupported PCI BIOS function 0x%02x\n", regs.u.r8.al);
7977 } else if (regs.u.r8.bl == 0x83) {
7978 BX_INFO("bad PCI vendor ID %04x\n", regs.u.r16.dx);
7979 } else if (regs.u.r8.bl == 0x86) {
7980 if (regs.u.r8.al == 0x02) {
7981 BX_INFO("PCI device %04x:%04x not found at index %d\n", regs.u.r16.dx, regs.u.r16.cx, regs.u.r16.si);
7982 } else {
7983 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);
7986 regs.u.r8.ah = regs.u.r8.bl;
7987 SetCF(iret_addr.flags);
7988 break;
7989 #endif
7991 default:
7992 SetCF(iret_addr.flags); // Unsupported
7996 void
7997 int70_function(regs, ds, iret_addr)
7998 pusha_regs_t regs; // regs pushed from PUSHA instruction
7999 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
8000 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
8002 // INT 70h: IRQ 8 - CMOS RTC interrupt from periodic or alarm modes
8003 Bit8u registerB = 0, registerC = 0;
8005 // Check which modes are enabled and have occurred.
8006 registerB = inb_cmos( 0xB );
8007 registerC = inb_cmos( 0xC );
8009 if( ( registerB & 0x60 ) != 0 ) {
8010 if( ( registerC & 0x20 ) != 0 ) {
8011 // Handle Alarm Interrupt.
8012 ASM_START
8014 int #0x4a
8016 ASM_END
8018 if( ( registerC & 0x40 ) != 0 ) {
8019 // Handle Periodic Interrupt.
8021 if( read_byte( 0x40, 0xA0 ) != 0 ) {
8022 // Wait Interval (Int 15, AH=83) active.
8023 Bit32u time, toggle;
8025 time = read_dword( 0x40, 0x9C ); // Time left in microseconds.
8026 if( time < 0x3D1 ) {
8027 // Done waiting.
8028 Bit16u segment, offset;
8030 segment = read_word( 0x40, 0x98 );
8031 offset = read_word( 0x40, 0x9A );
8032 write_byte( 0x40, 0xA0, 0 ); // Turn of status byte.
8033 outb_cmos( 0xB, registerB & 0x37 ); // Clear the Periodic Interrupt.
8034 write_byte(segment, offset, read_byte(segment, offset) | 0x80 ); // Write to specified flag byte.
8035 } else {
8036 // Continue waiting.
8037 time -= 0x3D1;
8038 write_dword( 0x40, 0x9C, time );
8044 ASM_START
8045 call eoi_both_pics
8046 ASM_END
8050 ASM_START
8051 ;------------------------------------------
8052 ;- INT74h : PS/2 mouse hardware interrupt -
8053 ;------------------------------------------
8054 int74_handler:
8056 pusha
8057 push ds ;; save DS
8058 push #0x00 ;; placeholder for status
8059 push #0x00 ;; placeholder for X
8060 push #0x00 ;; placeholder for Y
8061 push #0x00 ;; placeholder for Z
8062 push #0x00 ;; placeholder for make_far_call boolean
8063 call _int74_function
8064 pop cx ;; remove make_far_call from stack
8065 jcxz int74_done
8067 ;; make far call to EBDA:0022
8068 push #0x00
8069 pop ds
8070 push 0x040E ;; push 0000:040E (opcodes 0xff, 0x36, 0x0E, 0x04)
8071 pop ds
8072 //CALL_EP(0x0022) ;; call far routine (call_Ep DS:0022 :opcodes 0xff, 0x1e, 0x22, 0x00)
8073 call far ptr[0x22]
8074 int74_done:
8076 call eoi_both_pics
8077 add sp, #8 ;; pop status, x, y, z
8079 pop ds ;; restore DS
8080 popa
8081 iret
8084 ;; This will perform an IRET, but will retain value of current CF
8085 ;; by altering flags on stack. Better than RETF #02.
8086 iret_modify_cf:
8087 jc carry_set
8088 push bp
8089 mov bp, sp
8090 and BYTE [bp + 0x06], #0xfe
8091 pop bp
8092 iret
8093 carry_set:
8094 push bp
8095 mov bp, sp
8096 or BYTE [bp + 0x06], #0x01
8097 pop bp
8098 iret
8101 ;----------------------
8102 ;- INT13h (relocated) -
8103 ;----------------------
8105 ; int13_relocated is a little bit messed up since I played with it
8106 ; I have to rewrite it:
8107 ; - call a function that detect which function to call
8108 ; - make all called C function get the same parameters list
8110 int13_relocated:
8112 #if BX_ELTORITO_BOOT
8113 ;; check for an eltorito function
8114 cmp ah,#0x4a
8115 jb int13_not_eltorito
8116 cmp ah,#0x4d
8117 ja int13_not_eltorito
8119 pusha
8120 push es
8121 push ds
8122 push ss
8123 pop ds
8125 push #int13_out
8126 jmp _int13_eltorito ;; ELDX not used
8128 int13_not_eltorito:
8129 push ax
8130 push bx
8131 push cx
8132 push dx
8134 ;; check if emulation active
8135 call _cdemu_isactive
8136 cmp al,#0x00
8137 je int13_cdemu_inactive
8139 ;; check if access to the emulated drive
8140 call _cdemu_emulated_drive
8141 pop dx
8142 push dx
8143 cmp al,dl ;; int13 on emulated drive
8144 jne int13_nocdemu
8146 pop dx
8147 pop cx
8148 pop bx
8149 pop ax
8151 pusha
8152 push es
8153 push ds
8154 push ss
8155 pop ds
8157 push #int13_out
8158 jmp _int13_cdemu ;; ELDX not used
8160 int13_nocdemu:
8161 and dl,#0xE0 ;; mask to get device class, including cdroms
8162 cmp al,dl ;; al is 0x00 or 0x80
8163 jne int13_cdemu_inactive ;; inactive for device class
8165 pop dx
8166 pop cx
8167 pop bx
8168 pop ax
8170 push ax
8171 push cx
8172 push dx
8173 push bx
8175 dec dl ;; real drive is dl - 1
8176 jmp int13_legacy
8178 int13_cdemu_inactive:
8179 pop dx
8180 pop cx
8181 pop bx
8182 pop ax
8184 #endif // BX_ELTORITO_BOOT
8186 int13_noeltorito:
8188 push ax
8189 push cx
8190 push dx
8191 push bx
8193 int13_legacy:
8195 push dx ;; push eltorito value of dx instead of sp
8197 push bp
8198 push si
8199 push di
8201 push es
8202 push ds
8203 push ss
8204 pop ds
8206 ;; now the 16-bit registers can be restored with:
8207 ;; pop ds; pop es; popa; iret
8208 ;; arguments passed to functions should be
8209 ;; DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS
8211 test dl, #0x80
8212 jnz int13_notfloppy
8214 push #int13_out
8215 jmp _int13_diskette_function
8217 int13_notfloppy:
8219 #if BX_USE_ATADRV
8221 cmp dl, #0xE0
8222 jb int13_notcdrom
8224 // ebx is modified: BSD 5.2.1 boot loader problem
8225 // someone should figure out which 32 bit register that actually are used
8227 shr ebx, #16
8228 push bx
8230 call _int13_cdrom
8232 pop bx
8233 shl ebx, #16
8235 jmp int13_out
8237 int13_notcdrom:
8239 #endif
8241 int13_disk:
8242 ;; int13_harddisk modifies high word of EAX
8243 shr eax, #16
8244 push ax
8245 call _int13_harddisk
8246 pop ax
8247 shl eax, #16
8249 int13_out:
8250 pop ds
8251 pop es
8252 popa
8253 iret
8255 ;----------
8256 ;- INT18h -
8257 ;----------
8258 int18_handler: ;; Boot Failure recovery: try the next device.
8260 ;; Reset SP and SS
8261 mov ax, #0xfffe
8262 mov sp, ax
8263 xor ax, ax
8264 mov ss, ax
8266 ;; Get the boot sequence number out of the IPL memory
8267 mov bx, #IPL_SEG
8268 mov ds, bx ;; Set segment
8269 mov bx, IPL_SEQUENCE_OFFSET ;; BX is now the sequence number
8270 inc bx ;; ++
8271 mov IPL_SEQUENCE_OFFSET, bx ;; Write it back
8272 mov ds, ax ;; and reset the segment to zero.
8274 ;; Carry on in the INT 19h handler, using the new sequence number
8275 push bx
8277 jmp int19_next_boot
8279 ;----------
8280 ;- INT19h -
8281 ;----------
8282 int19_relocated: ;; Boot function, relocated
8284 ;; int19 was beginning to be really complex, so now it
8285 ;; just calls a C function that does the work
8287 push bp
8288 mov bp, sp
8290 ;; Reset SS and SP
8291 mov ax, #0xfffe
8292 mov sp, ax
8293 xor ax, ax
8294 mov ss, ax
8296 ;; Start from the first boot device (0, in AX)
8297 mov bx, #IPL_SEG
8298 mov ds, bx ;; Set segment to write to the IPL memory
8299 mov IPL_SEQUENCE_OFFSET, ax ;; Save the sequence number
8300 mov ds, ax ;; and reset the segment.
8302 push ax
8304 int19_next_boot:
8306 ;; Call the C code for the next boot device
8307 call _int19_function
8309 ;; Boot failed: invoke the boot recovery function
8310 int #0x18
8312 ;----------
8313 ;- INT1Ch -
8314 ;----------
8315 int1c_handler: ;; User Timer Tick
8316 iret
8319 ;----------------------
8320 ;- POST: Floppy Drive -
8321 ;----------------------
8322 floppy_drive_post:
8323 xor ax, ax
8324 mov ds, ax
8326 mov al, #0x00
8327 mov 0x043e, al ;; drive 0 & 1 uncalibrated, no interrupt has occurred
8329 mov 0x043f, al ;; diskette motor status: read op, drive0, motors off
8331 mov 0x0440, al ;; diskette motor timeout counter: not active
8332 mov 0x0441, al ;; diskette controller status return code
8334 mov 0x0442, al ;; disk & diskette controller status register 0
8335 mov 0x0443, al ;; diskette controller status register 1
8336 mov 0x0444, al ;; diskette controller status register 2
8337 mov 0x0445, al ;; diskette controller cylinder number
8338 mov 0x0446, al ;; diskette controller head number
8339 mov 0x0447, al ;; diskette controller sector number
8340 mov 0x0448, al ;; diskette controller bytes written
8342 mov 0x048b, al ;; diskette configuration data
8344 ;; -----------------------------------------------------------------
8345 ;; (048F) diskette controller information
8347 mov al, #0x10 ;; get CMOS diskette drive type
8348 out 0x70, AL
8349 in AL, 0x71
8350 mov ah, al ;; save byte to AH
8352 look_drive0:
8353 shr al, #4 ;; look at top 4 bits for drive 0
8354 jz f0_missing ;; jump if no drive0
8355 mov bl, #0x07 ;; drive0 determined, multi-rate, has changed line
8356 jmp look_drive1
8357 f0_missing:
8358 mov bl, #0x00 ;; no drive0
8360 look_drive1:
8361 mov al, ah ;; restore from AH
8362 and al, #0x0f ;; look at bottom 4 bits for drive 1
8363 jz f1_missing ;; jump if no drive1
8364 or bl, #0x70 ;; drive1 determined, multi-rate, has changed line
8365 f1_missing:
8366 ;; leave high bits in BL zerod
8367 mov 0x048f, bl ;; put new val in BDA (diskette controller information)
8368 ;; -----------------------------------------------------------------
8370 mov al, #0x00
8371 mov 0x0490, al ;; diskette 0 media state
8372 mov 0x0491, al ;; diskette 1 media state
8374 ;; diskette 0,1 operational starting state
8375 ;; drive type has not been determined,
8376 ;; has no changed detection line
8377 mov 0x0492, al
8378 mov 0x0493, al
8380 mov 0x0494, al ;; diskette 0 current cylinder
8381 mov 0x0495, al ;; diskette 1 current cylinder
8383 mov al, #0x02
8384 out #0x0a, al ;; clear DMA-1 channel 2 mask bit
8386 SET_INT_VECTOR(0x1E, #0xF000, #diskette_param_table2)
8387 SET_INT_VECTOR(0x40, #0xF000, #int13_diskette)
8388 SET_INT_VECTOR(0x0E, #0xF000, #int0e_handler) ;; IRQ 6
8393 ;--------------------
8394 ;- POST: HARD DRIVE -
8395 ;--------------------
8396 ; relocated here because the primary POST area isnt big enough.
8397 hard_drive_post:
8398 // IRQ 14 = INT 76h
8399 // INT 76h calls INT 15h function ax=9100
8401 mov al, #0x0a ; 0000 1010 = reserved, disable IRQ 14
8402 mov dx, #0x03f6
8403 out dx, al
8405 xor ax, ax
8406 mov ds, ax
8407 mov 0x0474, al /* hard disk status of last operation */
8408 mov 0x0477, al /* hard disk port offset (XT only ???) */
8409 mov 0x048c, al /* hard disk status register */
8410 mov 0x048d, al /* hard disk error register */
8411 mov 0x048e, al /* hard disk task complete flag */
8412 mov al, #0x01
8413 mov 0x0475, al /* hard disk number attached */
8414 mov al, #0xc0
8415 mov 0x0476, al /* hard disk control byte */
8416 SET_INT_VECTOR(0x13, #0xF000, #int13_handler)
8417 SET_INT_VECTOR(0x76, #0xF000, #int76_handler)
8418 ;; INT 41h: hard disk 0 configuration pointer
8419 ;; INT 46h: hard disk 1 configuration pointer
8420 SET_INT_VECTOR(0x41, #EBDA_SEG, #0x003D)
8421 SET_INT_VECTOR(0x46, #EBDA_SEG, #0x004D)
8423 ;; move disk geometry data from CMOS to EBDA disk parameter table(s)
8424 mov al, #0x12
8425 out #0x70, al
8426 in al, #0x71
8427 and al, #0xf0
8428 cmp al, #0xf0
8429 je post_d0_extended
8430 jmp check_for_hd1
8431 post_d0_extended:
8432 mov al, #0x19
8433 out #0x70, al
8434 in al, #0x71
8435 cmp al, #47 ;; decimal 47 - user definable
8436 je post_d0_type47
8437 HALT(__LINE__)
8438 post_d0_type47:
8439 ;; CMOS purpose param table offset
8440 ;; 1b cylinders low 0
8441 ;; 1c cylinders high 1
8442 ;; 1d heads 2
8443 ;; 1e write pre-comp low 5
8444 ;; 1f write pre-comp high 6
8445 ;; 20 retries/bad map/heads>8 8
8446 ;; 21 landing zone low C
8447 ;; 22 landing zone high D
8448 ;; 23 sectors/track E
8450 mov ax, #EBDA_SEG
8451 mov ds, ax
8453 ;;; Filling EBDA table for hard disk 0.
8454 mov al, #0x1f
8455 out #0x70, al
8456 in al, #0x71
8457 mov ah, al
8458 mov al, #0x1e
8459 out #0x70, al
8460 in al, #0x71
8461 mov (0x003d + 0x05), ax ;; write precomp word
8463 mov al, #0x20
8464 out #0x70, al
8465 in al, #0x71
8466 mov (0x003d + 0x08), al ;; drive control byte
8468 mov al, #0x22
8469 out #0x70, al
8470 in al, #0x71
8471 mov ah, al
8472 mov al, #0x21
8473 out #0x70, al
8474 in al, #0x71
8475 mov (0x003d + 0x0C), ax ;; landing zone word
8477 mov al, #0x1c ;; get cylinders word in AX
8478 out #0x70, al
8479 in al, #0x71 ;; high byte
8480 mov ah, al
8481 mov al, #0x1b
8482 out #0x70, al
8483 in al, #0x71 ;; low byte
8484 mov bx, ax ;; BX = cylinders
8486 mov al, #0x1d
8487 out #0x70, al
8488 in al, #0x71
8489 mov cl, al ;; CL = heads
8491 mov al, #0x23
8492 out #0x70, al
8493 in al, #0x71
8494 mov dl, al ;; DL = sectors
8496 cmp bx, #1024
8497 jnbe hd0_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8499 hd0_post_physical_chs:
8500 ;; no logical CHS mapping used, just physical CHS
8501 ;; use Standard Fixed Disk Parameter Table (FDPT)
8502 mov (0x003d + 0x00), bx ;; number of physical cylinders
8503 mov (0x003d + 0x02), cl ;; number of physical heads
8504 mov (0x003d + 0x0E), dl ;; number of physical sectors
8505 jmp check_for_hd1
8507 hd0_post_logical_chs:
8508 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8509 mov (0x003d + 0x09), bx ;; number of physical cylinders
8510 mov (0x003d + 0x0b), cl ;; number of physical heads
8511 mov (0x003d + 0x04), dl ;; number of physical sectors
8512 mov (0x003d + 0x0e), dl ;; number of logical sectors (same)
8513 mov al, #0xa0
8514 mov (0x003d + 0x03), al ;; A0h signature, indicates translated table
8516 cmp bx, #2048
8517 jnbe hd0_post_above_2048
8518 ;; 1024 < c <= 2048 cylinders
8519 shr bx, #0x01
8520 shl cl, #0x01
8521 jmp hd0_post_store_logical
8523 hd0_post_above_2048:
8524 cmp bx, #4096
8525 jnbe hd0_post_above_4096
8526 ;; 2048 < c <= 4096 cylinders
8527 shr bx, #0x02
8528 shl cl, #0x02
8529 jmp hd0_post_store_logical
8531 hd0_post_above_4096:
8532 cmp bx, #8192
8533 jnbe hd0_post_above_8192
8534 ;; 4096 < c <= 8192 cylinders
8535 shr bx, #0x03
8536 shl cl, #0x03
8537 jmp hd0_post_store_logical
8539 hd0_post_above_8192:
8540 ;; 8192 < c <= 16384 cylinders
8541 shr bx, #0x04
8542 shl cl, #0x04
8544 hd0_post_store_logical:
8545 mov (0x003d + 0x00), bx ;; number of physical cylinders
8546 mov (0x003d + 0x02), cl ;; number of physical heads
8547 ;; checksum
8548 mov cl, #0x0f ;; repeat count
8549 mov si, #0x003d ;; offset to disk0 FDPT
8550 mov al, #0x00 ;; sum
8551 hd0_post_checksum_loop:
8552 add al, [si]
8553 inc si
8554 dec cl
8555 jnz hd0_post_checksum_loop
8556 not al ;; now take 2s complement
8557 inc al
8558 mov [si], al
8559 ;;; Done filling EBDA table for hard disk 0.
8562 check_for_hd1:
8563 ;; is there really a second hard disk? if not, return now
8564 mov al, #0x12
8565 out #0x70, al
8566 in al, #0x71
8567 and al, #0x0f
8568 jnz post_d1_exists
8570 post_d1_exists:
8571 ;; check that the hd type is really 0x0f.
8572 cmp al, #0x0f
8573 jz post_d1_extended
8574 HALT(__LINE__)
8575 post_d1_extended:
8576 ;; check that the extended type is 47 - user definable
8577 mov al, #0x1a
8578 out #0x70, al
8579 in al, #0x71
8580 cmp al, #47 ;; decimal 47 - user definable
8581 je post_d1_type47
8582 HALT(__LINE__)
8583 post_d1_type47:
8584 ;; Table for disk1.
8585 ;; CMOS purpose param table offset
8586 ;; 0x24 cylinders low 0
8587 ;; 0x25 cylinders high 1
8588 ;; 0x26 heads 2
8589 ;; 0x27 write pre-comp low 5
8590 ;; 0x28 write pre-comp high 6
8591 ;; 0x29 heads>8 8
8592 ;; 0x2a landing zone low C
8593 ;; 0x2b landing zone high D
8594 ;; 0x2c sectors/track E
8595 ;;; Fill EBDA table for hard disk 1.
8596 mov ax, #EBDA_SEG
8597 mov ds, ax
8598 mov al, #0x28
8599 out #0x70, al
8600 in al, #0x71
8601 mov ah, al
8602 mov al, #0x27
8603 out #0x70, al
8604 in al, #0x71
8605 mov (0x004d + 0x05), ax ;; write precomp word
8607 mov al, #0x29
8608 out #0x70, al
8609 in al, #0x71
8610 mov (0x004d + 0x08), al ;; drive control byte
8612 mov al, #0x2b
8613 out #0x70, al
8614 in al, #0x71
8615 mov ah, al
8616 mov al, #0x2a
8617 out #0x70, al
8618 in al, #0x71
8619 mov (0x004d + 0x0C), ax ;; landing zone word
8621 mov al, #0x25 ;; get cylinders word in AX
8622 out #0x70, al
8623 in al, #0x71 ;; high byte
8624 mov ah, al
8625 mov al, #0x24
8626 out #0x70, al
8627 in al, #0x71 ;; low byte
8628 mov bx, ax ;; BX = cylinders
8630 mov al, #0x26
8631 out #0x70, al
8632 in al, #0x71
8633 mov cl, al ;; CL = heads
8635 mov al, #0x2c
8636 out #0x70, al
8637 in al, #0x71
8638 mov dl, al ;; DL = sectors
8640 cmp bx, #1024
8641 jnbe hd1_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8643 hd1_post_physical_chs:
8644 ;; no logical CHS mapping used, just physical CHS
8645 ;; use Standard Fixed Disk Parameter Table (FDPT)
8646 mov (0x004d + 0x00), bx ;; number of physical cylinders
8647 mov (0x004d + 0x02), cl ;; number of physical heads
8648 mov (0x004d + 0x0E), dl ;; number of physical sectors
8651 hd1_post_logical_chs:
8652 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8653 mov (0x004d + 0x09), bx ;; number of physical cylinders
8654 mov (0x004d + 0x0b), cl ;; number of physical heads
8655 mov (0x004d + 0x04), dl ;; number of physical sectors
8656 mov (0x004d + 0x0e), dl ;; number of logical sectors (same)
8657 mov al, #0xa0
8658 mov (0x004d + 0x03), al ;; A0h signature, indicates translated table
8660 cmp bx, #2048
8661 jnbe hd1_post_above_2048
8662 ;; 1024 < c <= 2048 cylinders
8663 shr bx, #0x01
8664 shl cl, #0x01
8665 jmp hd1_post_store_logical
8667 hd1_post_above_2048:
8668 cmp bx, #4096
8669 jnbe hd1_post_above_4096
8670 ;; 2048 < c <= 4096 cylinders
8671 shr bx, #0x02
8672 shl cl, #0x02
8673 jmp hd1_post_store_logical
8675 hd1_post_above_4096:
8676 cmp bx, #8192
8677 jnbe hd1_post_above_8192
8678 ;; 4096 < c <= 8192 cylinders
8679 shr bx, #0x03
8680 shl cl, #0x03
8681 jmp hd1_post_store_logical
8683 hd1_post_above_8192:
8684 ;; 8192 < c <= 16384 cylinders
8685 shr bx, #0x04
8686 shl cl, #0x04
8688 hd1_post_store_logical:
8689 mov (0x004d + 0x00), bx ;; number of physical cylinders
8690 mov (0x004d + 0x02), cl ;; number of physical heads
8691 ;; checksum
8692 mov cl, #0x0f ;; repeat count
8693 mov si, #0x004d ;; offset to disk0 FDPT
8694 mov al, #0x00 ;; sum
8695 hd1_post_checksum_loop:
8696 add al, [si]
8697 inc si
8698 dec cl
8699 jnz hd1_post_checksum_loop
8700 not al ;; now take 2s complement
8701 inc al
8702 mov [si], al
8703 ;;; Done filling EBDA table for hard disk 1.
8707 ;--------------------
8708 ;- POST: EBDA segment
8709 ;--------------------
8710 ; relocated here because the primary POST area isnt big enough.
8711 ebda_post:
8712 #if BX_USE_EBDA
8713 mov ax, #EBDA_SEG
8714 mov ds, ax
8715 mov byte ptr [0x0], #EBDA_SIZE
8716 #endif
8717 xor ax, ax ; mov EBDA seg into 40E
8718 mov ds, ax
8719 mov word ptr [0x40E], #EBDA_SEG
8720 ret;;
8722 ;--------------------
8723 ;- POST: EOI + jmp via [0x40:67)
8724 ;--------------------
8725 ; relocated here because the primary POST area isnt big enough.
8726 eoi_jmp_post:
8727 call eoi_both_pics
8729 xor ax, ax
8730 mov ds, ax
8732 jmp far ptr [0x467]
8735 ;--------------------
8736 eoi_both_pics:
8737 mov al, #0x20
8738 out #0xA0, al ;; slave PIC EOI
8739 eoi_master_pic:
8740 mov al, #0x20
8741 out #0x20, al ;; master PIC EOI
8744 ;--------------------
8745 BcdToBin:
8746 ;; in: AL in BCD format
8747 ;; out: AL in binary format, AH will always be 0
8748 ;; trashes BX
8749 mov bl, al
8750 and bl, #0x0f ;; bl has low digit
8751 shr al, #4 ;; al has high digit
8752 mov bh, #10
8753 mul al, bh ;; multiply high digit by 10 (result in AX)
8754 add al, bl ;; then add low digit
8757 ;--------------------
8758 timer_tick_post:
8759 ;; Setup the Timer Ticks Count (0x46C:dword) and
8760 ;; Timer Ticks Roller Flag (0x470:byte)
8761 ;; The Timer Ticks Count needs to be set according to
8762 ;; the current CMOS time, as if ticks have been occurring
8763 ;; at 18.2hz since midnight up to this point. Calculating
8764 ;; this is a little complicated. Here are the factors I gather
8765 ;; regarding this. 14,318,180 hz was the original clock speed,
8766 ;; chosen so it could be divided by either 3 to drive the 5Mhz CPU
8767 ;; at the time, or 4 to drive the CGA video adapter. The div3
8768 ;; source was divided again by 4 to feed a 1.193Mhz signal to
8769 ;; the timer. With a maximum 16bit timer count, this is again
8770 ;; divided down by 65536 to 18.2hz.
8772 ;; 14,318,180 Hz clock
8773 ;; /3 = 4,772,726 Hz fed to orginal 5Mhz CPU
8774 ;; /4 = 1,193,181 Hz fed to timer
8775 ;; /65536 (maximum timer count) = 18.20650736 ticks/second
8776 ;; 1 second = 18.20650736 ticks
8777 ;; 1 minute = 1092.390442 ticks
8778 ;; 1 hour = 65543.42651 ticks
8780 ;; Given the values in the CMOS clock, one could calculate
8781 ;; the number of ticks by the following:
8782 ;; ticks = (BcdToBin(seconds) * 18.206507) +
8783 ;; (BcdToBin(minutes) * 1092.3904)
8784 ;; (BcdToBin(hours) * 65543.427)
8785 ;; To get a little more accuracy, since Im using integer
8786 ;; arithmatic, I use:
8787 ;; ticks = (BcdToBin(seconds) * 18206507) / 1000000 +
8788 ;; (BcdToBin(minutes) * 10923904) / 10000 +
8789 ;; (BcdToBin(hours) * 65543427) / 1000
8791 ;; assuming DS=0000
8793 ;; get CMOS seconds
8794 xor eax, eax ;; clear EAX
8795 mov al, #0x00
8796 out #0x70, al
8797 in al, #0x71 ;; AL has CMOS seconds in BCD
8798 call BcdToBin ;; EAX now has seconds in binary
8799 mov edx, #18206507
8800 mul eax, edx
8801 mov ebx, #1000000
8802 xor edx, edx
8803 div eax, ebx
8804 mov ecx, eax ;; ECX will accumulate total ticks
8806 ;; get CMOS minutes
8807 xor eax, eax ;; clear EAX
8808 mov al, #0x02
8809 out #0x70, al
8810 in al, #0x71 ;; AL has CMOS minutes in BCD
8811 call BcdToBin ;; EAX now has minutes in binary
8812 mov edx, #10923904
8813 mul eax, edx
8814 mov ebx, #10000
8815 xor edx, edx
8816 div eax, ebx
8817 add ecx, eax ;; add to total ticks
8819 ;; get CMOS hours
8820 xor eax, eax ;; clear EAX
8821 mov al, #0x04
8822 out #0x70, al
8823 in al, #0x71 ;; AL has CMOS hours in BCD
8824 call BcdToBin ;; EAX now has hours in binary
8825 mov edx, #65543427
8826 mul eax, edx
8827 mov ebx, #1000
8828 xor edx, edx
8829 div eax, ebx
8830 add ecx, eax ;; add to total ticks
8832 mov 0x46C, ecx ;; Timer Ticks Count
8833 xor al, al
8834 mov 0x470, al ;; Timer Ticks Rollover Flag
8837 ;--------------------
8838 int76_handler:
8839 ;; record completion in BIOS task complete flag
8840 push ax
8841 push ds
8842 mov ax, #0x0040
8843 mov ds, ax
8844 mov 0x008E, #0xff
8845 call eoi_both_pics
8846 pop ds
8847 pop ax
8848 iret
8851 ;--------------------
8852 #if BX_APM
8854 use32 386
8855 #define APM_PROT32
8856 #include "apmbios.S"
8858 use16 386
8859 #define APM_PROT16
8860 #include "apmbios.S"
8862 #define APM_REAL
8863 #include "apmbios.S"
8865 #endif
8867 ;--------------------
8868 #if BX_PCIBIOS
8869 use32 386
8870 .align 16
8871 bios32_structure:
8872 db 0x5f, 0x33, 0x32, 0x5f ;; "_32_" signature
8873 dw bios32_entry_point, 0xf ;; 32 bit physical address
8874 db 0 ;; revision level
8875 ;; length in paragraphs and checksum stored in a word to prevent errors
8876 dw (~(((bios32_entry_point >> 8) + (bios32_entry_point & 0xff) + 0x32) \
8877 & 0xff) << 8) + 0x01
8878 db 0,0,0,0,0 ;; reserved
8880 .align 16
8881 bios32_entry_point:
8882 pushfd
8883 cmp eax, #0x49435024 ;; "$PCI"
8884 jne unknown_service
8885 mov eax, #0x80000000
8886 mov dx, #0x0cf8
8887 out dx, eax
8888 mov dx, #0x0cfc
8889 in eax, dx
8890 #ifdef PCI_FIXED_HOST_BRIDGE
8891 cmp eax, #PCI_FIXED_HOST_BRIDGE
8892 jne unknown_service
8893 #else
8894 ;; say ok if a device is present
8895 cmp eax, #0xffffffff
8896 je unknown_service
8897 #endif
8898 mov ebx, #0x000f0000
8899 mov ecx, #0
8900 mov edx, #pcibios_protected
8901 xor al, al
8902 jmp bios32_end
8903 unknown_service:
8904 mov al, #0x80
8905 bios32_end:
8906 #ifdef BX_QEMU
8907 and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
8908 #endif
8909 popfd
8910 retf
8912 .align 16
8913 pcibios_protected:
8914 pushfd
8916 push esi
8917 push edi
8918 cmp al, #0x01 ;; installation check
8919 jne pci_pro_f02
8920 mov bx, #0x0210
8921 mov cx, #0
8922 mov edx, #0x20494350 ;; "PCI "
8923 mov al, #0x01
8924 jmp pci_pro_ok
8925 pci_pro_f02: ;; find pci device
8926 cmp al, #0x02
8927 jne pci_pro_f03
8928 shl ecx, #16
8929 mov cx, dx
8930 xor bx, bx
8931 mov di, #0x00
8932 pci_pro_devloop:
8933 call pci_pro_select_reg
8934 mov dx, #0x0cfc
8935 in eax, dx
8936 cmp eax, ecx
8937 jne pci_pro_nextdev
8938 cmp si, #0
8939 je pci_pro_ok
8940 dec si
8941 pci_pro_nextdev:
8942 inc bx
8943 cmp bx, #0x0100
8944 jne pci_pro_devloop
8945 mov ah, #0x86
8946 jmp pci_pro_fail
8947 pci_pro_f03: ;; find class code
8948 cmp al, #0x03
8949 jne pci_pro_f08
8950 xor bx, bx
8951 mov di, #0x08
8952 pci_pro_devloop2:
8953 call pci_pro_select_reg
8954 mov dx, #0x0cfc
8955 in eax, dx
8956 shr eax, #8
8957 cmp eax, ecx
8958 jne pci_pro_nextdev2
8959 cmp si, #0
8960 je pci_pro_ok
8961 dec si
8962 pci_pro_nextdev2:
8963 inc bx
8964 cmp bx, #0x0100
8965 jne pci_pro_devloop2
8966 mov ah, #0x86
8967 jmp pci_pro_fail
8968 pci_pro_f08: ;; read configuration byte
8969 cmp al, #0x08
8970 jne pci_pro_f09
8971 call pci_pro_select_reg
8972 push edx
8973 mov dx, di
8974 and dx, #0x03
8975 add dx, #0x0cfc
8976 in al, dx
8977 pop edx
8978 mov cl, al
8979 jmp pci_pro_ok
8980 pci_pro_f09: ;; read configuration word
8981 cmp al, #0x09
8982 jne pci_pro_f0a
8983 call pci_pro_select_reg
8984 push edx
8985 mov dx, di
8986 and dx, #0x02
8987 add dx, #0x0cfc
8988 in ax, dx
8989 pop edx
8990 mov cx, ax
8991 jmp pci_pro_ok
8992 pci_pro_f0a: ;; read configuration dword
8993 cmp al, #0x0a
8994 jne pci_pro_f0b
8995 call pci_pro_select_reg
8996 push edx
8997 mov dx, #0x0cfc
8998 in eax, dx
8999 pop edx
9000 mov ecx, eax
9001 jmp pci_pro_ok
9002 pci_pro_f0b: ;; write configuration byte
9003 cmp al, #0x0b
9004 jne pci_pro_f0c
9005 call pci_pro_select_reg
9006 push edx
9007 mov dx, di
9008 and dx, #0x03
9009 add dx, #0x0cfc
9010 mov al, cl
9011 out dx, al
9012 pop edx
9013 jmp pci_pro_ok
9014 pci_pro_f0c: ;; write configuration word
9015 cmp al, #0x0c
9016 jne pci_pro_f0d
9017 call pci_pro_select_reg
9018 push edx
9019 mov dx, di
9020 and dx, #0x02
9021 add dx, #0x0cfc
9022 mov ax, cx
9023 out dx, ax
9024 pop edx
9025 jmp pci_pro_ok
9026 pci_pro_f0d: ;; write configuration dword
9027 cmp al, #0x0d
9028 jne pci_pro_unknown
9029 call pci_pro_select_reg
9030 push edx
9031 mov dx, #0x0cfc
9032 mov eax, ecx
9033 out dx, eax
9034 pop edx
9035 jmp pci_pro_ok
9036 pci_pro_unknown:
9037 mov ah, #0x81
9038 pci_pro_fail:
9039 pop edi
9040 pop esi
9041 #ifdef BX_QEMU
9042 and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
9043 #endif
9044 popfd
9046 retf
9047 pci_pro_ok:
9048 xor ah, ah
9049 pop edi
9050 pop esi
9051 #ifdef BX_QEMU
9052 and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
9053 #endif
9054 popfd
9056 retf
9058 pci_pro_select_reg:
9059 push edx
9060 mov eax, #0x800000
9061 mov ax, bx
9062 shl eax, #8
9063 and di, #0xff
9064 or ax, di
9065 and al, #0xfc
9066 mov dx, #0x0cf8
9067 out dx, eax
9068 pop edx
9071 use16 386
9073 pcibios_real:
9074 push eax
9075 push dx
9076 mov eax, #0x80000000
9077 mov dx, #0x0cf8
9078 out dx, eax
9079 mov dx, #0x0cfc
9080 in eax, dx
9081 #ifdef PCI_FIXED_HOST_BRIDGE
9082 cmp eax, #PCI_FIXED_HOST_BRIDGE
9083 je pci_present
9084 #else
9085 ;; say ok if a device is present
9086 cmp eax, #0xffffffff
9087 jne pci_present
9088 #endif
9089 pop dx
9090 pop eax
9091 mov ah, #0xff
9094 pci_present:
9095 pop dx
9096 pop eax
9097 cmp al, #0x01 ;; installation check
9098 jne pci_real_f02
9099 mov ax, #0x0001
9100 mov bx, #0x0210
9101 mov cx, #0
9102 mov edx, #0x20494350 ;; "PCI "
9103 mov edi, #0xf0000
9104 mov di, #pcibios_protected
9107 pci_real_f02: ;; find pci device
9108 push esi
9109 push edi
9110 cmp al, #0x02
9111 jne pci_real_f03
9112 shl ecx, #16
9113 mov cx, dx
9114 xor bx, bx
9115 mov di, #0x00
9116 pci_real_devloop:
9117 call pci_real_select_reg
9118 mov dx, #0x0cfc
9119 in eax, dx
9120 cmp eax, ecx
9121 jne pci_real_nextdev
9122 cmp si, #0
9123 je pci_real_ok
9124 dec si
9125 pci_real_nextdev:
9126 inc bx
9127 cmp bx, #0x0100
9128 jne pci_real_devloop
9129 mov dx, cx
9130 shr ecx, #16
9131 mov ax, #0x8602
9132 jmp pci_real_fail
9133 pci_real_f03: ;; find class code
9134 cmp al, #0x03
9135 jne pci_real_f08
9136 xor bx, bx
9137 mov di, #0x08
9138 pci_real_devloop2:
9139 call pci_real_select_reg
9140 mov dx, #0x0cfc
9141 in eax, dx
9142 shr eax, #8
9143 cmp eax, ecx
9144 jne pci_real_nextdev2
9145 cmp si, #0
9146 je pci_real_ok
9147 dec si
9148 pci_real_nextdev2:
9149 inc bx
9150 cmp bx, #0x0100
9151 jne pci_real_devloop2
9152 mov dx, cx
9153 shr ecx, #16
9154 mov ax, #0x8603
9155 jmp pci_real_fail
9156 pci_real_f08: ;; read configuration byte
9157 cmp al, #0x08
9158 jne pci_real_f09
9159 call pci_real_select_reg
9160 push dx
9161 mov dx, di
9162 and dx, #0x03
9163 add dx, #0x0cfc
9164 in al, dx
9165 pop dx
9166 mov cl, al
9167 jmp pci_real_ok
9168 pci_real_f09: ;; read configuration word
9169 cmp al, #0x09
9170 jne pci_real_f0a
9171 call pci_real_select_reg
9172 push dx
9173 mov dx, di
9174 and dx, #0x02
9175 add dx, #0x0cfc
9176 in ax, dx
9177 pop dx
9178 mov cx, ax
9179 jmp pci_real_ok
9180 pci_real_f0a: ;; read configuration dword
9181 cmp al, #0x0a
9182 jne pci_real_f0b
9183 call pci_real_select_reg
9184 push dx
9185 mov dx, #0x0cfc
9186 in eax, dx
9187 pop dx
9188 mov ecx, eax
9189 jmp pci_real_ok
9190 pci_real_f0b: ;; write configuration byte
9191 cmp al, #0x0b
9192 jne pci_real_f0c
9193 call pci_real_select_reg
9194 push dx
9195 mov dx, di
9196 and dx, #0x03
9197 add dx, #0x0cfc
9198 mov al, cl
9199 out dx, al
9200 pop dx
9201 jmp pci_real_ok
9202 pci_real_f0c: ;; write configuration word
9203 cmp al, #0x0c
9204 jne pci_real_f0d
9205 call pci_real_select_reg
9206 push dx
9207 mov dx, di
9208 and dx, #0x02
9209 add dx, #0x0cfc
9210 mov ax, cx
9211 out dx, ax
9212 pop dx
9213 jmp pci_real_ok
9214 pci_real_f0d: ;; write configuration dword
9215 cmp al, #0x0d
9216 jne pci_real_f0e
9217 call pci_real_select_reg
9218 push dx
9219 mov dx, #0x0cfc
9220 mov eax, ecx
9221 out dx, eax
9222 pop dx
9223 jmp pci_real_ok
9224 pci_real_f0e: ;; get irq routing options
9225 cmp al, #0x0e
9226 jne pci_real_unknown
9227 SEG ES
9228 cmp word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9229 jb pci_real_too_small
9230 SEG ES
9231 mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9232 pushf
9233 push ds
9234 push es
9235 push cx
9236 push si
9237 push di
9239 mov si, #pci_routing_table_structure_start
9240 push cs
9241 pop ds
9242 SEG ES
9243 mov cx, [di+2]
9244 SEG ES
9245 mov es, [di+4]
9246 mov di, cx
9247 mov cx, #pci_routing_table_structure_end - pci_routing_table_structure_start
9249 movsb
9250 pop di
9251 pop si
9252 pop cx
9253 pop es
9254 pop ds
9255 popf
9256 mov bx, #(1 << 9) | (1 << 11) ;; irq 9 and 11 are used
9257 jmp pci_real_ok
9258 pci_real_too_small:
9259 SEG ES
9260 mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9261 mov ah, #0x89
9262 jmp pci_real_fail
9264 pci_real_unknown:
9265 mov ah, #0x81
9266 pci_real_fail:
9267 pop edi
9268 pop esi
9271 pci_real_ok:
9272 xor ah, ah
9273 pop edi
9274 pop esi
9278 pci_real_select_reg:
9279 push dx
9280 mov eax, #0x800000
9281 mov ax, bx
9282 shl eax, #8
9283 and di, #0xff
9284 or ax, di
9285 and al, #0xfc
9286 mov dx, #0x0cf8
9287 out dx, eax
9288 pop dx
9291 .align 16
9292 pci_routing_table_structure:
9293 db 0x24, 0x50, 0x49, 0x52 ;; "$PIR" signature
9294 db 0, 1 ;; version
9295 dw 32 + (6 * 16) ;; table size
9296 db 0 ;; PCI interrupt router bus
9297 db 0x08 ;; PCI interrupt router DevFunc
9298 dw 0x0000 ;; PCI exclusive IRQs
9299 dw 0x8086 ;; compatible PCI interrupt router vendor ID
9300 dw 0x7000 ;; compatible PCI interrupt router device ID
9301 dw 0,0 ;; Miniport data
9302 db 0,0,0,0,0,0,0,0,0,0,0 ;; reserved
9303 db 0x07 ;; checksum
9304 pci_routing_table_structure_start:
9305 ;; first slot entry PCI-to-ISA (embedded)
9306 db 0 ;; pci bus number
9307 db 0x08 ;; pci device number (bit 7-3)
9308 db 0x60 ;; link value INTA#: pointer into PCI2ISA config space
9309 dw 0xdef8 ;; IRQ bitmap INTA#
9310 db 0x61 ;; link value INTB#
9311 dw 0xdef8 ;; IRQ bitmap INTB#
9312 db 0x62 ;; link value INTC#
9313 dw 0xdef8 ;; IRQ bitmap INTC#
9314 db 0x63 ;; link value INTD#
9315 dw 0xdef8 ;; IRQ bitmap INTD#
9316 db 0 ;; physical slot (0 = embedded)
9317 db 0 ;; reserved
9318 ;; second slot entry: 1st PCI slot
9319 db 0 ;; pci bus number
9320 db 0x10 ;; pci device number (bit 7-3)
9321 db 0x61 ;; link value INTA#
9322 dw 0xdef8 ;; IRQ bitmap INTA#
9323 db 0x62 ;; link value INTB#
9324 dw 0xdef8 ;; IRQ bitmap INTB#
9325 db 0x63 ;; link value INTC#
9326 dw 0xdef8 ;; IRQ bitmap INTC#
9327 db 0x60 ;; link value INTD#
9328 dw 0xdef8 ;; IRQ bitmap INTD#
9329 db 1 ;; physical slot (0 = embedded)
9330 db 0 ;; reserved
9331 ;; third slot entry: 2nd PCI slot
9332 db 0 ;; pci bus number
9333 db 0x18 ;; pci device number (bit 7-3)
9334 db 0x62 ;; link value INTA#
9335 dw 0xdef8 ;; IRQ bitmap INTA#
9336 db 0x63 ;; link value INTB#
9337 dw 0xdef8 ;; IRQ bitmap INTB#
9338 db 0x60 ;; link value INTC#
9339 dw 0xdef8 ;; IRQ bitmap INTC#
9340 db 0x61 ;; link value INTD#
9341 dw 0xdef8 ;; IRQ bitmap INTD#
9342 db 2 ;; physical slot (0 = embedded)
9343 db 0 ;; reserved
9344 ;; 4th slot entry: 3rd PCI slot
9345 db 0 ;; pci bus number
9346 db 0x20 ;; pci device number (bit 7-3)
9347 db 0x63 ;; link value INTA#
9348 dw 0xdef8 ;; IRQ bitmap INTA#
9349 db 0x60 ;; link value INTB#
9350 dw 0xdef8 ;; IRQ bitmap INTB#
9351 db 0x61 ;; link value INTC#
9352 dw 0xdef8 ;; IRQ bitmap INTC#
9353 db 0x62 ;; link value INTD#
9354 dw 0xdef8 ;; IRQ bitmap INTD#
9355 db 3 ;; physical slot (0 = embedded)
9356 db 0 ;; reserved
9357 ;; 5th slot entry: 4rd PCI slot
9358 db 0 ;; pci bus number
9359 db 0x28 ;; pci device number (bit 7-3)
9360 db 0x60 ;; link value INTA#
9361 dw 0xdef8 ;; IRQ bitmap INTA#
9362 db 0x61 ;; link value INTB#
9363 dw 0xdef8 ;; IRQ bitmap INTB#
9364 db 0x62 ;; link value INTC#
9365 dw 0xdef8 ;; IRQ bitmap INTC#
9366 db 0x63 ;; link value INTD#
9367 dw 0xdef8 ;; IRQ bitmap INTD#
9368 db 4 ;; physical slot (0 = embedded)
9369 db 0 ;; reserved
9370 ;; 6th slot entry: 5rd PCI slot
9371 db 0 ;; pci bus number
9372 db 0x30 ;; pci device number (bit 7-3)
9373 db 0x61 ;; link value INTA#
9374 dw 0xdef8 ;; IRQ bitmap INTA#
9375 db 0x62 ;; link value INTB#
9376 dw 0xdef8 ;; IRQ bitmap INTB#
9377 db 0x63 ;; link value INTC#
9378 dw 0xdef8 ;; IRQ bitmap INTC#
9379 db 0x60 ;; link value INTD#
9380 dw 0xdef8 ;; IRQ bitmap INTD#
9381 db 5 ;; physical slot (0 = embedded)
9382 db 0 ;; reserved
9383 pci_routing_table_structure_end:
9385 #if !BX_ROMBIOS32
9386 pci_irq_list:
9387 db 11, 10, 9, 5;
9389 pcibios_init_sel_reg:
9390 push eax
9391 mov eax, #0x800000
9392 mov ax, bx
9393 shl eax, #8
9394 and dl, #0xfc
9395 or al, dl
9396 mov dx, #0x0cf8
9397 out dx, eax
9398 pop eax
9401 pcibios_init_iomem_bases:
9402 push bp
9403 mov bp, sp
9404 mov eax, #0xe0000000 ;; base for memory init
9405 push eax
9406 mov ax, #0xc000 ;; base for i/o init
9407 push ax
9408 mov ax, #0x0010 ;; start at base address #0
9409 push ax
9410 mov bx, #0x0008
9411 pci_init_io_loop1:
9412 mov dl, #0x00
9413 call pcibios_init_sel_reg
9414 mov dx, #0x0cfc
9415 in ax, dx
9416 cmp ax, #0xffff
9417 jz next_pci_dev
9418 mov dl, #0x04 ;; disable i/o and memory space access
9419 call pcibios_init_sel_reg
9420 mov dx, #0x0cfc
9421 in al, dx
9422 and al, #0xfc
9423 out dx, al
9424 pci_init_io_loop2:
9425 mov dl, [bp-8]
9426 call pcibios_init_sel_reg
9427 mov dx, #0x0cfc
9428 in eax, dx
9429 test al, #0x01
9430 jnz init_io_base
9431 mov ecx, eax
9432 mov eax, #0xffffffff
9433 out dx, eax
9434 in eax, dx
9435 cmp eax, ecx
9436 je next_pci_base
9437 xor eax, #0xffffffff
9438 mov ecx, eax
9439 mov eax, [bp-4]
9440 out dx, eax
9441 add eax, ecx ;; calculate next free mem base
9442 add eax, #0x01000000
9443 and eax, #0xff000000
9444 mov [bp-4], eax
9445 jmp next_pci_base
9446 init_io_base:
9447 mov cx, ax
9448 mov ax, #0xffff
9449 out dx, ax
9450 in ax, dx
9451 cmp ax, cx
9452 je next_pci_base
9453 xor ax, #0xfffe
9454 mov cx, ax
9455 mov ax, [bp-6]
9456 out dx, ax
9457 add ax, cx ;; calculate next free i/o base
9458 add ax, #0x0100
9459 and ax, #0xff00
9460 mov [bp-6], ax
9461 next_pci_base:
9462 mov al, [bp-8]
9463 add al, #0x04
9464 cmp al, #0x28
9465 je enable_iomem_space
9466 mov byte ptr[bp-8], al
9467 jmp pci_init_io_loop2
9468 enable_iomem_space:
9469 mov dl, #0x04 ;; enable i/o and memory space access if available
9470 call pcibios_init_sel_reg
9471 mov dx, #0x0cfc
9472 in al, dx
9473 or al, #0x07
9474 out dx, al
9475 next_pci_dev:
9476 mov byte ptr[bp-8], #0x10
9477 inc bx
9478 cmp bx, #0x0100
9479 jne pci_init_io_loop1
9480 mov sp, bp
9481 pop bp
9484 pcibios_init_set_elcr:
9485 push ax
9486 push cx
9487 mov dx, #0x04d0
9488 test al, #0x08
9489 jz is_master_pic
9490 inc dx
9491 and al, #0x07
9492 is_master_pic:
9493 mov cl, al
9494 mov bl, #0x01
9495 shl bl, cl
9496 in al, dx
9497 or al, bl
9498 out dx, al
9499 pop cx
9500 pop ax
9503 pcibios_init_irqs:
9504 push ds
9505 push bp
9506 mov ax, #0xf000
9507 mov ds, ax
9508 mov dx, #0x04d0 ;; reset ELCR1 + ELCR2
9509 mov al, #0x00
9510 out dx, al
9511 inc dx
9512 out dx, al
9513 mov si, #pci_routing_table_structure
9514 mov bh, [si+8]
9515 mov bl, [si+9]
9516 mov dl, #0x00
9517 call pcibios_init_sel_reg
9518 mov dx, #0x0cfc
9519 in eax, dx
9520 cmp eax, [si+12] ;; check irq router
9521 jne pci_init_end
9522 mov dl, [si+34]
9523 call pcibios_init_sel_reg
9524 push bx ;; save irq router bus + devfunc
9525 mov dx, #0x0cfc
9526 mov ax, #0x8080
9527 out dx, ax ;; reset PIRQ route control
9528 inc dx
9529 inc dx
9530 out dx, ax
9531 mov ax, [si+6]
9532 sub ax, #0x20
9533 shr ax, #4
9534 mov cx, ax
9535 add si, #0x20 ;; set pointer to 1st entry
9536 mov bp, sp
9537 mov ax, #pci_irq_list
9538 push ax
9539 xor ax, ax
9540 push ax
9541 pci_init_irq_loop1:
9542 mov bh, [si]
9543 mov bl, [si+1]
9544 pci_init_irq_loop2:
9545 mov dl, #0x00
9546 call pcibios_init_sel_reg
9547 mov dx, #0x0cfc
9548 in ax, dx
9549 cmp ax, #0xffff
9550 jnz pci_test_int_pin
9551 test bl, #0x07
9552 jz next_pir_entry
9553 jmp next_pci_func
9554 pci_test_int_pin:
9555 mov dl, #0x3c
9556 call pcibios_init_sel_reg
9557 mov dx, #0x0cfd
9558 in al, dx
9559 and al, #0x07
9560 jz next_pci_func
9561 dec al ;; determine pirq reg
9562 mov dl, #0x03
9563 mul al, dl
9564 add al, #0x02
9565 xor ah, ah
9566 mov bx, ax
9567 mov al, [si+bx]
9568 mov dl, al
9569 mov bx, [bp]
9570 call pcibios_init_sel_reg
9571 mov dx, #0x0cfc
9572 and al, #0x03
9573 add dl, al
9574 in al, dx
9575 cmp al, #0x80
9576 jb pirq_found
9577 mov bx, [bp-2] ;; pci irq list pointer
9578 mov al, [bx]
9579 out dx, al
9580 inc bx
9581 mov [bp-2], bx
9582 call pcibios_init_set_elcr
9583 pirq_found:
9584 mov bh, [si]
9585 mov bl, [si+1]
9586 add bl, [bp-3] ;; pci function number
9587 mov dl, #0x3c
9588 call pcibios_init_sel_reg
9589 mov dx, #0x0cfc
9590 out dx, al
9591 next_pci_func:
9592 inc byte ptr[bp-3]
9593 inc bl
9594 test bl, #0x07
9595 jnz pci_init_irq_loop2
9596 next_pir_entry:
9597 add si, #0x10
9598 mov byte ptr[bp-3], #0x00
9599 loop pci_init_irq_loop1
9600 mov sp, bp
9601 pop bx
9602 pci_init_end:
9603 pop bp
9604 pop ds
9606 #endif // BX_ROMBIOS32
9607 #endif // BX_PCIBIOS
9609 #if BX_ROMBIOS32
9610 rombios32_init:
9611 ;; save a20 and enable it
9612 in al, 0x92
9613 push ax
9614 or al, #0x02
9615 out 0x92, al
9617 ;; save SS:SP to the BDA
9618 xor ax, ax
9619 mov ds, ax
9620 mov 0x0469, ss
9621 mov 0x0467, sp
9623 SEG CS
9624 lidt [pmode_IDT_info]
9625 SEG CS
9626 lgdt [rombios32_gdt_48]
9627 ;; set PE bit in CR0
9628 mov eax, cr0
9629 or al, #0x01
9630 mov cr0, eax
9631 ;; start protected mode code: ljmpl 0x10:rombios32_init1
9632 db 0x66, 0xea
9633 dw rombios32_05
9634 dw 0x000f ;; high 16 bit address
9635 dw 0x0010
9637 use32 386
9638 rombios32_05:
9639 ;; init data segments
9640 mov eax, #0x18
9641 mov ds, ax
9642 mov es, ax
9643 mov ss, ax
9644 xor eax, eax
9645 mov fs, ax
9646 mov gs, ax
9649 ;; copy rombios32 code to ram (ram offset = 1MB)
9650 mov esi, #0xfffe0000
9651 mov edi, #0x00040000
9652 mov ecx, #0x10000 / 4
9654 movsd
9656 ;; init the stack pointer
9657 mov esp, #0x00080000
9659 ;; call rombios32 code
9660 mov eax, #0x00040000
9661 call eax
9663 ;; reset the memory (some boot loaders such as syslinux suppose
9664 ;; that the memory is set to zero)
9665 mov edi, #0x00040000
9666 mov ecx, #0x40000 / 4
9667 xor eax, eax
9669 stosd
9671 ;; reset the memory (some boot loaders such as syslinux suppose
9672 ;; that the memory is set to zero)
9673 mov edi, #0x00040000
9674 mov ecx, #0x40000 / 4
9675 xor eax, eax
9676 rep
9677 stosd
9679 ;; return to 16 bit protected mode first
9680 db 0xea
9681 dd rombios32_10
9682 dw 0x20
9684 use16 386
9685 rombios32_10:
9686 ;; restore data segment limits to 0xffff
9687 mov ax, #0x28
9688 mov ds, ax
9689 mov es, ax
9690 mov ss, ax
9691 mov fs, ax
9692 mov gs, ax
9694 ;; reset PE bit in CR0
9695 mov eax, cr0
9696 and al, #0xFE
9697 mov cr0, eax
9699 ;; far jump to flush CPU queue after transition to real mode
9700 JMP_AP(0xf000, rombios32_real_mode)
9702 rombios32_real_mode:
9703 ;; restore IDT to normal real-mode defaults
9704 SEG CS
9705 lidt [rmode_IDT_info]
9707 xor ax, ax
9708 mov ds, ax
9709 mov es, ax
9710 mov fs, ax
9711 mov gs, ax
9713 ;; restore SS:SP from the BDA
9714 mov ss, 0x0469
9715 xor esp, esp
9716 mov sp, 0x0467
9717 ;; restore a20
9718 pop ax
9719 out 0x92, al
9722 rombios32_gdt_48:
9723 dw 0x30
9724 dw rombios32_gdt
9725 dw 0x000f
9727 rombios32_gdt:
9728 dw 0, 0, 0, 0
9729 dw 0, 0, 0, 0
9730 dw 0xffff, 0, 0x9b00, 0x00cf ; 32 bit flat code segment (0x10)
9731 dw 0xffff, 0, 0x9300, 0x00cf ; 32 bit flat data segment (0x18)
9732 dw 0xffff, 0, 0x9b0f, 0x0000 ; 16 bit code segment base=0xf0000 limit=0xffff
9733 dw 0xffff, 0, 0x9300, 0x0000 ; 16 bit data segment base=0x0 limit=0xffff
9734 #endif
9737 ; parallel port detection: base address in DX, index in BX, timeout in CL
9738 detect_parport:
9739 push dx
9740 add dx, #2
9741 in al, dx
9742 and al, #0xdf ; clear input mode
9743 out dx, al
9744 pop dx
9745 mov al, #0xaa
9746 out dx, al
9747 in al, dx
9748 cmp al, #0xaa
9749 jne no_parport
9750 push bx
9751 shl bx, #1
9752 mov [bx+0x408], dx ; Parallel I/O address
9753 pop bx
9754 mov [bx+0x478], cl ; Parallel printer timeout
9755 inc bx
9756 no_parport:
9759 ; serial port detection: base address in DX, index in BX, timeout in CL
9760 detect_serial:
9761 push dx
9762 inc dx
9763 mov al, #0x02
9764 out dx, al
9765 in al, dx
9766 cmp al, #0x02
9767 jne no_serial
9768 inc dx
9769 in al, dx
9770 cmp al, #0x02
9771 jne no_serial
9772 dec dx
9773 xor al, al
9774 out dx, al
9775 pop dx
9776 push bx
9777 shl bx, #1
9778 mov [bx+0x400], dx ; Serial I/O address
9779 pop bx
9780 mov [bx+0x47c], cl ; Serial timeout
9781 inc bx
9783 no_serial:
9784 pop dx
9787 rom_checksum:
9788 push ax
9789 push bx
9790 push cx
9791 xor ax, ax
9792 xor bx, bx
9793 xor cx, cx
9794 mov ch, [2]
9795 shl cx, #1
9796 checksum_loop:
9797 add al, [bx]
9798 inc bx
9799 loop checksum_loop
9800 and al, #0xff
9801 pop cx
9802 pop bx
9803 pop ax
9807 ;; We need a copy of this string, but we are not actually a PnP BIOS,
9808 ;; so make sure it is *not* aligned, so OSes will not see it if they scan.
9809 .align 16
9810 db 0
9811 pnp_string:
9812 .ascii "$PnP"
9815 rom_scan:
9816 ;; Scan for existence of valid expansion ROMS.
9817 ;; Video ROM: from 0xC0000..0xC7FFF in 2k increments
9818 ;; General ROM: from 0xC8000..0xDFFFF in 2k increments
9819 ;; System ROM: only 0xE0000
9821 ;; Header:
9822 ;; Offset Value
9823 ;; 0 0x55
9824 ;; 1 0xAA
9825 ;; 2 ROM length in 512-byte blocks
9826 ;; 3 ROM initialization entry point (FAR CALL)
9828 mov cx, #0xc000
9829 rom_scan_loop:
9830 mov ds, cx
9831 mov ax, #0x0004 ;; start with increment of 4 (512-byte) blocks = 2k
9832 cmp [0], #0xAA55 ;; look for signature
9833 jne rom_scan_increment
9834 call rom_checksum
9835 jnz rom_scan_increment
9836 mov al, [2] ;; change increment to ROM length in 512-byte blocks
9838 ;; We want our increment in 512-byte quantities, rounded to
9839 ;; the nearest 2k quantity, since we only scan at 2k intervals.
9840 test al, #0x03
9841 jz block_count_rounded
9842 and al, #0xfc ;; needs rounding up
9843 add al, #0x04
9844 block_count_rounded:
9846 xor bx, bx ;; Restore DS back to 0000:
9847 mov ds, bx
9848 push ax ;; Save AX
9849 push di ;; Save DI
9850 ;; Push addr of ROM entry point
9851 push cx ;; Push seg
9852 push #0x0003 ;; Push offset
9854 ;; Point ES:DI at "$PnP", which tells the ROM that we are a PnP BIOS.
9855 ;; That should stop it grabbing INT 19h; we will use its BEV instead.
9856 mov ax, #0xf000
9857 mov es, ax
9858 lea di, pnp_string
9860 mov bp, sp ;; Call ROM init routine using seg:off on stack
9861 db 0xff ;; call_far ss:[bp+0]
9862 db 0x5e
9863 db 0
9864 cli ;; In case expansion ROM BIOS turns IF on
9865 add sp, #2 ;; Pop offset value
9866 pop cx ;; Pop seg value (restore CX)
9868 ;; Look at the ROM's PnP Expansion header. Properly, we're supposed
9869 ;; to init all the ROMs and then go back and build an IPL table of
9870 ;; all the bootable devices, but we can get away with one pass.
9871 mov ds, cx ;; ROM base
9872 mov bx, 0x001a ;; 0x1A is the offset into ROM header that contains...
9873 mov ax, [bx] ;; the offset of PnP expansion header, where...
9874 cmp ax, #0x5024 ;; we look for signature "$PnP"
9875 jne no_bev
9876 mov ax, 2[bx]
9877 cmp ax, #0x506e
9878 jne no_bev
9879 mov ax, 0x1a[bx] ;; 0x1A is also the offset into the expansion header of...
9880 cmp ax, #0x0000 ;; the Bootstrap Entry Vector, or zero if there is none.
9881 je no_bev
9883 ;; Found a device that thinks it can boot the system. Record its BEV.
9884 mov bx, #IPL_SEG ;; Go to the segment where the IPL table lives
9885 mov ds, bx
9886 mov bx, IPL_COUNT_OFFSET ;; Read the number of entries so far
9887 cmp bx, #IPL_TABLE_ENTRIES
9888 je no_bev ;; Get out if the table is full
9889 shl bx, #0x4 ;; Turn count into offset (entries are 16 bytes)
9890 mov 0[bx], #0x80 ;; This entry is a BEV device
9891 mov 6[bx], cx ;; Build a far pointer from the segment...
9892 mov 4[bx], ax ;; and the offset
9893 shr bx, #0x4 ;; Turn the offset back into a count
9894 inc bx ;; We have one more entry now
9895 mov IPL_COUNT_OFFSET, bx ;; Remember that.
9897 no_bev:
9898 pop di ;; Restore DI
9899 pop ax ;; Restore AX
9900 rom_scan_increment:
9901 shl ax, #5 ;; convert 512-bytes blocks to 16-byte increments
9902 ;; because the segment selector is shifted left 4 bits.
9903 add cx, ax
9904 cmp cx, #0xe000
9905 jbe rom_scan_loop
9907 xor ax, ax ;; Restore DS back to 0000:
9908 mov ds, ax
9911 ;; for 'C' strings and other data, insert them here with
9912 ;; a the following hack:
9913 ;; DATA_SEG_DEFS_HERE
9916 ;; the following area can be used to write dynamically generated tables
9917 .align 16
9918 bios_table_area_start:
9919 dd 0xaafb4442
9920 dd bios_table_area_end - bios_table_area_start - 8;
9922 ;--------
9923 ;- POST -
9924 ;--------
9925 .org 0xe05b ; POST Entry Point
9926 bios_table_area_end:
9927 post:
9929 xor ax, ax
9931 ;; first reset the DMA controllers
9932 out 0x0d,al
9933 out 0xda,al
9935 ;; then initialize the DMA controllers
9936 mov al, #0xC0
9937 out 0xD6, al ; cascade mode of channel 4 enabled
9938 mov al, #0x00
9939 out 0xD4, al ; unmask channel 4
9941 ;; Examine CMOS shutdown status.
9942 mov AL, #0x0f
9943 out 0x70, AL
9944 in AL, 0x71
9946 ;; backup status
9947 mov bl, al
9949 ;; Reset CMOS shutdown status.
9950 mov AL, #0x0f
9951 out 0x70, AL ; select CMOS register Fh
9952 mov AL, #0x00
9953 out 0x71, AL ; set shutdown action to normal
9955 ;; Examine CMOS shutdown status.
9956 mov al, bl
9958 ;; 0x00, 0x09, 0x0D+ = normal startup
9959 cmp AL, #0x00
9960 jz normal_post
9961 cmp AL, #0x0d
9962 jae normal_post
9963 cmp AL, #0x09
9964 je normal_post
9966 ;; 0x05 = eoi + jmp via [0x40:0x67] jump
9967 cmp al, #0x05
9968 je eoi_jmp_post
9970 ;; Examine CMOS shutdown status.
9971 ;; 0x01,0x02,0x03,0x04,0x06,0x07,0x08, 0x0a, 0x0b, 0x0c = Unimplemented shutdown status.
9972 push bx
9973 call _shutdown_status_panic
9975 #if 0
9976 HALT(__LINE__)
9978 ;#if 0
9979 ; 0xb0, 0x20, /* mov al, #0x20 */
9980 ; 0xe6, 0x20, /* out 0x20, al ;send EOI to PIC */
9981 ;#endif
9983 pop es
9984 pop ds
9985 popa
9986 iret
9987 #endif
9989 normal_post:
9990 ; case 0: normal startup
9993 mov ax, #0xfffe
9994 mov sp, ax
9995 xor ax, ax
9996 mov ds, ax
9997 mov ss, ax
9999 ;; zero out BIOS data area (40:00..40:ff)
10000 mov es, ax
10001 mov cx, #0x0080 ;; 128 words
10002 mov di, #0x0400
10005 stosw
10007 call _log_bios_start
10009 ;; set all interrupts to default handler
10010 xor bx, bx ;; offset index
10011 mov cx, #0x0100 ;; counter (256 interrupts)
10012 mov ax, #dummy_iret_handler
10013 mov dx, #0xF000
10015 post_default_ints:
10016 mov [bx], ax
10017 inc bx
10018 inc bx
10019 mov [bx], dx
10020 inc bx
10021 inc bx
10022 loop post_default_ints
10024 ;; set vector 0x79 to zero
10025 ;; this is used by 'gardian angel' protection system
10026 SET_INT_VECTOR(0x79, #0, #0)
10028 ;; base memory in K 40:13 (word)
10029 mov ax, #BASE_MEM_IN_K
10030 mov 0x0413, ax
10033 ;; Manufacturing Test 40:12
10034 ;; zerod out above
10036 ;; Warm Boot Flag 0040:0072
10037 ;; value of 1234h = skip memory checks
10038 ;; zerod out above
10041 ;; Printer Services vector
10042 SET_INT_VECTOR(0x17, #0xF000, #int17_handler)
10044 ;; Bootstrap failure vector
10045 SET_INT_VECTOR(0x18, #0xF000, #int18_handler)
10047 ;; Bootstrap Loader vector
10048 SET_INT_VECTOR(0x19, #0xF000, #int19_handler)
10050 ;; User Timer Tick vector
10051 SET_INT_VECTOR(0x1c, #0xF000, #int1c_handler)
10053 ;; Memory Size Check vector
10054 SET_INT_VECTOR(0x12, #0xF000, #int12_handler)
10056 ;; Equipment Configuration Check vector
10057 SET_INT_VECTOR(0x11, #0xF000, #int11_handler)
10059 ;; System Services
10060 SET_INT_VECTOR(0x15, #0xF000, #int15_handler)
10062 ;; EBDA setup
10063 call ebda_post
10065 ;; PIT setup
10066 SET_INT_VECTOR(0x08, #0xF000, #int08_handler)
10067 ;; int 1C already points at dummy_iret_handler (above)
10068 mov al, #0x34 ; timer0: binary count, 16bit count, mode 2
10069 out 0x43, al
10070 mov al, #0x00 ; maximum count of 0000H = 18.2Hz
10071 out 0x40, al
10072 out 0x40, al
10074 ;; Keyboard
10075 SET_INT_VECTOR(0x09, #0xF000, #int09_handler)
10076 SET_INT_VECTOR(0x16, #0xF000, #int16_handler)
10078 xor ax, ax
10079 mov ds, ax
10080 mov 0x0417, al /* keyboard shift flags, set 1 */
10081 mov 0x0418, al /* keyboard shift flags, set 2 */
10082 mov 0x0419, al /* keyboard alt-numpad work area */
10083 mov 0x0471, al /* keyboard ctrl-break flag */
10084 mov 0x0497, al /* keyboard status flags 4 */
10085 mov al, #0x10
10086 mov 0x0496, al /* keyboard status flags 3 */
10089 /* keyboard head of buffer pointer */
10090 mov bx, #0x001E
10091 mov 0x041A, bx
10093 /* keyboard end of buffer pointer */
10094 mov 0x041C, bx
10096 /* keyboard pointer to start of buffer */
10097 mov bx, #0x001E
10098 mov 0x0480, bx
10100 /* keyboard pointer to end of buffer */
10101 mov bx, #0x003E
10102 mov 0x0482, bx
10104 /* init the keyboard */
10105 call _keyboard_init
10107 ;; mov CMOS Equipment Byte to BDA Equipment Word
10108 mov ax, 0x0410
10109 mov al, #0x14
10110 out 0x70, al
10111 in al, 0x71
10112 mov 0x0410, ax
10115 ;; Parallel setup
10116 SET_INT_VECTOR(0x0F, #0xF000, #dummy_iret_handler)
10117 xor ax, ax
10118 mov ds, ax
10119 xor bx, bx
10120 mov cl, #0x14 ; timeout value
10121 mov dx, #0x378 ; Parallel I/O address, port 1
10122 call detect_parport
10123 mov dx, #0x278 ; Parallel I/O address, port 2
10124 call detect_parport
10125 shl bx, #0x0e
10126 mov ax, 0x410 ; Equipment word bits 14..15 determing # parallel ports
10127 and ax, #0x3fff
10128 or ax, bx ; set number of parallel ports
10129 mov 0x410, ax
10131 ;; Serial setup
10132 SET_INT_VECTOR(0x0C, #0xF000, #dummy_iret_handler)
10133 SET_INT_VECTOR(0x14, #0xF000, #int14_handler)
10134 xor bx, bx
10135 mov cl, #0x0a ; timeout value
10136 mov dx, #0x03f8 ; Serial I/O address, port 1
10137 call detect_serial
10138 mov dx, #0x02f8 ; Serial I/O address, port 2
10139 call detect_serial
10140 mov dx, #0x03e8 ; Serial I/O address, port 3
10141 call detect_serial
10142 mov dx, #0x02e8 ; Serial I/O address, port 4
10143 call detect_serial
10144 shl bx, #0x09
10145 mov ax, 0x410 ; Equipment word bits 9..11 determing # serial ports
10146 and ax, #0xf1ff
10147 or ax, bx ; set number of serial port
10148 mov 0x410, ax
10150 ;; CMOS RTC
10151 SET_INT_VECTOR(0x1A, #0xF000, #int1a_handler)
10152 SET_INT_VECTOR(0x4A, #0xF000, #dummy_iret_handler)
10153 SET_INT_VECTOR(0x70, #0xF000, #int70_handler)
10154 ;; BIOS DATA AREA 0x4CE ???
10155 call timer_tick_post
10157 ;; PS/2 mouse setup
10158 SET_INT_VECTOR(0x74, #0xF000, #int74_handler)
10160 ;; IRQ13 (FPU exception) setup
10161 SET_INT_VECTOR(0x75, #0xF000, #int75_handler)
10163 ;; Video setup
10164 SET_INT_VECTOR(0x10, #0xF000, #int10_handler)
10166 ;; PIC
10167 mov al, #0x11 ; send initialisation commands
10168 out 0x20, al
10169 out 0xa0, al
10170 mov al, #0x08
10171 out 0x21, al
10172 mov al, #0x70
10173 out 0xa1, al
10174 mov al, #0x04
10175 out 0x21, al
10176 mov al, #0x02
10177 out 0xa1, al
10178 mov al, #0x01
10179 out 0x21, al
10180 out 0xa1, al
10181 mov al, #0xb8
10182 out 0x21, AL ;master pic: unmask IRQ 0, 1, 2, 6
10183 #if BX_USE_PS2_MOUSE
10184 mov al, #0x8f
10185 #else
10186 mov al, #0x9f
10187 #endif
10188 out 0xa1, AL ;slave pic: unmask IRQ 12, 13, 14
10190 #if BX_ROMBIOS32
10191 call rombios32_init
10192 #else
10193 call pcibios_init_iomem_bases
10194 call pcibios_init_irqs
10195 #endif
10197 call _init_boot_vectors
10199 call rom_scan
10201 call _print_bios_banner
10204 ;; Floppy setup
10206 call floppy_drive_post
10208 #if BX_USE_ATADRV
10211 ;; Hard Drive setup
10213 call hard_drive_post
10216 ;; ATA/ATAPI driver setup
10218 call _ata_init
10219 call _ata_detect
10221 #else // BX_USE_ATADRV
10224 ;; Hard Drive setup
10226 call hard_drive_post
10228 #endif // BX_USE_ATADRV
10230 #if BX_ELTORITO_BOOT
10232 ;; eltorito floppy/harddisk emulation from cd
10234 call _cdemu_init
10236 #endif // BX_ELTORITO_BOOT
10238 sti ;; enable interrupts
10239 int #0x19
10242 .org 0xe2c3 ; NMI Handler Entry Point
10243 nmi:
10244 ;; FIXME the NMI handler should not panic
10245 ;; but iret when called from int75 (fpu exception)
10246 call _nmi_handler_msg
10247 iret
10249 int75_handler:
10250 out 0xf0, al // clear irq13
10251 call eoi_both_pics // clear interrupt
10252 int 2 // legacy nmi call
10253 iret
10255 ;-------------------------------------------
10256 ;- INT 13h Fixed Disk Services Entry Point -
10257 ;-------------------------------------------
10258 .org 0xe3fe ; INT 13h Fixed Disk Services Entry Point
10259 int13_handler:
10260 //JMPL(int13_relocated)
10261 jmp int13_relocated
10263 .org 0xe401 ; Fixed Disk Parameter Table
10265 ;----------
10266 ;- INT19h -
10267 ;----------
10268 .org 0xe6f2 ; INT 19h Boot Load Service Entry Point
10269 int19_handler:
10271 jmp int19_relocated
10272 ;-------------------------------------------
10273 ;- System BIOS Configuration Data Table
10274 ;-------------------------------------------
10275 .org BIOS_CONFIG_TABLE
10276 db 0x08 ; Table size (bytes) -Lo
10277 db 0x00 ; Table size (bytes) -Hi
10278 db SYS_MODEL_ID
10279 db SYS_SUBMODEL_ID
10280 db BIOS_REVISION
10281 ; Feature byte 1
10282 ; b7: 1=DMA channel 3 used by hard disk
10283 ; b6: 1=2 interrupt controllers present
10284 ; b5: 1=RTC present
10285 ; b4: 1=BIOS calls int 15h/4Fh every key
10286 ; b3: 1=wait for extern event supported (Int 15h/41h)
10287 ; b2: 1=extended BIOS data area used
10288 ; b1: 0=AT or ESDI bus, 1=MicroChannel
10289 ; b0: 1=Dual bus (MicroChannel + ISA)
10290 db (0 << 7) | \
10291 (1 << 6) | \
10292 (1 << 5) | \
10293 (BX_CALL_INT15_4F << 4) | \
10294 (0 << 3) | \
10295 (BX_USE_EBDA << 2) | \
10296 (0 << 1) | \
10297 (0 << 0)
10298 ; Feature byte 2
10299 ; b7: 1=32-bit DMA supported
10300 ; b6: 1=int16h, function 9 supported
10301 ; b5: 1=int15h/C6h (get POS data) supported
10302 ; b4: 1=int15h/C7h (get mem map info) supported
10303 ; b3: 1=int15h/C8h (en/dis CPU) supported
10304 ; b2: 1=non-8042 kb controller
10305 ; b1: 1=data streaming supported
10306 ; b0: reserved
10307 db (0 << 7) | \
10308 (1 << 6) | \
10309 (0 << 5) | \
10310 (0 << 4) | \
10311 (0 << 3) | \
10312 (0 << 2) | \
10313 (0 << 1) | \
10314 (0 << 0)
10315 ; Feature byte 3
10316 ; b7: not used
10317 ; b6: reserved
10318 ; b5: reserved
10319 ; b4: POST supports ROM-to-RAM enable/disable
10320 ; b3: SCSI on system board
10321 ; b2: info panel installed
10322 ; b1: Initial Machine Load (IML) system - BIOS on disk
10323 ; b0: SCSI supported in IML
10324 db 0x00
10325 ; Feature byte 4
10326 ; b7: IBM private
10327 ; b6: EEPROM present
10328 ; b5-3: ABIOS presence (011 = not supported)
10329 ; b2: private
10330 ; b1: memory split above 16Mb supported
10331 ; b0: POSTEXT directly supported by POST
10332 db 0x00
10333 ; Feature byte 5 (IBM)
10334 ; b1: enhanced mouse
10335 ; b0: flash EPROM
10336 db 0x00
10340 .org 0xe729 ; Baud Rate Generator Table
10342 ;----------
10343 ;- INT14h -
10344 ;----------
10345 .org 0xe739 ; INT 14h Serial Communications Service Entry Point
10346 int14_handler:
10347 push ds
10348 pusha
10349 xor ax, ax
10350 mov ds, ax
10351 call _int14_function
10352 popa
10353 pop ds
10354 iret
10357 ;----------------------------------------
10358 ;- INT 16h Keyboard Service Entry Point -
10359 ;----------------------------------------
10360 .org 0xe82e
10361 int16_handler:
10364 push ds
10365 pushf
10366 pusha
10368 cmp ah, #0x00
10369 je int16_F00
10370 cmp ah, #0x10
10371 je int16_F00
10373 mov bx, #0xf000
10374 mov ds, bx
10375 call _int16_function
10376 popa
10377 popf
10378 pop ds
10379 jz int16_zero_set
10381 int16_zero_clear:
10382 push bp
10383 mov bp, sp
10384 //SEG SS
10385 and BYTE [bp + 0x06], #0xbf
10386 pop bp
10387 iret
10389 int16_zero_set:
10390 push bp
10391 mov bp, sp
10392 //SEG SS
10393 or BYTE [bp + 0x06], #0x40
10394 pop bp
10395 iret
10397 int16_F00:
10398 mov bx, #0x0040
10399 mov ds, bx
10401 int16_wait_for_key:
10403 mov bx, 0x001a
10404 cmp bx, 0x001c
10405 jne int16_key_found
10408 #if 0
10409 /* no key yet, call int 15h, function AX=9002 */
10410 0x50, /* push AX */
10411 0xb8, 0x02, 0x90, /* mov AX, #0x9002 */
10412 0xcd, 0x15, /* int 15h */
10413 0x58, /* pop AX */
10414 0xeb, 0xea, /* jmp WAIT_FOR_KEY */
10415 #endif
10416 jmp int16_wait_for_key
10418 int16_key_found:
10419 mov bx, #0xf000
10420 mov ds, bx
10421 call _int16_function
10422 popa
10423 popf
10424 pop ds
10425 #if 0
10426 /* notify int16 complete w/ int 15h, function AX=9102 */
10427 0x50, /* push AX */
10428 0xb8, 0x02, 0x91, /* mov AX, #0x9102 */
10429 0xcd, 0x15, /* int 15h */
10430 0x58, /* pop AX */
10431 #endif
10432 iret
10436 ;-------------------------------------------------
10437 ;- INT09h : Keyboard Hardware Service Entry Point -
10438 ;-------------------------------------------------
10439 .org 0xe987
10440 int09_handler:
10442 push ax
10444 mov al, #0xAD ;;disable keyboard
10445 out #0x64, al
10447 mov al, #0x0B
10448 out #0x20, al
10449 in al, #0x20
10450 and al, #0x02
10451 jz int09_finish
10453 in al, #0x60 ;;read key from keyboard controller
10455 push ds
10456 pusha
10457 #ifdef BX_CALL_INT15_4F
10458 mov ah, #0x4f ;; allow for keyboard intercept
10460 int #0x15
10461 jnc int09_done
10462 #endif
10464 ;; check for extended key
10465 cmp al, #0xe0
10466 jne int09_check_pause
10467 xor ax, ax
10468 mov ds, ax
10469 mov al, BYTE [0x496] ;; mf2_state |= 0x02
10470 or al, #0x02
10471 mov BYTE [0x496], al
10472 jmp int09_done
10474 int09_check_pause: ;; check for pause key
10475 cmp al, #0xe1
10476 jne int09_process_key
10477 xor ax, ax
10478 mov ds, ax
10479 mov al, BYTE [0x496] ;; mf2_state |= 0x01
10480 or al, #0x01
10481 mov BYTE [0x496], al
10482 jmp int09_done
10484 int09_process_key:
10485 mov bx, #0xf000
10486 mov ds, bx
10487 call _int09_function
10489 int09_done:
10490 popa
10491 pop ds
10493 call eoi_master_pic
10495 int09_finish:
10496 mov al, #0xAE ;;enable keyboard
10497 out #0x64, al
10498 pop ax
10499 iret
10502 ;----------------------------------------
10503 ;- INT 13h Diskette Service Entry Point -
10504 ;----------------------------------------
10505 .org 0xec59
10506 int13_diskette:
10507 jmp int13_noeltorito
10509 ;---------------------------------------------
10510 ;- INT 0Eh Diskette Hardware ISR Entry Point -
10511 ;---------------------------------------------
10512 .org 0xef57 ; INT 0Eh Diskette Hardware ISR Entry Point
10513 int0e_handler:
10514 push ax
10515 push dx
10516 mov dx, #0x03f4
10517 in al, dx
10518 and al, #0xc0
10519 cmp al, #0xc0
10520 je int0e_normal
10521 mov dx, #0x03f5
10522 mov al, #0x08 ; sense interrupt status
10523 out dx, al
10524 int0e_loop1:
10525 mov dx, #0x03f4
10526 in al, dx
10527 and al, #0xc0
10528 cmp al, #0xc0
10529 jne int0e_loop1
10530 int0e_loop2:
10531 mov dx, #0x03f5
10532 in al, dx
10533 mov dx, #0x03f4
10534 in al, dx
10535 and al, #0xc0
10536 cmp al, #0xc0
10537 je int0e_loop2
10538 int0e_normal:
10539 push ds
10540 xor ax, ax ;; segment 0000
10541 mov ds, ax
10542 call eoi_master_pic
10543 mov al, 0x043e
10544 or al, #0x80 ;; diskette interrupt has occurred
10545 mov 0x043e, al
10546 pop ds
10547 pop dx
10548 pop ax
10549 iret
10552 .org 0xefc7 ; Diskette Controller Parameter Table
10553 diskette_param_table:
10554 ;; Since no provisions are made for multiple drive types, most
10555 ;; values in this table are ignored. I set parameters for 1.44M
10556 ;; floppy here
10557 db 0xAF
10558 db 0x02 ;; head load time 0000001, DMA used
10559 db 0x25
10560 db 0x02
10561 db 18
10562 db 0x1B
10563 db 0xFF
10564 db 0x6C
10565 db 0xF6
10566 db 0x0F
10567 db 0x08
10570 ;----------------------------------------
10571 ;- INT17h : Printer Service Entry Point -
10572 ;----------------------------------------
10573 .org 0xefd2
10574 int17_handler:
10575 push ds
10576 pusha
10577 xor ax, ax
10578 mov ds, ax
10579 call _int17_function
10580 popa
10581 pop ds
10582 iret
10584 diskette_param_table2:
10585 ;; New diskette parameter table adding 3 parameters from IBM
10586 ;; Since no provisions are made for multiple drive types, most
10587 ;; values in this table are ignored. I set parameters for 1.44M
10588 ;; floppy here
10589 db 0xAF
10590 db 0x02 ;; head load time 0000001, DMA used
10591 db 0x25
10592 db 0x02
10593 db 18
10594 db 0x1B
10595 db 0xFF
10596 db 0x6C
10597 db 0xF6
10598 db 0x0F
10599 db 0x08
10600 db 79 ;; maximum track
10601 db 0 ;; data transfer rate
10602 db 4 ;; drive type in cmos
10604 .org 0xf045 ; INT 10 Functions 0-Fh Entry Point
10605 HALT(__LINE__)
10606 iret
10608 ;----------
10609 ;- INT10h -
10610 ;----------
10611 .org 0xf065 ; INT 10h Video Support Service Entry Point
10612 int10_handler:
10613 ;; dont do anything, since the VGA BIOS handles int10h requests
10614 iret
10616 .org 0xf0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
10618 ;----------
10619 ;- INT12h -
10620 ;----------
10621 .org 0xf841 ; INT 12h Memory Size Service Entry Point
10622 ; ??? different for Pentium (machine check)?
10623 int12_handler:
10624 push ds
10625 mov ax, #0x0040
10626 mov ds, ax
10627 mov ax, 0x0013
10628 pop ds
10629 iret
10631 ;----------
10632 ;- INT11h -
10633 ;----------
10634 .org 0xf84d ; INT 11h Equipment List Service Entry Point
10635 int11_handler:
10636 push ds
10637 mov ax, #0x0040
10638 mov ds, ax
10639 mov ax, 0x0010
10640 pop ds
10641 iret
10643 ;----------
10644 ;- INT15h -
10645 ;----------
10646 .org 0xf859 ; INT 15h System Services Entry Point
10647 int15_handler:
10648 pushf
10649 #if BX_APM
10650 cmp ah, #0x53
10651 je apm_call
10652 #endif
10653 push ds
10654 push es
10655 cmp ah, #0x86
10656 je int15_handler32
10657 cmp ah, #0xE8
10658 je int15_handler32
10659 pusha
10660 #if BX_USE_PS2_MOUSE
10661 cmp ah, #0xC2
10662 je int15_handler_mouse
10663 #endif
10664 call _int15_function
10665 int15_handler_mouse_ret:
10666 popa
10667 int15_handler32_ret:
10668 pop es
10669 pop ds
10670 popf
10671 jmp iret_modify_cf
10672 #if BX_APM
10673 apm_call:
10674 jmp _apmreal_entry
10675 #endif
10677 #if BX_USE_PS2_MOUSE
10678 int15_handler_mouse:
10679 call _int15_function_mouse
10680 jmp int15_handler_mouse_ret
10681 #endif
10683 int15_handler32:
10684 pushad
10685 call _int15_function32
10686 popad
10687 jmp int15_handler32_ret
10689 ;; Protected mode IDT descriptor
10691 ;; I just make the limit 0, so the machine will shutdown
10692 ;; if an exception occurs during protected mode memory
10693 ;; transfers.
10695 ;; Set base to f0000 to correspond to beginning of BIOS,
10696 ;; in case I actually define an IDT later
10697 ;; Set limit to 0
10699 pmode_IDT_info:
10700 dw 0x0000 ;; limit 15:00
10701 dw 0x0000 ;; base 15:00
10702 db 0x0f ;; base 23:16
10704 ;; Real mode IDT descriptor
10706 ;; Set to typical real-mode values.
10707 ;; base = 000000
10708 ;; limit = 03ff
10710 rmode_IDT_info:
10711 dw 0x03ff ;; limit 15:00
10712 dw 0x0000 ;; base 15:00
10713 db 0x00 ;; base 23:16
10716 ;----------
10717 ;- INT1Ah -
10718 ;----------
10719 .org 0xfe6e ; INT 1Ah Time-of-day Service Entry Point
10720 int1a_handler:
10721 #if BX_PCIBIOS
10722 cmp ah, #0xb1
10723 jne int1a_normal
10724 call pcibios_real
10725 jc pcibios_error
10726 retf 2
10727 pcibios_error:
10728 mov bl, ah
10729 mov ah, #0xb1
10730 push ds
10731 pusha
10732 mov ax, ss ; set readable descriptor to ds, for calling pcibios
10733 mov ds, ax ; on 16bit protected mode.
10734 jmp int1a_callfunction
10735 int1a_normal:
10736 #endif
10737 push ds
10738 pusha
10739 xor ax, ax
10740 mov ds, ax
10741 int1a_callfunction:
10742 call _int1a_function
10743 popa
10744 pop ds
10745 iret
10748 ;; int70h: IRQ8 - CMOS RTC
10750 int70_handler:
10751 push ds
10752 pushad
10753 xor ax, ax
10754 mov ds, ax
10755 call _int70_function
10756 popad
10757 pop ds
10758 iret
10760 ;---------
10761 ;- INT08 -
10762 ;---------
10763 .org 0xfea5 ; INT 08h System Timer ISR Entry Point
10764 int08_handler:
10766 push eax
10767 push ds
10768 xor ax, ax
10769 mov ds, ax
10771 ;; time to turn off drive(s)?
10772 mov al,0x0440
10773 or al,al
10774 jz int08_floppy_off
10775 dec al
10776 mov 0x0440,al
10777 jnz int08_floppy_off
10778 ;; turn motor(s) off
10779 push dx
10780 mov dx,#0x03f2
10781 in al,dx
10782 and al,#0xcf
10783 out dx,al
10784 pop dx
10785 int08_floppy_off:
10787 mov eax, 0x046c ;; get ticks dword
10788 inc eax
10790 ;; compare eax to one days worth of timer ticks at 18.2 hz
10791 cmp eax, #0x001800B0
10792 jb int08_store_ticks
10793 ;; there has been a midnight rollover at this point
10794 xor eax, eax ;; zero out counter
10795 inc BYTE 0x0470 ;; increment rollover flag
10797 int08_store_ticks:
10798 mov 0x046c, eax ;; store new ticks dword
10799 ;; chain to user timer tick INT #0x1c
10800 //pushf
10801 //;; call_ep [ds:loc]
10802 //CALL_EP( 0x1c << 2 )
10803 int #0x1c
10805 call eoi_master_pic
10806 pop ds
10807 pop eax
10808 iret
10810 .org 0xfef3 ; Initial Interrupt Vector Offsets Loaded by POST
10813 .org 0xff00
10814 .ascii BIOS_COPYRIGHT_STRING
10816 ;------------------------------------------------
10817 ;- IRET Instruction for Dummy Interrupt Handler -
10818 ;------------------------------------------------
10819 .org 0xff53 ; IRET Instruction for Dummy Interrupt Handler
10820 dummy_iret_handler:
10821 iret
10823 .org 0xff54 ; INT 05h Print Screen Service Entry Point
10824 HALT(__LINE__)
10825 iret
10827 .org 0xfff0 ; Power-up Entry Point
10828 jmp 0xf000:post
10830 .org 0xfff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
10831 .ascii BIOS_BUILD_DATE
10833 .org 0xfffe ; System Model ID
10834 db SYS_MODEL_ID
10835 db 0x00 ; filler
10837 .org 0xfa6e ;; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
10838 ASM_END
10840 * This font comes from the fntcol16.zip package (c) by Joseph Gil
10841 * found at ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip
10842 * This font is public domain
10844 static Bit8u vgafont8[128*8]=
10846 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
10847 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e,
10848 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e,
10849 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00,
10850 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00,
10851 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c,
10852 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c,
10853 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00,
10854 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff,
10855 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
10856 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff,
10857 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78,
10858 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18,
10859 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0,
10860 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0,
10861 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99,
10862 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00,
10863 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00,
10864 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18,
10865 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
10866 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00,
10867 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78,
10868 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00,
10869 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff,
10870 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00,
10871 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
10872 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00,
10873 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
10874 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00,
10875 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00,
10876 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00,
10877 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00,
10878 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
10879 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00,
10880 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
10881 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00,
10882 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00,
10883 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00,
10884 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00,
10885 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
10886 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00,
10887 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
10888 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
10889 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00,
10890 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60,
10891 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
10892 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00,
10893 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00,
10894 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00,
10895 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00,
10896 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00,
10897 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00,
10898 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00,
10899 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00,
10900 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00,
10901 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00,
10902 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00,
10903 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00,
10904 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00,
10905 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60,
10906 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00,
10907 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00,
10908 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00,
10909 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00,
10910 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00,
10911 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00,
10912 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00,
10913 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00,
10914 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00,
10915 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00,
10916 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00,
10917 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00,
10918 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00,
10919 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
10920 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00,
10921 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00,
10922 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00,
10923 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00,
10924 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00,
10925 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00,
10926 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
10927 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00,
10928 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00,
10929 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00,
10930 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
10931 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00,
10932 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
10933 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00,
10934 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00,
10935 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00,
10936 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00,
10937 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
10938 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00,
10939 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
10940 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
10941 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
10942 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
10943 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00,
10944 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00,
10945 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00,
10946 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00,
10947 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
10948 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00,
10949 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
10950 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00,
10951 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
10952 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78,
10953 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00,
10954 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
10955 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00,
10956 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
10957 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
10958 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0,
10959 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e,
10960 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00,
10961 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00,
10962 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00,
10963 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
10964 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
10965 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00,
10966 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00,
10967 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
10968 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00,
10969 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00,
10970 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
10971 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00,
10972 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
10973 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00,
10976 ASM_START
10977 .org 0xcc00
10978 // bcc-generated data will be placed here
10979 ASM_END