BIOS whitespace cleanup
[gplbios.git] / rombios.c
blob7d0e8e870ea249beb3debdb2329d9dbc307963e6
1 /////////////////////////////////////////////////////////////////////////
2 // $Id$
3 /////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (C) 2002 MandrakeSoft S.A.
6 //
7 // MandrakeSoft S.A.
8 // 43, rue d'Aboukir
9 // 75002 Paris - France
10 // http://www.linux-mandrake.com/
11 // http://www.mandrakesoft.com/
13 // This library is free software; you can redistribute it and/or
14 // modify it under the terms of the GNU Lesser General Public
15 // License as published by the Free Software Foundation; either
16 // version 2 of the License, or (at your option) any later version.
18 // This library is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 // Lesser General Public License for more details.
23 // You should have received a copy of the GNU Lesser General Public
24 // License along with this library; if not, write to the Free Software
25 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27 // ROM BIOS for use with Bochs/Plex86/QEMU emulation environment
30 // ROM BIOS compatability entry points:
31 // ===================================
32 // $e05b ; POST Entry Point
33 // $e2c3 ; NMI Handler Entry Point
34 // $e3fe ; INT 13h Fixed Disk Services Entry Point
35 // $e401 ; Fixed Disk Parameter Table
36 // $e6f2 ; INT 19h Boot Load Service Entry Point
37 // $e6f5 ; Configuration Data Table
38 // $e729 ; Baud Rate Generator Table
39 // $e739 ; INT 14h Serial Communications Service Entry Point
40 // $e82e ; INT 16h Keyboard Service Entry Point
41 // $e987 ; INT 09h Keyboard Service Entry Point
42 // $ec59 ; INT 13h Diskette Service Entry Point
43 // $ef57 ; INT 0Eh Diskette Hardware ISR Entry Point
44 // $efc7 ; Diskette Controller Parameter Table
45 // $efd2 ; INT 17h Printer Service Entry Point
46 // $f045 ; INT 10 Functions 0-Fh Entry Point
47 // $f065 ; INT 10h Video Support Service Entry Point
48 // $f0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
49 // $f841 ; INT 12h Memory Size Service Entry Point
50 // $f84d ; INT 11h Equipment List Service Entry Point
51 // $f859 ; INT 15h System Services Entry Point
52 // $fa6e ; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
53 // $fe6e ; INT 1Ah Time-of-day Service Entry Point
54 // $fea5 ; INT 08h System Timer ISR Entry Point
55 // $fef3 ; Initial Interrupt Vector Offsets Loaded by POST
56 // $ff53 ; IRET Instruction for Dummy Interrupt Handler
57 // $ff54 ; INT 05h Print Screen Service Entry Point
58 // $fff0 ; Power-up Entry Point
59 // $fff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
60 // $fffe ; System Model ID
62 // NOTES for ATA/ATAPI driver (cbbochs@free.fr)
63 // Features
64 // - supports up to 4 ATA interfaces
65 // - device/geometry detection
66 // - 16bits/32bits device access
67 // - pchs/lba access
68 // - datain/dataout/packet command support
70 // NOTES for El-Torito Boot (cbbochs@free.fr)
71 // - CD-ROM booting is only available if ATA/ATAPI Driver is available
72 // - Current code is only able to boot mono-session cds
73 // - Current code can not boot and emulate a hard-disk
74 // the bios will panic otherwise
75 // - Current code also use memory in EBDA segement.
76 // - I used cmos byte 0x3D to store extended information on boot-device
77 // - Code has to be modified modified to handle multiple cdrom drives
78 // - Here are the cdrom boot failure codes:
79 // 1 : no atapi device found
80 // 2 : no atapi cdrom found
81 // 3 : can not read cd - BRVD
82 // 4 : cd is not eltorito (BRVD)
83 // 5 : cd is not eltorito (ISO TAG)
84 // 6 : cd is not eltorito (ELTORITO TAG)
85 // 7 : can not read cd - boot catalog
86 // 8 : boot catalog : bad header
87 // 9 : boot catalog : bad platform
88 // 10 : boot catalog : bad signature
89 // 11 : boot catalog : bootable flag not set
90 // 12 : can not read cd - boot image
92 // ATA driver
93 // - EBDA segment.
94 // I used memory starting at 0x121 in the segment
95 // - the translation policy is defined in cmos regs 0x39 & 0x3a
97 // TODO :
99 // int74
100 // - needs to be reworked. Uses direct [bp] offsets. (?)
102 // int13:
103 // - f04 (verify sectors) isn't complete (?)
104 // - f02/03/04 should set current cyl,etc in BDA (?)
105 // - rewrite int13_relocated & clean up int13 entry code
107 // NOTES:
108 // - NMI access (bit7 of addr written to 70h)
110 // ATA driver
111 // - should handle the "don't detect" bit (cmos regs 0x3b & 0x3c)
112 // - could send the multiple-sector read/write commands
114 // El-Torito
115 // - Emulate a Hard-disk (currently only diskette can be emulated) see "FIXME ElTorito Harddisk"
116 // - Implement remaining int13_cdemu functions (as defined by El-Torito specs)
117 // - cdrom drive is hardcoded to ide 0 device 1 in several places. see "FIXME ElTorito Hardcoded"
118 // - int13 Fix DL when emulating a cd. In that case DL is decremented before calling real int13.
119 // This is ok. But DL should be reincremented afterwards.
120 // - Fix all "FIXME ElTorito Various"
121 // - should be able to boot any cdrom instead of the first one
123 // BCC Bug: find a generic way to handle the bug of #asm after an "if" (fixed in 0.16.7)
125 #include "rombios.h"
127 #define DEBUG_ATA 0
128 #define DEBUG_INT13_HD 0
129 #define DEBUG_INT13_CD 0
130 #define DEBUG_INT13_ET 0
131 #define DEBUG_INT13_FL 0
132 #define DEBUG_INT15 0
133 #define DEBUG_INT16 0
134 #define DEBUG_INT1A 0
135 #define DEBUG_INT74 0
136 #define DEBUG_APM 0
138 #define BX_CPU 3
139 #define BX_USE_PS2_MOUSE 1
140 #define BX_CALL_INT15_4F 1
141 #define BX_USE_EBDA 1
142 #define BX_SUPPORT_FLOPPY 1
143 #define BX_FLOPPY_ON_CNT 37 /* 2 seconds */
144 #define BX_PCIBIOS 1
145 #define BX_APM 1
147 #define BX_USE_ATADRV 1
148 #define BX_ELTORITO_BOOT 1
150 #define BX_MAX_ATA_INTERFACES 4
151 #define BX_MAX_ATA_DEVICES (BX_MAX_ATA_INTERFACES*2)
153 #define BX_VIRTUAL_PORTS 1 /* normal output to Bochs ports */
154 #define BX_DEBUG_SERIAL 0 /* output to COM1 */
156 /* model byte 0xFC = AT */
157 #define SYS_MODEL_ID 0xFC
158 #define SYS_SUBMODEL_ID 0x00
159 #define BIOS_REVISION 1
160 #define BIOS_CONFIG_TABLE 0xe6f5
162 #ifndef BIOS_BUILD_DATE
163 # define BIOS_BUILD_DATE "06/23/99"
164 #endif
166 // 1K of base memory used for Extended Bios Data Area (EBDA)
167 // EBDA is used for PS/2 mouse support, and IDE BIOS, etc.
168 #define EBDA_SEG 0x9FC0
169 #define EBDA_SIZE 1 // In KiB
170 #define BASE_MEM_IN_K (640 - EBDA_SIZE)
172 /* 256 bytes at 0x9ff00 -- 0x9ffff is used for the IPL boot table. */
173 #define IPL_SEG 0x9ff0
174 #define IPL_TABLE_OFFSET 0x0000
175 #define IPL_TABLE_ENTRIES 8
176 #define IPL_COUNT_OFFSET 0x0080 /* u16: number of valid table entries */
177 #define IPL_SEQUENCE_OFFSET 0x0082 /* u16: next boot device */
178 #define IPL_SIZE 0xff
179 #define IPL_TYPE_FLOPPY 0x01
180 #define IPL_TYPE_HARDDISK 0x02
181 #define IPL_TYPE_CDROM 0x03
182 #define IPL_TYPE_BEV 0x80
184 // Sanity Checks
185 #if BX_USE_ATADRV && BX_CPU<3
186 # error The ATA/ATAPI Driver can only to be used with a 386+ cpu
187 #endif
188 #if BX_USE_ATADRV && !BX_USE_EBDA
189 # error ATA/ATAPI Driver can only be used if EBDA is available
190 #endif
191 #if BX_ELTORITO_BOOT && !BX_USE_ATADRV
192 # error El-Torito Boot can only be use if ATA/ATAPI Driver is available
193 #endif
194 #if BX_PCIBIOS && BX_CPU<3
195 # error PCI BIOS can only be used with 386+ cpu
196 #endif
197 #if BX_APM && BX_CPU<3
198 # error APM BIOS can only be used with 386+ cpu
199 #endif
201 // define this if you want to make PCIBIOS working on a specific bridge only
202 // undef enables PCIBIOS when at least one PCI device is found
203 // i440FX is emulated by Bochs and QEMU
204 #define PCI_FIXED_HOST_BRIDGE 0x12378086 ;; i440FX PCI bridge
206 // #20 is dec 20
207 // #$20 is hex 20 = 32
208 // #0x20 is hex 20 = 32
209 // LDA #$20
210 // JSR $E820
211 // LDD .i,S
212 // JSR $C682
213 // mov al, #$20
215 // all hex literals should be prefixed with '0x'
216 // grep "#[0-9a-fA-F][0-9a-fA-F]" rombios.c
217 // no mov SEG-REG, #value, must mov register into seg-reg
218 // grep -i "mov[ ]*.s" rombios.c
220 // This is for compiling with gcc2 and gcc3
221 #define ASM_START #asm
222 #define ASM_END #endasm
224 ASM_START
225 .rom
227 .org 0x0000
229 #if BX_CPU >= 3
230 use16 386
231 #else
232 use16 286
233 #endif
235 MACRO HALT
236 ;; the HALT macro is called with the line number of the HALT call.
237 ;; The line number is then sent to the PANIC_PORT, causing Bochs/Plex
238 ;; to print a BX_PANIC message. This will normally halt the simulation
239 ;; with a message such as "BIOS panic at rombios.c, line 4091".
240 ;; However, users can choose to make panics non-fatal and continue.
241 #if BX_VIRTUAL_PORTS
242 mov dx,#PANIC_PORT
243 mov ax,#?1
244 out dx,ax
245 #else
246 mov dx,#0x80
247 mov ax,#?1
248 out dx,al
249 #endif
250 MEND
252 MACRO JMP_AP
253 db 0xea
254 dw ?2
255 dw ?1
256 MEND
258 MACRO SET_INT_VECTOR
259 mov ax, ?3
260 mov ?1*4, ax
261 mov ax, ?2
262 mov ?1*4+2, ax
263 MEND
265 ASM_END
267 typedef unsigned char Bit8u;
268 typedef unsigned short Bit16u;
269 typedef unsigned short bx_bool;
270 typedef unsigned long Bit32u;
273 void memsetb(seg,offset,value,count);
274 void memcpyb(dseg,doffset,sseg,soffset,count);
275 void memcpyd(dseg,doffset,sseg,soffset,count);
277 // memset of count bytes
278 void
279 memsetb(seg,offset,value,count)
280 Bit16u seg;
281 Bit16u offset;
282 Bit16u value;
283 Bit16u count;
285 ASM_START
286 push bp
287 mov bp, sp
289 push ax
290 push cx
291 push es
292 push di
294 mov cx, 10[bp] ; count
295 test cx, cx
296 je memsetb_end
297 mov ax, 4[bp] ; segment
298 mov es, ax
299 mov ax, 6[bp] ; offset
300 mov di, ax
301 mov al, 8[bp] ; value
304 stosb
306 memsetb_end:
307 pop di
308 pop es
309 pop cx
310 pop ax
312 pop bp
313 ASM_END
316 // memcpy of count bytes
317 void
318 memcpyb(dseg,doffset,sseg,soffset,count)
319 Bit16u dseg;
320 Bit16u doffset;
321 Bit16u sseg;
322 Bit16u soffset;
323 Bit16u count;
325 ASM_START
326 push bp
327 mov bp, sp
329 push ax
330 push cx
331 push es
332 push di
333 push ds
334 push si
336 mov cx, 12[bp] ; count
337 test cx, cx
338 je memcpyb_end
339 mov ax, 4[bp] ; dsegment
340 mov es, ax
341 mov ax, 6[bp] ; doffset
342 mov di, ax
343 mov ax, 8[bp] ; ssegment
344 mov ds, ax
345 mov ax, 10[bp] ; soffset
346 mov si, ax
349 movsb
351 memcpyb_end:
352 pop si
353 pop ds
354 pop di
355 pop es
356 pop cx
357 pop ax
359 pop bp
360 ASM_END
363 // memcpy of count dword
364 void
365 memcpyd(dseg,doffset,sseg,soffset,count)
366 Bit16u dseg;
367 Bit16u doffset;
368 Bit16u sseg;
369 Bit16u soffset;
370 Bit16u count;
372 ASM_START
373 push bp
374 mov bp, sp
376 push ax
377 push cx
378 push es
379 push di
380 push ds
381 push si
383 mov cx, 12[bp] ; count
384 test cx, cx
385 je memcpyd_end
386 mov ax, 4[bp] ; dsegment
387 mov es, ax
388 mov ax, 6[bp] ; doffset
389 mov di, ax
390 mov ax, 8[bp] ; ssegment
391 mov ds, ax
392 mov ax, 10[bp] ; soffset
393 mov si, ax
396 movsd
398 memcpyd_end:
399 pop si
400 pop ds
401 pop di
402 pop es
403 pop cx
404 pop ax
406 pop bp
407 ASM_END
410 // read_dword and write_dword functions
411 static Bit32u read_dword();
412 static void write_dword();
414 Bit32u
415 read_dword(seg, offset)
416 Bit16u seg;
417 Bit16u offset;
419 ASM_START
420 push bp
421 mov bp, sp
423 push bx
424 push ds
425 mov ax, 4[bp] ; segment
426 mov ds, ax
427 mov bx, 6[bp] ; offset
428 mov ax, [bx]
429 add bx, #2
430 mov dx, [bx]
431 ;; ax = return value (word)
432 ;; dx = return value (word)
433 pop ds
434 pop bx
436 pop bp
437 ASM_END
440 void
441 write_dword(seg, offset, data)
442 Bit16u seg;
443 Bit16u offset;
444 Bit32u data;
446 ASM_START
447 push bp
448 mov bp, sp
450 push ax
451 push bx
452 push ds
453 mov ax, 4[bp] ; segment
454 mov ds, ax
455 mov bx, 6[bp] ; offset
456 mov ax, 8[bp] ; data word
457 mov [bx], ax ; write data word
458 add bx, #2
459 mov ax, 10[bp] ; data word
460 mov [bx], ax ; write data word
461 pop ds
462 pop bx
463 pop ax
465 pop bp
466 ASM_END
469 // Bit32u (unsigned long) and long helper functions
470 ASM_START
472 ;; and function
473 landl:
474 landul:
475 SEG SS
476 and ax,[di]
477 SEG SS
478 and bx,2[di]
481 ;; add function
482 laddl:
483 laddul:
484 SEG SS
485 add ax,[di]
486 SEG SS
487 adc bx,2[di]
490 ;; cmp function
491 lcmpl:
492 lcmpul:
493 and eax, #0x0000FFFF
494 shl ebx, #16
495 or eax, ebx
496 shr ebx, #16
497 SEG SS
498 cmp eax, dword ptr [di]
501 ;; sub function
502 lsubl:
503 lsubul:
504 SEG SS
505 sub ax,[di]
506 SEG SS
507 sbb bx,2[di]
510 ;; mul function
511 lmull:
512 lmulul:
513 and eax, #0x0000FFFF
514 shl ebx, #16
515 or eax, ebx
516 SEG SS
517 mul eax, dword ptr [di]
518 mov ebx, eax
519 shr ebx, #16
522 ;; dec function
523 ldecl:
524 ldecul:
525 SEG SS
526 dec dword ptr [bx]
529 ;; or function
530 lorl:
531 lorul:
532 SEG SS
533 or ax,[di]
534 SEG SS
535 or bx,2[di]
538 ;; inc function
539 lincl:
540 lincul:
541 SEG SS
542 inc dword ptr [bx]
545 ;; tst function
546 ltstl:
547 ltstul:
548 and eax, #0x0000FFFF
549 shl ebx, #16
550 or eax, ebx
551 shr ebx, #16
552 test eax, eax
555 ;; sr function
556 lsrul:
557 mov cx,di
558 jcxz lsr_exit
559 and eax, #0x0000FFFF
560 shl ebx, #16
561 or eax, ebx
562 lsr_loop:
563 shr eax, #1
564 loop lsr_loop
565 mov ebx, eax
566 shr ebx, #16
567 lsr_exit:
570 ;; sl function
571 lsll:
572 lslul:
573 mov cx,di
574 jcxz lsl_exit
575 and eax, #0x0000FFFF
576 shl ebx, #16
577 or eax, ebx
578 lsl_loop:
579 shl eax, #1
580 loop lsl_loop
581 mov ebx, eax
582 shr ebx, #16
583 lsl_exit:
586 idiv_:
588 idiv bx
591 idiv_u:
592 xor dx,dx
593 div bx
596 ldivul:
597 and eax, #0x0000FFFF
598 shl ebx, #16
599 or eax, ebx
600 xor edx, edx
601 SEG SS
602 mov bx, 2[di]
603 shl ebx, #16
604 SEG SS
605 mov bx, [di]
606 div ebx
607 mov ebx, eax
608 shr ebx, #16
611 ASM_END
613 // for access to RAM area which is used by interrupt vectors
614 // and BIOS Data Area
616 typedef struct {
617 unsigned char filler1[0x400];
618 unsigned char filler2[0x6c];
619 Bit16u ticks_low;
620 Bit16u ticks_high;
621 Bit8u midnight_flag;
622 } bios_data_t;
624 #define BiosData ((bios_data_t *) 0)
626 #if BX_USE_ATADRV
627 typedef struct {
628 Bit16u heads; // # heads
629 Bit16u cylinders; // # cylinders
630 Bit16u spt; // # sectors / track
631 } chs_t;
633 // DPTE definition
634 typedef struct {
635 Bit16u iobase1;
636 Bit16u iobase2;
637 Bit8u prefix;
638 Bit8u unused;
639 Bit8u irq;
640 Bit8u blkcount;
641 Bit8u dma;
642 Bit8u pio;
643 Bit16u options;
644 Bit16u reserved;
645 Bit8u revision;
646 Bit8u checksum;
647 } dpte_t;
649 typedef struct {
650 Bit8u iface; // ISA or PCI
651 Bit16u iobase1; // IO Base 1
652 Bit16u iobase2; // IO Base 2
653 Bit8u irq; // IRQ
654 } ata_channel_t;
656 typedef struct {
657 Bit8u type; // Detected type of ata (ata/atapi/none/unknown)
658 Bit8u device; // Detected type of attached devices (hd/cd/none)
659 Bit8u removable; // Removable device flag
660 Bit8u lock; // Locks for removable devices
661 Bit8u mode; // transfer 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_offset;
762 Bit16u dpte_segment;
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;
840 typedef struct {
841 Bit16u type;
842 Bit16u flags;
843 Bit32u vector;
844 Bit32u description;
845 Bit32u reserved;
846 } ipl_entry_t;
850 static Bit8u inb();
851 static Bit8u inb_cmos();
852 static void outb();
853 static void outb_cmos();
854 static Bit16u inw();
855 static void outw();
856 static void init_rtc();
857 static bx_bool rtc_updating();
859 static Bit8u read_byte();
860 static Bit16u read_word();
861 static void write_byte();
862 static void write_word();
863 static void bios_printf();
865 static Bit8u inhibit_mouse_int_and_events();
866 static void enable_mouse_int_and_events();
867 static Bit8u send_to_mouse_ctrl();
868 static Bit8u get_mouse_data();
869 static void set_kbd_command_byte();
871 static void int09_function();
872 static void int13_harddisk();
873 static void int13_cdrom();
874 static void int13_cdemu();
875 static void int13_eltorito();
876 static void int13_diskette_function();
877 static void int14_function();
878 static void int15_function();
879 static void int16_function();
880 static void int17_function();
881 static void int19_function();
882 static void int1a_function();
883 static void int70_function();
884 static void int74_function();
885 static Bit16u get_CS();
886 static Bit16u get_SS();
887 static unsigned int enqueue_key();
888 static unsigned int dequeue_key();
889 static void get_hd_geometry();
890 static void set_diskette_ret_status();
891 static void set_diskette_current_cyl();
892 static void determine_floppy_media();
893 static bx_bool floppy_drive_exists();
894 static bx_bool floppy_drive_recal();
895 static bx_bool floppy_media_known();
896 static bx_bool floppy_media_sense();
897 static bx_bool set_enable_a20();
898 static void debugger_on();
899 static void debugger_off();
900 static void keyboard_init();
901 static void keyboard_panic();
902 static void shutdown_status_panic();
903 static void nmi_handler_msg();
905 static void print_bios_banner();
906 static void print_boot_device();
907 static void print_boot_failure();
908 static void print_cdromboot_failure();
910 # if BX_USE_ATADRV
912 // ATA / ATAPI driver
913 void ata_init();
914 void ata_detect();
915 void ata_reset();
917 Bit16u ata_cmd_non_data();
918 Bit16u ata_cmd_data_in();
919 Bit16u ata_cmd_data_out();
920 Bit16u ata_cmd_packet();
922 Bit16u atapi_get_sense();
923 Bit16u atapi_is_ready();
924 Bit16u atapi_is_cdrom();
926 #endif // BX_USE_ATADRV
928 #if BX_ELTORITO_BOOT
930 void cdemu_init();
931 Bit8u cdemu_isactive();
932 Bit8u cdemu_emulated_drive();
934 Bit16u cdrom_boot();
936 #endif // BX_ELTORITO_BOOT
938 static char bios_cvs_version_string[] = "$Revision$ $Date$";
940 #define BIOS_COPYRIGHT_STRING "(c) 2002 MandrakeSoft S.A. Written by Kevin Lawton & the Bochs team."
942 #if DEBUG_ATA
943 # define BX_DEBUG_ATA(a...) BX_DEBUG(a)
944 #else
945 # define BX_DEBUG_ATA(a...)
946 #endif
947 #if DEBUG_INT13_HD
948 # define BX_DEBUG_INT13_HD(a...) BX_DEBUG(a)
949 #else
950 # define BX_DEBUG_INT13_HD(a...)
951 #endif
952 #if DEBUG_INT13_CD
953 # define BX_DEBUG_INT13_CD(a...) BX_DEBUG(a)
954 #else
955 # define BX_DEBUG_INT13_CD(a...)
956 #endif
957 #if DEBUG_INT13_ET
958 # define BX_DEBUG_INT13_ET(a...) BX_DEBUG(a)
959 #else
960 # define BX_DEBUG_INT13_ET(a...)
961 #endif
962 #if DEBUG_INT13_FL
963 # define BX_DEBUG_INT13_FL(a...) BX_DEBUG(a)
964 #else
965 # define BX_DEBUG_INT13_FL(a...)
966 #endif
967 #if DEBUG_INT15
968 # define BX_DEBUG_INT15(a...) BX_DEBUG(a)
969 #else
970 # define BX_DEBUG_INT15(a...)
971 #endif
972 #if DEBUG_INT16
973 # define BX_DEBUG_INT16(a...) BX_DEBUG(a)
974 #else
975 # define BX_DEBUG_INT16(a...)
976 #endif
977 #if DEBUG_INT1A
978 # define BX_DEBUG_INT1A(a...) BX_DEBUG(a)
979 #else
980 # define BX_DEBUG_INT1A(a...)
981 #endif
982 #if DEBUG_INT74
983 # define BX_DEBUG_INT74(a...) BX_DEBUG(a)
984 #else
985 # define BX_DEBUG_INT74(a...)
986 #endif
988 #define SET_AL(val8) AX = ((AX & 0xff00) | (val8))
989 #define SET_BL(val8) BX = ((BX & 0xff00) | (val8))
990 #define SET_CL(val8) CX = ((CX & 0xff00) | (val8))
991 #define SET_DL(val8) DX = ((DX & 0xff00) | (val8))
992 #define SET_AH(val8) AX = ((AX & 0x00ff) | ((val8) << 8))
993 #define SET_BH(val8) BX = ((BX & 0x00ff) | ((val8) << 8))
994 #define SET_CH(val8) CX = ((CX & 0x00ff) | ((val8) << 8))
995 #define SET_DH(val8) DX = ((DX & 0x00ff) | ((val8) << 8))
997 #define GET_AL() ( AX & 0x00ff )
998 #define GET_BL() ( BX & 0x00ff )
999 #define GET_CL() ( CX & 0x00ff )
1000 #define GET_DL() ( DX & 0x00ff )
1001 #define GET_AH() ( AX >> 8 )
1002 #define GET_BH() ( BX >> 8 )
1003 #define GET_CH() ( CX >> 8 )
1004 #define GET_DH() ( DX >> 8 )
1006 #define GET_ELDL() ( ELDX & 0x00ff )
1007 #define GET_ELDH() ( ELDX >> 8 )
1009 #define SET_CF() FLAGS |= 0x0001
1010 #define CLEAR_CF() FLAGS &= 0xfffe
1011 #define GET_CF() (FLAGS & 0x0001)
1013 #define SET_ZF() FLAGS |= 0x0040
1014 #define CLEAR_ZF() FLAGS &= 0xffbf
1015 #define GET_ZF() (FLAGS & 0x0040)
1017 #define UNSUPPORTED_FUNCTION 0x86
1019 #define none 0
1020 #define MAX_SCAN_CODE 0x58
1022 static struct {
1023 Bit16u normal;
1024 Bit16u shift;
1025 Bit16u control;
1026 Bit16u alt;
1027 Bit8u lock_flags;
1028 } scan_to_scanascii[MAX_SCAN_CODE + 1] = {
1029 { none, none, none, none, none },
1030 { 0x011b, 0x011b, 0x011b, 0x0100, none }, /* escape */
1031 { 0x0231, 0x0221, none, 0x7800, none }, /* 1! */
1032 { 0x0332, 0x0340, 0x0300, 0x7900, none }, /* 2@ */
1033 { 0x0433, 0x0423, none, 0x7a00, none }, /* 3# */
1034 { 0x0534, 0x0524, none, 0x7b00, none }, /* 4$ */
1035 { 0x0635, 0x0625, none, 0x7c00, none }, /* 5% */
1036 { 0x0736, 0x075e, 0x071e, 0x7d00, none }, /* 6^ */
1037 { 0x0837, 0x0826, none, 0x7e00, none }, /* 7& */
1038 { 0x0938, 0x092a, none, 0x7f00, none }, /* 8* */
1039 { 0x0a39, 0x0a28, none, 0x8000, none }, /* 9( */
1040 { 0x0b30, 0x0b29, none, 0x8100, none }, /* 0) */
1041 { 0x0c2d, 0x0c5f, 0x0c1f, 0x8200, none }, /* -_ */
1042 { 0x0d3d, 0x0d2b, none, 0x8300, none }, /* =+ */
1043 { 0x0e08, 0x0e08, 0x0e7f, none, none }, /* backspace */
1044 { 0x0f09, 0x0f00, none, none, none }, /* tab */
1045 { 0x1071, 0x1051, 0x1011, 0x1000, 0x40 }, /* Q */
1046 { 0x1177, 0x1157, 0x1117, 0x1100, 0x40 }, /* W */
1047 { 0x1265, 0x1245, 0x1205, 0x1200, 0x40 }, /* E */
1048 { 0x1372, 0x1352, 0x1312, 0x1300, 0x40 }, /* R */
1049 { 0x1474, 0x1454, 0x1414, 0x1400, 0x40 }, /* T */
1050 { 0x1579, 0x1559, 0x1519, 0x1500, 0x40 }, /* Y */
1051 { 0x1675, 0x1655, 0x1615, 0x1600, 0x40 }, /* U */
1052 { 0x1769, 0x1749, 0x1709, 0x1700, 0x40 }, /* I */
1053 { 0x186f, 0x184f, 0x180f, 0x1800, 0x40 }, /* O */
1054 { 0x1970, 0x1950, 0x1910, 0x1900, 0x40 }, /* P */
1055 { 0x1a5b, 0x1a7b, 0x1a1b, none, none }, /* [{ */
1056 { 0x1b5d, 0x1b7d, 0x1b1d, none, none }, /* ]} */
1057 { 0x1c0d, 0x1c0d, 0x1c0a, none, none }, /* Enter */
1058 { none, none, none, none, none }, /* L Ctrl */
1059 { 0x1e61, 0x1e41, 0x1e01, 0x1e00, 0x40 }, /* A */
1060 { 0x1f73, 0x1f53, 0x1f13, 0x1f00, 0x40 }, /* S */
1061 { 0x2064, 0x2044, 0x2004, 0x2000, 0x40 }, /* D */
1062 { 0x2166, 0x2146, 0x2106, 0x2100, 0x40 }, /* F */
1063 { 0x2267, 0x2247, 0x2207, 0x2200, 0x40 }, /* G */
1064 { 0x2368, 0x2348, 0x2308, 0x2300, 0x40 }, /* H */
1065 { 0x246a, 0x244a, 0x240a, 0x2400, 0x40 }, /* J */
1066 { 0x256b, 0x254b, 0x250b, 0x2500, 0x40 }, /* K */
1067 { 0x266c, 0x264c, 0x260c, 0x2600, 0x40 }, /* L */
1068 { 0x273b, 0x273a, none, none, none }, /* ;: */
1069 { 0x2827, 0x2822, none, none, none }, /* '" */
1070 { 0x2960, 0x297e, none, none, none }, /* `~ */
1071 { none, none, none, none, none }, /* L shift */
1072 { 0x2b5c, 0x2b7c, 0x2b1c, none, none }, /* |\ */
1073 { 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00, 0x40 }, /* Z */
1074 { 0x2d78, 0x2d58, 0x2d18, 0x2d00, 0x40 }, /* X */
1075 { 0x2e63, 0x2e43, 0x2e03, 0x2e00, 0x40 }, /* C */
1076 { 0x2f76, 0x2f56, 0x2f16, 0x2f00, 0x40 }, /* V */
1077 { 0x3062, 0x3042, 0x3002, 0x3000, 0x40 }, /* B */
1078 { 0x316e, 0x314e, 0x310e, 0x3100, 0x40 }, /* N */
1079 { 0x326d, 0x324d, 0x320d, 0x3200, 0x40 }, /* M */
1080 { 0x332c, 0x333c, none, none, none }, /* ,< */
1081 { 0x342e, 0x343e, none, none, none }, /* .> */
1082 { 0x352f, 0x353f, none, none, none }, /* /? */
1083 { none, none, none, none, none }, /* R Shift */
1084 { 0x372a, 0x372a, none, none, none }, /* * */
1085 { none, none, none, none, none }, /* L Alt */
1086 { 0x3920, 0x3920, 0x3920, 0x3920, none }, /* space */
1087 { none, none, none, none, none }, /* caps lock */
1088 { 0x3b00, 0x5400, 0x5e00, 0x6800, none }, /* F1 */
1089 { 0x3c00, 0x5500, 0x5f00, 0x6900, none }, /* F2 */
1090 { 0x3d00, 0x5600, 0x6000, 0x6a00, none }, /* F3 */
1091 { 0x3e00, 0x5700, 0x6100, 0x6b00, none }, /* F4 */
1092 { 0x3f00, 0x5800, 0x6200, 0x6c00, none }, /* F5 */
1093 { 0x4000, 0x5900, 0x6300, 0x6d00, none }, /* F6 */
1094 { 0x4100, 0x5a00, 0x6400, 0x6e00, none }, /* F7 */
1095 { 0x4200, 0x5b00, 0x6500, 0x6f00, none }, /* F8 */
1096 { 0x4300, 0x5c00, 0x6600, 0x7000, none }, /* F9 */
1097 { 0x4400, 0x5d00, 0x6700, 0x7100, none }, /* F10 */
1098 { none, none, none, none, none }, /* Num Lock */
1099 { none, none, none, none, none }, /* Scroll Lock */
1100 { 0x4700, 0x4737, 0x7700, none, 0x20 }, /* 7 Home */
1101 { 0x4800, 0x4838, none, none, 0x20 }, /* 8 UP */
1102 { 0x4900, 0x4939, 0x8400, none, 0x20 }, /* 9 PgUp */
1103 { 0x4a2d, 0x4a2d, none, none, none }, /* - */
1104 { 0x4b00, 0x4b34, 0x7300, none, 0x20 }, /* 4 Left */
1105 { 0x4c00, 0x4c35, none, none, 0x20 }, /* 5 */
1106 { 0x4d00, 0x4d36, 0x7400, none, 0x20 }, /* 6 Right */
1107 { 0x4e2b, 0x4e2b, none, none, none }, /* + */
1108 { 0x4f00, 0x4f31, 0x7500, none, 0x20 }, /* 1 End */
1109 { 0x5000, 0x5032, none, none, 0x20 }, /* 2 Down */
1110 { 0x5100, 0x5133, 0x7600, none, 0x20 }, /* 3 PgDn */
1111 { 0x5200, 0x5230, none, none, 0x20 }, /* 0 Ins */
1112 { 0x5300, 0x532e, none, none, 0x20 }, /* Del */
1113 { none, none, none, none, none },
1114 { none, none, none, none, none },
1115 { 0x565c, 0x567c, none, none, none }, /* \| */
1116 { 0x5700, 0x5700, none, none, none }, /* F11 */
1117 { 0x5800, 0x5800, none, none, none } /* F12 */
1120 Bit8u
1121 inb(port)
1122 Bit16u port;
1124 ASM_START
1125 push bp
1126 mov bp, sp
1128 push dx
1129 mov dx, 4[bp]
1130 in al, dx
1131 pop dx
1133 pop bp
1134 ASM_END
1137 #if BX_USE_ATADRV
1138 Bit16u
1139 inw(port)
1140 Bit16u port;
1142 ASM_START
1143 push bp
1144 mov bp, sp
1146 push dx
1147 mov dx, 4[bp]
1148 in ax, dx
1149 pop dx
1151 pop bp
1152 ASM_END
1154 #endif
1156 void
1157 outb(port, val)
1158 Bit16u port;
1159 Bit8u val;
1161 ASM_START
1162 push bp
1163 mov bp, sp
1165 push ax
1166 push dx
1167 mov dx, 4[bp]
1168 mov al, 6[bp]
1169 out dx, al
1170 pop dx
1171 pop ax
1173 pop bp
1174 ASM_END
1177 #if BX_USE_ATADRV
1178 void
1179 outw(port, val)
1180 Bit16u port;
1181 Bit16u val;
1183 ASM_START
1184 push bp
1185 mov bp, sp
1187 push ax
1188 push dx
1189 mov dx, 4[bp]
1190 mov ax, 6[bp]
1191 out dx, ax
1192 pop dx
1193 pop ax
1195 pop bp
1196 ASM_END
1198 #endif
1200 void
1201 outb_cmos(cmos_reg, val)
1202 Bit8u cmos_reg;
1203 Bit8u val;
1205 ASM_START
1206 push bp
1207 mov bp, sp
1209 mov al, 4[bp] ;; cmos_reg
1210 out 0x70, al
1211 mov al, 6[bp] ;; val
1212 out 0x71, al
1214 pop bp
1215 ASM_END
1218 Bit8u
1219 inb_cmos(cmos_reg)
1220 Bit8u cmos_reg;
1222 ASM_START
1223 push bp
1224 mov bp, sp
1226 mov al, 4[bp] ;; cmos_reg
1227 out 0x70, al
1228 in al, 0x71
1230 pop bp
1231 ASM_END
1234 void
1235 init_rtc()
1237 outb_cmos(0x0a, 0x26);
1238 outb_cmos(0x0b, 0x02);
1239 inb_cmos(0x0c);
1240 inb_cmos(0x0d);
1243 bx_bool
1244 rtc_updating()
1246 // This function checks to see if the update-in-progress bit
1247 // is set in CMOS Status Register A. If not, it returns 0.
1248 // If it is set, it tries to wait until there is a transition
1249 // to 0, and will return 0 if such a transition occurs. A 1
1250 // is returned only after timing out. The maximum period
1251 // that this bit should be set is constrained to 244useconds.
1252 // The count I use below guarantees coverage or more than
1253 // this time, with any reasonable IPS setting.
1255 Bit16u count;
1257 count = 25000;
1258 while (--count != 0) {
1259 if ( (inb_cmos(0x0a) & 0x80) == 0 )
1260 return(0);
1262 return(1); // update-in-progress never transitioned to 0
1266 Bit8u
1267 read_byte(seg, offset)
1268 Bit16u seg;
1269 Bit16u offset;
1271 ASM_START
1272 push bp
1273 mov bp, sp
1275 push bx
1276 push ds
1277 mov ax, 4[bp] ; segment
1278 mov ds, ax
1279 mov bx, 6[bp] ; offset
1280 mov al, [bx]
1281 ;; al = return value (byte)
1282 pop ds
1283 pop bx
1285 pop bp
1286 ASM_END
1289 Bit16u
1290 read_word(seg, offset)
1291 Bit16u seg;
1292 Bit16u offset;
1294 ASM_START
1295 push bp
1296 mov bp, sp
1298 push bx
1299 push ds
1300 mov ax, 4[bp] ; segment
1301 mov ds, ax
1302 mov bx, 6[bp] ; offset
1303 mov ax, [bx]
1304 ;; ax = return value (word)
1305 pop ds
1306 pop bx
1308 pop bp
1309 ASM_END
1312 void
1313 write_byte(seg, offset, data)
1314 Bit16u seg;
1315 Bit16u offset;
1316 Bit8u data;
1318 ASM_START
1319 push bp
1320 mov bp, sp
1322 push ax
1323 push bx
1324 push ds
1325 mov ax, 4[bp] ; segment
1326 mov ds, ax
1327 mov bx, 6[bp] ; offset
1328 mov al, 8[bp] ; data byte
1329 mov [bx], al ; write data byte
1330 pop ds
1331 pop bx
1332 pop ax
1334 pop bp
1335 ASM_END
1338 void
1339 write_word(seg, offset, data)
1340 Bit16u seg;
1341 Bit16u offset;
1342 Bit16u data;
1344 ASM_START
1345 push bp
1346 mov bp, sp
1348 push ax
1349 push bx
1350 push ds
1351 mov ax, 4[bp] ; segment
1352 mov ds, ax
1353 mov bx, 6[bp] ; offset
1354 mov ax, 8[bp] ; data word
1355 mov [bx], ax ; write data word
1356 pop ds
1357 pop bx
1358 pop ax
1360 pop bp
1361 ASM_END
1364 Bit16u
1365 get_CS()
1367 ASM_START
1368 mov ax, cs
1369 ASM_END
1372 Bit16u
1373 get_SS()
1375 ASM_START
1376 mov ax, ss
1377 ASM_END
1380 #if BX_DEBUG_SERIAL
1381 /* serial debug port*/
1382 #define BX_DEBUG_PORT 0x03f8
1384 /* data */
1385 #define UART_RBR 0x00
1386 #define UART_THR 0x00
1388 /* control */
1389 #define UART_IER 0x01
1390 #define UART_IIR 0x02
1391 #define UART_FCR 0x02
1392 #define UART_LCR 0x03
1393 #define UART_MCR 0x04
1394 #define UART_DLL 0x00
1395 #define UART_DLM 0x01
1397 /* status */
1398 #define UART_LSR 0x05
1399 #define UART_MSR 0x06
1400 #define UART_SCR 0x07
1402 int uart_can_tx_byte(base_port)
1403 Bit16u base_port;
1405 return inb(base_port + UART_LSR) & 0x20;
1408 void uart_wait_to_tx_byte(base_port)
1409 Bit16u base_port;
1411 while (!uart_can_tx_byte(base_port));
1414 void uart_wait_until_sent(base_port)
1415 Bit16u base_port;
1417 while (!(inb(base_port + UART_LSR) & 0x40));
1420 void uart_tx_byte(base_port, data)
1421 Bit16u base_port;
1422 Bit8u data;
1424 uart_wait_to_tx_byte(base_port);
1425 outb(base_port + UART_THR, data);
1426 uart_wait_until_sent(base_port);
1428 #endif
1430 void
1431 wrch(c)
1432 Bit8u c;
1434 ASM_START
1435 push bp
1436 mov bp, sp
1438 push bx
1439 mov ah, #0x0e
1440 mov al, 4[bp]
1441 xor bx,bx
1442 int #0x10
1443 pop bx
1445 pop bp
1446 ASM_END
1449 void
1450 send(action, c)
1451 Bit16u action;
1452 Bit8u c;
1454 #if BX_DEBUG_SERIAL
1455 if (c == '\n') uart_tx_byte(BX_DEBUG_PORT, '\r');
1456 uart_tx_byte(BX_DEBUG_PORT, c);
1457 #endif
1458 #if BX_VIRTUAL_PORTS
1459 if (action & BIOS_PRINTF_DEBUG) outb(DEBUG_PORT, c);
1460 if (action & BIOS_PRINTF_INFO) outb(INFO_PORT, c);
1461 #endif
1462 if (action & BIOS_PRINTF_SCREEN) {
1463 if (c == '\n') wrch('\r');
1464 wrch(c);
1468 void
1469 put_int(action, val, width, neg)
1470 Bit16u action;
1471 short val, width;
1472 bx_bool neg;
1474 short nval = val / 10;
1475 if (nval)
1476 put_int(action, nval, width - 1, neg);
1477 else {
1478 while (--width > 0) send(action, ' ');
1479 if (neg) send(action, '-');
1481 send(action, val - (nval * 10) + '0');
1484 void
1485 put_uint(action, val, width, neg)
1486 Bit16u action;
1487 unsigned short val;
1488 short width;
1489 bx_bool neg;
1491 unsigned short nval = val / 10;
1492 if (nval)
1493 put_uint(action, nval, width - 1, neg);
1494 else {
1495 while (--width > 0) send(action, ' ');
1496 if (neg) send(action, '-');
1498 send(action, val - (nval * 10) + '0');
1501 void
1502 put_luint(action, val, width, neg)
1503 Bit16u action;
1504 unsigned long val;
1505 short width;
1506 bx_bool neg;
1508 unsigned long nval = val / 10;
1509 if (nval)
1510 put_luint(action, nval, width - 1, neg);
1511 else {
1512 while (--width > 0) send(action, ' ');
1513 if (neg) send(action, '-');
1515 send(action, val - (nval * 10) + '0');
1518 void put_str(action, s)
1519 Bit16u action;
1520 Bit8u *s;
1522 Bit8u c;
1523 if (!s)
1524 s = "<NULL>";
1526 while (c = read_byte(get_CS(), s)) {
1527 send(action, c);
1528 s++;
1532 //--------------------------------------------------------------------------
1533 // bios_printf()
1534 // A compact variable argument printf function.
1536 // Supports %[format_width][length]format
1537 // where format can be x,X,u,d,s,c
1538 // and the optional length modifier is l (ell)
1539 //--------------------------------------------------------------------------
1540 void
1541 bios_printf(action, s)
1542 Bit16u action;
1543 Bit8u *s;
1545 Bit8u c, format_char;
1546 bx_bool in_format;
1547 short i;
1548 Bit16u *arg_ptr;
1549 Bit16u arg_seg, arg, nibble, hibyte, shift_count, format_width, hexadd;
1551 arg_ptr = &s;
1552 arg_seg = get_SS();
1554 in_format = 0;
1555 format_width = 0;
1557 if ((action & BIOS_PRINTF_DEBHALT) == BIOS_PRINTF_DEBHALT) {
1558 #if BX_VIRTUAL_PORTS
1559 outb(PANIC_PORT2, 0x00);
1560 #endif
1561 bios_printf (BIOS_PRINTF_SCREEN, "FATAL: ");
1564 while (c = read_byte(get_CS(), s)) {
1565 if ( c == '%' ) {
1566 in_format = 1;
1567 format_width = 0;
1569 else if (in_format) {
1570 if ( (c>='0') && (c<='9') ) {
1571 format_width = (format_width * 10) + (c - '0');
1573 else {
1574 arg_ptr++; // increment to next arg
1575 arg = read_word(arg_seg, arg_ptr);
1576 if (c == 'x' || c == 'X') {
1577 if (format_width == 0)
1578 format_width = 4;
1579 if (c == 'x')
1580 hexadd = 'a';
1581 else
1582 hexadd = 'A';
1583 for (i=format_width-1; i>=0; i--) {
1584 nibble = (arg >> (4 * i)) & 0x000f;
1585 send (action, (nibble<=9)? (nibble+'0') : (nibble-10+hexadd));
1588 else if (c == 'u') {
1589 put_uint(action, arg, format_width, 0);
1591 else if (c == 'l') {
1592 s++;
1593 c = read_byte(get_CS(), s); /* is it ld,lx,lu? */
1594 arg_ptr++; /* increment to next arg */
1595 hibyte = read_word(arg_seg, arg_ptr);
1596 if (c == 'd') {
1597 if (hibyte & 0x8000)
1598 put_luint(action, 0L-(((Bit32u) hibyte << 16) | arg), format_width-1, 1);
1599 else
1600 put_luint(action, ((Bit32u) hibyte << 16) | arg, format_width, 0);
1602 else if (c == 'u') {
1603 put_luint(action, ((Bit32u) hibyte << 16) | arg, format_width, 0);
1605 else if (c == 'x' || c == 'X')
1607 if (format_width == 0)
1608 format_width = 8;
1609 if (c == 'x')
1610 hexadd = 'a';
1611 else
1612 hexadd = 'A';
1613 for (i=format_width-1; i>=0; i--) {
1614 nibble = ((((Bit32u) hibyte <<16) | arg) >> (4 * i)) & 0x000f;
1615 send (action, (nibble<=9)? (nibble+'0') : (nibble-10+hexadd));
1619 else if (c == 'd') {
1620 if (arg & 0x8000)
1621 put_int(action, -arg, format_width - 1, 1);
1622 else
1623 put_int(action, arg, format_width, 0);
1625 else if (c == 's') {
1626 put_str(action, arg);
1628 else if (c == 'c') {
1629 send(action, arg);
1631 else
1632 BX_PANIC("bios_printf: unknown format\n");
1633 in_format = 0;
1636 else {
1637 send(action, c);
1639 s ++;
1642 if (action & BIOS_PRINTF_HALT) {
1643 // freeze in a busy loop.
1644 ASM_START
1646 halt2_loop:
1648 jmp halt2_loop
1649 ASM_END
1653 //--------------------------------------------------------------------------
1654 // keyboard_init
1655 //--------------------------------------------------------------------------
1656 // this file is based on LinuxBIOS implementation of keyboard.c
1657 // could convert to #asm to gain space
1658 void
1659 keyboard_init()
1661 Bit16u max;
1663 /* ------------------- Flush buffers ------------------------*/
1664 /* Wait until buffer is empty */
1665 max=0xffff;
1666 while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
1668 /* flush incoming keys */
1669 max=0x2000;
1670 while (--max > 0) {
1671 outb(0x80, 0x00);
1672 if (inb(0x64) & 0x01) {
1673 inb(0x60);
1674 max = 0x2000;
1678 // Due to timer issues, and if the IPS setting is > 15000000,
1679 // the incoming keys might not be flushed here. That will
1680 // cause a panic a few lines below. See sourceforge bug report :
1681 // [ 642031 ] FATAL: Keyboard RESET error:993
1683 /* ------------------- controller side ----------------------*/
1684 /* send cmd = 0xAA, self test 8042 */
1685 outb(0x64, 0xaa);
1687 /* Wait until buffer is empty */
1688 max=0xffff;
1689 while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
1690 if (max==0x0) keyboard_panic(00);
1692 /* Wait for data */
1693 max=0xffff;
1694 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x01);
1695 if (max==0x0) keyboard_panic(01);
1697 /* read self-test result, 0x55 should be returned from 0x60 */
1698 if ((inb(0x60) != 0x55)){
1699 keyboard_panic(991);
1702 /* send cmd = 0xAB, keyboard interface test */
1703 outb(0x64,0xab);
1705 /* Wait until buffer is empty */
1706 max=0xffff;
1707 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x10);
1708 if (max==0x0) keyboard_panic(10);
1710 /* Wait for data */
1711 max=0xffff;
1712 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x11);
1713 if (max==0x0) keyboard_panic(11);
1715 /* read keyboard interface test result, */
1716 /* 0x00 should be returned form 0x60 */
1717 if ((inb(0x60) != 0x00)) {
1718 keyboard_panic(992);
1721 /* Enable Keyboard clock */
1722 outb(0x64,0xae);
1723 outb(0x64,0xa8);
1725 /* ------------------- keyboard side ------------------------*/
1726 /* reset kerboard and self test (keyboard side) */
1727 outb(0x60, 0xff);
1729 /* Wait until buffer is empty */
1730 max=0xffff;
1731 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x20);
1732 if (max==0x0) keyboard_panic(20);
1734 /* Wait for data */
1735 max=0xffff;
1736 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x21);
1737 if (max==0x0) keyboard_panic(21);
1739 /* keyboard should return ACK */
1740 if ((inb(0x60) != 0xfa)) {
1741 keyboard_panic(993);
1744 /* Wait for data */
1745 max=0xffff;
1746 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x31);
1747 if (max==0x0) keyboard_panic(31);
1749 if ((inb(0x60) != 0xaa)) {
1750 keyboard_panic(994);
1753 /* Disable keyboard */
1754 outb(0x60, 0xf5);
1756 /* Wait until buffer is empty */
1757 max=0xffff;
1758 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x40);
1759 if (max==0x0) keyboard_panic(40);
1761 /* Wait for data */
1762 max=0xffff;
1763 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x41);
1764 if (max==0x0) keyboard_panic(41);
1766 /* keyboard should return ACK */
1767 if ((inb(0x60) != 0xfa)) {
1768 keyboard_panic(995);
1771 /* Write Keyboard Mode */
1772 outb(0x64, 0x60);
1774 /* Wait until buffer is empty */
1775 max=0xffff;
1776 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x50);
1777 if (max==0x0) keyboard_panic(50);
1779 /* send cmd: scan code convert, disable mouse, enable IRQ 1 */
1780 outb(0x60, 0x61);
1782 /* Wait until buffer is empty */
1783 max=0xffff;
1784 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x60);
1785 if (max==0x0) keyboard_panic(60);
1787 /* Enable keyboard */
1788 outb(0x60, 0xf4);
1790 /* Wait until buffer is empty */
1791 max=0xffff;
1792 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x70);
1793 if (max==0x0) keyboard_panic(70);
1795 /* Wait for data */
1796 max=0xffff;
1797 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x71);
1798 if (max==0x0) keyboard_panic(70);
1800 /* keyboard should return ACK */
1801 if ((inb(0x60) != 0xfa)) {
1802 keyboard_panic(996);
1805 outb(0x80, 0x77);
1808 //--------------------------------------------------------------------------
1809 // keyboard_panic
1810 //--------------------------------------------------------------------------
1811 void
1812 keyboard_panic(status)
1813 Bit16u status;
1815 // If you're getting a 993 keyboard panic here,
1816 // please see the comment in keyboard_init
1818 BX_PANIC("Keyboard error:%u\n",status);
1821 //--------------------------------------------------------------------------
1822 // shutdown_status_panic
1823 // called when the shutdown statsu is not implemented, displays the status
1824 //--------------------------------------------------------------------------
1825 void
1826 shutdown_status_panic(status)
1827 Bit16u status;
1829 BX_PANIC("Unimplemented shutdown status: %02x\n",(Bit8u)status);
1832 //--------------------------------------------------------------------------
1833 // print_bios_banner
1834 // displays a the bios version
1835 //--------------------------------------------------------------------------
1836 void
1837 print_bios_banner()
1839 printf(BX_APPNAME" BIOS - build: %s\n%s\nOptions: ",
1840 BIOS_BUILD_DATE, bios_cvs_version_string);
1841 printf(
1842 #if BX_APM
1843 "apmbios "
1844 #endif
1845 #if BX_PCIBIOS
1846 "pcibios "
1847 #endif
1848 #if BX_ELTORITO_BOOT
1849 "eltorito "
1850 #endif
1851 #if BX_ROMBIOS32
1852 "rombios32 "
1853 #endif
1854 "\n\n");
1857 //--------------------------------------------------------------------------
1858 // BIOS Boot Specification 1.0.1 compatibility
1860 // Very basic support for the BIOS Boot Specification, which allows expansion
1861 // ROMs to register themselves as boot devices, instead of just stealing the
1862 // INT 19h boot vector.
1864 // This is a hack: to do it properly requires a proper PnP BIOS and we aren't
1865 // one; we just lie to the option ROMs to make them behave correctly.
1866 // We also don't support letting option ROMs register as bootable disk
1867 // drives (BCVs), only as bootable devices (BEVs).
1869 // http://www.phoenix.com/en/Customer+Services/White+Papers-Specs/pc+industry+specifications.htm
1870 //--------------------------------------------------------------------------
1873 static void
1874 init_boot_vectors()
1876 ipl_entry_t e;
1877 Bit16u count = 0;
1878 Bit16u ss = get_SS();
1880 /* Clear out the IPL table. */
1881 memsetb(IPL_SEG, IPL_TABLE_OFFSET, 0, IPL_SIZE);
1883 /* Floppy drive */
1884 e.type = IPL_TYPE_FLOPPY; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
1885 memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
1886 count++;
1888 /* First HDD */
1889 e.type = IPL_TYPE_HARDDISK; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
1890 memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
1891 count++;
1893 #if BX_ELTORITO_BOOT
1894 /* CDROM */
1895 e.type = IPL_TYPE_CDROM; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
1896 memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
1897 count++;
1898 #endif
1900 /* Remember how many devices we have */
1901 write_word(IPL_SEG, IPL_COUNT_OFFSET, count);
1902 /* Not tried booting anything yet */
1903 write_word(IPL_SEG, IPL_SEQUENCE_OFFSET, 0xffff);
1906 static Bit8u
1907 get_boot_vector(i, e)
1908 Bit16u i; ipl_entry_t *e;
1910 Bit16u count;
1911 Bit16u ss = get_SS();
1912 /* Get the count of boot devices, and refuse to overrun the array */
1913 count = read_word(IPL_SEG, IPL_COUNT_OFFSET);
1914 if (i >= count) return 0;
1915 /* OK to read this device */
1916 memcpyb(ss, e, IPL_SEG, IPL_TABLE_OFFSET + i * sizeof (*e), sizeof (*e));
1917 return 1;
1921 //--------------------------------------------------------------------------
1922 // print_boot_device
1923 // displays the boot device
1924 //--------------------------------------------------------------------------
1926 static char drivetypes[][10]={"", "Floppy","Hard Disk","CD-Rom", "Network"};
1928 void
1929 print_boot_device(type)
1930 Bit16u type;
1932 /* NIC appears as type 0x80 */
1933 if (type == IPL_TYPE_BEV) type = 0x4;
1934 if (type == 0 || type > 0x4) BX_PANIC("Bad drive type\n");
1935 printf("Booting from %s...\n", drivetypes[type]);
1938 //--------------------------------------------------------------------------
1939 // print_boot_failure
1940 // displays the reason why boot failed
1941 //--------------------------------------------------------------------------
1942 void
1943 print_boot_failure(type, reason)
1944 Bit16u type; Bit8u reason;
1946 if (type == 0 || type > 0x3) BX_PANIC("Bad drive type\n");
1948 printf("Boot from %s failed", drivetypes[type]);
1949 if (type < 4) {
1950 /* Report the reason too */
1951 if (reason==0)
1952 printf(": not a bootable disk");
1953 else
1954 printf(": could not read the boot disk");
1956 printf("\n");
1959 //--------------------------------------------------------------------------
1960 // print_cdromboot_failure
1961 // displays the reason why boot failed
1962 //--------------------------------------------------------------------------
1963 void
1964 print_cdromboot_failure( code )
1965 Bit16u code;
1967 bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, "CDROM boot failure code : %04x\n",code);
1969 return;
1972 void
1973 nmi_handler_msg()
1975 BX_PANIC("NMI Handler called\n");
1978 void
1979 int18_panic_msg()
1981 BX_PANIC("INT18: BOOT FAILURE\n");
1984 void
1985 log_bios_start()
1987 #if BX_DEBUG_SERIAL
1988 outb(BX_DEBUG_PORT+UART_LCR, 0x03); /* setup for serial logging: 8N1 */
1989 #endif
1990 BX_INFO("%s\n", bios_cvs_version_string);
1993 bx_bool
1994 set_enable_a20(val)
1995 bx_bool val;
1997 Bit8u oldval;
1999 // Use PS2 System Control port A to set A20 enable
2001 // get current setting first
2002 oldval = inb(0x92);
2004 // change A20 status
2005 if (val)
2006 outb(0x92, oldval | 0x02);
2007 else
2008 outb(0x92, oldval & 0xfd);
2010 return((oldval & 0x02) != 0);
2013 void
2014 debugger_on()
2016 outb(0xfedc, 0x01);
2019 void
2020 debugger_off()
2022 outb(0xfedc, 0x00);
2025 #if BX_USE_ATADRV
2027 // ---------------------------------------------------------------------------
2028 // Start of ATA/ATAPI Driver
2029 // ---------------------------------------------------------------------------
2031 // Global defines -- ATA register and register bits.
2032 // command block & control block regs
2033 #define ATA_CB_DATA 0 // data reg in/out pio_base_addr1+0
2034 #define ATA_CB_ERR 1 // error in pio_base_addr1+1
2035 #define ATA_CB_FR 1 // feature reg out pio_base_addr1+1
2036 #define ATA_CB_SC 2 // sector count in/out pio_base_addr1+2
2037 #define ATA_CB_SN 3 // sector number in/out pio_base_addr1+3
2038 #define ATA_CB_CL 4 // cylinder low in/out pio_base_addr1+4
2039 #define ATA_CB_CH 5 // cylinder high in/out pio_base_addr1+5
2040 #define ATA_CB_DH 6 // device head in/out pio_base_addr1+6
2041 #define ATA_CB_STAT 7 // primary status in pio_base_addr1+7
2042 #define ATA_CB_CMD 7 // command out pio_base_addr1+7
2043 #define ATA_CB_ASTAT 6 // alternate status in pio_base_addr2+6
2044 #define ATA_CB_DC 6 // device control out pio_base_addr2+6
2045 #define ATA_CB_DA 7 // device address in pio_base_addr2+7
2047 #define ATA_CB_ER_ICRC 0x80 // ATA Ultra DMA bad CRC
2048 #define ATA_CB_ER_BBK 0x80 // ATA bad block
2049 #define ATA_CB_ER_UNC 0x40 // ATA uncorrected error
2050 #define ATA_CB_ER_MC 0x20 // ATA media change
2051 #define ATA_CB_ER_IDNF 0x10 // ATA id not found
2052 #define ATA_CB_ER_MCR 0x08 // ATA media change request
2053 #define ATA_CB_ER_ABRT 0x04 // ATA command aborted
2054 #define ATA_CB_ER_NTK0 0x02 // ATA track 0 not found
2055 #define ATA_CB_ER_NDAM 0x01 // ATA address mark not found
2057 #define ATA_CB_ER_P_SNSKEY 0xf0 // ATAPI sense key (mask)
2058 #define ATA_CB_ER_P_MCR 0x08 // ATAPI Media Change Request
2059 #define ATA_CB_ER_P_ABRT 0x04 // ATAPI command abort
2060 #define ATA_CB_ER_P_EOM 0x02 // ATAPI End of Media
2061 #define ATA_CB_ER_P_ILI 0x01 // ATAPI Illegal Length Indication
2063 // ATAPI Interrupt Reason bits in the Sector Count reg (CB_SC)
2064 #define ATA_CB_SC_P_TAG 0xf8 // ATAPI tag (mask)
2065 #define ATA_CB_SC_P_REL 0x04 // ATAPI release
2066 #define ATA_CB_SC_P_IO 0x02 // ATAPI I/O
2067 #define ATA_CB_SC_P_CD 0x01 // ATAPI C/D
2069 // bits 7-4 of the device/head (CB_DH) reg
2070 #define ATA_CB_DH_DEV0 0xa0 // select device 0
2071 #define ATA_CB_DH_DEV1 0xb0 // select device 1
2072 #define ATA_CB_DH_LBA 0x40 // use LBA
2074 // status reg (CB_STAT and CB_ASTAT) bits
2075 #define ATA_CB_STAT_BSY 0x80 // busy
2076 #define ATA_CB_STAT_RDY 0x40 // ready
2077 #define ATA_CB_STAT_DF 0x20 // device fault
2078 #define ATA_CB_STAT_WFT 0x20 // write fault (old name)
2079 #define ATA_CB_STAT_SKC 0x10 // seek complete
2080 #define ATA_CB_STAT_SERV 0x10 // service
2081 #define ATA_CB_STAT_DRQ 0x08 // data request
2082 #define ATA_CB_STAT_CORR 0x04 // corrected
2083 #define ATA_CB_STAT_IDX 0x02 // index
2084 #define ATA_CB_STAT_ERR 0x01 // error (ATA)
2085 #define ATA_CB_STAT_CHK 0x01 // check (ATAPI)
2087 // device control reg (CB_DC) bits
2088 #define ATA_CB_DC_HD15 0x08 // bit should always be set to one
2089 #define ATA_CB_DC_SRST 0x04 // soft reset
2090 #define ATA_CB_DC_NIEN 0x02 // disable interrupts
2092 // Most mandtory and optional ATA commands (from ATA-3),
2093 #define ATA_CMD_CFA_ERASE_SECTORS 0xC0
2094 #define ATA_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03
2095 #define ATA_CMD_CFA_TRANSLATE_SECTOR 0x87
2096 #define ATA_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD
2097 #define ATA_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38
2098 #define ATA_CMD_CHECK_POWER_MODE1 0xE5
2099 #define ATA_CMD_CHECK_POWER_MODE2 0x98
2100 #define ATA_CMD_DEVICE_RESET 0x08
2101 #define ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90
2102 #define ATA_CMD_FLUSH_CACHE 0xE7
2103 #define ATA_CMD_FORMAT_TRACK 0x50
2104 #define ATA_CMD_IDENTIFY_DEVICE 0xEC
2105 #define ATA_CMD_IDENTIFY_DEVICE_PACKET 0xA1
2106 #define ATA_CMD_IDENTIFY_PACKET_DEVICE 0xA1
2107 #define ATA_CMD_IDLE1 0xE3
2108 #define ATA_CMD_IDLE2 0x97
2109 #define ATA_CMD_IDLE_IMMEDIATE1 0xE1
2110 #define ATA_CMD_IDLE_IMMEDIATE2 0x95
2111 #define ATA_CMD_INITIALIZE_DRIVE_PARAMETERS 0x91
2112 #define ATA_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91
2113 #define ATA_CMD_NOP 0x00
2114 #define ATA_CMD_PACKET 0xA0
2115 #define ATA_CMD_READ_BUFFER 0xE4
2116 #define ATA_CMD_READ_DMA 0xC8
2117 #define ATA_CMD_READ_DMA_QUEUED 0xC7
2118 #define ATA_CMD_READ_MULTIPLE 0xC4
2119 #define ATA_CMD_READ_SECTORS 0x20
2120 #define ATA_CMD_READ_VERIFY_SECTORS 0x40
2121 #define ATA_CMD_RECALIBRATE 0x10
2122 #define ATA_CMD_REQUEST_SENSE 0x03
2123 #define ATA_CMD_SEEK 0x70
2124 #define ATA_CMD_SET_FEATURES 0xEF
2125 #define ATA_CMD_SET_MULTIPLE_MODE 0xC6
2126 #define ATA_CMD_SLEEP1 0xE6
2127 #define ATA_CMD_SLEEP2 0x99
2128 #define ATA_CMD_STANDBY1 0xE2
2129 #define ATA_CMD_STANDBY2 0x96
2130 #define ATA_CMD_STANDBY_IMMEDIATE1 0xE0
2131 #define ATA_CMD_STANDBY_IMMEDIATE2 0x94
2132 #define ATA_CMD_WRITE_BUFFER 0xE8
2133 #define ATA_CMD_WRITE_DMA 0xCA
2134 #define ATA_CMD_WRITE_DMA_QUEUED 0xCC
2135 #define ATA_CMD_WRITE_MULTIPLE 0xC5
2136 #define ATA_CMD_WRITE_SECTORS 0x30
2137 #define ATA_CMD_WRITE_VERIFY 0x3C
2139 #define ATA_IFACE_NONE 0x00
2140 #define ATA_IFACE_ISA 0x00
2141 #define ATA_IFACE_PCI 0x01
2143 #define ATA_TYPE_NONE 0x00
2144 #define ATA_TYPE_UNKNOWN 0x01
2145 #define ATA_TYPE_ATA 0x02
2146 #define ATA_TYPE_ATAPI 0x03
2148 #define ATA_DEVICE_NONE 0x00
2149 #define ATA_DEVICE_HD 0xFF
2150 #define ATA_DEVICE_CDROM 0x05
2152 #define ATA_MODE_NONE 0x00
2153 #define ATA_MODE_PIO16 0x00
2154 #define ATA_MODE_PIO32 0x01
2155 #define ATA_MODE_ISADMA 0x02
2156 #define ATA_MODE_PCIDMA 0x03
2157 #define ATA_MODE_USEIRQ 0x10
2159 #define ATA_TRANSLATION_NONE 0
2160 #define ATA_TRANSLATION_LBA 1
2161 #define ATA_TRANSLATION_LARGE 2
2162 #define ATA_TRANSLATION_RECHS 3
2164 #define ATA_DATA_NO 0x00
2165 #define ATA_DATA_IN 0x01
2166 #define ATA_DATA_OUT 0x02
2168 // ---------------------------------------------------------------------------
2169 // ATA/ATAPI driver : initialization
2170 // ---------------------------------------------------------------------------
2171 void ata_init( )
2173 Bit16u ebda_seg=read_word(0x0040,0x000E);
2174 Bit8u channel, device;
2176 // Channels info init.
2177 for (channel=0; channel<BX_MAX_ATA_INTERFACES; channel++) {
2178 write_byte(ebda_seg,&EbdaData->ata.channels[channel].iface,ATA_IFACE_NONE);
2179 write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1,0x0);
2180 write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2,0x0);
2181 write_byte(ebda_seg,&EbdaData->ata.channels[channel].irq,0);
2184 // Devices info init.
2185 for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2186 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
2187 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_NONE);
2188 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable,0);
2189 write_byte(ebda_seg,&EbdaData->ata.devices[device].lock,0);
2190 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode,ATA_MODE_NONE);
2191 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize,0);
2192 write_byte(ebda_seg,&EbdaData->ata.devices[device].translation,ATA_TRANSLATION_NONE);
2193 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads,0);
2194 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders,0);
2195 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt,0);
2196 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads,0);
2197 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders,0);
2198 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt,0);
2200 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors,0L);
2203 // hdidmap and cdidmap init.
2204 for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2205 write_byte(ebda_seg,&EbdaData->ata.hdidmap[device],BX_MAX_ATA_DEVICES);
2206 write_byte(ebda_seg,&EbdaData->ata.cdidmap[device],BX_MAX_ATA_DEVICES);
2209 write_byte(ebda_seg,&EbdaData->ata.hdcount,0);
2210 write_byte(ebda_seg,&EbdaData->ata.cdcount,0);
2213 #define TIMEOUT 0
2214 #define BSY 1
2215 #define NOT_BSY 2
2216 #define NOT_BSY_DRQ 3
2217 #define NOT_BSY_NOT_DRQ 4
2218 #define NOT_BSY_RDY 5
2220 #define IDE_TIMEOUT 32000u //32 seconds max for IDE ops
2222 int await_ide();
2223 static int await_ide(when_done,base,timeout)
2224 Bit8u when_done;
2225 Bit16u base;
2226 Bit16u timeout;
2228 Bit32u time=0,last=0;
2229 Bit16u status;
2230 Bit8u result;
2231 status = inb(base + ATA_CB_STAT); // for the times you're supposed to throw one away
2232 for(;;) {
2233 status = inb(base+ATA_CB_STAT);
2234 time++;
2235 if (when_done == BSY)
2236 result = status & ATA_CB_STAT_BSY;
2237 else if (when_done == NOT_BSY)
2238 result = !(status & ATA_CB_STAT_BSY);
2239 else if (when_done == NOT_BSY_DRQ)
2240 result = !(status & ATA_CB_STAT_BSY) && (status & ATA_CB_STAT_DRQ);
2241 else if (when_done == NOT_BSY_NOT_DRQ)
2242 result = !(status & ATA_CB_STAT_BSY) && !(status & ATA_CB_STAT_DRQ);
2243 else if (when_done == NOT_BSY_RDY)
2244 result = !(status & ATA_CB_STAT_BSY) && (status & ATA_CB_STAT_RDY);
2245 else if (when_done == TIMEOUT)
2246 result = 0;
2248 if (result) return 0;
2249 if (time>>16 != last) // mod 2048 each 16 ms
2251 last = time >>16;
2252 BX_DEBUG_ATA("await_ide: (TIMEOUT,BSY,!BSY,!BSY_DRQ,!BSY_!DRQ,!BSY_RDY) %d time= %ld timeout= %d\n",when_done,time>>11, timeout);
2254 if (status & ATA_CB_STAT_ERR)
2256 BX_DEBUG_ATA("await_ide: ERROR (TIMEOUT,BSY,!BSY,!BSY_DRQ,!BSY_!DRQ,!BSY_RDY) %d time= %ld timeout= %d\n",when_done,time>>11, timeout);
2257 return -1;
2259 if ((timeout == 0) || ((time>>11) > timeout)) break;
2261 BX_INFO("IDE time out\n");
2262 return -1;
2265 // ---------------------------------------------------------------------------
2266 // ATA/ATAPI driver : device detection
2267 // ---------------------------------------------------------------------------
2269 void ata_detect( )
2271 Bit16u ebda_seg=read_word(0x0040,0x000E);
2272 Bit8u hdcount, cdcount, device, type;
2273 Bit8u buffer[0x0200];
2275 #if BX_MAX_ATA_INTERFACES > 0
2276 write_byte(ebda_seg,&EbdaData->ata.channels[0].iface,ATA_IFACE_ISA);
2277 write_word(ebda_seg,&EbdaData->ata.channels[0].iobase1,0x1f0);
2278 write_word(ebda_seg,&EbdaData->ata.channels[0].iobase2,0x3f0);
2279 write_byte(ebda_seg,&EbdaData->ata.channels[0].irq,14);
2280 #endif
2281 #if BX_MAX_ATA_INTERFACES > 1
2282 write_byte(ebda_seg,&EbdaData->ata.channels[1].iface,ATA_IFACE_ISA);
2283 write_word(ebda_seg,&EbdaData->ata.channels[1].iobase1,0x170);
2284 write_word(ebda_seg,&EbdaData->ata.channels[1].iobase2,0x370);
2285 write_byte(ebda_seg,&EbdaData->ata.channels[1].irq,15);
2286 #endif
2287 #if BX_MAX_ATA_INTERFACES > 2
2288 write_byte(ebda_seg,&EbdaData->ata.channels[2].iface,ATA_IFACE_ISA);
2289 write_word(ebda_seg,&EbdaData->ata.channels[2].iobase1,0x1e8);
2290 write_word(ebda_seg,&EbdaData->ata.channels[2].iobase2,0x3e0);
2291 write_byte(ebda_seg,&EbdaData->ata.channels[2].irq,12);
2292 #endif
2293 #if BX_MAX_ATA_INTERFACES > 3
2294 write_byte(ebda_seg,&EbdaData->ata.channels[3].iface,ATA_IFACE_ISA);
2295 write_word(ebda_seg,&EbdaData->ata.channels[3].iobase1,0x168);
2296 write_word(ebda_seg,&EbdaData->ata.channels[3].iobase2,0x360);
2297 write_byte(ebda_seg,&EbdaData->ata.channels[3].irq,11);
2298 #endif
2299 #if BX_MAX_ATA_INTERFACES > 4
2300 #error Please fill the ATA interface informations
2301 #endif
2303 // Device detection
2304 hdcount=cdcount=0;
2306 for(device=0; device<BX_MAX_ATA_DEVICES; device++) {
2307 Bit16u iobase1, iobase2;
2308 Bit8u channel, slave, shift;
2309 Bit8u sc, sn, cl, ch, st;
2311 channel = device / 2;
2312 slave = device % 2;
2314 iobase1 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1);
2315 iobase2 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2);
2317 // Disable interrupts
2318 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2320 // Look for device
2321 outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2322 outb(iobase1+ATA_CB_SC, 0x55);
2323 outb(iobase1+ATA_CB_SN, 0xaa);
2324 outb(iobase1+ATA_CB_SC, 0xaa);
2325 outb(iobase1+ATA_CB_SN, 0x55);
2326 outb(iobase1+ATA_CB_SC, 0x55);
2327 outb(iobase1+ATA_CB_SN, 0xaa);
2329 // If we found something
2330 sc = inb(iobase1+ATA_CB_SC);
2331 sn = inb(iobase1+ATA_CB_SN);
2333 if ( (sc == 0x55) && (sn == 0xaa) ) {
2334 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_UNKNOWN);
2336 // reset the channel
2337 ata_reset(device);
2339 // check for ATA or ATAPI
2340 outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2341 sc = inb(iobase1+ATA_CB_SC);
2342 sn = inb(iobase1+ATA_CB_SN);
2343 if ((sc==0x01) && (sn==0x01)) {
2344 cl = inb(iobase1+ATA_CB_CL);
2345 ch = inb(iobase1+ATA_CB_CH);
2346 st = inb(iobase1+ATA_CB_STAT);
2348 if ((cl==0x14) && (ch==0xeb)) {
2349 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATAPI);
2350 } else if ((cl==0x00) && (ch==0x00) && (st!=0x00)) {
2351 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATA);
2352 } else if ((cl==0xff) && (ch==0xff)) {
2353 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
2358 type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type);
2360 // Now we send a IDENTIFY command to ATA device
2361 if(type == ATA_TYPE_ATA) {
2362 Bit32u sectors;
2363 Bit16u cylinders, heads, spt, blksize;
2364 Bit8u translation, removable, mode;
2366 //Temporary values to do the transfer
2367 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2368 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2370 if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE, 1, 0, 0, 0, 0L, get_SS(),buffer) !=0 )
2371 BX_PANIC("ata-detect: Failed to detect ATA device\n");
2373 removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2374 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2375 blksize = read_word(get_SS(),buffer+10);
2377 cylinders = read_word(get_SS(),buffer+(1*2)); // word 1
2378 heads = read_word(get_SS(),buffer+(3*2)); // word 3
2379 spt = read_word(get_SS(),buffer+(6*2)); // word 6
2381 sectors = read_dword(get_SS(),buffer+(60*2)); // word 60 and word 61
2383 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2384 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2385 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2386 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2387 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads, heads);
2388 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders, cylinders);
2389 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt, spt);
2390 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors, sectors);
2391 BX_INFO("ata%d-%d: PCHS=%u/%d/%d translation=", channel, slave,cylinders, heads, spt);
2393 translation = inb_cmos(0x39 + channel/2);
2394 for (shift=device%4; shift>0; shift--) translation >>= 2;
2395 translation &= 0x03;
2397 write_byte(ebda_seg,&EbdaData->ata.devices[device].translation, translation);
2399 switch (translation) {
2400 case ATA_TRANSLATION_NONE:
2401 BX_INFO("none");
2402 break;
2403 case ATA_TRANSLATION_LBA:
2404 BX_INFO("lba");
2405 break;
2406 case ATA_TRANSLATION_LARGE:
2407 BX_INFO("large");
2408 break;
2409 case ATA_TRANSLATION_RECHS:
2410 BX_INFO("r-echs");
2411 break;
2413 switch (translation) {
2414 case ATA_TRANSLATION_NONE:
2415 break;
2416 case ATA_TRANSLATION_LBA:
2417 spt = 63;
2418 sectors /= 63;
2419 heads = sectors / 1024;
2420 if (heads>128) heads = 255;
2421 else if (heads>64) heads = 128;
2422 else if (heads>32) heads = 64;
2423 else if (heads>16) heads = 32;
2424 else heads=16;
2425 cylinders = sectors / heads;
2426 break;
2427 case ATA_TRANSLATION_RECHS:
2428 // Take care not to overflow
2429 if (heads==16) {
2430 if(cylinders>61439) cylinders=61439;
2431 heads=15;
2432 cylinders = (Bit16u)((Bit32u)(cylinders)*16/15);
2434 // then go through the large bitshift process
2435 case ATA_TRANSLATION_LARGE:
2436 while(cylinders > 1024) {
2437 cylinders >>= 1;
2438 heads <<= 1;
2440 // If we max out the head count
2441 if (heads > 127) break;
2443 break;
2445 // clip to 1024 cylinders in lchs
2446 if (cylinders > 1024) cylinders=1024;
2447 BX_INFO(" LCHS=%d/%d/%d\n", cylinders, heads, spt);
2449 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads, heads);
2450 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders, cylinders);
2451 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt, spt);
2453 // fill hdidmap
2454 write_byte(ebda_seg,&EbdaData->ata.hdidmap[hdcount], device);
2455 hdcount++;
2458 // Now we send a IDENTIFY command to ATAPI device
2459 if(type == ATA_TYPE_ATAPI) {
2461 Bit8u type, removable, mode;
2462 Bit16u blksize;
2464 //Temporary values to do the transfer
2465 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_CDROM);
2466 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2468 if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE_PACKET, 1, 0, 0, 0, 0L, get_SS(),buffer) != 0)
2469 BX_PANIC("ata-detect: Failed to detect ATAPI device\n");
2471 type = read_byte(get_SS(),buffer+1) & 0x1f;
2472 removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2473 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2474 blksize = 2048;
2476 write_byte(ebda_seg,&EbdaData->ata.devices[device].device, type);
2477 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2478 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2479 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2481 // fill cdidmap
2482 write_byte(ebda_seg,&EbdaData->ata.cdidmap[cdcount], device);
2483 cdcount++;
2487 Bit32u sizeinmb;
2488 Bit16u ataversion;
2489 Bit8u c, i, version, model[41];
2491 switch (type) {
2492 case ATA_TYPE_ATA:
2493 sizeinmb = read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors);
2494 sizeinmb >>= 11;
2495 case ATA_TYPE_ATAPI:
2496 // Read ATA/ATAPI version
2497 ataversion=((Bit16u)(read_byte(get_SS(),buffer+161))<<8)|read_byte(get_SS(),buffer+160);
2498 for(version=15;version>0;version--) {
2499 if((ataversion&(1<<version))!=0)
2500 break;
2503 // Read model name
2504 for(i=0;i<20;i++){
2505 write_byte(get_SS(),model+(i*2),read_byte(get_SS(),buffer+(i*2)+54+1));
2506 write_byte(get_SS(),model+(i*2)+1,read_byte(get_SS(),buffer+(i*2)+54));
2509 // Reformat
2510 write_byte(get_SS(),model+40,0x00);
2511 for(i=39;i>0;i--){
2512 if(read_byte(get_SS(),model+i)==0x20)
2513 write_byte(get_SS(),model+i,0x00);
2514 else break;
2516 break;
2519 switch (type) {
2520 case ATA_TYPE_ATA:
2521 printf("ata%d %s: ",channel,slave?" slave":"master");
2522 i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
2523 if (sizeinmb < (1UL<<16))
2524 printf(" ATA-%d Hard-Disk (%4u MBytes)\n", version, (Bit16u)sizeinmb);
2525 else
2526 printf(" ATA-%d Hard-Disk (%4u GBytes)\n", version, (Bit16u)(sizeinmb>>10));
2527 break;
2528 case ATA_TYPE_ATAPI:
2529 printf("ata%d %s: ",channel,slave?" slave":"master");
2530 i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
2531 if(read_byte(ebda_seg,&EbdaData->ata.devices[device].device)==ATA_DEVICE_CDROM)
2532 printf(" ATAPI-%d CD-Rom/DVD-Rom\n",version);
2533 else
2534 printf(" ATAPI-%d Device\n",version);
2535 break;
2536 case ATA_TYPE_UNKNOWN:
2537 printf("ata%d %s: Unknown device\n",channel,slave?" slave":"master");
2538 break;
2543 // Store the devices counts
2544 write_byte(ebda_seg,&EbdaData->ata.hdcount, hdcount);
2545 write_byte(ebda_seg,&EbdaData->ata.cdcount, cdcount);
2546 write_byte(0x40,0x75, hdcount);
2548 printf("\n");
2550 // FIXME : should use bios=cmos|auto|disable bits
2551 // FIXME : should know about translation bits
2552 // FIXME : move hard_drive_post here
2556 // ---------------------------------------------------------------------------
2557 // ATA/ATAPI driver : software reset
2558 // ---------------------------------------------------------------------------
2559 // ATA-3
2560 // 8.2.1 Software reset - Device 0
2562 void ata_reset(device)
2563 Bit16u device;
2565 Bit16u ebda_seg=read_word(0x0040,0x000E);
2566 Bit16u iobase1, iobase2;
2567 Bit8u channel, slave, sn, sc;
2568 Bit8u type;
2569 Bit16u max;
2571 channel = device / 2;
2572 slave = device % 2;
2574 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2575 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2577 // Reset
2579 // 8.2.1 (a) -- set SRST in DC
2580 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST);
2582 // 8.2.1 (b) -- wait for BSY
2583 await_ide(BSY, iobase1, 20);
2585 // 8.2.1 (f) -- clear SRST
2586 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2588 type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type);
2589 if (type != ATA_TYPE_NONE) {
2591 // 8.2.1 (g) -- check for sc==sn==0x01
2592 // select device
2593 outb(iobase1+ATA_CB_DH, slave?ATA_CB_DH_DEV1:ATA_CB_DH_DEV0);
2594 sc = inb(iobase1+ATA_CB_SC);
2595 sn = inb(iobase1+ATA_CB_SN);
2597 if ( (sc==0x01) && (sn==0x01) ) {
2598 if (type == ATA_TYPE_ATA) //ATA
2599 await_ide(NOT_BSY_RDY, iobase1, IDE_TIMEOUT);
2600 else //ATAPI
2601 await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
2604 // 8.2.1 (h) -- wait for not BSY
2605 await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
2608 // Enable interrupts
2609 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2612 // ---------------------------------------------------------------------------
2613 // ATA/ATAPI driver : execute a non data command
2614 // ---------------------------------------------------------------------------
2616 Bit16u ata_cmd_non_data()
2617 {return 0;}
2619 // ---------------------------------------------------------------------------
2620 // ATA/ATAPI driver : execute a data-in command
2621 // ---------------------------------------------------------------------------
2622 // returns
2623 // 0 : no error
2624 // 1 : BUSY bit set
2625 // 2 : read error
2626 // 3 : expected DRQ=1
2627 // 4 : no sectors left to read/verify
2628 // 5 : more sectors to read/verify
2629 // 6 : no sectors left to write
2630 // 7 : more sectors to write
2631 Bit16u ata_cmd_data_in(device, command, count, cylinder, head, sector, lba, segment, offset)
2632 Bit16u device, command, count, cylinder, head, sector, segment, offset;
2633 Bit32u lba;
2635 Bit16u ebda_seg=read_word(0x0040,0x000E);
2636 Bit16u iobase1, iobase2, blksize;
2637 Bit8u channel, slave;
2638 Bit8u status, current, mode;
2640 channel = device / 2;
2641 slave = device % 2;
2643 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2644 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2645 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2646 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
2647 if (mode == ATA_MODE_PIO32) blksize>>=2;
2648 else blksize>>=1;
2650 // Reset count of transferred data
2651 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2652 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2653 current = 0;
2655 status = inb(iobase1 + ATA_CB_STAT);
2656 if (status & ATA_CB_STAT_BSY) return 1;
2658 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2660 // sector will be 0 only on lba access. Convert to lba-chs
2661 if (sector == 0) {
2662 if ((count >= 1 << 8) || (lba + count >= 1UL << 28)) {
2663 outb(iobase1 + ATA_CB_FR, 0x00);
2664 outb(iobase1 + ATA_CB_SC, (count >> 8) & 0xff);
2665 outb(iobase1 + ATA_CB_SN, lba >> 24);
2666 outb(iobase1 + ATA_CB_CL, 0);
2667 outb(iobase1 + ATA_CB_CH, 0);
2668 command |= 0x04;
2669 count &= (1UL << 8) - 1;
2670 lba &= (1UL << 24) - 1;
2672 sector = (Bit16u) (lba & 0x000000ffL);
2673 cylinder = (Bit16u) ((lba>>8) & 0x0000ffffL);
2674 head = ((Bit16u) ((lba>>24) & 0x0000000fL)) | ATA_CB_DH_LBA;
2677 outb(iobase1 + ATA_CB_FR, 0x00);
2678 outb(iobase1 + ATA_CB_SC, count);
2679 outb(iobase1 + ATA_CB_SN, sector);
2680 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
2681 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
2682 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
2683 outb(iobase1 + ATA_CB_CMD, command);
2685 await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
2686 status = inb(iobase1 + ATA_CB_STAT);
2688 if (status & ATA_CB_STAT_ERR) {
2689 BX_DEBUG_ATA("ata_cmd_data_in : read error\n");
2690 return 2;
2691 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2692 BX_DEBUG_ATA("ata_cmd_data_in : DRQ not set (status %02x)\n", (unsigned) status);
2693 return 3;
2696 // FIXME : move seg/off translation here
2698 ASM_START
2699 sti ;; enable higher priority interrupts
2700 ASM_END
2702 while (1) {
2704 ASM_START
2705 push bp
2706 mov bp, sp
2707 mov di, _ata_cmd_data_in.offset + 2[bp]
2708 mov ax, _ata_cmd_data_in.segment + 2[bp]
2709 mov cx, _ata_cmd_data_in.blksize + 2[bp]
2711 ;; adjust if there will be an overrun. 2K max sector size
2712 cmp di, #0xf800 ;;
2713 jbe ata_in_no_adjust
2715 ata_in_adjust:
2716 sub di, #0x0800 ;; sub 2 kbytes from offset
2717 add ax, #0x0080 ;; add 2 Kbytes to segment
2719 ata_in_no_adjust:
2720 mov es, ax ;; segment in es
2722 mov dx, _ata_cmd_data_in.iobase1 + 2[bp] ;; ATA data read port
2724 mov ah, _ata_cmd_data_in.mode + 2[bp]
2725 cmp ah, #ATA_MODE_PIO32
2726 je ata_in_32
2728 ata_in_16:
2730 insw ;; CX words transfered from port(DX) to ES:[DI]
2731 jmp ata_in_done
2733 ata_in_32:
2735 insd ;; CX dwords transfered from port(DX) to ES:[DI]
2737 ata_in_done:
2738 mov _ata_cmd_data_in.offset + 2[bp], di
2739 mov _ata_cmd_data_in.segment + 2[bp], es
2740 pop bp
2741 ASM_END
2743 current++;
2744 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
2745 count--;
2746 await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
2747 status = inb(iobase1 + ATA_CB_STAT);
2748 if (count == 0) {
2749 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2750 != ATA_CB_STAT_RDY ) {
2751 BX_DEBUG_ATA("ata_cmd_data_in : no sectors left (status %02x)\n", (unsigned) status);
2752 return 4;
2754 break;
2756 else {
2757 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2758 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2759 BX_DEBUG_ATA("ata_cmd_data_in : more sectors left (status %02x)\n", (unsigned) status);
2760 return 5;
2762 continue;
2765 // Enable interrupts
2766 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2767 return 0;
2770 // ---------------------------------------------------------------------------
2771 // ATA/ATAPI driver : execute a data-out command
2772 // ---------------------------------------------------------------------------
2773 // returns
2774 // 0 : no error
2775 // 1 : BUSY bit set
2776 // 2 : read error
2777 // 3 : expected DRQ=1
2778 // 4 : no sectors left to read/verify
2779 // 5 : more sectors to read/verify
2780 // 6 : no sectors left to write
2781 // 7 : more sectors to write
2782 Bit16u ata_cmd_data_out(device, command, count, cylinder, head, sector, lba, segment, offset)
2783 Bit16u device, command, count, cylinder, head, sector, segment, offset;
2784 Bit32u lba;
2786 Bit16u ebda_seg=read_word(0x0040,0x000E);
2787 Bit16u iobase1, iobase2, blksize;
2788 Bit8u channel, slave;
2789 Bit8u status, current, mode;
2791 channel = device / 2;
2792 slave = device % 2;
2794 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2795 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2796 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2797 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
2798 if (mode == ATA_MODE_PIO32) blksize>>=2;
2799 else blksize>>=1;
2801 // Reset count of transferred data
2802 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2803 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2804 current = 0;
2806 status = inb(iobase1 + ATA_CB_STAT);
2807 if (status & ATA_CB_STAT_BSY) return 1;
2809 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2811 // sector will be 0 only on lba access. Convert to lba-chs
2812 if (sector == 0) {
2813 if ((count >= 1 << 8) || (lba + count >= 1UL << 28)) {
2814 outb(iobase1 + ATA_CB_FR, 0x00);
2815 outb(iobase1 + ATA_CB_SC, (count >> 8) & 0xff);
2816 outb(iobase1 + ATA_CB_SN, lba >> 24);
2817 outb(iobase1 + ATA_CB_CL, 0);
2818 outb(iobase1 + ATA_CB_CH, 0);
2819 command |= 0x04;
2820 count &= (1UL << 8) - 1;
2821 lba &= (1UL << 24) - 1;
2823 sector = (Bit16u) (lba & 0x000000ffL);
2824 cylinder = (Bit16u) ((lba>>8) & 0x0000ffffL);
2825 head = ((Bit16u) ((lba>>24) & 0x0000000fL)) | ATA_CB_DH_LBA;
2828 outb(iobase1 + ATA_CB_FR, 0x00);
2829 outb(iobase1 + ATA_CB_SC, count);
2830 outb(iobase1 + ATA_CB_SN, sector);
2831 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
2832 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
2833 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
2834 outb(iobase1 + ATA_CB_CMD, command);
2836 await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
2837 status = inb(iobase1 + ATA_CB_STAT);
2839 if (status & ATA_CB_STAT_ERR) {
2840 BX_DEBUG_ATA("ata_cmd_data_out : read error\n");
2841 return 2;
2842 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2843 BX_DEBUG_ATA("ata_cmd_data_out : DRQ not set (status %02x)\n", (unsigned) status);
2844 return 3;
2847 // FIXME : move seg/off translation here
2849 ASM_START
2850 sti ;; enable higher priority interrupts
2851 ASM_END
2853 while (1) {
2855 ASM_START
2856 push bp
2857 mov bp, sp
2858 mov si, _ata_cmd_data_out.offset + 2[bp]
2859 mov ax, _ata_cmd_data_out.segment + 2[bp]
2860 mov cx, _ata_cmd_data_out.blksize + 2[bp]
2862 ;; adjust if there will be an overrun. 2K max sector size
2863 cmp si, #0xf800 ;;
2864 jbe ata_out_no_adjust
2866 ata_out_adjust:
2867 sub si, #0x0800 ;; sub 2 kbytes from offset
2868 add ax, #0x0080 ;; add 2 Kbytes to segment
2870 ata_out_no_adjust:
2871 mov es, ax ;; segment in es
2873 mov dx, _ata_cmd_data_out.iobase1 + 2[bp] ;; ATA data write port
2875 mov ah, _ata_cmd_data_out.mode + 2[bp]
2876 cmp ah, #ATA_MODE_PIO32
2877 je ata_out_32
2879 ata_out_16:
2880 seg ES
2882 outsw ;; CX words transfered from port(DX) to ES:[SI]
2883 jmp ata_out_done
2885 ata_out_32:
2886 seg ES
2888 outsd ;; CX dwords transfered from port(DX) to ES:[SI]
2890 ata_out_done:
2891 mov _ata_cmd_data_out.offset + 2[bp], si
2892 mov _ata_cmd_data_out.segment + 2[bp], es
2893 pop bp
2894 ASM_END
2896 current++;
2897 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
2898 count--;
2899 status = inb(iobase1 + ATA_CB_STAT);
2900 if (count == 0) {
2901 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2902 != ATA_CB_STAT_RDY ) {
2903 BX_DEBUG_ATA("ata_cmd_data_out : no sectors left (status %02x)\n", (unsigned) status);
2904 return 6;
2906 break;
2908 else {
2909 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2910 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2911 BX_DEBUG_ATA("ata_cmd_data_out : more sectors left (status %02x)\n", (unsigned) status);
2912 return 7;
2914 continue;
2917 // Enable interrupts
2918 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2919 return 0;
2922 // ---------------------------------------------------------------------------
2923 // ATA/ATAPI driver : execute a packet command
2924 // ---------------------------------------------------------------------------
2925 // returns
2926 // 0 : no error
2927 // 1 : error in parameters
2928 // 2 : BUSY bit set
2929 // 3 : error
2930 // 4 : not ready
2931 Bit16u ata_cmd_packet(device, cmdlen, cmdseg, cmdoff, header, length, inout, bufseg, bufoff)
2932 Bit8u cmdlen,inout;
2933 Bit16u device,cmdseg, cmdoff, bufseg, bufoff;
2934 Bit16u header;
2935 Bit32u length;
2937 Bit16u ebda_seg=read_word(0x0040,0x000E);
2938 Bit16u iobase1, iobase2;
2939 Bit16u lcount, lbefore, lafter, count;
2940 Bit8u channel, slave;
2941 Bit8u status, mode, lmode;
2942 Bit32u total, transfer;
2944 channel = device / 2;
2945 slave = device % 2;
2947 // Data out is not supported yet
2948 if (inout == ATA_DATA_OUT) {
2949 BX_INFO("ata_cmd_packet: DATA_OUT not supported yet\n");
2950 return 1;
2953 // The header length must be even
2954 if (header & 1) {
2955 BX_DEBUG_ATA("ata_cmd_packet : header must be even (%04x)\n",header);
2956 return 1;
2959 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2960 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2961 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2962 transfer= 0L;
2964 if (cmdlen < 12) cmdlen=12;
2965 if (cmdlen > 12) cmdlen=16;
2966 cmdlen>>=1;
2968 // Reset count of transferred data
2969 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2970 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2972 status = inb(iobase1 + ATA_CB_STAT);
2973 if (status & ATA_CB_STAT_BSY) return 2;
2975 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2976 outb(iobase1 + ATA_CB_FR, 0x00);
2977 outb(iobase1 + ATA_CB_SC, 0x00);
2978 outb(iobase1 + ATA_CB_SN, 0x00);
2979 outb(iobase1 + ATA_CB_CL, 0xfff0 & 0x00ff);
2980 outb(iobase1 + ATA_CB_CH, 0xfff0 >> 8);
2981 outb(iobase1 + ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2982 outb(iobase1 + ATA_CB_CMD, ATA_CMD_PACKET);
2984 // Device should ok to receive command
2985 await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
2986 status = inb(iobase1 + ATA_CB_STAT);
2988 if (status & ATA_CB_STAT_ERR) {
2989 BX_DEBUG_ATA("ata_cmd_packet : error, status is %02x\n",status);
2990 return 3;
2991 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2992 BX_DEBUG_ATA("ata_cmd_packet : DRQ not set (status %02x)\n", (unsigned) status);
2993 return 4;
2996 // Normalize address
2997 cmdseg += (cmdoff / 16);
2998 cmdoff %= 16;
3000 // Send command to device
3001 ASM_START
3002 sti ;; enable higher priority interrupts
3004 push bp
3005 mov bp, sp
3007 mov si, _ata_cmd_packet.cmdoff + 2[bp]
3008 mov ax, _ata_cmd_packet.cmdseg + 2[bp]
3009 mov cx, _ata_cmd_packet.cmdlen + 2[bp]
3010 mov es, ax ;; segment in es
3012 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data write port
3014 seg ES
3016 outsw ;; CX words transfered from port(DX) to ES:[SI]
3018 pop bp
3019 ASM_END
3021 if (inout == ATA_DATA_NO) {
3022 await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
3023 status = inb(iobase1 + ATA_CB_STAT);
3025 else {
3026 Bit16u loops = 0;
3027 Bit8u sc;
3028 while (1) {
3030 if (loops == 0) {//first time through
3031 status = inb(iobase2 + ATA_CB_ASTAT);
3032 await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
3034 else
3035 await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
3036 loops++;
3038 status = inb(iobase1 + ATA_CB_STAT);
3039 sc = inb(iobase1 + ATA_CB_SC);
3041 // Check if command completed
3042 if(((inb(iobase1 + ATA_CB_SC)&0x7)==0x3) &&
3043 ((status & (ATA_CB_STAT_RDY | ATA_CB_STAT_ERR)) == ATA_CB_STAT_RDY)) break;
3045 if (status & ATA_CB_STAT_ERR) {
3046 BX_DEBUG_ATA("ata_cmd_packet : error (status %02x)\n",status);
3047 return 3;
3050 // Normalize address
3051 bufseg += (bufoff / 16);
3052 bufoff %= 16;
3054 // Get the byte count
3055 lcount = ((Bit16u)(inb(iobase1 + ATA_CB_CH))<<8)+inb(iobase1 + ATA_CB_CL);
3057 // adjust to read what we want
3058 if(header>lcount) {
3059 lbefore=lcount;
3060 header-=lcount;
3061 lcount=0;
3063 else {
3064 lbefore=header;
3065 header=0;
3066 lcount-=lbefore;
3069 if(lcount>length) {
3070 lafter=lcount-length;
3071 lcount=length;
3072 length=0;
3074 else {
3075 lafter=0;
3076 length-=lcount;
3079 // Save byte count
3080 count = lcount;
3082 BX_DEBUG_ATA("Trying to read %04x bytes (%04x %04x %04x) ",lbefore+lcount+lafter,lbefore,lcount,lafter);
3083 BX_DEBUG_ATA("to 0x%04x:0x%04x\n",bufseg,bufoff);
3085 // If counts not dividable by 4, use 16bits mode
3086 lmode = mode;
3087 if (lbefore & 0x03) lmode=ATA_MODE_PIO16;
3088 if (lcount & 0x03) lmode=ATA_MODE_PIO16;
3089 if (lafter & 0x03) lmode=ATA_MODE_PIO16;
3091 // adds an extra byte if count are odd. before is always even
3092 if (lcount & 0x01) {
3093 lcount+=1;
3094 if ((lafter > 0) && (lafter & 0x01)) {
3095 lafter-=1;
3099 if (lmode == ATA_MODE_PIO32) {
3100 lcount>>=2; lbefore>>=2; lafter>>=2;
3102 else {
3103 lcount>>=1; lbefore>>=1; lafter>>=1;
3106 ; // FIXME bcc bug
3108 ASM_START
3109 push bp
3110 mov bp, sp
3112 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data read port
3114 mov cx, _ata_cmd_packet.lbefore + 2[bp]
3115 jcxz ata_packet_no_before
3117 mov ah, _ata_cmd_packet.lmode + 2[bp]
3118 cmp ah, #ATA_MODE_PIO32
3119 je ata_packet_in_before_32
3121 ata_packet_in_before_16:
3122 in ax, dx
3123 loop ata_packet_in_before_16
3124 jmp ata_packet_no_before
3126 ata_packet_in_before_32:
3127 push eax
3128 ata_packet_in_before_32_loop:
3129 in eax, dx
3130 loop ata_packet_in_before_32_loop
3131 pop eax
3133 ata_packet_no_before:
3134 mov cx, _ata_cmd_packet.lcount + 2[bp]
3135 jcxz ata_packet_after
3137 mov di, _ata_cmd_packet.bufoff + 2[bp]
3138 mov ax, _ata_cmd_packet.bufseg + 2[bp]
3139 mov es, ax
3141 mov ah, _ata_cmd_packet.lmode + 2[bp]
3142 cmp ah, #ATA_MODE_PIO32
3143 je ata_packet_in_32
3145 ata_packet_in_16:
3147 insw ;; CX words transfered tp port(DX) to ES:[DI]
3148 jmp ata_packet_after
3150 ata_packet_in_32:
3152 insd ;; CX dwords transfered to port(DX) to ES:[DI]
3154 ata_packet_after:
3155 mov cx, _ata_cmd_packet.lafter + 2[bp]
3156 jcxz ata_packet_done
3158 mov ah, _ata_cmd_packet.lmode + 2[bp]
3159 cmp ah, #ATA_MODE_PIO32
3160 je ata_packet_in_after_32
3162 ata_packet_in_after_16:
3163 in ax, dx
3164 loop ata_packet_in_after_16
3165 jmp ata_packet_done
3167 ata_packet_in_after_32:
3168 push eax
3169 ata_packet_in_after_32_loop:
3170 in eax, dx
3171 loop ata_packet_in_after_32_loop
3172 pop eax
3174 ata_packet_done:
3175 pop bp
3176 ASM_END
3178 // Compute new buffer address
3179 bufoff += count;
3181 // Save transferred bytes count
3182 transfer += count;
3183 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,transfer);
3187 // Final check, device must be ready
3188 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
3189 != ATA_CB_STAT_RDY ) {
3190 BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", (unsigned) status);
3191 return 4;
3194 // Enable interrupts
3195 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
3196 return 0;
3199 // ---------------------------------------------------------------------------
3200 // End of ATA/ATAPI Driver
3201 // ---------------------------------------------------------------------------
3203 // ---------------------------------------------------------------------------
3204 // Start of ATA/ATAPI generic functions
3205 // ---------------------------------------------------------------------------
3207 Bit16u
3208 atapi_get_sense(device, seg, asc, ascq)
3209 Bit16u device;
3211 Bit8u atacmd[12];
3212 Bit8u buffer[18];
3213 Bit8u i;
3215 memsetb(get_SS(),atacmd,0,12);
3217 // Request SENSE
3218 atacmd[0]=ATA_CMD_REQUEST_SENSE;
3219 atacmd[4]=sizeof(buffer);
3220 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 18L, ATA_DATA_IN, get_SS(), buffer) != 0)
3221 return 0x0002;
3223 write_byte(seg,asc,buffer[12]);
3224 write_byte(seg,ascq,buffer[13]);
3226 return 0;
3229 Bit16u
3230 atapi_is_ready(device)
3231 Bit16u device;
3233 Bit8u packet[12];
3234 Bit8u buf[8];
3235 Bit32u block_len;
3236 Bit32u sectors;
3237 Bit32u timeout; //measured in ms
3238 Bit32u time;
3239 Bit8u asc, ascq;
3240 Bit8u in_progress;
3241 Bit16u ebda_seg = read_word(0x0040,0x000E);
3242 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI) {
3243 printf("not implemented for non-ATAPI device\n");
3244 return -1;
3247 BX_DEBUG_ATA("ata_detect_medium: begin\n");
3248 memsetb(get_SS(),packet, 0, sizeof packet);
3249 packet[0] = 0x25; /* READ CAPACITY */
3251 /* Retry READ CAPACITY 50 times unless MEDIUM NOT PRESENT
3252 * is reported by the device. If the device reports "IN PROGRESS",
3253 * 30 seconds is added. */
3254 timeout = 5000;
3255 time = 0;
3256 in_progress = 0;
3257 while (time < timeout) {
3258 if (ata_cmd_packet(device, sizeof(packet), get_SS(), packet, 0, 8L, ATA_DATA_IN, get_SS(), buf) == 0)
3259 goto ok;
3261 if (atapi_get_sense(device, get_SS(), &asc, &ascq) == 0) {
3262 if (asc == 0x3a) { /* MEDIUM NOT PRESENT */
3263 BX_DEBUG_ATA("Device reports MEDIUM NOT PRESENT\n");
3264 return -1;
3267 if (asc == 0x04 && ascq == 0x01 && !in_progress) {
3268 /* IN PROGRESS OF BECOMING READY */
3269 printf("Waiting for device to detect medium... ");
3270 /* Allow 30 seconds more */
3271 timeout = 30000;
3272 in_progress = 1;
3275 time += 100;
3277 BX_DEBUG_ATA("read capacity failed\n");
3278 return -1;
3281 block_len = (Bit32u) buf[4] << 24
3282 | (Bit32u) buf[5] << 16
3283 | (Bit32u) buf[6] << 8
3284 | (Bit32u) buf[7] << 0;
3285 BX_DEBUG_ATA("block_len=%u\n", block_len);
3287 if (block_len!= 2048 && block_len!= 512)
3289 printf("Unsupported sector size %u\n", block_len);
3290 return -1;
3292 write_dword(ebda_seg,&EbdaData->ata.devices[device].blksize, block_len);
3294 sectors = (Bit32u) buf[0] << 24
3295 | (Bit32u) buf[1] << 16
3296 | (Bit32u) buf[2] << 8
3297 | (Bit32u) buf[3] << 0;
3299 BX_DEBUG_ATA("sectors=%u\n", sectors);
3300 if (block_len == 2048)
3301 sectors <<= 2; /* # of sectors in 512-byte "soft" sector */
3302 if (sectors != read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors))
3303 printf("%dMB medium detected\n", sectors>>(20-9));
3304 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors, sectors);
3305 return 0;
3308 Bit16u
3309 atapi_is_cdrom(device)
3310 Bit8u device;
3312 Bit16u ebda_seg=read_word(0x0040,0x000E);
3314 if (device >= BX_MAX_ATA_DEVICES)
3315 return 0;
3317 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI)
3318 return 0;
3320 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].device) != ATA_DEVICE_CDROM)
3321 return 0;
3323 return 1;
3326 // ---------------------------------------------------------------------------
3327 // End of ATA/ATAPI generic functions
3328 // ---------------------------------------------------------------------------
3330 #endif // BX_USE_ATADRV
3332 #if BX_ELTORITO_BOOT
3334 // ---------------------------------------------------------------------------
3335 // Start of El-Torito boot functions
3336 // ---------------------------------------------------------------------------
3338 void
3339 cdemu_init()
3341 Bit16u ebda_seg=read_word(0x0040,0x000E);
3343 // the only important data is this one for now
3344 write_byte(ebda_seg,&EbdaData->cdemu.active,0x00);
3347 Bit8u
3348 cdemu_isactive()
3350 Bit16u ebda_seg=read_word(0x0040,0x000E);
3352 return(read_byte(ebda_seg,&EbdaData->cdemu.active));
3355 Bit8u
3356 cdemu_emulated_drive()
3358 Bit16u ebda_seg=read_word(0x0040,0x000E);
3360 return(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
3363 static char isotag[6]="CD001";
3364 static char eltorito[24]="EL TORITO SPECIFICATION";
3366 // Returns ah: emulated drive, al: error code
3368 Bit16u
3369 cdrom_boot()
3371 Bit16u ebda_seg=read_word(0x0040,0x000E);
3372 Bit8u atacmd[12], buffer[2048];
3373 Bit32u lba;
3374 Bit16u boot_segment, nbsectors, i, error;
3375 Bit8u device;
3377 // Find out the first cdrom
3378 for (device=0; device<BX_MAX_ATA_DEVICES;device++) {
3379 if (atapi_is_cdrom(device)) break;
3382 if(error = atapi_is_ready(device) != 0)
3383 BX_INFO("ata_is_ready returned %d\n",error);
3385 // if not found
3386 if(device >= BX_MAX_ATA_DEVICES) return 2;
3388 // Read the Boot Record Volume Descriptor
3389 memsetb(get_SS(),atacmd,0,12);
3390 atacmd[0]=0x28; // READ command
3391 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3392 atacmd[8]=(0x01 & 0x00ff); // Sectors
3393 atacmd[2]=(0x11 & 0xff000000) >> 24; // LBA
3394 atacmd[3]=(0x11 & 0x00ff0000) >> 16;
3395 atacmd[4]=(0x11 & 0x0000ff00) >> 8;
3396 atacmd[5]=(0x11 & 0x000000ff);
3397 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3398 return 3;
3400 // Validity checks
3401 if(buffer[0]!=0)return 4;
3402 for(i=0;i<5;i++){
3403 if(buffer[1+i]!=read_byte(0xf000,&isotag[i]))return 5;
3405 for(i=0;i<23;i++)
3406 if(buffer[7+i]!=read_byte(0xf000,&eltorito[i]))return 6;
3408 // ok, now we calculate the Boot catalog address
3409 lba=buffer[0x4A]*0x1000000+buffer[0x49]*0x10000+buffer[0x48]*0x100+buffer[0x47];
3411 // And we read the Boot Catalog
3412 memsetb(get_SS(),atacmd,0,12);
3413 atacmd[0]=0x28; // READ command
3414 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3415 atacmd[8]=(0x01 & 0x00ff); // Sectors
3416 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3417 atacmd[3]=(lba & 0x00ff0000) >> 16;
3418 atacmd[4]=(lba & 0x0000ff00) >> 8;
3419 atacmd[5]=(lba & 0x000000ff);
3420 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3421 return 7;
3423 // Validation entry
3424 if(buffer[0x00]!=0x01)return 8; // Header
3425 if(buffer[0x01]!=0x00)return 9; // Platform
3426 if(buffer[0x1E]!=0x55)return 10; // key 1
3427 if(buffer[0x1F]!=0xAA)return 10; // key 2
3429 // Initial/Default Entry
3430 if(buffer[0x20]!=0x88)return 11; // Bootable
3432 write_byte(ebda_seg,&EbdaData->cdemu.media,buffer[0x21]);
3433 if(buffer[0x21]==0){
3434 // FIXME ElTorito Hardcoded. cdrom is hardcoded as device 0xE0.
3435 // Win2000 cd boot needs to know it booted from cd
3436 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0xE0);
3438 else if(buffer[0x21]<4)
3439 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x00);
3440 else
3441 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x80);
3443 write_byte(ebda_seg,&EbdaData->cdemu.controller_index,device/2);
3444 write_byte(ebda_seg,&EbdaData->cdemu.device_spec,device%2);
3446 boot_segment=buffer[0x23]*0x100+buffer[0x22];
3447 if(boot_segment==0x0000)boot_segment=0x07C0;
3449 write_word(ebda_seg,&EbdaData->cdemu.load_segment,boot_segment);
3450 write_word(ebda_seg,&EbdaData->cdemu.buffer_segment,0x0000);
3452 nbsectors=buffer[0x27]*0x100+buffer[0x26];
3453 write_word(ebda_seg,&EbdaData->cdemu.sector_count,nbsectors);
3455 lba=buffer[0x2B]*0x1000000+buffer[0x2A]*0x10000+buffer[0x29]*0x100+buffer[0x28];
3456 write_dword(ebda_seg,&EbdaData->cdemu.ilba,lba);
3458 // And we read the image in memory
3459 memsetb(get_SS(),atacmd,0,12);
3460 atacmd[0]=0x28; // READ command
3461 atacmd[7]=((1+(nbsectors-1)/4) & 0xff00) >> 8; // Sectors
3462 atacmd[8]=((1+(nbsectors-1)/4) & 0x00ff); // Sectors
3463 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3464 atacmd[3]=(lba & 0x00ff0000) >> 16;
3465 atacmd[4]=(lba & 0x0000ff00) >> 8;
3466 atacmd[5]=(lba & 0x000000ff);
3467 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, nbsectors*512L, ATA_DATA_IN, boot_segment,0)) != 0)
3468 return 12;
3470 // Remember the media type
3471 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
3472 case 0x01: // 1.2M floppy
3473 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,15);
3474 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3475 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3476 break;
3477 case 0x02: // 1.44M floppy
3478 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,18);
3479 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3480 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3481 break;
3482 case 0x03: // 2.88M floppy
3483 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,36);
3484 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3485 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3486 break;
3487 case 0x04: // Harddrive
3488 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,read_byte(boot_segment,446+6)&0x3f);
3489 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,
3490 (read_byte(boot_segment,446+6)<<2) + read_byte(boot_segment,446+7) + 1);
3491 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,read_byte(boot_segment,446+5) + 1);
3492 break;
3495 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0) {
3496 // Increase bios installed hardware number of devices
3497 if(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)==0x00)
3498 write_byte(0x40,0x10,read_byte(0x40,0x10)|0x41);
3499 else
3500 write_byte(ebda_seg, &EbdaData->ata.hdcount, read_byte(ebda_seg, &EbdaData->ata.hdcount) + 1);
3504 // everything is ok, so from now on, the emulation is active
3505 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0)
3506 write_byte(ebda_seg,&EbdaData->cdemu.active,0x01);
3508 // return the boot drive + no error
3509 return (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)*0x100)+0;
3512 // ---------------------------------------------------------------------------
3513 // End of El-Torito boot functions
3514 // ---------------------------------------------------------------------------
3515 #endif // BX_ELTORITO_BOOT
3517 void
3518 int14_function(regs, ds, iret_addr)
3519 pusha_regs_t regs; // regs pushed from PUSHA instruction
3520 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
3521 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
3523 Bit16u addr,timer,val16;
3524 Bit8u timeout;
3526 ASM_START
3528 ASM_END
3530 addr = read_word(0x0040, (regs.u.r16.dx << 1));
3531 timeout = read_byte(0x0040, 0x007C + regs.u.r16.dx);
3532 if ((regs.u.r16.dx < 4) && (addr > 0)) {
3533 switch (regs.u.r8.ah) {
3534 case 0:
3535 outb(addr+3, inb(addr+3) | 0x80);
3536 if (regs.u.r8.al & 0xE0 == 0) {
3537 outb(addr, 0x17);
3538 outb(addr+1, 0x04);
3539 } else {
3540 val16 = 0x600 >> ((regs.u.r8.al & 0xE0) >> 5);
3541 outb(addr, val16 & 0xFF);
3542 outb(addr+1, val16 >> 8);
3544 outb(addr+3, regs.u.r8.al & 0x1F);
3545 regs.u.r8.ah = inb(addr+5);
3546 regs.u.r8.al = inb(addr+6);
3547 ClearCF(iret_addr.flags);
3548 break;
3549 case 1:
3550 timer = read_word(0x0040, 0x006C);
3551 while (((inb(addr+5) & 0x60) != 0x60) && (timeout)) {
3552 val16 = read_word(0x0040, 0x006C);
3553 if (val16 != timer) {
3554 timer = val16;
3555 timeout--;
3558 if (timeout) outb(addr, regs.u.r8.al);
3559 regs.u.r8.ah = inb(addr+5);
3560 if (!timeout) regs.u.r8.ah |= 0x80;
3561 ClearCF(iret_addr.flags);
3562 break;
3563 case 2:
3564 timer = read_word(0x0040, 0x006C);
3565 while (((inb(addr+5) & 0x01) == 0) && (timeout)) {
3566 val16 = read_word(0x0040, 0x006C);
3567 if (val16 != timer) {
3568 timer = val16;
3569 timeout--;
3572 if (timeout) {
3573 regs.u.r8.ah = 0;
3574 regs.u.r8.al = inb(addr);
3575 } else {
3576 regs.u.r8.ah = inb(addr+5);
3578 ClearCF(iret_addr.flags);
3579 break;
3580 case 3:
3581 regs.u.r8.ah = inb(addr+5);
3582 regs.u.r8.al = inb(addr+6);
3583 ClearCF(iret_addr.flags);
3584 break;
3585 default:
3586 SetCF(iret_addr.flags); // Unsupported
3588 } else {
3589 SetCF(iret_addr.flags); // Unsupported
3593 void
3594 int15_function(regs, ES, DS, FLAGS)
3595 pusha_regs_t regs; // REGS pushed via pusha
3596 Bit16u ES, DS, FLAGS;
3598 Bit16u ebda_seg=read_word(0x0040,0x000E);
3599 bx_bool prev_a20_enable;
3600 Bit16u base15_00;
3601 Bit8u base23_16;
3602 Bit16u ss;
3603 Bit16u CX,DX;
3605 Bit16u bRegister;
3606 Bit8u irqDisable;
3608 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3610 switch (regs.u.r8.ah) {
3611 case 0x24: /* A20 Control */
3612 switch (regs.u.r8.al) {
3613 case 0x00:
3614 set_enable_a20(0);
3615 CLEAR_CF();
3616 regs.u.r8.ah = 0;
3617 break;
3618 case 0x01:
3619 set_enable_a20(1);
3620 CLEAR_CF();
3621 regs.u.r8.ah = 0;
3622 break;
3623 case 0x02:
3624 regs.u.r8.al = (inb(0x92) >> 1) & 0x01;
3625 CLEAR_CF();
3626 regs.u.r8.ah = 0;
3627 break;
3628 case 0x03:
3629 CLEAR_CF();
3630 regs.u.r8.ah = 0;
3631 regs.u.r16.bx = 3;
3632 break;
3633 default:
3634 BX_INFO("int15: Func 24h, subfunc %02xh, A20 gate control not supported\n", (unsigned) regs.u.r8.al);
3635 SET_CF();
3636 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3638 break;
3640 case 0x41:
3641 SET_CF();
3642 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3643 break;
3645 case 0x4f:
3646 /* keyboard intercept */
3647 #if BX_CPU < 2
3648 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3649 #else
3650 // nop
3651 #endif
3652 SET_CF();
3653 break;
3655 case 0x52: // removable media eject
3656 CLEAR_CF();
3657 regs.u.r8.ah = 0; // "ok ejection may proceed"
3658 break;
3660 case 0x83: {
3661 if( regs.u.r8.al == 0 ) {
3662 // Set Interval requested.
3663 if( ( read_byte( 0x40, 0xA0 ) & 1 ) == 0 ) {
3664 // Interval not already set.
3665 write_byte( 0x40, 0xA0, 1 ); // Set status byte.
3666 write_word( 0x40, 0x98, ES ); // Byte location, segment
3667 write_word( 0x40, 0x9A, regs.u.r16.bx ); // Byte location, offset
3668 write_word( 0x40, 0x9C, regs.u.r16.dx ); // Low word, delay
3669 write_word( 0x40, 0x9E, regs.u.r16.cx ); // High word, delay.
3670 CLEAR_CF( );
3671 irqDisable = inb( 0xA1 );
3672 outb( 0xA1, irqDisable & 0xFE );
3673 bRegister = inb_cmos( 0xB ); // Unmask IRQ8 so INT70 will get through.
3674 outb_cmos( 0xB, bRegister | 0x40 ); // Turn on the Periodic Interrupt timer
3675 } else {
3676 // Interval already set.
3677 BX_DEBUG_INT15("int15: Func 83h, failed, already waiting.\n" );
3678 SET_CF();
3679 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3681 } else if( regs.u.r8.al == 1 ) {
3682 // Clear Interval requested
3683 write_byte( 0x40, 0xA0, 0 ); // Clear status byte
3684 CLEAR_CF( );
3685 bRegister = inb_cmos( 0xB );
3686 outb_cmos( 0xB, bRegister & ~0x40 ); // Turn off the Periodic Interrupt timer
3687 } else {
3688 BX_DEBUG_INT15("int15: Func 83h, failed.\n" );
3689 SET_CF();
3690 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3691 regs.u.r8.al--;
3694 break;
3697 case 0x87:
3698 #if BX_CPU < 3
3699 # error "Int15 function 87h not supported on < 80386"
3700 #endif
3701 // +++ should probably have descriptor checks
3702 // +++ should have exception handlers
3704 // turn off interrupts
3705 ASM_START
3707 ASM_END
3709 prev_a20_enable = set_enable_a20(1); // enable A20 line
3711 // 128K max of transfer on 386+ ???
3712 // source == destination ???
3714 // ES:SI points to descriptor table
3715 // offset use initially comments
3716 // ==============================================
3717 // 00..07 Unused zeros Null descriptor
3718 // 08..0f GDT zeros filled in by BIOS
3719 // 10..17 source ssssssss source of data
3720 // 18..1f dest dddddddd destination of data
3721 // 20..27 CS zeros filled in by BIOS
3722 // 28..2f SS zeros filled in by BIOS
3724 //es:si
3725 //eeee0
3726 //0ssss
3727 //-----
3729 // check for access rights of source & dest here
3731 // Initialize GDT descriptor
3732 base15_00 = (ES << 4) + regs.u.r16.si;
3733 base23_16 = ES >> 12;
3734 if (base15_00 < (ES<<4))
3735 base23_16++;
3736 write_word(ES, regs.u.r16.si+0x08+0, 47); // limit 15:00 = 6 * 8bytes/descriptor
3737 write_word(ES, regs.u.r16.si+0x08+2, base15_00);// base 15:00
3738 write_byte(ES, regs.u.r16.si+0x08+4, base23_16);// base 23:16
3739 write_byte(ES, regs.u.r16.si+0x08+5, 0x93); // access
3740 write_word(ES, regs.u.r16.si+0x08+6, 0x0000); // base 31:24/reserved/limit 19:16
3742 // Initialize CS descriptor
3743 write_word(ES, regs.u.r16.si+0x20+0, 0xffff);// limit 15:00 = normal 64K limit
3744 write_word(ES, regs.u.r16.si+0x20+2, 0x0000);// base 15:00
3745 write_byte(ES, regs.u.r16.si+0x20+4, 0x000f);// base 23:16
3746 write_byte(ES, regs.u.r16.si+0x20+5, 0x9b); // access
3747 write_word(ES, regs.u.r16.si+0x20+6, 0x0000);// base 31:24/reserved/limit 19:16
3749 // Initialize SS descriptor
3750 ss = get_SS();
3751 base15_00 = ss << 4;
3752 base23_16 = ss >> 12;
3753 write_word(ES, regs.u.r16.si+0x28+0, 0xffff); // limit 15:00 = normal 64K limit
3754 write_word(ES, regs.u.r16.si+0x28+2, base15_00);// base 15:00
3755 write_byte(ES, regs.u.r16.si+0x28+4, base23_16);// base 23:16
3756 write_byte(ES, regs.u.r16.si+0x28+5, 0x93); // access
3757 write_word(ES, regs.u.r16.si+0x28+6, 0x0000); // base 31:24/reserved/limit 19:16
3759 CX = regs.u.r16.cx;
3760 ASM_START
3761 // Compile generates locals offset info relative to SP.
3762 // Get CX (word count) from stack.
3763 mov bx, sp
3764 SEG SS
3765 mov cx, _int15_function.CX [bx]
3767 // since we need to set SS:SP, save them to the BDA
3768 // for future restore
3769 push eax
3770 xor eax, eax
3771 mov ds, ax
3772 mov 0x0469, ss
3773 mov 0x0467, sp
3775 SEG ES
3776 lgdt [si + 0x08]
3777 SEG CS
3778 lidt [pmode_IDT_info]
3779 ;; perhaps do something with IDT here
3781 ;; set PE bit in CR0
3782 mov eax, cr0
3783 or al, #0x01
3784 mov cr0, eax
3785 ;; far jump to flush CPU queue after transition to protected mode
3786 JMP_AP(0x0020, protected_mode)
3788 protected_mode:
3789 ;; GDT points to valid descriptor table, now load SS, DS, ES
3790 mov ax, #0x28 ;; 101 000 = 5th descriptor in table, TI=GDT, RPL=00
3791 mov ss, ax
3792 mov ax, #0x10 ;; 010 000 = 2nd descriptor in table, TI=GDT, RPL=00
3793 mov ds, ax
3794 mov ax, #0x18 ;; 011 000 = 3rd descriptor in table, TI=GDT, RPL=00
3795 mov es, ax
3796 xor si, si
3797 xor di, di
3800 movsw ;; move CX words from DS:SI to ES:DI
3802 ;; make sure DS and ES limits are 64KB
3803 mov ax, #0x28
3804 mov ds, ax
3805 mov es, ax
3807 ;; reset PG bit in CR0 ???
3808 mov eax, cr0
3809 and al, #0xFE
3810 mov cr0, eax
3812 ;; far jump to flush CPU queue after transition to real mode
3813 JMP_AP(0xf000, real_mode)
3815 real_mode:
3816 ;; restore IDT to normal real-mode defaults
3817 SEG CS
3818 lidt [rmode_IDT_info]
3820 // restore SS:SP from the BDA
3821 xor ax, ax
3822 mov ds, ax
3823 mov ss, 0x0469
3824 mov sp, 0x0467
3825 pop eax
3826 ASM_END
3828 set_enable_a20(prev_a20_enable);
3830 // turn back on interrupts
3831 ASM_START
3833 ASM_END
3835 regs.u.r8.ah = 0;
3836 CLEAR_CF();
3837 break;
3840 case 0x88:
3841 // Get the amount of extended memory (above 1M)
3842 #if BX_CPU < 2
3843 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3844 SET_CF();
3845 #else
3846 regs.u.r8.al = inb_cmos(0x30);
3847 regs.u.r8.ah = inb_cmos(0x31);
3849 // According to Ralf Brown's interrupt the limit should be 15M,
3850 // but real machines mostly return max. 63M.
3851 if(regs.u.r16.ax > 0xffc0)
3852 regs.u.r16.ax = 0xffc0;
3854 CLEAR_CF();
3855 #endif
3856 break;
3858 case 0x90:
3859 /* Device busy interrupt. Called by Int 16h when no key available */
3860 break;
3862 case 0x91:
3863 /* Interrupt complete. Called by Int 16h when key becomes available */
3864 break;
3866 case 0xbf:
3867 BX_INFO("*** int 15h function AH=bf not yet supported!\n");
3868 SET_CF();
3869 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3870 break;
3872 case 0xC0:
3873 #if 0
3874 SET_CF();
3875 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3876 break;
3877 #endif
3878 CLEAR_CF();
3879 regs.u.r8.ah = 0;
3880 regs.u.r16.bx = BIOS_CONFIG_TABLE;
3881 ES = 0xF000;
3882 break;
3884 case 0xc1:
3885 ES = ebda_seg;
3886 CLEAR_CF();
3887 break;
3889 case 0xd8:
3890 bios_printf(BIOS_PRINTF_DEBUG, "EISA BIOS not present\n");
3891 SET_CF();
3892 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3893 break;
3895 default:
3896 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
3897 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
3898 SET_CF();
3899 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3900 break;
3904 #if BX_USE_PS2_MOUSE
3905 void
3906 int15_function_mouse(regs, ES, DS, FLAGS)
3907 pusha_regs_t regs; // REGS pushed via pusha
3908 Bit16u ES, DS, FLAGS;
3910 Bit16u ebda_seg=read_word(0x0040,0x000E);
3911 Bit8u mouse_flags_1, mouse_flags_2;
3912 Bit16u mouse_driver_seg;
3913 Bit16u mouse_driver_offset;
3914 Bit8u comm_byte, prev_command_byte;
3915 Bit8u ret, mouse_data1, mouse_data2, mouse_data3;
3917 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3919 switch (regs.u.r8.ah) {
3920 case 0xC2:
3921 // Return Codes status in AH
3922 // =========================
3923 // 00: success
3924 // 01: invalid subfunction (AL > 7)
3925 // 02: invalid input value (out of allowable range)
3926 // 03: interface error
3927 // 04: resend command received from mouse controller,
3928 // device driver should attempt command again
3929 // 05: cannot enable mouse, since no far call has been installed
3930 // 80/86: mouse service not implemented
3932 switch (regs.u.r8.al) {
3933 case 0: // Disable/Enable Mouse
3934 BX_DEBUG_INT15("case 0:\n");
3935 switch (regs.u.r8.bh) {
3936 case 0: // Disable Mouse
3937 BX_DEBUG_INT15("case 0: disable mouse\n");
3938 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3939 ret = send_to_mouse_ctrl(0xF5); // disable mouse command
3940 if (ret == 0) {
3941 ret = get_mouse_data(&mouse_data1);
3942 if ( (ret == 0) || (mouse_data1 == 0xFA) ) {
3943 CLEAR_CF();
3944 regs.u.r8.ah = 0;
3945 return;
3949 // error
3950 SET_CF();
3951 regs.u.r8.ah = ret;
3952 return;
3953 break;
3955 case 1: // Enable Mouse
3956 BX_DEBUG_INT15("case 1: enable mouse\n");
3957 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
3958 if ( (mouse_flags_2 & 0x80) == 0 ) {
3959 BX_DEBUG_INT15("INT 15h C2 Enable Mouse, no far call handler\n");
3960 SET_CF(); // error
3961 regs.u.r8.ah = 5; // no far call installed
3962 return;
3964 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3965 ret = send_to_mouse_ctrl(0xF4); // enable mouse command
3966 if (ret == 0) {
3967 ret = get_mouse_data(&mouse_data1);
3968 if ( (ret == 0) && (mouse_data1 == 0xFA) ) {
3969 enable_mouse_int_and_events(); // turn IRQ12 and packet generation on
3970 CLEAR_CF();
3971 regs.u.r8.ah = 0;
3972 return;
3975 SET_CF();
3976 regs.u.r8.ah = ret;
3977 return;
3979 default: // invalid subfunction
3980 BX_DEBUG_INT15("INT 15h C2 AL=0, BH=%02x\n", (unsigned) regs.u.r8.bh);
3981 SET_CF(); // error
3982 regs.u.r8.ah = 1; // invalid subfunction
3983 return;
3985 break;
3987 case 1: // Reset Mouse
3988 case 5: // Initialize Mouse
3989 BX_DEBUG_INT15("case 1 or 5:\n");
3990 if (regs.u.r8.al == 5) {
3991 if (regs.u.r8.bh != 3) {
3992 SET_CF();
3993 regs.u.r8.ah = 0x02; // invalid input
3994 return;
3996 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
3997 mouse_flags_2 = (mouse_flags_2 & 0x00) | regs.u.r8.bh;
3998 mouse_flags_1 = 0x00;
3999 write_byte(ebda_seg, 0x0026, mouse_flags_1);
4000 write_byte(ebda_seg, 0x0027, mouse_flags_2);
4003 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4004 ret = send_to_mouse_ctrl(0xFF); // reset mouse command
4005 if (ret == 0) {
4006 ret = get_mouse_data(&mouse_data3);
4007 // if no mouse attached, it will return RESEND
4008 if (mouse_data3 == 0xfe) {
4009 SET_CF();
4010 return;
4012 if (mouse_data3 != 0xfa)
4013 BX_PANIC("Mouse reset returned %02x (should be ack)\n", (unsigned)mouse_data3);
4014 if ( ret == 0 ) {
4015 ret = get_mouse_data(&mouse_data1);
4016 if ( ret == 0 ) {
4017 ret = get_mouse_data(&mouse_data2);
4018 if ( ret == 0 ) {
4019 // turn IRQ12 and packet generation on
4020 enable_mouse_int_and_events();
4021 CLEAR_CF();
4022 regs.u.r8.ah = 0;
4023 regs.u.r8.bl = mouse_data1;
4024 regs.u.r8.bh = mouse_data2;
4025 return;
4031 // error
4032 SET_CF();
4033 regs.u.r8.ah = ret;
4034 return;
4036 case 2: // Set Sample Rate
4037 BX_DEBUG_INT15("case 2:\n");
4038 switch (regs.u.r8.bh) {
4039 case 0: mouse_data1 = 10; break; // 10 reports/sec
4040 case 1: mouse_data1 = 20; break; // 20 reports/sec
4041 case 2: mouse_data1 = 40; break; // 40 reports/sec
4042 case 3: mouse_data1 = 60; break; // 60 reports/sec
4043 case 4: mouse_data1 = 80; break; // 80 reports/sec
4044 case 5: mouse_data1 = 100; break; // 100 reports/sec (default)
4045 case 6: mouse_data1 = 200; break; // 200 reports/sec
4046 default: mouse_data1 = 0;
4048 if (mouse_data1 > 0) {
4049 ret = send_to_mouse_ctrl(0xF3); // set sample rate command
4050 if (ret == 0) {
4051 ret = get_mouse_data(&mouse_data2);
4052 ret = send_to_mouse_ctrl(mouse_data1);
4053 ret = get_mouse_data(&mouse_data2);
4054 CLEAR_CF();
4055 regs.u.r8.ah = 0;
4056 } else {
4057 // error
4058 SET_CF();
4059 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4061 } else {
4062 // error
4063 SET_CF();
4064 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4066 break;
4068 case 3: // Set Resolution
4069 BX_DEBUG_INT15("case 3:\n");
4070 // BH:
4071 // 0 = 25 dpi, 1 count per millimeter
4072 // 1 = 50 dpi, 2 counts per millimeter
4073 // 2 = 100 dpi, 4 counts per millimeter
4074 // 3 = 200 dpi, 8 counts per millimeter
4075 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4076 if (regs.u.r8.bh < 4) {
4077 ret = send_to_mouse_ctrl(0xE8); // set resolution command
4078 if (ret == 0) {
4079 ret = get_mouse_data(&mouse_data1);
4080 if (mouse_data1 != 0xfa)
4081 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
4082 ret = send_to_mouse_ctrl(regs.u.r8.bh);
4083 ret = get_mouse_data(&mouse_data1);
4084 if (mouse_data1 != 0xfa)
4085 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
4086 CLEAR_CF();
4087 regs.u.r8.ah = 0;
4088 } else {
4089 // error
4090 SET_CF();
4091 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4093 } else {
4094 // error
4095 SET_CF();
4096 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4098 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4099 break;
4101 case 4: // Get Device ID
4102 BX_DEBUG_INT15("case 4:\n");
4103 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4104 ret = send_to_mouse_ctrl(0xF2); // get mouse ID command
4105 if (ret == 0) {
4106 ret = get_mouse_data(&mouse_data1);
4107 ret = get_mouse_data(&mouse_data2);
4108 CLEAR_CF();
4109 regs.u.r8.ah = 0;
4110 regs.u.r8.bh = mouse_data2;
4111 } else {
4112 // error
4113 SET_CF();
4114 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4116 break;
4118 case 6: // Return Status & Set Scaling Factor...
4119 BX_DEBUG_INT15("case 6:\n");
4120 switch (regs.u.r8.bh) {
4121 case 0: // Return Status
4122 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4123 ret = send_to_mouse_ctrl(0xE9); // get mouse info command
4124 if (ret == 0) {
4125 ret = get_mouse_data(&mouse_data1);
4126 if (mouse_data1 != 0xfa)
4127 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
4128 if (ret == 0) {
4129 ret = get_mouse_data(&mouse_data1);
4130 if ( ret == 0 ) {
4131 ret = get_mouse_data(&mouse_data2);
4132 if ( ret == 0 ) {
4133 ret = get_mouse_data(&mouse_data3);
4134 if ( ret == 0 ) {
4135 CLEAR_CF();
4136 regs.u.r8.ah = 0;
4137 regs.u.r8.bl = mouse_data1;
4138 regs.u.r8.cl = mouse_data2;
4139 regs.u.r8.dl = mouse_data3;
4140 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4141 return;
4148 // error
4149 SET_CF();
4150 regs.u.r8.ah = ret;
4151 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4152 return;
4154 case 1: // Set Scaling Factor to 1:1
4155 case 2: // Set Scaling Factor to 2:1
4156 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4157 if (regs.u.r8.bh == 1) {
4158 ret = send_to_mouse_ctrl(0xE6);
4159 } else {
4160 ret = send_to_mouse_ctrl(0xE7);
4162 if (ret == 0) {
4163 get_mouse_data(&mouse_data1);
4164 ret = (mouse_data1 != 0xFA);
4166 if (ret == 0) {
4167 CLEAR_CF();
4168 regs.u.r8.ah = 0;
4169 } else {
4170 // error
4171 SET_CF();
4172 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4174 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4175 break;
4177 default:
4178 BX_PANIC("INT 15h C2 AL=6, BH=%02x\n", (unsigned) regs.u.r8.bh);
4180 break;
4182 case 7: // Set Mouse Handler Address
4183 BX_DEBUG_INT15("case 7:\n");
4184 mouse_driver_seg = ES;
4185 mouse_driver_offset = regs.u.r16.bx;
4186 write_word(ebda_seg, 0x0022, mouse_driver_offset);
4187 write_word(ebda_seg, 0x0024, mouse_driver_seg);
4188 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4189 if (mouse_driver_offset == 0 && mouse_driver_seg == 0) {
4190 /* remove handler */
4191 if ( (mouse_flags_2 & 0x80) != 0 ) {
4192 mouse_flags_2 &= ~0x80;
4193 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4196 else {
4197 /* install handler */
4198 mouse_flags_2 |= 0x80;
4200 write_byte(ebda_seg, 0x0027, mouse_flags_2);
4201 CLEAR_CF();
4202 regs.u.r8.ah = 0;
4203 break;
4205 default:
4206 BX_DEBUG_INT15("case default:\n");
4207 regs.u.r8.ah = 1; // invalid function
4208 SET_CF();
4210 break;
4212 default:
4213 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4214 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4215 SET_CF();
4216 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4217 break;
4220 #endif
4223 void set_e820_range(ES, DI, start, end, type)
4224 Bit16u ES;
4225 Bit16u DI;
4226 Bit32u start;
4227 Bit32u end;
4228 Bit16u type;
4230 write_word(ES, DI, start);
4231 write_word(ES, DI+2, start >> 16);
4232 write_word(ES, DI+4, 0x00);
4233 write_word(ES, DI+6, 0x00);
4235 end -= start;
4236 write_word(ES, DI+8, end);
4237 write_word(ES, DI+10, end >> 16);
4238 write_word(ES, DI+12, 0x0000);
4239 write_word(ES, DI+14, 0x0000);
4241 write_word(ES, DI+16, type);
4242 write_word(ES, DI+18, 0x0);
4245 void
4246 int15_function32(regs, ES, DS, FLAGS)
4247 pushad_regs_t regs; // REGS pushed via pushad
4248 Bit16u ES, DS, FLAGS;
4250 Bit32u extended_memory_size=0; // 64bits long
4251 Bit16u CX,DX;
4253 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
4255 switch (regs.u.r8.ah) {
4256 case 0x86:
4257 // Wait for CX:DX microseconds. currently using the
4258 // refresh request port 0x61 bit4, toggling every 15usec
4260 CX = regs.u.r16.cx;
4261 DX = regs.u.r16.dx;
4263 ASM_START
4266 ;; Get the count in eax
4267 mov bx, sp
4268 SEG SS
4269 mov ax, _int15_function32.CX [bx]
4270 shl eax, #16
4271 SEG SS
4272 mov ax, _int15_function32.DX [bx]
4274 ;; convert to numbers of 15usec ticks
4275 mov ebx, #15
4276 xor edx, edx
4277 div eax, ebx
4278 mov ecx, eax
4280 ;; wait for ecx number of refresh requests
4281 in al, #0x61
4282 and al,#0x10
4283 mov ah, al
4285 or ecx, ecx
4286 je int1586_tick_end
4287 int1586_tick:
4288 in al, #0x61
4289 and al,#0x10
4290 cmp al, ah
4291 je int1586_tick
4292 mov ah, al
4293 dec ecx
4294 jnz int1586_tick
4295 int1586_tick_end:
4296 ASM_END
4298 break;
4300 case 0xe8:
4301 switch(regs.u.r8.al)
4303 case 0x20: // coded by osmaker aka K.J.
4304 if(regs.u.r32.edx == 0x534D4150)
4306 extended_memory_size = inb_cmos(0x35);
4307 extended_memory_size <<= 8;
4308 extended_memory_size |= inb_cmos(0x34);
4309 extended_memory_size *= 64;
4310 // greater than EFF00000???
4311 if(extended_memory_size > 0x3bc000) {
4312 extended_memory_size = 0x3bc000; // everything after this is reserved memory until we get to 0x100000000
4314 extended_memory_size *= 1024;
4315 extended_memory_size += (16L * 1024 * 1024);
4317 if(extended_memory_size <= (16L * 1024 * 1024)) {
4318 extended_memory_size = inb_cmos(0x31);
4319 extended_memory_size <<= 8;
4320 extended_memory_size |= inb_cmos(0x30);
4321 extended_memory_size *= 1024;
4324 switch(regs.u.r16.bx)
4326 case 0:
4327 set_e820_range(ES, regs.u.r16.di,
4328 0x0000000L, 0x0009fc00L, 1);
4329 regs.u.r32.ebx = 1;
4330 regs.u.r32.eax = 0x534D4150;
4331 regs.u.r32.ecx = 0x14;
4332 CLEAR_CF();
4333 return;
4334 break;
4335 case 1:
4336 set_e820_range(ES, regs.u.r16.di,
4337 0x0009fc00L, 0x000a0000L, 2);
4338 regs.u.r32.ebx = 2;
4339 regs.u.r32.eax = 0x534D4150;
4340 regs.u.r32.ecx = 0x14;
4341 CLEAR_CF();
4342 return;
4343 break;
4344 case 2:
4345 set_e820_range(ES, regs.u.r16.di,
4346 0x000e8000L, 0x00100000L, 2);
4347 regs.u.r32.ebx = 3;
4348 regs.u.r32.eax = 0x534D4150;
4349 regs.u.r32.ecx = 0x14;
4350 CLEAR_CF();
4351 return;
4352 break;
4353 case 3:
4354 set_e820_range(ES, regs.u.r16.di,
4355 0x00100000L,
4356 extended_memory_size - ACPI_DATA_SIZE, 1);
4357 regs.u.r32.ebx = 4;
4358 regs.u.r32.eax = 0x534D4150;
4359 regs.u.r32.ecx = 0x14;
4360 CLEAR_CF();
4361 return;
4362 break;
4363 case 4:
4364 set_e820_range(ES, regs.u.r16.di,
4365 extended_memory_size - ACPI_DATA_SIZE,
4366 extended_memory_size, 3); // ACPI RAM
4367 regs.u.r32.ebx = 5;
4368 regs.u.r32.eax = 0x534D4150;
4369 regs.u.r32.ecx = 0x14;
4370 CLEAR_CF();
4371 return;
4372 break;
4373 case 5:
4374 /* 256KB BIOS area at the end of 4 GB */
4375 set_e820_range(ES, regs.u.r16.di,
4376 0xfffc0000L, 0x00000000L, 2);
4377 regs.u.r32.ebx = 0;
4378 regs.u.r32.eax = 0x534D4150;
4379 regs.u.r32.ecx = 0x14;
4380 CLEAR_CF();
4381 return;
4382 default: /* AX=E820, DX=534D4150, BX unrecognized */
4383 goto int15_unimplemented;
4384 break;
4386 } else {
4387 // if DX != 0x534D4150)
4388 goto int15_unimplemented;
4390 break;
4392 case 0x01:
4393 // do we have any reason to fail here ?
4394 CLEAR_CF();
4396 // my real system sets ax and bx to 0
4397 // this is confirmed by Ralph Brown list
4398 // but syslinux v1.48 is known to behave
4399 // strangely if ax is set to 0
4400 // regs.u.r16.ax = 0;
4401 // regs.u.r16.bx = 0;
4403 // Get the amount of extended memory (above 1M)
4404 regs.u.r8.cl = inb_cmos(0x30);
4405 regs.u.r8.ch = inb_cmos(0x31);
4407 // limit to 15M
4408 if(regs.u.r16.cx > 0x3c00)
4410 regs.u.r16.cx = 0x3c00;
4413 // Get the amount of extended memory above 16M in 64k blocs
4414 regs.u.r8.dl = inb_cmos(0x34);
4415 regs.u.r8.dh = inb_cmos(0x35);
4417 // Set configured memory equal to extended memory
4418 regs.u.r16.ax = regs.u.r16.cx;
4419 regs.u.r16.bx = regs.u.r16.dx;
4420 break;
4421 default: /* AH=0xE8?? but not implemented */
4422 goto int15_unimplemented;
4424 break;
4425 int15_unimplemented:
4426 // fall into the default
4427 default:
4428 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4429 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4430 SET_CF();
4431 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4432 break;
4436 void
4437 int16_function(DI, SI, BP, SP, BX, DX, CX, AX, FLAGS)
4438 Bit16u DI, SI, BP, SP, BX, DX, CX, AX, FLAGS;
4440 Bit8u scan_code, ascii_code, shift_flags, led_flags, count;
4441 Bit16u kbd_code, max;
4443 BX_DEBUG_INT16("int16: AX=%04x BX=%04x CX=%04x DX=%04x \n", AX, BX, CX, DX);
4445 shift_flags = read_byte(0x0040, 0x17);
4446 led_flags = read_byte(0x0040, 0x97);
4447 if ((((shift_flags >> 4) & 0x07) ^ (led_flags & 0x07)) != 0) {
4448 ASM_START
4450 ASM_END
4451 outb(0x60, 0xed);
4452 while ((inb(0x64) & 0x01) == 0) outb(0x80, 0x21);
4453 if ((inb(0x60) == 0xfa)) {
4454 led_flags &= 0xf8;
4455 led_flags |= ((shift_flags >> 4) & 0x07);
4456 outb(0x60, led_flags & 0x07);
4457 while ((inb(0x64) & 0x01) == 0) outb(0x80, 0x21);
4458 inb(0x60);
4459 write_byte(0x0040, 0x97, led_flags);
4461 ASM_START
4463 ASM_END
4466 switch (GET_AH()) {
4467 case 0x00: /* read keyboard input */
4469 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4470 BX_PANIC("KBD: int16h: out of keyboard input\n");
4472 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4473 else if (ascii_code == 0xE0) ascii_code = 0;
4474 AX = (scan_code << 8) | ascii_code;
4475 break;
4477 case 0x01: /* check keyboard status */
4478 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4479 SET_ZF();
4480 return;
4482 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4483 else if (ascii_code == 0xE0) ascii_code = 0;
4484 AX = (scan_code << 8) | ascii_code;
4485 CLEAR_ZF();
4486 break;
4488 case 0x02: /* get shift flag status */
4489 shift_flags = read_byte(0x0040, 0x17);
4490 SET_AL(shift_flags);
4491 break;
4493 case 0x05: /* store key-stroke into buffer */
4494 if ( !enqueue_key(GET_CH(), GET_CL()) ) {
4495 SET_AL(1);
4497 else {
4498 SET_AL(0);
4500 break;
4502 case 0x09: /* GET KEYBOARD FUNCTIONALITY */
4503 // bit Bochs Description
4504 // 7 0 reserved
4505 // 6 0 INT 16/AH=20h-22h supported (122-key keyboard support)
4506 // 5 1 INT 16/AH=10h-12h supported (enhanced keyboard support)
4507 // 4 1 INT 16/AH=0Ah supported
4508 // 3 0 INT 16/AX=0306h supported
4509 // 2 0 INT 16/AX=0305h supported
4510 // 1 0 INT 16/AX=0304h supported
4511 // 0 0 INT 16/AX=0300h supported
4513 SET_AL(0x30);
4514 break;
4516 case 0x0A: /* GET KEYBOARD ID */
4517 count = 2;
4518 kbd_code = 0x0;
4519 outb(0x60, 0xf2);
4520 /* Wait for data */
4521 max=0xffff;
4522 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4523 if (max>0x0) {
4524 if ((inb(0x60) == 0xfa)) {
4525 do {
4526 max=0xffff;
4527 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4528 if (max>0x0) {
4529 kbd_code >>= 8;
4530 kbd_code |= (inb(0x60) << 8);
4532 } while (--count>0);
4535 BX=kbd_code;
4536 break;
4538 case 0x10: /* read MF-II keyboard input */
4540 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4541 BX_PANIC("KBD: int16h: out of keyboard input\n");
4543 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4544 AX = (scan_code << 8) | ascii_code;
4545 break;
4547 case 0x11: /* check MF-II keyboard status */
4548 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4549 SET_ZF();
4550 return;
4552 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4553 AX = (scan_code << 8) | ascii_code;
4554 CLEAR_ZF();
4555 break;
4557 case 0x12: /* get extended keyboard status */
4558 shift_flags = read_byte(0x0040, 0x17);
4559 SET_AL(shift_flags);
4560 shift_flags = read_byte(0x0040, 0x18) & 0x73;
4561 shift_flags |= read_byte(0x0040, 0x96) & 0x0c;
4562 SET_AH(shift_flags);
4563 BX_DEBUG_INT16("int16: func 12 sending %04x\n",AX);
4564 break;
4566 case 0x92: /* keyboard capability check called by DOS 5.0+ keyb */
4567 SET_AH(0x80); // function int16 ah=0x10-0x12 supported
4568 break;
4570 case 0xA2: /* 122 keys capability check called by DOS 5.0+ keyb */
4571 // don't change AH : function int16 ah=0x20-0x22 NOT supported
4572 break;
4574 case 0x6F:
4575 if (GET_AL() == 0x08)
4576 SET_AH(0x02); // unsupported, aka normal keyboard
4578 default:
4579 BX_INFO("KBD: unsupported int 16h function %02x\n", GET_AH());
4583 unsigned int
4584 dequeue_key(scan_code, ascii_code, incr)
4585 Bit8u *scan_code;
4586 Bit8u *ascii_code;
4587 unsigned int incr;
4589 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail;
4590 Bit16u ss;
4591 Bit8u acode, scode;
4593 #if BX_CPU < 2
4594 buffer_start = 0x001E;
4595 buffer_end = 0x003E;
4596 #else
4597 buffer_start = read_word(0x0040, 0x0080);
4598 buffer_end = read_word(0x0040, 0x0082);
4599 #endif
4601 buffer_head = read_word(0x0040, 0x001a);
4602 buffer_tail = read_word(0x0040, 0x001c);
4604 if (buffer_head != buffer_tail) {
4605 ss = get_SS();
4606 acode = read_byte(0x0040, buffer_head);
4607 scode = read_byte(0x0040, buffer_head+1);
4608 write_byte(ss, ascii_code, acode);
4609 write_byte(ss, scan_code, scode);
4611 if (incr) {
4612 buffer_head += 2;
4613 if (buffer_head >= buffer_end)
4614 buffer_head = buffer_start;
4615 write_word(0x0040, 0x001a, buffer_head);
4617 return(1);
4619 else {
4620 return(0);
4624 static char panic_msg_keyb_buffer_full[] = "%s: keyboard input buffer full\n";
4626 Bit8u
4627 inhibit_mouse_int_and_events()
4629 Bit8u command_byte, prev_command_byte;
4631 // Turn off IRQ generation and aux data line
4632 if ( inb(0x64) & 0x02 )
4633 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4634 outb(0x64, 0x20); // get command byte
4635 while ( (inb(0x64) & 0x01) != 0x01 );
4636 prev_command_byte = inb(0x60);
4637 command_byte = prev_command_byte;
4638 //while ( (inb(0x64) & 0x02) );
4639 if ( inb(0x64) & 0x02 )
4640 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4641 command_byte &= 0xfd; // turn off IRQ 12 generation
4642 command_byte |= 0x20; // disable mouse serial clock line
4643 outb(0x64, 0x60); // write command byte
4644 outb(0x60, command_byte);
4645 return(prev_command_byte);
4648 void
4649 enable_mouse_int_and_events()
4651 Bit8u command_byte;
4653 // Turn on IRQ generation and aux data line
4654 if ( inb(0x64) & 0x02 )
4655 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4656 outb(0x64, 0x20); // get command byte
4657 while ( (inb(0x64) & 0x01) != 0x01 );
4658 command_byte = inb(0x60);
4659 //while ( (inb(0x64) & 0x02) );
4660 if ( inb(0x64) & 0x02 )
4661 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4662 command_byte |= 0x02; // turn on IRQ 12 generation
4663 command_byte &= 0xdf; // enable mouse serial clock line
4664 outb(0x64, 0x60); // write command byte
4665 outb(0x60, command_byte);
4668 Bit8u
4669 send_to_mouse_ctrl(sendbyte)
4670 Bit8u sendbyte;
4672 Bit8u response;
4674 // wait for chance to write to ctrl
4675 if ( inb(0x64) & 0x02 )
4676 BX_PANIC(panic_msg_keyb_buffer_full,"sendmouse");
4677 outb(0x64, 0xD4);
4678 outb(0x60, sendbyte);
4679 return(0);
4683 Bit8u
4684 get_mouse_data(data)
4685 Bit8u *data;
4687 Bit8u response;
4688 Bit16u ss;
4690 while ( (inb(0x64) & 0x21) != 0x21 ) {
4693 response = inb(0x60);
4695 ss = get_SS();
4696 write_byte(ss, data, response);
4697 return(0);
4700 void
4701 set_kbd_command_byte(command_byte)
4702 Bit8u command_byte;
4704 if ( inb(0x64) & 0x02 )
4705 BX_PANIC(panic_msg_keyb_buffer_full,"setkbdcomm");
4706 outb(0x64, 0xD4);
4708 outb(0x64, 0x60); // write command byte
4709 outb(0x60, command_byte);
4712 void
4713 int09_function(DI, SI, BP, SP, BX, DX, CX, AX)
4714 Bit16u DI, SI, BP, SP, BX, DX, CX, AX;
4716 Bit8u scancode, asciicode, shift_flags;
4717 Bit8u mf2_flags, mf2_state;
4720 // DS has been set to F000 before call
4724 scancode = GET_AL();
4726 if (scancode == 0) {
4727 BX_INFO("KBD: int09 handler: AL=0\n");
4728 return;
4732 shift_flags = read_byte(0x0040, 0x17);
4733 mf2_flags = read_byte(0x0040, 0x18);
4734 mf2_state = read_byte(0x0040, 0x96);
4735 asciicode = 0;
4737 switch (scancode) {
4738 case 0x3a: /* Caps Lock press */
4739 shift_flags ^= 0x40;
4740 write_byte(0x0040, 0x17, shift_flags);
4741 mf2_flags |= 0x40;
4742 write_byte(0x0040, 0x18, mf2_flags);
4743 break;
4744 case 0xba: /* Caps Lock release */
4745 mf2_flags &= ~0x40;
4746 write_byte(0x0040, 0x18, mf2_flags);
4747 break;
4749 case 0x2a: /* L Shift press */
4750 shift_flags |= 0x02;
4751 write_byte(0x0040, 0x17, shift_flags);
4752 break;
4753 case 0xaa: /* L Shift release */
4754 shift_flags &= ~0x02;
4755 write_byte(0x0040, 0x17, shift_flags);
4756 break;
4758 case 0x36: /* R Shift press */
4759 shift_flags |= 0x01;
4760 write_byte(0x0040, 0x17, shift_flags);
4761 break;
4762 case 0xb6: /* R Shift release */
4763 shift_flags &= ~0x01;
4764 write_byte(0x0040, 0x17, shift_flags);
4765 break;
4767 case 0x1d: /* Ctrl press */
4768 if ((mf2_state & 0x01) == 0) {
4769 shift_flags |= 0x04;
4770 write_byte(0x0040, 0x17, shift_flags);
4771 if (mf2_state & 0x02) {
4772 mf2_state |= 0x04;
4773 write_byte(0x0040, 0x96, mf2_state);
4774 } else {
4775 mf2_flags |= 0x01;
4776 write_byte(0x0040, 0x18, mf2_flags);
4779 break;
4780 case 0x9d: /* Ctrl release */
4781 if ((mf2_state & 0x01) == 0) {
4782 shift_flags &= ~0x04;
4783 write_byte(0x0040, 0x17, shift_flags);
4784 if (mf2_state & 0x02) {
4785 mf2_state &= ~0x04;
4786 write_byte(0x0040, 0x96, mf2_state);
4787 } else {
4788 mf2_flags &= ~0x01;
4789 write_byte(0x0040, 0x18, mf2_flags);
4792 break;
4794 case 0x38: /* Alt press */
4795 shift_flags |= 0x08;
4796 write_byte(0x0040, 0x17, shift_flags);
4797 if (mf2_state & 0x02) {
4798 mf2_state |= 0x08;
4799 write_byte(0x0040, 0x96, mf2_state);
4800 } else {
4801 mf2_flags |= 0x02;
4802 write_byte(0x0040, 0x18, mf2_flags);
4804 break;
4805 case 0xb8: /* Alt release */
4806 shift_flags &= ~0x08;
4807 write_byte(0x0040, 0x17, shift_flags);
4808 if (mf2_state & 0x02) {
4809 mf2_state &= ~0x08;
4810 write_byte(0x0040, 0x96, mf2_state);
4811 } else {
4812 mf2_flags &= ~0x02;
4813 write_byte(0x0040, 0x18, mf2_flags);
4815 break;
4817 case 0x45: /* Num Lock press */
4818 if ((mf2_state & 0x03) == 0) {
4819 mf2_flags |= 0x20;
4820 write_byte(0x0040, 0x18, mf2_flags);
4821 shift_flags ^= 0x20;
4822 write_byte(0x0040, 0x17, shift_flags);
4824 break;
4825 case 0xc5: /* Num Lock release */
4826 if ((mf2_state & 0x03) == 0) {
4827 mf2_flags &= ~0x20;
4828 write_byte(0x0040, 0x18, mf2_flags);
4830 break;
4832 case 0x46: /* Scroll Lock press */
4833 mf2_flags |= 0x10;
4834 write_byte(0x0040, 0x18, mf2_flags);
4835 shift_flags ^= 0x10;
4836 write_byte(0x0040, 0x17, shift_flags);
4837 break;
4839 case 0xc6: /* Scroll Lock release */
4840 mf2_flags &= ~0x10;
4841 write_byte(0x0040, 0x18, mf2_flags);
4842 break;
4844 default:
4845 if (scancode & 0x80) {
4846 break; /* toss key releases ... */
4848 if (scancode > MAX_SCAN_CODE) {
4849 BX_INFO("KBD: int09h_handler(): unknown scancode read: 0x%02x!\n", scancode);
4850 return;
4852 if (shift_flags & 0x08) { /* ALT */
4853 asciicode = scan_to_scanascii[scancode].alt;
4854 scancode = scan_to_scanascii[scancode].alt >> 8;
4855 } else if (shift_flags & 0x04) { /* CONTROL */
4856 asciicode = scan_to_scanascii[scancode].control;
4857 scancode = scan_to_scanascii[scancode].control >> 8;
4858 } else if (((mf2_state & 0x02) > 0) && ((scancode >= 0x47) && (scancode <= 0x53))) {
4859 /* extended keys handling */
4860 asciicode = 0xe0;
4861 scancode = scan_to_scanascii[scancode].normal >> 8;
4862 } else if (shift_flags & 0x03) { /* LSHIFT + RSHIFT */
4863 /* check if lock state should be ignored
4864 * because a SHIFT key are pressed */
4866 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
4867 asciicode = scan_to_scanascii[scancode].normal;
4868 scancode = scan_to_scanascii[scancode].normal >> 8;
4869 } else {
4870 asciicode = scan_to_scanascii[scancode].shift;
4871 scancode = scan_to_scanascii[scancode].shift >> 8;
4873 } else {
4874 /* check if lock is on */
4875 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
4876 asciicode = scan_to_scanascii[scancode].shift;
4877 scancode = scan_to_scanascii[scancode].shift >> 8;
4878 } else {
4879 asciicode = scan_to_scanascii[scancode].normal;
4880 scancode = scan_to_scanascii[scancode].normal >> 8;
4883 if (scancode==0 && asciicode==0) {
4884 BX_INFO("KBD: int09h_handler(): scancode & asciicode are zero?\n");
4886 enqueue_key(scancode, asciicode);
4887 break;
4889 if ((scancode & 0x7f) != 0x1d) {
4890 mf2_state &= ~0x01;
4892 mf2_state &= ~0x02;
4893 write_byte(0x0040, 0x96, mf2_state);
4896 unsigned int
4897 enqueue_key(scan_code, ascii_code)
4898 Bit8u scan_code, ascii_code;
4900 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail, temp_tail;
4902 #if BX_CPU < 2
4903 buffer_start = 0x001E;
4904 buffer_end = 0x003E;
4905 #else
4906 buffer_start = read_word(0x0040, 0x0080);
4907 buffer_end = read_word(0x0040, 0x0082);
4908 #endif
4910 buffer_head = read_word(0x0040, 0x001A);
4911 buffer_tail = read_word(0x0040, 0x001C);
4913 temp_tail = buffer_tail;
4914 buffer_tail += 2;
4915 if (buffer_tail >= buffer_end)
4916 buffer_tail = buffer_start;
4918 if (buffer_tail == buffer_head) {
4919 return(0);
4922 write_byte(0x0040, temp_tail, ascii_code);
4923 write_byte(0x0040, temp_tail+1, scan_code);
4924 write_word(0x0040, 0x001C, buffer_tail);
4925 return(1);
4929 void
4930 int74_function(make_farcall, Z, Y, X, status)
4931 Bit16u make_farcall, Z, Y, X, status;
4933 Bit16u ebda_seg=read_word(0x0040,0x000E);
4934 Bit8u in_byte, index, package_count;
4935 Bit8u mouse_flags_1, mouse_flags_2;
4937 BX_DEBUG_INT74("entering int74_function\n");
4938 make_farcall = 0;
4940 in_byte = inb(0x64);
4941 if ( (in_byte & 0x21) != 0x21 ) {
4942 return;
4944 in_byte = inb(0x60);
4945 BX_DEBUG_INT74("int74: read byte %02x\n", in_byte);
4947 mouse_flags_1 = read_byte(ebda_seg, 0x0026);
4948 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4950 if ( (mouse_flags_2 & 0x80) != 0x80 ) {
4951 return;
4954 package_count = mouse_flags_2 & 0x07;
4955 index = mouse_flags_1 & 0x07;
4956 write_byte(ebda_seg, 0x28 + index, in_byte);
4958 if ( (index+1) >= package_count ) {
4959 BX_DEBUG_INT74("int74_function: make_farcall=1\n");
4960 status = read_byte(ebda_seg, 0x0028 + 0);
4961 X = read_byte(ebda_seg, 0x0028 + 1);
4962 Y = read_byte(ebda_seg, 0x0028 + 2);
4963 Z = 0;
4964 mouse_flags_1 = 0;
4965 // check if far call handler installed
4966 if (mouse_flags_2 & 0x80)
4967 make_farcall = 1;
4969 else {
4970 mouse_flags_1++;
4972 write_byte(ebda_seg, 0x0026, mouse_flags_1);
4975 #define SET_DISK_RET_STATUS(status) write_byte(0x0040, 0x0074, status)
4977 #if BX_USE_ATADRV
4979 void
4980 int13_harddisk(EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
4981 Bit16u EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
4983 Bit32u lba;
4984 Bit16u ebda_seg=read_word(0x0040,0x000E);
4985 Bit16u cylinder, head, sector;
4986 Bit16u segment, offset;
4987 Bit16u npc, nph, npspt, nlc, nlh, nlspt;
4988 Bit16u size, count;
4989 Bit8u device, status;
4991 BX_DEBUG_INT13_HD("int13_harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
4993 write_byte(0x0040, 0x008e, 0); // clear completion flag
4995 // basic check : device has to be defined
4996 if ( (GET_ELDL() < 0x80) || (GET_ELDL() >= 0x80 + BX_MAX_ATA_DEVICES) ) {
4997 BX_INFO("int13_harddisk: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
4998 goto int13_fail;
5001 // Get the ata channel
5002 device=read_byte(ebda_seg,&EbdaData->ata.hdidmap[GET_ELDL()-0x80]);
5004 // basic check : device has to be valid
5005 if (device >= BX_MAX_ATA_DEVICES) {
5006 BX_INFO("int13_harddisk: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
5007 goto int13_fail;
5010 switch (GET_AH()) {
5012 case 0x00: /* disk controller reset */
5013 ata_reset (device);
5014 goto int13_success;
5015 break;
5017 case 0x01: /* read disk status */
5018 status = read_byte(0x0040, 0x0074);
5019 SET_AH(status);
5020 SET_DISK_RET_STATUS(0);
5021 /* set CF if error status read */
5022 if (status) goto int13_fail_nostatus;
5023 else goto int13_success_noah;
5024 break;
5026 case 0x02: // read disk sectors
5027 case 0x03: // write disk sectors
5028 case 0x04: // verify disk sectors
5030 count = GET_AL();
5031 cylinder = GET_CH();
5032 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
5033 sector = (GET_CL() & 0x3f);
5034 head = GET_DH();
5036 segment = ES;
5037 offset = BX;
5039 if ((count > 128) || (count == 0) || (sector == 0)) {
5040 BX_INFO("int13_harddisk: function %02x, parameter out of range!\n",GET_AH());
5041 goto int13_fail;
5044 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
5045 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
5046 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
5048 // sanity check on cyl heads, sec
5049 if( (cylinder >= nlc) || (head >= nlh) || (sector > nlspt )) {
5050 BX_INFO("int13_harddisk: function %02x, parameters out of range %04x/%04x/%04x!\n", GET_AH(), cylinder, head, sector);
5051 goto int13_fail;
5054 // FIXME verify
5055 if ( GET_AH() == 0x04 ) goto int13_success;
5057 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
5058 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
5060 // if needed, translate lchs to lba, and execute command
5061 if ( (nph != nlh) || (npspt != nlspt)) {
5062 lba = ((((Bit32u)cylinder * (Bit32u)nlh) + (Bit32u)head) * (Bit32u)nlspt) + (Bit32u)sector - 1;
5063 sector = 0; // this forces the command to be lba
5066 if ( GET_AH() == 0x02 )
5067 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, cylinder, head, sector, lba, segment, offset);
5068 else
5069 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, cylinder, head, sector, lba, segment, offset);
5071 // Set nb of sector transferred
5072 SET_AL(read_word(ebda_seg, &EbdaData->ata.trsfsectors));
5074 if (status != 0) {
5075 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
5076 SET_AH(0x0c);
5077 goto int13_fail_noah;
5080 goto int13_success;
5081 break;
5083 case 0x05: /* format disk track */
5084 BX_INFO("format disk track called\n");
5085 goto int13_success;
5086 return;
5087 break;
5089 case 0x08: /* read disk drive parameters */
5091 // Get logical geometry from table
5092 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
5093 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
5094 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
5095 count = read_byte(ebda_seg, &EbdaData->ata.hdcount);
5097 nlc = nlc - 2; /* 0 based , last sector not used */
5098 SET_AL(0);
5099 SET_CH(nlc & 0xff);
5100 SET_CL(((nlc >> 2) & 0xc0) | (nlspt & 0x3f));
5101 SET_DH(nlh - 1);
5102 SET_DL(count); /* FIXME returns 0, 1, or n hard drives */
5104 // FIXME should set ES & DI
5106 goto int13_success;
5107 break;
5109 case 0x10: /* check drive ready */
5110 // should look at 40:8E also???
5112 // Read the status from controller
5113 status = inb(read_word(ebda_seg, &EbdaData->ata.channels[device/2].iobase1) + ATA_CB_STAT);
5114 if ( (status & ( ATA_CB_STAT_BSY | ATA_CB_STAT_RDY )) == ATA_CB_STAT_RDY ) {
5115 goto int13_success;
5117 else {
5118 SET_AH(0xAA);
5119 goto int13_fail_noah;
5121 break;
5123 case 0x15: /* read disk drive size */
5125 // Get logical geometry from table
5126 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
5127 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
5128 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
5130 // Compute sector count seen by int13
5131 lba = (Bit32u)(nlc - 1) * (Bit32u)nlh * (Bit32u)nlspt;
5132 CX = lba >> 16;
5133 DX = lba & 0xffff;
5135 SET_AH(3); // hard disk accessible
5136 goto int13_success_noah;
5137 break;
5139 case 0x41: // IBM/MS installation check
5140 BX=0xaa55; // install check
5141 SET_AH(0x30); // EDD 3.0
5142 CX=0x0007; // ext disk access and edd, removable supported
5143 goto int13_success_noah;
5144 break;
5146 case 0x42: // IBM/MS extended read
5147 case 0x43: // IBM/MS extended write
5148 case 0x44: // IBM/MS verify
5149 case 0x47: // IBM/MS extended seek
5151 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
5152 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
5153 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
5155 // Can't use 64 bits lba
5156 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
5157 if (lba != 0L) {
5158 BX_PANIC("int13_harddisk: function %02x. Can't use 64bits lba\n",GET_AH());
5159 goto int13_fail;
5162 // Get 32 bits lba and check
5163 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5164 if (lba >= read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors) ) {
5165 BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH());
5166 goto int13_fail;
5169 // If verify or seek
5170 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
5171 goto int13_success;
5173 // Execute the command
5174 if ( GET_AH() == 0x42 )
5175 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, 0, 0, 0, lba, segment, offset);
5176 else
5177 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, 0, 0, 0, lba, segment, offset);
5179 count=read_word(ebda_seg, &EbdaData->ata.trsfsectors);
5180 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5182 if (status != 0) {
5183 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
5184 SET_AH(0x0c);
5185 goto int13_fail_noah;
5188 goto int13_success;
5189 break;
5191 case 0x45: // IBM/MS lock/unlock drive
5192 case 0x49: // IBM/MS extended media change
5193 goto int13_success; // Always success for HD
5194 break;
5196 case 0x46: // IBM/MS eject media
5197 SET_AH(0xb2); // Volume Not Removable
5198 goto int13_fail_noah; // Always fail for HD
5199 break;
5201 case 0x48: // IBM/MS get drive parameters
5202 size=read_word(DS,SI+(Bit16u)&Int13DPT->size);
5204 // Buffer is too small
5205 if(size < 0x1a)
5206 goto int13_fail;
5208 // EDD 1.x
5209 if(size >= 0x1a) {
5210 Bit16u blksize;
5212 npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
5213 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
5214 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
5215 lba = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors);
5216 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5218 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5219 if ((lba/npspt)/nph > 0x3fff)
5221 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x00); // geometry is invalid
5222 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0x3fff);
5224 else
5226 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x02); // geometry is valid
5227 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, (Bit32u)npc);
5229 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, (Bit32u)nph);
5230 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, (Bit32u)npspt);
5231 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, lba); // FIXME should be Bit64
5232 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0L);
5233 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5236 // EDD 2.x
5237 if(size >= 0x1e) {
5238 Bit8u channel, dev, irq, mode, checksum, i, translation;
5239 Bit16u iobase1, iobase2, options;
5241 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5243 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5244 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5246 // Fill in dpte
5247 channel = device / 2;
5248 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5249 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5250 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5251 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5252 translation = read_byte(ebda_seg, &EbdaData->ata.devices[device].translation);
5254 options = (translation==ATA_TRANSLATION_NONE?0:1)<<3; // chs translation
5255 options |= (1<<4); // lba translation
5256 options |= (mode==ATA_MODE_PIO32?1:0)<<7;
5257 options |= (translation==ATA_TRANSLATION_LBA?1:0)<<9;
5258 options |= (translation==ATA_TRANSLATION_RECHS?3:0)<<9;
5260 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5261 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2 + ATA_CB_DC);
5262 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5263 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5264 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5265 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5266 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5267 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5268 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5269 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5270 if (size >=0x42)
5271 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5272 else
5273 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x10);
5275 checksum=0;
5276 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, ((Bit8u*)(&EbdaData->ata.dpte)) + i);
5277 checksum = ~checksum;
5278 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5281 // EDD 3.x
5282 if(size >= 0x42) {
5283 Bit8u channel, iface, checksum, i;
5284 Bit16u iobase1;
5286 channel = device / 2;
5287 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5288 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5290 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5291 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5292 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5293 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5294 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5296 if (iface==ATA_IFACE_ISA) {
5297 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5298 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5299 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5300 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5302 else {
5303 // FIXME PCI
5305 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5306 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5307 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5308 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5310 if (iface==ATA_IFACE_ISA) {
5311 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5312 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5313 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5315 else {
5316 // FIXME PCI
5318 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5319 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5320 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5321 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5323 checksum=0;
5324 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5325 checksum = ~checksum;
5326 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5329 goto int13_success;
5330 break;
5332 case 0x4e: // // IBM/MS set hardware configuration
5333 // DMA, prefetch, PIO maximum not supported
5334 switch (GET_AL()) {
5335 case 0x01:
5336 case 0x03:
5337 case 0x04:
5338 case 0x06:
5339 goto int13_success;
5340 break;
5341 default :
5342 goto int13_fail;
5344 break;
5346 case 0x09: /* initialize drive parameters */
5347 case 0x0c: /* seek to specified cylinder */
5348 case 0x0d: /* alternate disk reset */
5349 case 0x11: /* recalibrate */
5350 case 0x14: /* controller internal diagnostic */
5351 BX_INFO("int13_harddisk: function %02xh unimplemented, returns success\n", GET_AH());
5352 goto int13_success;
5353 break;
5355 case 0x0a: /* read disk sectors with ECC */
5356 case 0x0b: /* write disk sectors with ECC */
5357 case 0x18: // set media type for format
5358 case 0x50: // IBM/MS send packet command
5359 default:
5360 BX_INFO("int13_harddisk: function %02xh unsupported, returns fail\n", GET_AH());
5361 goto int13_fail;
5362 break;
5365 int13_fail:
5366 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5367 int13_fail_noah:
5368 SET_DISK_RET_STATUS(GET_AH());
5369 int13_fail_nostatus:
5370 SET_CF(); // error occurred
5371 return;
5373 int13_success:
5374 SET_AH(0x00); // no error
5375 int13_success_noah:
5376 SET_DISK_RET_STATUS(0x00);
5377 CLEAR_CF(); // no error
5378 return;
5381 // ---------------------------------------------------------------------------
5382 // Start of int13 for cdrom
5383 // ---------------------------------------------------------------------------
5385 void
5386 int13_cdrom(EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
5387 Bit16u EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
5389 Bit16u ebda_seg=read_word(0x0040,0x000E);
5390 Bit8u device, status, locks;
5391 Bit8u atacmd[12];
5392 Bit32u lba;
5393 Bit16u count, segment, offset, i, size;
5395 BX_DEBUG_INT13_CD("int13_cdrom: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5397 SET_DISK_RET_STATUS(0x00);
5399 /* basic check : device should be 0xE0+ */
5400 if( (GET_ELDL() < 0xE0) || (GET_ELDL() >= 0xE0+BX_MAX_ATA_DEVICES) ) {
5401 BX_INFO("int13_cdrom: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
5402 goto int13_fail;
5405 // Get the ata channel
5406 device=read_byte(ebda_seg,&EbdaData->ata.cdidmap[GET_ELDL()-0xE0]);
5408 /* basic check : device has to be valid */
5409 if (device >= BX_MAX_ATA_DEVICES) {
5410 BX_INFO("int13_cdrom: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
5411 goto int13_fail;
5414 switch (GET_AH()) {
5416 // all those functions return SUCCESS
5417 case 0x00: /* disk controller reset */
5418 case 0x09: /* initialize drive parameters */
5419 case 0x0c: /* seek to specified cylinder */
5420 case 0x0d: /* alternate disk reset */
5421 case 0x10: /* check drive ready */
5422 case 0x11: /* recalibrate */
5423 case 0x14: /* controller internal diagnostic */
5424 case 0x16: /* detect disk change */
5425 goto int13_success;
5426 break;
5428 // all those functions return disk write-protected
5429 case 0x03: /* write disk sectors */
5430 case 0x05: /* format disk track */
5431 case 0x43: // IBM/MS extended write
5432 SET_AH(0x03);
5433 goto int13_fail_noah;
5434 break;
5436 case 0x01: /* read disk status */
5437 status = read_byte(0x0040, 0x0074);
5438 SET_AH(status);
5439 SET_DISK_RET_STATUS(0);
5441 /* set CF if error status read */
5442 if (status) goto int13_fail_nostatus;
5443 else goto int13_success_noah;
5444 break;
5446 case 0x15: /* read disk drive size */
5447 SET_AH(0x02);
5448 goto int13_fail_noah;
5449 break;
5451 case 0x41: // IBM/MS installation check
5452 BX=0xaa55; // install check
5453 SET_AH(0x30); // EDD 2.1
5454 CX=0x0007; // ext disk access, removable and edd
5455 goto int13_success_noah;
5456 break;
5458 case 0x42: // IBM/MS extended read
5459 case 0x44: // IBM/MS verify sectors
5460 case 0x47: // IBM/MS extended seek
5462 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
5463 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
5464 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
5466 // Can't use 64 bits lba
5467 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
5468 if (lba != 0L) {
5469 BX_PANIC("int13_cdrom: function %02x. Can't use 64bits lba\n",GET_AH());
5470 goto int13_fail;
5473 // Get 32 bits lba
5474 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5476 // If verify or seek
5477 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
5478 goto int13_success;
5480 memsetb(get_SS(),atacmd,0,12);
5481 atacmd[0]=0x28; // READ command
5482 atacmd[7]=(count & 0xff00) >> 8; // Sectors
5483 atacmd[8]=(count & 0x00ff); // Sectors
5484 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
5485 atacmd[3]=(lba & 0x00ff0000) >> 16;
5486 atacmd[4]=(lba & 0x0000ff00) >> 8;
5487 atacmd[5]=(lba & 0x000000ff);
5488 status = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, count*2048L, ATA_DATA_IN, segment,offset);
5490 count = (Bit16u)(read_dword(ebda_seg, &EbdaData->ata.trsfbytes) >> 11);
5491 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5493 if (status != 0) {
5494 BX_INFO("int13_cdrom: function %02x, status %02x !\n",GET_AH(),status);
5495 SET_AH(0x0c);
5496 goto int13_fail_noah;
5499 goto int13_success;
5500 break;
5502 case 0x45: // IBM/MS lock/unlock drive
5503 if (GET_AL() > 2) goto int13_fail;
5505 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5507 switch (GET_AL()) {
5508 case 0 : // lock
5509 if (locks == 0xff) {
5510 SET_AH(0xb4);
5511 SET_AL(1);
5512 goto int13_fail_noah;
5514 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, ++locks);
5515 SET_AL(1);
5516 break;
5517 case 1 : // unlock
5518 if (locks == 0x00) {
5519 SET_AH(0xb0);
5520 SET_AL(0);
5521 goto int13_fail_noah;
5523 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, --locks);
5524 SET_AL(locks==0?0:1);
5525 break;
5526 case 2 : // status
5527 SET_AL(locks==0?0:1);
5528 break;
5530 goto int13_success;
5531 break;
5533 case 0x46: // IBM/MS eject media
5534 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5536 if (locks != 0) {
5537 SET_AH(0xb1); // media locked
5538 goto int13_fail_noah;
5540 // FIXME should handle 0x31 no media in device
5541 // FIXME should handle 0xb5 valid request failed
5543 // Call removable media eject
5544 ASM_START
5545 push bp
5546 mov bp, sp
5548 mov ah, #0x52
5549 int #0x15
5550 mov _int13_cdrom.status + 2[bp], ah
5551 jnc int13_cdrom_rme_end
5552 mov _int13_cdrom.status, #1
5553 int13_cdrom_rme_end:
5554 pop bp
5555 ASM_END
5557 if (status != 0) {
5558 SET_AH(0xb1); // media locked
5559 goto int13_fail_noah;
5562 goto int13_success;
5563 break;
5565 case 0x48: // IBM/MS get drive parameters
5566 size = read_word(DS,SI+(Bit16u)&Int13Ext->size);
5568 // Buffer is too small
5569 if(size < 0x1a)
5570 goto int13_fail;
5572 // EDD 1.x
5573 if(size >= 0x1a) {
5574 Bit16u cylinders, heads, spt, blksize;
5576 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5578 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5579 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x74); // removable, media change, lockable, max values
5580 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0xffffffff);
5581 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, 0xffffffff);
5582 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, 0xffffffff);
5583 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, 0xffffffff); // FIXME should be Bit64
5584 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0xffffffff);
5585 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5588 // EDD 2.x
5589 if(size >= 0x1e) {
5590 Bit8u channel, dev, irq, mode, checksum, i;
5591 Bit16u iobase1, iobase2, options;
5593 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5595 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5596 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5598 // Fill in dpte
5599 channel = device / 2;
5600 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5601 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5602 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5603 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5605 // FIXME atapi device
5606 options = (1<<4); // lba translation
5607 options |= (1<<5); // removable device
5608 options |= (1<<6); // atapi device
5609 options |= (mode==ATA_MODE_PIO32?1:0<<7);
5611 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5612 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2 + ATA_CB_DC);
5613 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5614 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5615 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5616 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5617 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5618 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5619 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5620 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5621 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5623 checksum=0;
5624 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, ((Bit8u*)(&EbdaData->ata.dpte)) + i);
5625 checksum = ~checksum;
5626 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5629 // EDD 3.x
5630 if(size >= 0x42) {
5631 Bit8u channel, iface, checksum, i;
5632 Bit16u iobase1;
5634 channel = device / 2;
5635 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5636 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5638 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5639 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5640 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5641 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5642 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5644 if (iface==ATA_IFACE_ISA) {
5645 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5646 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5647 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5648 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5650 else {
5651 // FIXME PCI
5653 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5654 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5655 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5656 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5658 if (iface==ATA_IFACE_ISA) {
5659 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5660 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5661 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5663 else {
5664 // FIXME PCI
5666 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5667 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5668 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5669 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5671 checksum=0;
5672 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5673 checksum = ~checksum;
5674 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5677 goto int13_success;
5678 break;
5680 case 0x49: // IBM/MS extended media change
5681 // always send changed ??
5682 SET_AH(06);
5683 goto int13_fail_nostatus;
5684 break;
5686 case 0x4e: // // IBM/MS set hardware configuration
5687 // DMA, prefetch, PIO maximum not supported
5688 switch (GET_AL()) {
5689 case 0x01:
5690 case 0x03:
5691 case 0x04:
5692 case 0x06:
5693 goto int13_success;
5694 break;
5695 default :
5696 goto int13_fail;
5698 break;
5700 // all those functions return unimplemented
5701 case 0x02: /* read sectors */
5702 case 0x04: /* verify sectors */
5703 case 0x08: /* read disk drive parameters */
5704 case 0x0a: /* read disk sectors with ECC */
5705 case 0x0b: /* write disk sectors with ECC */
5706 case 0x18: /* set media type for format */
5707 case 0x50: // ? - send packet command
5708 default:
5709 BX_INFO("int13_cdrom: unsupported AH=%02x\n", GET_AH());
5710 goto int13_fail;
5711 break;
5714 int13_fail:
5715 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5716 int13_fail_noah:
5717 SET_DISK_RET_STATUS(GET_AH());
5718 int13_fail_nostatus:
5719 SET_CF(); // error occurred
5720 return;
5722 int13_success:
5723 SET_AH(0x00); // no error
5724 int13_success_noah:
5725 SET_DISK_RET_STATUS(0x00);
5726 CLEAR_CF(); // no error
5727 return;
5730 // ---------------------------------------------------------------------------
5731 // End of int13 for cdrom
5732 // ---------------------------------------------------------------------------
5734 #if BX_ELTORITO_BOOT
5735 // ---------------------------------------------------------------------------
5736 // Start of int13 for eltorito functions
5737 // ---------------------------------------------------------------------------
5739 void
5740 int13_eltorito(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
5741 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
5743 Bit16u ebda_seg=read_word(0x0040,0x000E);
5745 BX_DEBUG_INT13_ET("int13_eltorito: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5746 // BX_DEBUG_INT13_ET("int13_eltorito: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
5748 switch (GET_AH()) {
5750 // FIXME ElTorito Various. Should be implemented
5751 case 0x4a: // ElTorito - Initiate disk emu
5752 case 0x4c: // ElTorito - Initiate disk emu and boot
5753 case 0x4d: // ElTorito - Return Boot catalog
5754 BX_PANIC("Int13 eltorito call with AX=%04x. Please report\n",AX);
5755 goto int13_fail;
5756 break;
5758 case 0x4b: // ElTorito - Terminate disk emu
5759 // FIXME ElTorito Hardcoded
5760 write_byte(DS,SI+0x00,0x13);
5761 write_byte(DS,SI+0x01,read_byte(ebda_seg,&EbdaData->cdemu.media));
5762 write_byte(DS,SI+0x02,read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
5763 write_byte(DS,SI+0x03,read_byte(ebda_seg,&EbdaData->cdemu.controller_index));
5764 write_dword(DS,SI+0x04,read_dword(ebda_seg,&EbdaData->cdemu.ilba));
5765 write_word(DS,SI+0x08,read_word(ebda_seg,&EbdaData->cdemu.device_spec));
5766 write_word(DS,SI+0x0a,read_word(ebda_seg,&EbdaData->cdemu.buffer_segment));
5767 write_word(DS,SI+0x0c,read_word(ebda_seg,&EbdaData->cdemu.load_segment));
5768 write_word(DS,SI+0x0e,read_word(ebda_seg,&EbdaData->cdemu.sector_count));
5769 write_byte(DS,SI+0x10,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.cylinders));
5770 write_byte(DS,SI+0x11,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.spt));
5771 write_byte(DS,SI+0x12,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.heads));
5773 // If we have to terminate emulation
5774 if(GET_AL() == 0x00) {
5775 // FIXME ElTorito Various. Should be handled accordingly to spec
5776 write_byte(ebda_seg,&EbdaData->cdemu.active, 0x00); // bye bye
5779 goto int13_success;
5780 break;
5782 default:
5783 BX_INFO("int13_eltorito: unsupported AH=%02x\n", GET_AH());
5784 goto int13_fail;
5785 break;
5788 int13_fail:
5789 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5790 SET_DISK_RET_STATUS(GET_AH());
5791 SET_CF(); // error occurred
5792 return;
5794 int13_success:
5795 SET_AH(0x00); // no error
5796 SET_DISK_RET_STATUS(0x00);
5797 CLEAR_CF(); // no error
5798 return;
5801 // ---------------------------------------------------------------------------
5802 // End of int13 for eltorito functions
5803 // ---------------------------------------------------------------------------
5805 // ---------------------------------------------------------------------------
5806 // Start of int13 when emulating a device from the cd
5807 // ---------------------------------------------------------------------------
5809 void
5810 int13_cdemu(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
5811 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
5813 Bit16u ebda_seg=read_word(0x0040,0x000E);
5814 Bit8u device, status;
5815 Bit16u vheads, vspt, vcylinders;
5816 Bit16u head, sector, cylinder, nbsectors;
5817 Bit32u vlba, ilba, slba, elba;
5818 Bit16u before, segment, offset;
5819 Bit8u atacmd[12];
5821 BX_DEBUG_INT13_ET("int13_cdemu: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5823 /* at this point, we are emulating a floppy/harddisk */
5825 // Recompute the device number
5826 device = read_byte(ebda_seg,&EbdaData->cdemu.controller_index) * 2;
5827 device += read_byte(ebda_seg,&EbdaData->cdemu.device_spec);
5829 SET_DISK_RET_STATUS(0x00);
5831 /* basic checks : emulation should be active, dl should equal the emulated drive */
5832 if( (read_byte(ebda_seg,&EbdaData->cdemu.active) ==0 )
5833 || (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive ) != GET_DL())) {
5834 BX_INFO("int13_cdemu: function %02x, emulation not active for DL= %02x\n", GET_AH(), GET_DL());
5835 goto int13_fail;
5838 switch (GET_AH()) {
5840 // all those functions return SUCCESS
5841 case 0x00: /* disk controller reset */
5842 case 0x09: /* initialize drive parameters */
5843 case 0x0c: /* seek to specified cylinder */
5844 case 0x0d: /* alternate disk reset */ // FIXME ElTorito Various. should really reset ?
5845 case 0x10: /* check drive ready */ // FIXME ElTorito Various. should check if ready ?
5846 case 0x11: /* recalibrate */
5847 case 0x14: /* controller internal diagnostic */
5848 case 0x16: /* detect disk change */
5849 goto int13_success;
5850 break;
5852 // all those functions return disk write-protected
5853 case 0x03: /* write disk sectors */
5854 case 0x05: /* format disk track */
5855 SET_AH(0x03);
5856 goto int13_fail_noah;
5857 break;
5859 case 0x01: /* read disk status */
5860 status=read_byte(0x0040, 0x0074);
5861 SET_AH(status);
5862 SET_DISK_RET_STATUS(0);
5864 /* set CF if error status read */
5865 if (status) goto int13_fail_nostatus;
5866 else goto int13_success_noah;
5867 break;
5869 case 0x02: // read disk sectors
5870 case 0x04: // verify disk sectors
5871 vspt = read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
5872 vcylinders = read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders);
5873 vheads = read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads);
5875 ilba = read_dword(ebda_seg,&EbdaData->cdemu.ilba);
5877 sector = GET_CL() & 0x003f;
5878 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
5879 head = GET_DH();
5880 nbsectors = GET_AL();
5881 segment = ES;
5882 offset = BX;
5884 // no sector to read ?
5885 if(nbsectors==0) goto int13_success;
5887 // sanity checks sco openserver needs this!
5888 if ((sector > vspt)
5889 || (cylinder >= vcylinders)
5890 || (head >= vheads)) {
5891 goto int13_fail;
5894 // After controls, verify do nothing
5895 if (GET_AH() == 0x04) goto int13_success;
5897 segment = ES+(BX / 16);
5898 offset = BX % 16;
5900 // calculate the virtual lba inside the image
5901 vlba=((((Bit32u)cylinder*(Bit32u)vheads)+(Bit32u)head)*(Bit32u)vspt)+((Bit32u)(sector-1));
5903 // In advance so we don't loose the count
5904 SET_AL(nbsectors);
5906 // start lba on cd
5907 slba = (Bit32u)vlba/4;
5908 before= (Bit16u)vlba%4;
5910 // end lba on cd
5911 elba = (Bit32u)(vlba+nbsectors-1)/4;
5913 memsetb(get_SS(),atacmd,0,12);
5914 atacmd[0]=0x28; // READ command
5915 atacmd[7]=((Bit16u)(elba-slba+1) & 0xff00) >> 8; // Sectors
5916 atacmd[8]=((Bit16u)(elba-slba+1) & 0x00ff); // Sectors
5917 atacmd[2]=(ilba+slba & 0xff000000) >> 24; // LBA
5918 atacmd[3]=(ilba+slba & 0x00ff0000) >> 16;
5919 atacmd[4]=(ilba+slba & 0x0000ff00) >> 8;
5920 atacmd[5]=(ilba+slba & 0x000000ff);
5921 if((status = ata_cmd_packet(device, 12, get_SS(), atacmd, before*512, nbsectors*512L, ATA_DATA_IN, segment,offset)) != 0) {
5922 BX_INFO("int13_cdemu: function %02x, error %02x !\n",GET_AH(),status);
5923 SET_AH(0x02);
5924 SET_AL(0);
5925 goto int13_fail_noah;
5928 goto int13_success;
5929 break;
5931 case 0x08: /* read disk drive parameters */
5932 vspt=read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
5933 vcylinders=read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders) - 1;
5934 vheads=read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads) - 1;
5936 SET_AL( 0x00 );
5937 SET_BL( 0x00 );
5938 SET_CH( vcylinders & 0xff );
5939 SET_CL((( vcylinders >> 2) & 0xc0) | ( vspt & 0x3f ));
5940 SET_DH( vheads );
5941 SET_DL( 0x02 ); // FIXME ElTorito Various. should send the real count of drives 1 or 2
5942 // FIXME ElTorito Harddisk. should send the HD count
5944 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
5945 case 0x01: SET_BL( 0x02 ); break;
5946 case 0x02: SET_BL( 0x04 ); break;
5947 case 0x03: SET_BL( 0x06 ); break;
5950 ASM_START
5951 push bp
5952 mov bp, sp
5953 mov ax, #diskette_param_table2
5954 mov _int13_cdemu.DI+2[bp], ax
5955 mov _int13_cdemu.ES+2[bp], cs
5956 pop bp
5957 ASM_END
5958 goto int13_success;
5959 break;
5961 case 0x15: /* read disk drive size */
5962 // FIXME ElTorito Harddisk. What geometry to send ?
5963 SET_AH(0x03);
5964 goto int13_success_noah;
5965 break;
5967 // all those functions return unimplemented
5968 case 0x0a: /* read disk sectors with ECC */
5969 case 0x0b: /* write disk sectors with ECC */
5970 case 0x18: /* set media type for format */
5971 case 0x41: // IBM/MS installation check
5972 // FIXME ElTorito Harddisk. Darwin would like to use EDD
5973 case 0x42: // IBM/MS extended read
5974 case 0x43: // IBM/MS extended write
5975 case 0x44: // IBM/MS verify sectors
5976 case 0x45: // IBM/MS lock/unlock drive
5977 case 0x46: // IBM/MS eject media
5978 case 0x47: // IBM/MS extended seek
5979 case 0x48: // IBM/MS get drive parameters
5980 case 0x49: // IBM/MS extended media change
5981 case 0x4e: // ? - set hardware configuration
5982 case 0x50: // ? - send packet command
5983 default:
5984 BX_INFO("int13_cdemu function AH=%02x unsupported, returns fail\n", GET_AH());
5985 goto int13_fail;
5986 break;
5989 int13_fail:
5990 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5991 int13_fail_noah:
5992 SET_DISK_RET_STATUS(GET_AH());
5993 int13_fail_nostatus:
5994 SET_CF(); // error occurred
5995 return;
5997 int13_success:
5998 SET_AH(0x00); // no error
5999 int13_success_noah:
6000 SET_DISK_RET_STATUS(0x00);
6001 CLEAR_CF(); // no error
6002 return;
6005 // ---------------------------------------------------------------------------
6006 // End of int13 when emulating a device from the cd
6007 // ---------------------------------------------------------------------------
6009 #endif // BX_ELTORITO_BOOT
6011 #else //BX_USE_ATADRV
6013 void
6014 outLBA(cylinder,hd_heads,head,hd_sectors,sector,dl)
6015 Bit16u cylinder;
6016 Bit16u hd_heads;
6017 Bit16u head;
6018 Bit16u hd_sectors;
6019 Bit16u sector;
6020 Bit16u dl;
6022 ASM_START
6023 push bp
6024 mov bp, sp
6025 push eax
6026 push ebx
6027 push edx
6028 xor eax,eax
6029 mov ax,4[bp] // cylinder
6030 xor ebx,ebx
6031 mov bl,6[bp] // hd_heads
6032 imul ebx
6034 mov bl,8[bp] // head
6035 add eax,ebx
6036 mov bl,10[bp] // hd_sectors
6037 imul ebx
6038 mov bl,12[bp] // sector
6039 add eax,ebx
6041 dec eax
6042 mov dx,#0x1f3
6043 out dx,al
6044 mov dx,#0x1f4
6045 mov al,ah
6046 out dx,al
6047 shr eax,#16
6048 mov dx,#0x1f5
6049 out dx,al
6050 and ah,#0xf
6051 mov bl,14[bp] // dl
6052 and bl,#1
6053 shl bl,#4
6054 or ah,bl
6055 or ah,#0xe0
6056 mov al,ah
6057 mov dx,#0x01f6
6058 out dx,al
6059 pop edx
6060 pop ebx
6061 pop eax
6062 pop bp
6063 ASM_END
6066 void
6067 int13_harddisk(EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
6068 Bit16u EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
6070 Bit8u drive, num_sectors, sector, head, status, mod;
6071 Bit8u drive_map;
6072 Bit8u n_drives;
6073 Bit16u cyl_mod, ax;
6074 Bit16u max_cylinder, cylinder, total_sectors;
6075 Bit16u hd_cylinders;
6076 Bit8u hd_heads, hd_sectors;
6077 Bit16u val16;
6078 Bit8u sector_count;
6079 unsigned int i;
6080 Bit16u tempbx;
6081 Bit16u dpsize;
6083 Bit16u count, segment, offset;
6084 Bit32u lba;
6085 Bit16u error;
6087 BX_DEBUG_INT13_HD("int13 harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
6089 write_byte(0x0040, 0x008e, 0); // clear completion flag
6091 /* at this point, DL is >= 0x80 to be passed from the floppy int13h
6092 handler code */
6093 /* check how many disks first (cmos reg 0x12), return an error if
6094 drive not present */
6095 drive_map = inb_cmos(0x12);
6096 drive_map = (((drive_map & 0xf0)==0) ? 0 : 1) |
6097 (((drive_map & 0x0f)==0) ? 0 : 2);
6098 n_drives = (drive_map==0) ? 0 :
6099 ((drive_map==3) ? 2 : 1);
6101 if (!(drive_map & (1<<(GET_ELDL()&0x7f)))) { /* allow 0, 1, or 2 disks */
6102 SET_AH(0x01);
6103 SET_DISK_RET_STATUS(0x01);
6104 SET_CF(); /* error occurred */
6105 return;
6108 switch (GET_AH()) {
6110 case 0x00: /* disk controller reset */
6111 BX_DEBUG_INT13_HD("int13_f00\n");
6113 SET_AH(0);
6114 SET_DISK_RET_STATUS(0);
6115 set_diskette_ret_status(0);
6116 set_diskette_current_cyl(0, 0); /* current cylinder, diskette 1 */
6117 set_diskette_current_cyl(1, 0); /* current cylinder, diskette 2 */
6118 CLEAR_CF(); /* successful */
6119 return;
6120 break;
6122 case 0x01: /* read disk status */
6123 BX_DEBUG_INT13_HD("int13_f01\n");
6124 status = read_byte(0x0040, 0x0074);
6125 SET_AH(status);
6126 SET_DISK_RET_STATUS(0);
6127 /* set CF if error status read */
6128 if (status) SET_CF();
6129 else CLEAR_CF();
6130 return;
6131 break;
6133 case 0x04: // verify disk sectors
6134 case 0x02: // read disk sectors
6135 drive = GET_ELDL();
6136 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6138 num_sectors = GET_AL();
6139 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
6140 sector = (GET_CL() & 0x3f);
6141 head = GET_DH();
6144 if (hd_cylinders > 1024) {
6145 if (hd_cylinders <= 2048) {
6146 cylinder <<= 1;
6148 else if (hd_cylinders <= 4096) {
6149 cylinder <<= 2;
6151 else if (hd_cylinders <= 8192) {
6152 cylinder <<= 3;
6154 else { // hd_cylinders <= 16384
6155 cylinder <<= 4;
6158 ax = head / hd_heads;
6159 cyl_mod = ax & 0xff;
6160 head = ax >> 8;
6161 cylinder |= cyl_mod;
6164 if ( (cylinder >= hd_cylinders) ||
6165 (sector > hd_sectors) ||
6166 (head >= hd_heads) ) {
6167 SET_AH(1);
6168 SET_DISK_RET_STATUS(1);
6169 SET_CF(); /* error occurred */
6170 return;
6173 if ( (num_sectors > 128) || (num_sectors == 0) )
6174 BX_PANIC("int13_harddisk: num_sectors out of range!\n");
6176 if (head > 15)
6177 BX_PANIC("hard drive BIOS:(read/verify) head > 15\n");
6179 if ( GET_AH() == 0x04 ) {
6180 SET_AH(0);
6181 SET_DISK_RET_STATUS(0);
6182 CLEAR_CF();
6183 return;
6186 status = inb(0x1f7);
6187 if (status & 0x80) {
6188 BX_PANIC("hard drive BIOS:(read/verify) BUSY bit set\n");
6190 outb(0x01f2, num_sectors);
6191 /* activate LBA? (tomv) */
6192 if (hd_heads > 16) {
6193 BX_DEBUG_INT13_HD("CHS: %x %x %x\n", cylinder, head, sector);
6194 outLBA(cylinder,hd_heads,head,hd_sectors,sector,drive);
6196 else {
6197 outb(0x01f3, sector);
6198 outb(0x01f4, cylinder & 0x00ff);
6199 outb(0x01f5, cylinder >> 8);
6200 outb(0x01f6, 0xa0 | ((drive & 0x01)<<4) | (head & 0x0f));
6202 outb(0x01f7, 0x20);
6204 while (1) {
6205 status = inb(0x1f7);
6206 if ( !(status & 0x80) ) break;
6209 if (status & 0x01) {
6210 BX_PANIC("hard drive BIOS:(read/verify) read error\n");
6211 } else if ( !(status & 0x08) ) {
6212 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
6213 BX_PANIC("hard drive BIOS:(read/verify) expected DRQ=1\n");
6216 sector_count = 0;
6217 tempbx = BX;
6219 ASM_START
6220 sti ;; enable higher priority interrupts
6221 ASM_END
6223 while (1) {
6224 ASM_START
6225 ;; store temp bx in real DI register
6226 push bp
6227 mov bp, sp
6228 mov di, _int13_harddisk.tempbx + 2 [bp]
6229 pop bp
6231 ;; adjust if there will be an overrun
6232 cmp di, #0xfe00
6233 jbe i13_f02_no_adjust
6234 i13_f02_adjust:
6235 sub di, #0x0200 ; sub 512 bytes from offset
6236 mov ax, es
6237 add ax, #0x0020 ; add 512 to segment
6238 mov es, ax
6240 i13_f02_no_adjust:
6241 mov cx, #0x0100 ;; counter (256 words = 512b)
6242 mov dx, #0x01f0 ;; AT data read port
6245 insw ;; CX words transfered from port(DX) to ES:[DI]
6247 i13_f02_done:
6248 ;; store real DI register back to temp bx
6249 push bp
6250 mov bp, sp
6251 mov _int13_harddisk.tempbx + 2 [bp], di
6252 pop bp
6253 ASM_END
6255 sector_count++;
6256 num_sectors--;
6257 if (num_sectors == 0) {
6258 status = inb(0x1f7);
6259 if ( (status & 0xc9) != 0x40 )
6260 BX_PANIC("no sectors left to read/verify, status is %02x\n", (unsigned) status);
6261 break;
6263 else {
6264 status = inb(0x1f7);
6265 if ( (status & 0xc9) != 0x48 )
6266 BX_PANIC("more sectors left to read/verify, status is %02x\n", (unsigned) status);
6267 continue;
6271 SET_AH(0);
6272 SET_DISK_RET_STATUS(0);
6273 SET_AL(sector_count);
6274 CLEAR_CF(); /* successful */
6275 return;
6276 break;
6279 case 0x03: /* write disk sectors */
6280 BX_DEBUG_INT13_HD("int13_f03\n");
6281 drive = GET_ELDL ();
6282 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6284 num_sectors = GET_AL();
6285 cylinder = GET_CH();
6286 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
6287 sector = (GET_CL() & 0x3f);
6288 head = GET_DH();
6290 if (hd_cylinders > 1024) {
6291 if (hd_cylinders <= 2048) {
6292 cylinder <<= 1;
6294 else if (hd_cylinders <= 4096) {
6295 cylinder <<= 2;
6297 else if (hd_cylinders <= 8192) {
6298 cylinder <<= 3;
6300 else { // hd_cylinders <= 16384
6301 cylinder <<= 4;
6304 ax = head / hd_heads;
6305 cyl_mod = ax & 0xff;
6306 head = ax >> 8;
6307 cylinder |= cyl_mod;
6310 if ( (cylinder >= hd_cylinders) ||
6311 (sector > hd_sectors) ||
6312 (head >= hd_heads) ) {
6313 SET_AH( 1);
6314 SET_DISK_RET_STATUS(1);
6315 SET_CF(); /* error occurred */
6316 return;
6319 if ( (num_sectors > 128) || (num_sectors == 0) )
6320 BX_PANIC("int13_harddisk: num_sectors out of range!\n");
6322 if (head > 15)
6323 BX_PANIC("hard drive BIOS:(read) head > 15\n");
6325 status = inb(0x1f7);
6326 if (status & 0x80) {
6327 BX_PANIC("hard drive BIOS:(read) BUSY bit set\n");
6329 // should check for Drive Ready Bit also in status reg
6330 outb(0x01f2, num_sectors);
6332 /* activate LBA? (tomv) */
6333 if (hd_heads > 16) {
6334 BX_DEBUG_INT13_HD("CHS (write): %x %x %x\n", cylinder, head, sector);
6335 outLBA(cylinder,hd_heads,head,hd_sectors,sector,GET_ELDL());
6337 else {
6338 outb(0x01f3, sector);
6339 outb(0x01f4, cylinder & 0x00ff);
6340 outb(0x01f5, cylinder >> 8);
6341 outb(0x01f6, 0xa0 | ((GET_ELDL() & 0x01)<<4) | (head & 0x0f));
6343 outb(0x01f7, 0x30);
6345 // wait for busy bit to turn off after seeking
6346 while (1) {
6347 status = inb(0x1f7);
6348 if ( !(status & 0x80) ) break;
6351 if ( !(status & 0x08) ) {
6352 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
6353 BX_PANIC("hard drive BIOS:(write) data-request bit not set\n");
6356 sector_count = 0;
6357 tempbx = BX;
6359 ASM_START
6360 sti ;; enable higher priority interrupts
6361 ASM_END
6363 while (1) {
6364 ASM_START
6365 ;; store temp bx in real SI register
6366 push bp
6367 mov bp, sp
6368 mov si, _int13_harddisk.tempbx + 2 [bp]
6369 pop bp
6371 ;; adjust if there will be an overrun
6372 cmp si, #0xfe00
6373 jbe i13_f03_no_adjust
6374 i13_f03_adjust:
6375 sub si, #0x0200 ; sub 512 bytes from offset
6376 mov ax, es
6377 add ax, #0x0020 ; add 512 to segment
6378 mov es, ax
6380 i13_f03_no_adjust:
6381 mov cx, #0x0100 ;; counter (256 words = 512b)
6382 mov dx, #0x01f0 ;; AT data read port
6384 seg ES
6386 outsw ;; CX words tranfered from ES:[SI] to port(DX)
6388 ;; store real SI register back to temp bx
6389 push bp
6390 mov bp, sp
6391 mov _int13_harddisk.tempbx + 2 [bp], si
6392 pop bp
6393 ASM_END
6395 sector_count++;
6396 num_sectors--;
6397 if (num_sectors == 0) {
6398 status = inb(0x1f7);
6399 if ( (status & 0xe9) != 0x40 )
6400 BX_PANIC("no sectors left to write, status is %02x\n", (unsigned) status);
6401 break;
6403 else {
6404 status = inb(0x1f7);
6405 if ( (status & 0xc9) != 0x48 )
6406 BX_PANIC("more sectors left to write, status is %02x\n", (unsigned) status);
6407 continue;
6411 SET_AH(0);
6412 SET_DISK_RET_STATUS(0);
6413 SET_AL(sector_count);
6414 CLEAR_CF(); /* successful */
6415 return;
6416 break;
6418 case 0x05: /* format disk track */
6419 BX_DEBUG_INT13_HD("int13_f05\n");
6420 BX_PANIC("format disk track called\n");
6421 /* nop */
6422 SET_AH(0);
6423 SET_DISK_RET_STATUS(0);
6424 CLEAR_CF(); /* successful */
6425 return;
6426 break;
6428 case 0x08: /* read disk drive parameters */
6429 BX_DEBUG_INT13_HD("int13_f08\n");
6431 drive = GET_ELDL ();
6432 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6434 // translate CHS
6436 if (hd_cylinders <= 1024) {
6437 // hd_cylinders >>= 0;
6438 // hd_heads <<= 0;
6440 else if (hd_cylinders <= 2048) {
6441 hd_cylinders >>= 1;
6442 hd_heads <<= 1;
6444 else if (hd_cylinders <= 4096) {
6445 hd_cylinders >>= 2;
6446 hd_heads <<= 2;
6448 else if (hd_cylinders <= 8192) {
6449 hd_cylinders >>= 3;
6450 hd_heads <<= 3;
6452 else { // hd_cylinders <= 16384
6453 hd_cylinders >>= 4;
6454 hd_heads <<= 4;
6457 max_cylinder = hd_cylinders - 2; /* 0 based */
6458 SET_AL(0);
6459 SET_CH(max_cylinder & 0xff);
6460 SET_CL(((max_cylinder >> 2) & 0xc0) | (hd_sectors & 0x3f));
6461 SET_DH(hd_heads - 1);
6462 SET_DL(n_drives); /* returns 0, 1, or 2 hard drives */
6463 SET_AH(0);
6464 SET_DISK_RET_STATUS(0);
6465 CLEAR_CF(); /* successful */
6467 return;
6468 break;
6470 case 0x09: /* initialize drive parameters */
6471 BX_DEBUG_INT13_HD("int13_f09\n");
6472 SET_AH(0);
6473 SET_DISK_RET_STATUS(0);
6474 CLEAR_CF(); /* successful */
6475 return;
6476 break;
6478 case 0x0a: /* read disk sectors with ECC */
6479 BX_DEBUG_INT13_HD("int13_f0a\n");
6480 case 0x0b: /* write disk sectors with ECC */
6481 BX_DEBUG_INT13_HD("int13_f0b\n");
6482 BX_PANIC("int13h Functions 0Ah & 0Bh not implemented!\n");
6483 return;
6484 break;
6486 case 0x0c: /* seek to specified cylinder */
6487 BX_DEBUG_INT13_HD("int13_f0c\n");
6488 BX_INFO("int13h function 0ch (seek) not implemented!\n");
6489 SET_AH(0);
6490 SET_DISK_RET_STATUS(0);
6491 CLEAR_CF(); /* successful */
6492 return;
6493 break;
6495 case 0x0d: /* alternate disk reset */
6496 BX_DEBUG_INT13_HD("int13_f0d\n");
6497 SET_AH(0);
6498 SET_DISK_RET_STATUS(0);
6499 CLEAR_CF(); /* successful */
6500 return;
6501 break;
6503 case 0x10: /* check drive ready */
6504 BX_DEBUG_INT13_HD("int13_f10\n");
6505 //SET_AH(0);
6506 //SET_DISK_RET_STATUS(0);
6507 //CLEAR_CF(); /* successful */
6508 //return;
6509 //break;
6511 // should look at 40:8E also???
6512 status = inb(0x01f7);
6513 if ( (status & 0xc0) == 0x40 ) {
6514 SET_AH(0);
6515 SET_DISK_RET_STATUS(0);
6516 CLEAR_CF(); // drive ready
6517 return;
6519 else {
6520 SET_AH(0xAA);
6521 SET_DISK_RET_STATUS(0xAA);
6522 SET_CF(); // not ready
6523 return;
6525 break;
6527 case 0x11: /* recalibrate */
6528 BX_DEBUG_INT13_HD("int13_f11\n");
6529 SET_AH(0);
6530 SET_DISK_RET_STATUS(0);
6531 CLEAR_CF(); /* successful */
6532 return;
6533 break;
6535 case 0x14: /* controller internal diagnostic */
6536 BX_DEBUG_INT13_HD("int13_f14\n");
6537 SET_AH(0);
6538 SET_DISK_RET_STATUS(0);
6539 CLEAR_CF(); /* successful */
6540 SET_AL(0);
6541 return;
6542 break;
6544 case 0x15: /* read disk drive size */
6545 drive = GET_ELDL();
6546 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6547 ASM_START
6548 push bp
6549 mov bp, sp
6550 mov al, _int13_harddisk.hd_heads + 2 [bp]
6551 mov ah, _int13_harddisk.hd_sectors + 2 [bp]
6552 mul al, ah ;; ax = heads * sectors
6553 mov bx, _int13_harddisk.hd_cylinders + 2 [bp]
6554 dec bx ;; use (cylinders - 1) ???
6555 mul ax, bx ;; dx:ax = (cylinders -1) * (heads * sectors)
6556 ;; now we need to move the 32bit result dx:ax to what the
6557 ;; BIOS wants which is cx:dx.
6558 ;; and then into CX:DX on the stack
6559 mov _int13_harddisk.CX + 2 [bp], dx
6560 mov _int13_harddisk.DX + 2 [bp], ax
6561 pop bp
6562 ASM_END
6563 SET_AH(3); // hard disk accessible
6564 SET_DISK_RET_STATUS(0); // ??? should this be 0
6565 CLEAR_CF(); // successful
6566 return;
6567 break;
6569 case 0x18: // set media type for format
6570 case 0x41: // IBM/MS
6571 case 0x42: // IBM/MS
6572 case 0x43: // IBM/MS
6573 case 0x44: // IBM/MS
6574 case 0x45: // IBM/MS lock/unlock drive
6575 case 0x46: // IBM/MS eject media
6576 case 0x47: // IBM/MS extended seek
6577 case 0x49: // IBM/MS extended media change
6578 case 0x50: // IBM/MS send packet command
6579 default:
6580 BX_INFO("int13_harddisk: unsupported AH=%02x\n", GET_AH());
6582 SET_AH(1); // code=invalid function in AH or invalid parameter
6583 SET_DISK_RET_STATUS(1);
6584 SET_CF(); /* unsuccessful */
6585 return;
6586 break;
6590 static char panic_msg_reg12h[] = "HD%d cmos reg 12h not type F\n";
6591 static char panic_msg_reg19h[] = "HD%d cmos reg %02xh not user definable type 47\n";
6593 void
6594 get_hd_geometry(drive, hd_cylinders, hd_heads, hd_sectors)
6595 Bit8u drive;
6596 Bit16u *hd_cylinders;
6597 Bit8u *hd_heads;
6598 Bit8u *hd_sectors;
6600 Bit8u hd_type;
6601 Bit16u ss;
6602 Bit16u cylinders;
6603 Bit8u iobase;
6605 ss = get_SS();
6606 if (drive == 0x80) {
6607 hd_type = inb_cmos(0x12) & 0xf0;
6608 if (hd_type != 0xf0)
6609 BX_INFO(panic_msg_reg12h,0);
6610 hd_type = inb_cmos(0x19); // HD0: extended type
6611 if (hd_type != 47)
6612 BX_INFO(panic_msg_reg19h,0,0x19);
6613 iobase = 0x1b;
6614 } else {
6615 hd_type = inb_cmos(0x12) & 0x0f;
6616 if (hd_type != 0x0f)
6617 BX_INFO(panic_msg_reg12h,1);
6618 hd_type = inb_cmos(0x1a); // HD0: extended type
6619 if (hd_type != 47)
6620 BX_INFO(panic_msg_reg19h,0,0x1a);
6621 iobase = 0x24;
6624 // cylinders
6625 cylinders = inb_cmos(iobase) | (inb_cmos(iobase+1) << 8);
6626 write_word(ss, hd_cylinders, cylinders);
6628 // heads
6629 write_byte(ss, hd_heads, inb_cmos(iobase+2));
6631 // sectors per track
6632 write_byte(ss, hd_sectors, inb_cmos(iobase+8));
6635 #endif //else BX_USE_ATADRV
6637 #if BX_SUPPORT_FLOPPY
6639 //////////////////////
6640 // FLOPPY functions //
6641 //////////////////////
6643 void floppy_reset_controller()
6645 Bit8u val8;
6647 // Reset controller
6648 val8 = inb(0x03f2);
6649 outb(0x03f2, val8 & ~0x04);
6650 outb(0x03f2, val8 | 0x04);
6652 // Wait for controller to come out of reset
6653 do {
6654 val8 = inb(0x3f4);
6655 } while ( (val8 & 0xc0) != 0x80 );
6658 void floppy_prepare_controller(drive)
6659 Bit16u drive;
6661 Bit8u val8, dor, prev_reset;
6663 // set 40:3e bit 7 to 0
6664 val8 = read_byte(0x0040, 0x003e);
6665 val8 &= 0x7f;
6666 write_byte(0x0040, 0x003e, val8);
6668 // turn on motor of selected drive, DMA & int enabled, normal operation
6669 prev_reset = inb(0x03f2) & 0x04;
6670 if (drive)
6671 dor = 0x20;
6672 else
6673 dor = 0x10;
6674 dor |= 0x0c;
6675 dor |= drive;
6676 outb(0x03f2, dor);
6678 // reset the disk motor timeout value of INT 08
6679 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
6681 // wait for drive readiness
6682 do {
6683 val8 = inb(0x3f4);
6684 } while ( (val8 & 0xc0) != 0x80 );
6686 if (prev_reset == 0) {
6687 // turn on interrupts
6688 ASM_START
6690 ASM_END
6691 // wait on 40:3e bit 7 to become 1
6692 do {
6693 val8 = read_byte(0x0040, 0x003e);
6694 } while ( (val8 & 0x80) == 0 );
6695 val8 &= 0x7f;
6696 ASM_START
6698 ASM_END
6699 write_byte(0x0040, 0x003e, val8);
6703 bx_bool
6704 floppy_media_known(drive)
6705 Bit16u drive;
6707 Bit8u val8;
6708 Bit16u media_state_offset;
6710 val8 = read_byte(0x0040, 0x003e); // diskette recal status
6711 if (drive)
6712 val8 >>= 1;
6713 val8 &= 0x01;
6714 if (val8 == 0)
6715 return(0);
6717 media_state_offset = 0x0090;
6718 if (drive)
6719 media_state_offset += 1;
6721 val8 = read_byte(0x0040, media_state_offset);
6722 val8 = (val8 >> 4) & 0x01;
6723 if (val8 == 0)
6724 return(0);
6726 // check pass, return KNOWN
6727 return(1);
6730 bx_bool
6731 floppy_media_sense(drive)
6732 Bit16u drive;
6734 bx_bool retval;
6735 Bit16u media_state_offset;
6736 Bit8u drive_type, config_data, media_state;
6738 if (floppy_drive_recal(drive) == 0) {
6739 return(0);
6742 // for now cheat and get drive type from CMOS,
6743 // assume media is same as drive type
6745 // ** config_data **
6746 // Bitfields for diskette media control:
6747 // Bit(s) Description (Table M0028)
6748 // 7-6 last data rate set by controller
6749 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6750 // 5-4 last diskette drive step rate selected
6751 // 00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
6752 // 3-2 {data rate at start of operation}
6753 // 1-0 reserved
6755 // ** media_state **
6756 // Bitfields for diskette drive media state:
6757 // Bit(s) Description (Table M0030)
6758 // 7-6 data rate
6759 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6760 // 5 double stepping required (e.g. 360kB in 1.2MB)
6761 // 4 media type established
6762 // 3 drive capable of supporting 4MB media
6763 // 2-0 on exit from BIOS, contains
6764 // 000 trying 360kB in 360kB
6765 // 001 trying 360kB in 1.2MB
6766 // 010 trying 1.2MB in 1.2MB
6767 // 011 360kB in 360kB established
6768 // 100 360kB in 1.2MB established
6769 // 101 1.2MB in 1.2MB established
6770 // 110 reserved
6771 // 111 all other formats/drives
6773 drive_type = inb_cmos(0x10);
6774 if (drive == 0)
6775 drive_type >>= 4;
6776 else
6777 drive_type &= 0x0f;
6778 if ( drive_type == 1 ) {
6779 // 360K 5.25" drive
6780 config_data = 0x00; // 0000 0000
6781 media_state = 0x25; // 0010 0101
6782 retval = 1;
6784 else if ( drive_type == 2 ) {
6785 // 1.2 MB 5.25" drive
6786 config_data = 0x00; // 0000 0000
6787 media_state = 0x25; // 0010 0101 // need double stepping??? (bit 5)
6788 retval = 1;
6790 else if ( drive_type == 3 ) {
6791 // 720K 3.5" drive
6792 config_data = 0x00; // 0000 0000 ???
6793 media_state = 0x17; // 0001 0111
6794 retval = 1;
6796 else if ( drive_type == 4 ) {
6797 // 1.44 MB 3.5" drive
6798 config_data = 0x00; // 0000 0000
6799 media_state = 0x17; // 0001 0111
6800 retval = 1;
6802 else if ( drive_type == 5 ) {
6803 // 2.88 MB 3.5" drive
6804 config_data = 0xCC; // 1100 1100
6805 media_state = 0xD7; // 1101 0111
6806 retval = 1;
6809 // Extended floppy size uses special cmos setting
6810 else if ( drive_type == 6 ) {
6811 // 160k 5.25" drive
6812 config_data = 0x00; // 0000 0000
6813 media_state = 0x27; // 0010 0111
6814 retval = 1;
6816 else if ( drive_type == 7 ) {
6817 // 180k 5.25" drive
6818 config_data = 0x00; // 0000 0000
6819 media_state = 0x27; // 0010 0111
6820 retval = 1;
6822 else if ( drive_type == 8 ) {
6823 // 320k 5.25" drive
6824 config_data = 0x00; // 0000 0000
6825 media_state = 0x27; // 0010 0111
6826 retval = 1;
6829 else {
6830 // not recognized
6831 config_data = 0x00; // 0000 0000
6832 media_state = 0x00; // 0000 0000
6833 retval = 0;
6836 if (drive == 0)
6837 media_state_offset = 0x90;
6838 else
6839 media_state_offset = 0x91;
6840 write_byte(0x0040, 0x008B, config_data);
6841 write_byte(0x0040, media_state_offset, media_state);
6843 return(retval);
6846 bx_bool
6847 floppy_drive_recal(drive)
6848 Bit16u drive;
6850 Bit8u val8;
6851 Bit16u curr_cyl_offset;
6853 floppy_prepare_controller(drive);
6855 // send Recalibrate command (2 bytes) to controller
6856 outb(0x03f5, 0x07); // 07: Recalibrate
6857 outb(0x03f5, drive); // 0=drive0, 1=drive1
6859 // turn on interrupts
6860 ASM_START
6862 ASM_END
6864 // wait on 40:3e bit 7 to become 1
6865 do {
6866 val8 = (read_byte(0x0040, 0x003e) & 0x80);
6867 } while ( val8 == 0 );
6869 val8 = 0; // separate asm from while() loop
6870 // turn off interrupts
6871 ASM_START
6873 ASM_END
6875 // set 40:3e bit 7 to 0, and calibrated bit
6876 val8 = read_byte(0x0040, 0x003e);
6877 val8 &= 0x7f;
6878 if (drive) {
6879 val8 |= 0x02; // Drive 1 calibrated
6880 curr_cyl_offset = 0x0095;
6881 } else {
6882 val8 |= 0x01; // Drive 0 calibrated
6883 curr_cyl_offset = 0x0094;
6885 write_byte(0x0040, 0x003e, val8);
6886 write_byte(0x0040, curr_cyl_offset, 0); // current cylinder is 0
6888 return(1);
6893 bx_bool
6894 floppy_drive_exists(drive)
6895 Bit16u drive;
6897 Bit8u drive_type;
6899 // check CMOS to see if drive exists
6900 drive_type = inb_cmos(0x10);
6901 if (drive == 0)
6902 drive_type >>= 4;
6903 else
6904 drive_type &= 0x0f;
6905 if ( drive_type == 0 )
6906 return(0);
6907 else
6908 return(1);
6911 void
6912 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
6913 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
6915 Bit8u drive, num_sectors, track, sector, head, status;
6916 Bit16u base_address, base_count, base_es;
6917 Bit8u page, mode_register, val8, dor;
6918 Bit8u return_status[7];
6919 Bit8u drive_type, num_floppies, ah;
6920 Bit16u es, last_addr;
6922 BX_DEBUG_INT13_FL("int13_diskette: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
6924 ah = GET_AH();
6926 switch ( ah ) {
6927 case 0x00: // diskette controller reset
6928 BX_DEBUG_INT13_FL("floppy f00\n");
6929 drive = GET_ELDL();
6930 if (drive > 1) {
6931 SET_AH(1); // invalid param
6932 set_diskette_ret_status(1);
6933 SET_CF();
6934 return;
6936 drive_type = inb_cmos(0x10);
6938 if (drive == 0)
6939 drive_type >>= 4;
6940 else
6941 drive_type &= 0x0f;
6942 if (drive_type == 0) {
6943 SET_AH(0x80); // drive not responding
6944 set_diskette_ret_status(0x80);
6945 SET_CF();
6946 return;
6948 SET_AH(0);
6949 set_diskette_ret_status(0);
6950 CLEAR_CF(); // successful
6951 set_diskette_current_cyl(drive, 0); // current cylinder
6952 return;
6954 case 0x01: // Read Diskette Status
6955 CLEAR_CF();
6956 val8 = read_byte(0x0000, 0x0441);
6957 SET_AH(val8);
6958 if (val8) {
6959 SET_CF();
6961 return;
6963 case 0x02: // Read Diskette Sectors
6964 case 0x03: // Write Diskette Sectors
6965 case 0x04: // Verify Diskette Sectors
6966 num_sectors = GET_AL();
6967 track = GET_CH();
6968 sector = GET_CL();
6969 head = GET_DH();
6970 drive = GET_ELDL();
6972 if ((drive > 1) || (head > 1) || (sector == 0) ||
6973 (num_sectors == 0) || (num_sectors > 72)) {
6974 BX_INFO("int13_diskette: read/write/verify: parameter out of range\n");
6975 SET_AH(1);
6976 set_diskette_ret_status(1);
6977 SET_AL(0); // no sectors read
6978 SET_CF(); // error occurred
6979 return;
6982 // see if drive exists
6983 if (floppy_drive_exists(drive) == 0) {
6984 SET_AH(0x80); // not responding
6985 set_diskette_ret_status(0x80);
6986 SET_AL(0); // no sectors read
6987 SET_CF(); // error occurred
6988 return;
6991 // see if media in drive, and type is known
6992 if (floppy_media_known(drive) == 0) {
6993 if (floppy_media_sense(drive) == 0) {
6994 SET_AH(0x0C); // Media type not found
6995 set_diskette_ret_status(0x0C);
6996 SET_AL(0); // no sectors read
6997 SET_CF(); // error occurred
6998 return;
7002 if (ah == 0x02) {
7003 // Read Diskette Sectors
7005 //-----------------------------------
7006 // set up DMA controller for transfer
7007 //-----------------------------------
7009 // es:bx = pointer to where to place information from diskette
7010 // port 04: DMA-1 base and current address, channel 2
7011 // port 05: DMA-1 base and current count, channel 2
7012 page = (ES >> 12); // upper 4 bits
7013 base_es = (ES << 4); // lower 16bits contributed by ES
7014 base_address = base_es + BX; // lower 16 bits of address
7015 // contributed by ES:BX
7016 if ( base_address < base_es ) {
7017 // in case of carry, adjust page by 1
7018 page++;
7020 base_count = (num_sectors * 512) - 1;
7022 // check for 64K boundary overrun
7023 last_addr = base_address + base_count;
7024 if (last_addr < base_address) {
7025 SET_AH(0x09);
7026 set_diskette_ret_status(0x09);
7027 SET_AL(0); // no sectors read
7028 SET_CF(); // error occurred
7029 return;
7032 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
7033 outb(0x000a, 0x06);
7035 BX_DEBUG_INT13_FL("clear flip-flop\n");
7036 outb(0x000c, 0x00); // clear flip-flop
7037 outb(0x0004, base_address);
7038 outb(0x0004, base_address>>8);
7039 BX_DEBUG_INT13_FL("clear flip-flop\n");
7040 outb(0x000c, 0x00); // clear flip-flop
7041 outb(0x0005, base_count);
7042 outb(0x0005, base_count>>8);
7044 // port 0b: DMA-1 Mode Register
7045 mode_register = 0x46; // single mode, increment, autoinit disable,
7046 // transfer type=write, channel 2
7047 BX_DEBUG_INT13_FL("setting mode register\n");
7048 outb(0x000b, mode_register);
7050 BX_DEBUG_INT13_FL("setting page register\n");
7051 // port 81: DMA-1 Page Register, channel 2
7052 outb(0x0081, page);
7054 BX_DEBUG_INT13_FL("unmask chan 2\n");
7055 outb(0x000a, 0x02); // unmask channel 2
7057 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
7058 outb(0x000a, 0x02);
7060 //--------------------------------------
7061 // set up floppy controller for transfer
7062 //--------------------------------------
7063 floppy_prepare_controller(drive);
7065 // send read-normal-data command (9 bytes) to controller
7066 outb(0x03f5, 0xe6); // e6: read normal data
7067 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7068 outb(0x03f5, track);
7069 outb(0x03f5, head);
7070 outb(0x03f5, sector);
7071 outb(0x03f5, 2); // 512 byte sector size
7072 outb(0x03f5, sector + num_sectors - 1); // last sector to read on track
7073 outb(0x03f5, 0); // Gap length
7074 outb(0x03f5, 0xff); // Gap length
7076 // turn on interrupts
7077 ASM_START
7079 ASM_END
7081 // wait on 40:3e bit 7 to become 1
7082 do {
7083 val8 = read_byte(0x0040, 0x0040);
7084 if (val8 == 0) {
7085 floppy_reset_controller();
7086 SET_AH(0x80); // drive not ready (timeout)
7087 set_diskette_ret_status(0x80);
7088 SET_AL(0); // no sectors read
7089 SET_CF(); // error occurred
7090 return;
7092 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7093 } while ( val8 == 0 );
7095 val8 = 0; // separate asm from while() loop
7096 // turn off interrupts
7097 ASM_START
7099 ASM_END
7101 // set 40:3e bit 7 to 0
7102 val8 = read_byte(0x0040, 0x003e);
7103 val8 &= 0x7f;
7104 write_byte(0x0040, 0x003e, val8);
7106 // check port 3f4 for accessibility to status bytes
7107 val8 = inb(0x3f4);
7108 if ( (val8 & 0xc0) != 0xc0 )
7109 BX_PANIC("int13_diskette: ctrl not ready\n");
7111 // read 7 return status bytes from controller
7112 // using loop index broken, have to unroll...
7113 return_status[0] = inb(0x3f5);
7114 return_status[1] = inb(0x3f5);
7115 return_status[2] = inb(0x3f5);
7116 return_status[3] = inb(0x3f5);
7117 return_status[4] = inb(0x3f5);
7118 return_status[5] = inb(0x3f5);
7119 return_status[6] = inb(0x3f5);
7120 // record in BIOS Data Area
7121 write_byte(0x0040, 0x0042, return_status[0]);
7122 write_byte(0x0040, 0x0043, return_status[1]);
7123 write_byte(0x0040, 0x0044, return_status[2]);
7124 write_byte(0x0040, 0x0045, return_status[3]);
7125 write_byte(0x0040, 0x0046, return_status[4]);
7126 write_byte(0x0040, 0x0047, return_status[5]);
7127 write_byte(0x0040, 0x0048, return_status[6]);
7129 if ( (return_status[0] & 0xc0) != 0 ) {
7130 SET_AH(0x20);
7131 set_diskette_ret_status(0x20);
7132 SET_AL(0); // no sectors read
7133 SET_CF(); // error occurred
7134 return;
7137 // ??? should track be new val from return_status[3] ?
7138 set_diskette_current_cyl(drive, track);
7139 // AL = number of sectors read (same value as passed)
7140 SET_AH(0x00); // success
7141 CLEAR_CF(); // success
7142 return;
7143 } else if (ah == 0x03) {
7144 // Write Diskette Sectors
7146 //-----------------------------------
7147 // set up DMA controller for transfer
7148 //-----------------------------------
7150 // es:bx = pointer to where to place information from diskette
7151 // port 04: DMA-1 base and current address, channel 2
7152 // port 05: DMA-1 base and current count, channel 2
7153 page = (ES >> 12); // upper 4 bits
7154 base_es = (ES << 4); // lower 16bits contributed by ES
7155 base_address = base_es + BX; // lower 16 bits of address
7156 // contributed by ES:BX
7157 if ( base_address < base_es ) {
7158 // in case of carry, adjust page by 1
7159 page++;
7161 base_count = (num_sectors * 512) - 1;
7163 // check for 64K boundary overrun
7164 last_addr = base_address + base_count;
7165 if (last_addr < base_address) {
7166 SET_AH(0x09);
7167 set_diskette_ret_status(0x09);
7168 SET_AL(0); // no sectors read
7169 SET_CF(); // error occurred
7170 return;
7173 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
7174 outb(0x000a, 0x06);
7176 outb(0x000c, 0x00); // clear flip-flop
7177 outb(0x0004, base_address);
7178 outb(0x0004, base_address>>8);
7179 outb(0x000c, 0x00); // clear flip-flop
7180 outb(0x0005, base_count);
7181 outb(0x0005, base_count>>8);
7183 // port 0b: DMA-1 Mode Register
7184 mode_register = 0x4a; // single mode, increment, autoinit disable,
7185 // transfer type=read, channel 2
7186 outb(0x000b, mode_register);
7188 // port 81: DMA-1 Page Register, channel 2
7189 outb(0x0081, page);
7191 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
7192 outb(0x000a, 0x02);
7194 //--------------------------------------
7195 // set up floppy controller for transfer
7196 //--------------------------------------
7197 floppy_prepare_controller(drive);
7199 // send write-normal-data command (9 bytes) to controller
7200 outb(0x03f5, 0xc5); // c5: write normal data
7201 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7202 outb(0x03f5, track);
7203 outb(0x03f5, head);
7204 outb(0x03f5, sector);
7205 outb(0x03f5, 2); // 512 byte sector size
7206 outb(0x03f5, sector + num_sectors - 1); // last sector to write on track
7207 outb(0x03f5, 0); // Gap length
7208 outb(0x03f5, 0xff); // Gap length
7210 // turn on interrupts
7211 ASM_START
7213 ASM_END
7215 // wait on 40:3e bit 7 to become 1
7216 do {
7217 val8 = read_byte(0x0040, 0x0040);
7218 if (val8 == 0) {
7219 floppy_reset_controller();
7220 SET_AH(0x80); // drive not ready (timeout)
7221 set_diskette_ret_status(0x80);
7222 SET_AL(0); // no sectors written
7223 SET_CF(); // error occurred
7224 return;
7226 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7227 } while ( val8 == 0 );
7229 val8 = 0; // separate asm from while() loop
7230 // turn off interrupts
7231 ASM_START
7233 ASM_END
7235 // set 40:3e bit 7 to 0
7236 val8 = read_byte(0x0040, 0x003e);
7237 val8 &= 0x7f;
7238 write_byte(0x0040, 0x003e, val8);
7240 // check port 3f4 for accessibility to status bytes
7241 val8 = inb(0x3f4);
7242 if ( (val8 & 0xc0) != 0xc0 )
7243 BX_PANIC("int13_diskette: ctrl not ready\n");
7245 // read 7 return status bytes from controller
7246 // using loop index broken, have to unroll...
7247 return_status[0] = inb(0x3f5);
7248 return_status[1] = inb(0x3f5);
7249 return_status[2] = inb(0x3f5);
7250 return_status[3] = inb(0x3f5);
7251 return_status[4] = inb(0x3f5);
7252 return_status[5] = inb(0x3f5);
7253 return_status[6] = inb(0x3f5);
7254 // record in BIOS Data Area
7255 write_byte(0x0040, 0x0042, return_status[0]);
7256 write_byte(0x0040, 0x0043, return_status[1]);
7257 write_byte(0x0040, 0x0044, return_status[2]);
7258 write_byte(0x0040, 0x0045, return_status[3]);
7259 write_byte(0x0040, 0x0046, return_status[4]);
7260 write_byte(0x0040, 0x0047, return_status[5]);
7261 write_byte(0x0040, 0x0048, return_status[6]);
7263 if ( (return_status[0] & 0xc0) != 0 ) {
7264 if ( (return_status[1] & 0x02) != 0 ) {
7265 // diskette not writable.
7266 // AH=status code=0x03 (tried to write on write-protected disk)
7267 // AL=number of sectors written=0
7268 AX = 0x0300;
7269 SET_CF();
7270 return;
7271 } else {
7272 BX_PANIC("int13_diskette_function: read error\n");
7276 // ??? should track be new val from return_status[3] ?
7277 set_diskette_current_cyl(drive, track);
7278 // AL = number of sectors read (same value as passed)
7279 SET_AH(0x00); // success
7280 CLEAR_CF(); // success
7281 return;
7282 } else { // if (ah == 0x04)
7283 // Verify Diskette Sectors
7285 // ??? should track be new val from return_status[3] ?
7286 set_diskette_current_cyl(drive, track);
7287 // AL = number of sectors verified (same value as passed)
7288 CLEAR_CF(); // success
7289 SET_AH(0x00); // success
7290 return;
7292 break;
7294 case 0x05: // format diskette track
7295 BX_DEBUG_INT13_FL("floppy f05\n");
7297 num_sectors = GET_AL();
7298 track = GET_CH();
7299 head = GET_DH();
7300 drive = GET_ELDL();
7302 if ((drive > 1) || (head > 1) || (track > 79) ||
7303 (num_sectors == 0) || (num_sectors > 18)) {
7304 SET_AH(1);
7305 set_diskette_ret_status(1);
7306 SET_CF(); // error occurred
7309 // see if drive exists
7310 if (floppy_drive_exists(drive) == 0) {
7311 SET_AH(0x80); // drive not responding
7312 set_diskette_ret_status(0x80);
7313 SET_CF(); // error occurred
7314 return;
7317 // see if media in drive, and type is known
7318 if (floppy_media_known(drive) == 0) {
7319 if (floppy_media_sense(drive) == 0) {
7320 SET_AH(0x0C); // Media type not found
7321 set_diskette_ret_status(0x0C);
7322 SET_AL(0); // no sectors read
7323 SET_CF(); // error occurred
7324 return;
7328 // set up DMA controller for transfer
7329 page = (ES >> 12); // upper 4 bits
7330 base_es = (ES << 4); // lower 16bits contributed by ES
7331 base_address = base_es + BX; // lower 16 bits of address
7332 // contributed by ES:BX
7333 if ( base_address < base_es ) {
7334 // in case of carry, adjust page by 1
7335 page++;
7337 base_count = (num_sectors * 4) - 1;
7339 // check for 64K boundary overrun
7340 last_addr = base_address + base_count;
7341 if (last_addr < base_address) {
7342 SET_AH(0x09);
7343 set_diskette_ret_status(0x09);
7344 SET_AL(0); // no sectors read
7345 SET_CF(); // error occurred
7346 return;
7349 outb(0x000a, 0x06);
7350 outb(0x000c, 0x00); // clear flip-flop
7351 outb(0x0004, base_address);
7352 outb(0x0004, base_address>>8);
7353 outb(0x000c, 0x00); // clear flip-flop
7354 outb(0x0005, base_count);
7355 outb(0x0005, base_count>>8);
7356 mode_register = 0x4a; // single mode, increment, autoinit disable,
7357 // transfer type=read, channel 2
7358 outb(0x000b, mode_register);
7359 // port 81: DMA-1 Page Register, channel 2
7360 outb(0x0081, page);
7361 outb(0x000a, 0x02);
7363 // set up floppy controller for transfer
7364 floppy_prepare_controller(drive);
7366 // send format-track command (6 bytes) to controller
7367 outb(0x03f5, 0x4d); // 4d: format track
7368 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7369 outb(0x03f5, 2); // 512 byte sector size
7370 outb(0x03f5, num_sectors); // number of sectors per track
7371 outb(0x03f5, 0); // Gap length
7372 outb(0x03f5, 0xf6); // Fill byte
7373 // turn on interrupts
7374 ASM_START
7376 ASM_END
7378 // wait on 40:3e bit 7 to become 1
7379 do {
7380 val8 = read_byte(0x0040, 0x0040);
7381 if (val8 == 0) {
7382 floppy_reset_controller();
7383 SET_AH(0x80); // drive not ready (timeout)
7384 set_diskette_ret_status(0x80);
7385 SET_CF(); // error occurred
7386 return;
7388 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7389 } while ( val8 == 0 );
7391 val8 = 0; // separate asm from while() loop
7392 // turn off interrupts
7393 ASM_START
7395 ASM_END
7396 // set 40:3e bit 7 to 0
7397 val8 = read_byte(0x0040, 0x003e);
7398 val8 &= 0x7f;
7399 write_byte(0x0040, 0x003e, val8);
7400 // check port 3f4 for accessibility to status bytes
7401 val8 = inb(0x3f4);
7402 if ( (val8 & 0xc0) != 0xc0 )
7403 BX_PANIC("int13_diskette: ctrl not ready\n");
7405 // read 7 return status bytes from controller
7406 // using loop index broken, have to unroll...
7407 return_status[0] = inb(0x3f5);
7408 return_status[1] = inb(0x3f5);
7409 return_status[2] = inb(0x3f5);
7410 return_status[3] = inb(0x3f5);
7411 return_status[4] = inb(0x3f5);
7412 return_status[5] = inb(0x3f5);
7413 return_status[6] = inb(0x3f5);
7414 // record in BIOS Data Area
7415 write_byte(0x0040, 0x0042, return_status[0]);
7416 write_byte(0x0040, 0x0043, return_status[1]);
7417 write_byte(0x0040, 0x0044, return_status[2]);
7418 write_byte(0x0040, 0x0045, return_status[3]);
7419 write_byte(0x0040, 0x0046, return_status[4]);
7420 write_byte(0x0040, 0x0047, return_status[5]);
7421 write_byte(0x0040, 0x0048, return_status[6]);
7423 if ( (return_status[0] & 0xc0) != 0 ) {
7424 if ( (return_status[1] & 0x02) != 0 ) {
7425 // diskette not writable.
7426 // AH=status code=0x03 (tried to write on write-protected disk)
7427 // AL=number of sectors written=0
7428 AX = 0x0300;
7429 SET_CF();
7430 return;
7431 } else {
7432 BX_PANIC("int13_diskette_function: write error\n");
7436 SET_AH(0);
7437 set_diskette_ret_status(0);
7438 set_diskette_current_cyl(drive, 0);
7439 CLEAR_CF(); // successful
7440 return;
7443 case 0x08: // read diskette drive parameters
7444 BX_DEBUG_INT13_FL("floppy f08\n");
7445 drive = GET_ELDL();
7447 if (drive > 1) {
7448 AX = 0;
7449 BX = 0;
7450 CX = 0;
7451 DX = 0;
7452 ES = 0;
7453 DI = 0;
7454 SET_DL(num_floppies);
7455 SET_CF();
7456 return;
7459 drive_type = inb_cmos(0x10);
7460 num_floppies = 0;
7461 if (drive_type & 0xf0)
7462 num_floppies++;
7463 if (drive_type & 0x0f)
7464 num_floppies++;
7466 if (drive == 0)
7467 drive_type >>= 4;
7468 else
7469 drive_type &= 0x0f;
7471 SET_BH(0);
7472 SET_BL(drive_type);
7473 SET_AH(0);
7474 SET_AL(0);
7475 SET_DL(num_floppies);
7477 switch (drive_type) {
7478 case 0: // none
7479 CX = 0;
7480 SET_DH(0); // max head #
7481 break;
7483 case 1: // 360KB, 5.25"
7484 CX = 0x2709; // 40 tracks, 9 sectors
7485 SET_DH(1); // max head #
7486 break;
7488 case 2: // 1.2MB, 5.25"
7489 CX = 0x4f0f; // 80 tracks, 15 sectors
7490 SET_DH(1); // max head #
7491 break;
7493 case 3: // 720KB, 3.5"
7494 CX = 0x4f09; // 80 tracks, 9 sectors
7495 SET_DH(1); // max head #
7496 break;
7498 case 4: // 1.44MB, 3.5"
7499 CX = 0x4f12; // 80 tracks, 18 sectors
7500 SET_DH(1); // max head #
7501 break;
7503 case 5: // 2.88MB, 3.5"
7504 CX = 0x4f24; // 80 tracks, 36 sectors
7505 SET_DH(1); // max head #
7506 break;
7508 case 6: // 160k, 5.25"
7509 CX = 0x2708; // 40 tracks, 8 sectors
7510 SET_DH(0); // max head #
7511 break;
7513 case 7: // 180k, 5.25"
7514 CX = 0x2709; // 40 tracks, 9 sectors
7515 SET_DH(0); // max head #
7516 break;
7518 case 8: // 320k, 5.25"
7519 CX = 0x2708; // 40 tracks, 8 sectors
7520 SET_DH(1); // max head #
7521 break;
7523 default: // ?
7524 BX_PANIC("floppy: int13: bad floppy type\n");
7527 /* set es & di to point to 11 byte diskette param table in ROM */
7528 ASM_START
7529 push bp
7530 mov bp, sp
7531 mov ax, #diskette_param_table2
7532 mov _int13_diskette_function.DI+2[bp], ax
7533 mov _int13_diskette_function.ES+2[bp], cs
7534 pop bp
7535 ASM_END
7536 CLEAR_CF(); // success
7537 /* disk status not changed upon success */
7538 return;
7541 case 0x15: // read diskette drive type
7542 BX_DEBUG_INT13_FL("floppy f15\n");
7543 drive = GET_ELDL();
7544 if (drive > 1) {
7545 SET_AH(0); // only 2 drives supported
7546 // set_diskette_ret_status here ???
7547 SET_CF();
7548 return;
7550 drive_type = inb_cmos(0x10);
7552 if (drive == 0)
7553 drive_type >>= 4;
7554 else
7555 drive_type &= 0x0f;
7556 CLEAR_CF(); // successful, not present
7557 if (drive_type==0) {
7558 SET_AH(0); // drive not present
7560 else {
7561 SET_AH(1); // drive present, does not support change line
7564 return;
7566 case 0x16: // get diskette change line status
7567 BX_DEBUG_INT13_FL("floppy f16\n");
7568 drive = GET_ELDL();
7569 if (drive > 1) {
7570 SET_AH(0x01); // invalid drive
7571 set_diskette_ret_status(0x01);
7572 SET_CF();
7573 return;
7576 SET_AH(0x06); // change line not supported
7577 set_diskette_ret_status(0x06);
7578 SET_CF();
7579 return;
7581 case 0x17: // set diskette type for format(old)
7582 BX_DEBUG_INT13_FL("floppy f17\n");
7583 /* not used for 1.44M floppies */
7584 SET_AH(0x01); // not supported
7585 set_diskette_ret_status(1); /* not supported */
7586 SET_CF();
7587 return;
7589 case 0x18: // set diskette type for format(new)
7590 BX_DEBUG_INT13_FL("floppy f18\n");
7591 SET_AH(0x01); // do later
7592 set_diskette_ret_status(1);
7593 SET_CF();
7594 return;
7596 default:
7597 BX_INFO("int13_diskette: unsupported AH=%02x\n", GET_AH());
7599 // if ( (ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e) ) {
7600 SET_AH(0x01); // ???
7601 set_diskette_ret_status(1);
7602 SET_CF();
7603 return;
7604 // }
7607 #else // #if BX_SUPPORT_FLOPPY
7608 void
7609 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
7610 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
7612 Bit8u val8;
7614 switch ( GET_AH() ) {
7616 case 0x01: // Read Diskette Status
7617 CLEAR_CF();
7618 val8 = read_byte(0x0000, 0x0441);
7619 SET_AH(val8);
7620 if (val8) {
7621 SET_CF();
7623 return;
7625 default:
7626 SET_CF();
7627 write_byte(0x0000, 0x0441, 0x01);
7628 SET_AH(0x01);
7631 #endif // #if BX_SUPPORT_FLOPPY
7633 void
7634 set_diskette_ret_status(value)
7635 Bit8u value;
7637 write_byte(0x0040, 0x0041, value);
7640 void
7641 set_diskette_current_cyl(drive, cyl)
7642 Bit8u drive;
7643 Bit8u cyl;
7645 if (drive > 1)
7646 BX_PANIC("set_diskette_current_cyl(): drive > 1\n");
7647 write_byte(0x0040, 0x0094+drive, cyl);
7650 void
7651 determine_floppy_media(drive)
7652 Bit16u drive;
7654 #if 0
7655 Bit8u val8, DOR, ctrl_info;
7657 ctrl_info = read_byte(0x0040, 0x008F);
7658 if (drive==1)
7659 ctrl_info >>= 4;
7660 else
7661 ctrl_info &= 0x0f;
7663 #if 0
7664 if (drive == 0) {
7665 DOR = 0x1c; // DOR: drive0 motor on, DMA&int enabled, normal op, drive select 0
7667 else {
7668 DOR = 0x2d; // DOR: drive1 motor on, DMA&int enabled, normal op, drive select 1
7670 #endif
7672 if ( (ctrl_info & 0x04) != 0x04 ) {
7673 // Drive not determined means no drive exists, done.
7674 return;
7677 #if 0
7678 // check Main Status Register for readiness
7679 val8 = inb(0x03f4) & 0x80; // Main Status Register
7680 if (val8 != 0x80)
7681 BX_PANIC("d_f_m: MRQ bit not set\n");
7683 // change line
7685 // existing BDA values
7687 // turn on drive motor
7688 outb(0x03f2, DOR); // Digital Output Register
7690 #endif
7691 BX_PANIC("d_f_m: OK so far\n");
7692 #endif
7695 void
7696 int17_function(regs, ds, iret_addr)
7697 pusha_regs_t regs; // regs pushed from PUSHA instruction
7698 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7699 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7701 Bit16u addr,timeout;
7702 Bit8u val8;
7704 ASM_START
7706 ASM_END
7708 addr = read_word(0x0040, (regs.u.r16.dx << 1) + 8);
7709 if ((regs.u.r8.ah < 3) && (regs.u.r16.dx < 3) && (addr > 0)) {
7710 timeout = read_byte(0x0040, 0x0078 + regs.u.r16.dx) << 8;
7711 if (regs.u.r8.ah == 0) {
7712 outb(addr, regs.u.r8.al);
7713 val8 = inb(addr+2);
7714 outb(addr+2, val8 | 0x01); // send strobe
7715 ASM_START
7717 ASM_END
7718 outb(addr+2, val8 & ~0x01);
7719 while (((inb(addr+1) & 0x40) == 0x40) && (timeout)) {
7720 timeout--;
7723 if (regs.u.r8.ah == 1) {
7724 val8 = inb(addr+2);
7725 outb(addr+2, val8 & ~0x04); // send init
7726 ASM_START
7728 ASM_END
7729 outb(addr+2, val8 | 0x04);
7731 val8 = inb(addr+1);
7732 regs.u.r8.ah = (val8 ^ 0x48);
7733 if (!timeout) regs.u.r8.ah |= 0x01;
7734 ClearCF(iret_addr.flags);
7735 } else {
7736 SetCF(iret_addr.flags); // Unsupported
7740 void
7741 int19_function(seq_nr)
7742 Bit16u seq_nr;
7744 Bit16u ebda_seg=read_word(0x0040,0x000E);
7745 Bit16u bootdev;
7746 Bit8u bootdrv;
7747 Bit8u bootchk;
7748 Bit16u bootseg;
7749 Bit16u bootip;
7750 Bit16u status;
7752 ipl_entry_t e;
7754 // if BX_ELTORITO_BOOT is not defined, old behavior
7755 // check bit 5 in CMOS reg 0x2d. load either 0x00 or 0x80 into DL
7756 // in preparation for the intial INT 13h (0=floppy A:, 0x80=C:)
7757 // 0: system boot sequence, first drive C: then A:
7758 // 1: system boot sequence, first drive A: then C:
7759 // else BX_ELTORITO_BOOT is defined
7760 // CMOS regs 0x3D and 0x38 contain the boot sequence:
7761 // CMOS reg 0x3D & 0x0f : 1st boot device
7762 // CMOS reg 0x3D & 0xf0 : 2nd boot device
7763 // CMOS reg 0x38 & 0xf0 : 3rd boot device
7764 // boot device codes:
7765 // 0x00 : not defined
7766 // 0x01 : first floppy
7767 // 0x02 : first harddrive
7768 // 0x03 : first cdrom
7769 // 0x04 - 0x0f : PnP expansion ROMs (e.g. Etherboot)
7770 // else : boot failure
7772 // Get the boot sequence
7773 #if BX_ELTORITO_BOOT
7774 bootdev = inb_cmos(0x3d);
7775 bootdev |= ((inb_cmos(0x38) & 0xf0) << 4);
7776 bootdev >>= 4 * seq_nr;
7777 bootdev &= 0xf;
7778 if (bootdev == 0) BX_PANIC("No bootable device.\n");
7780 /* Translate from CMOS runes to an IPL table offset by subtracting 1 */
7781 bootdev -= 1;
7782 #else
7783 if (seq_nr ==2) BX_PANIC("No more boot devices.");
7784 if (!!(inb_cmos(0x2d) & 0x20) ^ (seq_nr == 1))
7785 /* Boot from floppy if the bit is set or it's the second boot */
7786 bootdev = 0x00;
7787 else
7788 bootdev = 0x01;
7789 #endif
7791 /* Read the boot device from the IPL table */
7792 if (get_boot_vector(bootdev, &e) == 0) {
7793 BX_INFO("Invalid boot device (0x%x)\n", bootdev);
7794 return;
7797 /* Do the loading, and set up vector as a far pointer to the boot
7798 * address, and bootdrv as the boot drive */
7799 print_boot_device(e.type);
7801 switch(e.type) {
7802 case IPL_TYPE_FLOPPY: /* FDD */
7803 case IPL_TYPE_HARDDISK: /* HDD */
7805 bootdrv = (e.type == IPL_TYPE_HARDDISK) ? 0x80 : 0x00;
7806 bootseg = 0x07c0;
7807 status = 0;
7809 ASM_START
7810 push bp
7811 mov bp, sp
7812 push ax
7813 push bx
7814 push cx
7815 push dx
7817 mov dl, _int19_function.bootdrv + 2[bp]
7818 mov ax, _int19_function.bootseg + 2[bp]
7819 mov es, ax ;; segment
7820 xor bx, bx ;; offset
7821 mov ah, #0x02 ;; function 2, read diskette sector
7822 mov al, #0x01 ;; read 1 sector
7823 mov ch, #0x00 ;; track 0
7824 mov cl, #0x01 ;; sector 1
7825 mov dh, #0x00 ;; head 0
7826 int #0x13 ;; read sector
7827 jnc int19_load_done
7828 mov ax, #0x0001
7829 mov _int19_function.status + 2[bp], ax
7831 int19_load_done:
7832 pop dx
7833 pop cx
7834 pop bx
7835 pop ax
7836 pop bp
7837 ASM_END
7839 if (status != 0) {
7840 print_boot_failure(e.type, 1);
7841 return;
7844 /* Always check the signature on a HDD boot sector; on FDD, only do
7845 * the check if the CMOS doesn't tell us to skip it */
7846 if ((e.type != IPL_TYPE_FLOPPY) || !((inb_cmos(0x38) & 0x01))) {
7847 if (read_word(bootseg,0x1fe) != 0xaa55) {
7848 print_boot_failure(e.type, 0);
7849 return;
7853 /* Canonicalize bootseg:bootip */
7854 bootip = (bootseg & 0x0fff) << 4;
7855 bootseg &= 0xf000;
7856 break;
7858 #if BX_ELTORITO_BOOT
7859 case IPL_TYPE_CDROM: /* CD-ROM */
7860 status = cdrom_boot();
7862 // If failure
7863 if ( (status & 0x00ff) !=0 ) {
7864 print_cdromboot_failure(status);
7865 print_boot_failure(e.type, 1);
7866 return;
7869 bootdrv = (Bit8u)(status>>8);
7870 bootseg = read_word(ebda_seg,&EbdaData->cdemu.load_segment);
7871 /* Canonicalize bootseg:bootip */
7872 bootip = (bootseg & 0x0fff) << 4;
7873 bootseg &= 0xf000;
7874 break;
7875 #endif
7877 case IPL_TYPE_BEV: /* Expansion ROM with a Bootstrap Entry Vector (a far pointer) */
7878 bootseg = e.vector >> 16;
7879 bootip = e.vector & 0xffff;
7880 break;
7882 default: return;
7885 /* Debugging info */
7886 BX_INFO("Booting from %x:%x\n", bootseg, bootip);
7888 /* Jump to the boot vector */
7889 ASM_START
7890 mov bp, sp
7891 ;; Build an iret stack frame that will take us to the boot vector.
7892 ;; iret pops ip, then cs, then flags, so push them in the opposite order.
7893 pushf
7894 mov ax, _int19_function.bootseg + 0[bp]
7895 push ax
7896 mov ax, _int19_function.bootip + 0[bp]
7897 push ax
7898 ;; Set the magic number in ax and the boot drive in dl.
7899 mov ax, #0xaa55
7900 mov dl, _int19_function.bootdrv + 0[bp]
7901 ;; Zero some of the other registers.
7902 xor bx, bx
7903 mov ds, bx
7904 mov es, bx
7905 mov bp, bx
7906 ;; Go!
7907 iret
7908 ASM_END
7911 void
7912 int1a_function(regs, ds, iret_addr)
7913 pusha_regs_t regs; // regs pushed from PUSHA instruction
7914 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7915 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7917 Bit8u val8;
7919 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);
7921 ASM_START
7923 ASM_END
7925 switch (regs.u.r8.ah) {
7926 case 0: // get current clock count
7927 ASM_START
7929 ASM_END
7930 regs.u.r16.cx = BiosData->ticks_high;
7931 regs.u.r16.dx = BiosData->ticks_low;
7932 regs.u.r8.al = BiosData->midnight_flag;
7933 BiosData->midnight_flag = 0; // reset flag
7934 ASM_START
7936 ASM_END
7937 // AH already 0
7938 ClearCF(iret_addr.flags); // OK
7939 break;
7941 case 1: // Set Current Clock Count
7942 ASM_START
7944 ASM_END
7945 BiosData->ticks_high = regs.u.r16.cx;
7946 BiosData->ticks_low = regs.u.r16.dx;
7947 BiosData->midnight_flag = 0; // reset flag
7948 ASM_START
7950 ASM_END
7951 regs.u.r8.ah = 0;
7952 ClearCF(iret_addr.flags); // OK
7953 break;
7956 case 2: // Read CMOS Time
7957 if (rtc_updating()) {
7958 SetCF(iret_addr.flags);
7959 break;
7962 regs.u.r8.dh = inb_cmos(0x00); // Seconds
7963 regs.u.r8.cl = inb_cmos(0x02); // Minutes
7964 regs.u.r8.ch = inb_cmos(0x04); // Hours
7965 regs.u.r8.dl = inb_cmos(0x0b) & 0x01; // Stat Reg B
7966 regs.u.r8.ah = 0;
7967 regs.u.r8.al = regs.u.r8.ch;
7968 ClearCF(iret_addr.flags); // OK
7969 break;
7971 case 3: // Set CMOS Time
7972 // Using a debugger, I notice the following masking/setting
7973 // of bits in Status Register B, by setting Reg B to
7974 // a few values and getting its value after INT 1A was called.
7976 // try#1 try#2 try#3
7977 // before 1111 1101 0111 1101 0000 0000
7978 // after 0110 0010 0110 0010 0000 0010
7980 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7981 // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
7982 if (rtc_updating()) {
7983 init_rtc();
7984 // fall through as if an update were not in progress
7986 outb_cmos(0x00, regs.u.r8.dh); // Seconds
7987 outb_cmos(0x02, regs.u.r8.cl); // Minutes
7988 outb_cmos(0x04, regs.u.r8.ch); // Hours
7989 // Set Daylight Savings time enabled bit to requested value
7990 val8 = (inb_cmos(0x0b) & 0x60) | 0x02 | (regs.u.r8.dl & 0x01);
7991 // (reg B already selected)
7992 outb_cmos(0x0b, val8);
7993 regs.u.r8.ah = 0;
7994 regs.u.r8.al = val8; // val last written to Reg B
7995 ClearCF(iret_addr.flags); // OK
7996 break;
7998 case 4: // Read CMOS Date
7999 regs.u.r8.ah = 0;
8000 if (rtc_updating()) {
8001 SetCF(iret_addr.flags);
8002 break;
8004 regs.u.r8.cl = inb_cmos(0x09); // Year
8005 regs.u.r8.dh = inb_cmos(0x08); // Month
8006 regs.u.r8.dl = inb_cmos(0x07); // Day of Month
8007 regs.u.r8.ch = inb_cmos(0x32); // Century
8008 regs.u.r8.al = regs.u.r8.ch;
8009 ClearCF(iret_addr.flags); // OK
8010 break;
8012 case 5: // Set CMOS Date
8013 // Using a debugger, I notice the following masking/setting
8014 // of bits in Status Register B, by setting Reg B to
8015 // a few values and getting its value after INT 1A was called.
8017 // try#1 try#2 try#3 try#4
8018 // before 1111 1101 0111 1101 0000 0010 0000 0000
8019 // after 0110 1101 0111 1101 0000 0010 0000 0000
8021 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8022 // My assumption: RegB = (RegB & 01111111b)
8023 if (rtc_updating()) {
8024 init_rtc();
8025 SetCF(iret_addr.flags);
8026 break;
8028 outb_cmos(0x09, regs.u.r8.cl); // Year
8029 outb_cmos(0x08, regs.u.r8.dh); // Month
8030 outb_cmos(0x07, regs.u.r8.dl); // Day of Month
8031 outb_cmos(0x32, regs.u.r8.ch); // Century
8032 val8 = inb_cmos(0x0b) & 0x7f; // clear halt-clock bit
8033 outb_cmos(0x0b, val8);
8034 regs.u.r8.ah = 0;
8035 regs.u.r8.al = val8; // AL = val last written to Reg B
8036 ClearCF(iret_addr.flags); // OK
8037 break;
8039 case 6: // Set Alarm Time in CMOS
8040 // Using a debugger, I notice the following masking/setting
8041 // of bits in Status Register B, by setting Reg B to
8042 // a few values and getting its value after INT 1A was called.
8044 // try#1 try#2 try#3
8045 // before 1101 1111 0101 1111 0000 0000
8046 // after 0110 1111 0111 1111 0010 0000
8048 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8049 // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
8050 val8 = inb_cmos(0x0b); // Get Status Reg B
8051 regs.u.r16.ax = 0;
8052 if (val8 & 0x20) {
8053 // Alarm interrupt enabled already
8054 SetCF(iret_addr.flags); // Error: alarm in use
8055 break;
8057 if (rtc_updating()) {
8058 init_rtc();
8059 // fall through as if an update were not in progress
8061 outb_cmos(0x01, regs.u.r8.dh); // Seconds alarm
8062 outb_cmos(0x03, regs.u.r8.cl); // Minutes alarm
8063 outb_cmos(0x05, regs.u.r8.ch); // Hours alarm
8064 outb(0xa1, inb(0xa1) & 0xfe); // enable IRQ 8
8065 // enable Status Reg B alarm bit, clear halt clock bit
8066 outb_cmos(0x0b, (val8 & 0x7f) | 0x20);
8067 ClearCF(iret_addr.flags); // OK
8068 break;
8070 case 7: // Turn off Alarm
8071 // Using a debugger, I notice the following masking/setting
8072 // of bits in Status Register B, by setting Reg B to
8073 // a few values and getting its value after INT 1A was called.
8075 // try#1 try#2 try#3 try#4
8076 // before 1111 1101 0111 1101 0010 0000 0010 0010
8077 // after 0100 0101 0101 0101 0000 0000 0000 0010
8079 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8080 // My assumption: RegB = (RegB & 01010111b)
8081 val8 = inb_cmos(0x0b); // Get Status Reg B
8082 // clear clock-halt bit, disable alarm bit
8083 outb_cmos(0x0b, val8 & 0x57); // disable alarm bit
8084 regs.u.r8.ah = 0;
8085 regs.u.r8.al = val8; // val last written to Reg B
8086 ClearCF(iret_addr.flags); // OK
8087 break;
8088 #if BX_PCIBIOS
8089 case 0xb1:
8090 // real mode PCI BIOS functions now handled in assembler code
8091 // this C code handles the error code for information only
8092 if (regs.u.r8.bl == 0xff) {
8093 BX_INFO("PCI BIOS: PCI not present\n");
8094 } else if (regs.u.r8.bl == 0x81) {
8095 BX_INFO("unsupported PCI BIOS function 0x%02x\n", regs.u.r8.al);
8096 } else if (regs.u.r8.bl == 0x83) {
8097 BX_INFO("bad PCI vendor ID %04x\n", regs.u.r16.dx);
8098 } else if (regs.u.r8.bl == 0x86) {
8099 if (regs.u.r8.al == 0x02) {
8100 BX_INFO("PCI device %04x:%04x not found at index %d\n", regs.u.r16.dx, regs.u.r16.cx, regs.u.r16.si);
8101 } else {
8102 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);
8105 regs.u.r8.ah = regs.u.r8.bl;
8106 SetCF(iret_addr.flags);
8107 break;
8108 #endif
8110 default:
8111 SetCF(iret_addr.flags); // Unsupported
8115 void
8116 int70_function(regs, ds, iret_addr)
8117 pusha_regs_t regs; // regs pushed from PUSHA instruction
8118 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
8119 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
8121 // INT 70h: IRQ 8 - CMOS RTC interrupt from periodic or alarm modes
8122 Bit8u registerB = 0, registerC = 0;
8124 // Check which modes are enabled and have occurred.
8125 registerB = inb_cmos( 0xB );
8126 registerC = inb_cmos( 0xC );
8128 if( ( registerB & 0x60 ) != 0 ) {
8129 if( ( registerC & 0x20 ) != 0 ) {
8130 // Handle Alarm Interrupt.
8131 ASM_START
8133 int #0x4a
8135 ASM_END
8137 if( ( registerC & 0x40 ) != 0 ) {
8138 // Handle Periodic Interrupt.
8140 if( read_byte( 0x40, 0xA0 ) != 0 ) {
8141 // Wait Interval (Int 15, AH=83) active.
8142 Bit32u time, toggle;
8144 time = read_dword( 0x40, 0x9C ); // Time left in microseconds.
8145 if( time < 0x3D1 ) {
8146 // Done waiting.
8147 Bit16u segment, offset;
8149 segment = read_word( 0x40, 0x98 );
8150 offset = read_word( 0x40, 0x9A );
8151 write_byte( 0x40, 0xA0, 0 ); // Turn of status byte.
8152 outb_cmos( 0xB, registerB & 0x37 ); // Clear the Periodic Interrupt.
8153 write_byte(segment, offset, read_byte(segment, offset) | 0x80 ); // Write to specified flag byte.
8154 } else {
8155 // Continue waiting.
8156 time -= 0x3D1;
8157 write_dword( 0x40, 0x9C, time );
8163 ASM_START
8164 call eoi_both_pics
8165 ASM_END
8169 ASM_START
8170 ;------------------------------------------
8171 ;- INT74h : PS/2 mouse hardware interrupt -
8172 ;------------------------------------------
8173 int74_handler:
8175 pusha
8176 push ds ;; save DS
8177 push #0x00 ;; placeholder for status
8178 push #0x00 ;; placeholder for X
8179 push #0x00 ;; placeholder for Y
8180 push #0x00 ;; placeholder for Z
8181 push #0x00 ;; placeholder for make_far_call boolean
8182 call _int74_function
8183 pop cx ;; remove make_far_call from stack
8184 jcxz int74_done
8186 ;; make far call to EBDA:0022
8187 push #0x00
8188 pop ds
8189 push 0x040E ;; push 0000:040E (opcodes 0xff, 0x36, 0x0E, 0x04)
8190 pop ds
8191 //CALL_EP(0x0022) ;; call far routine (call_Ep DS:0022 :opcodes 0xff, 0x1e, 0x22, 0x00)
8192 call far ptr[0x22]
8193 int74_done:
8195 call eoi_both_pics
8196 add sp, #8 ;; pop status, x, y, z
8198 pop ds ;; restore DS
8199 popa
8200 iret
8203 ;; This will perform an IRET, but will retain value of current CF
8204 ;; by altering flags on stack. Better than RETF #02.
8205 iret_modify_cf:
8206 jc carry_set
8207 push bp
8208 mov bp, sp
8209 and BYTE [bp + 0x06], #0xfe
8210 pop bp
8211 iret
8212 carry_set:
8213 push bp
8214 mov bp, sp
8215 or BYTE [bp + 0x06], #0x01
8216 pop bp
8217 iret
8220 ;----------------------
8221 ;- INT13h (relocated) -
8222 ;----------------------
8224 ; int13_relocated is a little bit messed up since I played with it
8225 ; I have to rewrite it:
8226 ; - call a function that detect which function to call
8227 ; - make all called C function get the same parameters list
8229 int13_relocated:
8231 #if BX_ELTORITO_BOOT
8232 ;; check for an eltorito function
8233 cmp ah,#0x4a
8234 jb int13_not_eltorito
8235 cmp ah,#0x4d
8236 ja int13_not_eltorito
8238 pusha
8239 push es
8240 push ds
8241 push ss
8242 pop ds
8244 push #int13_out
8245 jmp _int13_eltorito ;; ELDX not used
8247 int13_not_eltorito:
8248 push ax
8249 push bx
8250 push cx
8251 push dx
8253 ;; check if emulation active
8254 call _cdemu_isactive
8255 cmp al,#0x00
8256 je int13_cdemu_inactive
8258 ;; check if access to the emulated drive
8259 call _cdemu_emulated_drive
8260 pop dx
8261 push dx
8262 cmp al,dl ;; int13 on emulated drive
8263 jne int13_nocdemu
8265 pop dx
8266 pop cx
8267 pop bx
8268 pop ax
8270 pusha
8271 push es
8272 push ds
8273 push ss
8274 pop ds
8276 push #int13_out
8277 jmp _int13_cdemu ;; ELDX not used
8279 int13_nocdemu:
8280 and dl,#0xE0 ;; mask to get device class, including cdroms
8281 cmp al,dl ;; al is 0x00 or 0x80
8282 jne int13_cdemu_inactive ;; inactive for device class
8284 pop dx
8285 pop cx
8286 pop bx
8287 pop ax
8289 push ax
8290 push cx
8291 push dx
8292 push bx
8294 dec dl ;; real drive is dl - 1
8295 jmp int13_legacy
8297 int13_cdemu_inactive:
8298 pop dx
8299 pop cx
8300 pop bx
8301 pop ax
8303 #endif // BX_ELTORITO_BOOT
8305 int13_noeltorito:
8307 push ax
8308 push cx
8309 push dx
8310 push bx
8312 int13_legacy:
8314 push dx ;; push eltorito value of dx instead of sp
8316 push bp
8317 push si
8318 push di
8320 push es
8321 push ds
8322 push ss
8323 pop ds
8325 ;; now the 16-bit registers can be restored with:
8326 ;; pop ds; pop es; popa; iret
8327 ;; arguments passed to functions should be
8328 ;; DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS
8330 test dl, #0x80
8331 jnz int13_notfloppy
8333 push #int13_out
8334 jmp _int13_diskette_function
8336 int13_notfloppy:
8338 #if BX_USE_ATADRV
8340 cmp dl, #0xE0
8341 jb int13_notcdrom
8343 // ebx is modified: BSD 5.2.1 boot loader problem
8344 // someone should figure out which 32 bit register that actually are used
8346 shr ebx, #16
8347 push bx
8349 call _int13_cdrom
8351 pop bx
8352 shl ebx, #16
8354 jmp int13_out
8356 int13_notcdrom:
8358 #endif
8360 int13_disk:
8361 ;; int13_harddisk modifies high word of EAX
8362 shr eax, #16
8363 push ax
8364 call _int13_harddisk
8365 pop ax
8366 shl eax, #16
8368 int13_out:
8369 pop ds
8370 pop es
8371 popa
8372 iret
8374 ;----------
8375 ;- INT18h -
8376 ;----------
8377 int18_handler: ;; Boot Failure recovery: try the next device.
8379 ;; Reset SP and SS
8380 mov ax, #0xfffe
8381 mov sp, ax
8382 xor ax, ax
8383 mov ss, ax
8385 ;; Get the boot sequence number out of the IPL memory
8386 mov bx, #IPL_SEG
8387 mov ds, bx ;; Set segment
8388 mov bx, IPL_SEQUENCE_OFFSET ;; BX is now the sequence number
8389 inc bx ;; ++
8390 mov IPL_SEQUENCE_OFFSET, bx ;; Write it back
8391 mov ds, ax ;; and reset the segment to zero.
8393 ;; Carry on in the INT 19h handler, using the new sequence number
8394 push bx
8396 jmp int19_next_boot
8398 ;----------
8399 ;- INT19h -
8400 ;----------
8401 int19_relocated: ;; Boot function, relocated
8403 ;; int19 was beginning to be really complex, so now it
8404 ;; just calls a C function that does the work
8406 push bp
8407 mov bp, sp
8409 ;; Reset SS and SP
8410 mov ax, #0xfffe
8411 mov sp, ax
8412 xor ax, ax
8413 mov ss, ax
8415 ;; Start from the first boot device (0, in AX)
8416 mov bx, #IPL_SEG
8417 mov ds, bx ;; Set segment to write to the IPL memory
8418 mov IPL_SEQUENCE_OFFSET, ax ;; Save the sequence number
8419 mov ds, ax ;; and reset the segment.
8421 push ax
8423 int19_next_boot:
8425 ;; Call the C code for the next boot device
8426 call _int19_function
8428 ;; Boot failed: invoke the boot recovery function
8429 int #0x18
8431 ;----------
8432 ;- INT1Ch -
8433 ;----------
8434 int1c_handler: ;; User Timer Tick
8435 iret
8438 ;----------------------
8439 ;- POST: Floppy Drive -
8440 ;----------------------
8441 floppy_drive_post:
8442 xor ax, ax
8443 mov ds, ax
8445 mov al, #0x00
8446 mov 0x043e, al ;; drive 0 & 1 uncalibrated, no interrupt has occurred
8448 mov 0x043f, al ;; diskette motor status: read op, drive0, motors off
8450 mov 0x0440, al ;; diskette motor timeout counter: not active
8451 mov 0x0441, al ;; diskette controller status return code
8453 mov 0x0442, al ;; disk & diskette controller status register 0
8454 mov 0x0443, al ;; diskette controller status register 1
8455 mov 0x0444, al ;; diskette controller status register 2
8456 mov 0x0445, al ;; diskette controller cylinder number
8457 mov 0x0446, al ;; diskette controller head number
8458 mov 0x0447, al ;; diskette controller sector number
8459 mov 0x0448, al ;; diskette controller bytes written
8461 mov 0x048b, al ;; diskette configuration data
8463 ;; -----------------------------------------------------------------
8464 ;; (048F) diskette controller information
8466 mov al, #0x10 ;; get CMOS diskette drive type
8467 out 0x70, AL
8468 in AL, 0x71
8469 mov ah, al ;; save byte to AH
8471 look_drive0:
8472 shr al, #4 ;; look at top 4 bits for drive 0
8473 jz f0_missing ;; jump if no drive0
8474 mov bl, #0x07 ;; drive0 determined, multi-rate, has changed line
8475 jmp look_drive1
8476 f0_missing:
8477 mov bl, #0x00 ;; no drive0
8479 look_drive1:
8480 mov al, ah ;; restore from AH
8481 and al, #0x0f ;; look at bottom 4 bits for drive 1
8482 jz f1_missing ;; jump if no drive1
8483 or bl, #0x70 ;; drive1 determined, multi-rate, has changed line
8484 f1_missing:
8485 ;; leave high bits in BL zerod
8486 mov 0x048f, bl ;; put new val in BDA (diskette controller information)
8487 ;; -----------------------------------------------------------------
8489 mov al, #0x00
8490 mov 0x0490, al ;; diskette 0 media state
8491 mov 0x0491, al ;; diskette 1 media state
8493 ;; diskette 0,1 operational starting state
8494 ;; drive type has not been determined,
8495 ;; has no changed detection line
8496 mov 0x0492, al
8497 mov 0x0493, al
8499 mov 0x0494, al ;; diskette 0 current cylinder
8500 mov 0x0495, al ;; diskette 1 current cylinder
8502 mov al, #0x02
8503 out #0x0a, al ;; clear DMA-1 channel 2 mask bit
8505 SET_INT_VECTOR(0x1E, #0xF000, #diskette_param_table2)
8506 SET_INT_VECTOR(0x40, #0xF000, #int13_diskette)
8507 SET_INT_VECTOR(0x0E, #0xF000, #int0e_handler) ;; IRQ 6
8512 ;--------------------
8513 ;- POST: HARD DRIVE -
8514 ;--------------------
8515 ; relocated here because the primary POST area isnt big enough.
8516 hard_drive_post:
8517 // IRQ 14 = INT 76h
8518 // INT 76h calls INT 15h function ax=9100
8520 mov al, #0x0a ; 0000 1010 = reserved, disable IRQ 14
8521 mov dx, #0x03f6
8522 out dx, al
8524 xor ax, ax
8525 mov ds, ax
8526 mov 0x0474, al /* hard disk status of last operation */
8527 mov 0x0477, al /* hard disk port offset (XT only ???) */
8528 mov 0x048c, al /* hard disk status register */
8529 mov 0x048d, al /* hard disk error register */
8530 mov 0x048e, al /* hard disk task complete flag */
8531 mov al, #0x01
8532 mov 0x0475, al /* hard disk number attached */
8533 mov al, #0xc0
8534 mov 0x0476, al /* hard disk control byte */
8535 SET_INT_VECTOR(0x13, #0xF000, #int13_handler)
8536 SET_INT_VECTOR(0x76, #0xF000, #int76_handler)
8537 ;; INT 41h: hard disk 0 configuration pointer
8538 ;; INT 46h: hard disk 1 configuration pointer
8539 SET_INT_VECTOR(0x41, #EBDA_SEG, #0x003D)
8540 SET_INT_VECTOR(0x46, #EBDA_SEG, #0x004D)
8542 ;; move disk geometry data from CMOS to EBDA disk parameter table(s)
8543 mov al, #0x12
8544 out #0x70, al
8545 in al, #0x71
8546 and al, #0xf0
8547 cmp al, #0xf0
8548 je post_d0_extended
8549 jmp check_for_hd1
8550 post_d0_extended:
8551 mov al, #0x19
8552 out #0x70, al
8553 in al, #0x71
8554 cmp al, #47 ;; decimal 47 - user definable
8555 je post_d0_type47
8556 HALT(__LINE__)
8557 post_d0_type47:
8558 ;; CMOS purpose param table offset
8559 ;; 1b cylinders low 0
8560 ;; 1c cylinders high 1
8561 ;; 1d heads 2
8562 ;; 1e write pre-comp low 5
8563 ;; 1f write pre-comp high 6
8564 ;; 20 retries/bad map/heads>8 8
8565 ;; 21 landing zone low C
8566 ;; 22 landing zone high D
8567 ;; 23 sectors/track E
8569 mov ax, #EBDA_SEG
8570 mov ds, ax
8572 ;;; Filling EBDA table for hard disk 0.
8573 mov al, #0x1f
8574 out #0x70, al
8575 in al, #0x71
8576 mov ah, al
8577 mov al, #0x1e
8578 out #0x70, al
8579 in al, #0x71
8580 mov (0x003d + 0x05), ax ;; write precomp word
8582 mov al, #0x20
8583 out #0x70, al
8584 in al, #0x71
8585 mov (0x003d + 0x08), al ;; drive control byte
8587 mov al, #0x22
8588 out #0x70, al
8589 in al, #0x71
8590 mov ah, al
8591 mov al, #0x21
8592 out #0x70, al
8593 in al, #0x71
8594 mov (0x003d + 0x0C), ax ;; landing zone word
8596 mov al, #0x1c ;; get cylinders word in AX
8597 out #0x70, al
8598 in al, #0x71 ;; high byte
8599 mov ah, al
8600 mov al, #0x1b
8601 out #0x70, al
8602 in al, #0x71 ;; low byte
8603 mov bx, ax ;; BX = cylinders
8605 mov al, #0x1d
8606 out #0x70, al
8607 in al, #0x71
8608 mov cl, al ;; CL = heads
8610 mov al, #0x23
8611 out #0x70, al
8612 in al, #0x71
8613 mov dl, al ;; DL = sectors
8615 cmp bx, #1024
8616 jnbe hd0_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8618 hd0_post_physical_chs:
8619 ;; no logical CHS mapping used, just physical CHS
8620 ;; use Standard Fixed Disk Parameter Table (FDPT)
8621 mov (0x003d + 0x00), bx ;; number of physical cylinders
8622 mov (0x003d + 0x02), cl ;; number of physical heads
8623 mov (0x003d + 0x0E), dl ;; number of physical sectors
8624 jmp check_for_hd1
8626 hd0_post_logical_chs:
8627 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8628 mov (0x003d + 0x09), bx ;; number of physical cylinders
8629 mov (0x003d + 0x0b), cl ;; number of physical heads
8630 mov (0x003d + 0x04), dl ;; number of physical sectors
8631 mov (0x003d + 0x0e), dl ;; number of logical sectors (same)
8632 mov al, #0xa0
8633 mov (0x003d + 0x03), al ;; A0h signature, indicates translated table
8635 cmp bx, #2048
8636 jnbe hd0_post_above_2048
8637 ;; 1024 < c <= 2048 cylinders
8638 shr bx, #0x01
8639 shl cl, #0x01
8640 jmp hd0_post_store_logical
8642 hd0_post_above_2048:
8643 cmp bx, #4096
8644 jnbe hd0_post_above_4096
8645 ;; 2048 < c <= 4096 cylinders
8646 shr bx, #0x02
8647 shl cl, #0x02
8648 jmp hd0_post_store_logical
8650 hd0_post_above_4096:
8651 cmp bx, #8192
8652 jnbe hd0_post_above_8192
8653 ;; 4096 < c <= 8192 cylinders
8654 shr bx, #0x03
8655 shl cl, #0x03
8656 jmp hd0_post_store_logical
8658 hd0_post_above_8192:
8659 ;; 8192 < c <= 16384 cylinders
8660 shr bx, #0x04
8661 shl cl, #0x04
8663 hd0_post_store_logical:
8664 mov (0x003d + 0x00), bx ;; number of physical cylinders
8665 mov (0x003d + 0x02), cl ;; number of physical heads
8666 ;; checksum
8667 mov cl, #0x0f ;; repeat count
8668 mov si, #0x003d ;; offset to disk0 FDPT
8669 mov al, #0x00 ;; sum
8670 hd0_post_checksum_loop:
8671 add al, [si]
8672 inc si
8673 dec cl
8674 jnz hd0_post_checksum_loop
8675 not al ;; now take 2s complement
8676 inc al
8677 mov [si], al
8678 ;;; Done filling EBDA table for hard disk 0.
8681 check_for_hd1:
8682 ;; is there really a second hard disk? if not, return now
8683 mov al, #0x12
8684 out #0x70, al
8685 in al, #0x71
8686 and al, #0x0f
8687 jnz post_d1_exists
8689 post_d1_exists:
8690 ;; check that the hd type is really 0x0f.
8691 cmp al, #0x0f
8692 jz post_d1_extended
8693 HALT(__LINE__)
8694 post_d1_extended:
8695 ;; check that the extended type is 47 - user definable
8696 mov al, #0x1a
8697 out #0x70, al
8698 in al, #0x71
8699 cmp al, #47 ;; decimal 47 - user definable
8700 je post_d1_type47
8701 HALT(__LINE__)
8702 post_d1_type47:
8703 ;; Table for disk1.
8704 ;; CMOS purpose param table offset
8705 ;; 0x24 cylinders low 0
8706 ;; 0x25 cylinders high 1
8707 ;; 0x26 heads 2
8708 ;; 0x27 write pre-comp low 5
8709 ;; 0x28 write pre-comp high 6
8710 ;; 0x29 heads>8 8
8711 ;; 0x2a landing zone low C
8712 ;; 0x2b landing zone high D
8713 ;; 0x2c sectors/track E
8714 ;;; Fill EBDA table for hard disk 1.
8715 mov ax, #EBDA_SEG
8716 mov ds, ax
8717 mov al, #0x28
8718 out #0x70, al
8719 in al, #0x71
8720 mov ah, al
8721 mov al, #0x27
8722 out #0x70, al
8723 in al, #0x71
8724 mov (0x004d + 0x05), ax ;; write precomp word
8726 mov al, #0x29
8727 out #0x70, al
8728 in al, #0x71
8729 mov (0x004d + 0x08), al ;; drive control byte
8731 mov al, #0x2b
8732 out #0x70, al
8733 in al, #0x71
8734 mov ah, al
8735 mov al, #0x2a
8736 out #0x70, al
8737 in al, #0x71
8738 mov (0x004d + 0x0C), ax ;; landing zone word
8740 mov al, #0x25 ;; get cylinders word in AX
8741 out #0x70, al
8742 in al, #0x71 ;; high byte
8743 mov ah, al
8744 mov al, #0x24
8745 out #0x70, al
8746 in al, #0x71 ;; low byte
8747 mov bx, ax ;; BX = cylinders
8749 mov al, #0x26
8750 out #0x70, al
8751 in al, #0x71
8752 mov cl, al ;; CL = heads
8754 mov al, #0x2c
8755 out #0x70, al
8756 in al, #0x71
8757 mov dl, al ;; DL = sectors
8759 cmp bx, #1024
8760 jnbe hd1_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8762 hd1_post_physical_chs:
8763 ;; no logical CHS mapping used, just physical CHS
8764 ;; use Standard Fixed Disk Parameter Table (FDPT)
8765 mov (0x004d + 0x00), bx ;; number of physical cylinders
8766 mov (0x004d + 0x02), cl ;; number of physical heads
8767 mov (0x004d + 0x0E), dl ;; number of physical sectors
8770 hd1_post_logical_chs:
8771 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8772 mov (0x004d + 0x09), bx ;; number of physical cylinders
8773 mov (0x004d + 0x0b), cl ;; number of physical heads
8774 mov (0x004d + 0x04), dl ;; number of physical sectors
8775 mov (0x004d + 0x0e), dl ;; number of logical sectors (same)
8776 mov al, #0xa0
8777 mov (0x004d + 0x03), al ;; A0h signature, indicates translated table
8779 cmp bx, #2048
8780 jnbe hd1_post_above_2048
8781 ;; 1024 < c <= 2048 cylinders
8782 shr bx, #0x01
8783 shl cl, #0x01
8784 jmp hd1_post_store_logical
8786 hd1_post_above_2048:
8787 cmp bx, #4096
8788 jnbe hd1_post_above_4096
8789 ;; 2048 < c <= 4096 cylinders
8790 shr bx, #0x02
8791 shl cl, #0x02
8792 jmp hd1_post_store_logical
8794 hd1_post_above_4096:
8795 cmp bx, #8192
8796 jnbe hd1_post_above_8192
8797 ;; 4096 < c <= 8192 cylinders
8798 shr bx, #0x03
8799 shl cl, #0x03
8800 jmp hd1_post_store_logical
8802 hd1_post_above_8192:
8803 ;; 8192 < c <= 16384 cylinders
8804 shr bx, #0x04
8805 shl cl, #0x04
8807 hd1_post_store_logical:
8808 mov (0x004d + 0x00), bx ;; number of physical cylinders
8809 mov (0x004d + 0x02), cl ;; number of physical heads
8810 ;; checksum
8811 mov cl, #0x0f ;; repeat count
8812 mov si, #0x004d ;; offset to disk0 FDPT
8813 mov al, #0x00 ;; sum
8814 hd1_post_checksum_loop:
8815 add al, [si]
8816 inc si
8817 dec cl
8818 jnz hd1_post_checksum_loop
8819 not al ;; now take 2s complement
8820 inc al
8821 mov [si], al
8822 ;;; Done filling EBDA table for hard disk 1.
8826 ;--------------------
8827 ;- POST: EBDA segment
8828 ;--------------------
8829 ; relocated here because the primary POST area isnt big enough.
8830 ebda_post:
8831 #if BX_USE_EBDA
8832 mov ax, #EBDA_SEG
8833 mov ds, ax
8834 mov byte ptr [0x0], #EBDA_SIZE
8835 #endif
8836 xor ax, ax ; mov EBDA seg into 40E
8837 mov ds, ax
8838 mov word ptr [0x40E], #EBDA_SEG
8839 ret;;
8841 ;--------------------
8842 ;- POST: EOI + jmp via [0x40:67)
8843 ;--------------------
8844 ; relocated here because the primary POST area isnt big enough.
8845 eoi_jmp_post:
8846 call eoi_both_pics
8848 xor ax, ax
8849 mov ds, ax
8851 jmp far ptr [0x467]
8854 ;--------------------
8855 eoi_both_pics:
8856 mov al, #0x20
8857 out #0xA0, al ;; slave PIC EOI
8858 eoi_master_pic:
8859 mov al, #0x20
8860 out #0x20, al ;; master PIC EOI
8863 ;--------------------
8864 BcdToBin:
8865 ;; in: AL in BCD format
8866 ;; out: AL in binary format, AH will always be 0
8867 ;; trashes BX
8868 mov bl, al
8869 and bl, #0x0f ;; bl has low digit
8870 shr al, #4 ;; al has high digit
8871 mov bh, #10
8872 mul al, bh ;; multiply high digit by 10 (result in AX)
8873 add al, bl ;; then add low digit
8876 ;--------------------
8877 timer_tick_post:
8878 ;; Setup the Timer Ticks Count (0x46C:dword) and
8879 ;; Timer Ticks Roller Flag (0x470:byte)
8880 ;; The Timer Ticks Count needs to be set according to
8881 ;; the current CMOS time, as if ticks have been occurring
8882 ;; at 18.2hz since midnight up to this point. Calculating
8883 ;; this is a little complicated. Here are the factors I gather
8884 ;; regarding this. 14,318,180 hz was the original clock speed,
8885 ;; chosen so it could be divided by either 3 to drive the 5Mhz CPU
8886 ;; at the time, or 4 to drive the CGA video adapter. The div3
8887 ;; source was divided again by 4 to feed a 1.193Mhz signal to
8888 ;; the timer. With a maximum 16bit timer count, this is again
8889 ;; divided down by 65536 to 18.2hz.
8891 ;; 14,318,180 Hz clock
8892 ;; /3 = 4,772,726 Hz fed to orginal 5Mhz CPU
8893 ;; /4 = 1,193,181 Hz fed to timer
8894 ;; /65536 (maximum timer count) = 18.20650736 ticks/second
8895 ;; 1 second = 18.20650736 ticks
8896 ;; 1 minute = 1092.390442 ticks
8897 ;; 1 hour = 65543.42651 ticks
8899 ;; Given the values in the CMOS clock, one could calculate
8900 ;; the number of ticks by the following:
8901 ;; ticks = (BcdToBin(seconds) * 18.206507) +
8902 ;; (BcdToBin(minutes) * 1092.3904)
8903 ;; (BcdToBin(hours) * 65543.427)
8904 ;; To get a little more accuracy, since Im using integer
8905 ;; arithmatic, I use:
8906 ;; ticks = (BcdToBin(seconds) * 18206507) / 1000000 +
8907 ;; (BcdToBin(minutes) * 10923904) / 10000 +
8908 ;; (BcdToBin(hours) * 65543427) / 1000
8910 ;; assuming DS=0000
8912 ;; get CMOS seconds
8913 xor eax, eax ;; clear EAX
8914 mov al, #0x00
8915 out #0x70, al
8916 in al, #0x71 ;; AL has CMOS seconds in BCD
8917 call BcdToBin ;; EAX now has seconds in binary
8918 mov edx, #18206507
8919 mul eax, edx
8920 mov ebx, #1000000
8921 xor edx, edx
8922 div eax, ebx
8923 mov ecx, eax ;; ECX will accumulate total ticks
8925 ;; get CMOS minutes
8926 xor eax, eax ;; clear EAX
8927 mov al, #0x02
8928 out #0x70, al
8929 in al, #0x71 ;; AL has CMOS minutes in BCD
8930 call BcdToBin ;; EAX now has minutes in binary
8931 mov edx, #10923904
8932 mul eax, edx
8933 mov ebx, #10000
8934 xor edx, edx
8935 div eax, ebx
8936 add ecx, eax ;; add to total ticks
8938 ;; get CMOS hours
8939 xor eax, eax ;; clear EAX
8940 mov al, #0x04
8941 out #0x70, al
8942 in al, #0x71 ;; AL has CMOS hours in BCD
8943 call BcdToBin ;; EAX now has hours in binary
8944 mov edx, #65543427
8945 mul eax, edx
8946 mov ebx, #1000
8947 xor edx, edx
8948 div eax, ebx
8949 add ecx, eax ;; add to total ticks
8951 mov 0x46C, ecx ;; Timer Ticks Count
8952 xor al, al
8953 mov 0x470, al ;; Timer Ticks Rollover Flag
8956 ;--------------------
8957 int76_handler:
8958 ;; record completion in BIOS task complete flag
8959 push ax
8960 push ds
8961 mov ax, #0x0040
8962 mov ds, ax
8963 mov 0x008E, #0xff
8964 call eoi_both_pics
8965 pop ds
8966 pop ax
8967 iret
8970 ;--------------------
8971 #if BX_APM
8973 use32 386
8974 #define APM_PROT32
8975 #include "apmbios.S"
8977 use16 386
8978 #define APM_PROT16
8979 #include "apmbios.S"
8981 #define APM_REAL
8982 #include "apmbios.S"
8984 #endif
8986 ;--------------------
8987 #if BX_PCIBIOS
8988 use32 386
8989 .align 16
8990 bios32_structure:
8991 db 0x5f, 0x33, 0x32, 0x5f ;; "_32_" signature
8992 dw bios32_entry_point, 0xf ;; 32 bit physical address
8993 db 0 ;; revision level
8994 ;; length in paragraphs and checksum stored in a word to prevent errors
8995 dw (~(((bios32_entry_point >> 8) + (bios32_entry_point & 0xff) + 0x32) \
8996 & 0xff) << 8) + 0x01
8997 db 0,0,0,0,0 ;; reserved
8999 .align 16
9000 bios32_entry_point:
9001 pushfd
9002 cmp eax, #0x49435024 ;; "$PCI"
9003 jne unknown_service
9004 mov eax, #0x80000000
9005 mov dx, #0x0cf8
9006 out dx, eax
9007 mov dx, #0x0cfc
9008 in eax, dx
9009 #ifdef PCI_FIXED_HOST_BRIDGE
9010 cmp eax, #PCI_FIXED_HOST_BRIDGE
9011 jne unknown_service
9012 #else
9013 ;; say ok if a device is present
9014 cmp eax, #0xffffffff
9015 je unknown_service
9016 #endif
9017 mov ebx, #0x000f0000
9018 mov ecx, #0
9019 mov edx, #pcibios_protected
9020 xor al, al
9021 jmp bios32_end
9022 unknown_service:
9023 mov al, #0x80
9024 bios32_end:
9025 #ifdef BX_QEMU
9026 and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
9027 #endif
9028 popfd
9029 retf
9031 .align 16
9032 pcibios_protected:
9033 pushfd
9035 push esi
9036 push edi
9037 cmp al, #0x01 ;; installation check
9038 jne pci_pro_f02
9039 mov bx, #0x0210
9040 mov cx, #0
9041 mov edx, #0x20494350 ;; "PCI "
9042 mov al, #0x01
9043 jmp pci_pro_ok
9044 pci_pro_f02: ;; find pci device
9045 cmp al, #0x02
9046 jne pci_pro_f03
9047 shl ecx, #16
9048 mov cx, dx
9049 xor bx, bx
9050 mov di, #0x00
9051 pci_pro_devloop:
9052 call pci_pro_select_reg
9053 mov dx, #0x0cfc
9054 in eax, dx
9055 cmp eax, ecx
9056 jne pci_pro_nextdev
9057 cmp si, #0
9058 je pci_pro_ok
9059 dec si
9060 pci_pro_nextdev:
9061 inc bx
9062 cmp bx, #0x0100
9063 jne pci_pro_devloop
9064 mov ah, #0x86
9065 jmp pci_pro_fail
9066 pci_pro_f03: ;; find class code
9067 cmp al, #0x03
9068 jne pci_pro_f08
9069 xor bx, bx
9070 mov di, #0x08
9071 pci_pro_devloop2:
9072 call pci_pro_select_reg
9073 mov dx, #0x0cfc
9074 in eax, dx
9075 shr eax, #8
9076 cmp eax, ecx
9077 jne pci_pro_nextdev2
9078 cmp si, #0
9079 je pci_pro_ok
9080 dec si
9081 pci_pro_nextdev2:
9082 inc bx
9083 cmp bx, #0x0100
9084 jne pci_pro_devloop2
9085 mov ah, #0x86
9086 jmp pci_pro_fail
9087 pci_pro_f08: ;; read configuration byte
9088 cmp al, #0x08
9089 jne pci_pro_f09
9090 call pci_pro_select_reg
9091 push edx
9092 mov dx, di
9093 and dx, #0x03
9094 add dx, #0x0cfc
9095 in al, dx
9096 pop edx
9097 mov cl, al
9098 jmp pci_pro_ok
9099 pci_pro_f09: ;; read configuration word
9100 cmp al, #0x09
9101 jne pci_pro_f0a
9102 call pci_pro_select_reg
9103 push edx
9104 mov dx, di
9105 and dx, #0x02
9106 add dx, #0x0cfc
9107 in ax, dx
9108 pop edx
9109 mov cx, ax
9110 jmp pci_pro_ok
9111 pci_pro_f0a: ;; read configuration dword
9112 cmp al, #0x0a
9113 jne pci_pro_f0b
9114 call pci_pro_select_reg
9115 push edx
9116 mov dx, #0x0cfc
9117 in eax, dx
9118 pop edx
9119 mov ecx, eax
9120 jmp pci_pro_ok
9121 pci_pro_f0b: ;; write configuration byte
9122 cmp al, #0x0b
9123 jne pci_pro_f0c
9124 call pci_pro_select_reg
9125 push edx
9126 mov dx, di
9127 and dx, #0x03
9128 add dx, #0x0cfc
9129 mov al, cl
9130 out dx, al
9131 pop edx
9132 jmp pci_pro_ok
9133 pci_pro_f0c: ;; write configuration word
9134 cmp al, #0x0c
9135 jne pci_pro_f0d
9136 call pci_pro_select_reg
9137 push edx
9138 mov dx, di
9139 and dx, #0x02
9140 add dx, #0x0cfc
9141 mov ax, cx
9142 out dx, ax
9143 pop edx
9144 jmp pci_pro_ok
9145 pci_pro_f0d: ;; write configuration dword
9146 cmp al, #0x0d
9147 jne pci_pro_unknown
9148 call pci_pro_select_reg
9149 push edx
9150 mov dx, #0x0cfc
9151 mov eax, ecx
9152 out dx, eax
9153 pop edx
9154 jmp pci_pro_ok
9155 pci_pro_unknown:
9156 mov ah, #0x81
9157 pci_pro_fail:
9158 pop edi
9159 pop esi
9160 #ifdef BX_QEMU
9161 and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
9162 #endif
9163 popfd
9165 retf
9166 pci_pro_ok:
9167 xor ah, ah
9168 pop edi
9169 pop esi
9170 #ifdef BX_QEMU
9171 and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
9172 #endif
9173 popfd
9175 retf
9177 pci_pro_select_reg:
9178 push edx
9179 mov eax, #0x800000
9180 mov ax, bx
9181 shl eax, #8
9182 and di, #0xff
9183 or ax, di
9184 and al, #0xfc
9185 mov dx, #0x0cf8
9186 out dx, eax
9187 pop edx
9190 use16 386
9192 pcibios_real:
9193 push eax
9194 push dx
9195 mov eax, #0x80000000
9196 mov dx, #0x0cf8
9197 out dx, eax
9198 mov dx, #0x0cfc
9199 in eax, dx
9200 #ifdef PCI_FIXED_HOST_BRIDGE
9201 cmp eax, #PCI_FIXED_HOST_BRIDGE
9202 je pci_present
9203 #else
9204 ;; say ok if a device is present
9205 cmp eax, #0xffffffff
9206 jne pci_present
9207 #endif
9208 pop dx
9209 pop eax
9210 mov ah, #0xff
9213 pci_present:
9214 pop dx
9215 pop eax
9216 cmp al, #0x01 ;; installation check
9217 jne pci_real_f02
9218 mov ax, #0x0001
9219 mov bx, #0x0210
9220 mov cx, #0
9221 mov edx, #0x20494350 ;; "PCI "
9222 mov edi, #0xf0000
9223 mov di, #pcibios_protected
9226 pci_real_f02: ;; find pci device
9227 push esi
9228 push edi
9229 cmp al, #0x02
9230 jne pci_real_f03
9231 shl ecx, #16
9232 mov cx, dx
9233 xor bx, bx
9234 mov di, #0x00
9235 pci_real_devloop:
9236 call pci_real_select_reg
9237 mov dx, #0x0cfc
9238 in eax, dx
9239 cmp eax, ecx
9240 jne pci_real_nextdev
9241 cmp si, #0
9242 je pci_real_ok
9243 dec si
9244 pci_real_nextdev:
9245 inc bx
9246 cmp bx, #0x0100
9247 jne pci_real_devloop
9248 mov dx, cx
9249 shr ecx, #16
9250 mov ax, #0x8602
9251 jmp pci_real_fail
9252 pci_real_f03: ;; find class code
9253 cmp al, #0x03
9254 jne pci_real_f08
9255 xor bx, bx
9256 mov di, #0x08
9257 pci_real_devloop2:
9258 call pci_real_select_reg
9259 mov dx, #0x0cfc
9260 in eax, dx
9261 shr eax, #8
9262 cmp eax, ecx
9263 jne pci_real_nextdev2
9264 cmp si, #0
9265 je pci_real_ok
9266 dec si
9267 pci_real_nextdev2:
9268 inc bx
9269 cmp bx, #0x0100
9270 jne pci_real_devloop2
9271 mov dx, cx
9272 shr ecx, #16
9273 mov ax, #0x8603
9274 jmp pci_real_fail
9275 pci_real_f08: ;; read configuration byte
9276 cmp al, #0x08
9277 jne pci_real_f09
9278 call pci_real_select_reg
9279 push dx
9280 mov dx, di
9281 and dx, #0x03
9282 add dx, #0x0cfc
9283 in al, dx
9284 pop dx
9285 mov cl, al
9286 jmp pci_real_ok
9287 pci_real_f09: ;; read configuration word
9288 cmp al, #0x09
9289 jne pci_real_f0a
9290 call pci_real_select_reg
9291 push dx
9292 mov dx, di
9293 and dx, #0x02
9294 add dx, #0x0cfc
9295 in ax, dx
9296 pop dx
9297 mov cx, ax
9298 jmp pci_real_ok
9299 pci_real_f0a: ;; read configuration dword
9300 cmp al, #0x0a
9301 jne pci_real_f0b
9302 call pci_real_select_reg
9303 push dx
9304 mov dx, #0x0cfc
9305 in eax, dx
9306 pop dx
9307 mov ecx, eax
9308 jmp pci_real_ok
9309 pci_real_f0b: ;; write configuration byte
9310 cmp al, #0x0b
9311 jne pci_real_f0c
9312 call pci_real_select_reg
9313 push dx
9314 mov dx, di
9315 and dx, #0x03
9316 add dx, #0x0cfc
9317 mov al, cl
9318 out dx, al
9319 pop dx
9320 jmp pci_real_ok
9321 pci_real_f0c: ;; write configuration word
9322 cmp al, #0x0c
9323 jne pci_real_f0d
9324 call pci_real_select_reg
9325 push dx
9326 mov dx, di
9327 and dx, #0x02
9328 add dx, #0x0cfc
9329 mov ax, cx
9330 out dx, ax
9331 pop dx
9332 jmp pci_real_ok
9333 pci_real_f0d: ;; write configuration dword
9334 cmp al, #0x0d
9335 jne pci_real_f0e
9336 call pci_real_select_reg
9337 push dx
9338 mov dx, #0x0cfc
9339 mov eax, ecx
9340 out dx, eax
9341 pop dx
9342 jmp pci_real_ok
9343 pci_real_f0e: ;; get irq routing options
9344 cmp al, #0x0e
9345 jne pci_real_unknown
9346 SEG ES
9347 cmp word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9348 jb pci_real_too_small
9349 SEG ES
9350 mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9351 pushf
9352 push ds
9353 push es
9354 push cx
9355 push si
9356 push di
9358 mov si, #pci_routing_table_structure_start
9359 push cs
9360 pop ds
9361 SEG ES
9362 mov cx, [di+2]
9363 SEG ES
9364 mov es, [di+4]
9365 mov di, cx
9366 mov cx, #pci_routing_table_structure_end - pci_routing_table_structure_start
9368 movsb
9369 pop di
9370 pop si
9371 pop cx
9372 pop es
9373 pop ds
9374 popf
9375 mov bx, #(1 << 9) | (1 << 11) ;; irq 9 and 11 are used
9376 jmp pci_real_ok
9377 pci_real_too_small:
9378 SEG ES
9379 mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9380 mov ah, #0x89
9381 jmp pci_real_fail
9383 pci_real_unknown:
9384 mov ah, #0x81
9385 pci_real_fail:
9386 pop edi
9387 pop esi
9390 pci_real_ok:
9391 xor ah, ah
9392 pop edi
9393 pop esi
9397 pci_real_select_reg:
9398 push dx
9399 mov eax, #0x800000
9400 mov ax, bx
9401 shl eax, #8
9402 and di, #0xff
9403 or ax, di
9404 and al, #0xfc
9405 mov dx, #0x0cf8
9406 out dx, eax
9407 pop dx
9410 .align 16
9411 pci_routing_table_structure:
9412 db 0x24, 0x50, 0x49, 0x52 ;; "$PIR" signature
9413 db 0, 1 ;; version
9414 dw 32 + (6 * 16) ;; table size
9415 db 0 ;; PCI interrupt router bus
9416 db 0x08 ;; PCI interrupt router DevFunc
9417 dw 0x0000 ;; PCI exclusive IRQs
9418 dw 0x8086 ;; compatible PCI interrupt router vendor ID
9419 dw 0x7000 ;; compatible PCI interrupt router device ID
9420 dw 0,0 ;; Miniport data
9421 db 0,0,0,0,0,0,0,0,0,0,0 ;; reserved
9422 db 0x07 ;; checksum
9423 pci_routing_table_structure_start:
9424 ;; first slot entry PCI-to-ISA (embedded)
9425 db 0 ;; pci bus number
9426 db 0x08 ;; pci device number (bit 7-3)
9427 db 0x60 ;; link value INTA#: pointer into PCI2ISA config space
9428 dw 0xdef8 ;; IRQ bitmap INTA#
9429 db 0x61 ;; link value INTB#
9430 dw 0xdef8 ;; IRQ bitmap INTB#
9431 db 0x62 ;; link value INTC#
9432 dw 0xdef8 ;; IRQ bitmap INTC#
9433 db 0x63 ;; link value INTD#
9434 dw 0xdef8 ;; IRQ bitmap INTD#
9435 db 0 ;; physical slot (0 = embedded)
9436 db 0 ;; reserved
9437 ;; second slot entry: 1st PCI slot
9438 db 0 ;; pci bus number
9439 db 0x10 ;; pci device number (bit 7-3)
9440 db 0x61 ;; link value INTA#
9441 dw 0xdef8 ;; IRQ bitmap INTA#
9442 db 0x62 ;; link value INTB#
9443 dw 0xdef8 ;; IRQ bitmap INTB#
9444 db 0x63 ;; link value INTC#
9445 dw 0xdef8 ;; IRQ bitmap INTC#
9446 db 0x60 ;; link value INTD#
9447 dw 0xdef8 ;; IRQ bitmap INTD#
9448 db 1 ;; physical slot (0 = embedded)
9449 db 0 ;; reserved
9450 ;; third slot entry: 2nd PCI slot
9451 db 0 ;; pci bus number
9452 db 0x18 ;; pci device number (bit 7-3)
9453 db 0x62 ;; link value INTA#
9454 dw 0xdef8 ;; IRQ bitmap INTA#
9455 db 0x63 ;; link value INTB#
9456 dw 0xdef8 ;; IRQ bitmap INTB#
9457 db 0x60 ;; link value INTC#
9458 dw 0xdef8 ;; IRQ bitmap INTC#
9459 db 0x61 ;; link value INTD#
9460 dw 0xdef8 ;; IRQ bitmap INTD#
9461 db 2 ;; physical slot (0 = embedded)
9462 db 0 ;; reserved
9463 ;; 4th slot entry: 3rd PCI slot
9464 db 0 ;; pci bus number
9465 db 0x20 ;; pci device number (bit 7-3)
9466 db 0x63 ;; link value INTA#
9467 dw 0xdef8 ;; IRQ bitmap INTA#
9468 db 0x60 ;; link value INTB#
9469 dw 0xdef8 ;; IRQ bitmap INTB#
9470 db 0x61 ;; link value INTC#
9471 dw 0xdef8 ;; IRQ bitmap INTC#
9472 db 0x62 ;; link value INTD#
9473 dw 0xdef8 ;; IRQ bitmap INTD#
9474 db 3 ;; physical slot (0 = embedded)
9475 db 0 ;; reserved
9476 ;; 5th slot entry: 4rd PCI slot
9477 db 0 ;; pci bus number
9478 db 0x28 ;; pci device number (bit 7-3)
9479 db 0x60 ;; link value INTA#
9480 dw 0xdef8 ;; IRQ bitmap INTA#
9481 db 0x61 ;; link value INTB#
9482 dw 0xdef8 ;; IRQ bitmap INTB#
9483 db 0x62 ;; link value INTC#
9484 dw 0xdef8 ;; IRQ bitmap INTC#
9485 db 0x63 ;; link value INTD#
9486 dw 0xdef8 ;; IRQ bitmap INTD#
9487 db 4 ;; physical slot (0 = embedded)
9488 db 0 ;; reserved
9489 ;; 6th slot entry: 5rd PCI slot
9490 db 0 ;; pci bus number
9491 db 0x30 ;; pci device number (bit 7-3)
9492 db 0x61 ;; link value INTA#
9493 dw 0xdef8 ;; IRQ bitmap INTA#
9494 db 0x62 ;; link value INTB#
9495 dw 0xdef8 ;; IRQ bitmap INTB#
9496 db 0x63 ;; link value INTC#
9497 dw 0xdef8 ;; IRQ bitmap INTC#
9498 db 0x60 ;; link value INTD#
9499 dw 0xdef8 ;; IRQ bitmap INTD#
9500 db 5 ;; physical slot (0 = embedded)
9501 db 0 ;; reserved
9502 pci_routing_table_structure_end:
9504 #if !BX_ROMBIOS32
9505 pci_irq_list:
9506 db 11, 10, 9, 5;
9508 pcibios_init_sel_reg:
9509 push eax
9510 mov eax, #0x800000
9511 mov ax, bx
9512 shl eax, #8
9513 and dl, #0xfc
9514 or al, dl
9515 mov dx, #0x0cf8
9516 out dx, eax
9517 pop eax
9520 pcibios_init_iomem_bases:
9521 push bp
9522 mov bp, sp
9523 mov eax, #0xe0000000 ;; base for memory init
9524 push eax
9525 mov ax, #0xc000 ;; base for i/o init
9526 push ax
9527 mov ax, #0x0010 ;; start at base address #0
9528 push ax
9529 mov bx, #0x0008
9530 pci_init_io_loop1:
9531 mov dl, #0x00
9532 call pcibios_init_sel_reg
9533 mov dx, #0x0cfc
9534 in ax, dx
9535 cmp ax, #0xffff
9536 jz next_pci_dev
9537 mov dl, #0x04 ;; disable i/o and memory space access
9538 call pcibios_init_sel_reg
9539 mov dx, #0x0cfc
9540 in al, dx
9541 and al, #0xfc
9542 out dx, al
9543 pci_init_io_loop2:
9544 mov dl, [bp-8]
9545 call pcibios_init_sel_reg
9546 mov dx, #0x0cfc
9547 in eax, dx
9548 test al, #0x01
9549 jnz init_io_base
9550 mov ecx, eax
9551 mov eax, #0xffffffff
9552 out dx, eax
9553 in eax, dx
9554 cmp eax, ecx
9555 je next_pci_base
9556 xor eax, #0xffffffff
9557 mov ecx, eax
9558 mov eax, [bp-4]
9559 out dx, eax
9560 add eax, ecx ;; calculate next free mem base
9561 add eax, #0x01000000
9562 and eax, #0xff000000
9563 mov [bp-4], eax
9564 jmp next_pci_base
9565 init_io_base:
9566 mov cx, ax
9567 mov ax, #0xffff
9568 out dx, ax
9569 in ax, dx
9570 cmp ax, cx
9571 je next_pci_base
9572 xor ax, #0xfffe
9573 mov cx, ax
9574 mov ax, [bp-6]
9575 out dx, ax
9576 add ax, cx ;; calculate next free i/o base
9577 add ax, #0x0100
9578 and ax, #0xff00
9579 mov [bp-6], ax
9580 next_pci_base:
9581 mov al, [bp-8]
9582 add al, #0x04
9583 cmp al, #0x28
9584 je enable_iomem_space
9585 mov byte ptr[bp-8], al
9586 jmp pci_init_io_loop2
9587 enable_iomem_space:
9588 mov dl, #0x04 ;; enable i/o and memory space access if available
9589 call pcibios_init_sel_reg
9590 mov dx, #0x0cfc
9591 in al, dx
9592 or al, #0x07
9593 out dx, al
9594 next_pci_dev:
9595 mov byte ptr[bp-8], #0x10
9596 inc bx
9597 cmp bx, #0x0100
9598 jne pci_init_io_loop1
9599 mov sp, bp
9600 pop bp
9603 pcibios_init_set_elcr:
9604 push ax
9605 push cx
9606 mov dx, #0x04d0
9607 test al, #0x08
9608 jz is_master_pic
9609 inc dx
9610 and al, #0x07
9611 is_master_pic:
9612 mov cl, al
9613 mov bl, #0x01
9614 shl bl, cl
9615 in al, dx
9616 or al, bl
9617 out dx, al
9618 pop cx
9619 pop ax
9622 pcibios_init_irqs:
9623 push ds
9624 push bp
9625 mov ax, #0xf000
9626 mov ds, ax
9627 mov dx, #0x04d0 ;; reset ELCR1 + ELCR2
9628 mov al, #0x00
9629 out dx, al
9630 inc dx
9631 out dx, al
9632 mov si, #pci_routing_table_structure
9633 mov bh, [si+8]
9634 mov bl, [si+9]
9635 mov dl, #0x00
9636 call pcibios_init_sel_reg
9637 mov dx, #0x0cfc
9638 in eax, dx
9639 cmp eax, [si+12] ;; check irq router
9640 jne pci_init_end
9641 mov dl, [si+34]
9642 call pcibios_init_sel_reg
9643 push bx ;; save irq router bus + devfunc
9644 mov dx, #0x0cfc
9645 mov ax, #0x8080
9646 out dx, ax ;; reset PIRQ route control
9647 add dx, #2
9648 out dx, ax
9649 mov ax, [si+6]
9650 sub ax, #0x20
9651 shr ax, #4
9652 mov cx, ax
9653 add si, #0x20 ;; set pointer to 1st entry
9654 mov bp, sp
9655 mov ax, #pci_irq_list
9656 push ax
9657 xor ax, ax
9658 push ax
9659 pci_init_irq_loop1:
9660 mov bh, [si]
9661 mov bl, [si+1]
9662 pci_init_irq_loop2:
9663 mov dl, #0x00
9664 call pcibios_init_sel_reg
9665 mov dx, #0x0cfc
9666 in ax, dx
9667 cmp ax, #0xffff
9668 jnz pci_test_int_pin
9669 test bl, #0x07
9670 jz next_pir_entry
9671 jmp next_pci_func
9672 pci_test_int_pin:
9673 mov dl, #0x3c
9674 call pcibios_init_sel_reg
9675 mov dx, #0x0cfd
9676 in al, dx
9677 and al, #0x07
9678 jz next_pci_func
9679 dec al ;; determine pirq reg
9680 mov dl, #0x03
9681 mul al, dl
9682 add al, #0x02
9683 xor ah, ah
9684 mov bx, ax
9685 mov al, [si+bx]
9686 mov dl, al
9687 mov bx, [bp]
9688 call pcibios_init_sel_reg
9689 mov dx, #0x0cfc
9690 and al, #0x03
9691 add dl, al
9692 in al, dx
9693 cmp al, #0x80
9694 jb pirq_found
9695 mov bx, [bp-2] ;; pci irq list pointer
9696 mov al, [bx]
9697 out dx, al
9698 inc bx
9699 mov [bp-2], bx
9700 call pcibios_init_set_elcr
9701 pirq_found:
9702 mov bh, [si]
9703 mov bl, [si+1]
9704 add bl, [bp-3] ;; pci function number
9705 mov dl, #0x3c
9706 call pcibios_init_sel_reg
9707 mov dx, #0x0cfc
9708 out dx, al
9709 next_pci_func:
9710 inc byte ptr[bp-3]
9711 inc bl
9712 test bl, #0x07
9713 jnz pci_init_irq_loop2
9714 next_pir_entry:
9715 add si, #0x10
9716 mov byte ptr[bp-3], #0x00
9717 loop pci_init_irq_loop1
9718 mov sp, bp
9719 pop bx
9720 pci_init_end:
9721 pop bp
9722 pop ds
9724 #endif // BX_ROMBIOS32
9725 #endif // BX_PCIBIOS
9727 #if BX_ROMBIOS32
9728 rombios32_init:
9729 ;; save a20 and enable it
9730 in al, 0x92
9731 push ax
9732 or al, #0x02
9733 out 0x92, al
9735 ;; save SS:SP to the BDA
9736 xor ax, ax
9737 mov ds, ax
9738 mov 0x0469, ss
9739 mov 0x0467, sp
9741 SEG CS
9742 lidt [pmode_IDT_info]
9743 SEG CS
9744 lgdt [rombios32_gdt_48]
9745 ;; set PE bit in CR0
9746 mov eax, cr0
9747 or al, #0x01
9748 mov cr0, eax
9749 ;; start protected mode code: ljmpl 0x10:rombios32_init1
9750 db 0x66, 0xea
9751 dw rombios32_05
9752 dw 0x000f ;; high 16 bit address
9753 dw 0x0010
9755 use32 386
9756 rombios32_05:
9757 ;; init data segments
9758 mov eax, #0x18
9759 mov ds, ax
9760 mov es, ax
9761 mov ss, ax
9762 xor eax, eax
9763 mov fs, ax
9764 mov gs, ax
9767 ;; copy rombios32 code to ram (ram offset = 1MB)
9768 mov esi, #0xfffe0000
9769 mov edi, #0x00040000
9770 mov ecx, #0x10000 / 4
9772 movsd
9774 ;; init the stack pointer
9775 mov esp, #0x00080000
9777 ;; call rombios32 code
9778 mov eax, #0x00040000
9779 call eax
9781 ;; reset the memory (some boot loaders such as syslinux suppose
9782 ;; that the memory is set to zero)
9783 mov edi, #0x00040000
9784 mov ecx, #0x40000 / 4
9785 xor eax, eax
9787 stosd
9789 ;; return to 16 bit protected mode first
9790 db 0xea
9791 dd rombios32_10
9792 dw 0x20
9794 use16 386
9795 rombios32_10:
9796 ;; restore data segment limits to 0xffff
9797 mov ax, #0x28
9798 mov ds, ax
9799 mov es, ax
9800 mov ss, ax
9801 mov fs, ax
9802 mov gs, ax
9804 ;; reset PE bit in CR0
9805 mov eax, cr0
9806 and al, #0xFE
9807 mov cr0, eax
9809 ;; far jump to flush CPU queue after transition to real mode
9810 JMP_AP(0xf000, rombios32_real_mode)
9812 rombios32_real_mode:
9813 ;; restore IDT to normal real-mode defaults
9814 SEG CS
9815 lidt [rmode_IDT_info]
9817 xor ax, ax
9818 mov ds, ax
9819 mov es, ax
9820 mov fs, ax
9821 mov gs, ax
9823 ;; restore SS:SP from the BDA
9824 mov ss, 0x0469
9825 xor esp, esp
9826 mov sp, 0x0467
9827 ;; restore a20
9828 pop ax
9829 out 0x92, al
9832 rombios32_gdt_48:
9833 dw 0x30
9834 dw rombios32_gdt
9835 dw 0x000f
9837 rombios32_gdt:
9838 dw 0, 0, 0, 0
9839 dw 0, 0, 0, 0
9840 dw 0xffff, 0, 0x9b00, 0x00cf ; 32 bit flat code segment (0x10)
9841 dw 0xffff, 0, 0x9300, 0x00cf ; 32 bit flat data segment (0x18)
9842 dw 0xffff, 0, 0x9b0f, 0x0000 ; 16 bit code segment base=0xf0000 limit=0xffff
9843 dw 0xffff, 0, 0x9300, 0x0000 ; 16 bit data segment base=0x0 limit=0xffff
9844 #endif
9847 ; parallel port detection: base address in DX, index in BX, timeout in CL
9848 detect_parport:
9849 push dx
9850 add dx, #2
9851 in al, dx
9852 and al, #0xdf ; clear input mode
9853 out dx, al
9854 pop dx
9855 mov al, #0xaa
9856 out dx, al
9857 in al, dx
9858 cmp al, #0xaa
9859 jne no_parport
9860 push bx
9861 shl bx, #1
9862 mov [bx+0x408], dx ; Parallel I/O address
9863 pop bx
9864 mov [bx+0x478], cl ; Parallel printer timeout
9865 inc bx
9866 no_parport:
9869 ; serial port detection: base address in DX, index in BX, timeout in CL
9870 detect_serial:
9871 push dx
9872 inc dx
9873 mov al, #0x02
9874 out dx, al
9875 in al, dx
9876 cmp al, #0x02
9877 jne no_serial
9878 inc dx
9879 in al, dx
9880 cmp al, #0x02
9881 jne no_serial
9882 dec dx
9883 xor al, al
9884 out dx, al
9885 pop dx
9886 push bx
9887 shl bx, #1
9888 mov [bx+0x400], dx ; Serial I/O address
9889 pop bx
9890 mov [bx+0x47c], cl ; Serial timeout
9891 inc bx
9893 no_serial:
9894 pop dx
9897 rom_checksum:
9898 push ax
9899 push bx
9900 push cx
9901 xor ax, ax
9902 xor bx, bx
9903 xor cx, cx
9904 mov ch, [2]
9905 shl cx, #1
9906 checksum_loop:
9907 add al, [bx]
9908 inc bx
9909 loop checksum_loop
9910 and al, #0xff
9911 pop cx
9912 pop bx
9913 pop ax
9917 ;; We need a copy of this string, but we are not actually a PnP BIOS,
9918 ;; so make sure it is *not* aligned, so OSes will not see it if they scan.
9919 .align 16
9920 db 0
9921 pnp_string:
9922 .ascii "$PnP"
9925 rom_scan:
9926 ;; Scan for existence of valid expansion ROMS.
9927 ;; Video ROM: from 0xC0000..0xC7FFF in 2k increments
9928 ;; General ROM: from 0xC8000..0xDFFFF in 2k increments
9929 ;; System ROM: only 0xE0000
9931 ;; Header:
9932 ;; Offset Value
9933 ;; 0 0x55
9934 ;; 1 0xAA
9935 ;; 2 ROM length in 512-byte blocks
9936 ;; 3 ROM initialization entry point (FAR CALL)
9938 rom_scan_loop:
9939 push ax ;; Save AX
9940 mov ds, cx
9941 mov ax, #0x0004 ;; start with increment of 4 (512-byte) blocks = 2k
9942 cmp [0], #0xAA55 ;; look for signature
9943 jne rom_scan_increment
9944 call rom_checksum
9945 jnz rom_scan_increment
9946 mov al, [2] ;; change increment to ROM length in 512-byte blocks
9948 ;; We want our increment in 512-byte quantities, rounded to
9949 ;; the nearest 2k quantity, since we only scan at 2k intervals.
9950 test al, #0x03
9951 jz block_count_rounded
9952 and al, #0xfc ;; needs rounding up
9953 add al, #0x04
9954 block_count_rounded:
9956 xor bx, bx ;; Restore DS back to 0000:
9957 mov ds, bx
9958 push ax ;; Save AX
9959 push di ;; Save DI
9960 ;; Push addr of ROM entry point
9961 push cx ;; Push seg
9962 push #0x0003 ;; Push offset
9964 ;; Point ES:DI at "$PnP", which tells the ROM that we are a PnP BIOS.
9965 ;; That should stop it grabbing INT 19h; we will use its BEV instead.
9966 mov ax, #0xf000
9967 mov es, ax
9968 lea di, pnp_string
9970 mov bp, sp ;; Call ROM init routine using seg:off on stack
9971 db 0xff ;; call_far ss:[bp+0]
9972 db 0x5e
9973 db 0
9974 cli ;; In case expansion ROM BIOS turns IF on
9975 add sp, #2 ;; Pop offset value
9976 pop cx ;; Pop seg value (restore CX)
9978 ;; Look at the ROM's PnP Expansion header. Properly, we're supposed
9979 ;; to init all the ROMs and then go back and build an IPL table of
9980 ;; all the bootable devices, but we can get away with one pass.
9981 mov ds, cx ;; ROM base
9982 mov bx, 0x001a ;; 0x1A is the offset into ROM header that contains...
9983 mov ax, [bx] ;; the offset of PnP expansion header, where...
9984 cmp ax, #0x5024 ;; we look for signature "$PnP"
9985 jne no_bev
9986 mov ax, 2[bx]
9987 cmp ax, #0x506e
9988 jne no_bev
9989 mov ax, 0x1a[bx] ;; 0x1A is also the offset into the expansion header of...
9990 cmp ax, #0x0000 ;; the Bootstrap Entry Vector, or zero if there is none.
9991 je no_bev
9993 ;; Found a device that thinks it can boot the system. Record its BEV and product name string.
9994 mov di, 0x10[bx] ;; Pointer to the product name string or zero if none
9995 mov bx, #IPL_SEG ;; Go to the segment where the IPL table lives
9996 mov ds, bx
9997 mov bx, IPL_COUNT_OFFSET ;; Read the number of entries so far
9998 cmp bx, #IPL_TABLE_ENTRIES
9999 je no_bev ;; Get out if the table is full
10000 shl bx, #0x4 ;; Turn count into offset (entries are 16 bytes)
10001 mov 0[bx], #IPL_TYPE_BEV ;; This entry is a BEV device
10002 mov 6[bx], cx ;; Build a far pointer from the segment...
10003 mov 4[bx], ax ;; and the offset
10004 cmp di, #0x0000
10005 je no_prod_str
10006 mov 0xA[bx], cx ;; Build a far pointer from the segment...
10007 mov 8[bx], di ;; and the offset
10008 no_prod_str:
10009 shr bx, #0x4 ;; Turn the offset back into a count
10010 inc bx ;; We have one more entry now
10011 mov IPL_COUNT_OFFSET, bx ;; Remember that.
10013 no_bev:
10014 pop di ;; Restore DI
10015 pop ax ;; Restore AX
10016 rom_scan_increment:
10017 shl ax, #5 ;; convert 512-bytes blocks to 16-byte increments
10018 ;; because the segment selector is shifted left 4 bits.
10019 add cx, ax
10020 pop ax ;; Restore AX
10021 cmp cx, ax
10022 jbe rom_scan_loop
10024 xor ax, ax ;; Restore DS back to 0000:
10025 mov ds, ax
10028 ;; for 'C' strings and other data, insert them here with
10029 ;; a the following hack:
10030 ;; DATA_SEG_DEFS_HERE
10033 ;; the following area can be used to write dynamically generated tables
10034 .align 16
10035 bios_table_area_start:
10036 dd 0xaafb4442
10037 dd bios_table_area_end - bios_table_area_start - 8;
10039 ;--------
10040 ;- POST -
10041 ;--------
10042 .org 0xe05b ; POST Entry Point
10043 post:
10045 xor ax, ax
10047 ;; first reset the DMA controllers
10048 out 0x0d,al
10049 out 0xda,al
10051 ;; then initialize the DMA controllers
10052 mov al, #0xC0
10053 out 0xD6, al ; cascade mode of channel 4 enabled
10054 mov al, #0x00
10055 out 0xD4, al ; unmask channel 4
10057 ;; Examine CMOS shutdown status.
10058 mov AL, #0x0f
10059 out 0x70, AL
10060 in AL, 0x71
10062 ;; backup status
10063 mov bl, al
10065 ;; Reset CMOS shutdown status.
10066 mov AL, #0x0f
10067 out 0x70, AL ; select CMOS register Fh
10068 mov AL, #0x00
10069 out 0x71, AL ; set shutdown action to normal
10071 ;; Examine CMOS shutdown status.
10072 mov al, bl
10074 ;; 0x00, 0x09, 0x0D+ = normal startup
10075 cmp AL, #0x00
10076 jz normal_post
10077 cmp AL, #0x0d
10078 jae normal_post
10079 cmp AL, #0x09
10080 je normal_post
10082 ;; 0x05 = eoi + jmp via [0x40:0x67] jump
10083 cmp al, #0x05
10084 je eoi_jmp_post
10086 ;; Examine CMOS shutdown status.
10087 ;; 0x01,0x02,0x03,0x04,0x06,0x07,0x08, 0x0a, 0x0b, 0x0c = Unimplemented shutdown status.
10088 push bx
10089 call _shutdown_status_panic
10091 #if 0
10092 HALT(__LINE__)
10094 ;#if 0
10095 ; 0xb0, 0x20, /* mov al, #0x20 */
10096 ; 0xe6, 0x20, /* out 0x20, al ;send EOI to PIC */
10097 ;#endif
10099 pop es
10100 pop ds
10101 popa
10102 iret
10103 #endif
10105 normal_post:
10106 ; case 0: normal startup
10109 mov ax, #0xfffe
10110 mov sp, ax
10111 xor ax, ax
10112 mov ds, ax
10113 mov ss, ax
10115 ;; zero out BIOS data area (40:00..40:ff)
10116 mov es, ax
10117 mov cx, #0x0080 ;; 128 words
10118 mov di, #0x0400
10121 stosw
10123 call _log_bios_start
10125 ;; set all interrupts to default handler
10126 xor bx, bx ;; offset index
10127 mov cx, #0x0100 ;; counter (256 interrupts)
10128 mov ax, #dummy_iret_handler
10129 mov dx, #0xF000
10131 post_default_ints:
10132 mov [bx], ax
10133 add bx, #2
10134 mov [bx], dx
10135 add bx, #2
10136 loop post_default_ints
10138 ;; set vector 0x79 to zero
10139 ;; this is used by 'gardian angel' protection system
10140 SET_INT_VECTOR(0x79, #0, #0)
10142 ;; base memory in K 40:13 (word)
10143 mov ax, #BASE_MEM_IN_K
10144 mov 0x0413, ax
10147 ;; Manufacturing Test 40:12
10148 ;; zerod out above
10150 ;; Warm Boot Flag 0040:0072
10151 ;; value of 1234h = skip memory checks
10152 ;; zerod out above
10155 ;; Printer Services vector
10156 SET_INT_VECTOR(0x17, #0xF000, #int17_handler)
10158 ;; Bootstrap failure vector
10159 SET_INT_VECTOR(0x18, #0xF000, #int18_handler)
10161 ;; Bootstrap Loader vector
10162 SET_INT_VECTOR(0x19, #0xF000, #int19_handler)
10164 ;; User Timer Tick vector
10165 SET_INT_VECTOR(0x1c, #0xF000, #int1c_handler)
10167 ;; Memory Size Check vector
10168 SET_INT_VECTOR(0x12, #0xF000, #int12_handler)
10170 ;; Equipment Configuration Check vector
10171 SET_INT_VECTOR(0x11, #0xF000, #int11_handler)
10173 ;; System Services
10174 SET_INT_VECTOR(0x15, #0xF000, #int15_handler)
10176 ;; EBDA setup
10177 call ebda_post
10179 ;; PIT setup
10180 SET_INT_VECTOR(0x08, #0xF000, #int08_handler)
10181 ;; int 1C already points at dummy_iret_handler (above)
10182 mov al, #0x34 ; timer0: binary count, 16bit count, mode 2
10183 out 0x43, al
10184 mov al, #0x00 ; maximum count of 0000H = 18.2Hz
10185 out 0x40, al
10186 out 0x40, al
10188 ;; Keyboard
10189 SET_INT_VECTOR(0x09, #0xF000, #int09_handler)
10190 SET_INT_VECTOR(0x16, #0xF000, #int16_handler)
10192 xor ax, ax
10193 mov ds, ax
10194 mov 0x0417, al /* keyboard shift flags, set 1 */
10195 mov 0x0418, al /* keyboard shift flags, set 2 */
10196 mov 0x0419, al /* keyboard alt-numpad work area */
10197 mov 0x0471, al /* keyboard ctrl-break flag */
10198 mov 0x0497, al /* keyboard status flags 4 */
10199 mov al, #0x10
10200 mov 0x0496, al /* keyboard status flags 3 */
10203 /* keyboard head of buffer pointer */
10204 mov bx, #0x001E
10205 mov 0x041A, bx
10207 /* keyboard end of buffer pointer */
10208 mov 0x041C, bx
10210 /* keyboard pointer to start of buffer */
10211 mov bx, #0x001E
10212 mov 0x0480, bx
10214 /* keyboard pointer to end of buffer */
10215 mov bx, #0x003E
10216 mov 0x0482, bx
10218 /* init the keyboard */
10219 call _keyboard_init
10221 ;; mov CMOS Equipment Byte to BDA Equipment Word
10222 mov ax, 0x0410
10223 mov al, #0x14
10224 out 0x70, al
10225 in al, 0x71
10226 mov 0x0410, ax
10229 ;; Parallel setup
10230 SET_INT_VECTOR(0x0F, #0xF000, #dummy_iret_handler)
10231 xor ax, ax
10232 mov ds, ax
10233 xor bx, bx
10234 mov cl, #0x14 ; timeout value
10235 mov dx, #0x378 ; Parallel I/O address, port 1
10236 call detect_parport
10237 mov dx, #0x278 ; Parallel I/O address, port 2
10238 call detect_parport
10239 shl bx, #0x0e
10240 mov ax, 0x410 ; Equipment word bits 14..15 determing # parallel ports
10241 and ax, #0x3fff
10242 or ax, bx ; set number of parallel ports
10243 mov 0x410, ax
10245 ;; Serial setup
10246 SET_INT_VECTOR(0x0C, #0xF000, #dummy_iret_handler)
10247 SET_INT_VECTOR(0x14, #0xF000, #int14_handler)
10248 xor bx, bx
10249 mov cl, #0x0a ; timeout value
10250 mov dx, #0x03f8 ; Serial I/O address, port 1
10251 call detect_serial
10252 mov dx, #0x02f8 ; Serial I/O address, port 2
10253 call detect_serial
10254 mov dx, #0x03e8 ; Serial I/O address, port 3
10255 call detect_serial
10256 mov dx, #0x02e8 ; Serial I/O address, port 4
10257 call detect_serial
10258 shl bx, #0x09
10259 mov ax, 0x410 ; Equipment word bits 9..11 determing # serial ports
10260 and ax, #0xf1ff
10261 or ax, bx ; set number of serial port
10262 mov 0x410, ax
10264 ;; CMOS RTC
10265 SET_INT_VECTOR(0x1A, #0xF000, #int1a_handler)
10266 SET_INT_VECTOR(0x4A, #0xF000, #dummy_iret_handler)
10267 SET_INT_VECTOR(0x70, #0xF000, #int70_handler)
10268 ;; BIOS DATA AREA 0x4CE ???
10269 call timer_tick_post
10271 ;; PS/2 mouse setup
10272 SET_INT_VECTOR(0x74, #0xF000, #int74_handler)
10274 ;; IRQ13 (FPU exception) setup
10275 SET_INT_VECTOR(0x75, #0xF000, #int75_handler)
10277 ;; Video setup
10278 SET_INT_VECTOR(0x10, #0xF000, #int10_handler)
10280 ;; PIC
10281 mov al, #0x11 ; send initialisation commands
10282 out 0x20, al
10283 out 0xa0, al
10284 mov al, #0x08
10285 out 0x21, al
10286 mov al, #0x70
10287 out 0xa1, al
10288 mov al, #0x04
10289 out 0x21, al
10290 mov al, #0x02
10291 out 0xa1, al
10292 mov al, #0x01
10293 out 0x21, al
10294 out 0xa1, al
10295 mov al, #0xb8
10296 out 0x21, AL ;master pic: unmask IRQ 0, 1, 2, 6
10297 #if BX_USE_PS2_MOUSE
10298 mov al, #0x8f
10299 #else
10300 mov al, #0x9f
10301 #endif
10302 out 0xa1, AL ;slave pic: unmask IRQ 12, 13, 14
10304 mov cx, #0xc000 ;; init vga bios
10305 mov ax, #0xc780
10306 call rom_scan
10308 call _print_bios_banner
10310 #if BX_ROMBIOS32
10311 call rombios32_init
10312 #else
10313 #if BX_PCIBIOS
10314 call pcibios_init_iomem_bases
10315 call pcibios_init_irqs
10316 #endif //BX_PCIBIOS
10317 #endif
10320 ;; Floppy setup
10322 call floppy_drive_post
10324 #if BX_USE_ATADRV
10327 ;; Hard Drive setup
10329 call hard_drive_post
10332 ;; ATA/ATAPI driver setup
10334 call _ata_init
10335 call _ata_detect
10337 #else // BX_USE_ATADRV
10340 ;; Hard Drive setup
10342 call hard_drive_post
10344 #endif // BX_USE_ATADRV
10346 #if BX_ELTORITO_BOOT
10348 ;; eltorito floppy/harddisk emulation from cd
10350 call _cdemu_init
10352 #endif // BX_ELTORITO_BOOT
10354 call _init_boot_vectors
10356 mov cx, #0xc800 ;; init option roms
10357 mov ax, #0xe000
10358 call rom_scan
10360 sti ;; enable interrupts
10361 int #0x19
10363 .org 0xe2c3 ; NMI Handler Entry Point
10364 nmi:
10365 ;; FIXME the NMI handler should not panic
10366 ;; but iret when called from int75 (fpu exception)
10367 call _nmi_handler_msg
10368 iret
10370 int75_handler:
10371 out 0xf0, al // clear irq13
10372 call eoi_both_pics // clear interrupt
10373 int 2 // legacy nmi call
10374 iret
10376 ;-------------------------------------------
10377 ;- INT 13h Fixed Disk Services Entry Point -
10378 ;-------------------------------------------
10379 .org 0xe3fe ; INT 13h Fixed Disk Services Entry Point
10380 int13_handler:
10381 //JMPL(int13_relocated)
10382 jmp int13_relocated
10384 .org 0xe401 ; Fixed Disk Parameter Table
10386 ;----------
10387 ;- INT19h -
10388 ;----------
10389 .org 0xe6f2 ; INT 19h Boot Load Service Entry Point
10390 int19_handler:
10392 jmp int19_relocated
10393 ;-------------------------------------------
10394 ;- System BIOS Configuration Data Table
10395 ;-------------------------------------------
10396 .org BIOS_CONFIG_TABLE
10397 db 0x08 ; Table size (bytes) -Lo
10398 db 0x00 ; Table size (bytes) -Hi
10399 db SYS_MODEL_ID
10400 db SYS_SUBMODEL_ID
10401 db BIOS_REVISION
10402 ; Feature byte 1
10403 ; b7: 1=DMA channel 3 used by hard disk
10404 ; b6: 1=2 interrupt controllers present
10405 ; b5: 1=RTC present
10406 ; b4: 1=BIOS calls int 15h/4Fh every key
10407 ; b3: 1=wait for extern event supported (Int 15h/41h)
10408 ; b2: 1=extended BIOS data area used
10409 ; b1: 0=AT or ESDI bus, 1=MicroChannel
10410 ; b0: 1=Dual bus (MicroChannel + ISA)
10411 db (0 << 7) | \
10412 (1 << 6) | \
10413 (1 << 5) | \
10414 (BX_CALL_INT15_4F << 4) | \
10415 (0 << 3) | \
10416 (BX_USE_EBDA << 2) | \
10417 (0 << 1) | \
10418 (0 << 0)
10419 ; Feature byte 2
10420 ; b7: 1=32-bit DMA supported
10421 ; b6: 1=int16h, function 9 supported
10422 ; b5: 1=int15h/C6h (get POS data) supported
10423 ; b4: 1=int15h/C7h (get mem map info) supported
10424 ; b3: 1=int15h/C8h (en/dis CPU) supported
10425 ; b2: 1=non-8042 kb controller
10426 ; b1: 1=data streaming supported
10427 ; b0: reserved
10428 db (0 << 7) | \
10429 (1 << 6) | \
10430 (0 << 5) | \
10431 (0 << 4) | \
10432 (0 << 3) | \
10433 (0 << 2) | \
10434 (0 << 1) | \
10435 (0 << 0)
10436 ; Feature byte 3
10437 ; b7: not used
10438 ; b6: reserved
10439 ; b5: reserved
10440 ; b4: POST supports ROM-to-RAM enable/disable
10441 ; b3: SCSI on system board
10442 ; b2: info panel installed
10443 ; b1: Initial Machine Load (IML) system - BIOS on disk
10444 ; b0: SCSI supported in IML
10445 db 0x00
10446 ; Feature byte 4
10447 ; b7: IBM private
10448 ; b6: EEPROM present
10449 ; b5-3: ABIOS presence (011 = not supported)
10450 ; b2: private
10451 ; b1: memory split above 16Mb supported
10452 ; b0: POSTEXT directly supported by POST
10453 db 0x00
10454 ; Feature byte 5 (IBM)
10455 ; b1: enhanced mouse
10456 ; b0: flash EPROM
10457 db 0x00
10461 .org 0xe729 ; Baud Rate Generator Table
10463 ;----------
10464 ;- INT14h -
10465 ;----------
10466 .org 0xe739 ; INT 14h Serial Communications Service Entry Point
10467 int14_handler:
10468 push ds
10469 pusha
10470 xor ax, ax
10471 mov ds, ax
10472 call _int14_function
10473 popa
10474 pop ds
10475 iret
10478 ;----------------------------------------
10479 ;- INT 16h Keyboard Service Entry Point -
10480 ;----------------------------------------
10481 .org 0xe82e
10482 int16_handler:
10485 push ds
10486 pushf
10487 pusha
10489 cmp ah, #0x00
10490 je int16_F00
10491 cmp ah, #0x10
10492 je int16_F00
10494 mov bx, #0xf000
10495 mov ds, bx
10496 call _int16_function
10497 popa
10498 popf
10499 pop ds
10500 jz int16_zero_set
10502 int16_zero_clear:
10503 push bp
10504 mov bp, sp
10505 //SEG SS
10506 and BYTE [bp + 0x06], #0xbf
10507 pop bp
10508 iret
10510 int16_zero_set:
10511 push bp
10512 mov bp, sp
10513 //SEG SS
10514 or BYTE [bp + 0x06], #0x40
10515 pop bp
10516 iret
10518 int16_F00:
10519 mov bx, #0x0040
10520 mov ds, bx
10522 int16_wait_for_key:
10524 mov bx, 0x001a
10525 cmp bx, 0x001c
10526 jne int16_key_found
10529 #if 0
10530 /* no key yet, call int 15h, function AX=9002 */
10531 0x50, /* push AX */
10532 0xb8, 0x02, 0x90, /* mov AX, #0x9002 */
10533 0xcd, 0x15, /* int 15h */
10534 0x58, /* pop AX */
10535 0xeb, 0xea, /* jmp WAIT_FOR_KEY */
10536 #endif
10537 jmp int16_wait_for_key
10539 int16_key_found:
10540 mov bx, #0xf000
10541 mov ds, bx
10542 call _int16_function
10543 popa
10544 popf
10545 pop ds
10546 #if 0
10547 /* notify int16 complete w/ int 15h, function AX=9102 */
10548 0x50, /* push AX */
10549 0xb8, 0x02, 0x91, /* mov AX, #0x9102 */
10550 0xcd, 0x15, /* int 15h */
10551 0x58, /* pop AX */
10552 #endif
10553 iret
10557 ;-------------------------------------------------
10558 ;- INT09h : Keyboard Hardware Service Entry Point -
10559 ;-------------------------------------------------
10560 .org 0xe987
10561 int09_handler:
10563 push ax
10565 mov al, #0xAD ;;disable keyboard
10566 out #0x64, al
10568 mov al, #0x0B
10569 out #0x20, al
10570 in al, #0x20
10571 and al, #0x02
10572 jz int09_finish
10574 in al, #0x60 ;;read key from keyboard controller
10576 push ds
10577 pusha
10578 #ifdef BX_CALL_INT15_4F
10579 mov ah, #0x4f ;; allow for keyboard intercept
10581 int #0x15
10582 jnc int09_done
10583 #endif
10585 ;; check for extended key
10586 cmp al, #0xe0
10587 jne int09_check_pause
10588 xor ax, ax
10589 mov ds, ax
10590 mov al, BYTE [0x496] ;; mf2_state |= 0x02
10591 or al, #0x02
10592 mov BYTE [0x496], al
10593 jmp int09_done
10595 int09_check_pause: ;; check for pause key
10596 cmp al, #0xe1
10597 jne int09_process_key
10598 xor ax, ax
10599 mov ds, ax
10600 mov al, BYTE [0x496] ;; mf2_state |= 0x01
10601 or al, #0x01
10602 mov BYTE [0x496], al
10603 jmp int09_done
10605 int09_process_key:
10606 mov bx, #0xf000
10607 mov ds, bx
10608 call _int09_function
10610 int09_done:
10611 popa
10612 pop ds
10614 call eoi_master_pic
10616 int09_finish:
10617 mov al, #0xAE ;;enable keyboard
10618 out #0x64, al
10619 pop ax
10620 iret
10623 ;----------------------------------------
10624 ;- INT 13h Diskette Service Entry Point -
10625 ;----------------------------------------
10626 .org 0xec59
10627 int13_diskette:
10628 jmp int13_noeltorito
10630 ;---------------------------------------------
10631 ;- INT 0Eh Diskette Hardware ISR Entry Point -
10632 ;---------------------------------------------
10633 .org 0xef57 ; INT 0Eh Diskette Hardware ISR Entry Point
10634 int0e_handler:
10635 push ax
10636 push dx
10637 mov dx, #0x03f4
10638 in al, dx
10639 and al, #0xc0
10640 cmp al, #0xc0
10641 je int0e_normal
10642 mov dx, #0x03f5
10643 mov al, #0x08 ; sense interrupt status
10644 out dx, al
10645 int0e_loop1:
10646 mov dx, #0x03f4
10647 in al, dx
10648 and al, #0xc0
10649 cmp al, #0xc0
10650 jne int0e_loop1
10651 int0e_loop2:
10652 mov dx, #0x03f5
10653 in al, dx
10654 mov dx, #0x03f4
10655 in al, dx
10656 and al, #0xc0
10657 cmp al, #0xc0
10658 je int0e_loop2
10659 int0e_normal:
10660 push ds
10661 xor ax, ax ;; segment 0000
10662 mov ds, ax
10663 call eoi_master_pic
10664 mov al, 0x043e
10665 or al, #0x80 ;; diskette interrupt has occurred
10666 mov 0x043e, al
10667 pop ds
10668 pop dx
10669 pop ax
10670 iret
10673 .org 0xefc7 ; Diskette Controller Parameter Table
10674 diskette_param_table:
10675 ;; Since no provisions are made for multiple drive types, most
10676 ;; values in this table are ignored. I set parameters for 1.44M
10677 ;; floppy here
10678 db 0xAF
10679 db 0x02 ;; head load time 0000001, DMA used
10680 db 0x25
10681 db 0x02
10682 db 18
10683 db 0x1B
10684 db 0xFF
10685 db 0x6C
10686 db 0xF6
10687 db 0x0F
10688 db 0x08
10691 ;----------------------------------------
10692 ;- INT17h : Printer Service Entry Point -
10693 ;----------------------------------------
10694 .org 0xefd2
10695 int17_handler:
10696 push ds
10697 pusha
10698 xor ax, ax
10699 mov ds, ax
10700 call _int17_function
10701 popa
10702 pop ds
10703 iret
10705 diskette_param_table2:
10706 ;; New diskette parameter table adding 3 parameters from IBM
10707 ;; Since no provisions are made for multiple drive types, most
10708 ;; values in this table are ignored. I set parameters for 1.44M
10709 ;; floppy here
10710 db 0xAF
10711 db 0x02 ;; head load time 0000001, DMA used
10712 db 0x25
10713 db 0x02
10714 db 18
10715 db 0x1B
10716 db 0xFF
10717 db 0x6C
10718 db 0xF6
10719 db 0x0F
10720 db 0x08
10721 db 79 ;; maximum track
10722 db 0 ;; data transfer rate
10723 db 4 ;; drive type in cmos
10725 .org 0xf045 ; INT 10 Functions 0-Fh Entry Point
10726 HALT(__LINE__)
10727 iret
10729 ;----------
10730 ;- INT10h -
10731 ;----------
10732 .org 0xf065 ; INT 10h Video Support Service Entry Point
10733 int10_handler:
10734 ;; dont do anything, since the VGA BIOS handles int10h requests
10735 iret
10737 .org 0xf0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
10739 ;----------
10740 ;- INT12h -
10741 ;----------
10742 .org 0xf841 ; INT 12h Memory Size Service Entry Point
10743 ; ??? different for Pentium (machine check)?
10744 int12_handler:
10745 push ds
10746 mov ax, #0x0040
10747 mov ds, ax
10748 mov ax, 0x0013
10749 pop ds
10750 iret
10752 ;----------
10753 ;- INT11h -
10754 ;----------
10755 .org 0xf84d ; INT 11h Equipment List Service Entry Point
10756 int11_handler:
10757 push ds
10758 mov ax, #0x0040
10759 mov ds, ax
10760 mov ax, 0x0010
10761 pop ds
10762 iret
10764 ;----------
10765 ;- INT15h -
10766 ;----------
10767 .org 0xf859 ; INT 15h System Services Entry Point
10768 int15_handler:
10769 pushf
10770 #if BX_APM
10771 cmp ah, #0x53
10772 je apm_call
10773 #endif
10774 push ds
10775 push es
10776 cmp ah, #0x86
10777 je int15_handler32
10778 cmp ah, #0xE8
10779 je int15_handler32
10780 pusha
10781 #if BX_USE_PS2_MOUSE
10782 cmp ah, #0xC2
10783 je int15_handler_mouse
10784 #endif
10785 call _int15_function
10786 int15_handler_mouse_ret:
10787 popa
10788 int15_handler32_ret:
10789 pop es
10790 pop ds
10791 popf
10792 jmp iret_modify_cf
10793 #if BX_APM
10794 apm_call:
10795 jmp _apmreal_entry
10796 #endif
10798 #if BX_USE_PS2_MOUSE
10799 int15_handler_mouse:
10800 call _int15_function_mouse
10801 jmp int15_handler_mouse_ret
10802 #endif
10804 int15_handler32:
10805 pushad
10806 call _int15_function32
10807 popad
10808 jmp int15_handler32_ret
10810 ;; Protected mode IDT descriptor
10812 ;; I just make the limit 0, so the machine will shutdown
10813 ;; if an exception occurs during protected mode memory
10814 ;; transfers.
10816 ;; Set base to f0000 to correspond to beginning of BIOS,
10817 ;; in case I actually define an IDT later
10818 ;; Set limit to 0
10820 pmode_IDT_info:
10821 dw 0x0000 ;; limit 15:00
10822 dw 0x0000 ;; base 15:00
10823 db 0x0f ;; base 23:16
10825 ;; Real mode IDT descriptor
10827 ;; Set to typical real-mode values.
10828 ;; base = 000000
10829 ;; limit = 03ff
10831 rmode_IDT_info:
10832 dw 0x03ff ;; limit 15:00
10833 dw 0x0000 ;; base 15:00
10834 db 0x00 ;; base 23:16
10837 ;----------
10838 ;- INT1Ah -
10839 ;----------
10840 .org 0xfe6e ; INT 1Ah Time-of-day Service Entry Point
10841 int1a_handler:
10842 #if BX_PCIBIOS
10843 cmp ah, #0xb1
10844 jne int1a_normal
10845 call pcibios_real
10846 jc pcibios_error
10847 retf 2
10848 pcibios_error:
10849 mov bl, ah
10850 mov ah, #0xb1
10851 push ds
10852 pusha
10853 mov ax, ss ; set readable descriptor to ds, for calling pcibios
10854 mov ds, ax ; on 16bit protected mode.
10855 jmp int1a_callfunction
10856 int1a_normal:
10857 #endif
10858 push ds
10859 pusha
10860 xor ax, ax
10861 mov ds, ax
10862 int1a_callfunction:
10863 call _int1a_function
10864 popa
10865 pop ds
10866 iret
10869 ;; int70h: IRQ8 - CMOS RTC
10871 int70_handler:
10872 push ds
10873 pushad
10874 xor ax, ax
10875 mov ds, ax
10876 call _int70_function
10877 popad
10878 pop ds
10879 iret
10881 ;---------
10882 ;- INT08 -
10883 ;---------
10884 .org 0xfea5 ; INT 08h System Timer ISR Entry Point
10885 int08_handler:
10887 push eax
10888 push ds
10889 xor ax, ax
10890 mov ds, ax
10892 ;; time to turn off drive(s)?
10893 mov al,0x0440
10894 or al,al
10895 jz int08_floppy_off
10896 dec al
10897 mov 0x0440,al
10898 jnz int08_floppy_off
10899 ;; turn motor(s) off
10900 push dx
10901 mov dx,#0x03f2
10902 in al,dx
10903 and al,#0xcf
10904 out dx,al
10905 pop dx
10906 int08_floppy_off:
10908 mov eax, 0x046c ;; get ticks dword
10909 inc eax
10911 ;; compare eax to one days worth of timer ticks at 18.2 hz
10912 cmp eax, #0x001800B0
10913 jb int08_store_ticks
10914 ;; there has been a midnight rollover at this point
10915 xor eax, eax ;; zero out counter
10916 inc BYTE 0x0470 ;; increment rollover flag
10918 int08_store_ticks:
10919 mov 0x046c, eax ;; store new ticks dword
10920 ;; chain to user timer tick INT #0x1c
10921 //pushf
10922 //;; call_ep [ds:loc]
10923 //CALL_EP( 0x1c << 2 )
10924 int #0x1c
10926 call eoi_master_pic
10927 pop ds
10928 pop eax
10929 iret
10931 .org 0xfef3 ; Initial Interrupt Vector Offsets Loaded by POST
10934 .org 0xff00
10935 .ascii BIOS_COPYRIGHT_STRING
10937 ;------------------------------------------------
10938 ;- IRET Instruction for Dummy Interrupt Handler -
10939 ;------------------------------------------------
10940 .org 0xff53 ; IRET Instruction for Dummy Interrupt Handler
10941 dummy_iret_handler:
10942 iret
10944 .org 0xff54 ; INT 05h Print Screen Service Entry Point
10945 HALT(__LINE__)
10946 iret
10948 .org 0xfff0 ; Power-up Entry Point
10949 jmp 0xf000:post
10951 .org 0xfff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
10952 .ascii BIOS_BUILD_DATE
10954 .org 0xfffe ; System Model ID
10955 db SYS_MODEL_ID
10956 db 0x00 ; filler
10958 .org 0xfa6e ;; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
10959 ASM_END
10961 * This font comes from the fntcol16.zip package (c) by Joseph Gil
10962 * found at ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip
10963 * This font is public domain
10965 static Bit8u vgafont8[128*8]=
10967 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
10968 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e,
10969 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e,
10970 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00,
10971 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00,
10972 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c,
10973 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c,
10974 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00,
10975 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff,
10976 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
10977 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff,
10978 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78,
10979 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18,
10980 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0,
10981 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0,
10982 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99,
10983 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00,
10984 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00,
10985 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18,
10986 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
10987 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00,
10988 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78,
10989 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00,
10990 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff,
10991 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00,
10992 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
10993 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00,
10994 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
10995 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00,
10996 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00,
10997 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00,
10998 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00,
10999 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
11000 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00,
11001 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
11002 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00,
11003 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00,
11004 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00,
11005 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00,
11006 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
11007 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00,
11008 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
11009 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
11010 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00,
11011 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60,
11012 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
11013 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00,
11014 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00,
11015 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00,
11016 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00,
11017 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00,
11018 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00,
11019 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00,
11020 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00,
11021 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00,
11022 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00,
11023 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00,
11024 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00,
11025 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00,
11026 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60,
11027 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00,
11028 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00,
11029 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00,
11030 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00,
11031 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00,
11032 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00,
11033 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00,
11034 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00,
11035 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00,
11036 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00,
11037 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00,
11038 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00,
11039 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00,
11040 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
11041 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00,
11042 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00,
11043 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00,
11044 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00,
11045 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00,
11046 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00,
11047 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
11048 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00,
11049 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00,
11050 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00,
11051 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
11052 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00,
11053 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
11054 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00,
11055 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00,
11056 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00,
11057 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00,
11058 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
11059 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00,
11060 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
11061 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
11062 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
11063 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
11064 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00,
11065 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00,
11066 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00,
11067 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00,
11068 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
11069 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00,
11070 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
11071 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00,
11072 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
11073 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78,
11074 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00,
11075 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
11076 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00,
11077 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
11078 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
11079 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0,
11080 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e,
11081 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00,
11082 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00,
11083 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00,
11084 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
11085 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
11086 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00,
11087 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00,
11088 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
11089 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00,
11090 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00,
11091 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
11092 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00,
11093 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
11094 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00,
11097 ASM_START
11098 .org 0xcc00
11099 bios_table_area_end:
11100 // bcc-generated data will be placed here
11101 ASM_END