kvm: external module: compatibility for pagefault_{enable,disable}()
[kvm-userspace.git] / bios / rombios.c
blob9c195dfe113f0b4dded2673ba02b4f0ccf1edd3b
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 if (sizeinmb < 1UL<<16)
2451 printf(" ATA-%d Hard-Disk (%04u MBytes)\n",version,(Bit16u)sizeinmb);
2452 else
2453 printf(" ATA-%d Hard-Disk (%04u GBytes)\n",version,(Bit16u)(sizeinmb>>10));
2454 break;
2455 case ATA_TYPE_ATAPI:
2456 printf("ata%d %s: ",channel,slave?" slave":"master");
2457 i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
2458 if(read_byte(ebda_seg,&EbdaData->ata.devices[device].device)==ATA_DEVICE_CDROM)
2459 printf(" ATAPI-%d CD-Rom/DVD-Rom\n",version);
2460 else
2461 printf(" ATAPI-%d Device\n",version);
2462 break;
2463 case ATA_TYPE_UNKNOWN:
2464 printf("ata%d %s: Unknown device\n",channel,slave?" slave":"master");
2465 break;
2470 // Store the devices counts
2471 write_byte(ebda_seg,&EbdaData->ata.hdcount, hdcount);
2472 write_byte(ebda_seg,&EbdaData->ata.cdcount, cdcount);
2473 write_byte(0x40,0x75, hdcount);
2475 printf("\n");
2477 // FIXME : should use bios=cmos|auto|disable bits
2478 // FIXME : should know about translation bits
2479 // FIXME : move hard_drive_post here
2483 // ---------------------------------------------------------------------------
2484 // ATA/ATAPI driver : software reset
2485 // ---------------------------------------------------------------------------
2486 // ATA-3
2487 // 8.2.1 Software reset - Device 0
2489 void ata_reset(device)
2490 Bit16u device;
2492 Bit16u ebda_seg=read_word(0x0040,0x000E);
2493 Bit16u iobase1, iobase2;
2494 Bit8u channel, slave, sn, sc;
2495 Bit16u max;
2497 channel = device / 2;
2498 slave = device % 2;
2500 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2501 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2503 // Reset
2505 // 8.2.1 (a) -- set SRST in DC
2506 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST);
2508 // 8.2.1 (b) -- wait for BSY
2509 max=0xff;
2510 while(--max>0) {
2511 Bit8u status = inb(iobase1+ATA_CB_STAT);
2512 if ((status & ATA_CB_STAT_BSY) != 0) break;
2515 // 8.2.1 (f) -- clear SRST
2516 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2518 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_NONE) {
2520 // 8.2.1 (g) -- check for sc==sn==0x01
2521 // select device
2522 outb(iobase1+ATA_CB_DH, slave?ATA_CB_DH_DEV1:ATA_CB_DH_DEV0);
2523 sc = inb(iobase1+ATA_CB_SC);
2524 sn = inb(iobase1+ATA_CB_SN);
2526 if ( (sc==0x01) && (sn==0x01) ) {
2528 // 8.2.1 (h) -- wait for not BSY
2529 max=0xff;
2530 while(--max>0) {
2531 Bit8u status = inb(iobase1+ATA_CB_STAT);
2532 if ((status & ATA_CB_STAT_BSY) == 0) break;
2537 // 8.2.1 (i) -- wait for DRDY
2538 max=0xfff;
2539 while(--max>0) {
2540 Bit8u status = inb(iobase1+ATA_CB_STAT);
2541 if ((status & ATA_CB_STAT_RDY) != 0) break;
2544 // Enable interrupts
2545 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2548 // ---------------------------------------------------------------------------
2549 // ATA/ATAPI driver : execute a non data command
2550 // ---------------------------------------------------------------------------
2552 Bit16u ata_cmd_non_data()
2553 {return 0;}
2555 // ---------------------------------------------------------------------------
2556 // ATA/ATAPI driver : execute a data-in command
2557 // ---------------------------------------------------------------------------
2558 // returns
2559 // 0 : no error
2560 // 1 : BUSY bit set
2561 // 2 : read error
2562 // 3 : expected DRQ=1
2563 // 4 : no sectors left to read/verify
2564 // 5 : more sectors to read/verify
2565 // 6 : no sectors left to write
2566 // 7 : more sectors to write
2567 Bit16u ata_cmd_data_in(device, command, count, cylinder, head, sector, lba, segment, offset)
2568 Bit16u device, command, count, cylinder, head, sector, segment, offset;
2569 Bit32u lba;
2571 Bit16u ebda_seg=read_word(0x0040,0x000E);
2572 Bit16u iobase1, iobase2, blksize;
2573 Bit8u channel, slave;
2574 Bit8u status, current, mode;
2576 channel = device / 2;
2577 slave = device % 2;
2579 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2580 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2581 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2582 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
2583 if (mode == ATA_MODE_PIO32) blksize>>=2;
2584 else blksize>>=1;
2586 // Reset count of transferred data
2587 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2588 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2589 current = 0;
2591 status = inb(iobase1 + ATA_CB_STAT);
2592 if (status & ATA_CB_STAT_BSY) return 1;
2594 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2596 // sector will be 0 only on lba access. Convert to lba-chs
2597 if (sector == 0) {
2598 if ((count >= 1 << 8) || (lba + count >= 1UL << 28)) {
2599 outb(iobase1 + ATA_CB_FR, 0x00);
2600 outb(iobase1 + ATA_CB_SC, (count >> 8) & 0xff);
2601 outb(iobase1 + ATA_CB_SN, lba >> 24);
2602 outb(iobase1 + ATA_CB_CL, 0);
2603 outb(iobase1 + ATA_CB_CH, 0);
2604 command |= 0x04;
2605 count &= (1UL << 8) - 1;
2606 lba &= (1UL << 24) - 1;
2608 sector = (Bit16u) (lba & 0x000000ffL);
2609 lba >>= 8;
2610 cylinder = (Bit16u) (lba & 0x0000ffffL);
2611 lba >>= 16;
2612 head = ((Bit16u) (lba & 0x0000000fL)) | 0x40;
2615 outb(iobase1 + ATA_CB_FR, 0x00);
2616 outb(iobase1 + ATA_CB_SC, count);
2617 outb(iobase1 + ATA_CB_SN, sector);
2618 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
2619 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
2620 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
2621 outb(iobase1 + ATA_CB_CMD, command);
2623 while (1) {
2624 status = inb(iobase1 + ATA_CB_STAT);
2625 if ( !(status & ATA_CB_STAT_BSY) ) break;
2628 if (status & ATA_CB_STAT_ERR) {
2629 BX_DEBUG_ATA("ata_cmd_data_in : read error\n");
2630 return 2;
2631 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2632 BX_DEBUG_ATA("ata_cmd_data_in : DRQ not set (status %02x)\n", (unsigned) status);
2633 return 3;
2636 // FIXME : move seg/off translation here
2638 ASM_START
2639 sti ;; enable higher priority interrupts
2640 ASM_END
2642 while (1) {
2644 ASM_START
2645 push bp
2646 mov bp, sp
2647 mov di, _ata_cmd_data_in.offset + 2[bp]
2648 mov ax, _ata_cmd_data_in.segment + 2[bp]
2649 mov cx, _ata_cmd_data_in.blksize + 2[bp]
2651 ;; adjust if there will be an overrun. 2K max sector size
2652 cmp di, #0xf800 ;;
2653 jbe ata_in_no_adjust
2655 ata_in_adjust:
2656 sub di, #0x0800 ;; sub 2 kbytes from offset
2657 add ax, #0x0080 ;; add 2 Kbytes to segment
2659 ata_in_no_adjust:
2660 mov es, ax ;; segment in es
2662 mov dx, _ata_cmd_data_in.iobase1 + 2[bp] ;; ATA data read port
2664 mov ah, _ata_cmd_data_in.mode + 2[bp]
2665 cmp ah, #ATA_MODE_PIO32
2666 je ata_in_32
2668 ata_in_16:
2670 insw ;; CX words transfered from port(DX) to ES:[DI]
2671 jmp ata_in_done
2673 ata_in_32:
2675 insd ;; CX dwords transfered from port(DX) to ES:[DI]
2677 ata_in_done:
2678 mov _ata_cmd_data_in.offset + 2[bp], di
2679 mov _ata_cmd_data_in.segment + 2[bp], es
2680 pop bp
2681 ASM_END
2683 current++;
2684 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
2685 count--;
2686 status = inb(iobase1 + ATA_CB_STAT);
2687 if (count == 0) {
2688 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2689 != ATA_CB_STAT_RDY ) {
2690 BX_DEBUG_ATA("ata_cmd_data_in : no sectors left (status %02x)\n", (unsigned) status);
2691 return 4;
2693 break;
2695 else {
2696 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2697 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2698 BX_DEBUG_ATA("ata_cmd_data_in : more sectors left (status %02x)\n", (unsigned) status);
2699 return 5;
2701 continue;
2704 // Enable interrupts
2705 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2706 return 0;
2709 // ---------------------------------------------------------------------------
2710 // ATA/ATAPI driver : execute a data-out command
2711 // ---------------------------------------------------------------------------
2712 // returns
2713 // 0 : no error
2714 // 1 : BUSY bit set
2715 // 2 : read error
2716 // 3 : expected DRQ=1
2717 // 4 : no sectors left to read/verify
2718 // 5 : more sectors to read/verify
2719 // 6 : no sectors left to write
2720 // 7 : more sectors to write
2721 Bit16u ata_cmd_data_out(device, command, count, cylinder, head, sector, lba, segment, offset)
2722 Bit16u device, command, count, cylinder, head, sector, segment, offset;
2723 Bit32u lba;
2725 Bit16u ebda_seg=read_word(0x0040,0x000E);
2726 Bit16u iobase1, iobase2, blksize;
2727 Bit8u channel, slave;
2728 Bit8u status, current, mode;
2730 channel = device / 2;
2731 slave = device % 2;
2733 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2734 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2735 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2736 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
2737 if (mode == ATA_MODE_PIO32) blksize>>=2;
2738 else blksize>>=1;
2740 // Reset count of transferred data
2741 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2742 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2743 current = 0;
2745 status = inb(iobase1 + ATA_CB_STAT);
2746 if (status & ATA_CB_STAT_BSY) return 1;
2748 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2750 // sector will be 0 only on lba access. Convert to lba-chs
2751 if (sector == 0) {
2752 if ((count >= 1 << 8) || (lba + count >= 1UL << 28)) {
2753 outb(iobase1 + ATA_CB_FR, 0x00);
2754 outb(iobase1 + ATA_CB_SC, (count >> 8) & 0xff);
2755 outb(iobase1 + ATA_CB_SN, lba >> 24);
2756 outb(iobase1 + ATA_CB_CL, 0);
2757 outb(iobase1 + ATA_CB_CH, 0);
2758 command |= 0x04;
2759 count &= (1UL << 8) - 1;
2760 lba &= (1UL << 24) - 1;
2762 sector = (Bit16u) (lba & 0x000000ffL);
2763 lba >>= 8;
2764 cylinder = (Bit16u) (lba & 0x0000ffffL);
2765 lba >>= 16;
2766 head = ((Bit16u) (lba & 0x0000000fL)) | 0x40;
2769 outb(iobase1 + ATA_CB_FR, 0x00);
2770 outb(iobase1 + ATA_CB_SC, count);
2771 outb(iobase1 + ATA_CB_SN, sector);
2772 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
2773 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
2774 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
2775 outb(iobase1 + ATA_CB_CMD, command);
2777 while (1) {
2778 status = inb(iobase1 + ATA_CB_STAT);
2779 if ( !(status & ATA_CB_STAT_BSY) ) break;
2782 if (status & ATA_CB_STAT_ERR) {
2783 BX_DEBUG_ATA("ata_cmd_data_out : read error\n");
2784 return 2;
2785 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2786 BX_DEBUG_ATA("ata_cmd_data_out : DRQ not set (status %02x)\n", (unsigned) status);
2787 return 3;
2790 // FIXME : move seg/off translation here
2792 ASM_START
2793 sti ;; enable higher priority interrupts
2794 ASM_END
2796 while (1) {
2798 ASM_START
2799 push bp
2800 mov bp, sp
2801 mov si, _ata_cmd_data_out.offset + 2[bp]
2802 mov ax, _ata_cmd_data_out.segment + 2[bp]
2803 mov cx, _ata_cmd_data_out.blksize + 2[bp]
2805 ;; adjust if there will be an overrun. 2K max sector size
2806 cmp si, #0xf800 ;;
2807 jbe ata_out_no_adjust
2809 ata_out_adjust:
2810 sub si, #0x0800 ;; sub 2 kbytes from offset
2811 add ax, #0x0080 ;; add 2 Kbytes to segment
2813 ata_out_no_adjust:
2814 mov es, ax ;; segment in es
2816 mov dx, _ata_cmd_data_out.iobase1 + 2[bp] ;; ATA data write port
2818 mov ah, _ata_cmd_data_out.mode + 2[bp]
2819 cmp ah, #ATA_MODE_PIO32
2820 je ata_out_32
2822 ata_out_16:
2823 seg ES
2825 outsw ;; CX words transfered from port(DX) to ES:[SI]
2826 jmp ata_out_done
2828 ata_out_32:
2829 seg ES
2831 outsd ;; CX dwords transfered from port(DX) to ES:[SI]
2833 ata_out_done:
2834 mov _ata_cmd_data_out.offset + 2[bp], si
2835 mov _ata_cmd_data_out.segment + 2[bp], es
2836 pop bp
2837 ASM_END
2839 current++;
2840 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
2841 count--;
2842 status = inb(iobase1 + ATA_CB_STAT);
2843 if (count == 0) {
2844 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2845 != ATA_CB_STAT_RDY ) {
2846 BX_DEBUG_ATA("ata_cmd_data_out : no sectors left (status %02x)\n", (unsigned) status);
2847 return 6;
2849 break;
2851 else {
2852 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2853 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2854 BX_DEBUG_ATA("ata_cmd_data_out : more sectors left (status %02x)\n", (unsigned) status);
2855 return 7;
2857 continue;
2860 // Enable interrupts
2861 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2862 return 0;
2865 // ---------------------------------------------------------------------------
2866 // ATA/ATAPI driver : execute a packet command
2867 // ---------------------------------------------------------------------------
2868 // returns
2869 // 0 : no error
2870 // 1 : error in parameters
2871 // 2 : BUSY bit set
2872 // 3 : error
2873 // 4 : not ready
2874 Bit16u ata_cmd_packet(device, cmdlen, cmdseg, cmdoff, header, length, inout, bufseg, bufoff)
2875 Bit8u cmdlen,inout;
2876 Bit16u device,cmdseg, cmdoff, bufseg, bufoff;
2877 Bit16u header;
2878 Bit32u length;
2880 Bit16u ebda_seg=read_word(0x0040,0x000E);
2881 Bit16u iobase1, iobase2;
2882 Bit16u lcount, lbefore, lafter, count;
2883 Bit8u channel, slave;
2884 Bit8u status, mode, lmode;
2885 Bit32u total, transfer;
2887 channel = device / 2;
2888 slave = device % 2;
2890 // Data out is not supported yet
2891 if (inout == ATA_DATA_OUT) {
2892 BX_INFO("ata_cmd_packet: DATA_OUT not supported yet\n");
2893 return 1;
2896 // The header length must be even
2897 if (header & 1) {
2898 BX_DEBUG_ATA("ata_cmd_packet : header must be even (%04x)\n",header);
2899 return 1;
2902 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2903 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2904 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2905 transfer= 0L;
2907 if (cmdlen < 12) cmdlen=12;
2908 if (cmdlen > 12) cmdlen=16;
2909 cmdlen>>=1;
2911 // Reset count of transferred data
2912 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2913 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2915 status = inb(iobase1 + ATA_CB_STAT);
2916 if (status & ATA_CB_STAT_BSY) return 2;
2918 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2919 // outb(iobase1 + ATA_CB_FR, 0x00);
2920 // outb(iobase1 + ATA_CB_SC, 0x00);
2921 // outb(iobase1 + ATA_CB_SN, 0x00);
2922 outb(iobase1 + ATA_CB_CL, 0xfff0 & 0x00ff);
2923 outb(iobase1 + ATA_CB_CH, 0xfff0 >> 8);
2924 outb(iobase1 + ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2925 outb(iobase1 + ATA_CB_CMD, ATA_CMD_PACKET);
2927 // Device should ok to receive command
2928 while (1) {
2929 status = inb(iobase1 + ATA_CB_STAT);
2930 if ( !(status & ATA_CB_STAT_BSY) ) break;
2933 if (status & ATA_CB_STAT_ERR) {
2934 BX_DEBUG_ATA("ata_cmd_packet : error, status is %02x\n",status);
2935 return 3;
2936 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2937 BX_DEBUG_ATA("ata_cmd_packet : DRQ not set (status %02x)\n", (unsigned) status);
2938 return 4;
2941 // Normalize address
2942 cmdseg += (cmdoff / 16);
2943 cmdoff %= 16;
2945 // Send command to device
2946 ASM_START
2947 sti ;; enable higher priority interrupts
2949 push bp
2950 mov bp, sp
2952 mov si, _ata_cmd_packet.cmdoff + 2[bp]
2953 mov ax, _ata_cmd_packet.cmdseg + 2[bp]
2954 mov cx, _ata_cmd_packet.cmdlen + 2[bp]
2955 mov es, ax ;; segment in es
2957 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data write port
2959 seg ES
2961 outsw ;; CX words transfered from port(DX) to ES:[SI]
2963 pop bp
2964 ASM_END
2966 if (inout == ATA_DATA_NO) {
2967 status = inb(iobase1 + ATA_CB_STAT);
2969 else {
2970 while (1) {
2972 status = inb(iobase1 + ATA_CB_STAT);
2974 // Check if command completed
2975 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ) ) ==0 ) break;
2977 if (status & ATA_CB_STAT_ERR) {
2978 BX_DEBUG_ATA("ata_cmd_packet : error (status %02x)\n",status);
2979 return 3;
2982 // Device must be ready to send data
2983 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2984 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2985 BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", status);
2986 return 4;
2989 // Normalize address
2990 bufseg += (bufoff / 16);
2991 bufoff %= 16;
2993 // Get the byte count
2994 lcount = ((Bit16u)(inb(iobase1 + ATA_CB_CH))<<8)+inb(iobase1 + ATA_CB_CL);
2996 // adjust to read what we want
2997 if(header>lcount) {
2998 lbefore=lcount;
2999 header-=lcount;
3000 lcount=0;
3002 else {
3003 lbefore=header;
3004 header=0;
3005 lcount-=lbefore;
3008 if(lcount>length) {
3009 lafter=lcount-length;
3010 lcount=length;
3011 length=0;
3013 else {
3014 lafter=0;
3015 length-=lcount;
3018 // Save byte count
3019 count = lcount;
3021 BX_DEBUG_ATA("Trying to read %04x bytes (%04x %04x %04x) ",lbefore+lcount+lafter,lbefore,lcount,lafter);
3022 BX_DEBUG_ATA("to 0x%04x:0x%04x\n",bufseg,bufoff);
3024 // If counts not dividable by 4, use 16bits mode
3025 lmode = mode;
3026 if (lbefore & 0x03) lmode=ATA_MODE_PIO16;
3027 if (lcount & 0x03) lmode=ATA_MODE_PIO16;
3028 if (lafter & 0x03) lmode=ATA_MODE_PIO16;
3030 // adds an extra byte if count are odd. before is always even
3031 if (lcount & 0x01) {
3032 lcount+=1;
3033 if ((lafter > 0) && (lafter & 0x01)) {
3034 lafter-=1;
3038 if (lmode == ATA_MODE_PIO32) {
3039 lcount>>=2; lbefore>>=2; lafter>>=2;
3041 else {
3042 lcount>>=1; lbefore>>=1; lafter>>=1;
3045 ; // FIXME bcc bug
3047 ASM_START
3048 push bp
3049 mov bp, sp
3051 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data read port
3053 mov cx, _ata_cmd_packet.lbefore + 2[bp]
3054 jcxz ata_packet_no_before
3056 mov ah, _ata_cmd_packet.lmode + 2[bp]
3057 cmp ah, #ATA_MODE_PIO32
3058 je ata_packet_in_before_32
3060 ata_packet_in_before_16:
3061 in ax, dx
3062 loop ata_packet_in_before_16
3063 jmp ata_packet_no_before
3065 ata_packet_in_before_32:
3066 push eax
3067 ata_packet_in_before_32_loop:
3068 in eax, dx
3069 loop ata_packet_in_before_32_loop
3070 pop eax
3072 ata_packet_no_before:
3073 mov cx, _ata_cmd_packet.lcount + 2[bp]
3074 jcxz ata_packet_after
3076 mov di, _ata_cmd_packet.bufoff + 2[bp]
3077 mov ax, _ata_cmd_packet.bufseg + 2[bp]
3078 mov es, ax
3080 mov ah, _ata_cmd_packet.lmode + 2[bp]
3081 cmp ah, #ATA_MODE_PIO32
3082 je ata_packet_in_32
3084 ata_packet_in_16:
3086 insw ;; CX words transfered tp port(DX) to ES:[DI]
3087 jmp ata_packet_after
3089 ata_packet_in_32:
3091 insd ;; CX dwords transfered to port(DX) to ES:[DI]
3093 ata_packet_after:
3094 mov cx, _ata_cmd_packet.lafter + 2[bp]
3095 jcxz ata_packet_done
3097 mov ah, _ata_cmd_packet.lmode + 2[bp]
3098 cmp ah, #ATA_MODE_PIO32
3099 je ata_packet_in_after_32
3101 ata_packet_in_after_16:
3102 in ax, dx
3103 loop ata_packet_in_after_16
3104 jmp ata_packet_done
3106 ata_packet_in_after_32:
3107 push eax
3108 ata_packet_in_after_32_loop:
3109 in eax, dx
3110 loop ata_packet_in_after_32_loop
3111 pop eax
3113 ata_packet_done:
3114 pop bp
3115 ASM_END
3117 // Compute new buffer address
3118 bufoff += count;
3120 // Save transferred bytes count
3121 transfer += count;
3122 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,transfer);
3126 // Final check, device must be ready
3127 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
3128 != ATA_CB_STAT_RDY ) {
3129 BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", (unsigned) status);
3130 return 4;
3133 // Enable interrupts
3134 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
3135 return 0;
3138 // ---------------------------------------------------------------------------
3139 // End of ATA/ATAPI Driver
3140 // ---------------------------------------------------------------------------
3142 // ---------------------------------------------------------------------------
3143 // Start of ATA/ATAPI generic functions
3144 // ---------------------------------------------------------------------------
3146 Bit16u
3147 atapi_get_sense(device)
3148 Bit16u device;
3150 Bit8u atacmd[12];
3151 Bit8u buffer[16];
3152 Bit8u i;
3154 memsetb(get_SS(),atacmd,0,12);
3156 // Request SENSE
3157 atacmd[0]=0x03;
3158 atacmd[4]=0x20;
3159 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 16L, ATA_DATA_IN, get_SS(), buffer) != 0)
3160 return 0x0002;
3162 if ((buffer[0] & 0x7e) == 0x70) {
3163 return (((Bit16u)buffer[2]&0x0f)*0x100)+buffer[12];
3166 return 0;
3169 Bit16u
3170 atapi_is_ready(device)
3171 Bit16u device;
3173 Bit8u atacmd[12];
3174 Bit8u buffer[];
3176 memsetb(get_SS(),atacmd,0,12);
3178 // Test Unit Ready
3179 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0)
3180 return 0x000f;
3182 if (atapi_get_sense(device) !=0 ) {
3183 memsetb(get_SS(),atacmd,0,12);
3185 // try to send Test Unit Ready again
3186 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0)
3187 return 0x000f;
3189 return atapi_get_sense(device);
3191 return 0;
3194 Bit16u
3195 atapi_is_cdrom(device)
3196 Bit8u device;
3198 Bit16u ebda_seg=read_word(0x0040,0x000E);
3200 if (device >= BX_MAX_ATA_DEVICES)
3201 return 0;
3203 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI)
3204 return 0;
3206 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].device) != ATA_DEVICE_CDROM)
3207 return 0;
3209 return 1;
3212 // ---------------------------------------------------------------------------
3213 // End of ATA/ATAPI generic functions
3214 // ---------------------------------------------------------------------------
3216 #endif // BX_USE_ATADRV
3218 #if BX_ELTORITO_BOOT
3220 // ---------------------------------------------------------------------------
3221 // Start of El-Torito boot functions
3222 // ---------------------------------------------------------------------------
3224 void
3225 cdemu_init()
3227 Bit16u ebda_seg=read_word(0x0040,0x000E);
3229 // the only important data is this one for now
3230 write_byte(ebda_seg,&EbdaData->cdemu.active,0x00);
3233 Bit8u
3234 cdemu_isactive()
3236 Bit16u ebda_seg=read_word(0x0040,0x000E);
3238 return(read_byte(ebda_seg,&EbdaData->cdemu.active));
3241 Bit8u
3242 cdemu_emulated_drive()
3244 Bit16u ebda_seg=read_word(0x0040,0x000E);
3246 return(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
3249 static char isotag[6]="CD001";
3250 static char eltorito[24]="EL TORITO SPECIFICATION";
3252 // Returns ah: emulated drive, al: error code
3254 Bit16u
3255 cdrom_boot()
3257 Bit16u ebda_seg=read_word(0x0040,0x000E);
3258 Bit8u atacmd[12], buffer[2048];
3259 Bit32u lba;
3260 Bit16u boot_segment, nbsectors, i, error;
3261 Bit8u device;
3263 // Find out the first cdrom
3264 for (device=0; device<BX_MAX_ATA_DEVICES;device++) {
3265 if (atapi_is_cdrom(device)) break;
3268 // if not found
3269 if(device >= BX_MAX_ATA_DEVICES) return 2;
3271 // Read the Boot Record Volume Descriptor
3272 memsetb(get_SS(),atacmd,0,12);
3273 atacmd[0]=0x28; // READ command
3274 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3275 atacmd[8]=(0x01 & 0x00ff); // Sectors
3276 atacmd[2]=(0x11 & 0xff000000) >> 24; // LBA
3277 atacmd[3]=(0x11 & 0x00ff0000) >> 16;
3278 atacmd[4]=(0x11 & 0x0000ff00) >> 8;
3279 atacmd[5]=(0x11 & 0x000000ff);
3280 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3281 return 3;
3283 // Validity checks
3284 if(buffer[0]!=0)return 4;
3285 for(i=0;i<5;i++){
3286 if(buffer[1+i]!=read_byte(0xf000,&isotag[i]))return 5;
3288 for(i=0;i<23;i++)
3289 if(buffer[7+i]!=read_byte(0xf000,&eltorito[i]))return 6;
3291 // ok, now we calculate the Boot catalog address
3292 lba=buffer[0x4A]*0x1000000+buffer[0x49]*0x10000+buffer[0x48]*0x100+buffer[0x47];
3294 // And we read the Boot Catalog
3295 memsetb(get_SS(),atacmd,0,12);
3296 atacmd[0]=0x28; // READ command
3297 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3298 atacmd[8]=(0x01 & 0x00ff); // Sectors
3299 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3300 atacmd[3]=(lba & 0x00ff0000) >> 16;
3301 atacmd[4]=(lba & 0x0000ff00) >> 8;
3302 atacmd[5]=(lba & 0x000000ff);
3303 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3304 return 7;
3306 // Validation entry
3307 if(buffer[0x00]!=0x01)return 8; // Header
3308 if(buffer[0x01]!=0x00)return 9; // Platform
3309 if(buffer[0x1E]!=0x55)return 10; // key 1
3310 if(buffer[0x1F]!=0xAA)return 10; // key 2
3312 // Initial/Default Entry
3313 if(buffer[0x20]!=0x88)return 11; // Bootable
3315 write_byte(ebda_seg,&EbdaData->cdemu.media,buffer[0x21]);
3316 if(buffer[0x21]==0){
3317 // FIXME ElTorito Hardcoded. cdrom is hardcoded as device 0xE0.
3318 // Win2000 cd boot needs to know it booted from cd
3319 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0xE0);
3321 else if(buffer[0x21]<4)
3322 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x00);
3323 else
3324 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x80);
3326 write_byte(ebda_seg,&EbdaData->cdemu.controller_index,device/2);
3327 write_byte(ebda_seg,&EbdaData->cdemu.device_spec,device%2);
3329 boot_segment=buffer[0x23]*0x100+buffer[0x22];
3330 if(boot_segment==0x0000)boot_segment=0x07C0;
3332 write_word(ebda_seg,&EbdaData->cdemu.load_segment,boot_segment);
3333 write_word(ebda_seg,&EbdaData->cdemu.buffer_segment,0x0000);
3335 nbsectors=buffer[0x27]*0x100+buffer[0x26];
3336 write_word(ebda_seg,&EbdaData->cdemu.sector_count,nbsectors);
3338 lba=buffer[0x2B]*0x1000000+buffer[0x2A]*0x10000+buffer[0x29]*0x100+buffer[0x28];
3339 write_dword(ebda_seg,&EbdaData->cdemu.ilba,lba);
3341 // And we read the image in memory
3342 memsetb(get_SS(),atacmd,0,12);
3343 atacmd[0]=0x28; // READ command
3344 atacmd[7]=((1+(nbsectors-1)/4) & 0xff00) >> 8; // Sectors
3345 atacmd[8]=((1+(nbsectors-1)/4) & 0x00ff); // Sectors
3346 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3347 atacmd[3]=(lba & 0x00ff0000) >> 16;
3348 atacmd[4]=(lba & 0x0000ff00) >> 8;
3349 atacmd[5]=(lba & 0x000000ff);
3350 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, nbsectors*512L, ATA_DATA_IN, boot_segment,0)) != 0)
3351 return 12;
3353 // Remember the media type
3354 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
3355 case 0x01: // 1.2M floppy
3356 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,15);
3357 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3358 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3359 break;
3360 case 0x02: // 1.44M floppy
3361 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,18);
3362 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3363 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3364 break;
3365 case 0x03: // 2.88M floppy
3366 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,36);
3367 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3368 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3369 break;
3370 case 0x04: // Harddrive
3371 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,read_byte(boot_segment,446+6)&0x3f);
3372 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,
3373 (read_byte(boot_segment,446+6)<<2) + read_byte(boot_segment,446+7) + 1);
3374 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,read_byte(boot_segment,446+5) + 1);
3375 break;
3378 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0) {
3379 // Increase bios installed hardware number of devices
3380 if(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)==0x00)
3381 write_byte(0x40,0x10,read_byte(0x40,0x10)|0x41);
3382 else
3383 write_byte(ebda_seg, &EbdaData->ata.hdcount, read_byte(ebda_seg, &EbdaData->ata.hdcount) + 1);
3387 // everything is ok, so from now on, the emulation is active
3388 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0)
3389 write_byte(ebda_seg,&EbdaData->cdemu.active,0x01);
3391 // return the boot drive + no error
3392 return (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)*0x100)+0;
3395 // ---------------------------------------------------------------------------
3396 // End of El-Torito boot functions
3397 // ---------------------------------------------------------------------------
3398 #endif // BX_ELTORITO_BOOT
3400 void
3401 int14_function(regs, ds, iret_addr)
3402 pusha_regs_t regs; // regs pushed from PUSHA instruction
3403 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
3404 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
3406 Bit16u addr,timer,val16;
3407 Bit8u timeout;
3409 ASM_START
3411 ASM_END
3413 addr = read_word(0x0040, (regs.u.r16.dx << 1));
3414 timeout = read_byte(0x0040, 0x007C + regs.u.r16.dx);
3415 if ((regs.u.r16.dx < 4) && (addr > 0)) {
3416 switch (regs.u.r8.ah) {
3417 case 0:
3418 outb(addr+3, inb(addr+3) | 0x80);
3419 if (regs.u.r8.al & 0xE0 == 0) {
3420 outb(addr, 0x17);
3421 outb(addr+1, 0x04);
3422 } else {
3423 val16 = 0x600 >> ((regs.u.r8.al & 0xE0) >> 5);
3424 outb(addr, val16 & 0xFF);
3425 outb(addr+1, val16 >> 8);
3427 outb(addr+3, regs.u.r8.al & 0x1F);
3428 regs.u.r8.ah = inb(addr+5);
3429 regs.u.r8.al = inb(addr+6);
3430 ClearCF(iret_addr.flags);
3431 break;
3432 case 1:
3433 timer = read_word(0x0040, 0x006C);
3434 while (((inb(addr+5) & 0x60) != 0x60) && (timeout)) {
3435 val16 = read_word(0x0040, 0x006C);
3436 if (val16 != timer) {
3437 timer = val16;
3438 timeout--;
3441 if (timeout) outb(addr, regs.u.r8.al);
3442 regs.u.r8.ah = inb(addr+5);
3443 if (!timeout) regs.u.r8.ah |= 0x80;
3444 ClearCF(iret_addr.flags);
3445 break;
3446 case 2:
3447 timer = read_word(0x0040, 0x006C);
3448 while (((inb(addr+5) & 0x01) == 0) && (timeout)) {
3449 val16 = read_word(0x0040, 0x006C);
3450 if (val16 != timer) {
3451 timer = val16;
3452 timeout--;
3455 if (timeout) {
3456 regs.u.r8.ah = 0;
3457 regs.u.r8.al = inb(addr);
3458 } else {
3459 regs.u.r8.ah = inb(addr+5);
3461 ClearCF(iret_addr.flags);
3462 break;
3463 case 3:
3464 regs.u.r8.ah = inb(addr+5);
3465 regs.u.r8.al = inb(addr+6);
3466 ClearCF(iret_addr.flags);
3467 break;
3468 default:
3469 SetCF(iret_addr.flags); // Unsupported
3471 } else {
3472 SetCF(iret_addr.flags); // Unsupported
3476 void
3477 int15_function(regs, ES, DS, FLAGS)
3478 pusha_regs_t regs; // REGS pushed via pusha
3479 Bit16u ES, DS, FLAGS;
3481 Bit16u ebda_seg=read_word(0x0040,0x000E);
3482 bx_bool prev_a20_enable;
3483 Bit16u base15_00;
3484 Bit8u base23_16;
3485 Bit16u ss;
3486 Bit16u CX,DX;
3488 Bit16u bRegister;
3489 Bit8u irqDisable;
3491 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3493 switch (regs.u.r8.ah) {
3494 case 0x24: /* A20 Control */
3495 switch (regs.u.r8.al) {
3496 case 0x00:
3497 set_enable_a20(0);
3498 CLEAR_CF();
3499 regs.u.r8.ah = 0;
3500 break;
3501 case 0x01:
3502 set_enable_a20(1);
3503 CLEAR_CF();
3504 regs.u.r8.ah = 0;
3505 break;
3506 case 0x02:
3507 regs.u.r8.al = (inb(0x92) >> 1) & 0x01;
3508 CLEAR_CF();
3509 regs.u.r8.ah = 0;
3510 break;
3511 case 0x03:
3512 CLEAR_CF();
3513 regs.u.r8.ah = 0;
3514 regs.u.r16.bx = 3;
3515 break;
3516 default:
3517 BX_INFO("int15: Func 24h, subfunc %02xh, A20 gate control not supported\n", (unsigned) regs.u.r8.al);
3518 SET_CF();
3519 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3521 break;
3523 case 0x41:
3524 SET_CF();
3525 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3526 break;
3528 case 0x4f:
3529 /* keyboard intercept */
3530 #if BX_CPU < 2
3531 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3532 #else
3533 // nop
3534 #endif
3535 SET_CF();
3536 break;
3538 case 0x52: // removable media eject
3539 CLEAR_CF();
3540 regs.u.r8.ah = 0; // "ok ejection may proceed"
3541 break;
3543 case 0x83: {
3544 if( regs.u.r8.al == 0 ) {
3545 // Set Interval requested.
3546 if( ( read_byte( 0x40, 0xA0 ) & 1 ) == 0 ) {
3547 // Interval not already set.
3548 write_byte( 0x40, 0xA0, 1 ); // Set status byte.
3549 write_word( 0x40, 0x98, ES ); // Byte location, segment
3550 write_word( 0x40, 0x9A, regs.u.r16.bx ); // Byte location, offset
3551 write_word( 0x40, 0x9C, regs.u.r16.dx ); // Low word, delay
3552 write_word( 0x40, 0x9E, regs.u.r16.cx ); // High word, delay.
3553 CLEAR_CF( );
3554 irqDisable = inb( 0xA1 );
3555 outb( 0xA1, irqDisable & 0xFE );
3556 bRegister = inb_cmos( 0xB ); // Unmask IRQ8 so INT70 will get through.
3557 outb_cmos( 0xB, bRegister | 0x40 ); // Turn on the Periodic Interrupt timer
3558 } else {
3559 // Interval already set.
3560 BX_DEBUG_INT15("int15: Func 83h, failed, already waiting.\n" );
3561 SET_CF();
3562 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3564 } else if( regs.u.r8.al == 1 ) {
3565 // Clear Interval requested
3566 write_byte( 0x40, 0xA0, 0 ); // Clear status byte
3567 CLEAR_CF( );
3568 bRegister = inb_cmos( 0xB );
3569 outb_cmos( 0xB, bRegister & ~0x40 ); // Turn off the Periodic Interrupt timer
3570 } else {
3571 BX_DEBUG_INT15("int15: Func 83h, failed.\n" );
3572 SET_CF();
3573 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3574 regs.u.r8.al--;
3577 break;
3580 case 0x87:
3581 #if BX_CPU < 3
3582 # error "Int15 function 87h not supported on < 80386"
3583 #endif
3584 // +++ should probably have descriptor checks
3585 // +++ should have exception handlers
3587 // turn off interrupts
3588 ASM_START
3590 ASM_END
3592 prev_a20_enable = set_enable_a20(1); // enable A20 line
3594 // 128K max of transfer on 386+ ???
3595 // source == destination ???
3597 // ES:SI points to descriptor table
3598 // offset use initially comments
3599 // ==============================================
3600 // 00..07 Unused zeros Null descriptor
3601 // 08..0f GDT zeros filled in by BIOS
3602 // 10..17 source ssssssss source of data
3603 // 18..1f dest dddddddd destination of data
3604 // 20..27 CS zeros filled in by BIOS
3605 // 28..2f SS zeros filled in by BIOS
3607 //es:si
3608 //eeee0
3609 //0ssss
3610 //-----
3612 // check for access rights of source & dest here
3614 // Initialize GDT descriptor
3615 base15_00 = (ES << 4) + regs.u.r16.si;
3616 base23_16 = ES >> 12;
3617 if (base15_00 < (ES<<4))
3618 base23_16++;
3619 write_word(ES, regs.u.r16.si+0x08+0, 47); // limit 15:00 = 6 * 8bytes/descriptor
3620 write_word(ES, regs.u.r16.si+0x08+2, base15_00);// base 15:00
3621 write_byte(ES, regs.u.r16.si+0x08+4, base23_16);// base 23:16
3622 write_byte(ES, regs.u.r16.si+0x08+5, 0x93); // access
3623 write_word(ES, regs.u.r16.si+0x08+6, 0x0000); // base 31:24/reserved/limit 19:16
3625 // Initialize CS descriptor
3626 write_word(ES, regs.u.r16.si+0x20+0, 0xffff);// limit 15:00 = normal 64K limit
3627 write_word(ES, regs.u.r16.si+0x20+2, 0x0000);// base 15:00
3628 write_byte(ES, regs.u.r16.si+0x20+4, 0x000f);// base 23:16
3629 write_byte(ES, regs.u.r16.si+0x20+5, 0x9b); // access
3630 write_word(ES, regs.u.r16.si+0x20+6, 0x0000);// base 31:24/reserved/limit 19:16
3632 // Initialize SS descriptor
3633 ss = get_SS();
3634 base15_00 = ss << 4;
3635 base23_16 = ss >> 12;
3636 write_word(ES, regs.u.r16.si+0x28+0, 0xffff); // limit 15:00 = normal 64K limit
3637 write_word(ES, regs.u.r16.si+0x28+2, base15_00);// base 15:00
3638 write_byte(ES, regs.u.r16.si+0x28+4, base23_16);// base 23:16
3639 write_byte(ES, regs.u.r16.si+0x28+5, 0x93); // access
3640 write_word(ES, regs.u.r16.si+0x28+6, 0x0000); // base 31:24/reserved/limit 19:16
3642 CX = regs.u.r16.cx;
3643 ASM_START
3644 // Compile generates locals offset info relative to SP.
3645 // Get CX (word count) from stack.
3646 mov bx, sp
3647 SEG SS
3648 mov cx, _int15_function.CX [bx]
3650 // since we need to set SS:SP, save them to the BDA
3651 // for future restore
3652 push eax
3653 xor eax, eax
3654 mov ds, ax
3655 mov 0x0469, ss
3656 mov 0x0467, sp
3658 SEG ES
3659 lgdt [si + 0x08]
3660 SEG CS
3661 lidt [pmode_IDT_info]
3662 ;; perhaps do something with IDT here
3664 ;; set PE bit in CR0
3665 mov eax, cr0
3666 or al, #0x01
3667 mov cr0, eax
3668 ;; far jump to flush CPU queue after transition to protected mode
3669 JMP_AP(0x0020, protected_mode)
3671 protected_mode:
3672 ;; GDT points to valid descriptor table, now load SS, DS, ES
3673 mov ax, #0x28 ;; 101 000 = 5th descriptor in table, TI=GDT, RPL=00
3674 mov ss, ax
3675 mov ax, #0x10 ;; 010 000 = 2nd descriptor in table, TI=GDT, RPL=00
3676 mov ds, ax
3677 mov ax, #0x18 ;; 011 000 = 3rd descriptor in table, TI=GDT, RPL=00
3678 mov es, ax
3679 xor si, si
3680 xor di, di
3683 movsw ;; move CX words from DS:SI to ES:DI
3685 ;; make sure DS and ES limits are 64KB
3686 mov ax, #0x28
3687 mov ds, ax
3688 mov es, ax
3690 ;; reset PG bit in CR0 ???
3691 mov eax, cr0
3692 and al, #0xFE
3693 mov cr0, eax
3695 ;; far jump to flush CPU queue after transition to real mode
3696 JMP_AP(0xf000, real_mode)
3698 real_mode:
3699 ;; restore IDT to normal real-mode defaults
3700 SEG CS
3701 lidt [rmode_IDT_info]
3703 // restore SS:SP from the BDA
3704 xor ax, ax
3705 mov ds, ax
3706 mov ss, 0x0469
3707 mov sp, 0x0467
3708 pop eax
3709 ASM_END
3711 set_enable_a20(prev_a20_enable);
3713 // turn back on interrupts
3714 ASM_START
3716 ASM_END
3718 regs.u.r8.ah = 0;
3719 CLEAR_CF();
3720 break;
3723 case 0x88:
3724 // Get the amount of extended memory (above 1M)
3725 #if BX_CPU < 2
3726 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3727 SET_CF();
3728 #else
3729 regs.u.r8.al = inb_cmos(0x30);
3730 regs.u.r8.ah = inb_cmos(0x31);
3732 // According to Ralf Brown's interrupt the limit should be 15M,
3733 // but real machines mostly return max. 63M.
3734 if(regs.u.r16.ax > 0xffc0)
3735 regs.u.r16.ax = 0xffc0;
3737 CLEAR_CF();
3738 #endif
3739 break;
3741 case 0x90:
3742 /* Device busy interrupt. Called by Int 16h when no key available */
3743 break;
3745 case 0x91:
3746 /* Interrupt complete. Called by Int 16h when key becomes available */
3747 break;
3749 case 0xbf:
3750 BX_INFO("*** int 15h function AH=bf not yet supported!\n");
3751 SET_CF();
3752 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3753 break;
3755 case 0xC0:
3756 #if 0
3757 SET_CF();
3758 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3759 break;
3760 #endif
3761 CLEAR_CF();
3762 regs.u.r8.ah = 0;
3763 regs.u.r16.bx = BIOS_CONFIG_TABLE;
3764 ES = 0xF000;
3765 break;
3767 case 0xc1:
3768 ES = ebda_seg;
3769 CLEAR_CF();
3770 break;
3772 case 0xd8:
3773 bios_printf(BIOS_PRINTF_DEBUG, "EISA BIOS not present\n");
3774 SET_CF();
3775 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3776 break;
3778 default:
3779 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
3780 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
3781 SET_CF();
3782 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3783 break;
3787 #if BX_USE_PS2_MOUSE
3788 void
3789 int15_function_mouse(regs, ES, DS, FLAGS)
3790 pusha_regs_t regs; // REGS pushed via pusha
3791 Bit16u ES, DS, FLAGS;
3793 Bit16u ebda_seg=read_word(0x0040,0x000E);
3794 Bit8u mouse_flags_1, mouse_flags_2;
3795 Bit16u mouse_driver_seg;
3796 Bit16u mouse_driver_offset;
3797 Bit8u comm_byte, prev_command_byte;
3798 Bit8u ret, mouse_data1, mouse_data2, mouse_data3;
3800 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3802 switch (regs.u.r8.ah) {
3803 case 0xC2:
3804 // Return Codes status in AH
3805 // =========================
3806 // 00: success
3807 // 01: invalid subfunction (AL > 7)
3808 // 02: invalid input value (out of allowable range)
3809 // 03: interface error
3810 // 04: resend command received from mouse controller,
3811 // device driver should attempt command again
3812 // 05: cannot enable mouse, since no far call has been installed
3813 // 80/86: mouse service not implemented
3815 switch (regs.u.r8.al) {
3816 case 0: // Disable/Enable Mouse
3817 BX_DEBUG_INT15("case 0:\n");
3818 switch (regs.u.r8.bh) {
3819 case 0: // Disable Mouse
3820 BX_DEBUG_INT15("case 0: disable mouse\n");
3821 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3822 ret = send_to_mouse_ctrl(0xF5); // disable mouse command
3823 if (ret == 0) {
3824 ret = get_mouse_data(&mouse_data1);
3825 if ( (ret == 0) || (mouse_data1 == 0xFA) ) {
3826 CLEAR_CF();
3827 regs.u.r8.ah = 0;
3828 return;
3832 // error
3833 SET_CF();
3834 regs.u.r8.ah = ret;
3835 return;
3836 break;
3838 case 1: // Enable Mouse
3839 BX_DEBUG_INT15("case 1: enable mouse\n");
3840 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
3841 if ( (mouse_flags_2 & 0x80) == 0 ) {
3842 BX_DEBUG_INT15("INT 15h C2 Enable Mouse, no far call handler\n");
3843 SET_CF(); // error
3844 regs.u.r8.ah = 5; // no far call installed
3845 return;
3847 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3848 ret = send_to_mouse_ctrl(0xF4); // enable mouse command
3849 if (ret == 0) {
3850 ret = get_mouse_data(&mouse_data1);
3851 if ( (ret == 0) && (mouse_data1 == 0xFA) ) {
3852 enable_mouse_int_and_events(); // turn IRQ12 and packet generation on
3853 CLEAR_CF();
3854 regs.u.r8.ah = 0;
3855 return;
3858 SET_CF();
3859 regs.u.r8.ah = ret;
3860 return;
3862 default: // invalid subfunction
3863 BX_DEBUG_INT15("INT 15h C2 AL=0, BH=%02x\n", (unsigned) regs.u.r8.bh);
3864 SET_CF(); // error
3865 regs.u.r8.ah = 1; // invalid subfunction
3866 return;
3868 break;
3870 case 1: // Reset Mouse
3871 case 5: // Initialize Mouse
3872 BX_DEBUG_INT15("case 1 or 5:\n");
3873 if (regs.u.r8.al == 5) {
3874 if (regs.u.r8.bh != 3) {
3875 SET_CF();
3876 regs.u.r8.ah = 0x02; // invalid input
3877 return;
3879 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
3880 mouse_flags_2 = (mouse_flags_2 & 0x00) | regs.u.r8.bh;
3881 mouse_flags_1 = 0x00;
3882 write_byte(ebda_seg, 0x0026, mouse_flags_1);
3883 write_byte(ebda_seg, 0x0027, mouse_flags_2);
3886 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3887 ret = send_to_mouse_ctrl(0xFF); // reset mouse command
3888 if (ret == 0) {
3889 ret = get_mouse_data(&mouse_data3);
3890 // if no mouse attached, it will return RESEND
3891 if (mouse_data3 == 0xfe) {
3892 SET_CF();
3893 return;
3895 if (mouse_data3 != 0xfa)
3896 BX_PANIC("Mouse reset returned %02x (should be ack)\n", (unsigned)mouse_data3);
3897 if ( ret == 0 ) {
3898 ret = get_mouse_data(&mouse_data1);
3899 if ( ret == 0 ) {
3900 ret = get_mouse_data(&mouse_data2);
3901 if ( ret == 0 ) {
3902 // turn IRQ12 and packet generation on
3903 enable_mouse_int_and_events();
3904 CLEAR_CF();
3905 regs.u.r8.ah = 0;
3906 regs.u.r8.bl = mouse_data1;
3907 regs.u.r8.bh = mouse_data2;
3908 return;
3914 // error
3915 SET_CF();
3916 regs.u.r8.ah = ret;
3917 return;
3919 case 2: // Set Sample Rate
3920 BX_DEBUG_INT15("case 2:\n");
3921 switch (regs.u.r8.bh) {
3922 case 0: mouse_data1 = 10; break; // 10 reports/sec
3923 case 1: mouse_data1 = 20; break; // 20 reports/sec
3924 case 2: mouse_data1 = 40; break; // 40 reports/sec
3925 case 3: mouse_data1 = 60; break; // 60 reports/sec
3926 case 4: mouse_data1 = 80; break; // 80 reports/sec
3927 case 5: mouse_data1 = 100; break; // 100 reports/sec (default)
3928 case 6: mouse_data1 = 200; break; // 200 reports/sec
3929 default: mouse_data1 = 0;
3931 if (mouse_data1 > 0) {
3932 ret = send_to_mouse_ctrl(0xF3); // set sample rate command
3933 if (ret == 0) {
3934 ret = get_mouse_data(&mouse_data2);
3935 ret = send_to_mouse_ctrl(mouse_data1);
3936 ret = get_mouse_data(&mouse_data2);
3937 CLEAR_CF();
3938 regs.u.r8.ah = 0;
3939 } else {
3940 // error
3941 SET_CF();
3942 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3944 } else {
3945 // error
3946 SET_CF();
3947 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3949 break;
3951 case 3: // Set Resolution
3952 BX_DEBUG_INT15("case 3:\n");
3953 // BH:
3954 // 0 = 25 dpi, 1 count per millimeter
3955 // 1 = 50 dpi, 2 counts per millimeter
3956 // 2 = 100 dpi, 4 counts per millimeter
3957 // 3 = 200 dpi, 8 counts per millimeter
3958 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3959 if (regs.u.r8.bh < 4) {
3960 ret = send_to_mouse_ctrl(0xE8); // set resolution command
3961 if (ret == 0) {
3962 ret = get_mouse_data(&mouse_data1);
3963 if (mouse_data1 != 0xfa)
3964 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
3965 ret = send_to_mouse_ctrl(regs.u.r8.bh);
3966 ret = get_mouse_data(&mouse_data1);
3967 if (mouse_data1 != 0xfa)
3968 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
3969 CLEAR_CF();
3970 regs.u.r8.ah = 0;
3971 } else {
3972 // error
3973 SET_CF();
3974 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3976 } else {
3977 // error
3978 SET_CF();
3979 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3981 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
3982 break;
3984 case 4: // Get Device ID
3985 BX_DEBUG_INT15("case 4:\n");
3986 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3987 ret = send_to_mouse_ctrl(0xF2); // get mouse ID command
3988 if (ret == 0) {
3989 ret = get_mouse_data(&mouse_data1);
3990 ret = get_mouse_data(&mouse_data2);
3991 CLEAR_CF();
3992 regs.u.r8.ah = 0;
3993 regs.u.r8.bh = mouse_data2;
3994 } else {
3995 // error
3996 SET_CF();
3997 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3999 break;
4001 case 6: // Return Status & Set Scaling Factor...
4002 BX_DEBUG_INT15("case 6:\n");
4003 switch (regs.u.r8.bh) {
4004 case 0: // Return Status
4005 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4006 ret = send_to_mouse_ctrl(0xE9); // get mouse info command
4007 if (ret == 0) {
4008 ret = get_mouse_data(&mouse_data1);
4009 if (mouse_data1 != 0xfa)
4010 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
4011 if (ret == 0) {
4012 ret = get_mouse_data(&mouse_data1);
4013 if ( ret == 0 ) {
4014 ret = get_mouse_data(&mouse_data2);
4015 if ( ret == 0 ) {
4016 ret = get_mouse_data(&mouse_data3);
4017 if ( ret == 0 ) {
4018 CLEAR_CF();
4019 regs.u.r8.ah = 0;
4020 regs.u.r8.bl = mouse_data1;
4021 regs.u.r8.cl = mouse_data2;
4022 regs.u.r8.dl = mouse_data3;
4023 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4024 return;
4031 // error
4032 SET_CF();
4033 regs.u.r8.ah = ret;
4034 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4035 return;
4037 case 1: // Set Scaling Factor to 1:1
4038 case 2: // Set Scaling Factor to 2:1
4039 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4040 if (regs.u.r8.bh == 1) {
4041 ret = send_to_mouse_ctrl(0xE6);
4042 } else {
4043 ret = send_to_mouse_ctrl(0xE7);
4045 if (ret == 0) {
4046 get_mouse_data(&mouse_data1);
4047 ret = (mouse_data1 != 0xFA);
4049 if (ret == 0) {
4050 CLEAR_CF();
4051 regs.u.r8.ah = 0;
4052 } else {
4053 // error
4054 SET_CF();
4055 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4057 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4058 break;
4060 default:
4061 BX_PANIC("INT 15h C2 AL=6, BH=%02x\n", (unsigned) regs.u.r8.bh);
4063 break;
4065 case 7: // Set Mouse Handler Address
4066 BX_DEBUG_INT15("case 7:\n");
4067 mouse_driver_seg = ES;
4068 mouse_driver_offset = regs.u.r16.bx;
4069 write_word(ebda_seg, 0x0022, mouse_driver_offset);
4070 write_word(ebda_seg, 0x0024, mouse_driver_seg);
4071 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4072 if (mouse_driver_offset == 0 && mouse_driver_seg == 0) {
4073 /* remove handler */
4074 if ( (mouse_flags_2 & 0x80) != 0 ) {
4075 mouse_flags_2 &= ~0x80;
4076 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4079 else {
4080 /* install handler */
4081 mouse_flags_2 |= 0x80;
4083 write_byte(ebda_seg, 0x0027, mouse_flags_2);
4084 CLEAR_CF();
4085 regs.u.r8.ah = 0;
4086 break;
4088 default:
4089 BX_DEBUG_INT15("case default:\n");
4090 regs.u.r8.ah = 1; // invalid function
4091 SET_CF();
4093 break;
4095 default:
4096 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4097 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4098 SET_CF();
4099 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4100 break;
4103 #endif
4106 void set_e820_range(ES, DI, start, end, extra_start, extra_end, type)
4107 Bit16u ES;
4108 Bit16u DI;
4109 Bit32u start;
4110 Bit32u end;
4111 Bit8u extra_start;
4112 Bit8u extra_end;
4113 Bit16u type;
4115 write_word(ES, DI, start);
4116 write_word(ES, DI+2, start >> 16);
4117 write_word(ES, DI+4, extra_start);
4118 write_word(ES, DI+6, 0x00);
4120 end -= start;
4121 extra_end -= extra_start;
4122 write_word(ES, DI+8, end);
4123 write_word(ES, DI+10, end >> 16);
4124 write_word(ES, DI+12, extra_end);
4125 write_word(ES, DI+14, 0x0000);
4127 write_word(ES, DI+16, type);
4128 write_word(ES, DI+18, 0x0);
4131 void
4132 int15_function32(regs, ES, DS, FLAGS)
4133 pushad_regs_t regs; // REGS pushed via pushad
4134 Bit16u ES, DS, FLAGS;
4136 Bit32u extended_memory_size=0; // 64bits long
4137 Bit32u extra_lowbits_memory_size=0;
4138 Bit16u CX,DX;
4139 Bit8u extra_highbits_memory_size=0;
4141 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
4143 switch (regs.u.r8.ah) {
4144 case 0x86:
4145 // Wait for CX:DX microseconds. currently using the
4146 // refresh request port 0x61 bit4, toggling every 15usec
4148 CX = regs.u.r16.cx;
4149 DX = regs.u.r16.dx;
4151 ASM_START
4154 ;; Get the count in eax
4155 mov bx, sp
4156 SEG SS
4157 mov ax, _int15_function.CX [bx]
4158 shl eax, #16
4159 SEG SS
4160 mov ax, _int15_function.DX [bx]
4162 ;; convert to numbers of 15usec ticks
4163 mov ebx, #15
4164 xor edx, edx
4165 div eax, ebx
4166 mov ecx, eax
4168 ;; wait for ecx number of refresh requests
4169 in al, #0x61
4170 and al,#0x10
4171 mov ah, al
4173 or ecx, ecx
4174 je int1586_tick_end
4175 int1586_tick:
4176 in al, #0x61
4177 and al,#0x10
4178 cmp al, ah
4179 je int1586_tick
4180 mov ah, al
4181 dec ecx
4182 jnz int1586_tick
4183 int1586_tick_end:
4184 ASM_END
4186 break;
4188 case 0xe8:
4189 switch(regs.u.r8.al)
4191 case 0x20: // coded by osmaker aka K.J.
4192 if(regs.u.r32.edx == 0x534D4150)
4194 extended_memory_size = inb_cmos(0x35);
4195 extended_memory_size <<= 8;
4196 extended_memory_size |= inb_cmos(0x34);
4197 extended_memory_size *= 64;
4198 // greater than EFF00000???
4199 if(extended_memory_size > 0x3bc000) {
4200 extended_memory_size = 0x3bc000; // everything after this is reserved memory until we get to 0x100000000
4202 extended_memory_size *= 1024;
4203 extended_memory_size += (16L * 1024 * 1024);
4205 if(extended_memory_size <= (16L * 1024 * 1024)) {
4206 extended_memory_size = inb_cmos(0x31);
4207 extended_memory_size <<= 8;
4208 extended_memory_size |= inb_cmos(0x30);
4209 extended_memory_size *= 1024;
4212 extra_lowbits_memory_size = inb_cmos(0x5c);
4213 extra_lowbits_memory_size <<= 8;
4214 extra_lowbits_memory_size |= inb_cmos(0x5b);
4215 extra_lowbits_memory_size *= 64;
4216 extra_lowbits_memory_size *= 1024;
4217 extra_highbits_memory_size = inb_cmos(0x5d);
4219 switch(regs.u.r16.bx)
4221 case 0:
4222 set_e820_range(ES, regs.u.r16.di,
4223 0x0000000L, 0x0009fc00L, 0, 0, 1);
4224 regs.u.r32.ebx = 1;
4225 regs.u.r32.eax = 0x534D4150;
4226 regs.u.r32.ecx = 0x14;
4227 CLEAR_CF();
4228 return;
4229 break;
4230 case 1:
4231 set_e820_range(ES, regs.u.r16.di,
4232 0x0009fc00L, 0x000a0000L, 0, 0, 2);
4233 regs.u.r32.ebx = 2;
4234 regs.u.r32.eax = 0x534D4150;
4235 regs.u.r32.ecx = 0x14;
4236 CLEAR_CF();
4237 return;
4238 break;
4239 case 2:
4240 set_e820_range(ES, regs.u.r16.di,
4241 0x000e8000L, 0x00100000L, 0, 0, 2);
4242 regs.u.r32.ebx = 3;
4243 regs.u.r32.eax = 0x534D4150;
4244 regs.u.r32.ecx = 0x14;
4245 CLEAR_CF();
4246 return;
4247 break;
4248 case 3:
4249 set_e820_range(ES, regs.u.r16.di,
4250 0x00100000L,
4251 extended_memory_size - ACPI_DATA_SIZE ,0, 0, 1);
4252 regs.u.r32.ebx = 4;
4253 regs.u.r32.eax = 0x534D4150;
4254 regs.u.r32.ecx = 0x14;
4255 CLEAR_CF();
4256 return;
4257 break;
4258 case 4:
4259 set_e820_range(ES, regs.u.r16.di,
4260 extended_memory_size - ACPI_DATA_SIZE,
4261 extended_memory_size ,0, 0, 3); // ACPI RAM
4262 regs.u.r32.ebx = 5;
4263 regs.u.r32.eax = 0x534D4150;
4264 regs.u.r32.ecx = 0x14;
4265 CLEAR_CF();
4266 return;
4267 break;
4268 case 5:
4269 /* 3 pages before the bios, we map the vmx tss pages */
4270 set_e820_range(ES, regs.u.r16.di, 0xfffbd000L,
4271 0xfffc0000L, 0, 0, 2);
4272 regs.u.r32.ebx = 6;
4273 regs.u.r32.eax = 0x534D4150;
4274 regs.u.r32.ecx = 0x14;
4275 CLEAR_CF();
4276 return;
4277 case 6:
4278 /* 256KB BIOS area at the end of 4 GB */
4279 set_e820_range(ES, regs.u.r16.di,
4280 0xfffc0000L, 0x00000000L ,0, 0, 2);
4281 if (extra_highbits_memory_size || extra_lowbits_memory_size)
4282 regs.u.r32.ebx = 7;
4283 else
4284 regs.u.r32.ebx = 0;
4285 regs.u.r32.eax = 0x534D4150;
4286 regs.u.r32.ecx = 0x14;
4287 CLEAR_CF();
4288 return;
4289 case 7:
4290 /* Maping of memory above 4 GB */
4291 set_e820_range(ES, regs.u.r16.di, 0x00000000L,
4292 extra_lowbits_memory_size, 1, extra_highbits_memory_size
4293 + 1, 1);
4294 regs.u.r32.ebx = 0;
4295 regs.u.r32.eax = 0x534D4150;
4296 regs.u.r32.ecx = 0x14;
4297 CLEAR_CF();
4298 return;
4299 default: /* AX=E820, DX=534D4150, BX unrecognized */
4300 goto int15_unimplemented;
4301 break;
4303 } else {
4304 // if DX != 0x534D4150)
4305 goto int15_unimplemented;
4307 break;
4309 case 0x01:
4310 // do we have any reason to fail here ?
4311 CLEAR_CF();
4313 // my real system sets ax and bx to 0
4314 // this is confirmed by Ralph Brown list
4315 // but syslinux v1.48 is known to behave
4316 // strangely if ax is set to 0
4317 // regs.u.r16.ax = 0;
4318 // regs.u.r16.bx = 0;
4320 // Get the amount of extended memory (above 1M)
4321 regs.u.r8.cl = inb_cmos(0x30);
4322 regs.u.r8.ch = inb_cmos(0x31);
4324 // limit to 15M
4325 if(regs.u.r16.cx > 0x3c00)
4327 regs.u.r16.cx = 0x3c00;
4330 // Get the amount of extended memory above 16M in 64k blocs
4331 regs.u.r8.dl = inb_cmos(0x34);
4332 regs.u.r8.dh = inb_cmos(0x35);
4334 // Set configured memory equal to extended memory
4335 regs.u.r16.ax = regs.u.r16.cx;
4336 regs.u.r16.bx = regs.u.r16.dx;
4337 break;
4338 default: /* AH=0xE8?? but not implemented */
4339 goto int15_unimplemented;
4341 break;
4342 int15_unimplemented:
4343 // fall into the default
4344 default:
4345 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4346 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4347 SET_CF();
4348 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4349 break;
4353 void
4354 int16_function(DI, SI, BP, SP, BX, DX, CX, AX, FLAGS)
4355 Bit16u DI, SI, BP, SP, BX, DX, CX, AX, FLAGS;
4357 Bit8u scan_code, ascii_code, shift_flags, led_flags, count;
4358 Bit16u kbd_code, max;
4360 BX_DEBUG_INT16("int16: AX=%04x BX=%04x CX=%04x DX=%04x \n", AX, BX, CX, DX);
4362 shift_flags = read_byte(0x0040, 0x17);
4363 led_flags = read_byte(0x0040, 0x97);
4364 if ((((shift_flags >> 4) & 0x07) ^ (led_flags & 0x07)) != 0) {
4365 ASM_START
4367 ASM_END
4368 outb(0x60, 0xed);
4369 while ((inb(0x64) & 0x01) == 0) outb(0x80, 0x21);
4370 if ((inb(0x60) == 0xfa)) {
4371 led_flags &= 0xf8;
4372 led_flags |= ((shift_flags >> 4) & 0x07);
4373 outb(0x60, led_flags & 0x07);
4374 while ((inb(0x64) & 0x01) == 0) outb(0x80, 0x21);
4375 inb(0x60);
4376 write_byte(0x0040, 0x97, led_flags);
4378 ASM_START
4380 ASM_END
4383 switch (GET_AH()) {
4384 case 0x00: /* read keyboard input */
4386 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4387 BX_PANIC("KBD: int16h: out of keyboard input\n");
4389 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4390 else if (ascii_code == 0xE0) ascii_code = 0;
4391 AX = (scan_code << 8) | ascii_code;
4392 break;
4394 case 0x01: /* check keyboard status */
4395 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4396 SET_ZF();
4397 return;
4399 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4400 else if (ascii_code == 0xE0) ascii_code = 0;
4401 AX = (scan_code << 8) | ascii_code;
4402 CLEAR_ZF();
4403 break;
4405 case 0x02: /* get shift flag status */
4406 shift_flags = read_byte(0x0040, 0x17);
4407 SET_AL(shift_flags);
4408 break;
4410 case 0x05: /* store key-stroke into buffer */
4411 if ( !enqueue_key(GET_CH(), GET_CL()) ) {
4412 SET_AL(1);
4414 else {
4415 SET_AL(0);
4417 break;
4419 case 0x09: /* GET KEYBOARD FUNCTIONALITY */
4420 // bit Bochs Description
4421 // 7 0 reserved
4422 // 6 0 INT 16/AH=20h-22h supported (122-key keyboard support)
4423 // 5 1 INT 16/AH=10h-12h supported (enhanced keyboard support)
4424 // 4 1 INT 16/AH=0Ah supported
4425 // 3 0 INT 16/AX=0306h supported
4426 // 2 0 INT 16/AX=0305h supported
4427 // 1 0 INT 16/AX=0304h supported
4428 // 0 0 INT 16/AX=0300h supported
4430 SET_AL(0x30);
4431 break;
4433 case 0x0A: /* GET KEYBOARD ID */
4434 count = 2;
4435 kbd_code = 0x0;
4436 outb(0x60, 0xf2);
4437 /* Wait for data */
4438 max=0xffff;
4439 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4440 if (max>0x0) {
4441 if ((inb(0x60) == 0xfa)) {
4442 do {
4443 max=0xffff;
4444 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4445 if (max>0x0) {
4446 kbd_code >>= 8;
4447 kbd_code |= (inb(0x60) << 8);
4449 } while (--count>0);
4452 BX=kbd_code;
4453 break;
4455 case 0x10: /* read MF-II keyboard input */
4457 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4458 BX_PANIC("KBD: int16h: out of keyboard input\n");
4460 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4461 AX = (scan_code << 8) | ascii_code;
4462 break;
4464 case 0x11: /* check MF-II keyboard status */
4465 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4466 SET_ZF();
4467 return;
4469 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4470 AX = (scan_code << 8) | ascii_code;
4471 CLEAR_ZF();
4472 break;
4474 case 0x12: /* get extended keyboard status */
4475 shift_flags = read_byte(0x0040, 0x17);
4476 SET_AL(shift_flags);
4477 shift_flags = read_byte(0x0040, 0x18) & 0x73;
4478 shift_flags |= read_byte(0x0040, 0x96) & 0x0c;
4479 SET_AH(shift_flags);
4480 BX_DEBUG_INT16("int16: func 12 sending %04x\n",AX);
4481 break;
4483 case 0x92: /* keyboard capability check called by DOS 5.0+ keyb */
4484 SET_AH(0x80); // function int16 ah=0x10-0x12 supported
4485 break;
4487 case 0xA2: /* 122 keys capability check called by DOS 5.0+ keyb */
4488 // don't change AH : function int16 ah=0x20-0x22 NOT supported
4489 break;
4491 case 0x6F:
4492 if (GET_AL() == 0x08)
4493 SET_AH(0x02); // unsupported, aka normal keyboard
4495 default:
4496 BX_INFO("KBD: unsupported int 16h function %02x\n", GET_AH());
4500 unsigned int
4501 dequeue_key(scan_code, ascii_code, incr)
4502 Bit8u *scan_code;
4503 Bit8u *ascii_code;
4504 unsigned int incr;
4506 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail;
4507 Bit16u ss;
4508 Bit8u acode, scode;
4510 #if BX_CPU < 2
4511 buffer_start = 0x001E;
4512 buffer_end = 0x003E;
4513 #else
4514 buffer_start = read_word(0x0040, 0x0080);
4515 buffer_end = read_word(0x0040, 0x0082);
4516 #endif
4518 buffer_head = read_word(0x0040, 0x001a);
4519 buffer_tail = read_word(0x0040, 0x001c);
4521 if (buffer_head != buffer_tail) {
4522 ss = get_SS();
4523 acode = read_byte(0x0040, buffer_head);
4524 scode = read_byte(0x0040, buffer_head+1);
4525 write_byte(ss, ascii_code, acode);
4526 write_byte(ss, scan_code, scode);
4528 if (incr) {
4529 buffer_head += 2;
4530 if (buffer_head >= buffer_end)
4531 buffer_head = buffer_start;
4532 write_word(0x0040, 0x001a, buffer_head);
4534 return(1);
4536 else {
4537 return(0);
4541 static char panic_msg_keyb_buffer_full[] = "%s: keyboard input buffer full\n";
4543 Bit8u
4544 inhibit_mouse_int_and_events()
4546 Bit8u command_byte, prev_command_byte;
4548 // Turn off IRQ generation and aux data line
4549 if ( inb(0x64) & 0x02 )
4550 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4551 outb(0x64, 0x20); // get command byte
4552 while ( (inb(0x64) & 0x01) != 0x01 );
4553 prev_command_byte = inb(0x60);
4554 command_byte = prev_command_byte;
4555 //while ( (inb(0x64) & 0x02) );
4556 if ( inb(0x64) & 0x02 )
4557 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4558 command_byte &= 0xfd; // turn off IRQ 12 generation
4559 command_byte |= 0x20; // disable mouse serial clock line
4560 outb(0x64, 0x60); // write command byte
4561 outb(0x60, command_byte);
4562 return(prev_command_byte);
4565 void
4566 enable_mouse_int_and_events()
4568 Bit8u command_byte;
4570 // Turn on IRQ generation and aux data line
4571 if ( inb(0x64) & 0x02 )
4572 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4573 outb(0x64, 0x20); // get command byte
4574 while ( (inb(0x64) & 0x01) != 0x01 );
4575 command_byte = inb(0x60);
4576 //while ( (inb(0x64) & 0x02) );
4577 if ( inb(0x64) & 0x02 )
4578 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4579 command_byte |= 0x02; // turn on IRQ 12 generation
4580 command_byte &= 0xdf; // enable mouse serial clock line
4581 outb(0x64, 0x60); // write command byte
4582 outb(0x60, command_byte);
4585 Bit8u
4586 send_to_mouse_ctrl(sendbyte)
4587 Bit8u sendbyte;
4589 Bit8u response;
4591 // wait for chance to write to ctrl
4592 if ( inb(0x64) & 0x02 )
4593 BX_PANIC(panic_msg_keyb_buffer_full,"sendmouse");
4594 outb(0x64, 0xD4);
4595 outb(0x60, sendbyte);
4596 return(0);
4600 Bit8u
4601 get_mouse_data(data)
4602 Bit8u *data;
4604 Bit8u response;
4605 Bit16u ss;
4607 while ( (inb(0x64) & 0x21) != 0x21 ) {
4610 response = inb(0x60);
4612 ss = get_SS();
4613 write_byte(ss, data, response);
4614 return(0);
4617 void
4618 set_kbd_command_byte(command_byte)
4619 Bit8u command_byte;
4621 if ( inb(0x64) & 0x02 )
4622 BX_PANIC(panic_msg_keyb_buffer_full,"setkbdcomm");
4623 outb(0x64, 0xD4);
4625 outb(0x64, 0x60); // write command byte
4626 outb(0x60, command_byte);
4629 void
4630 int09_function(DI, SI, BP, SP, BX, DX, CX, AX)
4631 Bit16u DI, SI, BP, SP, BX, DX, CX, AX;
4633 Bit8u scancode, asciicode, shift_flags;
4634 Bit8u mf2_flags, mf2_state;
4637 // DS has been set to F000 before call
4641 scancode = GET_AL();
4643 if (scancode == 0) {
4644 BX_INFO("KBD: int09 handler: AL=0\n");
4645 return;
4649 shift_flags = read_byte(0x0040, 0x17);
4650 mf2_flags = read_byte(0x0040, 0x18);
4651 mf2_state = read_byte(0x0040, 0x96);
4652 asciicode = 0;
4654 switch (scancode) {
4655 case 0x3a: /* Caps Lock press */
4656 shift_flags ^= 0x40;
4657 write_byte(0x0040, 0x17, shift_flags);
4658 mf2_flags |= 0x40;
4659 write_byte(0x0040, 0x18, mf2_flags);
4660 break;
4661 case 0xba: /* Caps Lock release */
4662 mf2_flags &= ~0x40;
4663 write_byte(0x0040, 0x18, mf2_flags);
4664 break;
4666 case 0x2a: /* L Shift press */
4667 shift_flags |= 0x02;
4668 write_byte(0x0040, 0x17, shift_flags);
4669 break;
4670 case 0xaa: /* L Shift release */
4671 shift_flags &= ~0x02;
4672 write_byte(0x0040, 0x17, shift_flags);
4673 break;
4675 case 0x36: /* R Shift press */
4676 shift_flags |= 0x01;
4677 write_byte(0x0040, 0x17, shift_flags);
4678 break;
4679 case 0xb6: /* R Shift release */
4680 shift_flags &= ~0x01;
4681 write_byte(0x0040, 0x17, shift_flags);
4682 break;
4684 case 0x1d: /* Ctrl press */
4685 if ((mf2_state & 0x01) == 0) {
4686 shift_flags |= 0x04;
4687 write_byte(0x0040, 0x17, shift_flags);
4688 if (mf2_state & 0x02) {
4689 mf2_state |= 0x04;
4690 write_byte(0x0040, 0x96, mf2_state);
4691 } else {
4692 mf2_flags |= 0x01;
4693 write_byte(0x0040, 0x18, mf2_flags);
4696 break;
4697 case 0x9d: /* Ctrl release */
4698 if ((mf2_state & 0x01) == 0) {
4699 shift_flags &= ~0x04;
4700 write_byte(0x0040, 0x17, shift_flags);
4701 if (mf2_state & 0x02) {
4702 mf2_state &= ~0x04;
4703 write_byte(0x0040, 0x96, mf2_state);
4704 } else {
4705 mf2_flags &= ~0x01;
4706 write_byte(0x0040, 0x18, mf2_flags);
4709 break;
4711 case 0x38: /* Alt press */
4712 shift_flags |= 0x08;
4713 write_byte(0x0040, 0x17, shift_flags);
4714 if (mf2_state & 0x02) {
4715 mf2_state |= 0x08;
4716 write_byte(0x0040, 0x96, mf2_state);
4717 } else {
4718 mf2_flags |= 0x02;
4719 write_byte(0x0040, 0x18, mf2_flags);
4721 break;
4722 case 0xb8: /* Alt release */
4723 shift_flags &= ~0x08;
4724 write_byte(0x0040, 0x17, shift_flags);
4725 if (mf2_state & 0x02) {
4726 mf2_state &= ~0x08;
4727 write_byte(0x0040, 0x96, mf2_state);
4728 } else {
4729 mf2_flags &= ~0x02;
4730 write_byte(0x0040, 0x18, mf2_flags);
4732 break;
4734 case 0x45: /* Num Lock press */
4735 if ((mf2_state & 0x03) == 0) {
4736 mf2_flags |= 0x20;
4737 write_byte(0x0040, 0x18, mf2_flags);
4738 shift_flags ^= 0x20;
4739 write_byte(0x0040, 0x17, shift_flags);
4741 break;
4742 case 0xc5: /* Num Lock release */
4743 if ((mf2_state & 0x03) == 0) {
4744 mf2_flags &= ~0x20;
4745 write_byte(0x0040, 0x18, mf2_flags);
4747 break;
4749 case 0x46: /* Scroll Lock press */
4750 mf2_flags |= 0x10;
4751 write_byte(0x0040, 0x18, mf2_flags);
4752 shift_flags ^= 0x10;
4753 write_byte(0x0040, 0x17, shift_flags);
4754 break;
4756 case 0xc6: /* Scroll Lock release */
4757 mf2_flags &= ~0x10;
4758 write_byte(0x0040, 0x18, mf2_flags);
4759 break;
4761 default:
4762 if (scancode & 0x80) {
4763 break; /* toss key releases ... */
4765 if (scancode > MAX_SCAN_CODE) {
4766 BX_INFO("KBD: int09h_handler(): unknown scancode read: 0x%02x!\n", scancode);
4767 return;
4769 if (shift_flags & 0x08) { /* ALT */
4770 asciicode = scan_to_scanascii[scancode].alt;
4771 scancode = scan_to_scanascii[scancode].alt >> 8;
4772 } else if (shift_flags & 0x04) { /* CONTROL */
4773 asciicode = scan_to_scanascii[scancode].control;
4774 scancode = scan_to_scanascii[scancode].control >> 8;
4775 } else if (((mf2_state & 0x02) > 0) && ((scancode >= 0x47) && (scancode <= 0x53))) {
4776 /* extended keys handling */
4777 asciicode = 0xe0;
4778 scancode = scan_to_scanascii[scancode].normal >> 8;
4779 } else if (shift_flags & 0x03) { /* LSHIFT + RSHIFT */
4780 /* check if lock state should be ignored
4781 * because a SHIFT key are pressed */
4783 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
4784 asciicode = scan_to_scanascii[scancode].normal;
4785 scancode = scan_to_scanascii[scancode].normal >> 8;
4786 } else {
4787 asciicode = scan_to_scanascii[scancode].shift;
4788 scancode = scan_to_scanascii[scancode].shift >> 8;
4790 } else {
4791 /* check if lock is on */
4792 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
4793 asciicode = scan_to_scanascii[scancode].shift;
4794 scancode = scan_to_scanascii[scancode].shift >> 8;
4795 } else {
4796 asciicode = scan_to_scanascii[scancode].normal;
4797 scancode = scan_to_scanascii[scancode].normal >> 8;
4800 if (scancode==0 && asciicode==0) {
4801 BX_INFO("KBD: int09h_handler(): scancode & asciicode are zero?\n");
4803 enqueue_key(scancode, asciicode);
4804 break;
4806 if ((scancode & 0x7f) != 0x1d) {
4807 mf2_state &= ~0x01;
4809 mf2_state &= ~0x02;
4810 write_byte(0x0040, 0x96, mf2_state);
4813 unsigned int
4814 enqueue_key(scan_code, ascii_code)
4815 Bit8u scan_code, ascii_code;
4817 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail, temp_tail;
4819 #if BX_CPU < 2
4820 buffer_start = 0x001E;
4821 buffer_end = 0x003E;
4822 #else
4823 buffer_start = read_word(0x0040, 0x0080);
4824 buffer_end = read_word(0x0040, 0x0082);
4825 #endif
4827 buffer_head = read_word(0x0040, 0x001A);
4828 buffer_tail = read_word(0x0040, 0x001C);
4830 temp_tail = buffer_tail;
4831 buffer_tail += 2;
4832 if (buffer_tail >= buffer_end)
4833 buffer_tail = buffer_start;
4835 if (buffer_tail == buffer_head) {
4836 return(0);
4839 write_byte(0x0040, temp_tail, ascii_code);
4840 write_byte(0x0040, temp_tail+1, scan_code);
4841 write_word(0x0040, 0x001C, buffer_tail);
4842 return(1);
4846 void
4847 int74_function(make_farcall, Z, Y, X, status)
4848 Bit16u make_farcall, Z, Y, X, status;
4850 Bit16u ebda_seg=read_word(0x0040,0x000E);
4851 Bit8u in_byte, index, package_count;
4852 Bit8u mouse_flags_1, mouse_flags_2;
4854 BX_DEBUG_INT74("entering int74_function\n");
4855 make_farcall = 0;
4857 in_byte = inb(0x64);
4858 if ( (in_byte & 0x21) != 0x21 ) {
4859 return;
4861 in_byte = inb(0x60);
4862 BX_DEBUG_INT74("int74: read byte %02x\n", in_byte);
4864 mouse_flags_1 = read_byte(ebda_seg, 0x0026);
4865 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4867 if ( (mouse_flags_2 & 0x80) != 0x80 ) {
4868 return;
4871 package_count = mouse_flags_2 & 0x07;
4872 index = mouse_flags_1 & 0x07;
4873 write_byte(ebda_seg, 0x28 + index, in_byte);
4875 if ( (index+1) >= package_count ) {
4876 BX_DEBUG_INT74("int74_function: make_farcall=1\n");
4877 status = read_byte(ebda_seg, 0x0028 + 0);
4878 X = read_byte(ebda_seg, 0x0028 + 1);
4879 Y = read_byte(ebda_seg, 0x0028 + 2);
4880 Z = 0;
4881 mouse_flags_1 = 0;
4882 // check if far call handler installed
4883 if (mouse_flags_2 & 0x80)
4884 make_farcall = 1;
4886 else {
4887 mouse_flags_1++;
4889 write_byte(ebda_seg, 0x0026, mouse_flags_1);
4892 #define SET_DISK_RET_STATUS(status) write_byte(0x0040, 0x0074, status)
4894 #if BX_USE_ATADRV
4896 void
4897 int13_harddisk(EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
4898 Bit16u EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
4900 Bit32u lba;
4901 Bit16u ebda_seg=read_word(0x0040,0x000E);
4902 Bit16u cylinder, head, sector;
4903 Bit16u segment, offset;
4904 Bit16u npc, nph, npspt, nlc, nlh, nlspt;
4905 Bit16u size, count;
4906 Bit8u device, status;
4908 BX_DEBUG_INT13_HD("int13_harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
4910 write_byte(0x0040, 0x008e, 0); // clear completion flag
4912 // basic check : device has to be defined
4913 if ( (GET_ELDL() < 0x80) || (GET_ELDL() >= 0x80 + BX_MAX_ATA_DEVICES) ) {
4914 BX_INFO("int13_harddisk: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
4915 goto int13_fail;
4918 // Get the ata channel
4919 device=read_byte(ebda_seg,&EbdaData->ata.hdidmap[GET_ELDL()-0x80]);
4921 // basic check : device has to be valid
4922 if (device >= BX_MAX_ATA_DEVICES) {
4923 BX_INFO("int13_harddisk: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
4924 goto int13_fail;
4927 switch (GET_AH()) {
4929 case 0x00: /* disk controller reset */
4930 ata_reset (device);
4931 goto int13_success;
4932 break;
4934 case 0x01: /* read disk status */
4935 status = read_byte(0x0040, 0x0074);
4936 SET_AH(status);
4937 SET_DISK_RET_STATUS(0);
4938 /* set CF if error status read */
4939 if (status) goto int13_fail_nostatus;
4940 else goto int13_success_noah;
4941 break;
4943 case 0x02: // read disk sectors
4944 case 0x03: // write disk sectors
4945 case 0x04: // verify disk sectors
4947 count = GET_AL();
4948 cylinder = GET_CH();
4949 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
4950 sector = (GET_CL() & 0x3f);
4951 head = GET_DH();
4953 segment = ES;
4954 offset = BX;
4956 if ((count > 128) || (count == 0) || (sector == 0)) {
4957 BX_INFO("int13_harddisk: function %02x, parameter out of range!\n",GET_AH());
4958 goto int13_fail;
4961 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
4962 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
4963 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
4965 // sanity check on cyl heads, sec
4966 if( (cylinder >= nlc) || (head >= nlh) || (sector > nlspt )) {
4967 BX_INFO("int13_harddisk: function %02x, parameters out of range %04x/%04x/%04x!\n", GET_AH(), cylinder, head, sector);
4968 goto int13_fail;
4971 // FIXME verify
4972 if ( GET_AH() == 0x04 ) goto int13_success;
4974 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
4975 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
4977 // if needed, translate lchs to lba, and execute command
4978 if ( (nph != nlh) || (npspt != nlspt)) {
4979 lba = ((((Bit32u)cylinder * (Bit32u)nlh) + (Bit32u)head) * (Bit32u)nlspt) + (Bit32u)sector - 1;
4980 sector = 0; // this forces the command to be lba
4983 if ( GET_AH() == 0x02 )
4984 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, cylinder, head, sector, lba, segment, offset);
4985 else
4986 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, cylinder, head, sector, lba, segment, offset);
4988 // Set nb of sector transferred
4989 SET_AL(read_word(ebda_seg, &EbdaData->ata.trsfsectors));
4991 if (status != 0) {
4992 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
4993 SET_AH(0x0c);
4994 goto int13_fail_noah;
4997 goto int13_success;
4998 break;
5000 case 0x05: /* format disk track */
5001 BX_INFO("format disk track called\n");
5002 goto int13_success;
5003 return;
5004 break;
5006 case 0x08: /* read disk drive parameters */
5008 // Get logical geometry from table
5009 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
5010 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
5011 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
5012 count = read_byte(ebda_seg, &EbdaData->ata.hdcount);
5014 nlc = nlc - 2; /* 0 based , last sector not used */
5015 SET_AL(0);
5016 SET_CH(nlc & 0xff);
5017 SET_CL(((nlc >> 2) & 0xc0) | (nlspt & 0x3f));
5018 SET_DH(nlh - 1);
5019 SET_DL(count); /* FIXME returns 0, 1, or n hard drives */
5021 // FIXME should set ES & DI
5023 goto int13_success;
5024 break;
5026 case 0x10: /* check drive ready */
5027 // should look at 40:8E also???
5029 // Read the status from controller
5030 status = inb(read_word(ebda_seg, &EbdaData->ata.channels[device/2].iobase1) + ATA_CB_STAT);
5031 if ( (status & ( ATA_CB_STAT_BSY | ATA_CB_STAT_RDY )) == ATA_CB_STAT_RDY ) {
5032 goto int13_success;
5034 else {
5035 SET_AH(0xAA);
5036 goto int13_fail_noah;
5038 break;
5040 case 0x15: /* read disk drive size */
5042 // Get physical geometry from table
5043 npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
5044 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
5045 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
5047 // Compute sector count seen by int13
5048 lba = (Bit32u)(npc - 1) * (Bit32u)nph * (Bit32u)npspt;
5049 CX = lba >> 16;
5050 DX = lba & 0xffff;
5052 SET_AH(3); // hard disk accessible
5053 goto int13_success_noah;
5054 break;
5056 case 0x41: // IBM/MS installation check
5057 BX=0xaa55; // install check
5058 SET_AH(0x30); // EDD 3.0
5059 CX=0x0007; // ext disk access and edd, removable supported
5060 goto int13_success_noah;
5061 break;
5063 case 0x42: // IBM/MS extended read
5064 case 0x43: // IBM/MS extended write
5065 case 0x44: // IBM/MS verify
5066 case 0x47: // IBM/MS extended seek
5068 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
5069 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
5070 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
5072 // Can't use 64 bits lba
5073 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
5074 if (lba != 0L) {
5075 BX_PANIC("int13_harddisk: function %02x. Can't use 64bits lba\n",GET_AH());
5076 goto int13_fail;
5079 // Get 32 bits lba and check
5080 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5081 if (lba >= read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors) ) {
5082 BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH());
5083 goto int13_fail;
5086 // If verify or seek
5087 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
5088 goto int13_success;
5090 // Execute the command
5091 if ( GET_AH() == 0x42 )
5092 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, 0, 0, 0, lba, segment, offset);
5093 else
5094 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, 0, 0, 0, lba, segment, offset);
5096 count=read_word(ebda_seg, &EbdaData->ata.trsfsectors);
5097 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5099 if (status != 0) {
5100 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
5101 SET_AH(0x0c);
5102 goto int13_fail_noah;
5105 goto int13_success;
5106 break;
5108 case 0x45: // IBM/MS lock/unlock drive
5109 case 0x49: // IBM/MS extended media change
5110 goto int13_success; // Always success for HD
5111 break;
5113 case 0x46: // IBM/MS eject media
5114 SET_AH(0xb2); // Volume Not Removable
5115 goto int13_fail_noah; // Always fail for HD
5116 break;
5118 case 0x48: // IBM/MS get drive parameters
5119 size=read_word(DS,SI+(Bit16u)&Int13DPT->size);
5121 // Buffer is too small
5122 if(size < 0x1a)
5123 goto int13_fail;
5125 // EDD 1.x
5126 if(size >= 0x1a) {
5127 Bit16u blksize;
5129 npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
5130 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
5131 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
5132 lba = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors);
5133 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5135 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5136 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x02); // geometry is valid
5137 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, (Bit32u)npc);
5138 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, (Bit32u)nph);
5139 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, (Bit32u)npspt);
5140 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, lba); // FIXME should be Bit64
5141 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0L);
5142 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5145 // EDD 2.x
5146 if(size >= 0x1e) {
5147 Bit8u channel, dev, irq, mode, checksum, i, translation;
5148 Bit16u iobase1, iobase2, options;
5150 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5152 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5153 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5155 // Fill in dpte
5156 channel = device / 2;
5157 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5158 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5159 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5160 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5161 translation = read_byte(ebda_seg, &EbdaData->ata.devices[device].translation);
5163 options = (translation==ATA_TRANSLATION_NONE?0:1<<3); // chs translation
5164 options |= (1<<4); // lba translation
5165 options |= (mode==ATA_MODE_PIO32?1:0<<7);
5166 options |= (translation==ATA_TRANSLATION_LBA?1:0<<9);
5167 options |= (translation==ATA_TRANSLATION_RECHS?3:0<<9);
5169 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5170 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2);
5171 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5172 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5173 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5174 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5175 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5176 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5177 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5178 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5179 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5181 checksum=0;
5182 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, (&EbdaData->ata.dpte) + i);
5183 checksum = ~checksum;
5184 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5187 // EDD 3.x
5188 if(size >= 0x42) {
5189 Bit8u channel, iface, checksum, i;
5190 Bit16u iobase1;
5192 channel = device / 2;
5193 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5194 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5196 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5197 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5198 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5199 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5200 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5202 if (iface==ATA_IFACE_ISA) {
5203 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5204 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5205 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5206 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5208 else {
5209 // FIXME PCI
5211 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5212 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5213 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5214 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5216 if (iface==ATA_IFACE_ISA) {
5217 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5218 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5219 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5221 else {
5222 // FIXME PCI
5224 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5225 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5226 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5227 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5229 checksum=0;
5230 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5231 checksum = ~checksum;
5232 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5235 goto int13_success;
5236 break;
5238 case 0x4e: // // IBM/MS set hardware configuration
5239 // DMA, prefetch, PIO maximum not supported
5240 switch (GET_AL()) {
5241 case 0x01:
5242 case 0x03:
5243 case 0x04:
5244 case 0x06:
5245 goto int13_success;
5246 break;
5247 default :
5248 goto int13_fail;
5250 break;
5252 case 0x09: /* initialize drive parameters */
5253 case 0x0c: /* seek to specified cylinder */
5254 case 0x0d: /* alternate disk reset */
5255 case 0x11: /* recalibrate */
5256 case 0x14: /* controller internal diagnostic */
5257 BX_INFO("int13_harddisk: function %02xh unimplemented, returns success\n", GET_AH());
5258 goto int13_success;
5259 break;
5261 case 0x0a: /* read disk sectors with ECC */
5262 case 0x0b: /* write disk sectors with ECC */
5263 case 0x18: // set media type for format
5264 case 0x50: // IBM/MS send packet command
5265 default:
5266 BX_INFO("int13_harddisk: function %02xh unsupported, returns fail\n", GET_AH());
5267 goto int13_fail;
5268 break;
5271 int13_fail:
5272 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5273 int13_fail_noah:
5274 SET_DISK_RET_STATUS(GET_AH());
5275 int13_fail_nostatus:
5276 SET_CF(); // error occurred
5277 return;
5279 int13_success:
5280 SET_AH(0x00); // no error
5281 int13_success_noah:
5282 SET_DISK_RET_STATUS(0x00);
5283 CLEAR_CF(); // no error
5284 return;
5287 // ---------------------------------------------------------------------------
5288 // Start of int13 for cdrom
5289 // ---------------------------------------------------------------------------
5291 void
5292 int13_cdrom(EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
5293 Bit16u EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
5295 Bit16u ebda_seg=read_word(0x0040,0x000E);
5296 Bit8u device, status, locks;
5297 Bit8u atacmd[12];
5298 Bit32u lba;
5299 Bit16u count, segment, offset, i, size;
5301 BX_DEBUG_INT13_CD("int13_cdrom: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5303 SET_DISK_RET_STATUS(0x00);
5305 /* basic check : device should be 0xE0+ */
5306 if( (GET_ELDL() < 0xE0) || (GET_ELDL() >= 0xE0+BX_MAX_ATA_DEVICES) ) {
5307 BX_INFO("int13_cdrom: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
5308 goto int13_fail;
5311 // Get the ata channel
5312 device=read_byte(ebda_seg,&EbdaData->ata.cdidmap[GET_ELDL()-0xE0]);
5314 /* basic check : device has to be valid */
5315 if (device >= BX_MAX_ATA_DEVICES) {
5316 BX_INFO("int13_cdrom: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
5317 goto int13_fail;
5320 switch (GET_AH()) {
5322 // all those functions return SUCCESS
5323 case 0x00: /* disk controller reset */
5324 case 0x09: /* initialize drive parameters */
5325 case 0x0c: /* seek to specified cylinder */
5326 case 0x0d: /* alternate disk reset */
5327 case 0x10: /* check drive ready */
5328 case 0x11: /* recalibrate */
5329 case 0x14: /* controller internal diagnostic */
5330 case 0x16: /* detect disk change */
5331 goto int13_success;
5332 break;
5334 // all those functions return disk write-protected
5335 case 0x03: /* write disk sectors */
5336 case 0x05: /* format disk track */
5337 case 0x43: // IBM/MS extended write
5338 SET_AH(0x03);
5339 goto int13_fail_noah;
5340 break;
5342 case 0x01: /* read disk status */
5343 status = read_byte(0x0040, 0x0074);
5344 SET_AH(status);
5345 SET_DISK_RET_STATUS(0);
5347 /* set CF if error status read */
5348 if (status) goto int13_fail_nostatus;
5349 else goto int13_success_noah;
5350 break;
5352 case 0x15: /* read disk drive size */
5353 SET_AH(0x02);
5354 goto int13_fail_noah;
5355 break;
5357 case 0x41: // IBM/MS installation check
5358 BX=0xaa55; // install check
5359 SET_AH(0x30); // EDD 2.1
5360 CX=0x0007; // ext disk access, removable and edd
5361 goto int13_success_noah;
5362 break;
5364 case 0x42: // IBM/MS extended read
5365 case 0x44: // IBM/MS verify sectors
5366 case 0x47: // IBM/MS extended seek
5368 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
5369 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
5370 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
5372 // Can't use 64 bits lba
5373 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
5374 if (lba != 0L) {
5375 BX_PANIC("int13_cdrom: function %02x. Can't use 64bits lba\n",GET_AH());
5376 goto int13_fail;
5379 // Get 32 bits lba
5380 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5382 // If verify or seek
5383 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
5384 goto int13_success;
5386 memsetb(get_SS(),atacmd,0,12);
5387 atacmd[0]=0x28; // READ command
5388 atacmd[7]=(count & 0xff00) >> 8; // Sectors
5389 atacmd[8]=(count & 0x00ff); // Sectors
5390 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
5391 atacmd[3]=(lba & 0x00ff0000) >> 16;
5392 atacmd[4]=(lba & 0x0000ff00) >> 8;
5393 atacmd[5]=(lba & 0x000000ff);
5394 status = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, count*2048L, ATA_DATA_IN, segment,offset);
5396 count = (Bit16u)(read_dword(ebda_seg, &EbdaData->ata.trsfbytes) >> 11);
5397 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5399 if (status != 0) {
5400 BX_INFO("int13_cdrom: function %02x, status %02x !\n",GET_AH(),status);
5401 SET_AH(0x0c);
5402 goto int13_fail_noah;
5405 goto int13_success;
5406 break;
5408 case 0x45: // IBM/MS lock/unlock drive
5409 if (GET_AL() > 2) goto int13_fail;
5411 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5413 switch (GET_AL()) {
5414 case 0 : // lock
5415 if (locks == 0xff) {
5416 SET_AH(0xb4);
5417 SET_AL(1);
5418 goto int13_fail_noah;
5420 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, ++locks);
5421 SET_AL(1);
5422 break;
5423 case 1 : // unlock
5424 if (locks == 0x00) {
5425 SET_AH(0xb0);
5426 SET_AL(0);
5427 goto int13_fail_noah;
5429 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, --locks);
5430 SET_AL(locks==0?0:1);
5431 break;
5432 case 2 : // status
5433 SET_AL(locks==0?0:1);
5434 break;
5436 goto int13_success;
5437 break;
5439 case 0x46: // IBM/MS eject media
5440 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5442 if (locks != 0) {
5443 SET_AH(0xb1); // media locked
5444 goto int13_fail_noah;
5446 // FIXME should handle 0x31 no media in device
5447 // FIXME should handle 0xb5 valid request failed
5449 // Call removable media eject
5450 ASM_START
5451 push bp
5452 mov bp, sp
5454 mov ah, #0x52
5455 int 15
5456 mov _int13_cdrom.status + 2[bp], ah
5457 jnc int13_cdrom_rme_end
5458 mov _int13_cdrom.status, #1
5459 int13_cdrom_rme_end:
5460 pop bp
5461 ASM_END
5463 if (status != 0) {
5464 SET_AH(0xb1); // media locked
5465 goto int13_fail_noah;
5468 goto int13_success;
5469 break;
5471 case 0x48: // IBM/MS get drive parameters
5472 size = read_word(DS,SI+(Bit16u)&Int13Ext->size);
5474 // Buffer is too small
5475 if(size < 0x1a)
5476 goto int13_fail;
5478 // EDD 1.x
5479 if(size >= 0x1a) {
5480 Bit16u cylinders, heads, spt, blksize;
5482 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5484 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5485 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x74); // removable, media change, lockable, max values
5486 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0xffffffff);
5487 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, 0xffffffff);
5488 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, 0xffffffff);
5489 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, 0xffffffff); // FIXME should be Bit64
5490 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0xffffffff);
5491 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5494 // EDD 2.x
5495 if(size >= 0x1e) {
5496 Bit8u channel, dev, irq, mode, checksum, i;
5497 Bit16u iobase1, iobase2, options;
5499 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5501 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5502 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5504 // Fill in dpte
5505 channel = device / 2;
5506 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5507 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5508 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5509 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5511 // FIXME atapi device
5512 options = (1<<4); // lba translation
5513 options |= (1<<5); // removable device
5514 options |= (1<<6); // atapi device
5515 options |= (mode==ATA_MODE_PIO32?1:0<<7);
5517 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5518 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2);
5519 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5520 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5521 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5522 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5523 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5524 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5525 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5526 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5527 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5529 checksum=0;
5530 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, (&EbdaData->ata.dpte) + i);
5531 checksum = ~checksum;
5532 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5535 // EDD 3.x
5536 if(size >= 0x42) {
5537 Bit8u channel, iface, checksum, i;
5538 Bit16u iobase1;
5540 channel = device / 2;
5541 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5542 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5544 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5545 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5546 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5547 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5548 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5550 if (iface==ATA_IFACE_ISA) {
5551 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5552 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5553 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5554 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5556 else {
5557 // FIXME PCI
5559 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5560 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5561 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5562 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5564 if (iface==ATA_IFACE_ISA) {
5565 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5566 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5567 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5569 else {
5570 // FIXME PCI
5572 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5573 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5574 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5575 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5577 checksum=0;
5578 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5579 checksum = ~checksum;
5580 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5583 goto int13_success;
5584 break;
5586 case 0x49: // IBM/MS extended media change
5587 // always send changed ??
5588 SET_AH(06);
5589 goto int13_fail_nostatus;
5590 break;
5592 case 0x4e: // // IBM/MS set hardware configuration
5593 // DMA, prefetch, PIO maximum not supported
5594 switch (GET_AL()) {
5595 case 0x01:
5596 case 0x03:
5597 case 0x04:
5598 case 0x06:
5599 goto int13_success;
5600 break;
5601 default :
5602 goto int13_fail;
5604 break;
5606 // all those functions return unimplemented
5607 case 0x02: /* read sectors */
5608 case 0x04: /* verify sectors */
5609 case 0x08: /* read disk drive parameters */
5610 case 0x0a: /* read disk sectors with ECC */
5611 case 0x0b: /* write disk sectors with ECC */
5612 case 0x18: /* set media type for format */
5613 case 0x50: // ? - send packet command
5614 default:
5615 BX_INFO("int13_cdrom: unsupported AH=%02x\n", GET_AH());
5616 goto int13_fail;
5617 break;
5620 int13_fail:
5621 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5622 int13_fail_noah:
5623 SET_DISK_RET_STATUS(GET_AH());
5624 int13_fail_nostatus:
5625 SET_CF(); // error occurred
5626 return;
5628 int13_success:
5629 SET_AH(0x00); // no error
5630 int13_success_noah:
5631 SET_DISK_RET_STATUS(0x00);
5632 CLEAR_CF(); // no error
5633 return;
5636 // ---------------------------------------------------------------------------
5637 // End of int13 for cdrom
5638 // ---------------------------------------------------------------------------
5640 #if BX_ELTORITO_BOOT
5641 // ---------------------------------------------------------------------------
5642 // Start of int13 for eltorito functions
5643 // ---------------------------------------------------------------------------
5645 void
5646 int13_eltorito(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
5647 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
5649 Bit16u ebda_seg=read_word(0x0040,0x000E);
5651 BX_DEBUG_INT13_ET("int13_eltorito: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5652 // BX_DEBUG_INT13_ET("int13_eltorito: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
5654 switch (GET_AH()) {
5656 // FIXME ElTorito Various. Should be implemented
5657 case 0x4a: // ElTorito - Initiate disk emu
5658 case 0x4c: // ElTorito - Initiate disk emu and boot
5659 case 0x4d: // ElTorito - Return Boot catalog
5660 BX_PANIC("Int13 eltorito call with AX=%04x. Please report\n",AX);
5661 goto int13_fail;
5662 break;
5664 case 0x4b: // ElTorito - Terminate disk emu
5665 // FIXME ElTorito Hardcoded
5666 write_byte(DS,SI+0x00,0x13);
5667 write_byte(DS,SI+0x01,read_byte(ebda_seg,&EbdaData->cdemu.media));
5668 write_byte(DS,SI+0x02,read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
5669 write_byte(DS,SI+0x03,read_byte(ebda_seg,&EbdaData->cdemu.controller_index));
5670 write_dword(DS,SI+0x04,read_dword(ebda_seg,&EbdaData->cdemu.ilba));
5671 write_word(DS,SI+0x08,read_word(ebda_seg,&EbdaData->cdemu.device_spec));
5672 write_word(DS,SI+0x0a,read_word(ebda_seg,&EbdaData->cdemu.buffer_segment));
5673 write_word(DS,SI+0x0c,read_word(ebda_seg,&EbdaData->cdemu.load_segment));
5674 write_word(DS,SI+0x0e,read_word(ebda_seg,&EbdaData->cdemu.sector_count));
5675 write_byte(DS,SI+0x10,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.cylinders));
5676 write_byte(DS,SI+0x11,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.spt));
5677 write_byte(DS,SI+0x12,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.heads));
5679 // If we have to terminate emulation
5680 if(GET_AL() == 0x00) {
5681 // FIXME ElTorito Various. Should be handled accordingly to spec
5682 write_byte(ebda_seg,&EbdaData->cdemu.active, 0x00); // bye bye
5685 goto int13_success;
5686 break;
5688 default:
5689 BX_INFO("int13_eltorito: unsupported AH=%02x\n", GET_AH());
5690 goto int13_fail;
5691 break;
5694 int13_fail:
5695 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5696 SET_DISK_RET_STATUS(GET_AH());
5697 SET_CF(); // error occurred
5698 return;
5700 int13_success:
5701 SET_AH(0x00); // no error
5702 SET_DISK_RET_STATUS(0x00);
5703 CLEAR_CF(); // no error
5704 return;
5707 // ---------------------------------------------------------------------------
5708 // End of int13 for eltorito functions
5709 // ---------------------------------------------------------------------------
5711 // ---------------------------------------------------------------------------
5712 // Start of int13 when emulating a device from the cd
5713 // ---------------------------------------------------------------------------
5715 void
5716 int13_cdemu(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
5717 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
5719 Bit16u ebda_seg=read_word(0x0040,0x000E);
5720 Bit8u device, status;
5721 Bit16u vheads, vspt, vcylinders;
5722 Bit16u head, sector, cylinder, nbsectors;
5723 Bit32u vlba, ilba, slba, elba;
5724 Bit16u before, segment, offset;
5725 Bit8u atacmd[12];
5727 BX_DEBUG_INT13_ET("int13_cdemu: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5729 /* at this point, we are emulating a floppy/harddisk */
5731 // Recompute the device number
5732 device = read_byte(ebda_seg,&EbdaData->cdemu.controller_index) * 2;
5733 device += read_byte(ebda_seg,&EbdaData->cdemu.device_spec);
5735 SET_DISK_RET_STATUS(0x00);
5737 /* basic checks : emulation should be active, dl should equal the emulated drive */
5738 if( (read_byte(ebda_seg,&EbdaData->cdemu.active) ==0 )
5739 || (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive ) != GET_DL())) {
5740 BX_INFO("int13_cdemu: function %02x, emulation not active for DL= %02x\n", GET_AH(), GET_DL());
5741 goto int13_fail;
5744 switch (GET_AH()) {
5746 // all those functions return SUCCESS
5747 case 0x00: /* disk controller reset */
5748 case 0x09: /* initialize drive parameters */
5749 case 0x0c: /* seek to specified cylinder */
5750 case 0x0d: /* alternate disk reset */ // FIXME ElTorito Various. should really reset ?
5751 case 0x10: /* check drive ready */ // FIXME ElTorito Various. should check if ready ?
5752 case 0x11: /* recalibrate */
5753 case 0x14: /* controller internal diagnostic */
5754 case 0x16: /* detect disk change */
5755 goto int13_success;
5756 break;
5758 // all those functions return disk write-protected
5759 case 0x03: /* write disk sectors */
5760 case 0x05: /* format disk track */
5761 SET_AH(0x03);
5762 goto int13_fail_noah;
5763 break;
5765 case 0x01: /* read disk status */
5766 status=read_byte(0x0040, 0x0074);
5767 SET_AH(status);
5768 SET_DISK_RET_STATUS(0);
5770 /* set CF if error status read */
5771 if (status) goto int13_fail_nostatus;
5772 else goto int13_success_noah;
5773 break;
5775 case 0x02: // read disk sectors
5776 case 0x04: // verify disk sectors
5777 vspt = read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
5778 vcylinders = read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders);
5779 vheads = read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads);
5781 ilba = read_dword(ebda_seg,&EbdaData->cdemu.ilba);
5783 sector = GET_CL() & 0x003f;
5784 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
5785 head = GET_DH();
5786 nbsectors = GET_AL();
5787 segment = ES;
5788 offset = BX;
5790 // no sector to read ?
5791 if(nbsectors==0) goto int13_success;
5793 // sanity checks sco openserver needs this!
5794 if ((sector > vspt)
5795 || (cylinder >= vcylinders)
5796 || (head >= vheads)) {
5797 goto int13_fail;
5800 // After controls, verify do nothing
5801 if (GET_AH() == 0x04) goto int13_success;
5803 segment = ES+(BX / 16);
5804 offset = BX % 16;
5806 // calculate the virtual lba inside the image
5807 vlba=((((Bit32u)cylinder*(Bit32u)vheads)+(Bit32u)head)*(Bit32u)vspt)+((Bit32u)(sector-1));
5809 // In advance so we don't loose the count
5810 SET_AL(nbsectors);
5812 // start lba on cd
5813 slba = (Bit32u)vlba/4;
5814 before= (Bit16u)vlba%4;
5816 // end lba on cd
5817 elba = (Bit32u)(vlba+nbsectors-1)/4;
5819 memsetb(get_SS(),atacmd,0,12);
5820 atacmd[0]=0x28; // READ command
5821 atacmd[7]=((Bit16u)(elba-slba+1) & 0xff00) >> 8; // Sectors
5822 atacmd[8]=((Bit16u)(elba-slba+1) & 0x00ff); // Sectors
5823 atacmd[2]=(ilba+slba & 0xff000000) >> 24; // LBA
5824 atacmd[3]=(ilba+slba & 0x00ff0000) >> 16;
5825 atacmd[4]=(ilba+slba & 0x0000ff00) >> 8;
5826 atacmd[5]=(ilba+slba & 0x000000ff);
5827 if((status = ata_cmd_packet(device, 12, get_SS(), atacmd, before*512, nbsectors*512L, ATA_DATA_IN, segment,offset)) != 0) {
5828 BX_INFO("int13_cdemu: function %02x, error %02x !\n",GET_AH(),status);
5829 SET_AH(0x02);
5830 SET_AL(0);
5831 goto int13_fail_noah;
5834 goto int13_success;
5835 break;
5837 case 0x08: /* read disk drive parameters */
5838 vspt=read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
5839 vcylinders=read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders) - 1;
5840 vheads=read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads) - 1;
5842 SET_AL( 0x00 );
5843 SET_BL( 0x00 );
5844 SET_CH( vcylinders & 0xff );
5845 SET_CL((( vcylinders >> 2) & 0xc0) | ( vspt & 0x3f ));
5846 SET_DH( vheads );
5847 SET_DL( 0x02 ); // FIXME ElTorito Various. should send the real count of drives 1 or 2
5848 // FIXME ElTorito Harddisk. should send the HD count
5850 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
5851 case 0x01: SET_BL( 0x02 ); break;
5852 case 0x02: SET_BL( 0x04 ); break;
5853 case 0x03: SET_BL( 0x06 ); break;
5856 ASM_START
5857 push bp
5858 mov bp, sp
5859 mov ax, #diskette_param_table2
5860 mov _int13_cdemu.DI+2[bp], ax
5861 mov _int13_cdemu.ES+2[bp], cs
5862 pop bp
5863 ASM_END
5864 goto int13_success;
5865 break;
5867 case 0x15: /* read disk drive size */
5868 // FIXME ElTorito Harddisk. What geometry to send ?
5869 SET_AH(0x03);
5870 goto int13_success_noah;
5871 break;
5873 // all those functions return unimplemented
5874 case 0x0a: /* read disk sectors with ECC */
5875 case 0x0b: /* write disk sectors with ECC */
5876 case 0x18: /* set media type for format */
5877 case 0x41: // IBM/MS installation check
5878 // FIXME ElTorito Harddisk. Darwin would like to use EDD
5879 case 0x42: // IBM/MS extended read
5880 case 0x43: // IBM/MS extended write
5881 case 0x44: // IBM/MS verify sectors
5882 case 0x45: // IBM/MS lock/unlock drive
5883 case 0x46: // IBM/MS eject media
5884 case 0x47: // IBM/MS extended seek
5885 case 0x48: // IBM/MS get drive parameters
5886 case 0x49: // IBM/MS extended media change
5887 case 0x4e: // ? - set hardware configuration
5888 case 0x50: // ? - send packet command
5889 default:
5890 BX_INFO("int13_cdemu function AH=%02x unsupported, returns fail\n", GET_AH());
5891 goto int13_fail;
5892 break;
5895 int13_fail:
5896 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5897 int13_fail_noah:
5898 SET_DISK_RET_STATUS(GET_AH());
5899 int13_fail_nostatus:
5900 SET_CF(); // error occurred
5901 return;
5903 int13_success:
5904 SET_AH(0x00); // no error
5905 int13_success_noah:
5906 SET_DISK_RET_STATUS(0x00);
5907 CLEAR_CF(); // no error
5908 return;
5911 // ---------------------------------------------------------------------------
5912 // End of int13 when emulating a device from the cd
5913 // ---------------------------------------------------------------------------
5915 #endif // BX_ELTORITO_BOOT
5917 #else //BX_USE_ATADRV
5919 void
5920 outLBA(cylinder,hd_heads,head,hd_sectors,sector,dl)
5921 Bit16u cylinder;
5922 Bit16u hd_heads;
5923 Bit16u head;
5924 Bit16u hd_sectors;
5925 Bit16u sector;
5926 Bit16u dl;
5928 ASM_START
5929 push bp
5930 mov bp, sp
5931 push eax
5932 push ebx
5933 push edx
5934 xor eax,eax
5935 mov ax,4[bp] // cylinder
5936 xor ebx,ebx
5937 mov bl,6[bp] // hd_heads
5938 imul ebx
5940 mov bl,8[bp] // head
5941 add eax,ebx
5942 mov bl,10[bp] // hd_sectors
5943 imul ebx
5944 mov bl,12[bp] // sector
5945 add eax,ebx
5947 dec eax
5948 mov dx,#0x1f3
5949 out dx,al
5950 mov dx,#0x1f4
5951 mov al,ah
5952 out dx,al
5953 shr eax,#16
5954 mov dx,#0x1f5
5955 out dx,al
5956 and ah,#0xf
5957 mov bl,14[bp] // dl
5958 and bl,#1
5959 shl bl,#4
5960 or ah,bl
5961 or ah,#0xe0
5962 mov al,ah
5963 mov dx,#0x01f6
5964 out dx,al
5965 pop edx
5966 pop ebx
5967 pop eax
5968 pop bp
5969 ASM_END
5972 void
5973 int13_harddisk(EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
5974 Bit16u EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
5976 Bit8u drive, num_sectors, sector, head, status, mod;
5977 Bit8u drive_map;
5978 Bit8u n_drives;
5979 Bit16u cyl_mod, ax;
5980 Bit16u max_cylinder, cylinder, total_sectors;
5981 Bit16u hd_cylinders;
5982 Bit8u hd_heads, hd_sectors;
5983 Bit16u val16;
5984 Bit8u sector_count;
5985 unsigned int i;
5986 Bit16u tempbx;
5987 Bit16u dpsize;
5989 Bit16u count, segment, offset;
5990 Bit32u lba;
5991 Bit16u error;
5993 BX_DEBUG_INT13_HD("int13 harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5995 write_byte(0x0040, 0x008e, 0); // clear completion flag
5997 /* at this point, DL is >= 0x80 to be passed from the floppy int13h
5998 handler code */
5999 /* check how many disks first (cmos reg 0x12), return an error if
6000 drive not present */
6001 drive_map = inb_cmos(0x12);
6002 drive_map = (((drive_map & 0xf0)==0) ? 0 : 1) |
6003 (((drive_map & 0x0f)==0) ? 0 : 2);
6004 n_drives = (drive_map==0) ? 0 :
6005 ((drive_map==3) ? 2 : 1);
6007 if (!(drive_map & (1<<(GET_ELDL()&0x7f)))) { /* allow 0, 1, or 2 disks */
6008 SET_AH(0x01);
6009 SET_DISK_RET_STATUS(0x01);
6010 SET_CF(); /* error occurred */
6011 return;
6014 switch (GET_AH()) {
6016 case 0x00: /* disk controller reset */
6017 BX_DEBUG_INT13_HD("int13_f00\n");
6019 SET_AH(0);
6020 SET_DISK_RET_STATUS(0);
6021 set_diskette_ret_status(0);
6022 set_diskette_current_cyl(0, 0); /* current cylinder, diskette 1 */
6023 set_diskette_current_cyl(1, 0); /* current cylinder, diskette 2 */
6024 CLEAR_CF(); /* successful */
6025 return;
6026 break;
6028 case 0x01: /* read disk status */
6029 BX_DEBUG_INT13_HD("int13_f01\n");
6030 status = read_byte(0x0040, 0x0074);
6031 SET_AH(status);
6032 SET_DISK_RET_STATUS(0);
6033 /* set CF if error status read */
6034 if (status) SET_CF();
6035 else CLEAR_CF();
6036 return;
6037 break;
6039 case 0x04: // verify disk sectors
6040 case 0x02: // read disk sectors
6041 drive = GET_ELDL();
6042 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6044 num_sectors = GET_AL();
6045 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
6046 sector = (GET_CL() & 0x3f);
6047 head = GET_DH();
6050 if (hd_cylinders > 1024) {
6051 if (hd_cylinders <= 2048) {
6052 cylinder <<= 1;
6054 else if (hd_cylinders <= 4096) {
6055 cylinder <<= 2;
6057 else if (hd_cylinders <= 8192) {
6058 cylinder <<= 3;
6060 else { // hd_cylinders <= 16384
6061 cylinder <<= 4;
6064 ax = head / hd_heads;
6065 cyl_mod = ax & 0xff;
6066 head = ax >> 8;
6067 cylinder |= cyl_mod;
6070 if ( (cylinder >= hd_cylinders) ||
6071 (sector > hd_sectors) ||
6072 (head >= hd_heads) ) {
6073 SET_AH(1);
6074 SET_DISK_RET_STATUS(1);
6075 SET_CF(); /* error occurred */
6076 return;
6079 if ( (num_sectors > 128) || (num_sectors == 0) )
6080 BX_PANIC("int13_harddisk: num_sectors out of range!\n");
6082 if (head > 15)
6083 BX_PANIC("hard drive BIOS:(read/verify) head > 15\n");
6085 if ( GET_AH() == 0x04 ) {
6086 SET_AH(0);
6087 SET_DISK_RET_STATUS(0);
6088 CLEAR_CF();
6089 return;
6092 status = inb(0x1f7);
6093 if (status & 0x80) {
6094 BX_PANIC("hard drive BIOS:(read/verify) BUSY bit set\n");
6096 outb(0x01f2, num_sectors);
6097 /* activate LBA? (tomv) */
6098 if (hd_heads > 16) {
6099 BX_DEBUG_INT13_HD("CHS: %x %x %x\n", cylinder, head, sector);
6100 outLBA(cylinder,hd_heads,head,hd_sectors,sector,drive);
6102 else {
6103 outb(0x01f3, sector);
6104 outb(0x01f4, cylinder & 0x00ff);
6105 outb(0x01f5, cylinder >> 8);
6106 outb(0x01f6, 0xa0 | ((drive & 0x01)<<4) | (head & 0x0f));
6108 outb(0x01f7, 0x20);
6110 while (1) {
6111 status = inb(0x1f7);
6112 if ( !(status & 0x80) ) break;
6115 if (status & 0x01) {
6116 BX_PANIC("hard drive BIOS:(read/verify) read error\n");
6117 } else if ( !(status & 0x08) ) {
6118 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
6119 BX_PANIC("hard drive BIOS:(read/verify) expected DRQ=1\n");
6122 sector_count = 0;
6123 tempbx = BX;
6125 ASM_START
6126 sti ;; enable higher priority interrupts
6127 ASM_END
6129 while (1) {
6130 ASM_START
6131 ;; store temp bx in real DI register
6132 push bp
6133 mov bp, sp
6134 mov di, _int13_harddisk.tempbx + 2 [bp]
6135 pop bp
6137 ;; adjust if there will be an overrun
6138 cmp di, #0xfe00
6139 jbe i13_f02_no_adjust
6140 i13_f02_adjust:
6141 sub di, #0x0200 ; sub 512 bytes from offset
6142 mov ax, es
6143 add ax, #0x0020 ; add 512 to segment
6144 mov es, ax
6146 i13_f02_no_adjust:
6147 mov cx, #0x0100 ;; counter (256 words = 512b)
6148 mov dx, #0x01f0 ;; AT data read port
6151 insw ;; CX words transfered from port(DX) to ES:[DI]
6153 i13_f02_done:
6154 ;; store real DI register back to temp bx
6155 push bp
6156 mov bp, sp
6157 mov _int13_harddisk.tempbx + 2 [bp], di
6158 pop bp
6159 ASM_END
6161 sector_count++;
6162 num_sectors--;
6163 if (num_sectors == 0) {
6164 status = inb(0x1f7);
6165 if ( (status & 0xc9) != 0x40 )
6166 BX_PANIC("no sectors left to read/verify, status is %02x\n", (unsigned) status);
6167 break;
6169 else {
6170 status = inb(0x1f7);
6171 if ( (status & 0xc9) != 0x48 )
6172 BX_PANIC("more sectors left to read/verify, status is %02x\n", (unsigned) status);
6173 continue;
6177 SET_AH(0);
6178 SET_DISK_RET_STATUS(0);
6179 SET_AL(sector_count);
6180 CLEAR_CF(); /* successful */
6181 return;
6182 break;
6185 case 0x03: /* write disk sectors */
6186 BX_DEBUG_INT13_HD("int13_f03\n");
6187 drive = GET_ELDL ();
6188 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6190 num_sectors = GET_AL();
6191 cylinder = GET_CH();
6192 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
6193 sector = (GET_CL() & 0x3f);
6194 head = GET_DH();
6196 if (hd_cylinders > 1024) {
6197 if (hd_cylinders <= 2048) {
6198 cylinder <<= 1;
6200 else if (hd_cylinders <= 4096) {
6201 cylinder <<= 2;
6203 else if (hd_cylinders <= 8192) {
6204 cylinder <<= 3;
6206 else { // hd_cylinders <= 16384
6207 cylinder <<= 4;
6210 ax = head / hd_heads;
6211 cyl_mod = ax & 0xff;
6212 head = ax >> 8;
6213 cylinder |= cyl_mod;
6216 if ( (cylinder >= hd_cylinders) ||
6217 (sector > hd_sectors) ||
6218 (head >= hd_heads) ) {
6219 SET_AH( 1);
6220 SET_DISK_RET_STATUS(1);
6221 SET_CF(); /* error occurred */
6222 return;
6225 if ( (num_sectors > 128) || (num_sectors == 0) )
6226 BX_PANIC("int13_harddisk: num_sectors out of range!\n");
6228 if (head > 15)
6229 BX_PANIC("hard drive BIOS:(read) head > 15\n");
6231 status = inb(0x1f7);
6232 if (status & 0x80) {
6233 BX_PANIC("hard drive BIOS:(read) BUSY bit set\n");
6235 // should check for Drive Ready Bit also in status reg
6236 outb(0x01f2, num_sectors);
6238 /* activate LBA? (tomv) */
6239 if (hd_heads > 16) {
6240 BX_DEBUG_INT13_HD("CHS (write): %x %x %x\n", cylinder, head, sector);
6241 outLBA(cylinder,hd_heads,head,hd_sectors,sector,GET_ELDL());
6243 else {
6244 outb(0x01f3, sector);
6245 outb(0x01f4, cylinder & 0x00ff);
6246 outb(0x01f5, cylinder >> 8);
6247 outb(0x01f6, 0xa0 | ((GET_ELDL() & 0x01)<<4) | (head & 0x0f));
6249 outb(0x01f7, 0x30);
6251 // wait for busy bit to turn off after seeking
6252 while (1) {
6253 status = inb(0x1f7);
6254 if ( !(status & 0x80) ) break;
6257 if ( !(status & 0x08) ) {
6258 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
6259 BX_PANIC("hard drive BIOS:(write) data-request bit not set\n");
6262 sector_count = 0;
6263 tempbx = BX;
6265 ASM_START
6266 sti ;; enable higher priority interrupts
6267 ASM_END
6269 while (1) {
6270 ASM_START
6271 ;; store temp bx in real SI register
6272 push bp
6273 mov bp, sp
6274 mov si, _int13_harddisk.tempbx + 2 [bp]
6275 pop bp
6277 ;; adjust if there will be an overrun
6278 cmp si, #0xfe00
6279 jbe i13_f03_no_adjust
6280 i13_f03_adjust:
6281 sub si, #0x0200 ; sub 512 bytes from offset
6282 mov ax, es
6283 add ax, #0x0020 ; add 512 to segment
6284 mov es, ax
6286 i13_f03_no_adjust:
6287 mov cx, #0x0100 ;; counter (256 words = 512b)
6288 mov dx, #0x01f0 ;; AT data read port
6290 seg ES
6292 outsw ;; CX words tranfered from ES:[SI] to port(DX)
6294 ;; store real SI register back to temp bx
6295 push bp
6296 mov bp, sp
6297 mov _int13_harddisk.tempbx + 2 [bp], si
6298 pop bp
6299 ASM_END
6301 sector_count++;
6302 num_sectors--;
6303 if (num_sectors == 0) {
6304 status = inb(0x1f7);
6305 if ( (status & 0xe9) != 0x40 )
6306 BX_PANIC("no sectors left to write, status is %02x\n", (unsigned) status);
6307 break;
6309 else {
6310 status = inb(0x1f7);
6311 if ( (status & 0xc9) != 0x48 )
6312 BX_PANIC("more sectors left to write, status is %02x\n", (unsigned) status);
6313 continue;
6317 SET_AH(0);
6318 SET_DISK_RET_STATUS(0);
6319 SET_AL(sector_count);
6320 CLEAR_CF(); /* successful */
6321 return;
6322 break;
6324 case 0x05: /* format disk track */
6325 BX_DEBUG_INT13_HD("int13_f05\n");
6326 BX_PANIC("format disk track called\n");
6327 /* nop */
6328 SET_AH(0);
6329 SET_DISK_RET_STATUS(0);
6330 CLEAR_CF(); /* successful */
6331 return;
6332 break;
6334 case 0x08: /* read disk drive parameters */
6335 BX_DEBUG_INT13_HD("int13_f08\n");
6337 drive = GET_ELDL ();
6338 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6340 // translate CHS
6342 if (hd_cylinders <= 1024) {
6343 // hd_cylinders >>= 0;
6344 // hd_heads <<= 0;
6346 else if (hd_cylinders <= 2048) {
6347 hd_cylinders >>= 1;
6348 hd_heads <<= 1;
6350 else if (hd_cylinders <= 4096) {
6351 hd_cylinders >>= 2;
6352 hd_heads <<= 2;
6354 else if (hd_cylinders <= 8192) {
6355 hd_cylinders >>= 3;
6356 hd_heads <<= 3;
6358 else { // hd_cylinders <= 16384
6359 hd_cylinders >>= 4;
6360 hd_heads <<= 4;
6363 max_cylinder = hd_cylinders - 2; /* 0 based */
6364 SET_AL(0);
6365 SET_CH(max_cylinder & 0xff);
6366 SET_CL(((max_cylinder >> 2) & 0xc0) | (hd_sectors & 0x3f));
6367 SET_DH(hd_heads - 1);
6368 SET_DL(n_drives); /* returns 0, 1, or 2 hard drives */
6369 SET_AH(0);
6370 SET_DISK_RET_STATUS(0);
6371 CLEAR_CF(); /* successful */
6373 return;
6374 break;
6376 case 0x09: /* initialize drive parameters */
6377 BX_DEBUG_INT13_HD("int13_f09\n");
6378 SET_AH(0);
6379 SET_DISK_RET_STATUS(0);
6380 CLEAR_CF(); /* successful */
6381 return;
6382 break;
6384 case 0x0a: /* read disk sectors with ECC */
6385 BX_DEBUG_INT13_HD("int13_f0a\n");
6386 case 0x0b: /* write disk sectors with ECC */
6387 BX_DEBUG_INT13_HD("int13_f0b\n");
6388 BX_PANIC("int13h Functions 0Ah & 0Bh not implemented!\n");
6389 return;
6390 break;
6392 case 0x0c: /* seek to specified cylinder */
6393 BX_DEBUG_INT13_HD("int13_f0c\n");
6394 BX_INFO("int13h function 0ch (seek) not implemented!\n");
6395 SET_AH(0);
6396 SET_DISK_RET_STATUS(0);
6397 CLEAR_CF(); /* successful */
6398 return;
6399 break;
6401 case 0x0d: /* alternate disk reset */
6402 BX_DEBUG_INT13_HD("int13_f0d\n");
6403 SET_AH(0);
6404 SET_DISK_RET_STATUS(0);
6405 CLEAR_CF(); /* successful */
6406 return;
6407 break;
6409 case 0x10: /* check drive ready */
6410 BX_DEBUG_INT13_HD("int13_f10\n");
6411 //SET_AH(0);
6412 //SET_DISK_RET_STATUS(0);
6413 //CLEAR_CF(); /* successful */
6414 //return;
6415 //break;
6417 // should look at 40:8E also???
6418 status = inb(0x01f7);
6419 if ( (status & 0xc0) == 0x40 ) {
6420 SET_AH(0);
6421 SET_DISK_RET_STATUS(0);
6422 CLEAR_CF(); // drive ready
6423 return;
6425 else {
6426 SET_AH(0xAA);
6427 SET_DISK_RET_STATUS(0xAA);
6428 SET_CF(); // not ready
6429 return;
6431 break;
6433 case 0x11: /* recalibrate */
6434 BX_DEBUG_INT13_HD("int13_f11\n");
6435 SET_AH(0);
6436 SET_DISK_RET_STATUS(0);
6437 CLEAR_CF(); /* successful */
6438 return;
6439 break;
6441 case 0x14: /* controller internal diagnostic */
6442 BX_DEBUG_INT13_HD("int13_f14\n");
6443 SET_AH(0);
6444 SET_DISK_RET_STATUS(0);
6445 CLEAR_CF(); /* successful */
6446 SET_AL(0);
6447 return;
6448 break;
6450 case 0x15: /* read disk drive size */
6451 drive = GET_ELDL();
6452 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6453 ASM_START
6454 push bp
6455 mov bp, sp
6456 mov al, _int13_harddisk.hd_heads + 2 [bp]
6457 mov ah, _int13_harddisk.hd_sectors + 2 [bp]
6458 mul al, ah ;; ax = heads * sectors
6459 mov bx, _int13_harddisk.hd_cylinders + 2 [bp]
6460 dec bx ;; use (cylinders - 1) ???
6461 mul ax, bx ;; dx:ax = (cylinders -1) * (heads * sectors)
6462 ;; now we need to move the 32bit result dx:ax to what the
6463 ;; BIOS wants which is cx:dx.
6464 ;; and then into CX:DX on the stack
6465 mov _int13_harddisk.CX + 2 [bp], dx
6466 mov _int13_harddisk.DX + 2 [bp], ax
6467 pop bp
6468 ASM_END
6469 SET_AH(3); // hard disk accessible
6470 SET_DISK_RET_STATUS(0); // ??? should this be 0
6471 CLEAR_CF(); // successful
6472 return;
6473 break;
6475 case 0x18: // set media type for format
6476 case 0x41: // IBM/MS
6477 case 0x42: // IBM/MS
6478 case 0x43: // IBM/MS
6479 case 0x44: // IBM/MS
6480 case 0x45: // IBM/MS lock/unlock drive
6481 case 0x46: // IBM/MS eject media
6482 case 0x47: // IBM/MS extended seek
6483 case 0x49: // IBM/MS extended media change
6484 case 0x50: // IBM/MS send packet command
6485 default:
6486 BX_INFO("int13_harddisk: unsupported AH=%02x\n", GET_AH());
6488 SET_AH(1); // code=invalid function in AH or invalid parameter
6489 SET_DISK_RET_STATUS(1);
6490 SET_CF(); /* unsuccessful */
6491 return;
6492 break;
6496 static char panic_msg_reg12h[] = "HD%d cmos reg 12h not type F\n";
6497 static char panic_msg_reg19h[] = "HD%d cmos reg %02xh not user definable type 47\n";
6499 void
6500 get_hd_geometry(drive, hd_cylinders, hd_heads, hd_sectors)
6501 Bit8u drive;
6502 Bit16u *hd_cylinders;
6503 Bit8u *hd_heads;
6504 Bit8u *hd_sectors;
6506 Bit8u hd_type;
6507 Bit16u ss;
6508 Bit16u cylinders;
6509 Bit8u iobase;
6511 ss = get_SS();
6512 if (drive == 0x80) {
6513 hd_type = inb_cmos(0x12) & 0xf0;
6514 if (hd_type != 0xf0)
6515 BX_INFO(panic_msg_reg12h,0);
6516 hd_type = inb_cmos(0x19); // HD0: extended type
6517 if (hd_type != 47)
6518 BX_INFO(panic_msg_reg19h,0,0x19);
6519 iobase = 0x1b;
6520 } else {
6521 hd_type = inb_cmos(0x12) & 0x0f;
6522 if (hd_type != 0x0f)
6523 BX_INFO(panic_msg_reg12h,1);
6524 hd_type = inb_cmos(0x1a); // HD0: extended type
6525 if (hd_type != 47)
6526 BX_INFO(panic_msg_reg19h,0,0x1a);
6527 iobase = 0x24;
6530 // cylinders
6531 cylinders = inb_cmos(iobase) | (inb_cmos(iobase+1) << 8);
6532 write_word(ss, hd_cylinders, cylinders);
6534 // heads
6535 write_byte(ss, hd_heads, inb_cmos(iobase+2));
6537 // sectors per track
6538 write_byte(ss, hd_sectors, inb_cmos(iobase+8));
6541 #endif //else BX_USE_ATADRV
6544 //////////////////////
6545 // FLOPPY functions //
6546 //////////////////////
6548 void floppy_reset_controller()
6550 Bit8u val8;
6552 // Reset controller
6553 val8 = inb(0x03f2);
6554 outb(0x03f2, val8 & ~0x04);
6555 outb(0x03f2, val8 | 0x04);
6557 // Wait for controller to come out of reset
6558 do {
6559 val8 = inb(0x3f4);
6560 } while ( (val8 & 0xc0) != 0x80 );
6563 void floppy_prepare_controller(drive)
6564 Bit16u drive;
6566 Bit8u val8, dor, prev_reset;
6568 // set 40:3e bit 7 to 0
6569 val8 = read_byte(0x0040, 0x003e);
6570 val8 &= 0x7f;
6571 write_byte(0x0040, 0x003e, val8);
6573 // turn on motor of selected drive, DMA & int enabled, normal operation
6574 prev_reset = inb(0x03f2) & 0x04;
6575 if (drive)
6576 dor = 0x20;
6577 else
6578 dor = 0x10;
6579 dor |= 0x0c;
6580 dor |= drive;
6581 outb(0x03f2, dor);
6583 // reset the disk motor timeout value of INT 08
6584 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
6586 // wait for drive readiness
6587 do {
6588 val8 = inb(0x3f4);
6589 } while ( (val8 & 0xc0) != 0x80 );
6591 if (prev_reset == 0) {
6592 // turn on interrupts
6593 ASM_START
6595 ASM_END
6596 // wait on 40:3e bit 7 to become 1
6597 do {
6598 val8 = read_byte(0x0040, 0x003e);
6599 } while ( (val8 & 0x80) == 0 );
6600 val8 &= 0x7f;
6601 ASM_START
6603 ASM_END
6604 write_byte(0x0040, 0x003e, val8);
6608 bx_bool
6609 floppy_media_known(drive)
6610 Bit16u drive;
6612 Bit8u val8;
6613 Bit16u media_state_offset;
6615 val8 = read_byte(0x0040, 0x003e); // diskette recal status
6616 if (drive)
6617 val8 >>= 1;
6618 val8 &= 0x01;
6619 if (val8 == 0)
6620 return(0);
6622 media_state_offset = 0x0090;
6623 if (drive)
6624 media_state_offset += 1;
6626 val8 = read_byte(0x0040, media_state_offset);
6627 val8 = (val8 >> 4) & 0x01;
6628 if (val8 == 0)
6629 return(0);
6631 // check pass, return KNOWN
6632 return(1);
6635 bx_bool
6636 floppy_media_sense(drive)
6637 Bit16u drive;
6639 bx_bool retval;
6640 Bit16u media_state_offset;
6641 Bit8u drive_type, config_data, media_state;
6643 if (floppy_drive_recal(drive) == 0) {
6644 return(0);
6647 // for now cheat and get drive type from CMOS,
6648 // assume media is same as drive type
6650 // ** config_data **
6651 // Bitfields for diskette media control:
6652 // Bit(s) Description (Table M0028)
6653 // 7-6 last data rate set by controller
6654 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6655 // 5-4 last diskette drive step rate selected
6656 // 00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
6657 // 3-2 {data rate at start of operation}
6658 // 1-0 reserved
6660 // ** media_state **
6661 // Bitfields for diskette drive media state:
6662 // Bit(s) Description (Table M0030)
6663 // 7-6 data rate
6664 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6665 // 5 double stepping required (e.g. 360kB in 1.2MB)
6666 // 4 media type established
6667 // 3 drive capable of supporting 4MB media
6668 // 2-0 on exit from BIOS, contains
6669 // 000 trying 360kB in 360kB
6670 // 001 trying 360kB in 1.2MB
6671 // 010 trying 1.2MB in 1.2MB
6672 // 011 360kB in 360kB established
6673 // 100 360kB in 1.2MB established
6674 // 101 1.2MB in 1.2MB established
6675 // 110 reserved
6676 // 111 all other formats/drives
6678 drive_type = inb_cmos(0x10);
6679 if (drive == 0)
6680 drive_type >>= 4;
6681 else
6682 drive_type &= 0x0f;
6683 if ( drive_type == 1 ) {
6684 // 360K 5.25" drive
6685 config_data = 0x00; // 0000 0000
6686 media_state = 0x25; // 0010 0101
6687 retval = 1;
6689 else if ( drive_type == 2 ) {
6690 // 1.2 MB 5.25" drive
6691 config_data = 0x00; // 0000 0000
6692 media_state = 0x25; // 0010 0101 // need double stepping??? (bit 5)
6693 retval = 1;
6695 else if ( drive_type == 3 ) {
6696 // 720K 3.5" drive
6697 config_data = 0x00; // 0000 0000 ???
6698 media_state = 0x17; // 0001 0111
6699 retval = 1;
6701 else if ( drive_type == 4 ) {
6702 // 1.44 MB 3.5" drive
6703 config_data = 0x00; // 0000 0000
6704 media_state = 0x17; // 0001 0111
6705 retval = 1;
6707 else if ( drive_type == 5 ) {
6708 // 2.88 MB 3.5" drive
6709 config_data = 0xCC; // 1100 1100
6710 media_state = 0xD7; // 1101 0111
6711 retval = 1;
6714 // Extended floppy size uses special cmos setting
6715 else if ( drive_type == 6 ) {
6716 // 160k 5.25" drive
6717 config_data = 0x00; // 0000 0000
6718 media_state = 0x27; // 0010 0111
6719 retval = 1;
6721 else if ( drive_type == 7 ) {
6722 // 180k 5.25" drive
6723 config_data = 0x00; // 0000 0000
6724 media_state = 0x27; // 0010 0111
6725 retval = 1;
6727 else if ( drive_type == 8 ) {
6728 // 320k 5.25" drive
6729 config_data = 0x00; // 0000 0000
6730 media_state = 0x27; // 0010 0111
6731 retval = 1;
6734 else {
6735 // not recognized
6736 config_data = 0x00; // 0000 0000
6737 media_state = 0x00; // 0000 0000
6738 retval = 0;
6741 if (drive == 0)
6742 media_state_offset = 0x90;
6743 else
6744 media_state_offset = 0x91;
6745 write_byte(0x0040, 0x008B, config_data);
6746 write_byte(0x0040, media_state_offset, media_state);
6748 return(retval);
6751 bx_bool
6752 floppy_drive_recal(drive)
6753 Bit16u drive;
6755 Bit8u val8;
6756 Bit16u curr_cyl_offset;
6758 floppy_prepare_controller(drive);
6760 // send Recalibrate command (2 bytes) to controller
6761 outb(0x03f5, 0x07); // 07: Recalibrate
6762 outb(0x03f5, drive); // 0=drive0, 1=drive1
6764 // turn on interrupts
6765 ASM_START
6767 ASM_END
6769 // wait on 40:3e bit 7 to become 1
6770 do {
6771 val8 = (read_byte(0x0040, 0x003e) & 0x80);
6772 } while ( val8 == 0 );
6774 val8 = 0; // separate asm from while() loop
6775 // turn off interrupts
6776 ASM_START
6778 ASM_END
6780 // set 40:3e bit 7 to 0, and calibrated bit
6781 val8 = read_byte(0x0040, 0x003e);
6782 val8 &= 0x7f;
6783 if (drive) {
6784 val8 |= 0x02; // Drive 1 calibrated
6785 curr_cyl_offset = 0x0095;
6786 } else {
6787 val8 |= 0x01; // Drive 0 calibrated
6788 curr_cyl_offset = 0x0094;
6790 write_byte(0x0040, 0x003e, val8);
6791 write_byte(0x0040, curr_cyl_offset, 0); // current cylinder is 0
6793 return(1);
6798 bx_bool
6799 floppy_drive_exists(drive)
6800 Bit16u drive;
6802 Bit8u drive_type;
6804 // check CMOS to see if drive exists
6805 drive_type = inb_cmos(0x10);
6806 if (drive == 0)
6807 drive_type >>= 4;
6808 else
6809 drive_type &= 0x0f;
6810 if ( drive_type == 0 )
6811 return(0);
6812 else
6813 return(1);
6816 #if BX_SUPPORT_FLOPPY
6817 void
6818 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
6819 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
6821 Bit8u drive, num_sectors, track, sector, head, status;
6822 Bit16u base_address, base_count, base_es;
6823 Bit8u page, mode_register, val8, dor;
6824 Bit8u return_status[7];
6825 Bit8u drive_type, num_floppies, ah;
6826 Bit16u es, last_addr;
6828 BX_DEBUG_INT13_FL("int13_diskette: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
6830 ah = GET_AH();
6832 switch ( ah ) {
6833 case 0x00: // diskette controller reset
6834 BX_DEBUG_INT13_FL("floppy f00\n");
6835 drive = GET_ELDL();
6836 if (drive > 1) {
6837 SET_AH(1); // invalid param
6838 set_diskette_ret_status(1);
6839 SET_CF();
6840 return;
6842 drive_type = inb_cmos(0x10);
6844 if (drive == 0)
6845 drive_type >>= 4;
6846 else
6847 drive_type &= 0x0f;
6848 if (drive_type == 0) {
6849 SET_AH(0x80); // drive not responding
6850 set_diskette_ret_status(0x80);
6851 SET_CF();
6852 return;
6854 SET_AH(0);
6855 set_diskette_ret_status(0);
6856 CLEAR_CF(); // successful
6857 set_diskette_current_cyl(drive, 0); // current cylinder
6858 return;
6860 case 0x01: // Read Diskette Status
6861 CLEAR_CF();
6862 val8 = read_byte(0x0000, 0x0441);
6863 SET_AH(val8);
6864 if (val8) {
6865 SET_CF();
6867 return;
6869 case 0x02: // Read Diskette Sectors
6870 case 0x03: // Write Diskette Sectors
6871 case 0x04: // Verify Diskette Sectors
6872 num_sectors = GET_AL();
6873 track = GET_CH();
6874 sector = GET_CL();
6875 head = GET_DH();
6876 drive = GET_ELDL();
6878 if ((drive > 1) || (head > 1) || (sector == 0) ||
6879 (num_sectors == 0) || (num_sectors > 72)) {
6880 BX_INFO("int13_diskette: read/write/verify: parameter out of range\n");
6881 SET_AH(1);
6882 set_diskette_ret_status(1);
6883 SET_AL(0); // no sectors read
6884 SET_CF(); // error occurred
6885 return;
6888 // see if drive exists
6889 if (floppy_drive_exists(drive) == 0) {
6890 SET_AH(0x80); // not responding
6891 set_diskette_ret_status(0x80);
6892 SET_AL(0); // no sectors read
6893 SET_CF(); // error occurred
6894 return;
6897 // see if media in drive, and type is known
6898 if (floppy_media_known(drive) == 0) {
6899 if (floppy_media_sense(drive) == 0) {
6900 SET_AH(0x0C); // Media type not found
6901 set_diskette_ret_status(0x0C);
6902 SET_AL(0); // no sectors read
6903 SET_CF(); // error occurred
6904 return;
6908 if (ah == 0x02) {
6909 // Read Diskette Sectors
6911 //-----------------------------------
6912 // set up DMA controller for transfer
6913 //-----------------------------------
6915 // es:bx = pointer to where to place information from diskette
6916 // port 04: DMA-1 base and current address, channel 2
6917 // port 05: DMA-1 base and current count, channel 2
6918 page = (ES >> 12); // upper 4 bits
6919 base_es = (ES << 4); // lower 16bits contributed by ES
6920 base_address = base_es + BX; // lower 16 bits of address
6921 // contributed by ES:BX
6922 if ( base_address < base_es ) {
6923 // in case of carry, adjust page by 1
6924 page++;
6926 base_count = (num_sectors * 512) - 1;
6928 // check for 64K boundary overrun
6929 last_addr = base_address + base_count;
6930 if (last_addr < base_address) {
6931 SET_AH(0x09);
6932 set_diskette_ret_status(0x09);
6933 SET_AL(0); // no sectors read
6934 SET_CF(); // error occurred
6935 return;
6938 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
6939 outb(0x000a, 0x06);
6941 BX_DEBUG_INT13_FL("clear flip-flop\n");
6942 outb(0x000c, 0x00); // clear flip-flop
6943 outb(0x0004, base_address);
6944 outb(0x0004, base_address>>8);
6945 BX_DEBUG_INT13_FL("clear flip-flop\n");
6946 outb(0x000c, 0x00); // clear flip-flop
6947 outb(0x0005, base_count);
6948 outb(0x0005, base_count>>8);
6950 // port 0b: DMA-1 Mode Register
6951 mode_register = 0x46; // single mode, increment, autoinit disable,
6952 // transfer type=write, channel 2
6953 BX_DEBUG_INT13_FL("setting mode register\n");
6954 outb(0x000b, mode_register);
6956 BX_DEBUG_INT13_FL("setting page register\n");
6957 // port 81: DMA-1 Page Register, channel 2
6958 outb(0x0081, page);
6960 BX_DEBUG_INT13_FL("unmask chan 2\n");
6961 outb(0x000a, 0x02); // unmask channel 2
6963 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
6964 outb(0x000a, 0x02);
6966 //--------------------------------------
6967 // set up floppy controller for transfer
6968 //--------------------------------------
6969 floppy_prepare_controller(drive);
6971 // send read-normal-data command (9 bytes) to controller
6972 outb(0x03f5, 0xe6); // e6: read normal data
6973 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
6974 outb(0x03f5, track);
6975 outb(0x03f5, head);
6976 outb(0x03f5, sector);
6977 outb(0x03f5, 2); // 512 byte sector size
6978 outb(0x03f5, sector + num_sectors - 1); // last sector to read on track
6979 outb(0x03f5, 0); // Gap length
6980 outb(0x03f5, 0xff); // Gap length
6982 // turn on interrupts
6983 ASM_START
6985 ASM_END
6987 // wait on 40:3e bit 7 to become 1
6988 do {
6989 val8 = read_byte(0x0040, 0x0040);
6990 if (val8 == 0) {
6991 floppy_reset_controller();
6992 SET_AH(0x80); // drive not ready (timeout)
6993 set_diskette_ret_status(0x80);
6994 SET_AL(0); // no sectors read
6995 SET_CF(); // error occurred
6996 return;
6998 val8 = (read_byte(0x0040, 0x003e) & 0x80);
6999 } while ( val8 == 0 );
7001 val8 = 0; // separate asm from while() loop
7002 // turn off interrupts
7003 ASM_START
7005 ASM_END
7007 // set 40:3e bit 7 to 0
7008 val8 = read_byte(0x0040, 0x003e);
7009 val8 &= 0x7f;
7010 write_byte(0x0040, 0x003e, val8);
7012 // check port 3f4 for accessibility to status bytes
7013 val8 = inb(0x3f4);
7014 if ( (val8 & 0xc0) != 0xc0 )
7015 BX_PANIC("int13_diskette: ctrl not ready\n");
7017 // read 7 return status bytes from controller
7018 // using loop index broken, have to unroll...
7019 return_status[0] = inb(0x3f5);
7020 return_status[1] = inb(0x3f5);
7021 return_status[2] = inb(0x3f5);
7022 return_status[3] = inb(0x3f5);
7023 return_status[4] = inb(0x3f5);
7024 return_status[5] = inb(0x3f5);
7025 return_status[6] = inb(0x3f5);
7026 // record in BIOS Data Area
7027 write_byte(0x0040, 0x0042, return_status[0]);
7028 write_byte(0x0040, 0x0043, return_status[1]);
7029 write_byte(0x0040, 0x0044, return_status[2]);
7030 write_byte(0x0040, 0x0045, return_status[3]);
7031 write_byte(0x0040, 0x0046, return_status[4]);
7032 write_byte(0x0040, 0x0047, return_status[5]);
7033 write_byte(0x0040, 0x0048, return_status[6]);
7035 if ( (return_status[0] & 0xc0) != 0 ) {
7036 SET_AH(0x20);
7037 set_diskette_ret_status(0x20);
7038 SET_AL(0); // no sectors read
7039 SET_CF(); // error occurred
7040 return;
7043 // ??? should track be new val from return_status[3] ?
7044 set_diskette_current_cyl(drive, track);
7045 // AL = number of sectors read (same value as passed)
7046 SET_AH(0x00); // success
7047 CLEAR_CF(); // success
7048 return;
7049 } else if (ah == 0x03) {
7050 // Write Diskette Sectors
7052 //-----------------------------------
7053 // set up DMA controller for transfer
7054 //-----------------------------------
7056 // es:bx = pointer to where to place information from diskette
7057 // port 04: DMA-1 base and current address, channel 2
7058 // port 05: DMA-1 base and current count, channel 2
7059 page = (ES >> 12); // upper 4 bits
7060 base_es = (ES << 4); // lower 16bits contributed by ES
7061 base_address = base_es + BX; // lower 16 bits of address
7062 // contributed by ES:BX
7063 if ( base_address < base_es ) {
7064 // in case of carry, adjust page by 1
7065 page++;
7067 base_count = (num_sectors * 512) - 1;
7069 // check for 64K boundary overrun
7070 last_addr = base_address + base_count;
7071 if (last_addr < base_address) {
7072 SET_AH(0x09);
7073 set_diskette_ret_status(0x09);
7074 SET_AL(0); // no sectors read
7075 SET_CF(); // error occurred
7076 return;
7079 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
7080 outb(0x000a, 0x06);
7082 outb(0x000c, 0x00); // clear flip-flop
7083 outb(0x0004, base_address);
7084 outb(0x0004, base_address>>8);
7085 outb(0x000c, 0x00); // clear flip-flop
7086 outb(0x0005, base_count);
7087 outb(0x0005, base_count>>8);
7089 // port 0b: DMA-1 Mode Register
7090 mode_register = 0x4a; // single mode, increment, autoinit disable,
7091 // transfer type=read, channel 2
7092 outb(0x000b, mode_register);
7094 // port 81: DMA-1 Page Register, channel 2
7095 outb(0x0081, page);
7097 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
7098 outb(0x000a, 0x02);
7100 //--------------------------------------
7101 // set up floppy controller for transfer
7102 //--------------------------------------
7103 floppy_prepare_controller(drive);
7105 // send write-normal-data command (9 bytes) to controller
7106 outb(0x03f5, 0xc5); // c5: write normal data
7107 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7108 outb(0x03f5, track);
7109 outb(0x03f5, head);
7110 outb(0x03f5, sector);
7111 outb(0x03f5, 2); // 512 byte sector size
7112 outb(0x03f5, sector + num_sectors - 1); // last sector to write on track
7113 outb(0x03f5, 0); // Gap length
7114 outb(0x03f5, 0xff); // Gap length
7116 // turn on interrupts
7117 ASM_START
7119 ASM_END
7121 // wait on 40:3e bit 7 to become 1
7122 do {
7123 val8 = read_byte(0x0040, 0x0040);
7124 if (val8 == 0) {
7125 floppy_reset_controller();
7126 SET_AH(0x80); // drive not ready (timeout)
7127 set_diskette_ret_status(0x80);
7128 SET_AL(0); // no sectors written
7129 SET_CF(); // error occurred
7130 return;
7132 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7133 } while ( val8 == 0 );
7135 val8 = 0; // separate asm from while() loop
7136 // turn off interrupts
7137 ASM_START
7139 ASM_END
7141 // set 40:3e bit 7 to 0
7142 val8 = read_byte(0x0040, 0x003e);
7143 val8 &= 0x7f;
7144 write_byte(0x0040, 0x003e, val8);
7146 // check port 3f4 for accessibility to status bytes
7147 val8 = inb(0x3f4);
7148 if ( (val8 & 0xc0) != 0xc0 )
7149 BX_PANIC("int13_diskette: ctrl not ready\n");
7151 // read 7 return status bytes from controller
7152 // using loop index broken, have to unroll...
7153 return_status[0] = inb(0x3f5);
7154 return_status[1] = inb(0x3f5);
7155 return_status[2] = inb(0x3f5);
7156 return_status[3] = inb(0x3f5);
7157 return_status[4] = inb(0x3f5);
7158 return_status[5] = inb(0x3f5);
7159 return_status[6] = inb(0x3f5);
7160 // record in BIOS Data Area
7161 write_byte(0x0040, 0x0042, return_status[0]);
7162 write_byte(0x0040, 0x0043, return_status[1]);
7163 write_byte(0x0040, 0x0044, return_status[2]);
7164 write_byte(0x0040, 0x0045, return_status[3]);
7165 write_byte(0x0040, 0x0046, return_status[4]);
7166 write_byte(0x0040, 0x0047, return_status[5]);
7167 write_byte(0x0040, 0x0048, return_status[6]);
7169 if ( (return_status[0] & 0xc0) != 0 ) {
7170 if ( (return_status[1] & 0x02) != 0 ) {
7171 // diskette not writable.
7172 // AH=status code=0x03 (tried to write on write-protected disk)
7173 // AL=number of sectors written=0
7174 AX = 0x0300;
7175 SET_CF();
7176 return;
7177 } else {
7178 BX_PANIC("int13_diskette_function: read error\n");
7182 // ??? should track be new val from return_status[3] ?
7183 set_diskette_current_cyl(drive, track);
7184 // AL = number of sectors read (same value as passed)
7185 SET_AH(0x00); // success
7186 CLEAR_CF(); // success
7187 return;
7188 } else { // if (ah == 0x04)
7189 // Verify Diskette Sectors
7191 // ??? should track be new val from return_status[3] ?
7192 set_diskette_current_cyl(drive, track);
7193 // AL = number of sectors verified (same value as passed)
7194 CLEAR_CF(); // success
7195 SET_AH(0x00); // success
7196 return;
7198 break;
7200 case 0x05: // format diskette track
7201 BX_DEBUG_INT13_FL("floppy f05\n");
7203 num_sectors = GET_AL();
7204 track = GET_CH();
7205 head = GET_DH();
7206 drive = GET_ELDL();
7208 if ((drive > 1) || (head > 1) || (track > 79) ||
7209 (num_sectors == 0) || (num_sectors > 18)) {
7210 SET_AH(1);
7211 set_diskette_ret_status(1);
7212 SET_CF(); // error occurred
7215 // see if drive exists
7216 if (floppy_drive_exists(drive) == 0) {
7217 SET_AH(0x80); // drive not responding
7218 set_diskette_ret_status(0x80);
7219 SET_CF(); // error occurred
7220 return;
7223 // see if media in drive, and type is known
7224 if (floppy_media_known(drive) == 0) {
7225 if (floppy_media_sense(drive) == 0) {
7226 SET_AH(0x0C); // Media type not found
7227 set_diskette_ret_status(0x0C);
7228 SET_AL(0); // no sectors read
7229 SET_CF(); // error occurred
7230 return;
7234 // set up DMA controller for transfer
7235 page = (ES >> 12); // upper 4 bits
7236 base_es = (ES << 4); // lower 16bits contributed by ES
7237 base_address = base_es + BX; // lower 16 bits of address
7238 // contributed by ES:BX
7239 if ( base_address < base_es ) {
7240 // in case of carry, adjust page by 1
7241 page++;
7243 base_count = (num_sectors * 4) - 1;
7245 // check for 64K boundary overrun
7246 last_addr = base_address + base_count;
7247 if (last_addr < base_address) {
7248 SET_AH(0x09);
7249 set_diskette_ret_status(0x09);
7250 SET_AL(0); // no sectors read
7251 SET_CF(); // error occurred
7252 return;
7255 outb(0x000a, 0x06);
7256 outb(0x000c, 0x00); // clear flip-flop
7257 outb(0x0004, base_address);
7258 outb(0x0004, base_address>>8);
7259 outb(0x000c, 0x00); // clear flip-flop
7260 outb(0x0005, base_count);
7261 outb(0x0005, base_count>>8);
7262 mode_register = 0x4a; // single mode, increment, autoinit disable,
7263 // transfer type=read, channel 2
7264 outb(0x000b, mode_register);
7265 // port 81: DMA-1 Page Register, channel 2
7266 outb(0x0081, page);
7267 outb(0x000a, 0x02);
7269 // set up floppy controller for transfer
7270 floppy_prepare_controller(drive);
7272 // send format-track command (6 bytes) to controller
7273 outb(0x03f5, 0x4d); // 4d: format track
7274 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7275 outb(0x03f5, 2); // 512 byte sector size
7276 outb(0x03f5, num_sectors); // number of sectors per track
7277 outb(0x03f5, 0); // Gap length
7278 outb(0x03f5, 0xf6); // Fill byte
7279 // turn on interrupts
7280 ASM_START
7282 ASM_END
7284 // wait on 40:3e bit 7 to become 1
7285 do {
7286 val8 = read_byte(0x0040, 0x0040);
7287 if (val8 == 0) {
7288 floppy_reset_controller();
7289 SET_AH(0x80); // drive not ready (timeout)
7290 set_diskette_ret_status(0x80);
7291 SET_CF(); // error occurred
7292 return;
7294 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7295 } while ( val8 == 0 );
7297 val8 = 0; // separate asm from while() loop
7298 // turn off interrupts
7299 ASM_START
7301 ASM_END
7302 // set 40:3e bit 7 to 0
7303 val8 = read_byte(0x0040, 0x003e);
7304 val8 &= 0x7f;
7305 write_byte(0x0040, 0x003e, val8);
7306 // check port 3f4 for accessibility to status bytes
7307 val8 = inb(0x3f4);
7308 if ( (val8 & 0xc0) != 0xc0 )
7309 BX_PANIC("int13_diskette: ctrl not ready\n");
7311 // read 7 return status bytes from controller
7312 // using loop index broken, have to unroll...
7313 return_status[0] = inb(0x3f5);
7314 return_status[1] = inb(0x3f5);
7315 return_status[2] = inb(0x3f5);
7316 return_status[3] = inb(0x3f5);
7317 return_status[4] = inb(0x3f5);
7318 return_status[5] = inb(0x3f5);
7319 return_status[6] = inb(0x3f5);
7320 // record in BIOS Data Area
7321 write_byte(0x0040, 0x0042, return_status[0]);
7322 write_byte(0x0040, 0x0043, return_status[1]);
7323 write_byte(0x0040, 0x0044, return_status[2]);
7324 write_byte(0x0040, 0x0045, return_status[3]);
7325 write_byte(0x0040, 0x0046, return_status[4]);
7326 write_byte(0x0040, 0x0047, return_status[5]);
7327 write_byte(0x0040, 0x0048, return_status[6]);
7329 if ( (return_status[0] & 0xc0) != 0 ) {
7330 if ( (return_status[1] & 0x02) != 0 ) {
7331 // diskette not writable.
7332 // AH=status code=0x03 (tried to write on write-protected disk)
7333 // AL=number of sectors written=0
7334 AX = 0x0300;
7335 SET_CF();
7336 return;
7337 } else {
7338 BX_PANIC("int13_diskette_function: write error\n");
7342 SET_AH(0);
7343 set_diskette_ret_status(0);
7344 set_diskette_current_cyl(drive, 0);
7345 CLEAR_CF(); // successful
7346 return;
7349 case 0x08: // read diskette drive parameters
7350 BX_DEBUG_INT13_FL("floppy f08\n");
7351 drive = GET_ELDL();
7353 if (drive > 1) {
7354 AX = 0;
7355 BX = 0;
7356 CX = 0;
7357 DX = 0;
7358 ES = 0;
7359 DI = 0;
7360 SET_DL(num_floppies);
7361 SET_CF();
7362 return;
7365 drive_type = inb_cmos(0x10);
7366 num_floppies = 0;
7367 if (drive_type & 0xf0)
7368 num_floppies++;
7369 if (drive_type & 0x0f)
7370 num_floppies++;
7372 if (drive == 0)
7373 drive_type >>= 4;
7374 else
7375 drive_type &= 0x0f;
7377 SET_BH(0);
7378 SET_BL(drive_type);
7379 SET_AH(0);
7380 SET_AL(0);
7381 SET_DL(num_floppies);
7383 switch (drive_type) {
7384 case 0: // none
7385 CX = 0;
7386 SET_DH(0); // max head #
7387 break;
7389 case 1: // 360KB, 5.25"
7390 CX = 0x2709; // 40 tracks, 9 sectors
7391 SET_DH(1); // max head #
7392 break;
7394 case 2: // 1.2MB, 5.25"
7395 CX = 0x4f0f; // 80 tracks, 15 sectors
7396 SET_DH(1); // max head #
7397 break;
7399 case 3: // 720KB, 3.5"
7400 CX = 0x4f09; // 80 tracks, 9 sectors
7401 SET_DH(1); // max head #
7402 break;
7404 case 4: // 1.44MB, 3.5"
7405 CX = 0x4f12; // 80 tracks, 18 sectors
7406 SET_DH(1); // max head #
7407 break;
7409 case 5: // 2.88MB, 3.5"
7410 CX = 0x4f24; // 80 tracks, 36 sectors
7411 SET_DH(1); // max head #
7412 break;
7414 case 6: // 160k, 5.25"
7415 CX = 0x2708; // 40 tracks, 8 sectors
7416 SET_DH(0); // max head #
7417 break;
7419 case 7: // 180k, 5.25"
7420 CX = 0x2709; // 40 tracks, 9 sectors
7421 SET_DH(0); // max head #
7422 break;
7424 case 8: // 320k, 5.25"
7425 CX = 0x2708; // 40 tracks, 8 sectors
7426 SET_DH(1); // max head #
7427 break;
7429 default: // ?
7430 BX_PANIC("floppy: int13: bad floppy type\n");
7433 /* set es & di to point to 11 byte diskette param table in ROM */
7434 ASM_START
7435 push bp
7436 mov bp, sp
7437 mov ax, #diskette_param_table2
7438 mov _int13_diskette_function.DI+2[bp], ax
7439 mov _int13_diskette_function.ES+2[bp], cs
7440 pop bp
7441 ASM_END
7442 CLEAR_CF(); // success
7443 /* disk status not changed upon success */
7444 return;
7447 case 0x15: // read diskette drive type
7448 BX_DEBUG_INT13_FL("floppy f15\n");
7449 drive = GET_ELDL();
7450 if (drive > 1) {
7451 SET_AH(0); // only 2 drives supported
7452 // set_diskette_ret_status here ???
7453 SET_CF();
7454 return;
7456 drive_type = inb_cmos(0x10);
7458 if (drive == 0)
7459 drive_type >>= 4;
7460 else
7461 drive_type &= 0x0f;
7462 CLEAR_CF(); // successful, not present
7463 if (drive_type==0) {
7464 SET_AH(0); // drive not present
7466 else {
7467 SET_AH(1); // drive present, does not support change line
7470 return;
7472 case 0x16: // get diskette change line status
7473 BX_DEBUG_INT13_FL("floppy f16\n");
7474 drive = GET_ELDL();
7475 if (drive > 1) {
7476 SET_AH(0x01); // invalid drive
7477 set_diskette_ret_status(0x01);
7478 SET_CF();
7479 return;
7482 SET_AH(0x06); // change line not supported
7483 set_diskette_ret_status(0x06);
7484 SET_CF();
7485 return;
7487 case 0x17: // set diskette type for format(old)
7488 BX_DEBUG_INT13_FL("floppy f17\n");
7489 /* not used for 1.44M floppies */
7490 SET_AH(0x01); // not supported
7491 set_diskette_ret_status(1); /* not supported */
7492 SET_CF();
7493 return;
7495 case 0x18: // set diskette type for format(new)
7496 BX_DEBUG_INT13_FL("floppy f18\n");
7497 SET_AH(0x01); // do later
7498 set_diskette_ret_status(1);
7499 SET_CF();
7500 return;
7502 default:
7503 BX_INFO("int13_diskette: unsupported AH=%02x\n", GET_AH());
7505 // if ( (ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e) ) {
7506 SET_AH(0x01); // ???
7507 set_diskette_ret_status(1);
7508 SET_CF();
7509 return;
7510 // }
7513 #else // #if BX_SUPPORT_FLOPPY
7514 void
7515 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
7516 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
7518 Bit8u val8;
7520 switch ( GET_AH() ) {
7522 case 0x01: // Read Diskette Status
7523 CLEAR_CF();
7524 val8 = read_byte(0x0000, 0x0441);
7525 SET_AH(val8);
7526 if (val8) {
7527 SET_CF();
7529 return;
7531 default:
7532 SET_CF();
7533 write_byte(0x0000, 0x0441, 0x01);
7534 SET_AH(0x01);
7537 #endif // #if BX_SUPPORT_FLOPPY
7539 void
7540 set_diskette_ret_status(value)
7541 Bit8u value;
7543 write_byte(0x0040, 0x0041, value);
7546 void
7547 set_diskette_current_cyl(drive, cyl)
7548 Bit8u drive;
7549 Bit8u cyl;
7551 if (drive > 1)
7552 BX_PANIC("set_diskette_current_cyl(): drive > 1\n");
7553 write_byte(0x0040, 0x0094+drive, cyl);
7556 void
7557 determine_floppy_media(drive)
7558 Bit16u drive;
7560 #if 0
7561 Bit8u val8, DOR, ctrl_info;
7563 ctrl_info = read_byte(0x0040, 0x008F);
7564 if (drive==1)
7565 ctrl_info >>= 4;
7566 else
7567 ctrl_info &= 0x0f;
7569 #if 0
7570 if (drive == 0) {
7571 DOR = 0x1c; // DOR: drive0 motor on, DMA&int enabled, normal op, drive select 0
7573 else {
7574 DOR = 0x2d; // DOR: drive1 motor on, DMA&int enabled, normal op, drive select 1
7576 #endif
7578 if ( (ctrl_info & 0x04) != 0x04 ) {
7579 // Drive not determined means no drive exists, done.
7580 return;
7583 #if 0
7584 // check Main Status Register for readiness
7585 val8 = inb(0x03f4) & 0x80; // Main Status Register
7586 if (val8 != 0x80)
7587 BX_PANIC("d_f_m: MRQ bit not set\n");
7589 // change line
7591 // existing BDA values
7593 // turn on drive motor
7594 outb(0x03f2, DOR); // Digital Output Register
7596 #endif
7597 BX_PANIC("d_f_m: OK so far\n");
7598 #endif
7601 void
7602 int17_function(regs, ds, iret_addr)
7603 pusha_regs_t regs; // regs pushed from PUSHA instruction
7604 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7605 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7607 Bit16u addr,timeout;
7608 Bit8u val8;
7610 ASM_START
7612 ASM_END
7614 addr = read_word(0x0040, (regs.u.r16.dx << 1) + 8);
7615 if ((regs.u.r8.ah < 3) && (regs.u.r16.dx < 3) && (addr > 0)) {
7616 timeout = read_byte(0x0040, 0x0078 + regs.u.r16.dx) << 8;
7617 if (regs.u.r8.ah == 0) {
7618 outb(addr, regs.u.r8.al);
7619 val8 = inb(addr+2);
7620 outb(addr+2, val8 | 0x01); // send strobe
7621 ASM_START
7623 ASM_END
7624 outb(addr+2, val8 & ~0x01);
7625 while (((inb(addr+1) & 0x40) == 0x40) && (timeout)) {
7626 timeout--;
7629 if (regs.u.r8.ah == 1) {
7630 val8 = inb(addr+2);
7631 outb(addr+2, val8 & ~0x04); // send init
7632 ASM_START
7634 ASM_END
7635 outb(addr+2, val8 | 0x04);
7637 val8 = inb(addr+1);
7638 regs.u.r8.ah = (val8 ^ 0x48);
7639 if (!timeout) regs.u.r8.ah |= 0x01;
7640 ClearCF(iret_addr.flags);
7641 } else {
7642 SetCF(iret_addr.flags); // Unsupported
7646 void
7647 int19_function(seq_nr)
7648 Bit16u seq_nr;
7650 Bit16u ebda_seg=read_word(0x0040,0x000E);
7651 Bit16u bootdev;
7652 Bit8u bootdrv;
7653 Bit8u bootchk;
7654 Bit16u bootseg;
7655 Bit16u bootip;
7656 Bit16u status;
7658 struct ipl_entry e;
7660 // if BX_ELTORITO_BOOT is not defined, old behavior
7661 // check bit 5 in CMOS reg 0x2d. load either 0x00 or 0x80 into DL
7662 // in preparation for the intial INT 13h (0=floppy A:, 0x80=C:)
7663 // 0: system boot sequence, first drive C: then A:
7664 // 1: system boot sequence, first drive A: then C:
7665 // else BX_ELTORITO_BOOT is defined
7666 // CMOS regs 0x3D and 0x38 contain the boot sequence:
7667 // CMOS reg 0x3D & 0x0f : 1st boot device
7668 // CMOS reg 0x3D & 0xf0 : 2nd boot device
7669 // CMOS reg 0x38 & 0xf0 : 3rd boot device
7670 // boot device codes:
7671 // 0x00 : not defined
7672 // 0x01 : first floppy
7673 // 0x02 : first harddrive
7674 // 0x03 : first cdrom
7675 // 0x04 - 0x0f : PnP expansion ROMs (e.g. Etherboot)
7676 // else : boot failure
7678 // Get the boot sequence
7679 #if BX_ELTORITO_BOOT
7680 bootdev = inb_cmos(0x3d);
7681 bootdev |= ((inb_cmos(0x38) & 0xf0) << 4);
7682 bootdev >>= 4 * seq_nr;
7683 bootdev &= 0xf;
7684 if (bootdev == 0) BX_PANIC("No bootable device.\n");
7686 /* Translate from CMOS runes to an IPL table offset by subtracting 1 */
7687 bootdev -= 1;
7688 #else
7689 if (seq_nr ==2) BX_PANIC("No more boot devices.");
7690 if (!!(inb_cmos(0x2d) & 0x20) ^ (seq_nr == 1))
7691 /* Boot from floppy if the bit is set or it's the second boot */
7692 bootdev = 0x00;
7693 else
7694 bootdev = 0x01;
7695 #endif
7697 /* Read the boot device from the IPL table */
7698 if (get_boot_vector(bootdev, &e) == 0) {
7699 BX_INFO("Invalid boot device (0x%x)\n", bootdev);
7700 return;
7703 /* Do the loading, and set up vector as a far pointer to the boot
7704 * address, and bootdrv as the boot drive */
7705 print_boot_device(e.type);
7707 switch(e.type) {
7708 case 0x01: /* FDD */
7709 case 0x02: /* HDD */
7711 bootdrv = (e.type == 0x02) ? 0x80 : 0x00;
7712 bootseg = 0x07c0;
7713 status = 0;
7715 ASM_START
7716 push bp
7717 mov bp, sp
7718 push ax
7719 push bx
7720 push cx
7721 push dx
7723 mov dl, _int19_function.bootdrv + 2[bp]
7724 mov ax, _int19_function.bootseg + 2[bp]
7725 mov es, ax ;; segment
7726 xor bx, bx ;; offset
7727 mov ah, #0x02 ;; function 2, read diskette sector
7728 mov al, #0x01 ;; read 1 sector
7729 mov ch, #0x00 ;; track 0
7730 mov cl, #0x01 ;; sector 1
7731 mov dh, #0x00 ;; head 0
7732 int #0x13 ;; read sector
7733 jnc int19_load_done
7734 mov ax, #0x0001
7735 mov _int19_function.status + 2[bp], ax
7737 int19_load_done:
7738 pop dx
7739 pop cx
7740 pop bx
7741 pop ax
7742 pop bp
7743 ASM_END
7745 if (status != 0) {
7746 print_boot_failure(e.type, 1);
7747 return;
7750 /* Always check the signature on a HDD boot sector; on FDD, only do
7751 * the check if the CMOS doesn't tell us to skip it */
7752 if ((e.type != 0x01) || !((inb_cmos(0x38) & 0x01))) {
7753 if (read_word(bootseg,0x1fe) != 0xaa55) {
7754 print_boot_failure(e.type, 0);
7755 return;
7759 /* Canonicalize bootseg:bootip */
7760 bootip = (bootseg & 0x0fff) << 4;
7761 bootseg &= 0xf000;
7762 break;
7764 #if BX_ELTORITO_BOOT
7765 case 0x03: /* CD-ROM */
7766 status = cdrom_boot();
7768 // If failure
7769 if ( (status & 0x00ff) !=0 ) {
7770 print_cdromboot_failure(status);
7771 print_boot_failure(e.type, 1);
7772 return;
7775 bootdrv = (Bit8u)(status>>8);
7776 bootseg = read_word(ebda_seg,&EbdaData->cdemu.load_segment);
7777 /* Canonicalize bootseg:bootip */
7778 bootip = (bootseg & 0x0fff) << 4;
7779 bootseg &= 0xf000;
7780 break;
7781 #endif
7783 case 0x80: /* Expansion ROM with a Bootstrap Entry Vector (a far pointer) */
7784 bootseg = e.vector >> 16;
7785 bootip = e.vector & 0xffff;
7786 break;
7788 default: return;
7791 /* Debugging info */
7792 BX_INFO("Booting from %x:%x\n", bootseg, bootip);
7794 /* Jump to the boot vector */
7795 ASM_START
7796 mov bp, sp
7797 ;; Build an iret stack frame that will take us to the boot vector.
7798 ;; iret pops ip, then cs, then flags, so push them in the opposite order.
7799 pushf
7800 mov ax, _int19_function.bootseg + 0[bp]
7801 push ax
7802 mov ax, _int19_function.bootip + 0[bp]
7803 push ax
7804 ;; Set the magic number in ax and the boot drive in dl.
7805 mov ax, #0xaa55
7806 mov dl, _int19_function.bootdrv + 0[bp]
7807 ;; Zero some of the other registers.
7808 xor bx, bx
7809 mov ds, bx
7810 mov es, bx
7811 mov bp, bx
7812 ;; Go!
7813 iret
7814 ASM_END
7817 void
7818 int1a_function(regs, ds, iret_addr)
7819 pusha_regs_t regs; // regs pushed from PUSHA instruction
7820 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7821 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7823 Bit8u val8;
7825 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);
7827 ASM_START
7829 ASM_END
7831 switch (regs.u.r8.ah) {
7832 case 0: // get current clock count
7833 ASM_START
7835 ASM_END
7836 regs.u.r16.cx = BiosData->ticks_high;
7837 regs.u.r16.dx = BiosData->ticks_low;
7838 regs.u.r8.al = BiosData->midnight_flag;
7839 BiosData->midnight_flag = 0; // reset flag
7840 ASM_START
7842 ASM_END
7843 // AH already 0
7844 ClearCF(iret_addr.flags); // OK
7845 break;
7847 case 1: // Set Current Clock Count
7848 ASM_START
7850 ASM_END
7851 BiosData->ticks_high = regs.u.r16.cx;
7852 BiosData->ticks_low = regs.u.r16.dx;
7853 BiosData->midnight_flag = 0; // reset flag
7854 ASM_START
7856 ASM_END
7857 regs.u.r8.ah = 0;
7858 ClearCF(iret_addr.flags); // OK
7859 break;
7862 case 2: // Read CMOS Time
7863 if (rtc_updating()) {
7864 SetCF(iret_addr.flags);
7865 break;
7868 regs.u.r8.dh = inb_cmos(0x00); // Seconds
7869 regs.u.r8.cl = inb_cmos(0x02); // Minutes
7870 regs.u.r8.ch = inb_cmos(0x04); // Hours
7871 regs.u.r8.dl = inb_cmos(0x0b) & 0x01; // Stat Reg B
7872 regs.u.r8.ah = 0;
7873 regs.u.r8.al = regs.u.r8.ch;
7874 ClearCF(iret_addr.flags); // OK
7875 break;
7877 case 3: // Set CMOS Time
7878 // Using a debugger, I notice the following masking/setting
7879 // of bits in Status Register B, by setting Reg B to
7880 // a few values and getting its value after INT 1A was called.
7882 // try#1 try#2 try#3
7883 // before 1111 1101 0111 1101 0000 0000
7884 // after 0110 0010 0110 0010 0000 0010
7886 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7887 // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
7888 if (rtc_updating()) {
7889 init_rtc();
7890 // fall through as if an update were not in progress
7892 outb_cmos(0x00, regs.u.r8.dh); // Seconds
7893 outb_cmos(0x02, regs.u.r8.cl); // Minutes
7894 outb_cmos(0x04, regs.u.r8.ch); // Hours
7895 // Set Daylight Savings time enabled bit to requested value
7896 val8 = (inb_cmos(0x0b) & 0x60) | 0x02 | (regs.u.r8.dl & 0x01);
7897 // (reg B already selected)
7898 outb_cmos(0x0b, val8);
7899 regs.u.r8.ah = 0;
7900 regs.u.r8.al = val8; // val last written to Reg B
7901 ClearCF(iret_addr.flags); // OK
7902 break;
7904 case 4: // Read CMOS Date
7905 regs.u.r8.ah = 0;
7906 if (rtc_updating()) {
7907 SetCF(iret_addr.flags);
7908 break;
7910 regs.u.r8.cl = inb_cmos(0x09); // Year
7911 regs.u.r8.dh = inb_cmos(0x08); // Month
7912 regs.u.r8.dl = inb_cmos(0x07); // Day of Month
7913 regs.u.r8.ch = inb_cmos(0x32); // Century
7914 regs.u.r8.al = regs.u.r8.ch;
7915 ClearCF(iret_addr.flags); // OK
7916 break;
7918 case 5: // Set CMOS Date
7919 // Using a debugger, I notice the following masking/setting
7920 // of bits in Status Register B, by setting Reg B to
7921 // a few values and getting its value after INT 1A was called.
7923 // try#1 try#2 try#3 try#4
7924 // before 1111 1101 0111 1101 0000 0010 0000 0000
7925 // after 0110 1101 0111 1101 0000 0010 0000 0000
7927 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7928 // My assumption: RegB = (RegB & 01111111b)
7929 if (rtc_updating()) {
7930 init_rtc();
7931 SetCF(iret_addr.flags);
7932 break;
7934 outb_cmos(0x09, regs.u.r8.cl); // Year
7935 outb_cmos(0x08, regs.u.r8.dh); // Month
7936 outb_cmos(0x07, regs.u.r8.dl); // Day of Month
7937 outb_cmos(0x32, regs.u.r8.ch); // Century
7938 val8 = inb_cmos(0x0b) & 0x7f; // clear halt-clock bit
7939 outb_cmos(0x0b, val8);
7940 regs.u.r8.ah = 0;
7941 regs.u.r8.al = val8; // AL = val last written to Reg B
7942 ClearCF(iret_addr.flags); // OK
7943 break;
7945 case 6: // Set Alarm Time in CMOS
7946 // Using a debugger, I notice the following masking/setting
7947 // of bits in Status Register B, by setting Reg B to
7948 // a few values and getting its value after INT 1A was called.
7950 // try#1 try#2 try#3
7951 // before 1101 1111 0101 1111 0000 0000
7952 // after 0110 1111 0111 1111 0010 0000
7954 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7955 // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
7956 val8 = inb_cmos(0x0b); // Get Status Reg B
7957 regs.u.r16.ax = 0;
7958 if (val8 & 0x20) {
7959 // Alarm interrupt enabled already
7960 SetCF(iret_addr.flags); // Error: alarm in use
7961 break;
7963 if (rtc_updating()) {
7964 init_rtc();
7965 // fall through as if an update were not in progress
7967 outb_cmos(0x01, regs.u.r8.dh); // Seconds alarm
7968 outb_cmos(0x03, regs.u.r8.cl); // Minutes alarm
7969 outb_cmos(0x05, regs.u.r8.ch); // Hours alarm
7970 outb(0xa1, inb(0xa1) & 0xfe); // enable IRQ 8
7971 // enable Status Reg B alarm bit, clear halt clock bit
7972 outb_cmos(0x0b, (val8 & 0x7f) | 0x20);
7973 ClearCF(iret_addr.flags); // OK
7974 break;
7976 case 7: // Turn off Alarm
7977 // Using a debugger, I notice the following masking/setting
7978 // of bits in Status Register B, by setting Reg B to
7979 // a few values and getting its value after INT 1A was called.
7981 // try#1 try#2 try#3 try#4
7982 // before 1111 1101 0111 1101 0010 0000 0010 0010
7983 // after 0100 0101 0101 0101 0000 0000 0000 0010
7985 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7986 // My assumption: RegB = (RegB & 01010111b)
7987 val8 = inb_cmos(0x0b); // Get Status Reg B
7988 // clear clock-halt bit, disable alarm bit
7989 outb_cmos(0x0b, val8 & 0x57); // disable alarm bit
7990 regs.u.r8.ah = 0;
7991 regs.u.r8.al = val8; // val last written to Reg B
7992 ClearCF(iret_addr.flags); // OK
7993 break;
7994 #if BX_PCIBIOS
7995 case 0xb1:
7996 // real mode PCI BIOS functions now handled in assembler code
7997 // this C code handles the error code for information only
7998 if (regs.u.r8.bl == 0xff) {
7999 BX_INFO("PCI BIOS: PCI not present\n");
8000 } else if (regs.u.r8.bl == 0x81) {
8001 BX_INFO("unsupported PCI BIOS function 0x%02x\n", regs.u.r8.al);
8002 } else if (regs.u.r8.bl == 0x83) {
8003 BX_INFO("bad PCI vendor ID %04x\n", regs.u.r16.dx);
8004 } else if (regs.u.r8.bl == 0x86) {
8005 if (regs.u.r8.al == 0x02) {
8006 BX_INFO("PCI device %04x:%04x not found at index %d\n", regs.u.r16.dx, regs.u.r16.cx, regs.u.r16.si);
8007 } else {
8008 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);
8011 regs.u.r8.ah = regs.u.r8.bl;
8012 SetCF(iret_addr.flags);
8013 break;
8014 #endif
8016 default:
8017 SetCF(iret_addr.flags); // Unsupported
8021 void
8022 int70_function(regs, ds, iret_addr)
8023 pusha_regs_t regs; // regs pushed from PUSHA instruction
8024 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
8025 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
8027 // INT 70h: IRQ 8 - CMOS RTC interrupt from periodic or alarm modes
8028 Bit8u registerB = 0, registerC = 0;
8030 // Check which modes are enabled and have occurred.
8031 registerB = inb_cmos( 0xB );
8032 registerC = inb_cmos( 0xC );
8034 if( ( registerB & 0x60 ) != 0 ) {
8035 if( ( registerC & 0x20 ) != 0 ) {
8036 // Handle Alarm Interrupt.
8037 ASM_START
8039 int #0x4a
8041 ASM_END
8043 if( ( registerC & 0x40 ) != 0 ) {
8044 // Handle Periodic Interrupt.
8046 if( read_byte( 0x40, 0xA0 ) != 0 ) {
8047 // Wait Interval (Int 15, AH=83) active.
8048 Bit32u time, toggle;
8050 time = read_dword( 0x40, 0x9C ); // Time left in microseconds.
8051 if( time < 0x3D1 ) {
8052 // Done waiting.
8053 Bit16u segment, offset;
8055 segment = read_word( 0x40, 0x98 );
8056 offset = read_word( 0x40, 0x9A );
8057 write_byte( 0x40, 0xA0, 0 ); // Turn of status byte.
8058 outb_cmos( 0xB, registerB & 0x37 ); // Clear the Periodic Interrupt.
8059 write_byte(segment, offset, read_byte(segment, offset) | 0x80 ); // Write to specified flag byte.
8060 } else {
8061 // Continue waiting.
8062 time -= 0x3D1;
8063 write_dword( 0x40, 0x9C, time );
8069 ASM_START
8070 call eoi_both_pics
8071 ASM_END
8075 ASM_START
8076 ;------------------------------------------
8077 ;- INT74h : PS/2 mouse hardware interrupt -
8078 ;------------------------------------------
8079 int74_handler:
8081 pusha
8082 push ds ;; save DS
8083 push #0x00 ;; placeholder for status
8084 push #0x00 ;; placeholder for X
8085 push #0x00 ;; placeholder for Y
8086 push #0x00 ;; placeholder for Z
8087 push #0x00 ;; placeholder for make_far_call boolean
8088 call _int74_function
8089 pop cx ;; remove make_far_call from stack
8090 jcxz int74_done
8092 ;; make far call to EBDA:0022
8093 push #0x00
8094 pop ds
8095 push 0x040E ;; push 0000:040E (opcodes 0xff, 0x36, 0x0E, 0x04)
8096 pop ds
8097 //CALL_EP(0x0022) ;; call far routine (call_Ep DS:0022 :opcodes 0xff, 0x1e, 0x22, 0x00)
8098 call far ptr[0x22]
8099 int74_done:
8101 call eoi_both_pics
8102 add sp, #8 ;; pop status, x, y, z
8104 pop ds ;; restore DS
8105 popa
8106 iret
8109 ;; This will perform an IRET, but will retain value of current CF
8110 ;; by altering flags on stack. Better than RETF #02.
8111 iret_modify_cf:
8112 jc carry_set
8113 push bp
8114 mov bp, sp
8115 and BYTE [bp + 0x06], #0xfe
8116 pop bp
8117 iret
8118 carry_set:
8119 push bp
8120 mov bp, sp
8121 or BYTE [bp + 0x06], #0x01
8122 pop bp
8123 iret
8126 ;----------------------
8127 ;- INT13h (relocated) -
8128 ;----------------------
8130 ; int13_relocated is a little bit messed up since I played with it
8131 ; I have to rewrite it:
8132 ; - call a function that detect which function to call
8133 ; - make all called C function get the same parameters list
8135 int13_relocated:
8137 #if BX_ELTORITO_BOOT
8138 ;; check for an eltorito function
8139 cmp ah,#0x4a
8140 jb int13_not_eltorito
8141 cmp ah,#0x4d
8142 ja int13_not_eltorito
8144 pusha
8145 push es
8146 push ds
8147 push ss
8148 pop ds
8150 push #int13_out
8151 jmp _int13_eltorito ;; ELDX not used
8153 int13_not_eltorito:
8154 push ax
8155 push bx
8156 push cx
8157 push dx
8159 ;; check if emulation active
8160 call _cdemu_isactive
8161 cmp al,#0x00
8162 je int13_cdemu_inactive
8164 ;; check if access to the emulated drive
8165 call _cdemu_emulated_drive
8166 pop dx
8167 push dx
8168 cmp al,dl ;; int13 on emulated drive
8169 jne int13_nocdemu
8171 pop dx
8172 pop cx
8173 pop bx
8174 pop ax
8176 pusha
8177 push es
8178 push ds
8179 push ss
8180 pop ds
8182 push #int13_out
8183 jmp _int13_cdemu ;; ELDX not used
8185 int13_nocdemu:
8186 and dl,#0xE0 ;; mask to get device class, including cdroms
8187 cmp al,dl ;; al is 0x00 or 0x80
8188 jne int13_cdemu_inactive ;; inactive for device class
8190 pop dx
8191 pop cx
8192 pop bx
8193 pop ax
8195 push ax
8196 push cx
8197 push dx
8198 push bx
8200 dec dl ;; real drive is dl - 1
8201 jmp int13_legacy
8203 int13_cdemu_inactive:
8204 pop dx
8205 pop cx
8206 pop bx
8207 pop ax
8209 #endif // BX_ELTORITO_BOOT
8211 int13_noeltorito:
8213 push ax
8214 push cx
8215 push dx
8216 push bx
8218 int13_legacy:
8220 push dx ;; push eltorito value of dx instead of sp
8222 push bp
8223 push si
8224 push di
8226 push es
8227 push ds
8228 push ss
8229 pop ds
8231 ;; now the 16-bit registers can be restored with:
8232 ;; pop ds; pop es; popa; iret
8233 ;; arguments passed to functions should be
8234 ;; DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS
8236 test dl, #0x80
8237 jnz int13_notfloppy
8239 push #int13_out
8240 jmp _int13_diskette_function
8242 int13_notfloppy:
8244 #if BX_USE_ATADRV
8246 cmp dl, #0xE0
8247 jb int13_notcdrom
8249 // ebx is modified: BSD 5.2.1 boot loader problem
8250 // someone should figure out which 32 bit register that actually are used
8252 shr ebx, #16
8253 push bx
8255 call _int13_cdrom
8257 pop bx
8258 shl ebx, #16
8260 jmp int13_out
8262 int13_notcdrom:
8264 #endif
8266 int13_disk:
8267 ;; int13_harddisk modifies high word of EAX
8268 shr eax, #16
8269 push ax
8270 call _int13_harddisk
8271 pop ax
8272 shl eax, #16
8274 int13_out:
8275 pop ds
8276 pop es
8277 popa
8278 iret
8280 ;----------
8281 ;- INT18h -
8282 ;----------
8283 int18_handler: ;; Boot Failure recovery: try the next device.
8285 ;; Reset SP and SS
8286 mov ax, #0xfffe
8287 mov sp, ax
8288 xor ax, ax
8289 mov ss, ax
8291 ;; Get the boot sequence number out of the IPL memory
8292 mov bx, #IPL_SEG
8293 mov ds, bx ;; Set segment
8294 mov bx, IPL_SEQUENCE_OFFSET ;; BX is now the sequence number
8295 inc bx ;; ++
8296 mov IPL_SEQUENCE_OFFSET, bx ;; Write it back
8297 mov ds, ax ;; and reset the segment to zero.
8299 ;; Carry on in the INT 19h handler, using the new sequence number
8300 push bx
8302 jmp int19_next_boot
8304 ;----------
8305 ;- INT19h -
8306 ;----------
8307 int19_relocated: ;; Boot function, relocated
8309 ;; int19 was beginning to be really complex, so now it
8310 ;; just calls a C function that does the work
8312 push bp
8313 mov bp, sp
8315 ;; Reset SS and SP
8316 mov ax, #0xfffe
8317 mov sp, ax
8318 xor ax, ax
8319 mov ss, ax
8321 ;; Start from the first boot device (0, in AX)
8322 mov bx, #IPL_SEG
8323 mov ds, bx ;; Set segment to write to the IPL memory
8324 mov IPL_SEQUENCE_OFFSET, ax ;; Save the sequence number
8325 mov ds, ax ;; and reset the segment.
8327 push ax
8329 int19_next_boot:
8331 ;; Call the C code for the next boot device
8332 call _int19_function
8334 ;; Boot failed: invoke the boot recovery function
8335 int #0x18
8337 ;----------
8338 ;- INT1Ch -
8339 ;----------
8340 int1c_handler: ;; User Timer Tick
8341 iret
8344 ;----------------------
8345 ;- POST: Floppy Drive -
8346 ;----------------------
8347 floppy_drive_post:
8348 xor ax, ax
8349 mov ds, ax
8351 mov al, #0x00
8352 mov 0x043e, al ;; drive 0 & 1 uncalibrated, no interrupt has occurred
8354 mov 0x043f, al ;; diskette motor status: read op, drive0, motors off
8356 mov 0x0440, al ;; diskette motor timeout counter: not active
8357 mov 0x0441, al ;; diskette controller status return code
8359 mov 0x0442, al ;; disk & diskette controller status register 0
8360 mov 0x0443, al ;; diskette controller status register 1
8361 mov 0x0444, al ;; diskette controller status register 2
8362 mov 0x0445, al ;; diskette controller cylinder number
8363 mov 0x0446, al ;; diskette controller head number
8364 mov 0x0447, al ;; diskette controller sector number
8365 mov 0x0448, al ;; diskette controller bytes written
8367 mov 0x048b, al ;; diskette configuration data
8369 ;; -----------------------------------------------------------------
8370 ;; (048F) diskette controller information
8372 mov al, #0x10 ;; get CMOS diskette drive type
8373 out 0x70, AL
8374 in AL, 0x71
8375 mov ah, al ;; save byte to AH
8377 look_drive0:
8378 shr al, #4 ;; look at top 4 bits for drive 0
8379 jz f0_missing ;; jump if no drive0
8380 mov bl, #0x07 ;; drive0 determined, multi-rate, has changed line
8381 jmp look_drive1
8382 f0_missing:
8383 mov bl, #0x00 ;; no drive0
8385 look_drive1:
8386 mov al, ah ;; restore from AH
8387 and al, #0x0f ;; look at bottom 4 bits for drive 1
8388 jz f1_missing ;; jump if no drive1
8389 or bl, #0x70 ;; drive1 determined, multi-rate, has changed line
8390 f1_missing:
8391 ;; leave high bits in BL zerod
8392 mov 0x048f, bl ;; put new val in BDA (diskette controller information)
8393 ;; -----------------------------------------------------------------
8395 mov al, #0x00
8396 mov 0x0490, al ;; diskette 0 media state
8397 mov 0x0491, al ;; diskette 1 media state
8399 ;; diskette 0,1 operational starting state
8400 ;; drive type has not been determined,
8401 ;; has no changed detection line
8402 mov 0x0492, al
8403 mov 0x0493, al
8405 mov 0x0494, al ;; diskette 0 current cylinder
8406 mov 0x0495, al ;; diskette 1 current cylinder
8408 mov al, #0x02
8409 out #0x0a, al ;; clear DMA-1 channel 2 mask bit
8411 SET_INT_VECTOR(0x1E, #0xF000, #diskette_param_table2)
8412 SET_INT_VECTOR(0x40, #0xF000, #int13_diskette)
8413 SET_INT_VECTOR(0x0E, #0xF000, #int0e_handler) ;; IRQ 6
8418 ;--------------------
8419 ;- POST: HARD DRIVE -
8420 ;--------------------
8421 ; relocated here because the primary POST area isnt big enough.
8422 hard_drive_post:
8423 // IRQ 14 = INT 76h
8424 // INT 76h calls INT 15h function ax=9100
8426 mov al, #0x0a ; 0000 1010 = reserved, disable IRQ 14
8427 mov dx, #0x03f6
8428 out dx, al
8430 xor ax, ax
8431 mov ds, ax
8432 mov 0x0474, al /* hard disk status of last operation */
8433 mov 0x0477, al /* hard disk port offset (XT only ???) */
8434 mov 0x048c, al /* hard disk status register */
8435 mov 0x048d, al /* hard disk error register */
8436 mov 0x048e, al /* hard disk task complete flag */
8437 mov al, #0x01
8438 mov 0x0475, al /* hard disk number attached */
8439 mov al, #0xc0
8440 mov 0x0476, al /* hard disk control byte */
8441 SET_INT_VECTOR(0x13, #0xF000, #int13_handler)
8442 SET_INT_VECTOR(0x76, #0xF000, #int76_handler)
8443 ;; INT 41h: hard disk 0 configuration pointer
8444 ;; INT 46h: hard disk 1 configuration pointer
8445 SET_INT_VECTOR(0x41, #EBDA_SEG, #0x003D)
8446 SET_INT_VECTOR(0x46, #EBDA_SEG, #0x004D)
8448 ;; move disk geometry data from CMOS to EBDA disk parameter table(s)
8449 mov al, #0x12
8450 out #0x70, al
8451 in al, #0x71
8452 and al, #0xf0
8453 cmp al, #0xf0
8454 je post_d0_extended
8455 jmp check_for_hd1
8456 post_d0_extended:
8457 mov al, #0x19
8458 out #0x70, al
8459 in al, #0x71
8460 cmp al, #47 ;; decimal 47 - user definable
8461 je post_d0_type47
8462 HALT(__LINE__)
8463 post_d0_type47:
8464 ;; CMOS purpose param table offset
8465 ;; 1b cylinders low 0
8466 ;; 1c cylinders high 1
8467 ;; 1d heads 2
8468 ;; 1e write pre-comp low 5
8469 ;; 1f write pre-comp high 6
8470 ;; 20 retries/bad map/heads>8 8
8471 ;; 21 landing zone low C
8472 ;; 22 landing zone high D
8473 ;; 23 sectors/track E
8475 mov ax, #EBDA_SEG
8476 mov ds, ax
8478 ;;; Filling EBDA table for hard disk 0.
8479 mov al, #0x1f
8480 out #0x70, al
8481 in al, #0x71
8482 mov ah, al
8483 mov al, #0x1e
8484 out #0x70, al
8485 in al, #0x71
8486 mov (0x003d + 0x05), ax ;; write precomp word
8488 mov al, #0x20
8489 out #0x70, al
8490 in al, #0x71
8491 mov (0x003d + 0x08), al ;; drive control byte
8493 mov al, #0x22
8494 out #0x70, al
8495 in al, #0x71
8496 mov ah, al
8497 mov al, #0x21
8498 out #0x70, al
8499 in al, #0x71
8500 mov (0x003d + 0x0C), ax ;; landing zone word
8502 mov al, #0x1c ;; get cylinders word in AX
8503 out #0x70, al
8504 in al, #0x71 ;; high byte
8505 mov ah, al
8506 mov al, #0x1b
8507 out #0x70, al
8508 in al, #0x71 ;; low byte
8509 mov bx, ax ;; BX = cylinders
8511 mov al, #0x1d
8512 out #0x70, al
8513 in al, #0x71
8514 mov cl, al ;; CL = heads
8516 mov al, #0x23
8517 out #0x70, al
8518 in al, #0x71
8519 mov dl, al ;; DL = sectors
8521 cmp bx, #1024
8522 jnbe hd0_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8524 hd0_post_physical_chs:
8525 ;; no logical CHS mapping used, just physical CHS
8526 ;; use Standard Fixed Disk Parameter Table (FDPT)
8527 mov (0x003d + 0x00), bx ;; number of physical cylinders
8528 mov (0x003d + 0x02), cl ;; number of physical heads
8529 mov (0x003d + 0x0E), dl ;; number of physical sectors
8530 jmp check_for_hd1
8532 hd0_post_logical_chs:
8533 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8534 mov (0x003d + 0x09), bx ;; number of physical cylinders
8535 mov (0x003d + 0x0b), cl ;; number of physical heads
8536 mov (0x003d + 0x04), dl ;; number of physical sectors
8537 mov (0x003d + 0x0e), dl ;; number of logical sectors (same)
8538 mov al, #0xa0
8539 mov (0x003d + 0x03), al ;; A0h signature, indicates translated table
8541 cmp bx, #2048
8542 jnbe hd0_post_above_2048
8543 ;; 1024 < c <= 2048 cylinders
8544 shr bx, #0x01
8545 shl cl, #0x01
8546 jmp hd0_post_store_logical
8548 hd0_post_above_2048:
8549 cmp bx, #4096
8550 jnbe hd0_post_above_4096
8551 ;; 2048 < c <= 4096 cylinders
8552 shr bx, #0x02
8553 shl cl, #0x02
8554 jmp hd0_post_store_logical
8556 hd0_post_above_4096:
8557 cmp bx, #8192
8558 jnbe hd0_post_above_8192
8559 ;; 4096 < c <= 8192 cylinders
8560 shr bx, #0x03
8561 shl cl, #0x03
8562 jmp hd0_post_store_logical
8564 hd0_post_above_8192:
8565 ;; 8192 < c <= 16384 cylinders
8566 shr bx, #0x04
8567 shl cl, #0x04
8569 hd0_post_store_logical:
8570 mov (0x003d + 0x00), bx ;; number of physical cylinders
8571 mov (0x003d + 0x02), cl ;; number of physical heads
8572 ;; checksum
8573 mov cl, #0x0f ;; repeat count
8574 mov si, #0x003d ;; offset to disk0 FDPT
8575 mov al, #0x00 ;; sum
8576 hd0_post_checksum_loop:
8577 add al, [si]
8578 inc si
8579 dec cl
8580 jnz hd0_post_checksum_loop
8581 not al ;; now take 2s complement
8582 inc al
8583 mov [si], al
8584 ;;; Done filling EBDA table for hard disk 0.
8587 check_for_hd1:
8588 ;; is there really a second hard disk? if not, return now
8589 mov al, #0x12
8590 out #0x70, al
8591 in al, #0x71
8592 and al, #0x0f
8593 jnz post_d1_exists
8595 post_d1_exists:
8596 ;; check that the hd type is really 0x0f.
8597 cmp al, #0x0f
8598 jz post_d1_extended
8599 HALT(__LINE__)
8600 post_d1_extended:
8601 ;; check that the extended type is 47 - user definable
8602 mov al, #0x1a
8603 out #0x70, al
8604 in al, #0x71
8605 cmp al, #47 ;; decimal 47 - user definable
8606 je post_d1_type47
8607 HALT(__LINE__)
8608 post_d1_type47:
8609 ;; Table for disk1.
8610 ;; CMOS purpose param table offset
8611 ;; 0x24 cylinders low 0
8612 ;; 0x25 cylinders high 1
8613 ;; 0x26 heads 2
8614 ;; 0x27 write pre-comp low 5
8615 ;; 0x28 write pre-comp high 6
8616 ;; 0x29 heads>8 8
8617 ;; 0x2a landing zone low C
8618 ;; 0x2b landing zone high D
8619 ;; 0x2c sectors/track E
8620 ;;; Fill EBDA table for hard disk 1.
8621 mov ax, #EBDA_SEG
8622 mov ds, ax
8623 mov al, #0x28
8624 out #0x70, al
8625 in al, #0x71
8626 mov ah, al
8627 mov al, #0x27
8628 out #0x70, al
8629 in al, #0x71
8630 mov (0x004d + 0x05), ax ;; write precomp word
8632 mov al, #0x29
8633 out #0x70, al
8634 in al, #0x71
8635 mov (0x004d + 0x08), al ;; drive control byte
8637 mov al, #0x2b
8638 out #0x70, al
8639 in al, #0x71
8640 mov ah, al
8641 mov al, #0x2a
8642 out #0x70, al
8643 in al, #0x71
8644 mov (0x004d + 0x0C), ax ;; landing zone word
8646 mov al, #0x25 ;; get cylinders word in AX
8647 out #0x70, al
8648 in al, #0x71 ;; high byte
8649 mov ah, al
8650 mov al, #0x24
8651 out #0x70, al
8652 in al, #0x71 ;; low byte
8653 mov bx, ax ;; BX = cylinders
8655 mov al, #0x26
8656 out #0x70, al
8657 in al, #0x71
8658 mov cl, al ;; CL = heads
8660 mov al, #0x2c
8661 out #0x70, al
8662 in al, #0x71
8663 mov dl, al ;; DL = sectors
8665 cmp bx, #1024
8666 jnbe hd1_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8668 hd1_post_physical_chs:
8669 ;; no logical CHS mapping used, just physical CHS
8670 ;; use Standard Fixed Disk Parameter Table (FDPT)
8671 mov (0x004d + 0x00), bx ;; number of physical cylinders
8672 mov (0x004d + 0x02), cl ;; number of physical heads
8673 mov (0x004d + 0x0E), dl ;; number of physical sectors
8676 hd1_post_logical_chs:
8677 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8678 mov (0x004d + 0x09), bx ;; number of physical cylinders
8679 mov (0x004d + 0x0b), cl ;; number of physical heads
8680 mov (0x004d + 0x04), dl ;; number of physical sectors
8681 mov (0x004d + 0x0e), dl ;; number of logical sectors (same)
8682 mov al, #0xa0
8683 mov (0x004d + 0x03), al ;; A0h signature, indicates translated table
8685 cmp bx, #2048
8686 jnbe hd1_post_above_2048
8687 ;; 1024 < c <= 2048 cylinders
8688 shr bx, #0x01
8689 shl cl, #0x01
8690 jmp hd1_post_store_logical
8692 hd1_post_above_2048:
8693 cmp bx, #4096
8694 jnbe hd1_post_above_4096
8695 ;; 2048 < c <= 4096 cylinders
8696 shr bx, #0x02
8697 shl cl, #0x02
8698 jmp hd1_post_store_logical
8700 hd1_post_above_4096:
8701 cmp bx, #8192
8702 jnbe hd1_post_above_8192
8703 ;; 4096 < c <= 8192 cylinders
8704 shr bx, #0x03
8705 shl cl, #0x03
8706 jmp hd1_post_store_logical
8708 hd1_post_above_8192:
8709 ;; 8192 < c <= 16384 cylinders
8710 shr bx, #0x04
8711 shl cl, #0x04
8713 hd1_post_store_logical:
8714 mov (0x004d + 0x00), bx ;; number of physical cylinders
8715 mov (0x004d + 0x02), cl ;; number of physical heads
8716 ;; checksum
8717 mov cl, #0x0f ;; repeat count
8718 mov si, #0x004d ;; offset to disk0 FDPT
8719 mov al, #0x00 ;; sum
8720 hd1_post_checksum_loop:
8721 add al, [si]
8722 inc si
8723 dec cl
8724 jnz hd1_post_checksum_loop
8725 not al ;; now take 2s complement
8726 inc al
8727 mov [si], al
8728 ;;; Done filling EBDA table for hard disk 1.
8732 ;--------------------
8733 ;- POST: EBDA segment
8734 ;--------------------
8735 ; relocated here because the primary POST area isnt big enough.
8736 ebda_post:
8737 #if BX_USE_EBDA
8738 mov ax, #EBDA_SEG
8739 mov ds, ax
8740 mov byte ptr [0x0], #EBDA_SIZE
8741 #endif
8742 xor ax, ax ; mov EBDA seg into 40E
8743 mov ds, ax
8744 mov word ptr [0x40E], #EBDA_SEG
8745 ret;;
8747 ;--------------------
8748 ;- POST: EOI + jmp via [0x40:67)
8749 ;--------------------
8750 ; relocated here because the primary POST area isnt big enough.
8751 eoi_jmp_post:
8752 call eoi_both_pics
8754 xor ax, ax
8755 mov ds, ax
8757 jmp far ptr [0x467]
8760 ;--------------------
8761 eoi_both_pics:
8762 mov al, #0x20
8763 out #0xA0, al ;; slave PIC EOI
8764 eoi_master_pic:
8765 mov al, #0x20
8766 out #0x20, al ;; master PIC EOI
8769 ;--------------------
8770 BcdToBin:
8771 ;; in: AL in BCD format
8772 ;; out: AL in binary format, AH will always be 0
8773 ;; trashes BX
8774 mov bl, al
8775 and bl, #0x0f ;; bl has low digit
8776 shr al, #4 ;; al has high digit
8777 mov bh, #10
8778 mul al, bh ;; multiply high digit by 10 (result in AX)
8779 add al, bl ;; then add low digit
8782 ;--------------------
8783 timer_tick_post:
8784 ;; Setup the Timer Ticks Count (0x46C:dword) and
8785 ;; Timer Ticks Roller Flag (0x470:byte)
8786 ;; The Timer Ticks Count needs to be set according to
8787 ;; the current CMOS time, as if ticks have been occurring
8788 ;; at 18.2hz since midnight up to this point. Calculating
8789 ;; this is a little complicated. Here are the factors I gather
8790 ;; regarding this. 14,318,180 hz was the original clock speed,
8791 ;; chosen so it could be divided by either 3 to drive the 5Mhz CPU
8792 ;; at the time, or 4 to drive the CGA video adapter. The div3
8793 ;; source was divided again by 4 to feed a 1.193Mhz signal to
8794 ;; the timer. With a maximum 16bit timer count, this is again
8795 ;; divided down by 65536 to 18.2hz.
8797 ;; 14,318,180 Hz clock
8798 ;; /3 = 4,772,726 Hz fed to orginal 5Mhz CPU
8799 ;; /4 = 1,193,181 Hz fed to timer
8800 ;; /65536 (maximum timer count) = 18.20650736 ticks/second
8801 ;; 1 second = 18.20650736 ticks
8802 ;; 1 minute = 1092.390442 ticks
8803 ;; 1 hour = 65543.42651 ticks
8805 ;; Given the values in the CMOS clock, one could calculate
8806 ;; the number of ticks by the following:
8807 ;; ticks = (BcdToBin(seconds) * 18.206507) +
8808 ;; (BcdToBin(minutes) * 1092.3904)
8809 ;; (BcdToBin(hours) * 65543.427)
8810 ;; To get a little more accuracy, since Im using integer
8811 ;; arithmatic, I use:
8812 ;; ticks = (BcdToBin(seconds) * 18206507) / 1000000 +
8813 ;; (BcdToBin(minutes) * 10923904) / 10000 +
8814 ;; (BcdToBin(hours) * 65543427) / 1000
8816 ;; assuming DS=0000
8818 ;; get CMOS seconds
8819 xor eax, eax ;; clear EAX
8820 mov al, #0x00
8821 out #0x70, al
8822 in al, #0x71 ;; AL has CMOS seconds in BCD
8823 call BcdToBin ;; EAX now has seconds in binary
8824 mov edx, #18206507
8825 mul eax, edx
8826 mov ebx, #1000000
8827 xor edx, edx
8828 div eax, ebx
8829 mov ecx, eax ;; ECX will accumulate total ticks
8831 ;; get CMOS minutes
8832 xor eax, eax ;; clear EAX
8833 mov al, #0x02
8834 out #0x70, al
8835 in al, #0x71 ;; AL has CMOS minutes in BCD
8836 call BcdToBin ;; EAX now has minutes in binary
8837 mov edx, #10923904
8838 mul eax, edx
8839 mov ebx, #10000
8840 xor edx, edx
8841 div eax, ebx
8842 add ecx, eax ;; add to total ticks
8844 ;; get CMOS hours
8845 xor eax, eax ;; clear EAX
8846 mov al, #0x04
8847 out #0x70, al
8848 in al, #0x71 ;; AL has CMOS hours in BCD
8849 call BcdToBin ;; EAX now has hours in binary
8850 mov edx, #65543427
8851 mul eax, edx
8852 mov ebx, #1000
8853 xor edx, edx
8854 div eax, ebx
8855 add ecx, eax ;; add to total ticks
8857 mov 0x46C, ecx ;; Timer Ticks Count
8858 xor al, al
8859 mov 0x470, al ;; Timer Ticks Rollover Flag
8862 ;--------------------
8863 int76_handler:
8864 ;; record completion in BIOS task complete flag
8865 push ax
8866 push ds
8867 mov ax, #0x0040
8868 mov ds, ax
8869 mov 0x008E, #0xff
8870 call eoi_both_pics
8871 pop ds
8872 pop ax
8873 iret
8876 ;--------------------
8877 #if BX_APM
8879 use32 386
8880 #define APM_PROT32
8881 #include "apmbios.S"
8883 use16 386
8884 #define APM_PROT16
8885 #include "apmbios.S"
8887 #define APM_REAL
8888 #include "apmbios.S"
8890 #endif
8892 ;--------------------
8893 #if BX_PCIBIOS
8894 use32 386
8895 .align 16
8896 bios32_structure:
8897 db 0x5f, 0x33, 0x32, 0x5f ;; "_32_" signature
8898 dw bios32_entry_point, 0xf ;; 32 bit physical address
8899 db 0 ;; revision level
8900 ;; length in paragraphs and checksum stored in a word to prevent errors
8901 dw (~(((bios32_entry_point >> 8) + (bios32_entry_point & 0xff) + 0x32) \
8902 & 0xff) << 8) + 0x01
8903 db 0,0,0,0,0 ;; reserved
8905 .align 16
8906 bios32_entry_point:
8907 pushfd
8908 cmp eax, #0x49435024 ;; "$PCI"
8909 jne unknown_service
8910 mov eax, #0x80000000
8911 mov dx, #0x0cf8
8912 out dx, eax
8913 mov dx, #0x0cfc
8914 in eax, dx
8915 #ifdef PCI_FIXED_HOST_BRIDGE
8916 cmp eax, #PCI_FIXED_HOST_BRIDGE
8917 jne unknown_service
8918 #else
8919 ;; say ok if a device is present
8920 cmp eax, #0xffffffff
8921 je unknown_service
8922 #endif
8923 mov ebx, #0x000f0000
8924 mov ecx, #0
8925 mov edx, #pcibios_protected
8926 xor al, al
8927 jmp bios32_end
8928 unknown_service:
8929 mov al, #0x80
8930 bios32_end:
8931 #ifdef BX_QEMU
8932 and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
8933 #endif
8934 popfd
8935 retf
8937 .align 16
8938 pcibios_protected:
8939 pushfd
8941 push esi
8942 push edi
8943 cmp al, #0x01 ;; installation check
8944 jne pci_pro_f02
8945 mov bx, #0x0210
8946 mov cx, #0
8947 mov edx, #0x20494350 ;; "PCI "
8948 mov al, #0x01
8949 jmp pci_pro_ok
8950 pci_pro_f02: ;; find pci device
8951 cmp al, #0x02
8952 jne pci_pro_f03
8953 shl ecx, #16
8954 mov cx, dx
8955 xor bx, bx
8956 mov di, #0x00
8957 pci_pro_devloop:
8958 call pci_pro_select_reg
8959 mov dx, #0x0cfc
8960 in eax, dx
8961 cmp eax, ecx
8962 jne pci_pro_nextdev
8963 cmp si, #0
8964 je pci_pro_ok
8965 dec si
8966 pci_pro_nextdev:
8967 inc bx
8968 cmp bx, #0x0100
8969 jne pci_pro_devloop
8970 mov ah, #0x86
8971 jmp pci_pro_fail
8972 pci_pro_f03: ;; find class code
8973 cmp al, #0x03
8974 jne pci_pro_f08
8975 xor bx, bx
8976 mov di, #0x08
8977 pci_pro_devloop2:
8978 call pci_pro_select_reg
8979 mov dx, #0x0cfc
8980 in eax, dx
8981 shr eax, #8
8982 cmp eax, ecx
8983 jne pci_pro_nextdev2
8984 cmp si, #0
8985 je pci_pro_ok
8986 dec si
8987 pci_pro_nextdev2:
8988 inc bx
8989 cmp bx, #0x0100
8990 jne pci_pro_devloop2
8991 mov ah, #0x86
8992 jmp pci_pro_fail
8993 pci_pro_f08: ;; read configuration byte
8994 cmp al, #0x08
8995 jne pci_pro_f09
8996 call pci_pro_select_reg
8997 push edx
8998 mov dx, di
8999 and dx, #0x03
9000 add dx, #0x0cfc
9001 in al, dx
9002 pop edx
9003 mov cl, al
9004 jmp pci_pro_ok
9005 pci_pro_f09: ;; read configuration word
9006 cmp al, #0x09
9007 jne pci_pro_f0a
9008 call pci_pro_select_reg
9009 push edx
9010 mov dx, di
9011 and dx, #0x02
9012 add dx, #0x0cfc
9013 in ax, dx
9014 pop edx
9015 mov cx, ax
9016 jmp pci_pro_ok
9017 pci_pro_f0a: ;; read configuration dword
9018 cmp al, #0x0a
9019 jne pci_pro_f0b
9020 call pci_pro_select_reg
9021 push edx
9022 mov dx, #0x0cfc
9023 in eax, dx
9024 pop edx
9025 mov ecx, eax
9026 jmp pci_pro_ok
9027 pci_pro_f0b: ;; write configuration byte
9028 cmp al, #0x0b
9029 jne pci_pro_f0c
9030 call pci_pro_select_reg
9031 push edx
9032 mov dx, di
9033 and dx, #0x03
9034 add dx, #0x0cfc
9035 mov al, cl
9036 out dx, al
9037 pop edx
9038 jmp pci_pro_ok
9039 pci_pro_f0c: ;; write configuration word
9040 cmp al, #0x0c
9041 jne pci_pro_f0d
9042 call pci_pro_select_reg
9043 push edx
9044 mov dx, di
9045 and dx, #0x02
9046 add dx, #0x0cfc
9047 mov ax, cx
9048 out dx, ax
9049 pop edx
9050 jmp pci_pro_ok
9051 pci_pro_f0d: ;; write configuration dword
9052 cmp al, #0x0d
9053 jne pci_pro_unknown
9054 call pci_pro_select_reg
9055 push edx
9056 mov dx, #0x0cfc
9057 mov eax, ecx
9058 out dx, eax
9059 pop edx
9060 jmp pci_pro_ok
9061 pci_pro_unknown:
9062 mov ah, #0x81
9063 pci_pro_fail:
9064 pop edi
9065 pop esi
9066 #ifdef BX_QEMU
9067 and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
9068 #endif
9069 popfd
9071 retf
9072 pci_pro_ok:
9073 xor ah, ah
9074 pop edi
9075 pop esi
9076 #ifdef BX_QEMU
9077 and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
9078 #endif
9079 popfd
9081 retf
9083 pci_pro_select_reg:
9084 push edx
9085 mov eax, #0x800000
9086 mov ax, bx
9087 shl eax, #8
9088 and di, #0xff
9089 or ax, di
9090 and al, #0xfc
9091 mov dx, #0x0cf8
9092 out dx, eax
9093 pop edx
9096 use16 386
9098 pcibios_real:
9099 push eax
9100 push dx
9101 mov eax, #0x80000000
9102 mov dx, #0x0cf8
9103 out dx, eax
9104 mov dx, #0x0cfc
9105 in eax, dx
9106 #ifdef PCI_FIXED_HOST_BRIDGE
9107 cmp eax, #PCI_FIXED_HOST_BRIDGE
9108 je pci_present
9109 #else
9110 ;; say ok if a device is present
9111 cmp eax, #0xffffffff
9112 jne pci_present
9113 #endif
9114 pop dx
9115 pop eax
9116 mov ah, #0xff
9119 pci_present:
9120 pop dx
9121 pop eax
9122 cmp al, #0x01 ;; installation check
9123 jne pci_real_f02
9124 mov ax, #0x0001
9125 mov bx, #0x0210
9126 mov cx, #0
9127 mov edx, #0x20494350 ;; "PCI "
9128 mov edi, #0xf0000
9129 mov di, #pcibios_protected
9132 pci_real_f02: ;; find pci device
9133 push esi
9134 push edi
9135 cmp al, #0x02
9136 jne pci_real_f03
9137 shl ecx, #16
9138 mov cx, dx
9139 xor bx, bx
9140 mov di, #0x00
9141 pci_real_devloop:
9142 call pci_real_select_reg
9143 mov dx, #0x0cfc
9144 in eax, dx
9145 cmp eax, ecx
9146 jne pci_real_nextdev
9147 cmp si, #0
9148 je pci_real_ok
9149 dec si
9150 pci_real_nextdev:
9151 inc bx
9152 cmp bx, #0x0100
9153 jne pci_real_devloop
9154 mov dx, cx
9155 shr ecx, #16
9156 mov ax, #0x8602
9157 jmp pci_real_fail
9158 pci_real_f03: ;; find class code
9159 cmp al, #0x03
9160 jne pci_real_f08
9161 xor bx, bx
9162 mov di, #0x08
9163 pci_real_devloop2:
9164 call pci_real_select_reg
9165 mov dx, #0x0cfc
9166 in eax, dx
9167 shr eax, #8
9168 cmp eax, ecx
9169 jne pci_real_nextdev2
9170 cmp si, #0
9171 je pci_real_ok
9172 dec si
9173 pci_real_nextdev2:
9174 inc bx
9175 cmp bx, #0x0100
9176 jne pci_real_devloop2
9177 mov dx, cx
9178 shr ecx, #16
9179 mov ax, #0x8603
9180 jmp pci_real_fail
9181 pci_real_f08: ;; read configuration byte
9182 cmp al, #0x08
9183 jne pci_real_f09
9184 call pci_real_select_reg
9185 push dx
9186 mov dx, di
9187 and dx, #0x03
9188 add dx, #0x0cfc
9189 in al, dx
9190 pop dx
9191 mov cl, al
9192 jmp pci_real_ok
9193 pci_real_f09: ;; read configuration word
9194 cmp al, #0x09
9195 jne pci_real_f0a
9196 call pci_real_select_reg
9197 push dx
9198 mov dx, di
9199 and dx, #0x02
9200 add dx, #0x0cfc
9201 in ax, dx
9202 pop dx
9203 mov cx, ax
9204 jmp pci_real_ok
9205 pci_real_f0a: ;; read configuration dword
9206 cmp al, #0x0a
9207 jne pci_real_f0b
9208 call pci_real_select_reg
9209 push dx
9210 mov dx, #0x0cfc
9211 in eax, dx
9212 pop dx
9213 mov ecx, eax
9214 jmp pci_real_ok
9215 pci_real_f0b: ;; write configuration byte
9216 cmp al, #0x0b
9217 jne pci_real_f0c
9218 call pci_real_select_reg
9219 push dx
9220 mov dx, di
9221 and dx, #0x03
9222 add dx, #0x0cfc
9223 mov al, cl
9224 out dx, al
9225 pop dx
9226 jmp pci_real_ok
9227 pci_real_f0c: ;; write configuration word
9228 cmp al, #0x0c
9229 jne pci_real_f0d
9230 call pci_real_select_reg
9231 push dx
9232 mov dx, di
9233 and dx, #0x02
9234 add dx, #0x0cfc
9235 mov ax, cx
9236 out dx, ax
9237 pop dx
9238 jmp pci_real_ok
9239 pci_real_f0d: ;; write configuration dword
9240 cmp al, #0x0d
9241 jne pci_real_f0e
9242 call pci_real_select_reg
9243 push dx
9244 mov dx, #0x0cfc
9245 mov eax, ecx
9246 out dx, eax
9247 pop dx
9248 jmp pci_real_ok
9249 pci_real_f0e: ;; get irq routing options
9250 cmp al, #0x0e
9251 jne pci_real_unknown
9252 SEG ES
9253 cmp word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9254 jb pci_real_too_small
9255 SEG ES
9256 mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9257 pushf
9258 push ds
9259 push es
9260 push cx
9261 push si
9262 push di
9264 mov si, #pci_routing_table_structure_start
9265 push cs
9266 pop ds
9267 SEG ES
9268 mov cx, [di+2]
9269 SEG ES
9270 mov es, [di+4]
9271 mov di, cx
9272 mov cx, #pci_routing_table_structure_end - pci_routing_table_structure_start
9274 movsb
9275 pop di
9276 pop si
9277 pop cx
9278 pop es
9279 pop ds
9280 popf
9281 mov bx, #(1 << 9) | (1 << 11) ;; irq 9 and 11 are used
9282 jmp pci_real_ok
9283 pci_real_too_small:
9284 SEG ES
9285 mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9286 mov ah, #0x89
9287 jmp pci_real_fail
9289 pci_real_unknown:
9290 mov ah, #0x81
9291 pci_real_fail:
9292 pop edi
9293 pop esi
9296 pci_real_ok:
9297 xor ah, ah
9298 pop edi
9299 pop esi
9303 pci_real_select_reg:
9304 push dx
9305 mov eax, #0x800000
9306 mov ax, bx
9307 shl eax, #8
9308 and di, #0xff
9309 or ax, di
9310 and al, #0xfc
9311 mov dx, #0x0cf8
9312 out dx, eax
9313 pop dx
9316 .align 16
9317 pci_routing_table_structure:
9318 db 0x24, 0x50, 0x49, 0x52 ;; "$PIR" signature
9319 db 0, 1 ;; version
9320 dw 32 + (6 * 16) ;; table size
9321 db 0 ;; PCI interrupt router bus
9322 db 0x08 ;; PCI interrupt router DevFunc
9323 dw 0x0000 ;; PCI exclusive IRQs
9324 dw 0x8086 ;; compatible PCI interrupt router vendor ID
9325 dw 0x7000 ;; compatible PCI interrupt router device ID
9326 dw 0,0 ;; Miniport data
9327 db 0,0,0,0,0,0,0,0,0,0,0 ;; reserved
9328 db 0x07 ;; checksum
9329 pci_routing_table_structure_start:
9330 ;; first slot entry PCI-to-ISA (embedded)
9331 db 0 ;; pci bus number
9332 db 0x08 ;; pci device number (bit 7-3)
9333 db 0x60 ;; link value INTA#: pointer into PCI2ISA config space
9334 dw 0xdef8 ;; IRQ bitmap INTA#
9335 db 0x61 ;; link value INTB#
9336 dw 0xdef8 ;; IRQ bitmap INTB#
9337 db 0x62 ;; link value INTC#
9338 dw 0xdef8 ;; IRQ bitmap INTC#
9339 db 0x63 ;; link value INTD#
9340 dw 0xdef8 ;; IRQ bitmap INTD#
9341 db 0 ;; physical slot (0 = embedded)
9342 db 0 ;; reserved
9343 ;; second slot entry: 1st PCI slot
9344 db 0 ;; pci bus number
9345 db 0x10 ;; pci device number (bit 7-3)
9346 db 0x61 ;; link value INTA#
9347 dw 0xdef8 ;; IRQ bitmap INTA#
9348 db 0x62 ;; link value INTB#
9349 dw 0xdef8 ;; IRQ bitmap INTB#
9350 db 0x63 ;; link value INTC#
9351 dw 0xdef8 ;; IRQ bitmap INTC#
9352 db 0x60 ;; link value INTD#
9353 dw 0xdef8 ;; IRQ bitmap INTD#
9354 db 1 ;; physical slot (0 = embedded)
9355 db 0 ;; reserved
9356 ;; third slot entry: 2nd PCI slot
9357 db 0 ;; pci bus number
9358 db 0x18 ;; pci device number (bit 7-3)
9359 db 0x62 ;; link value INTA#
9360 dw 0xdef8 ;; IRQ bitmap INTA#
9361 db 0x63 ;; link value INTB#
9362 dw 0xdef8 ;; IRQ bitmap INTB#
9363 db 0x60 ;; link value INTC#
9364 dw 0xdef8 ;; IRQ bitmap INTC#
9365 db 0x61 ;; link value INTD#
9366 dw 0xdef8 ;; IRQ bitmap INTD#
9367 db 2 ;; physical slot (0 = embedded)
9368 db 0 ;; reserved
9369 ;; 4th slot entry: 3rd PCI slot
9370 db 0 ;; pci bus number
9371 db 0x20 ;; pci device number (bit 7-3)
9372 db 0x63 ;; link value INTA#
9373 dw 0xdef8 ;; IRQ bitmap INTA#
9374 db 0x60 ;; link value INTB#
9375 dw 0xdef8 ;; IRQ bitmap INTB#
9376 db 0x61 ;; link value INTC#
9377 dw 0xdef8 ;; IRQ bitmap INTC#
9378 db 0x62 ;; link value INTD#
9379 dw 0xdef8 ;; IRQ bitmap INTD#
9380 db 3 ;; physical slot (0 = embedded)
9381 db 0 ;; reserved
9382 ;; 5th slot entry: 4rd PCI slot
9383 db 0 ;; pci bus number
9384 db 0x28 ;; pci device number (bit 7-3)
9385 db 0x60 ;; link value INTA#
9386 dw 0xdef8 ;; IRQ bitmap INTA#
9387 db 0x61 ;; link value INTB#
9388 dw 0xdef8 ;; IRQ bitmap INTB#
9389 db 0x62 ;; link value INTC#
9390 dw 0xdef8 ;; IRQ bitmap INTC#
9391 db 0x63 ;; link value INTD#
9392 dw 0xdef8 ;; IRQ bitmap INTD#
9393 db 4 ;; physical slot (0 = embedded)
9394 db 0 ;; reserved
9395 ;; 6th slot entry: 5rd PCI slot
9396 db 0 ;; pci bus number
9397 db 0x30 ;; pci device number (bit 7-3)
9398 db 0x61 ;; link value INTA#
9399 dw 0xdef8 ;; IRQ bitmap INTA#
9400 db 0x62 ;; link value INTB#
9401 dw 0xdef8 ;; IRQ bitmap INTB#
9402 db 0x63 ;; link value INTC#
9403 dw 0xdef8 ;; IRQ bitmap INTC#
9404 db 0x60 ;; link value INTD#
9405 dw 0xdef8 ;; IRQ bitmap INTD#
9406 db 5 ;; physical slot (0 = embedded)
9407 db 0 ;; reserved
9408 pci_routing_table_structure_end:
9410 #if !BX_ROMBIOS32
9411 pci_irq_list:
9412 db 11, 10, 9, 5;
9414 pcibios_init_sel_reg:
9415 push eax
9416 mov eax, #0x800000
9417 mov ax, bx
9418 shl eax, #8
9419 and dl, #0xfc
9420 or al, dl
9421 mov dx, #0x0cf8
9422 out dx, eax
9423 pop eax
9426 pcibios_init_iomem_bases:
9427 push bp
9428 mov bp, sp
9429 mov eax, #0xe0000000 ;; base for memory init
9430 push eax
9431 mov ax, #0xc000 ;; base for i/o init
9432 push ax
9433 mov ax, #0x0010 ;; start at base address #0
9434 push ax
9435 mov bx, #0x0008
9436 pci_init_io_loop1:
9437 mov dl, #0x00
9438 call pcibios_init_sel_reg
9439 mov dx, #0x0cfc
9440 in ax, dx
9441 cmp ax, #0xffff
9442 jz next_pci_dev
9443 mov dl, #0x04 ;; disable i/o and memory space access
9444 call pcibios_init_sel_reg
9445 mov dx, #0x0cfc
9446 in al, dx
9447 and al, #0xfc
9448 out dx, al
9449 pci_init_io_loop2:
9450 mov dl, [bp-8]
9451 call pcibios_init_sel_reg
9452 mov dx, #0x0cfc
9453 in eax, dx
9454 test al, #0x01
9455 jnz init_io_base
9456 mov ecx, eax
9457 mov eax, #0xffffffff
9458 out dx, eax
9459 in eax, dx
9460 cmp eax, ecx
9461 je next_pci_base
9462 xor eax, #0xffffffff
9463 mov ecx, eax
9464 mov eax, [bp-4]
9465 out dx, eax
9466 add eax, ecx ;; calculate next free mem base
9467 add eax, #0x01000000
9468 and eax, #0xff000000
9469 mov [bp-4], eax
9470 jmp next_pci_base
9471 init_io_base:
9472 mov cx, ax
9473 mov ax, #0xffff
9474 out dx, ax
9475 in ax, dx
9476 cmp ax, cx
9477 je next_pci_base
9478 xor ax, #0xfffe
9479 mov cx, ax
9480 mov ax, [bp-6]
9481 out dx, ax
9482 add ax, cx ;; calculate next free i/o base
9483 add ax, #0x0100
9484 and ax, #0xff00
9485 mov [bp-6], ax
9486 next_pci_base:
9487 mov al, [bp-8]
9488 add al, #0x04
9489 cmp al, #0x28
9490 je enable_iomem_space
9491 mov byte ptr[bp-8], al
9492 jmp pci_init_io_loop2
9493 enable_iomem_space:
9494 mov dl, #0x04 ;; enable i/o and memory space access if available
9495 call pcibios_init_sel_reg
9496 mov dx, #0x0cfc
9497 in al, dx
9498 or al, #0x07
9499 out dx, al
9500 next_pci_dev:
9501 mov byte ptr[bp-8], #0x10
9502 inc bx
9503 cmp bx, #0x0100
9504 jne pci_init_io_loop1
9505 mov sp, bp
9506 pop bp
9509 pcibios_init_set_elcr:
9510 push ax
9511 push cx
9512 mov dx, #0x04d0
9513 test al, #0x08
9514 jz is_master_pic
9515 inc dx
9516 and al, #0x07
9517 is_master_pic:
9518 mov cl, al
9519 mov bl, #0x01
9520 shl bl, cl
9521 in al, dx
9522 or al, bl
9523 out dx, al
9524 pop cx
9525 pop ax
9528 pcibios_init_irqs:
9529 push ds
9530 push bp
9531 mov ax, #0xf000
9532 mov ds, ax
9533 mov dx, #0x04d0 ;; reset ELCR1 + ELCR2
9534 mov al, #0x00
9535 out dx, al
9536 inc dx
9537 out dx, al
9538 mov si, #pci_routing_table_structure
9539 mov bh, [si+8]
9540 mov bl, [si+9]
9541 mov dl, #0x00
9542 call pcibios_init_sel_reg
9543 mov dx, #0x0cfc
9544 in eax, dx
9545 cmp eax, [si+12] ;; check irq router
9546 jne pci_init_end
9547 mov dl, [si+34]
9548 call pcibios_init_sel_reg
9549 push bx ;; save irq router bus + devfunc
9550 mov dx, #0x0cfc
9551 mov ax, #0x8080
9552 out dx, ax ;; reset PIRQ route control
9553 inc dx
9554 inc dx
9555 out dx, ax
9556 mov ax, [si+6]
9557 sub ax, #0x20
9558 shr ax, #4
9559 mov cx, ax
9560 add si, #0x20 ;; set pointer to 1st entry
9561 mov bp, sp
9562 mov ax, #pci_irq_list
9563 push ax
9564 xor ax, ax
9565 push ax
9566 pci_init_irq_loop1:
9567 mov bh, [si]
9568 mov bl, [si+1]
9569 pci_init_irq_loop2:
9570 mov dl, #0x00
9571 call pcibios_init_sel_reg
9572 mov dx, #0x0cfc
9573 in ax, dx
9574 cmp ax, #0xffff
9575 jnz pci_test_int_pin
9576 test bl, #0x07
9577 jz next_pir_entry
9578 jmp next_pci_func
9579 pci_test_int_pin:
9580 mov dl, #0x3c
9581 call pcibios_init_sel_reg
9582 mov dx, #0x0cfd
9583 in al, dx
9584 and al, #0x07
9585 jz next_pci_func
9586 dec al ;; determine pirq reg
9587 mov dl, #0x03
9588 mul al, dl
9589 add al, #0x02
9590 xor ah, ah
9591 mov bx, ax
9592 mov al, [si+bx]
9593 mov dl, al
9594 mov bx, [bp]
9595 call pcibios_init_sel_reg
9596 mov dx, #0x0cfc
9597 and al, #0x03
9598 add dl, al
9599 in al, dx
9600 cmp al, #0x80
9601 jb pirq_found
9602 mov bx, [bp-2] ;; pci irq list pointer
9603 mov al, [bx]
9604 out dx, al
9605 inc bx
9606 mov [bp-2], bx
9607 call pcibios_init_set_elcr
9608 pirq_found:
9609 mov bh, [si]
9610 mov bl, [si+1]
9611 add bl, [bp-3] ;; pci function number
9612 mov dl, #0x3c
9613 call pcibios_init_sel_reg
9614 mov dx, #0x0cfc
9615 out dx, al
9616 next_pci_func:
9617 inc byte ptr[bp-3]
9618 inc bl
9619 test bl, #0x07
9620 jnz pci_init_irq_loop2
9621 next_pir_entry:
9622 add si, #0x10
9623 mov byte ptr[bp-3], #0x00
9624 loop pci_init_irq_loop1
9625 mov sp, bp
9626 pop bx
9627 pci_init_end:
9628 pop bp
9629 pop ds
9631 #endif // BX_ROMBIOS32
9632 #endif // BX_PCIBIOS
9634 #if BX_ROMBIOS32
9635 rombios32_init:
9636 ;; save a20 and enable it
9637 in al, 0x92
9638 push ax
9639 or al, #0x02
9640 out 0x92, al
9642 ;; save SS:SP to the BDA
9643 xor ax, ax
9644 mov ds, ax
9645 mov 0x0469, ss
9646 mov 0x0467, sp
9648 SEG CS
9649 lidt [pmode_IDT_info]
9650 SEG CS
9651 lgdt [rombios32_gdt_48]
9652 ;; set PE bit in CR0
9653 mov eax, cr0
9654 or al, #0x01
9655 mov cr0, eax
9656 ;; start protected mode code: ljmpl 0x10:rombios32_init1
9657 db 0x66, 0xea
9658 dw rombios32_05
9659 dw 0x000f ;; high 16 bit address
9660 dw 0x0010
9662 use32 386
9663 rombios32_05:
9664 ;; init data segments
9665 mov eax, #0x18
9666 mov ds, ax
9667 mov es, ax
9668 mov ss, ax
9669 xor eax, eax
9670 mov fs, ax
9671 mov gs, ax
9674 ;; copy rombios32 code to ram (ram offset = 1MB)
9675 mov esi, #0xfffe0000
9676 mov edi, #0x00040000
9677 mov ecx, #0x10000 / 4
9679 movsd
9681 ;; init the stack pointer
9682 mov esp, #0x00080000
9684 ;; call rombios32 code
9685 mov eax, #0x00040000
9686 call eax
9688 ;; reset the memory (some boot loaders such as syslinux suppose
9689 ;; that the memory is set to zero)
9690 mov edi, #0x00040000
9691 mov ecx, #0x40000 / 4
9692 xor eax, eax
9694 stosd
9696 ;; reset the memory (some boot loaders such as syslinux suppose
9697 ;; that the memory is set to zero)
9698 mov edi, #0x00040000
9699 mov ecx, #0x40000 / 4
9700 xor eax, eax
9701 rep
9702 stosd
9704 ;; return to 16 bit protected mode first
9705 db 0xea
9706 dd rombios32_10
9707 dw 0x20
9709 use16 386
9710 rombios32_10:
9711 ;; restore data segment limits to 0xffff
9712 mov ax, #0x28
9713 mov ds, ax
9714 mov es, ax
9715 mov ss, ax
9716 mov fs, ax
9717 mov gs, ax
9719 ;; reset PE bit in CR0
9720 mov eax, cr0
9721 and al, #0xFE
9722 mov cr0, eax
9724 ;; far jump to flush CPU queue after transition to real mode
9725 JMP_AP(0xf000, rombios32_real_mode)
9727 rombios32_real_mode:
9728 ;; restore IDT to normal real-mode defaults
9729 SEG CS
9730 lidt [rmode_IDT_info]
9732 xor ax, ax
9733 mov ds, ax
9734 mov es, ax
9735 mov fs, ax
9736 mov gs, ax
9738 ;; restore SS:SP from the BDA
9739 mov ss, 0x0469
9740 xor esp, esp
9741 mov sp, 0x0467
9742 ;; restore a20
9743 pop ax
9744 out 0x92, al
9747 rombios32_gdt_48:
9748 dw 0x30
9749 dw rombios32_gdt
9750 dw 0x000f
9752 rombios32_gdt:
9753 dw 0, 0, 0, 0
9754 dw 0, 0, 0, 0
9755 dw 0xffff, 0, 0x9b00, 0x00cf ; 32 bit flat code segment (0x10)
9756 dw 0xffff, 0, 0x9300, 0x00cf ; 32 bit flat data segment (0x18)
9757 dw 0xffff, 0, 0x9b0f, 0x0000 ; 16 bit code segment base=0xf0000 limit=0xffff
9758 dw 0xffff, 0, 0x9300, 0x0000 ; 16 bit data segment base=0x0 limit=0xffff
9759 #endif
9762 ; parallel port detection: base address in DX, index in BX, timeout in CL
9763 detect_parport:
9764 push dx
9765 add dx, #2
9766 in al, dx
9767 and al, #0xdf ; clear input mode
9768 out dx, al
9769 pop dx
9770 mov al, #0xaa
9771 out dx, al
9772 in al, dx
9773 cmp al, #0xaa
9774 jne no_parport
9775 push bx
9776 shl bx, #1
9777 mov [bx+0x408], dx ; Parallel I/O address
9778 pop bx
9779 mov [bx+0x478], cl ; Parallel printer timeout
9780 inc bx
9781 no_parport:
9784 ; serial port detection: base address in DX, index in BX, timeout in CL
9785 detect_serial:
9786 push dx
9787 inc dx
9788 mov al, #0x02
9789 out dx, al
9790 in al, dx
9791 cmp al, #0x02
9792 jne no_serial
9793 inc dx
9794 in al, dx
9795 cmp al, #0x02
9796 jne no_serial
9797 dec dx
9798 xor al, al
9799 out dx, al
9800 pop dx
9801 push bx
9802 shl bx, #1
9803 mov [bx+0x400], dx ; Serial I/O address
9804 pop bx
9805 mov [bx+0x47c], cl ; Serial timeout
9806 inc bx
9808 no_serial:
9809 pop dx
9812 rom_checksum:
9813 push ax
9814 push bx
9815 push cx
9816 xor ax, ax
9817 xor bx, bx
9818 xor cx, cx
9819 mov ch, [2]
9820 shl cx, #1
9821 checksum_loop:
9822 add al, [bx]
9823 inc bx
9824 loop checksum_loop
9825 and al, #0xff
9826 pop cx
9827 pop bx
9828 pop ax
9832 ;; We need a copy of this string, but we are not actually a PnP BIOS,
9833 ;; so make sure it is *not* aligned, so OSes will not see it if they scan.
9834 .align 16
9835 db 0
9836 pnp_string:
9837 .ascii "$PnP"
9840 rom_scan:
9841 ;; Scan for existence of valid expansion ROMS.
9842 ;; Video ROM: from 0xC0000..0xC7FFF in 2k increments
9843 ;; General ROM: from 0xC8000..0xDFFFF in 2k increments
9844 ;; System ROM: only 0xE0000
9846 ;; Header:
9847 ;; Offset Value
9848 ;; 0 0x55
9849 ;; 1 0xAA
9850 ;; 2 ROM length in 512-byte blocks
9851 ;; 3 ROM initialization entry point (FAR CALL)
9853 mov cx, #0xc000
9854 rom_scan_loop:
9855 mov ds, cx
9856 mov ax, #0x0004 ;; start with increment of 4 (512-byte) blocks = 2k
9857 cmp [0], #0xAA55 ;; look for signature
9858 jne rom_scan_increment
9859 call rom_checksum
9860 jnz rom_scan_increment
9861 mov al, [2] ;; change increment to ROM length in 512-byte blocks
9863 ;; We want our increment in 512-byte quantities, rounded to
9864 ;; the nearest 2k quantity, since we only scan at 2k intervals.
9865 test al, #0x03
9866 jz block_count_rounded
9867 and al, #0xfc ;; needs rounding up
9868 add al, #0x04
9869 block_count_rounded:
9871 xor bx, bx ;; Restore DS back to 0000:
9872 mov ds, bx
9873 push ax ;; Save AX
9874 push di ;; Save DI
9875 ;; Push addr of ROM entry point
9876 push cx ;; Push seg
9877 push #0x0003 ;; Push offset
9879 ;; Point ES:DI at "$PnP", which tells the ROM that we are a PnP BIOS.
9880 ;; That should stop it grabbing INT 19h; we will use its BEV instead.
9881 mov ax, #0xf000
9882 mov es, ax
9883 lea di, pnp_string
9885 mov bp, sp ;; Call ROM init routine using seg:off on stack
9886 db 0xff ;; call_far ss:[bp+0]
9887 db 0x5e
9888 db 0
9889 cli ;; In case expansion ROM BIOS turns IF on
9890 add sp, #2 ;; Pop offset value
9891 pop cx ;; Pop seg value (restore CX)
9893 ;; Look at the ROM's PnP Expansion header. Properly, we're supposed
9894 ;; to init all the ROMs and then go back and build an IPL table of
9895 ;; all the bootable devices, but we can get away with one pass.
9896 mov ds, cx ;; ROM base
9897 mov bx, 0x001a ;; 0x1A is the offset into ROM header that contains...
9898 mov ax, [bx] ;; the offset of PnP expansion header, where...
9899 cmp ax, #0x5024 ;; we look for signature "$PnP"
9900 jne no_bev
9901 mov ax, 2[bx]
9902 cmp ax, #0x506e
9903 jne no_bev
9904 mov ax, 0x1a[bx] ;; 0x1A is also the offset into the expansion header of...
9905 cmp ax, #0x0000 ;; the Bootstrap Entry Vector, or zero if there is none.
9906 je no_bev
9908 ;; Found a device that thinks it can boot the system. Record its BEV.
9909 mov bx, #IPL_SEG ;; Go to the segment where the IPL table lives
9910 mov ds, bx
9911 mov bx, IPL_COUNT_OFFSET ;; Read the number of entries so far
9912 cmp bx, #IPL_TABLE_ENTRIES
9913 je no_bev ;; Get out if the table is full
9914 shl bx, #0x4 ;; Turn count into offset (entries are 16 bytes)
9915 mov 0[bx], #0x80 ;; This entry is a BEV device
9916 mov 6[bx], cx ;; Build a far pointer from the segment...
9917 mov 4[bx], ax ;; and the offset
9918 shr bx, #0x4 ;; Turn the offset back into a count
9919 inc bx ;; We have one more entry now
9920 mov IPL_COUNT_OFFSET, bx ;; Remember that.
9922 no_bev:
9923 pop di ;; Restore DI
9924 pop ax ;; Restore AX
9925 rom_scan_increment:
9926 shl ax, #5 ;; convert 512-bytes blocks to 16-byte increments
9927 ;; because the segment selector is shifted left 4 bits.
9928 add cx, ax
9929 cmp cx, #0xe000
9930 jbe rom_scan_loop
9932 xor ax, ax ;; Restore DS back to 0000:
9933 mov ds, ax
9936 ;; for 'C' strings and other data, insert them here with
9937 ;; a the following hack:
9938 ;; DATA_SEG_DEFS_HERE
9941 ;; the following area can be used to write dynamically generated tables
9942 .align 16
9943 bios_table_area_start:
9944 dd 0xaafb4442
9945 dd bios_table_area_end - bios_table_area_start - 8;
9947 ;--------
9948 ;- POST -
9949 ;--------
9950 .org 0xe05b ; POST Entry Point
9951 bios_table_area_end:
9952 post:
9953 ;; enable cache
9954 mov eax, cr0
9955 and eax, #0x9fffffff
9956 mov cr0, eax
9958 xor ax, ax
9960 ;; first reset the DMA controllers
9961 out 0x0d,al
9962 out 0xda,al
9964 ;; then initialize the DMA controllers
9965 mov al, #0xC0
9966 out 0xD6, al ; cascade mode of channel 4 enabled
9967 mov al, #0x00
9968 out 0xD4, al ; unmask channel 4
9970 ;; Examine CMOS shutdown status.
9971 mov AL, #0x0f
9972 out 0x70, AL
9973 in AL, 0x71
9975 ;; backup status
9976 mov bl, al
9978 ;; Reset CMOS shutdown status.
9979 mov AL, #0x0f
9980 out 0x70, AL ; select CMOS register Fh
9981 mov AL, #0x00
9982 out 0x71, AL ; set shutdown action to normal
9984 ;; Examine CMOS shutdown status.
9985 mov al, bl
9987 ;; 0x00, 0x09, 0x0D+ = normal startup
9988 cmp AL, #0x00
9989 jz normal_post
9990 cmp AL, #0x0d
9991 jae normal_post
9992 cmp AL, #0x09
9993 je normal_post
9995 ;; 0x05 = eoi + jmp via [0x40:0x67] jump
9996 cmp al, #0x05
9997 je eoi_jmp_post
9999 ;; Examine CMOS shutdown status.
10000 ;; 0x01,0x02,0x03,0x04,0x06,0x07,0x08, 0x0a, 0x0b, 0x0c = Unimplemented shutdown status.
10001 push bx
10002 call _shutdown_status_panic
10004 #if 0
10005 HALT(__LINE__)
10007 ;#if 0
10008 ; 0xb0, 0x20, /* mov al, #0x20 */
10009 ; 0xe6, 0x20, /* out 0x20, al ;send EOI to PIC */
10010 ;#endif
10012 pop es
10013 pop ds
10014 popa
10015 iret
10016 #endif
10018 normal_post:
10019 ; case 0: normal startup
10022 mov ax, #0xfffe
10023 mov sp, ax
10024 xor ax, ax
10025 mov ds, ax
10026 mov ss, ax
10028 ;; zero out BIOS data area (40:00..40:ff)
10029 mov es, ax
10030 mov cx, #0x0080 ;; 128 words
10031 mov di, #0x0400
10034 stosw
10036 call _log_bios_start
10038 ;; set all interrupts to default handler
10039 xor bx, bx ;; offset index
10040 mov cx, #0x0100 ;; counter (256 interrupts)
10041 mov ax, #dummy_iret_handler
10042 mov dx, #0xF000
10044 post_default_ints:
10045 mov [bx], ax
10046 inc bx
10047 inc bx
10048 mov [bx], dx
10049 inc bx
10050 inc bx
10051 loop post_default_ints
10053 ;; set vector 0x79 to zero
10054 ;; this is used by 'gardian angel' protection system
10055 SET_INT_VECTOR(0x79, #0, #0)
10057 ;; base memory in K 40:13 (word)
10058 mov ax, #BASE_MEM_IN_K
10059 mov 0x0413, ax
10062 ;; Manufacturing Test 40:12
10063 ;; zerod out above
10065 ;; Warm Boot Flag 0040:0072
10066 ;; value of 1234h = skip memory checks
10067 ;; zerod out above
10070 ;; Printer Services vector
10071 SET_INT_VECTOR(0x17, #0xF000, #int17_handler)
10073 ;; Bootstrap failure vector
10074 SET_INT_VECTOR(0x18, #0xF000, #int18_handler)
10076 ;; Bootstrap Loader vector
10077 SET_INT_VECTOR(0x19, #0xF000, #int19_handler)
10079 ;; User Timer Tick vector
10080 SET_INT_VECTOR(0x1c, #0xF000, #int1c_handler)
10082 ;; Memory Size Check vector
10083 SET_INT_VECTOR(0x12, #0xF000, #int12_handler)
10085 ;; Equipment Configuration Check vector
10086 SET_INT_VECTOR(0x11, #0xF000, #int11_handler)
10088 ;; System Services
10089 SET_INT_VECTOR(0x15, #0xF000, #int15_handler)
10091 ;; EBDA setup
10092 call ebda_post
10094 ;; PIT setup
10095 SET_INT_VECTOR(0x08, #0xF000, #int08_handler)
10096 ;; int 1C already points at dummy_iret_handler (above)
10097 mov al, #0x34 ; timer0: binary count, 16bit count, mode 2
10098 out 0x43, al
10099 mov al, #0x00 ; maximum count of 0000H = 18.2Hz
10100 out 0x40, al
10101 out 0x40, al
10103 ;; Keyboard
10104 SET_INT_VECTOR(0x09, #0xF000, #int09_handler)
10105 SET_INT_VECTOR(0x16, #0xF000, #int16_handler)
10107 xor ax, ax
10108 mov ds, ax
10109 mov 0x0417, al /* keyboard shift flags, set 1 */
10110 mov 0x0418, al /* keyboard shift flags, set 2 */
10111 mov 0x0419, al /* keyboard alt-numpad work area */
10112 mov 0x0471, al /* keyboard ctrl-break flag */
10113 mov 0x0497, al /* keyboard status flags 4 */
10114 mov al, #0x10
10115 mov 0x0496, al /* keyboard status flags 3 */
10118 /* keyboard head of buffer pointer */
10119 mov bx, #0x001E
10120 mov 0x041A, bx
10122 /* keyboard end of buffer pointer */
10123 mov 0x041C, bx
10125 /* keyboard pointer to start of buffer */
10126 mov bx, #0x001E
10127 mov 0x0480, bx
10129 /* keyboard pointer to end of buffer */
10130 mov bx, #0x003E
10131 mov 0x0482, bx
10133 /* init the keyboard */
10134 call _keyboard_init
10136 ;; mov CMOS Equipment Byte to BDA Equipment Word
10137 mov ax, 0x0410
10138 mov al, #0x14
10139 out 0x70, al
10140 in al, 0x71
10141 mov 0x0410, ax
10144 ;; Parallel setup
10145 SET_INT_VECTOR(0x0F, #0xF000, #dummy_iret_handler)
10146 xor ax, ax
10147 mov ds, ax
10148 xor bx, bx
10149 mov cl, #0x14 ; timeout value
10150 mov dx, #0x378 ; Parallel I/O address, port 1
10151 call detect_parport
10152 mov dx, #0x278 ; Parallel I/O address, port 2
10153 call detect_parport
10154 shl bx, #0x0e
10155 mov ax, 0x410 ; Equipment word bits 14..15 determing # parallel ports
10156 and ax, #0x3fff
10157 or ax, bx ; set number of parallel ports
10158 mov 0x410, ax
10160 ;; Serial setup
10161 SET_INT_VECTOR(0x0C, #0xF000, #dummy_iret_handler)
10162 SET_INT_VECTOR(0x14, #0xF000, #int14_handler)
10163 xor bx, bx
10164 mov cl, #0x0a ; timeout value
10165 mov dx, #0x03f8 ; Serial I/O address, port 1
10166 call detect_serial
10167 mov dx, #0x02f8 ; Serial I/O address, port 2
10168 call detect_serial
10169 mov dx, #0x03e8 ; Serial I/O address, port 3
10170 call detect_serial
10171 mov dx, #0x02e8 ; Serial I/O address, port 4
10172 call detect_serial
10173 shl bx, #0x09
10174 mov ax, 0x410 ; Equipment word bits 9..11 determing # serial ports
10175 and ax, #0xf1ff
10176 or ax, bx ; set number of serial port
10177 mov 0x410, ax
10179 ;; CMOS RTC
10180 SET_INT_VECTOR(0x1A, #0xF000, #int1a_handler)
10181 SET_INT_VECTOR(0x4A, #0xF000, #dummy_iret_handler)
10182 SET_INT_VECTOR(0x70, #0xF000, #int70_handler)
10183 ;; BIOS DATA AREA 0x4CE ???
10184 call timer_tick_post
10186 ;; PS/2 mouse setup
10187 SET_INT_VECTOR(0x74, #0xF000, #int74_handler)
10189 ;; IRQ13 (FPU exception) setup
10190 SET_INT_VECTOR(0x75, #0xF000, #int75_handler)
10192 ;; Video setup
10193 SET_INT_VECTOR(0x10, #0xF000, #int10_handler)
10195 ;; PIC
10196 mov al, #0x11 ; send initialisation commands
10197 out 0x20, al
10198 out 0xa0, al
10199 mov al, #0x08
10200 out 0x21, al
10201 mov al, #0x70
10202 out 0xa1, al
10203 mov al, #0x04
10204 out 0x21, al
10205 mov al, #0x02
10206 out 0xa1, al
10207 mov al, #0x01
10208 out 0x21, al
10209 out 0xa1, al
10210 mov al, #0xb8
10211 out 0x21, AL ;master pic: unmask IRQ 0, 1, 2, 6
10212 #if BX_USE_PS2_MOUSE
10213 mov al, #0x8f
10214 #else
10215 mov al, #0x9f
10216 #endif
10217 out 0xa1, AL ;slave pic: unmask IRQ 12, 13, 14
10219 #if BX_ROMBIOS32
10220 call rombios32_init
10221 #else
10222 call pcibios_init_iomem_bases
10223 call pcibios_init_irqs
10224 #endif
10226 call _init_boot_vectors
10228 call rom_scan
10230 call _print_bios_banner
10233 ;; Floppy setup
10235 call floppy_drive_post
10237 #if BX_USE_ATADRV
10240 ;; Hard Drive setup
10242 call hard_drive_post
10245 ;; ATA/ATAPI driver setup
10247 call _ata_init
10248 call _ata_detect
10250 #else // BX_USE_ATADRV
10253 ;; Hard Drive setup
10255 call hard_drive_post
10257 #endif // BX_USE_ATADRV
10259 #if BX_ELTORITO_BOOT
10261 ;; eltorito floppy/harddisk emulation from cd
10263 call _cdemu_init
10265 #endif // BX_ELTORITO_BOOT
10267 sti ;; enable interrupts
10268 int #0x19
10271 .org 0xe2c3 ; NMI Handler Entry Point
10272 nmi:
10273 ;; FIXME the NMI handler should not panic
10274 ;; but iret when called from int75 (fpu exception)
10275 call _nmi_handler_msg
10276 iret
10278 int75_handler:
10279 out 0xf0, al // clear irq13
10280 call eoi_both_pics // clear interrupt
10281 int 2 // legacy nmi call
10282 iret
10284 ;-------------------------------------------
10285 ;- INT 13h Fixed Disk Services Entry Point -
10286 ;-------------------------------------------
10287 .org 0xe3fe ; INT 13h Fixed Disk Services Entry Point
10288 int13_handler:
10289 //JMPL(int13_relocated)
10290 jmp int13_relocated
10292 .org 0xe401 ; Fixed Disk Parameter Table
10294 ;----------
10295 ;- INT19h -
10296 ;----------
10297 .org 0xe6f2 ; INT 19h Boot Load Service Entry Point
10298 int19_handler:
10300 jmp int19_relocated
10301 ;-------------------------------------------
10302 ;- System BIOS Configuration Data Table
10303 ;-------------------------------------------
10304 .org BIOS_CONFIG_TABLE
10305 db 0x08 ; Table size (bytes) -Lo
10306 db 0x00 ; Table size (bytes) -Hi
10307 db SYS_MODEL_ID
10308 db SYS_SUBMODEL_ID
10309 db BIOS_REVISION
10310 ; Feature byte 1
10311 ; b7: 1=DMA channel 3 used by hard disk
10312 ; b6: 1=2 interrupt controllers present
10313 ; b5: 1=RTC present
10314 ; b4: 1=BIOS calls int 15h/4Fh every key
10315 ; b3: 1=wait for extern event supported (Int 15h/41h)
10316 ; b2: 1=extended BIOS data area used
10317 ; b1: 0=AT or ESDI bus, 1=MicroChannel
10318 ; b0: 1=Dual bus (MicroChannel + ISA)
10319 db (0 << 7) | \
10320 (1 << 6) | \
10321 (1 << 5) | \
10322 (BX_CALL_INT15_4F << 4) | \
10323 (0 << 3) | \
10324 (BX_USE_EBDA << 2) | \
10325 (0 << 1) | \
10326 (0 << 0)
10327 ; Feature byte 2
10328 ; b7: 1=32-bit DMA supported
10329 ; b6: 1=int16h, function 9 supported
10330 ; b5: 1=int15h/C6h (get POS data) supported
10331 ; b4: 1=int15h/C7h (get mem map info) supported
10332 ; b3: 1=int15h/C8h (en/dis CPU) supported
10333 ; b2: 1=non-8042 kb controller
10334 ; b1: 1=data streaming supported
10335 ; b0: reserved
10336 db (0 << 7) | \
10337 (1 << 6) | \
10338 (0 << 5) | \
10339 (0 << 4) | \
10340 (0 << 3) | \
10341 (0 << 2) | \
10342 (0 << 1) | \
10343 (0 << 0)
10344 ; Feature byte 3
10345 ; b7: not used
10346 ; b6: reserved
10347 ; b5: reserved
10348 ; b4: POST supports ROM-to-RAM enable/disable
10349 ; b3: SCSI on system board
10350 ; b2: info panel installed
10351 ; b1: Initial Machine Load (IML) system - BIOS on disk
10352 ; b0: SCSI supported in IML
10353 db 0x00
10354 ; Feature byte 4
10355 ; b7: IBM private
10356 ; b6: EEPROM present
10357 ; b5-3: ABIOS presence (011 = not supported)
10358 ; b2: private
10359 ; b1: memory split above 16Mb supported
10360 ; b0: POSTEXT directly supported by POST
10361 db 0x00
10362 ; Feature byte 5 (IBM)
10363 ; b1: enhanced mouse
10364 ; b0: flash EPROM
10365 db 0x00
10369 .org 0xe729 ; Baud Rate Generator Table
10371 ;----------
10372 ;- INT14h -
10373 ;----------
10374 .org 0xe739 ; INT 14h Serial Communications Service Entry Point
10375 int14_handler:
10376 push ds
10377 pusha
10378 xor ax, ax
10379 mov ds, ax
10380 call _int14_function
10381 popa
10382 pop ds
10383 iret
10386 ;----------------------------------------
10387 ;- INT 16h Keyboard Service Entry Point -
10388 ;----------------------------------------
10389 .org 0xe82e
10390 int16_handler:
10393 push ds
10394 pushf
10395 pusha
10397 cmp ah, #0x00
10398 je int16_F00
10399 cmp ah, #0x10
10400 je int16_F00
10402 mov bx, #0xf000
10403 mov ds, bx
10404 call _int16_function
10405 popa
10406 popf
10407 pop ds
10408 jz int16_zero_set
10410 int16_zero_clear:
10411 push bp
10412 mov bp, sp
10413 //SEG SS
10414 and BYTE [bp + 0x06], #0xbf
10415 pop bp
10416 iret
10418 int16_zero_set:
10419 push bp
10420 mov bp, sp
10421 //SEG SS
10422 or BYTE [bp + 0x06], #0x40
10423 pop bp
10424 iret
10426 int16_F00:
10427 mov bx, #0x0040
10428 mov ds, bx
10430 int16_wait_for_key:
10432 mov bx, 0x001a
10433 cmp bx, 0x001c
10434 jne int16_key_found
10437 #if 0
10438 /* no key yet, call int 15h, function AX=9002 */
10439 0x50, /* push AX */
10440 0xb8, 0x02, 0x90, /* mov AX, #0x9002 */
10441 0xcd, 0x15, /* int 15h */
10442 0x58, /* pop AX */
10443 0xeb, 0xea, /* jmp WAIT_FOR_KEY */
10444 #endif
10445 jmp int16_wait_for_key
10447 int16_key_found:
10448 mov bx, #0xf000
10449 mov ds, bx
10450 call _int16_function
10451 popa
10452 popf
10453 pop ds
10454 #if 0
10455 /* notify int16 complete w/ int 15h, function AX=9102 */
10456 0x50, /* push AX */
10457 0xb8, 0x02, 0x91, /* mov AX, #0x9102 */
10458 0xcd, 0x15, /* int 15h */
10459 0x58, /* pop AX */
10460 #endif
10461 iret
10465 ;-------------------------------------------------
10466 ;- INT09h : Keyboard Hardware Service Entry Point -
10467 ;-------------------------------------------------
10468 .org 0xe987
10469 int09_handler:
10471 push ax
10473 mov al, #0xAD ;;disable keyboard
10474 out #0x64, al
10476 mov al, #0x0B
10477 out #0x20, al
10478 in al, #0x20
10479 and al, #0x02
10480 jz int09_finish
10482 in al, #0x60 ;;read key from keyboard controller
10484 push ds
10485 pusha
10486 #ifdef BX_CALL_INT15_4F
10487 mov ah, #0x4f ;; allow for keyboard intercept
10489 int #0x15
10490 jnc int09_done
10491 #endif
10493 ;; check for extended key
10494 cmp al, #0xe0
10495 jne int09_check_pause
10496 xor ax, ax
10497 mov ds, ax
10498 mov al, BYTE [0x496] ;; mf2_state |= 0x02
10499 or al, #0x02
10500 mov BYTE [0x496], al
10501 jmp int09_done
10503 int09_check_pause: ;; check for pause key
10504 cmp al, #0xe1
10505 jne int09_process_key
10506 xor ax, ax
10507 mov ds, ax
10508 mov al, BYTE [0x496] ;; mf2_state |= 0x01
10509 or al, #0x01
10510 mov BYTE [0x496], al
10511 jmp int09_done
10513 int09_process_key:
10514 mov bx, #0xf000
10515 mov ds, bx
10516 call _int09_function
10518 int09_done:
10519 popa
10520 pop ds
10522 call eoi_master_pic
10524 int09_finish:
10525 mov al, #0xAE ;;enable keyboard
10526 out #0x64, al
10527 pop ax
10528 iret
10531 ;----------------------------------------
10532 ;- INT 13h Diskette Service Entry Point -
10533 ;----------------------------------------
10534 .org 0xec59
10535 int13_diskette:
10536 jmp int13_noeltorito
10538 ;---------------------------------------------
10539 ;- INT 0Eh Diskette Hardware ISR Entry Point -
10540 ;---------------------------------------------
10541 .org 0xef57 ; INT 0Eh Diskette Hardware ISR Entry Point
10542 int0e_handler:
10543 push ax
10544 push dx
10545 mov dx, #0x03f4
10546 in al, dx
10547 and al, #0xc0
10548 cmp al, #0xc0
10549 je int0e_normal
10550 mov dx, #0x03f5
10551 mov al, #0x08 ; sense interrupt status
10552 out dx, al
10553 int0e_loop1:
10554 mov dx, #0x03f4
10555 in al, dx
10556 and al, #0xc0
10557 cmp al, #0xc0
10558 jne int0e_loop1
10559 int0e_loop2:
10560 mov dx, #0x03f5
10561 in al, dx
10562 mov dx, #0x03f4
10563 in al, dx
10564 and al, #0xc0
10565 cmp al, #0xc0
10566 je int0e_loop2
10567 int0e_normal:
10568 push ds
10569 xor ax, ax ;; segment 0000
10570 mov ds, ax
10571 call eoi_master_pic
10572 mov al, 0x043e
10573 or al, #0x80 ;; diskette interrupt has occurred
10574 mov 0x043e, al
10575 pop ds
10576 pop dx
10577 pop ax
10578 iret
10581 .org 0xefc7 ; Diskette Controller Parameter Table
10582 diskette_param_table:
10583 ;; Since no provisions are made for multiple drive types, most
10584 ;; values in this table are ignored. I set parameters for 1.44M
10585 ;; floppy here
10586 db 0xAF
10587 db 0x02 ;; head load time 0000001, DMA used
10588 db 0x25
10589 db 0x02
10590 db 18
10591 db 0x1B
10592 db 0xFF
10593 db 0x6C
10594 db 0xF6
10595 db 0x0F
10596 db 0x08
10599 ;----------------------------------------
10600 ;- INT17h : Printer Service Entry Point -
10601 ;----------------------------------------
10602 .org 0xefd2
10603 int17_handler:
10604 push ds
10605 pusha
10606 xor ax, ax
10607 mov ds, ax
10608 call _int17_function
10609 popa
10610 pop ds
10611 iret
10613 diskette_param_table2:
10614 ;; New diskette parameter table adding 3 parameters from IBM
10615 ;; Since no provisions are made for multiple drive types, most
10616 ;; values in this table are ignored. I set parameters for 1.44M
10617 ;; floppy here
10618 db 0xAF
10619 db 0x02 ;; head load time 0000001, DMA used
10620 db 0x25
10621 db 0x02
10622 db 18
10623 db 0x1B
10624 db 0xFF
10625 db 0x6C
10626 db 0xF6
10627 db 0x0F
10628 db 0x08
10629 db 79 ;; maximum track
10630 db 0 ;; data transfer rate
10631 db 4 ;; drive type in cmos
10633 .org 0xf045 ; INT 10 Functions 0-Fh Entry Point
10634 HALT(__LINE__)
10635 iret
10637 ;----------
10638 ;- INT10h -
10639 ;----------
10640 .org 0xf065 ; INT 10h Video Support Service Entry Point
10641 int10_handler:
10642 ;; dont do anything, since the VGA BIOS handles int10h requests
10643 iret
10645 .org 0xf0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
10647 ;----------
10648 ;- INT12h -
10649 ;----------
10650 .org 0xf841 ; INT 12h Memory Size Service Entry Point
10651 ; ??? different for Pentium (machine check)?
10652 int12_handler:
10653 push ds
10654 mov ax, #0x0040
10655 mov ds, ax
10656 mov ax, 0x0013
10657 pop ds
10658 iret
10660 ;----------
10661 ;- INT11h -
10662 ;----------
10663 .org 0xf84d ; INT 11h Equipment List Service Entry Point
10664 int11_handler:
10665 push ds
10666 mov ax, #0x0040
10667 mov ds, ax
10668 mov ax, 0x0010
10669 pop ds
10670 iret
10672 ;----------
10673 ;- INT15h -
10674 ;----------
10675 .org 0xf859 ; INT 15h System Services Entry Point
10676 int15_handler:
10677 pushf
10678 #if BX_APM
10679 cmp ah, #0x53
10680 je apm_call
10681 #endif
10682 push ds
10683 push es
10684 cmp ah, #0x86
10685 je int15_handler32
10686 cmp ah, #0xE8
10687 je int15_handler32
10688 pusha
10689 #if BX_USE_PS2_MOUSE
10690 cmp ah, #0xC2
10691 je int15_handler_mouse
10692 #endif
10693 call _int15_function
10694 int15_handler_mouse_ret:
10695 popa
10696 int15_handler32_ret:
10697 pop es
10698 pop ds
10699 popf
10700 jmp iret_modify_cf
10701 #if BX_APM
10702 apm_call:
10703 jmp _apmreal_entry
10704 #endif
10706 #if BX_USE_PS2_MOUSE
10707 int15_handler_mouse:
10708 call _int15_function_mouse
10709 jmp int15_handler_mouse_ret
10710 #endif
10712 int15_handler32:
10713 pushad
10714 call _int15_function32
10715 popad
10716 jmp int15_handler32_ret
10718 ;; Protected mode IDT descriptor
10720 ;; I just make the limit 0, so the machine will shutdown
10721 ;; if an exception occurs during protected mode memory
10722 ;; transfers.
10724 ;; Set base to f0000 to correspond to beginning of BIOS,
10725 ;; in case I actually define an IDT later
10726 ;; Set limit to 0
10728 pmode_IDT_info:
10729 dw 0x0000 ;; limit 15:00
10730 dw 0x0000 ;; base 15:00
10731 db 0x0f ;; base 23:16
10733 ;; Real mode IDT descriptor
10735 ;; Set to typical real-mode values.
10736 ;; base = 000000
10737 ;; limit = 03ff
10739 rmode_IDT_info:
10740 dw 0x03ff ;; limit 15:00
10741 dw 0x0000 ;; base 15:00
10742 db 0x00 ;; base 23:16
10745 ;----------
10746 ;- INT1Ah -
10747 ;----------
10748 .org 0xfe6e ; INT 1Ah Time-of-day Service Entry Point
10749 int1a_handler:
10750 #if BX_PCIBIOS
10751 cmp ah, #0xb1
10752 jne int1a_normal
10753 call pcibios_real
10754 jc pcibios_error
10755 retf 2
10756 pcibios_error:
10757 mov bl, ah
10758 mov ah, #0xb1
10759 push ds
10760 pusha
10761 mov ax, ss ; set readable descriptor to ds, for calling pcibios
10762 mov ds, ax ; on 16bit protected mode.
10763 jmp int1a_callfunction
10764 int1a_normal:
10765 #endif
10766 push ds
10767 pusha
10768 xor ax, ax
10769 mov ds, ax
10770 int1a_callfunction:
10771 call _int1a_function
10772 popa
10773 pop ds
10774 iret
10777 ;; int70h: IRQ8 - CMOS RTC
10779 int70_handler:
10780 push ds
10781 pushad
10782 xor ax, ax
10783 mov ds, ax
10784 call _int70_function
10785 popad
10786 pop ds
10787 iret
10789 ;---------
10790 ;- INT08 -
10791 ;---------
10792 .org 0xfea5 ; INT 08h System Timer ISR Entry Point
10793 int08_handler:
10795 push eax
10796 push ds
10797 xor ax, ax
10798 mov ds, ax
10800 ;; time to turn off drive(s)?
10801 mov al,0x0440
10802 or al,al
10803 jz int08_floppy_off
10804 dec al
10805 mov 0x0440,al
10806 jnz int08_floppy_off
10807 ;; turn motor(s) off
10808 push dx
10809 mov dx,#0x03f2
10810 in al,dx
10811 and al,#0xcf
10812 out dx,al
10813 pop dx
10814 int08_floppy_off:
10816 mov eax, 0x046c ;; get ticks dword
10817 inc eax
10819 ;; compare eax to one days worth of timer ticks at 18.2 hz
10820 cmp eax, #0x001800B0
10821 jb int08_store_ticks
10822 ;; there has been a midnight rollover at this point
10823 xor eax, eax ;; zero out counter
10824 inc BYTE 0x0470 ;; increment rollover flag
10826 int08_store_ticks:
10827 mov 0x046c, eax ;; store new ticks dword
10828 ;; chain to user timer tick INT #0x1c
10829 //pushf
10830 //;; call_ep [ds:loc]
10831 //CALL_EP( 0x1c << 2 )
10832 int #0x1c
10834 call eoi_master_pic
10835 pop ds
10836 pop eax
10837 iret
10839 .org 0xfef3 ; Initial Interrupt Vector Offsets Loaded by POST
10842 .org 0xff00
10843 .ascii BIOS_COPYRIGHT_STRING
10845 ;------------------------------------------------
10846 ;- IRET Instruction for Dummy Interrupt Handler -
10847 ;------------------------------------------------
10848 .org 0xff53 ; IRET Instruction for Dummy Interrupt Handler
10849 dummy_iret_handler:
10850 iret
10852 .org 0xff54 ; INT 05h Print Screen Service Entry Point
10853 HALT(__LINE__)
10854 iret
10856 .org 0xfff0 ; Power-up Entry Point
10857 jmp 0xf000:post
10859 .org 0xfff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
10860 .ascii BIOS_BUILD_DATE
10862 .org 0xfffe ; System Model ID
10863 db SYS_MODEL_ID
10864 db 0x00 ; filler
10866 .org 0xfa6e ;; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
10867 ASM_END
10869 * This font comes from the fntcol16.zip package (c) by Joseph Gil
10870 * found at ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip
10871 * This font is public domain
10873 static Bit8u vgafont8[128*8]=
10875 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
10876 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e,
10877 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e,
10878 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00,
10879 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00,
10880 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c,
10881 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c,
10882 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00,
10883 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff,
10884 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
10885 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff,
10886 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78,
10887 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18,
10888 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0,
10889 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0,
10890 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99,
10891 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00,
10892 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00,
10893 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18,
10894 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
10895 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00,
10896 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78,
10897 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00,
10898 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff,
10899 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00,
10900 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
10901 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00,
10902 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
10903 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00,
10904 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00,
10905 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00,
10906 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00,
10907 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
10908 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00,
10909 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
10910 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00,
10911 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00,
10912 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00,
10913 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00,
10914 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
10915 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00,
10916 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
10917 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
10918 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00,
10919 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60,
10920 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
10921 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00,
10922 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00,
10923 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00,
10924 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00,
10925 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00,
10926 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00,
10927 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00,
10928 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00,
10929 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00,
10930 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00,
10931 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00,
10932 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00,
10933 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00,
10934 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60,
10935 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00,
10936 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00,
10937 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00,
10938 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00,
10939 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00,
10940 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00,
10941 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00,
10942 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00,
10943 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00,
10944 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00,
10945 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00,
10946 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00,
10947 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00,
10948 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
10949 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00,
10950 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00,
10951 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00,
10952 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00,
10953 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00,
10954 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00,
10955 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
10956 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00,
10957 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00,
10958 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00,
10959 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
10960 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00,
10961 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
10962 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00,
10963 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00,
10964 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00,
10965 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00,
10966 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
10967 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00,
10968 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
10969 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
10970 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
10971 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
10972 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00,
10973 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00,
10974 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00,
10975 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00,
10976 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
10977 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00,
10978 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
10979 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00,
10980 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
10981 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78,
10982 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00,
10983 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
10984 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00,
10985 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
10986 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
10987 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0,
10988 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e,
10989 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00,
10990 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00,
10991 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00,
10992 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
10993 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
10994 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00,
10995 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00,
10996 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
10997 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00,
10998 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00,
10999 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
11000 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00,
11001 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
11002 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00,
11005 ASM_START
11006 .org 0xcc00
11007 // bcc-generated data will be placed here
11008 ASM_END