kvm: qemu: LSI SCSI and e1000 unregister callbacks
[kvm-userspace.git] / bios / rombios.c
blob318de57bad428b17cc186cdce5e951ea3773af16
1 /////////////////////////////////////////////////////////////////////////
2 // $Id: rombios.c,v 1.182 2007/08/01 17:09:51 vruppert Exp $
3 /////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (C) 2002 MandrakeSoft S.A.
6 //
7 // MandrakeSoft S.A.
8 // 43, rue d'Aboukir
9 // 75002 Paris - France
10 // http://www.linux-mandrake.com/
11 // http://www.mandrakesoft.com/
13 // This library is free software; you can redistribute it and/or
14 // modify it under the terms of the GNU Lesser General Public
15 // License as published by the Free Software Foundation; either
16 // version 2 of the License, or (at your option) any later version.
18 // This library is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 // Lesser General Public License for more details.
23 // You should have received a copy of the GNU Lesser General Public
24 // License along with this library; if not, write to the Free Software
25 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27 // ROM BIOS for use with Bochs/Plex86/QEMU emulation environment
30 // ROM BIOS compatability entry points:
31 // ===================================
32 // $e05b ; POST Entry Point
33 // $e2c3 ; NMI Handler Entry Point
34 // $e3fe ; INT 13h Fixed Disk Services Entry Point
35 // $e401 ; Fixed Disk Parameter Table
36 // $e6f2 ; INT 19h Boot Load Service Entry Point
37 // $e6f5 ; Configuration Data Table
38 // $e729 ; Baud Rate Generator Table
39 // $e739 ; INT 14h Serial Communications Service Entry Point
40 // $e82e ; INT 16h Keyboard Service Entry Point
41 // $e987 ; INT 09h Keyboard Service Entry Point
42 // $ec59 ; INT 13h Diskette Service Entry Point
43 // $ef57 ; INT 0Eh Diskette Hardware ISR Entry Point
44 // $efc7 ; Diskette Controller Parameter Table
45 // $efd2 ; INT 17h Printer Service Entry Point
46 // $f045 ; INT 10 Functions 0-Fh Entry Point
47 // $f065 ; INT 10h Video Support Service Entry Point
48 // $f0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
49 // $f841 ; INT 12h Memory Size Service Entry Point
50 // $f84d ; INT 11h Equipment List Service Entry Point
51 // $f859 ; INT 15h System Services Entry Point
52 // $fa6e ; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
53 // $fe6e ; INT 1Ah Time-of-day Service Entry Point
54 // $fea5 ; INT 08h System Timer ISR Entry Point
55 // $fef3 ; Initial Interrupt Vector Offsets Loaded by POST
56 // $ff53 ; IRET Instruction for Dummy Interrupt Handler
57 // $ff54 ; INT 05h Print Screen Service Entry Point
58 // $fff0 ; Power-up Entry Point
59 // $fff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
60 // $fffe ; System Model ID
62 // NOTES for ATA/ATAPI driver (cbbochs@free.fr)
63 // Features
64 // - supports up to 4 ATA interfaces
65 // - device/geometry detection
66 // - 16bits/32bits device access
67 // - pchs/lba access
68 // - datain/dataout/packet command support
70 // NOTES for El-Torito Boot (cbbochs@free.fr)
71 // - CD-ROM booting is only available if ATA/ATAPI Driver is available
72 // - Current code is only able to boot mono-session cds
73 // - Current code can not boot and emulate a hard-disk
74 // the bios will panic otherwise
75 // - Current code also use memory in EBDA segement.
76 // - I used cmos byte 0x3D to store extended information on boot-device
77 // - Code has to be modified modified to handle multiple cdrom drives
78 // - Here are the cdrom boot failure codes:
79 // 1 : no atapi device found
80 // 2 : no atapi cdrom found
81 // 3 : can not read cd - BRVD
82 // 4 : cd is not eltorito (BRVD)
83 // 5 : cd is not eltorito (ISO TAG)
84 // 6 : cd is not eltorito (ELTORITO TAG)
85 // 7 : can not read cd - boot catalog
86 // 8 : boot catalog : bad header
87 // 9 : boot catalog : bad platform
88 // 10 : boot catalog : bad signature
89 // 11 : boot catalog : bootable flag not set
90 // 12 : can not read cd - boot image
92 // ATA driver
93 // - EBDA segment.
94 // I used memory starting at 0x121 in the segment
95 // - the translation policy is defined in cmos regs 0x39 & 0x3a
97 // TODO :
99 // int74
100 // - needs to be reworked. Uses direct [bp] offsets. (?)
102 // int13:
103 // - f04 (verify sectors) isn't complete (?)
104 // - f02/03/04 should set current cyl,etc in BDA (?)
105 // - rewrite int13_relocated & clean up int13 entry code
107 // NOTES:
108 // - NMI access (bit7 of addr written to 70h)
110 // ATA driver
111 // - should handle the "don't detect" bit (cmos regs 0x3b & 0x3c)
112 // - could send the multiple-sector read/write commands
114 // El-Torito
115 // - Emulate a Hard-disk (currently only diskette can be emulated) see "FIXME ElTorito Harddisk"
116 // - Implement remaining int13_cdemu functions (as defined by El-Torito specs)
117 // - cdrom drive is hardcoded to ide 0 device 1 in several places. see "FIXME ElTorito Hardcoded"
118 // - int13 Fix DL when emulating a cd. In that case DL is decremented before calling real int13.
119 // This is ok. But DL should be reincremented afterwards.
120 // - Fix all "FIXME ElTorito Various"
121 // - should be able to boot any cdrom instead of the first one
123 // BCC Bug: find a generic way to handle the bug of #asm after an "if" (fixed in 0.16.7)
125 #include "rombios.h"
127 #define DEBUG_ATA 0
128 #define DEBUG_INT13_HD 0
129 #define DEBUG_INT13_CD 0
130 #define DEBUG_INT13_ET 0
131 #define DEBUG_INT13_FL 0
132 #define DEBUG_INT15 0
133 #define DEBUG_INT16 0
134 #define DEBUG_INT1A 0
135 #define DEBUG_INT74 0
136 #define DEBUG_APM 0
138 #define BX_CPU 3
139 #define BX_USE_PS2_MOUSE 1
140 #define BX_CALL_INT15_4F 1
141 #define BX_USE_EBDA 1
142 #define BX_SUPPORT_FLOPPY 1
143 #define BX_FLOPPY_ON_CNT 37 /* 2 seconds */
144 #define BX_PCIBIOS 1
145 #define BX_APM 1
147 #define BX_USE_ATADRV 1
148 #define BX_ELTORITO_BOOT 1
150 #define BX_MAX_ATA_INTERFACES 4
151 #define BX_MAX_ATA_DEVICES (BX_MAX_ATA_INTERFACES*2)
153 #define BX_VIRTUAL_PORTS 1 /* normal output to Bochs ports */
154 #define BX_DEBUG_SERIAL 0 /* output to COM1 */
156 /* model byte 0xFC = AT */
157 #define SYS_MODEL_ID 0xFC
158 #define SYS_SUBMODEL_ID 0x00
159 #define BIOS_REVISION 1
160 #define BIOS_CONFIG_TABLE 0xe6f5
162 #ifndef BIOS_BUILD_DATE
163 # define BIOS_BUILD_DATE "06/23/99"
164 #endif
166 // 1K of base memory used for Extended Bios Data Area (EBDA)
167 // EBDA is used for PS/2 mouse support, and IDE BIOS, etc.
168 #define EBDA_SEG 0x9FC0
169 #define EBDA_SIZE 1 // In KiB
170 #define BASE_MEM_IN_K (640 - EBDA_SIZE)
172 /* 256 bytes at 0x9ff00 -- 0x9ffff is used for the IPL boot table. */
173 #define IPL_SEG 0x9ff0
174 #define IPL_TABLE_OFFSET 0x0000
175 #define IPL_TABLE_ENTRIES 8
176 #define IPL_COUNT_OFFSET 0x0080 /* u16: number of valid table entries */
177 #define IPL_SEQUENCE_OFFSET 0x0082 /* u16: next boot device */
178 #define IPL_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: 1.182 $ $Date: 2007/08/01 17:09:51 $";
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 not found
3383 if(device >= BX_MAX_ATA_DEVICES) return 2;
3385 // Read the Boot Record Volume Descriptor
3386 memsetb(get_SS(),atacmd,0,12);
3387 atacmd[0]=0x28; // READ command
3388 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3389 atacmd[8]=(0x01 & 0x00ff); // Sectors
3390 atacmd[2]=(0x11 & 0xff000000) >> 24; // LBA
3391 atacmd[3]=(0x11 & 0x00ff0000) >> 16;
3392 atacmd[4]=(0x11 & 0x0000ff00) >> 8;
3393 atacmd[5]=(0x11 & 0x000000ff);
3394 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3395 return 3;
3397 // Validity checks
3398 if(buffer[0]!=0)return 4;
3399 for(i=0;i<5;i++){
3400 if(buffer[1+i]!=read_byte(0xf000,&isotag[i]))return 5;
3402 for(i=0;i<23;i++)
3403 if(buffer[7+i]!=read_byte(0xf000,&eltorito[i]))return 6;
3405 // ok, now we calculate the Boot catalog address
3406 lba=buffer[0x4A]*0x1000000+buffer[0x49]*0x10000+buffer[0x48]*0x100+buffer[0x47];
3408 // And we read the Boot Catalog
3409 memsetb(get_SS(),atacmd,0,12);
3410 atacmd[0]=0x28; // READ command
3411 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3412 atacmd[8]=(0x01 & 0x00ff); // Sectors
3413 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3414 atacmd[3]=(lba & 0x00ff0000) >> 16;
3415 atacmd[4]=(lba & 0x0000ff00) >> 8;
3416 atacmd[5]=(lba & 0x000000ff);
3417 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3418 return 7;
3420 // Validation entry
3421 if(buffer[0x00]!=0x01)return 8; // Header
3422 if(buffer[0x01]!=0x00)return 9; // Platform
3423 if(buffer[0x1E]!=0x55)return 10; // key 1
3424 if(buffer[0x1F]!=0xAA)return 10; // key 2
3426 // Initial/Default Entry
3427 if(buffer[0x20]!=0x88)return 11; // Bootable
3429 write_byte(ebda_seg,&EbdaData->cdemu.media,buffer[0x21]);
3430 if(buffer[0x21]==0){
3431 // FIXME ElTorito Hardcoded. cdrom is hardcoded as device 0xE0.
3432 // Win2000 cd boot needs to know it booted from cd
3433 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0xE0);
3435 else if(buffer[0x21]<4)
3436 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x00);
3437 else
3438 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x80);
3440 write_byte(ebda_seg,&EbdaData->cdemu.controller_index,device/2);
3441 write_byte(ebda_seg,&EbdaData->cdemu.device_spec,device%2);
3443 boot_segment=buffer[0x23]*0x100+buffer[0x22];
3444 if(boot_segment==0x0000)boot_segment=0x07C0;
3446 write_word(ebda_seg,&EbdaData->cdemu.load_segment,boot_segment);
3447 write_word(ebda_seg,&EbdaData->cdemu.buffer_segment,0x0000);
3449 nbsectors=buffer[0x27]*0x100+buffer[0x26];
3450 write_word(ebda_seg,&EbdaData->cdemu.sector_count,nbsectors);
3452 lba=buffer[0x2B]*0x1000000+buffer[0x2A]*0x10000+buffer[0x29]*0x100+buffer[0x28];
3453 write_dword(ebda_seg,&EbdaData->cdemu.ilba,lba);
3455 // And we read the image in memory
3456 memsetb(get_SS(),atacmd,0,12);
3457 atacmd[0]=0x28; // READ command
3458 atacmd[7]=((1+(nbsectors-1)/4) & 0xff00) >> 8; // Sectors
3459 atacmd[8]=((1+(nbsectors-1)/4) & 0x00ff); // Sectors
3460 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3461 atacmd[3]=(lba & 0x00ff0000) >> 16;
3462 atacmd[4]=(lba & 0x0000ff00) >> 8;
3463 atacmd[5]=(lba & 0x000000ff);
3464 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, nbsectors*512L, ATA_DATA_IN, boot_segment,0)) != 0)
3465 return 12;
3467 // Remember the media type
3468 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
3469 case 0x01: // 1.2M floppy
3470 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,15);
3471 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3472 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3473 break;
3474 case 0x02: // 1.44M floppy
3475 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,18);
3476 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3477 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3478 break;
3479 case 0x03: // 2.88M floppy
3480 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,36);
3481 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3482 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3483 break;
3484 case 0x04: // Harddrive
3485 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,read_byte(boot_segment,446+6)&0x3f);
3486 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,
3487 (read_byte(boot_segment,446+6)<<2) + read_byte(boot_segment,446+7) + 1);
3488 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,read_byte(boot_segment,446+5) + 1);
3489 break;
3492 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0) {
3493 // Increase bios installed hardware number of devices
3494 if(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)==0x00)
3495 write_byte(0x40,0x10,read_byte(0x40,0x10)|0x41);
3496 else
3497 write_byte(ebda_seg, &EbdaData->ata.hdcount, read_byte(ebda_seg, &EbdaData->ata.hdcount) + 1);
3501 // everything is ok, so from now on, the emulation is active
3502 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0)
3503 write_byte(ebda_seg,&EbdaData->cdemu.active,0x01);
3505 // return the boot drive + no error
3506 return (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)*0x100)+0;
3509 // ---------------------------------------------------------------------------
3510 // End of El-Torito boot functions
3511 // ---------------------------------------------------------------------------
3512 #endif // BX_ELTORITO_BOOT
3514 void
3515 int14_function(regs, ds, iret_addr)
3516 pusha_regs_t regs; // regs pushed from PUSHA instruction
3517 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
3518 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
3520 Bit16u addr,timer,val16;
3521 Bit8u timeout;
3523 ASM_START
3525 ASM_END
3527 addr = read_word(0x0040, (regs.u.r16.dx << 1));
3528 timeout = read_byte(0x0040, 0x007C + regs.u.r16.dx);
3529 if ((regs.u.r16.dx < 4) && (addr > 0)) {
3530 switch (regs.u.r8.ah) {
3531 case 0:
3532 outb(addr+3, inb(addr+3) | 0x80);
3533 if (regs.u.r8.al & 0xE0 == 0) {
3534 outb(addr, 0x17);
3535 outb(addr+1, 0x04);
3536 } else {
3537 val16 = 0x600 >> ((regs.u.r8.al & 0xE0) >> 5);
3538 outb(addr, val16 & 0xFF);
3539 outb(addr+1, val16 >> 8);
3541 outb(addr+3, regs.u.r8.al & 0x1F);
3542 regs.u.r8.ah = inb(addr+5);
3543 regs.u.r8.al = inb(addr+6);
3544 ClearCF(iret_addr.flags);
3545 break;
3546 case 1:
3547 timer = read_word(0x0040, 0x006C);
3548 while (((inb(addr+5) & 0x60) != 0x60) && (timeout)) {
3549 val16 = read_word(0x0040, 0x006C);
3550 if (val16 != timer) {
3551 timer = val16;
3552 timeout--;
3555 if (timeout) outb(addr, regs.u.r8.al);
3556 regs.u.r8.ah = inb(addr+5);
3557 if (!timeout) regs.u.r8.ah |= 0x80;
3558 ClearCF(iret_addr.flags);
3559 break;
3560 case 2:
3561 timer = read_word(0x0040, 0x006C);
3562 while (((inb(addr+5) & 0x01) == 0) && (timeout)) {
3563 val16 = read_word(0x0040, 0x006C);
3564 if (val16 != timer) {
3565 timer = val16;
3566 timeout--;
3569 if (timeout) {
3570 regs.u.r8.ah = 0;
3571 regs.u.r8.al = inb(addr);
3572 } else {
3573 regs.u.r8.ah = inb(addr+5);
3575 ClearCF(iret_addr.flags);
3576 break;
3577 case 3:
3578 regs.u.r8.ah = inb(addr+5);
3579 regs.u.r8.al = inb(addr+6);
3580 ClearCF(iret_addr.flags);
3581 break;
3582 default:
3583 SetCF(iret_addr.flags); // Unsupported
3585 } else {
3586 SetCF(iret_addr.flags); // Unsupported
3590 void
3591 int15_function(regs, ES, DS, FLAGS)
3592 pusha_regs_t regs; // REGS pushed via pusha
3593 Bit16u ES, DS, FLAGS;
3595 Bit16u ebda_seg=read_word(0x0040,0x000E);
3596 bx_bool prev_a20_enable;
3597 Bit16u base15_00;
3598 Bit8u base23_16;
3599 Bit16u ss;
3600 Bit16u CX,DX;
3602 Bit16u bRegister;
3603 Bit8u irqDisable;
3605 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3607 switch (regs.u.r8.ah) {
3608 case 0x24: /* A20 Control */
3609 switch (regs.u.r8.al) {
3610 case 0x00:
3611 set_enable_a20(0);
3612 CLEAR_CF();
3613 regs.u.r8.ah = 0;
3614 break;
3615 case 0x01:
3616 set_enable_a20(1);
3617 CLEAR_CF();
3618 regs.u.r8.ah = 0;
3619 break;
3620 case 0x02:
3621 regs.u.r8.al = (inb(0x92) >> 1) & 0x01;
3622 CLEAR_CF();
3623 regs.u.r8.ah = 0;
3624 break;
3625 case 0x03:
3626 CLEAR_CF();
3627 regs.u.r8.ah = 0;
3628 regs.u.r16.bx = 3;
3629 break;
3630 default:
3631 BX_INFO("int15: Func 24h, subfunc %02xh, A20 gate control not supported\n", (unsigned) regs.u.r8.al);
3632 SET_CF();
3633 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3635 break;
3637 case 0x41:
3638 SET_CF();
3639 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3640 break;
3642 case 0x4f:
3643 /* keyboard intercept */
3644 #if BX_CPU < 2
3645 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3646 #else
3647 // nop
3648 #endif
3649 SET_CF();
3650 break;
3652 case 0x52: // removable media eject
3653 CLEAR_CF();
3654 regs.u.r8.ah = 0; // "ok ejection may proceed"
3655 break;
3657 case 0x83: {
3658 if( regs.u.r8.al == 0 ) {
3659 // Set Interval requested.
3660 if( ( read_byte( 0x40, 0xA0 ) & 1 ) == 0 ) {
3661 // Interval not already set.
3662 write_byte( 0x40, 0xA0, 1 ); // Set status byte.
3663 write_word( 0x40, 0x98, ES ); // Byte location, segment
3664 write_word( 0x40, 0x9A, regs.u.r16.bx ); // Byte location, offset
3665 write_word( 0x40, 0x9C, regs.u.r16.dx ); // Low word, delay
3666 write_word( 0x40, 0x9E, regs.u.r16.cx ); // High word, delay.
3667 CLEAR_CF( );
3668 irqDisable = inb( 0xA1 );
3669 outb( 0xA1, irqDisable & 0xFE );
3670 bRegister = inb_cmos( 0xB ); // Unmask IRQ8 so INT70 will get through.
3671 outb_cmos( 0xB, bRegister | 0x40 ); // Turn on the Periodic Interrupt timer
3672 } else {
3673 // Interval already set.
3674 BX_DEBUG_INT15("int15: Func 83h, failed, already waiting.\n" );
3675 SET_CF();
3676 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3678 } else if( regs.u.r8.al == 1 ) {
3679 // Clear Interval requested
3680 write_byte( 0x40, 0xA0, 0 ); // Clear status byte
3681 CLEAR_CF( );
3682 bRegister = inb_cmos( 0xB );
3683 outb_cmos( 0xB, bRegister & ~0x40 ); // Turn off the Periodic Interrupt timer
3684 } else {
3685 BX_DEBUG_INT15("int15: Func 83h, failed.\n" );
3686 SET_CF();
3687 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3688 regs.u.r8.al--;
3691 break;
3694 case 0x87:
3695 #if BX_CPU < 3
3696 # error "Int15 function 87h not supported on < 80386"
3697 #endif
3698 // +++ should probably have descriptor checks
3699 // +++ should have exception handlers
3701 // turn off interrupts
3702 ASM_START
3704 ASM_END
3706 prev_a20_enable = set_enable_a20(1); // enable A20 line
3708 // 128K max of transfer on 386+ ???
3709 // source == destination ???
3711 // ES:SI points to descriptor table
3712 // offset use initially comments
3713 // ==============================================
3714 // 00..07 Unused zeros Null descriptor
3715 // 08..0f GDT zeros filled in by BIOS
3716 // 10..17 source ssssssss source of data
3717 // 18..1f dest dddddddd destination of data
3718 // 20..27 CS zeros filled in by BIOS
3719 // 28..2f SS zeros filled in by BIOS
3721 //es:si
3722 //eeee0
3723 //0ssss
3724 //-----
3726 // check for access rights of source & dest here
3728 // Initialize GDT descriptor
3729 base15_00 = (ES << 4) + regs.u.r16.si;
3730 base23_16 = ES >> 12;
3731 if (base15_00 < (ES<<4))
3732 base23_16++;
3733 write_word(ES, regs.u.r16.si+0x08+0, 47); // limit 15:00 = 6 * 8bytes/descriptor
3734 write_word(ES, regs.u.r16.si+0x08+2, base15_00);// base 15:00
3735 write_byte(ES, regs.u.r16.si+0x08+4, base23_16);// base 23:16
3736 write_byte(ES, regs.u.r16.si+0x08+5, 0x93); // access
3737 write_word(ES, regs.u.r16.si+0x08+6, 0x0000); // base 31:24/reserved/limit 19:16
3739 // Initialize CS descriptor
3740 write_word(ES, regs.u.r16.si+0x20+0, 0xffff);// limit 15:00 = normal 64K limit
3741 write_word(ES, regs.u.r16.si+0x20+2, 0x0000);// base 15:00
3742 write_byte(ES, regs.u.r16.si+0x20+4, 0x000f);// base 23:16
3743 write_byte(ES, regs.u.r16.si+0x20+5, 0x9b); // access
3744 write_word(ES, regs.u.r16.si+0x20+6, 0x0000);// base 31:24/reserved/limit 19:16
3746 // Initialize SS descriptor
3747 ss = get_SS();
3748 base15_00 = ss << 4;
3749 base23_16 = ss >> 12;
3750 write_word(ES, regs.u.r16.si+0x28+0, 0xffff); // limit 15:00 = normal 64K limit
3751 write_word(ES, regs.u.r16.si+0x28+2, base15_00);// base 15:00
3752 write_byte(ES, regs.u.r16.si+0x28+4, base23_16);// base 23:16
3753 write_byte(ES, regs.u.r16.si+0x28+5, 0x93); // access
3754 write_word(ES, regs.u.r16.si+0x28+6, 0x0000); // base 31:24/reserved/limit 19:16
3756 CX = regs.u.r16.cx;
3757 ASM_START
3758 // Compile generates locals offset info relative to SP.
3759 // Get CX (word count) from stack.
3760 mov bx, sp
3761 SEG SS
3762 mov cx, _int15_function.CX [bx]
3764 // since we need to set SS:SP, save them to the BDA
3765 // for future restore
3766 push eax
3767 xor eax, eax
3768 mov ds, ax
3769 mov 0x0469, ss
3770 mov 0x0467, sp
3772 SEG ES
3773 lgdt [si + 0x08]
3774 SEG CS
3775 lidt [pmode_IDT_info]
3776 ;; perhaps do something with IDT here
3778 ;; set PE bit in CR0
3779 mov eax, cr0
3780 or al, #0x01
3781 mov cr0, eax
3782 ;; far jump to flush CPU queue after transition to protected mode
3783 JMP_AP(0x0020, protected_mode)
3785 protected_mode:
3786 ;; GDT points to valid descriptor table, now load SS, DS, ES
3787 mov ax, #0x28 ;; 101 000 = 5th descriptor in table, TI=GDT, RPL=00
3788 mov ss, ax
3789 mov ax, #0x10 ;; 010 000 = 2nd descriptor in table, TI=GDT, RPL=00
3790 mov ds, ax
3791 mov ax, #0x18 ;; 011 000 = 3rd descriptor in table, TI=GDT, RPL=00
3792 mov es, ax
3793 xor si, si
3794 xor di, di
3797 movsw ;; move CX words from DS:SI to ES:DI
3799 ;; make sure DS and ES limits are 64KB
3800 mov ax, #0x28
3801 mov ds, ax
3802 mov es, ax
3804 ;; reset PG bit in CR0 ???
3805 mov eax, cr0
3806 and al, #0xFE
3807 mov cr0, eax
3809 ;; far jump to flush CPU queue after transition to real mode
3810 JMP_AP(0xf000, real_mode)
3812 real_mode:
3813 ;; restore IDT to normal real-mode defaults
3814 SEG CS
3815 lidt [rmode_IDT_info]
3817 // restore SS:SP from the BDA
3818 xor ax, ax
3819 mov ds, ax
3820 mov ss, 0x0469
3821 mov sp, 0x0467
3822 pop eax
3823 ASM_END
3825 set_enable_a20(prev_a20_enable);
3827 // turn back on interrupts
3828 ASM_START
3830 ASM_END
3832 regs.u.r8.ah = 0;
3833 CLEAR_CF();
3834 break;
3837 case 0x88:
3838 // Get the amount of extended memory (above 1M)
3839 #if BX_CPU < 2
3840 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3841 SET_CF();
3842 #else
3843 regs.u.r8.al = inb_cmos(0x30);
3844 regs.u.r8.ah = inb_cmos(0x31);
3846 // According to Ralf Brown's interrupt the limit should be 15M,
3847 // but real machines mostly return max. 63M.
3848 if(regs.u.r16.ax > 0xffc0)
3849 regs.u.r16.ax = 0xffc0;
3851 CLEAR_CF();
3852 #endif
3853 break;
3855 case 0x90:
3856 /* Device busy interrupt. Called by Int 16h when no key available */
3857 break;
3859 case 0x91:
3860 /* Interrupt complete. Called by Int 16h when key becomes available */
3861 break;
3863 case 0xbf:
3864 BX_INFO("*** int 15h function AH=bf not yet supported!\n");
3865 SET_CF();
3866 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3867 break;
3869 case 0xC0:
3870 #if 0
3871 SET_CF();
3872 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3873 break;
3874 #endif
3875 CLEAR_CF();
3876 regs.u.r8.ah = 0;
3877 regs.u.r16.bx = BIOS_CONFIG_TABLE;
3878 ES = 0xF000;
3879 break;
3881 case 0xc1:
3882 ES = ebda_seg;
3883 CLEAR_CF();
3884 break;
3886 case 0xd8:
3887 bios_printf(BIOS_PRINTF_DEBUG, "EISA BIOS not present\n");
3888 SET_CF();
3889 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3890 break;
3892 default:
3893 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
3894 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
3895 SET_CF();
3896 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3897 break;
3901 #if BX_USE_PS2_MOUSE
3902 void
3903 int15_function_mouse(regs, ES, DS, FLAGS)
3904 pusha_regs_t regs; // REGS pushed via pusha
3905 Bit16u ES, DS, FLAGS;
3907 Bit16u ebda_seg=read_word(0x0040,0x000E);
3908 Bit8u mouse_flags_1, mouse_flags_2;
3909 Bit16u mouse_driver_seg;
3910 Bit16u mouse_driver_offset;
3911 Bit8u comm_byte, prev_command_byte;
3912 Bit8u ret, mouse_data1, mouse_data2, mouse_data3;
3914 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3916 switch (regs.u.r8.ah) {
3917 case 0xC2:
3918 // Return Codes status in AH
3919 // =========================
3920 // 00: success
3921 // 01: invalid subfunction (AL > 7)
3922 // 02: invalid input value (out of allowable range)
3923 // 03: interface error
3924 // 04: resend command received from mouse controller,
3925 // device driver should attempt command again
3926 // 05: cannot enable mouse, since no far call has been installed
3927 // 80/86: mouse service not implemented
3929 switch (regs.u.r8.al) {
3930 case 0: // Disable/Enable Mouse
3931 BX_DEBUG_INT15("case 0:\n");
3932 switch (regs.u.r8.bh) {
3933 case 0: // Disable Mouse
3934 BX_DEBUG_INT15("case 0: disable mouse\n");
3935 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3936 ret = send_to_mouse_ctrl(0xF5); // disable mouse command
3937 if (ret == 0) {
3938 ret = get_mouse_data(&mouse_data1);
3939 if ( (ret == 0) || (mouse_data1 == 0xFA) ) {
3940 CLEAR_CF();
3941 regs.u.r8.ah = 0;
3942 return;
3946 // error
3947 SET_CF();
3948 regs.u.r8.ah = ret;
3949 return;
3950 break;
3952 case 1: // Enable Mouse
3953 BX_DEBUG_INT15("case 1: enable mouse\n");
3954 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
3955 if ( (mouse_flags_2 & 0x80) == 0 ) {
3956 BX_DEBUG_INT15("INT 15h C2 Enable Mouse, no far call handler\n");
3957 SET_CF(); // error
3958 regs.u.r8.ah = 5; // no far call installed
3959 return;
3961 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3962 ret = send_to_mouse_ctrl(0xF4); // enable mouse command
3963 if (ret == 0) {
3964 ret = get_mouse_data(&mouse_data1);
3965 if ( (ret == 0) && (mouse_data1 == 0xFA) ) {
3966 enable_mouse_int_and_events(); // turn IRQ12 and packet generation on
3967 CLEAR_CF();
3968 regs.u.r8.ah = 0;
3969 return;
3972 SET_CF();
3973 regs.u.r8.ah = ret;
3974 return;
3976 default: // invalid subfunction
3977 BX_DEBUG_INT15("INT 15h C2 AL=0, BH=%02x\n", (unsigned) regs.u.r8.bh);
3978 SET_CF(); // error
3979 regs.u.r8.ah = 1; // invalid subfunction
3980 return;
3982 break;
3984 case 1: // Reset Mouse
3985 case 5: // Initialize Mouse
3986 BX_DEBUG_INT15("case 1 or 5:\n");
3987 if (regs.u.r8.al == 5) {
3988 if (regs.u.r8.bh != 3) {
3989 SET_CF();
3990 regs.u.r8.ah = 0x02; // invalid input
3991 return;
3993 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
3994 mouse_flags_2 = (mouse_flags_2 & 0x00) | regs.u.r8.bh;
3995 mouse_flags_1 = 0x00;
3996 write_byte(ebda_seg, 0x0026, mouse_flags_1);
3997 write_byte(ebda_seg, 0x0027, mouse_flags_2);
4000 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4001 ret = send_to_mouse_ctrl(0xFF); // reset mouse command
4002 if (ret == 0) {
4003 ret = get_mouse_data(&mouse_data3);
4004 // if no mouse attached, it will return RESEND
4005 if (mouse_data3 == 0xfe) {
4006 SET_CF();
4007 return;
4009 if (mouse_data3 != 0xfa)
4010 BX_PANIC("Mouse reset returned %02x (should be ack)\n", (unsigned)mouse_data3);
4011 if ( ret == 0 ) {
4012 ret = get_mouse_data(&mouse_data1);
4013 if ( ret == 0 ) {
4014 ret = get_mouse_data(&mouse_data2);
4015 if ( ret == 0 ) {
4016 // turn IRQ12 and packet generation on
4017 enable_mouse_int_and_events();
4018 CLEAR_CF();
4019 regs.u.r8.ah = 0;
4020 regs.u.r8.bl = mouse_data1;
4021 regs.u.r8.bh = mouse_data2;
4022 return;
4028 // error
4029 SET_CF();
4030 regs.u.r8.ah = ret;
4031 return;
4033 case 2: // Set Sample Rate
4034 BX_DEBUG_INT15("case 2:\n");
4035 switch (regs.u.r8.bh) {
4036 case 0: mouse_data1 = 10; break; // 10 reports/sec
4037 case 1: mouse_data1 = 20; break; // 20 reports/sec
4038 case 2: mouse_data1 = 40; break; // 40 reports/sec
4039 case 3: mouse_data1 = 60; break; // 60 reports/sec
4040 case 4: mouse_data1 = 80; break; // 80 reports/sec
4041 case 5: mouse_data1 = 100; break; // 100 reports/sec (default)
4042 case 6: mouse_data1 = 200; break; // 200 reports/sec
4043 default: mouse_data1 = 0;
4045 if (mouse_data1 > 0) {
4046 ret = send_to_mouse_ctrl(0xF3); // set sample rate command
4047 if (ret == 0) {
4048 ret = get_mouse_data(&mouse_data2);
4049 ret = send_to_mouse_ctrl(mouse_data1);
4050 ret = get_mouse_data(&mouse_data2);
4051 CLEAR_CF();
4052 regs.u.r8.ah = 0;
4053 } else {
4054 // error
4055 SET_CF();
4056 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4058 } else {
4059 // error
4060 SET_CF();
4061 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4063 break;
4065 case 3: // Set Resolution
4066 BX_DEBUG_INT15("case 3:\n");
4067 // BH:
4068 // 0 = 25 dpi, 1 count per millimeter
4069 // 1 = 50 dpi, 2 counts per millimeter
4070 // 2 = 100 dpi, 4 counts per millimeter
4071 // 3 = 200 dpi, 8 counts per millimeter
4072 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4073 if (regs.u.r8.bh < 4) {
4074 ret = send_to_mouse_ctrl(0xE8); // set resolution command
4075 if (ret == 0) {
4076 ret = get_mouse_data(&mouse_data1);
4077 if (mouse_data1 != 0xfa)
4078 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
4079 ret = send_to_mouse_ctrl(regs.u.r8.bh);
4080 ret = get_mouse_data(&mouse_data1);
4081 if (mouse_data1 != 0xfa)
4082 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
4083 CLEAR_CF();
4084 regs.u.r8.ah = 0;
4085 } else {
4086 // error
4087 SET_CF();
4088 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4090 } else {
4091 // error
4092 SET_CF();
4093 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4095 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4096 break;
4098 case 4: // Get Device ID
4099 BX_DEBUG_INT15("case 4:\n");
4100 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4101 ret = send_to_mouse_ctrl(0xF2); // get mouse ID command
4102 if (ret == 0) {
4103 ret = get_mouse_data(&mouse_data1);
4104 ret = get_mouse_data(&mouse_data2);
4105 CLEAR_CF();
4106 regs.u.r8.ah = 0;
4107 regs.u.r8.bh = mouse_data2;
4108 } else {
4109 // error
4110 SET_CF();
4111 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4113 break;
4115 case 6: // Return Status & Set Scaling Factor...
4116 BX_DEBUG_INT15("case 6:\n");
4117 switch (regs.u.r8.bh) {
4118 case 0: // Return Status
4119 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4120 ret = send_to_mouse_ctrl(0xE9); // get mouse info command
4121 if (ret == 0) {
4122 ret = get_mouse_data(&mouse_data1);
4123 if (mouse_data1 != 0xfa)
4124 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
4125 if (ret == 0) {
4126 ret = get_mouse_data(&mouse_data1);
4127 if ( ret == 0 ) {
4128 ret = get_mouse_data(&mouse_data2);
4129 if ( ret == 0 ) {
4130 ret = get_mouse_data(&mouse_data3);
4131 if ( ret == 0 ) {
4132 CLEAR_CF();
4133 regs.u.r8.ah = 0;
4134 regs.u.r8.bl = mouse_data1;
4135 regs.u.r8.cl = mouse_data2;
4136 regs.u.r8.dl = mouse_data3;
4137 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4138 return;
4145 // error
4146 SET_CF();
4147 regs.u.r8.ah = ret;
4148 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4149 return;
4151 case 1: // Set Scaling Factor to 1:1
4152 case 2: // Set Scaling Factor to 2:1
4153 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4154 if (regs.u.r8.bh == 1) {
4155 ret = send_to_mouse_ctrl(0xE6);
4156 } else {
4157 ret = send_to_mouse_ctrl(0xE7);
4159 if (ret == 0) {
4160 get_mouse_data(&mouse_data1);
4161 ret = (mouse_data1 != 0xFA);
4163 if (ret == 0) {
4164 CLEAR_CF();
4165 regs.u.r8.ah = 0;
4166 } else {
4167 // error
4168 SET_CF();
4169 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4171 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4172 break;
4174 default:
4175 BX_PANIC("INT 15h C2 AL=6, BH=%02x\n", (unsigned) regs.u.r8.bh);
4177 break;
4179 case 7: // Set Mouse Handler Address
4180 BX_DEBUG_INT15("case 7:\n");
4181 mouse_driver_seg = ES;
4182 mouse_driver_offset = regs.u.r16.bx;
4183 write_word(ebda_seg, 0x0022, mouse_driver_offset);
4184 write_word(ebda_seg, 0x0024, mouse_driver_seg);
4185 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4186 if (mouse_driver_offset == 0 && mouse_driver_seg == 0) {
4187 /* remove handler */
4188 if ( (mouse_flags_2 & 0x80) != 0 ) {
4189 mouse_flags_2 &= ~0x80;
4190 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4193 else {
4194 /* install handler */
4195 mouse_flags_2 |= 0x80;
4197 write_byte(ebda_seg, 0x0027, mouse_flags_2);
4198 CLEAR_CF();
4199 regs.u.r8.ah = 0;
4200 break;
4202 default:
4203 BX_DEBUG_INT15("case default:\n");
4204 regs.u.r8.ah = 1; // invalid function
4205 SET_CF();
4207 break;
4209 default:
4210 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4211 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4212 SET_CF();
4213 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4214 break;
4217 #endif
4220 void set_e820_range(ES, DI, start, end, extra_start, extra_end, type)
4221 Bit16u ES;
4222 Bit16u DI;
4223 Bit32u start;
4224 Bit32u end;
4225 Bit8u extra_start;
4226 Bit8u extra_end;
4227 Bit16u type;
4229 write_word(ES, DI, start);
4230 write_word(ES, DI+2, start >> 16);
4231 write_word(ES, DI+4, extra_start);
4232 write_word(ES, DI+6, 0x00);
4234 end -= start;
4235 extra_end -= extra_start;
4236 write_word(ES, DI+8, end);
4237 write_word(ES, DI+10, end >> 16);
4238 write_word(ES, DI+12, extra_end);
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 Bit32u extra_lowbits_memory_size=0;
4252 Bit16u CX,DX;
4253 Bit8u extra_highbits_memory_size=0;
4255 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
4257 switch (regs.u.r8.ah) {
4258 case 0x86:
4259 // Wait for CX:DX microseconds. currently using the
4260 // refresh request port 0x61 bit4, toggling every 15usec
4262 CX = regs.u.r16.cx;
4263 DX = regs.u.r16.dx;
4265 ASM_START
4268 ;; Get the count in eax
4269 mov bx, sp
4270 SEG SS
4271 mov ax, _int15_function32.CX [bx]
4272 shl eax, #16
4273 SEG SS
4274 mov ax, _int15_function32.DX [bx]
4276 ;; convert to numbers of 15usec ticks
4277 mov ebx, #15
4278 xor edx, edx
4279 div eax, ebx
4280 mov ecx, eax
4282 ;; wait for ecx number of refresh requests
4283 in al, #0x61
4284 and al,#0x10
4285 mov ah, al
4287 or ecx, ecx
4288 je int1586_tick_end
4289 int1586_tick:
4290 in al, #0x61
4291 and al,#0x10
4292 cmp al, ah
4293 je int1586_tick
4294 mov ah, al
4295 dec ecx
4296 jnz int1586_tick
4297 int1586_tick_end:
4298 ASM_END
4300 break;
4302 case 0xe8:
4303 switch(regs.u.r8.al)
4305 case 0x20: // coded by osmaker aka K.J.
4306 if(regs.u.r32.edx == 0x534D4150)
4308 extended_memory_size = inb_cmos(0x35);
4309 extended_memory_size <<= 8;
4310 extended_memory_size |= inb_cmos(0x34);
4311 extended_memory_size *= 64;
4312 // greater than EFF00000???
4313 if(extended_memory_size > 0x3bc000) {
4314 extended_memory_size = 0x3bc000; // everything after this is reserved memory until we get to 0x100000000
4316 extended_memory_size *= 1024;
4317 extended_memory_size += (16L * 1024 * 1024);
4319 if(extended_memory_size <= (16L * 1024 * 1024)) {
4320 extended_memory_size = inb_cmos(0x31);
4321 extended_memory_size <<= 8;
4322 extended_memory_size |= inb_cmos(0x30);
4323 extended_memory_size *= 1024;
4326 extra_lowbits_memory_size = inb_cmos(0x5c);
4327 extra_lowbits_memory_size <<= 8;
4328 extra_lowbits_memory_size |= inb_cmos(0x5b);
4329 extra_lowbits_memory_size *= 64;
4330 extra_lowbits_memory_size *= 1024;
4331 extra_highbits_memory_size = inb_cmos(0x5d);
4333 switch(regs.u.r16.bx)
4335 case 0:
4336 set_e820_range(ES, regs.u.r16.di,
4337 0x0000000L, 0x0009fc00L, 0, 0, 1);
4338 regs.u.r32.ebx = 1;
4339 regs.u.r32.eax = 0x534D4150;
4340 regs.u.r32.ecx = 0x14;
4341 CLEAR_CF();
4342 return;
4343 break;
4344 case 1:
4345 set_e820_range(ES, regs.u.r16.di,
4346 0x0009fc00L, 0x000a0000L, 0, 0, 2);
4347 regs.u.r32.ebx = 2;
4348 regs.u.r32.eax = 0x534D4150;
4349 regs.u.r32.ecx = 0x14;
4350 CLEAR_CF();
4351 return;
4352 break;
4353 case 2:
4354 set_e820_range(ES, regs.u.r16.di,
4355 0x000e8000L, 0x00100000L, 0, 0, 2);
4356 regs.u.r32.ebx = 3;
4357 regs.u.r32.eax = 0x534D4150;
4358 regs.u.r32.ecx = 0x14;
4359 CLEAR_CF();
4360 return;
4361 break;
4362 case 3:
4363 set_e820_range(ES, regs.u.r16.di,
4364 0x00100000L,
4365 extended_memory_size - ACPI_DATA_SIZE ,0, 0, 1);
4366 regs.u.r32.ebx = 4;
4367 regs.u.r32.eax = 0x534D4150;
4368 regs.u.r32.ecx = 0x14;
4369 CLEAR_CF();
4370 return;
4371 break;
4372 case 4:
4373 set_e820_range(ES, regs.u.r16.di,
4374 extended_memory_size - ACPI_DATA_SIZE,
4375 extended_memory_size ,0, 0, 3); // ACPI RAM
4376 regs.u.r32.ebx = 5;
4377 regs.u.r32.eax = 0x534D4150;
4378 regs.u.r32.ecx = 0x14;
4379 CLEAR_CF();
4380 return;
4381 break;
4382 case 5:
4383 /* 3 pages before the bios, we map the vmx tss pages */
4384 set_e820_range(ES, regs.u.r16.di, 0xfffbd000L,
4385 0xfffc0000L, 0, 0, 2);
4386 regs.u.r32.ebx = 6;
4387 regs.u.r32.eax = 0x534D4150;
4388 regs.u.r32.ecx = 0x14;
4389 CLEAR_CF();
4390 return;
4391 case 6:
4392 /* 256KB BIOS area at the end of 4 GB */
4393 set_e820_range(ES, regs.u.r16.di,
4394 0xfffc0000L, 0x00000000L ,0, 0, 2);
4395 if (extra_highbits_memory_size || extra_lowbits_memory_size)
4396 regs.u.r32.ebx = 7;
4397 else
4398 regs.u.r32.ebx = 0;
4399 regs.u.r32.eax = 0x534D4150;
4400 regs.u.r32.ecx = 0x14;
4401 CLEAR_CF();
4402 return;
4403 case 7:
4404 /* Maping of memory above 4 GB */
4405 set_e820_range(ES, regs.u.r16.di, 0x00000000L,
4406 extra_lowbits_memory_size, 1, extra_highbits_memory_size
4407 + 1, 1);
4408 regs.u.r32.ebx = 0;
4409 regs.u.r32.eax = 0x534D4150;
4410 regs.u.r32.ecx = 0x14;
4411 CLEAR_CF();
4412 return;
4413 default: /* AX=E820, DX=534D4150, BX unrecognized */
4414 goto int15_unimplemented;
4415 break;
4417 } else {
4418 // if DX != 0x534D4150)
4419 goto int15_unimplemented;
4421 break;
4423 case 0x01:
4424 // do we have any reason to fail here ?
4425 CLEAR_CF();
4427 // my real system sets ax and bx to 0
4428 // this is confirmed by Ralph Brown list
4429 // but syslinux v1.48 is known to behave
4430 // strangely if ax is set to 0
4431 // regs.u.r16.ax = 0;
4432 // regs.u.r16.bx = 0;
4434 // Get the amount of extended memory (above 1M)
4435 regs.u.r8.cl = inb_cmos(0x30);
4436 regs.u.r8.ch = inb_cmos(0x31);
4438 // limit to 15M
4439 if(regs.u.r16.cx > 0x3c00)
4441 regs.u.r16.cx = 0x3c00;
4444 // Get the amount of extended memory above 16M in 64k blocs
4445 regs.u.r8.dl = inb_cmos(0x34);
4446 regs.u.r8.dh = inb_cmos(0x35);
4448 // Set configured memory equal to extended memory
4449 regs.u.r16.ax = regs.u.r16.cx;
4450 regs.u.r16.bx = regs.u.r16.dx;
4451 break;
4452 default: /* AH=0xE8?? but not implemented */
4453 goto int15_unimplemented;
4455 break;
4456 int15_unimplemented:
4457 // fall into the default
4458 default:
4459 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4460 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4461 SET_CF();
4462 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4463 break;
4467 void
4468 int16_function(DI, SI, BP, SP, BX, DX, CX, AX, FLAGS)
4469 Bit16u DI, SI, BP, SP, BX, DX, CX, AX, FLAGS;
4471 Bit8u scan_code, ascii_code, shift_flags, led_flags, count;
4472 Bit16u kbd_code, max;
4474 BX_DEBUG_INT16("int16: AX=%04x BX=%04x CX=%04x DX=%04x \n", AX, BX, CX, DX);
4476 shift_flags = read_byte(0x0040, 0x17);
4477 led_flags = read_byte(0x0040, 0x97);
4478 if ((((shift_flags >> 4) & 0x07) ^ (led_flags & 0x07)) != 0) {
4479 ASM_START
4481 ASM_END
4482 outb(0x60, 0xed);
4483 while ((inb(0x64) & 0x01) == 0) outb(0x80, 0x21);
4484 if ((inb(0x60) == 0xfa)) {
4485 led_flags &= 0xf8;
4486 led_flags |= ((shift_flags >> 4) & 0x07);
4487 outb(0x60, led_flags & 0x07);
4488 while ((inb(0x64) & 0x01) == 0) outb(0x80, 0x21);
4489 inb(0x60);
4490 write_byte(0x0040, 0x97, led_flags);
4492 ASM_START
4494 ASM_END
4497 switch (GET_AH()) {
4498 case 0x00: /* read keyboard input */
4500 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4501 BX_PANIC("KBD: int16h: out of keyboard input\n");
4503 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4504 else if (ascii_code == 0xE0) ascii_code = 0;
4505 AX = (scan_code << 8) | ascii_code;
4506 break;
4508 case 0x01: /* check keyboard status */
4509 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4510 SET_ZF();
4511 return;
4513 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4514 else if (ascii_code == 0xE0) ascii_code = 0;
4515 AX = (scan_code << 8) | ascii_code;
4516 CLEAR_ZF();
4517 break;
4519 case 0x02: /* get shift flag status */
4520 shift_flags = read_byte(0x0040, 0x17);
4521 SET_AL(shift_flags);
4522 break;
4524 case 0x05: /* store key-stroke into buffer */
4525 if ( !enqueue_key(GET_CH(), GET_CL()) ) {
4526 SET_AL(1);
4528 else {
4529 SET_AL(0);
4531 break;
4533 case 0x09: /* GET KEYBOARD FUNCTIONALITY */
4534 // bit Bochs Description
4535 // 7 0 reserved
4536 // 6 0 INT 16/AH=20h-22h supported (122-key keyboard support)
4537 // 5 1 INT 16/AH=10h-12h supported (enhanced keyboard support)
4538 // 4 1 INT 16/AH=0Ah supported
4539 // 3 0 INT 16/AX=0306h supported
4540 // 2 0 INT 16/AX=0305h supported
4541 // 1 0 INT 16/AX=0304h supported
4542 // 0 0 INT 16/AX=0300h supported
4544 SET_AL(0x30);
4545 break;
4547 case 0x0A: /* GET KEYBOARD ID */
4548 count = 2;
4549 kbd_code = 0x0;
4550 outb(0x60, 0xf2);
4551 /* Wait for data */
4552 max=0xffff;
4553 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4554 if (max>0x0) {
4555 if ((inb(0x60) == 0xfa)) {
4556 do {
4557 max=0xffff;
4558 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4559 if (max>0x0) {
4560 kbd_code >>= 8;
4561 kbd_code |= (inb(0x60) << 8);
4563 } while (--count>0);
4566 BX=kbd_code;
4567 break;
4569 case 0x10: /* read MF-II keyboard input */
4571 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4572 BX_PANIC("KBD: int16h: out of keyboard input\n");
4574 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4575 AX = (scan_code << 8) | ascii_code;
4576 break;
4578 case 0x11: /* check MF-II keyboard status */
4579 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4580 SET_ZF();
4581 return;
4583 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4584 AX = (scan_code << 8) | ascii_code;
4585 CLEAR_ZF();
4586 break;
4588 case 0x12: /* get extended keyboard status */
4589 shift_flags = read_byte(0x0040, 0x17);
4590 SET_AL(shift_flags);
4591 shift_flags = read_byte(0x0040, 0x18) & 0x73;
4592 shift_flags |= read_byte(0x0040, 0x96) & 0x0c;
4593 SET_AH(shift_flags);
4594 BX_DEBUG_INT16("int16: func 12 sending %04x\n",AX);
4595 break;
4597 case 0x92: /* keyboard capability check called by DOS 5.0+ keyb */
4598 SET_AH(0x80); // function int16 ah=0x10-0x12 supported
4599 break;
4601 case 0xA2: /* 122 keys capability check called by DOS 5.0+ keyb */
4602 // don't change AH : function int16 ah=0x20-0x22 NOT supported
4603 break;
4605 case 0x6F:
4606 if (GET_AL() == 0x08)
4607 SET_AH(0x02); // unsupported, aka normal keyboard
4609 default:
4610 BX_INFO("KBD: unsupported int 16h function %02x\n", GET_AH());
4614 unsigned int
4615 dequeue_key(scan_code, ascii_code, incr)
4616 Bit8u *scan_code;
4617 Bit8u *ascii_code;
4618 unsigned int incr;
4620 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail;
4621 Bit16u ss;
4622 Bit8u acode, scode;
4624 #if BX_CPU < 2
4625 buffer_start = 0x001E;
4626 buffer_end = 0x003E;
4627 #else
4628 buffer_start = read_word(0x0040, 0x0080);
4629 buffer_end = read_word(0x0040, 0x0082);
4630 #endif
4632 buffer_head = read_word(0x0040, 0x001a);
4633 buffer_tail = read_word(0x0040, 0x001c);
4635 if (buffer_head != buffer_tail) {
4636 ss = get_SS();
4637 acode = read_byte(0x0040, buffer_head);
4638 scode = read_byte(0x0040, buffer_head+1);
4639 write_byte(ss, ascii_code, acode);
4640 write_byte(ss, scan_code, scode);
4642 if (incr) {
4643 buffer_head += 2;
4644 if (buffer_head >= buffer_end)
4645 buffer_head = buffer_start;
4646 write_word(0x0040, 0x001a, buffer_head);
4648 return(1);
4650 else {
4651 return(0);
4655 static char panic_msg_keyb_buffer_full[] = "%s: keyboard input buffer full\n";
4657 Bit8u
4658 inhibit_mouse_int_and_events()
4660 Bit8u command_byte, prev_command_byte;
4662 // Turn off IRQ generation and aux data line
4663 if ( inb(0x64) & 0x02 )
4664 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4665 outb(0x64, 0x20); // get command byte
4666 while ( (inb(0x64) & 0x01) != 0x01 );
4667 prev_command_byte = inb(0x60);
4668 command_byte = prev_command_byte;
4669 //while ( (inb(0x64) & 0x02) );
4670 if ( inb(0x64) & 0x02 )
4671 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4672 command_byte &= 0xfd; // turn off IRQ 12 generation
4673 command_byte |= 0x20; // disable mouse serial clock line
4674 outb(0x64, 0x60); // write command byte
4675 outb(0x60, command_byte);
4676 return(prev_command_byte);
4679 void
4680 enable_mouse_int_and_events()
4682 Bit8u command_byte;
4684 // Turn on IRQ generation and aux data line
4685 if ( inb(0x64) & 0x02 )
4686 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4687 outb(0x64, 0x20); // get command byte
4688 while ( (inb(0x64) & 0x01) != 0x01 );
4689 command_byte = inb(0x60);
4690 //while ( (inb(0x64) & 0x02) );
4691 if ( inb(0x64) & 0x02 )
4692 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4693 command_byte |= 0x02; // turn on IRQ 12 generation
4694 command_byte &= 0xdf; // enable mouse serial clock line
4695 outb(0x64, 0x60); // write command byte
4696 outb(0x60, command_byte);
4699 Bit8u
4700 send_to_mouse_ctrl(sendbyte)
4701 Bit8u sendbyte;
4703 Bit8u response;
4705 // wait for chance to write to ctrl
4706 if ( inb(0x64) & 0x02 )
4707 BX_PANIC(panic_msg_keyb_buffer_full,"sendmouse");
4708 outb(0x64, 0xD4);
4709 outb(0x60, sendbyte);
4710 return(0);
4714 Bit8u
4715 get_mouse_data(data)
4716 Bit8u *data;
4718 Bit8u response;
4719 Bit16u ss;
4721 while ( (inb(0x64) & 0x21) != 0x21 ) {
4724 response = inb(0x60);
4726 ss = get_SS();
4727 write_byte(ss, data, response);
4728 return(0);
4731 void
4732 set_kbd_command_byte(command_byte)
4733 Bit8u command_byte;
4735 if ( inb(0x64) & 0x02 )
4736 BX_PANIC(panic_msg_keyb_buffer_full,"setkbdcomm");
4737 outb(0x64, 0xD4);
4739 outb(0x64, 0x60); // write command byte
4740 outb(0x60, command_byte);
4743 void
4744 int09_function(DI, SI, BP, SP, BX, DX, CX, AX)
4745 Bit16u DI, SI, BP, SP, BX, DX, CX, AX;
4747 Bit8u scancode, asciicode, shift_flags;
4748 Bit8u mf2_flags, mf2_state;
4751 // DS has been set to F000 before call
4755 scancode = GET_AL();
4757 if (scancode == 0) {
4758 BX_INFO("KBD: int09 handler: AL=0\n");
4759 return;
4763 shift_flags = read_byte(0x0040, 0x17);
4764 mf2_flags = read_byte(0x0040, 0x18);
4765 mf2_state = read_byte(0x0040, 0x96);
4766 asciicode = 0;
4768 switch (scancode) {
4769 case 0x3a: /* Caps Lock press */
4770 shift_flags ^= 0x40;
4771 write_byte(0x0040, 0x17, shift_flags);
4772 mf2_flags |= 0x40;
4773 write_byte(0x0040, 0x18, mf2_flags);
4774 break;
4775 case 0xba: /* Caps Lock release */
4776 mf2_flags &= ~0x40;
4777 write_byte(0x0040, 0x18, mf2_flags);
4778 break;
4780 case 0x2a: /* L Shift press */
4781 shift_flags |= 0x02;
4782 write_byte(0x0040, 0x17, shift_flags);
4783 break;
4784 case 0xaa: /* L Shift release */
4785 shift_flags &= ~0x02;
4786 write_byte(0x0040, 0x17, shift_flags);
4787 break;
4789 case 0x36: /* R Shift press */
4790 shift_flags |= 0x01;
4791 write_byte(0x0040, 0x17, shift_flags);
4792 break;
4793 case 0xb6: /* R Shift release */
4794 shift_flags &= ~0x01;
4795 write_byte(0x0040, 0x17, shift_flags);
4796 break;
4798 case 0x1d: /* Ctrl press */
4799 if ((mf2_state & 0x01) == 0) {
4800 shift_flags |= 0x04;
4801 write_byte(0x0040, 0x17, shift_flags);
4802 if (mf2_state & 0x02) {
4803 mf2_state |= 0x04;
4804 write_byte(0x0040, 0x96, mf2_state);
4805 } else {
4806 mf2_flags |= 0x01;
4807 write_byte(0x0040, 0x18, mf2_flags);
4810 break;
4811 case 0x9d: /* Ctrl release */
4812 if ((mf2_state & 0x01) == 0) {
4813 shift_flags &= ~0x04;
4814 write_byte(0x0040, 0x17, shift_flags);
4815 if (mf2_state & 0x02) {
4816 mf2_state &= ~0x04;
4817 write_byte(0x0040, 0x96, mf2_state);
4818 } else {
4819 mf2_flags &= ~0x01;
4820 write_byte(0x0040, 0x18, mf2_flags);
4823 break;
4825 case 0x38: /* Alt press */
4826 shift_flags |= 0x08;
4827 write_byte(0x0040, 0x17, shift_flags);
4828 if (mf2_state & 0x02) {
4829 mf2_state |= 0x08;
4830 write_byte(0x0040, 0x96, mf2_state);
4831 } else {
4832 mf2_flags |= 0x02;
4833 write_byte(0x0040, 0x18, mf2_flags);
4835 break;
4836 case 0xb8: /* Alt release */
4837 shift_flags &= ~0x08;
4838 write_byte(0x0040, 0x17, shift_flags);
4839 if (mf2_state & 0x02) {
4840 mf2_state &= ~0x08;
4841 write_byte(0x0040, 0x96, mf2_state);
4842 } else {
4843 mf2_flags &= ~0x02;
4844 write_byte(0x0040, 0x18, mf2_flags);
4846 break;
4848 case 0x45: /* Num Lock press */
4849 if ((mf2_state & 0x03) == 0) {
4850 mf2_flags |= 0x20;
4851 write_byte(0x0040, 0x18, mf2_flags);
4852 shift_flags ^= 0x20;
4853 write_byte(0x0040, 0x17, shift_flags);
4855 break;
4856 case 0xc5: /* Num Lock release */
4857 if ((mf2_state & 0x03) == 0) {
4858 mf2_flags &= ~0x20;
4859 write_byte(0x0040, 0x18, mf2_flags);
4861 break;
4863 case 0x46: /* Scroll Lock press */
4864 mf2_flags |= 0x10;
4865 write_byte(0x0040, 0x18, mf2_flags);
4866 shift_flags ^= 0x10;
4867 write_byte(0x0040, 0x17, shift_flags);
4868 break;
4870 case 0xc6: /* Scroll Lock release */
4871 mf2_flags &= ~0x10;
4872 write_byte(0x0040, 0x18, mf2_flags);
4873 break;
4875 default:
4876 if (scancode & 0x80) {
4877 break; /* toss key releases ... */
4879 if (scancode > MAX_SCAN_CODE) {
4880 BX_INFO("KBD: int09h_handler(): unknown scancode read: 0x%02x!\n", scancode);
4881 return;
4883 if (shift_flags & 0x08) { /* ALT */
4884 asciicode = scan_to_scanascii[scancode].alt;
4885 scancode = scan_to_scanascii[scancode].alt >> 8;
4886 } else if (shift_flags & 0x04) { /* CONTROL */
4887 asciicode = scan_to_scanascii[scancode].control;
4888 scancode = scan_to_scanascii[scancode].control >> 8;
4889 } else if (((mf2_state & 0x02) > 0) && ((scancode >= 0x47) && (scancode <= 0x53))) {
4890 /* extended keys handling */
4891 asciicode = 0xe0;
4892 scancode = scan_to_scanascii[scancode].normal >> 8;
4893 } else if (shift_flags & 0x03) { /* LSHIFT + RSHIFT */
4894 /* check if lock state should be ignored
4895 * because a SHIFT key are pressed */
4897 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
4898 asciicode = scan_to_scanascii[scancode].normal;
4899 scancode = scan_to_scanascii[scancode].normal >> 8;
4900 } else {
4901 asciicode = scan_to_scanascii[scancode].shift;
4902 scancode = scan_to_scanascii[scancode].shift >> 8;
4904 } else {
4905 /* check if lock is on */
4906 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
4907 asciicode = scan_to_scanascii[scancode].shift;
4908 scancode = scan_to_scanascii[scancode].shift >> 8;
4909 } else {
4910 asciicode = scan_to_scanascii[scancode].normal;
4911 scancode = scan_to_scanascii[scancode].normal >> 8;
4914 if (scancode==0 && asciicode==0) {
4915 BX_INFO("KBD: int09h_handler(): scancode & asciicode are zero?\n");
4917 enqueue_key(scancode, asciicode);
4918 break;
4920 if ((scancode & 0x7f) != 0x1d) {
4921 mf2_state &= ~0x01;
4923 mf2_state &= ~0x02;
4924 write_byte(0x0040, 0x96, mf2_state);
4927 unsigned int
4928 enqueue_key(scan_code, ascii_code)
4929 Bit8u scan_code, ascii_code;
4931 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail, temp_tail;
4933 #if BX_CPU < 2
4934 buffer_start = 0x001E;
4935 buffer_end = 0x003E;
4936 #else
4937 buffer_start = read_word(0x0040, 0x0080);
4938 buffer_end = read_word(0x0040, 0x0082);
4939 #endif
4941 buffer_head = read_word(0x0040, 0x001A);
4942 buffer_tail = read_word(0x0040, 0x001C);
4944 temp_tail = buffer_tail;
4945 buffer_tail += 2;
4946 if (buffer_tail >= buffer_end)
4947 buffer_tail = buffer_start;
4949 if (buffer_tail == buffer_head) {
4950 return(0);
4953 write_byte(0x0040, temp_tail, ascii_code);
4954 write_byte(0x0040, temp_tail+1, scan_code);
4955 write_word(0x0040, 0x001C, buffer_tail);
4956 return(1);
4960 void
4961 int74_function(make_farcall, Z, Y, X, status)
4962 Bit16u make_farcall, Z, Y, X, status;
4964 Bit16u ebda_seg=read_word(0x0040,0x000E);
4965 Bit8u in_byte, index, package_count;
4966 Bit8u mouse_flags_1, mouse_flags_2;
4968 BX_DEBUG_INT74("entering int74_function\n");
4969 make_farcall = 0;
4971 in_byte = inb(0x64);
4972 if ( (in_byte & 0x21) != 0x21 ) {
4973 return;
4975 in_byte = inb(0x60);
4976 BX_DEBUG_INT74("int74: read byte %02x\n", in_byte);
4978 mouse_flags_1 = read_byte(ebda_seg, 0x0026);
4979 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4981 if ( (mouse_flags_2 & 0x80) != 0x80 ) {
4982 return;
4985 package_count = mouse_flags_2 & 0x07;
4986 index = mouse_flags_1 & 0x07;
4987 write_byte(ebda_seg, 0x28 + index, in_byte);
4989 if ( (index+1) >= package_count ) {
4990 BX_DEBUG_INT74("int74_function: make_farcall=1\n");
4991 status = read_byte(ebda_seg, 0x0028 + 0);
4992 X = read_byte(ebda_seg, 0x0028 + 1);
4993 Y = read_byte(ebda_seg, 0x0028 + 2);
4994 Z = 0;
4995 mouse_flags_1 = 0;
4996 // check if far call handler installed
4997 if (mouse_flags_2 & 0x80)
4998 make_farcall = 1;
5000 else {
5001 mouse_flags_1++;
5003 write_byte(ebda_seg, 0x0026, mouse_flags_1);
5006 #define SET_DISK_RET_STATUS(status) write_byte(0x0040, 0x0074, status)
5008 #if BX_USE_ATADRV
5010 void
5011 int13_harddisk(EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
5012 Bit16u EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
5014 Bit32u lba;
5015 Bit16u ebda_seg=read_word(0x0040,0x000E);
5016 Bit16u cylinder, head, sector;
5017 Bit16u segment, offset;
5018 Bit16u npc, nph, npspt, nlc, nlh, nlspt;
5019 Bit16u size, count;
5020 Bit8u device, status;
5022 BX_DEBUG_INT13_HD("int13_harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5024 write_byte(0x0040, 0x008e, 0); // clear completion flag
5026 // basic check : device has to be defined
5027 if ( (GET_ELDL() < 0x80) || (GET_ELDL() >= 0x80 + BX_MAX_ATA_DEVICES) ) {
5028 BX_INFO("int13_harddisk: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
5029 goto int13_fail;
5032 // Get the ata channel
5033 device=read_byte(ebda_seg,&EbdaData->ata.hdidmap[GET_ELDL()-0x80]);
5035 // basic check : device has to be valid
5036 if (device >= BX_MAX_ATA_DEVICES) {
5037 BX_INFO("int13_harddisk: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
5038 goto int13_fail;
5041 switch (GET_AH()) {
5043 case 0x00: /* disk controller reset */
5044 ata_reset (device);
5045 goto int13_success;
5046 break;
5048 case 0x01: /* read disk status */
5049 status = read_byte(0x0040, 0x0074);
5050 SET_AH(status);
5051 SET_DISK_RET_STATUS(0);
5052 /* set CF if error status read */
5053 if (status) goto int13_fail_nostatus;
5054 else goto int13_success_noah;
5055 break;
5057 case 0x02: // read disk sectors
5058 case 0x03: // write disk sectors
5059 case 0x04: // verify disk sectors
5061 count = GET_AL();
5062 cylinder = GET_CH();
5063 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
5064 sector = (GET_CL() & 0x3f);
5065 head = GET_DH();
5067 segment = ES;
5068 offset = BX;
5070 if ((count > 128) || (count == 0) || (sector == 0)) {
5071 BX_INFO("int13_harddisk: function %02x, parameter out of range!\n",GET_AH());
5072 goto int13_fail;
5075 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
5076 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
5077 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
5079 // sanity check on cyl heads, sec
5080 if( (cylinder >= nlc) || (head >= nlh) || (sector > nlspt )) {
5081 BX_INFO("int13_harddisk: function %02x, parameters out of range %04x/%04x/%04x!\n", GET_AH(), cylinder, head, sector);
5082 goto int13_fail;
5085 // FIXME verify
5086 if ( GET_AH() == 0x04 ) goto int13_success;
5088 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
5089 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
5091 // if needed, translate lchs to lba, and execute command
5092 if ( (nph != nlh) || (npspt != nlspt)) {
5093 lba = ((((Bit32u)cylinder * (Bit32u)nlh) + (Bit32u)head) * (Bit32u)nlspt) + (Bit32u)sector - 1;
5094 sector = 0; // this forces the command to be lba
5097 if ( GET_AH() == 0x02 )
5098 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, cylinder, head, sector, lba, segment, offset);
5099 else
5100 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, cylinder, head, sector, lba, segment, offset);
5102 // Set nb of sector transferred
5103 SET_AL(read_word(ebda_seg, &EbdaData->ata.trsfsectors));
5105 if (status != 0) {
5106 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
5107 SET_AH(0x0c);
5108 goto int13_fail_noah;
5111 goto int13_success;
5112 break;
5114 case 0x05: /* format disk track */
5115 BX_INFO("format disk track called\n");
5116 goto int13_success;
5117 return;
5118 break;
5120 case 0x08: /* read disk drive parameters */
5122 // Get logical geometry from table
5123 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
5124 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
5125 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
5126 count = read_byte(ebda_seg, &EbdaData->ata.hdcount);
5128 nlc = nlc - 2; /* 0 based , last sector not used */
5129 SET_AL(0);
5130 SET_CH(nlc & 0xff);
5131 SET_CL(((nlc >> 2) & 0xc0) | (nlspt & 0x3f));
5132 SET_DH(nlh - 1);
5133 SET_DL(count); /* FIXME returns 0, 1, or n hard drives */
5135 // FIXME should set ES & DI
5137 goto int13_success;
5138 break;
5140 case 0x10: /* check drive ready */
5141 // should look at 40:8E also???
5143 // Read the status from controller
5144 status = inb(read_word(ebda_seg, &EbdaData->ata.channels[device/2].iobase1) + ATA_CB_STAT);
5145 if ( (status & ( ATA_CB_STAT_BSY | ATA_CB_STAT_RDY )) == ATA_CB_STAT_RDY ) {
5146 goto int13_success;
5148 else {
5149 SET_AH(0xAA);
5150 goto int13_fail_noah;
5152 break;
5154 case 0x15: /* read disk drive size */
5156 // Get logical geometry from table
5157 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
5158 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
5159 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
5161 // Compute sector count seen by int13
5162 lba = (Bit32u)(nlc - 1) * (Bit32u)nlh * (Bit32u)nlspt;
5163 CX = lba >> 16;
5164 DX = lba & 0xffff;
5166 SET_AH(3); // hard disk accessible
5167 goto int13_success_noah;
5168 break;
5170 case 0x41: // IBM/MS installation check
5171 BX=0xaa55; // install check
5172 SET_AH(0x30); // EDD 3.0
5173 CX=0x0007; // ext disk access and edd, removable supported
5174 goto int13_success_noah;
5175 break;
5177 case 0x42: // IBM/MS extended read
5178 case 0x43: // IBM/MS extended write
5179 case 0x44: // IBM/MS verify
5180 case 0x47: // IBM/MS extended seek
5182 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
5183 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
5184 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
5186 // Can't use 64 bits lba
5187 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
5188 if (lba != 0L) {
5189 BX_PANIC("int13_harddisk: function %02x. Can't use 64bits lba\n",GET_AH());
5190 goto int13_fail;
5193 // Get 32 bits lba and check
5194 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5195 if (lba >= read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors) ) {
5196 BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH());
5197 goto int13_fail;
5200 // If verify or seek
5201 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
5202 goto int13_success;
5204 // Execute the command
5205 if ( GET_AH() == 0x42 )
5206 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, 0, 0, 0, lba, segment, offset);
5207 else
5208 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, 0, 0, 0, lba, segment, offset);
5210 count=read_word(ebda_seg, &EbdaData->ata.trsfsectors);
5211 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5213 if (status != 0) {
5214 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
5215 SET_AH(0x0c);
5216 goto int13_fail_noah;
5219 goto int13_success;
5220 break;
5222 case 0x45: // IBM/MS lock/unlock drive
5223 case 0x49: // IBM/MS extended media change
5224 goto int13_success; // Always success for HD
5225 break;
5227 case 0x46: // IBM/MS eject media
5228 SET_AH(0xb2); // Volume Not Removable
5229 goto int13_fail_noah; // Always fail for HD
5230 break;
5232 case 0x48: // IBM/MS get drive parameters
5233 size=read_word(DS,SI+(Bit16u)&Int13DPT->size);
5235 // Buffer is too small
5236 if(size < 0x1a)
5237 goto int13_fail;
5239 // EDD 1.x
5240 if(size >= 0x1a) {
5241 Bit16u blksize;
5243 npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
5244 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
5245 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
5246 lba = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors);
5247 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5249 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5250 if ((lba/npspt)/nph > 0x3fff)
5252 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x00); // geometry is invalid
5253 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0x3fff);
5255 else
5257 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x02); // geometry is valid
5258 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, (Bit32u)npc);
5260 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, (Bit32u)nph);
5261 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, (Bit32u)npspt);
5262 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, lba); // FIXME should be Bit64
5263 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0L);
5264 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5267 // EDD 2.x
5268 if(size >= 0x1e) {
5269 Bit8u channel, dev, irq, mode, checksum, i, translation;
5270 Bit16u iobase1, iobase2, options;
5272 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5274 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5275 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5277 // Fill in dpte
5278 channel = device / 2;
5279 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5280 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5281 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5282 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5283 translation = read_byte(ebda_seg, &EbdaData->ata.devices[device].translation);
5285 options = (translation==ATA_TRANSLATION_NONE?0:1)<<3; // chs translation
5286 options |= (1<<4); // lba translation
5287 options |= (mode==ATA_MODE_PIO32?1:0)<<7;
5288 options |= (translation==ATA_TRANSLATION_LBA?1:0)<<9;
5289 options |= (translation==ATA_TRANSLATION_RECHS?3:0)<<9;
5291 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5292 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2 + ATA_CB_DC);
5293 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5294 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5295 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5296 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5297 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5298 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5299 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5300 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5301 if (size >=0x42)
5302 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5303 else
5304 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x10);
5306 checksum=0;
5307 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, ((Bit8u*)(&EbdaData->ata.dpte)) + i);
5308 checksum = ~checksum;
5309 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5312 // EDD 3.x
5313 if(size >= 0x42) {
5314 Bit8u channel, iface, checksum, i;
5315 Bit16u iobase1;
5317 channel = device / 2;
5318 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5319 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5321 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5322 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5323 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5324 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5325 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5327 if (iface==ATA_IFACE_ISA) {
5328 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5329 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5330 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5331 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5333 else {
5334 // FIXME PCI
5336 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5337 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5338 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5339 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5341 if (iface==ATA_IFACE_ISA) {
5342 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5343 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5344 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5346 else {
5347 // FIXME PCI
5349 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5350 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5351 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5352 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5354 checksum=0;
5355 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5356 checksum = ~checksum;
5357 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5360 goto int13_success;
5361 break;
5363 case 0x4e: // // IBM/MS set hardware configuration
5364 // DMA, prefetch, PIO maximum not supported
5365 switch (GET_AL()) {
5366 case 0x01:
5367 case 0x03:
5368 case 0x04:
5369 case 0x06:
5370 goto int13_success;
5371 break;
5372 default :
5373 goto int13_fail;
5375 break;
5377 case 0x09: /* initialize drive parameters */
5378 case 0x0c: /* seek to specified cylinder */
5379 case 0x0d: /* alternate disk reset */
5380 case 0x11: /* recalibrate */
5381 case 0x14: /* controller internal diagnostic */
5382 BX_INFO("int13_harddisk: function %02xh unimplemented, returns success\n", GET_AH());
5383 goto int13_success;
5384 break;
5386 case 0x0a: /* read disk sectors with ECC */
5387 case 0x0b: /* write disk sectors with ECC */
5388 case 0x18: // set media type for format
5389 case 0x50: // IBM/MS send packet command
5390 default:
5391 BX_INFO("int13_harddisk: function %02xh unsupported, returns fail\n", GET_AH());
5392 goto int13_fail;
5393 break;
5396 int13_fail:
5397 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5398 int13_fail_noah:
5399 SET_DISK_RET_STATUS(GET_AH());
5400 int13_fail_nostatus:
5401 SET_CF(); // error occurred
5402 return;
5404 int13_success:
5405 SET_AH(0x00); // no error
5406 int13_success_noah:
5407 SET_DISK_RET_STATUS(0x00);
5408 CLEAR_CF(); // no error
5409 return;
5412 // ---------------------------------------------------------------------------
5413 // Start of int13 for cdrom
5414 // ---------------------------------------------------------------------------
5416 void
5417 int13_cdrom(EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
5418 Bit16u EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
5420 Bit16u ebda_seg=read_word(0x0040,0x000E);
5421 Bit8u device, status, locks;
5422 Bit8u atacmd[12];
5423 Bit32u lba;
5424 Bit16u count, segment, offset, i, size;
5426 BX_DEBUG_INT13_CD("int13_cdrom: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5428 SET_DISK_RET_STATUS(0x00);
5430 /* basic check : device should be 0xE0+ */
5431 if( (GET_ELDL() < 0xE0) || (GET_ELDL() >= 0xE0+BX_MAX_ATA_DEVICES) ) {
5432 BX_INFO("int13_cdrom: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
5433 goto int13_fail;
5436 // Get the ata channel
5437 device=read_byte(ebda_seg,&EbdaData->ata.cdidmap[GET_ELDL()-0xE0]);
5439 /* basic check : device has to be valid */
5440 if (device >= BX_MAX_ATA_DEVICES) {
5441 BX_INFO("int13_cdrom: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
5442 goto int13_fail;
5445 switch (GET_AH()) {
5447 // all those functions return SUCCESS
5448 case 0x00: /* disk controller reset */
5449 case 0x09: /* initialize drive parameters */
5450 case 0x0c: /* seek to specified cylinder */
5451 case 0x0d: /* alternate disk reset */
5452 case 0x10: /* check drive ready */
5453 case 0x11: /* recalibrate */
5454 case 0x14: /* controller internal diagnostic */
5455 case 0x16: /* detect disk change */
5456 goto int13_success;
5457 break;
5459 // all those functions return disk write-protected
5460 case 0x03: /* write disk sectors */
5461 case 0x05: /* format disk track */
5462 case 0x43: // IBM/MS extended write
5463 SET_AH(0x03);
5464 goto int13_fail_noah;
5465 break;
5467 case 0x01: /* read disk status */
5468 status = read_byte(0x0040, 0x0074);
5469 SET_AH(status);
5470 SET_DISK_RET_STATUS(0);
5472 /* set CF if error status read */
5473 if (status) goto int13_fail_nostatus;
5474 else goto int13_success_noah;
5475 break;
5477 case 0x15: /* read disk drive size */
5478 SET_AH(0x02);
5479 goto int13_fail_noah;
5480 break;
5482 case 0x41: // IBM/MS installation check
5483 BX=0xaa55; // install check
5484 SET_AH(0x30); // EDD 2.1
5485 CX=0x0007; // ext disk access, removable and edd
5486 goto int13_success_noah;
5487 break;
5489 case 0x42: // IBM/MS extended read
5490 case 0x44: // IBM/MS verify sectors
5491 case 0x47: // IBM/MS extended seek
5493 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
5494 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
5495 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
5497 // Can't use 64 bits lba
5498 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
5499 if (lba != 0L) {
5500 BX_PANIC("int13_cdrom: function %02x. Can't use 64bits lba\n",GET_AH());
5501 goto int13_fail;
5504 // Get 32 bits lba
5505 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5507 // If verify or seek
5508 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
5509 goto int13_success;
5511 memsetb(get_SS(),atacmd,0,12);
5512 atacmd[0]=0x28; // READ command
5513 atacmd[7]=(count & 0xff00) >> 8; // Sectors
5514 atacmd[8]=(count & 0x00ff); // Sectors
5515 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
5516 atacmd[3]=(lba & 0x00ff0000) >> 16;
5517 atacmd[4]=(lba & 0x0000ff00) >> 8;
5518 atacmd[5]=(lba & 0x000000ff);
5519 status = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, count*2048L, ATA_DATA_IN, segment,offset);
5521 count = (Bit16u)(read_dword(ebda_seg, &EbdaData->ata.trsfbytes) >> 11);
5522 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5524 if (status != 0) {
5525 BX_INFO("int13_cdrom: function %02x, status %02x !\n",GET_AH(),status);
5526 SET_AH(0x0c);
5527 goto int13_fail_noah;
5530 goto int13_success;
5531 break;
5533 case 0x45: // IBM/MS lock/unlock drive
5534 if (GET_AL() > 2) goto int13_fail;
5536 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5538 switch (GET_AL()) {
5539 case 0 : // lock
5540 if (locks == 0xff) {
5541 SET_AH(0xb4);
5542 SET_AL(1);
5543 goto int13_fail_noah;
5545 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, ++locks);
5546 SET_AL(1);
5547 break;
5548 case 1 : // unlock
5549 if (locks == 0x00) {
5550 SET_AH(0xb0);
5551 SET_AL(0);
5552 goto int13_fail_noah;
5554 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, --locks);
5555 SET_AL(locks==0?0:1);
5556 break;
5557 case 2 : // status
5558 SET_AL(locks==0?0:1);
5559 break;
5561 goto int13_success;
5562 break;
5564 case 0x46: // IBM/MS eject media
5565 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5567 if (locks != 0) {
5568 SET_AH(0xb1); // media locked
5569 goto int13_fail_noah;
5571 // FIXME should handle 0x31 no media in device
5572 // FIXME should handle 0xb5 valid request failed
5574 // Call removable media eject
5575 ASM_START
5576 push bp
5577 mov bp, sp
5579 mov ah, #0x52
5580 int #0x15
5581 mov _int13_cdrom.status + 2[bp], ah
5582 jnc int13_cdrom_rme_end
5583 mov _int13_cdrom.status, #1
5584 int13_cdrom_rme_end:
5585 pop bp
5586 ASM_END
5588 if (status != 0) {
5589 SET_AH(0xb1); // media locked
5590 goto int13_fail_noah;
5593 goto int13_success;
5594 break;
5596 case 0x48: // IBM/MS get drive parameters
5597 size = read_word(DS,SI+(Bit16u)&Int13Ext->size);
5599 // Buffer is too small
5600 if(size < 0x1a)
5601 goto int13_fail;
5603 // EDD 1.x
5604 if(size >= 0x1a) {
5605 Bit16u cylinders, heads, spt, blksize;
5607 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5609 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5610 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x74); // removable, media change, lockable, max values
5611 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0xffffffff);
5612 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, 0xffffffff);
5613 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, 0xffffffff);
5614 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, 0xffffffff); // FIXME should be Bit64
5615 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0xffffffff);
5616 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5619 // EDD 2.x
5620 if(size >= 0x1e) {
5621 Bit8u channel, dev, irq, mode, checksum, i;
5622 Bit16u iobase1, iobase2, options;
5624 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5626 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5627 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5629 // Fill in dpte
5630 channel = device / 2;
5631 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5632 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5633 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5634 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5636 // FIXME atapi device
5637 options = (1<<4); // lba translation
5638 options |= (1<<5); // removable device
5639 options |= (1<<6); // atapi device
5640 options |= (mode==ATA_MODE_PIO32?1:0<<7);
5642 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5643 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2 + ATA_CB_DC);
5644 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5645 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5646 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5647 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5648 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5649 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5650 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5651 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5652 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5654 checksum=0;
5655 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, ((Bit8u*)(&EbdaData->ata.dpte)) + i);
5656 checksum = ~checksum;
5657 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5660 // EDD 3.x
5661 if(size >= 0x42) {
5662 Bit8u channel, iface, checksum, i;
5663 Bit16u iobase1;
5665 channel = device / 2;
5666 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5667 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5669 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5670 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5671 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5672 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5673 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5675 if (iface==ATA_IFACE_ISA) {
5676 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5677 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5678 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5679 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5681 else {
5682 // FIXME PCI
5684 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5685 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5686 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5687 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5689 if (iface==ATA_IFACE_ISA) {
5690 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5691 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5692 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5694 else {
5695 // FIXME PCI
5697 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5698 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5699 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5700 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5702 checksum=0;
5703 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5704 checksum = ~checksum;
5705 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5708 goto int13_success;
5709 break;
5711 case 0x49: // IBM/MS extended media change
5712 // always send changed ??
5713 SET_AH(06);
5714 goto int13_fail_nostatus;
5715 break;
5717 case 0x4e: // // IBM/MS set hardware configuration
5718 // DMA, prefetch, PIO maximum not supported
5719 switch (GET_AL()) {
5720 case 0x01:
5721 case 0x03:
5722 case 0x04:
5723 case 0x06:
5724 goto int13_success;
5725 break;
5726 default :
5727 goto int13_fail;
5729 break;
5731 // all those functions return unimplemented
5732 case 0x02: /* read sectors */
5733 case 0x04: /* verify sectors */
5734 case 0x08: /* read disk drive parameters */
5735 case 0x0a: /* read disk sectors with ECC */
5736 case 0x0b: /* write disk sectors with ECC */
5737 case 0x18: /* set media type for format */
5738 case 0x50: // ? - send packet command
5739 default:
5740 BX_INFO("int13_cdrom: unsupported AH=%02x\n", GET_AH());
5741 goto int13_fail;
5742 break;
5745 int13_fail:
5746 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5747 int13_fail_noah:
5748 SET_DISK_RET_STATUS(GET_AH());
5749 int13_fail_nostatus:
5750 SET_CF(); // error occurred
5751 return;
5753 int13_success:
5754 SET_AH(0x00); // no error
5755 int13_success_noah:
5756 SET_DISK_RET_STATUS(0x00);
5757 CLEAR_CF(); // no error
5758 return;
5761 // ---------------------------------------------------------------------------
5762 // End of int13 for cdrom
5763 // ---------------------------------------------------------------------------
5765 #if BX_ELTORITO_BOOT
5766 // ---------------------------------------------------------------------------
5767 // Start of int13 for eltorito functions
5768 // ---------------------------------------------------------------------------
5770 void
5771 int13_eltorito(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
5772 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
5774 Bit16u ebda_seg=read_word(0x0040,0x000E);
5776 BX_DEBUG_INT13_ET("int13_eltorito: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5777 // BX_DEBUG_INT13_ET("int13_eltorito: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
5779 switch (GET_AH()) {
5781 // FIXME ElTorito Various. Should be implemented
5782 case 0x4a: // ElTorito - Initiate disk emu
5783 case 0x4c: // ElTorito - Initiate disk emu and boot
5784 case 0x4d: // ElTorito - Return Boot catalog
5785 BX_PANIC("Int13 eltorito call with AX=%04x. Please report\n",AX);
5786 goto int13_fail;
5787 break;
5789 case 0x4b: // ElTorito - Terminate disk emu
5790 // FIXME ElTorito Hardcoded
5791 write_byte(DS,SI+0x00,0x13);
5792 write_byte(DS,SI+0x01,read_byte(ebda_seg,&EbdaData->cdemu.media));
5793 write_byte(DS,SI+0x02,read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
5794 write_byte(DS,SI+0x03,read_byte(ebda_seg,&EbdaData->cdemu.controller_index));
5795 write_dword(DS,SI+0x04,read_dword(ebda_seg,&EbdaData->cdemu.ilba));
5796 write_word(DS,SI+0x08,read_word(ebda_seg,&EbdaData->cdemu.device_spec));
5797 write_word(DS,SI+0x0a,read_word(ebda_seg,&EbdaData->cdemu.buffer_segment));
5798 write_word(DS,SI+0x0c,read_word(ebda_seg,&EbdaData->cdemu.load_segment));
5799 write_word(DS,SI+0x0e,read_word(ebda_seg,&EbdaData->cdemu.sector_count));
5800 write_byte(DS,SI+0x10,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.cylinders));
5801 write_byte(DS,SI+0x11,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.spt));
5802 write_byte(DS,SI+0x12,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.heads));
5804 // If we have to terminate emulation
5805 if(GET_AL() == 0x00) {
5806 // FIXME ElTorito Various. Should be handled accordingly to spec
5807 write_byte(ebda_seg,&EbdaData->cdemu.active, 0x00); // bye bye
5810 goto int13_success;
5811 break;
5813 default:
5814 BX_INFO("int13_eltorito: unsupported AH=%02x\n", GET_AH());
5815 goto int13_fail;
5816 break;
5819 int13_fail:
5820 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5821 SET_DISK_RET_STATUS(GET_AH());
5822 SET_CF(); // error occurred
5823 return;
5825 int13_success:
5826 SET_AH(0x00); // no error
5827 SET_DISK_RET_STATUS(0x00);
5828 CLEAR_CF(); // no error
5829 return;
5832 // ---------------------------------------------------------------------------
5833 // End of int13 for eltorito functions
5834 // ---------------------------------------------------------------------------
5836 // ---------------------------------------------------------------------------
5837 // Start of int13 when emulating a device from the cd
5838 // ---------------------------------------------------------------------------
5840 void
5841 int13_cdemu(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
5842 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
5844 Bit16u ebda_seg=read_word(0x0040,0x000E);
5845 Bit8u device, status;
5846 Bit16u vheads, vspt, vcylinders;
5847 Bit16u head, sector, cylinder, nbsectors;
5848 Bit32u vlba, ilba, slba, elba;
5849 Bit16u before, segment, offset;
5850 Bit8u atacmd[12];
5852 BX_DEBUG_INT13_ET("int13_cdemu: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5854 /* at this point, we are emulating a floppy/harddisk */
5856 // Recompute the device number
5857 device = read_byte(ebda_seg,&EbdaData->cdemu.controller_index) * 2;
5858 device += read_byte(ebda_seg,&EbdaData->cdemu.device_spec);
5860 SET_DISK_RET_STATUS(0x00);
5862 /* basic checks : emulation should be active, dl should equal the emulated drive */
5863 if( (read_byte(ebda_seg,&EbdaData->cdemu.active) ==0 )
5864 || (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive ) != GET_DL())) {
5865 BX_INFO("int13_cdemu: function %02x, emulation not active for DL= %02x\n", GET_AH(), GET_DL());
5866 goto int13_fail;
5869 switch (GET_AH()) {
5871 // all those functions return SUCCESS
5872 case 0x00: /* disk controller reset */
5873 case 0x09: /* initialize drive parameters */
5874 case 0x0c: /* seek to specified cylinder */
5875 case 0x0d: /* alternate disk reset */ // FIXME ElTorito Various. should really reset ?
5876 case 0x10: /* check drive ready */ // FIXME ElTorito Various. should check if ready ?
5877 case 0x11: /* recalibrate */
5878 case 0x14: /* controller internal diagnostic */
5879 case 0x16: /* detect disk change */
5880 goto int13_success;
5881 break;
5883 // all those functions return disk write-protected
5884 case 0x03: /* write disk sectors */
5885 case 0x05: /* format disk track */
5886 SET_AH(0x03);
5887 goto int13_fail_noah;
5888 break;
5890 case 0x01: /* read disk status */
5891 status=read_byte(0x0040, 0x0074);
5892 SET_AH(status);
5893 SET_DISK_RET_STATUS(0);
5895 /* set CF if error status read */
5896 if (status) goto int13_fail_nostatus;
5897 else goto int13_success_noah;
5898 break;
5900 case 0x02: // read disk sectors
5901 case 0x04: // verify disk sectors
5902 vspt = read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
5903 vcylinders = read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders);
5904 vheads = read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads);
5906 ilba = read_dword(ebda_seg,&EbdaData->cdemu.ilba);
5908 sector = GET_CL() & 0x003f;
5909 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
5910 head = GET_DH();
5911 nbsectors = GET_AL();
5912 segment = ES;
5913 offset = BX;
5915 // no sector to read ?
5916 if(nbsectors==0) goto int13_success;
5918 // sanity checks sco openserver needs this!
5919 if ((sector > vspt)
5920 || (cylinder >= vcylinders)
5921 || (head >= vheads)) {
5922 goto int13_fail;
5925 // After controls, verify do nothing
5926 if (GET_AH() == 0x04) goto int13_success;
5928 segment = ES+(BX / 16);
5929 offset = BX % 16;
5931 // calculate the virtual lba inside the image
5932 vlba=((((Bit32u)cylinder*(Bit32u)vheads)+(Bit32u)head)*(Bit32u)vspt)+((Bit32u)(sector-1));
5934 // In advance so we don't loose the count
5935 SET_AL(nbsectors);
5937 // start lba on cd
5938 slba = (Bit32u)vlba/4;
5939 before= (Bit16u)vlba%4;
5941 // end lba on cd
5942 elba = (Bit32u)(vlba+nbsectors-1)/4;
5944 memsetb(get_SS(),atacmd,0,12);
5945 atacmd[0]=0x28; // READ command
5946 atacmd[7]=((Bit16u)(elba-slba+1) & 0xff00) >> 8; // Sectors
5947 atacmd[8]=((Bit16u)(elba-slba+1) & 0x00ff); // Sectors
5948 atacmd[2]=(ilba+slba & 0xff000000) >> 24; // LBA
5949 atacmd[3]=(ilba+slba & 0x00ff0000) >> 16;
5950 atacmd[4]=(ilba+slba & 0x0000ff00) >> 8;
5951 atacmd[5]=(ilba+slba & 0x000000ff);
5952 if((status = ata_cmd_packet(device, 12, get_SS(), atacmd, before*512, nbsectors*512L, ATA_DATA_IN, segment,offset)) != 0) {
5953 BX_INFO("int13_cdemu: function %02x, error %02x !\n",GET_AH(),status);
5954 SET_AH(0x02);
5955 SET_AL(0);
5956 goto int13_fail_noah;
5959 goto int13_success;
5960 break;
5962 case 0x08: /* read disk drive parameters */
5963 vspt=read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
5964 vcylinders=read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders) - 1;
5965 vheads=read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads) - 1;
5967 SET_AL( 0x00 );
5968 SET_BL( 0x00 );
5969 SET_CH( vcylinders & 0xff );
5970 SET_CL((( vcylinders >> 2) & 0xc0) | ( vspt & 0x3f ));
5971 SET_DH( vheads );
5972 SET_DL( 0x02 ); // FIXME ElTorito Various. should send the real count of drives 1 or 2
5973 // FIXME ElTorito Harddisk. should send the HD count
5975 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
5976 case 0x01: SET_BL( 0x02 ); break;
5977 case 0x02: SET_BL( 0x04 ); break;
5978 case 0x03: SET_BL( 0x06 ); break;
5981 ASM_START
5982 push bp
5983 mov bp, sp
5984 mov ax, #diskette_param_table2
5985 mov _int13_cdemu.DI+2[bp], ax
5986 mov _int13_cdemu.ES+2[bp], cs
5987 pop bp
5988 ASM_END
5989 goto int13_success;
5990 break;
5992 case 0x15: /* read disk drive size */
5993 // FIXME ElTorito Harddisk. What geometry to send ?
5994 SET_AH(0x03);
5995 goto int13_success_noah;
5996 break;
5998 // all those functions return unimplemented
5999 case 0x0a: /* read disk sectors with ECC */
6000 case 0x0b: /* write disk sectors with ECC */
6001 case 0x18: /* set media type for format */
6002 case 0x41: // IBM/MS installation check
6003 // FIXME ElTorito Harddisk. Darwin would like to use EDD
6004 case 0x42: // IBM/MS extended read
6005 case 0x43: // IBM/MS extended write
6006 case 0x44: // IBM/MS verify sectors
6007 case 0x45: // IBM/MS lock/unlock drive
6008 case 0x46: // IBM/MS eject media
6009 case 0x47: // IBM/MS extended seek
6010 case 0x48: // IBM/MS get drive parameters
6011 case 0x49: // IBM/MS extended media change
6012 case 0x4e: // ? - set hardware configuration
6013 case 0x50: // ? - send packet command
6014 default:
6015 BX_INFO("int13_cdemu function AH=%02x unsupported, returns fail\n", GET_AH());
6016 goto int13_fail;
6017 break;
6020 int13_fail:
6021 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
6022 int13_fail_noah:
6023 SET_DISK_RET_STATUS(GET_AH());
6024 int13_fail_nostatus:
6025 SET_CF(); // error occurred
6026 return;
6028 int13_success:
6029 SET_AH(0x00); // no error
6030 int13_success_noah:
6031 SET_DISK_RET_STATUS(0x00);
6032 CLEAR_CF(); // no error
6033 return;
6036 // ---------------------------------------------------------------------------
6037 // End of int13 when emulating a device from the cd
6038 // ---------------------------------------------------------------------------
6040 #endif // BX_ELTORITO_BOOT
6042 #else //BX_USE_ATADRV
6044 void
6045 outLBA(cylinder,hd_heads,head,hd_sectors,sector,dl)
6046 Bit16u cylinder;
6047 Bit16u hd_heads;
6048 Bit16u head;
6049 Bit16u hd_sectors;
6050 Bit16u sector;
6051 Bit16u dl;
6053 ASM_START
6054 push bp
6055 mov bp, sp
6056 push eax
6057 push ebx
6058 push edx
6059 xor eax,eax
6060 mov ax,4[bp] // cylinder
6061 xor ebx,ebx
6062 mov bl,6[bp] // hd_heads
6063 imul ebx
6065 mov bl,8[bp] // head
6066 add eax,ebx
6067 mov bl,10[bp] // hd_sectors
6068 imul ebx
6069 mov bl,12[bp] // sector
6070 add eax,ebx
6072 dec eax
6073 mov dx,#0x1f3
6074 out dx,al
6075 mov dx,#0x1f4
6076 mov al,ah
6077 out dx,al
6078 shr eax,#16
6079 mov dx,#0x1f5
6080 out dx,al
6081 and ah,#0xf
6082 mov bl,14[bp] // dl
6083 and bl,#1
6084 shl bl,#4
6085 or ah,bl
6086 or ah,#0xe0
6087 mov al,ah
6088 mov dx,#0x01f6
6089 out dx,al
6090 pop edx
6091 pop ebx
6092 pop eax
6093 pop bp
6094 ASM_END
6097 void
6098 int13_harddisk(EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
6099 Bit16u EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
6101 Bit8u drive, num_sectors, sector, head, status, mod;
6102 Bit8u drive_map;
6103 Bit8u n_drives;
6104 Bit16u cyl_mod, ax;
6105 Bit16u max_cylinder, cylinder, total_sectors;
6106 Bit16u hd_cylinders;
6107 Bit8u hd_heads, hd_sectors;
6108 Bit16u val16;
6109 Bit8u sector_count;
6110 unsigned int i;
6111 Bit16u tempbx;
6112 Bit16u dpsize;
6114 Bit16u count, segment, offset;
6115 Bit32u lba;
6116 Bit16u error;
6118 BX_DEBUG_INT13_HD("int13 harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
6120 write_byte(0x0040, 0x008e, 0); // clear completion flag
6122 /* at this point, DL is >= 0x80 to be passed from the floppy int13h
6123 handler code */
6124 /* check how many disks first (cmos reg 0x12), return an error if
6125 drive not present */
6126 drive_map = inb_cmos(0x12);
6127 drive_map = (((drive_map & 0xf0)==0) ? 0 : 1) |
6128 (((drive_map & 0x0f)==0) ? 0 : 2);
6129 n_drives = (drive_map==0) ? 0 :
6130 ((drive_map==3) ? 2 : 1);
6132 if (!(drive_map & (1<<(GET_ELDL()&0x7f)))) { /* allow 0, 1, or 2 disks */
6133 SET_AH(0x01);
6134 SET_DISK_RET_STATUS(0x01);
6135 SET_CF(); /* error occurred */
6136 return;
6139 switch (GET_AH()) {
6141 case 0x00: /* disk controller reset */
6142 BX_DEBUG_INT13_HD("int13_f00\n");
6144 SET_AH(0);
6145 SET_DISK_RET_STATUS(0);
6146 set_diskette_ret_status(0);
6147 set_diskette_current_cyl(0, 0); /* current cylinder, diskette 1 */
6148 set_diskette_current_cyl(1, 0); /* current cylinder, diskette 2 */
6149 CLEAR_CF(); /* successful */
6150 return;
6151 break;
6153 case 0x01: /* read disk status */
6154 BX_DEBUG_INT13_HD("int13_f01\n");
6155 status = read_byte(0x0040, 0x0074);
6156 SET_AH(status);
6157 SET_DISK_RET_STATUS(0);
6158 /* set CF if error status read */
6159 if (status) SET_CF();
6160 else CLEAR_CF();
6161 return;
6162 break;
6164 case 0x04: // verify disk sectors
6165 case 0x02: // read disk sectors
6166 drive = GET_ELDL();
6167 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6169 num_sectors = GET_AL();
6170 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
6171 sector = (GET_CL() & 0x3f);
6172 head = GET_DH();
6175 if (hd_cylinders > 1024) {
6176 if (hd_cylinders <= 2048) {
6177 cylinder <<= 1;
6179 else if (hd_cylinders <= 4096) {
6180 cylinder <<= 2;
6182 else if (hd_cylinders <= 8192) {
6183 cylinder <<= 3;
6185 else { // hd_cylinders <= 16384
6186 cylinder <<= 4;
6189 ax = head / hd_heads;
6190 cyl_mod = ax & 0xff;
6191 head = ax >> 8;
6192 cylinder |= cyl_mod;
6195 if ( (cylinder >= hd_cylinders) ||
6196 (sector > hd_sectors) ||
6197 (head >= hd_heads) ) {
6198 SET_AH(1);
6199 SET_DISK_RET_STATUS(1);
6200 SET_CF(); /* error occurred */
6201 return;
6204 if ( (num_sectors > 128) || (num_sectors == 0) )
6205 BX_PANIC("int13_harddisk: num_sectors out of range!\n");
6207 if (head > 15)
6208 BX_PANIC("hard drive BIOS:(read/verify) head > 15\n");
6210 if ( GET_AH() == 0x04 ) {
6211 SET_AH(0);
6212 SET_DISK_RET_STATUS(0);
6213 CLEAR_CF();
6214 return;
6217 status = inb(0x1f7);
6218 if (status & 0x80) {
6219 BX_PANIC("hard drive BIOS:(read/verify) BUSY bit set\n");
6221 outb(0x01f2, num_sectors);
6222 /* activate LBA? (tomv) */
6223 if (hd_heads > 16) {
6224 BX_DEBUG_INT13_HD("CHS: %x %x %x\n", cylinder, head, sector);
6225 outLBA(cylinder,hd_heads,head,hd_sectors,sector,drive);
6227 else {
6228 outb(0x01f3, sector);
6229 outb(0x01f4, cylinder & 0x00ff);
6230 outb(0x01f5, cylinder >> 8);
6231 outb(0x01f6, 0xa0 | ((drive & 0x01)<<4) | (head & 0x0f));
6233 outb(0x01f7, 0x20);
6235 while (1) {
6236 status = inb(0x1f7);
6237 if ( !(status & 0x80) ) break;
6240 if (status & 0x01) {
6241 BX_PANIC("hard drive BIOS:(read/verify) read error\n");
6242 } else if ( !(status & 0x08) ) {
6243 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
6244 BX_PANIC("hard drive BIOS:(read/verify) expected DRQ=1\n");
6247 sector_count = 0;
6248 tempbx = BX;
6250 ASM_START
6251 sti ;; enable higher priority interrupts
6252 ASM_END
6254 while (1) {
6255 ASM_START
6256 ;; store temp bx in real DI register
6257 push bp
6258 mov bp, sp
6259 mov di, _int13_harddisk.tempbx + 2 [bp]
6260 pop bp
6262 ;; adjust if there will be an overrun
6263 cmp di, #0xfe00
6264 jbe i13_f02_no_adjust
6265 i13_f02_adjust:
6266 sub di, #0x0200 ; sub 512 bytes from offset
6267 mov ax, es
6268 add ax, #0x0020 ; add 512 to segment
6269 mov es, ax
6271 i13_f02_no_adjust:
6272 mov cx, #0x0100 ;; counter (256 words = 512b)
6273 mov dx, #0x01f0 ;; AT data read port
6276 insw ;; CX words transfered from port(DX) to ES:[DI]
6278 i13_f02_done:
6279 ;; store real DI register back to temp bx
6280 push bp
6281 mov bp, sp
6282 mov _int13_harddisk.tempbx + 2 [bp], di
6283 pop bp
6284 ASM_END
6286 sector_count++;
6287 num_sectors--;
6288 if (num_sectors == 0) {
6289 status = inb(0x1f7);
6290 if ( (status & 0xc9) != 0x40 )
6291 BX_PANIC("no sectors left to read/verify, status is %02x\n", (unsigned) status);
6292 break;
6294 else {
6295 status = inb(0x1f7);
6296 if ( (status & 0xc9) != 0x48 )
6297 BX_PANIC("more sectors left to read/verify, status is %02x\n", (unsigned) status);
6298 continue;
6302 SET_AH(0);
6303 SET_DISK_RET_STATUS(0);
6304 SET_AL(sector_count);
6305 CLEAR_CF(); /* successful */
6306 return;
6307 break;
6310 case 0x03: /* write disk sectors */
6311 BX_DEBUG_INT13_HD("int13_f03\n");
6312 drive = GET_ELDL ();
6313 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6315 num_sectors = GET_AL();
6316 cylinder = GET_CH();
6317 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
6318 sector = (GET_CL() & 0x3f);
6319 head = GET_DH();
6321 if (hd_cylinders > 1024) {
6322 if (hd_cylinders <= 2048) {
6323 cylinder <<= 1;
6325 else if (hd_cylinders <= 4096) {
6326 cylinder <<= 2;
6328 else if (hd_cylinders <= 8192) {
6329 cylinder <<= 3;
6331 else { // hd_cylinders <= 16384
6332 cylinder <<= 4;
6335 ax = head / hd_heads;
6336 cyl_mod = ax & 0xff;
6337 head = ax >> 8;
6338 cylinder |= cyl_mod;
6341 if ( (cylinder >= hd_cylinders) ||
6342 (sector > hd_sectors) ||
6343 (head >= hd_heads) ) {
6344 SET_AH( 1);
6345 SET_DISK_RET_STATUS(1);
6346 SET_CF(); /* error occurred */
6347 return;
6350 if ( (num_sectors > 128) || (num_sectors == 0) )
6351 BX_PANIC("int13_harddisk: num_sectors out of range!\n");
6353 if (head > 15)
6354 BX_PANIC("hard drive BIOS:(read) head > 15\n");
6356 status = inb(0x1f7);
6357 if (status & 0x80) {
6358 BX_PANIC("hard drive BIOS:(read) BUSY bit set\n");
6360 // should check for Drive Ready Bit also in status reg
6361 outb(0x01f2, num_sectors);
6363 /* activate LBA? (tomv) */
6364 if (hd_heads > 16) {
6365 BX_DEBUG_INT13_HD("CHS (write): %x %x %x\n", cylinder, head, sector);
6366 outLBA(cylinder,hd_heads,head,hd_sectors,sector,GET_ELDL());
6368 else {
6369 outb(0x01f3, sector);
6370 outb(0x01f4, cylinder & 0x00ff);
6371 outb(0x01f5, cylinder >> 8);
6372 outb(0x01f6, 0xa0 | ((GET_ELDL() & 0x01)<<4) | (head & 0x0f));
6374 outb(0x01f7, 0x30);
6376 // wait for busy bit to turn off after seeking
6377 while (1) {
6378 status = inb(0x1f7);
6379 if ( !(status & 0x80) ) break;
6382 if ( !(status & 0x08) ) {
6383 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
6384 BX_PANIC("hard drive BIOS:(write) data-request bit not set\n");
6387 sector_count = 0;
6388 tempbx = BX;
6390 ASM_START
6391 sti ;; enable higher priority interrupts
6392 ASM_END
6394 while (1) {
6395 ASM_START
6396 ;; store temp bx in real SI register
6397 push bp
6398 mov bp, sp
6399 mov si, _int13_harddisk.tempbx + 2 [bp]
6400 pop bp
6402 ;; adjust if there will be an overrun
6403 cmp si, #0xfe00
6404 jbe i13_f03_no_adjust
6405 i13_f03_adjust:
6406 sub si, #0x0200 ; sub 512 bytes from offset
6407 mov ax, es
6408 add ax, #0x0020 ; add 512 to segment
6409 mov es, ax
6411 i13_f03_no_adjust:
6412 mov cx, #0x0100 ;; counter (256 words = 512b)
6413 mov dx, #0x01f0 ;; AT data read port
6415 seg ES
6417 outsw ;; CX words tranfered from ES:[SI] to port(DX)
6419 ;; store real SI register back to temp bx
6420 push bp
6421 mov bp, sp
6422 mov _int13_harddisk.tempbx + 2 [bp], si
6423 pop bp
6424 ASM_END
6426 sector_count++;
6427 num_sectors--;
6428 if (num_sectors == 0) {
6429 status = inb(0x1f7);
6430 if ( (status & 0xe9) != 0x40 )
6431 BX_PANIC("no sectors left to write, status is %02x\n", (unsigned) status);
6432 break;
6434 else {
6435 status = inb(0x1f7);
6436 if ( (status & 0xc9) != 0x48 )
6437 BX_PANIC("more sectors left to write, status is %02x\n", (unsigned) status);
6438 continue;
6442 SET_AH(0);
6443 SET_DISK_RET_STATUS(0);
6444 SET_AL(sector_count);
6445 CLEAR_CF(); /* successful */
6446 return;
6447 break;
6449 case 0x05: /* format disk track */
6450 BX_DEBUG_INT13_HD("int13_f05\n");
6451 BX_PANIC("format disk track called\n");
6452 /* nop */
6453 SET_AH(0);
6454 SET_DISK_RET_STATUS(0);
6455 CLEAR_CF(); /* successful */
6456 return;
6457 break;
6459 case 0x08: /* read disk drive parameters */
6460 BX_DEBUG_INT13_HD("int13_f08\n");
6462 drive = GET_ELDL ();
6463 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6465 // translate CHS
6467 if (hd_cylinders <= 1024) {
6468 // hd_cylinders >>= 0;
6469 // hd_heads <<= 0;
6471 else if (hd_cylinders <= 2048) {
6472 hd_cylinders >>= 1;
6473 hd_heads <<= 1;
6475 else if (hd_cylinders <= 4096) {
6476 hd_cylinders >>= 2;
6477 hd_heads <<= 2;
6479 else if (hd_cylinders <= 8192) {
6480 hd_cylinders >>= 3;
6481 hd_heads <<= 3;
6483 else { // hd_cylinders <= 16384
6484 hd_cylinders >>= 4;
6485 hd_heads <<= 4;
6488 max_cylinder = hd_cylinders - 2; /* 0 based */
6489 SET_AL(0);
6490 SET_CH(max_cylinder & 0xff);
6491 SET_CL(((max_cylinder >> 2) & 0xc0) | (hd_sectors & 0x3f));
6492 SET_DH(hd_heads - 1);
6493 SET_DL(n_drives); /* returns 0, 1, or 2 hard drives */
6494 SET_AH(0);
6495 SET_DISK_RET_STATUS(0);
6496 CLEAR_CF(); /* successful */
6498 return;
6499 break;
6501 case 0x09: /* initialize drive parameters */
6502 BX_DEBUG_INT13_HD("int13_f09\n");
6503 SET_AH(0);
6504 SET_DISK_RET_STATUS(0);
6505 CLEAR_CF(); /* successful */
6506 return;
6507 break;
6509 case 0x0a: /* read disk sectors with ECC */
6510 BX_DEBUG_INT13_HD("int13_f0a\n");
6511 case 0x0b: /* write disk sectors with ECC */
6512 BX_DEBUG_INT13_HD("int13_f0b\n");
6513 BX_PANIC("int13h Functions 0Ah & 0Bh not implemented!\n");
6514 return;
6515 break;
6517 case 0x0c: /* seek to specified cylinder */
6518 BX_DEBUG_INT13_HD("int13_f0c\n");
6519 BX_INFO("int13h function 0ch (seek) not implemented!\n");
6520 SET_AH(0);
6521 SET_DISK_RET_STATUS(0);
6522 CLEAR_CF(); /* successful */
6523 return;
6524 break;
6526 case 0x0d: /* alternate disk reset */
6527 BX_DEBUG_INT13_HD("int13_f0d\n");
6528 SET_AH(0);
6529 SET_DISK_RET_STATUS(0);
6530 CLEAR_CF(); /* successful */
6531 return;
6532 break;
6534 case 0x10: /* check drive ready */
6535 BX_DEBUG_INT13_HD("int13_f10\n");
6536 //SET_AH(0);
6537 //SET_DISK_RET_STATUS(0);
6538 //CLEAR_CF(); /* successful */
6539 //return;
6540 //break;
6542 // should look at 40:8E also???
6543 status = inb(0x01f7);
6544 if ( (status & 0xc0) == 0x40 ) {
6545 SET_AH(0);
6546 SET_DISK_RET_STATUS(0);
6547 CLEAR_CF(); // drive ready
6548 return;
6550 else {
6551 SET_AH(0xAA);
6552 SET_DISK_RET_STATUS(0xAA);
6553 SET_CF(); // not ready
6554 return;
6556 break;
6558 case 0x11: /* recalibrate */
6559 BX_DEBUG_INT13_HD("int13_f11\n");
6560 SET_AH(0);
6561 SET_DISK_RET_STATUS(0);
6562 CLEAR_CF(); /* successful */
6563 return;
6564 break;
6566 case 0x14: /* controller internal diagnostic */
6567 BX_DEBUG_INT13_HD("int13_f14\n");
6568 SET_AH(0);
6569 SET_DISK_RET_STATUS(0);
6570 CLEAR_CF(); /* successful */
6571 SET_AL(0);
6572 return;
6573 break;
6575 case 0x15: /* read disk drive size */
6576 drive = GET_ELDL();
6577 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6578 ASM_START
6579 push bp
6580 mov bp, sp
6581 mov al, _int13_harddisk.hd_heads + 2 [bp]
6582 mov ah, _int13_harddisk.hd_sectors + 2 [bp]
6583 mul al, ah ;; ax = heads * sectors
6584 mov bx, _int13_harddisk.hd_cylinders + 2 [bp]
6585 dec bx ;; use (cylinders - 1) ???
6586 mul ax, bx ;; dx:ax = (cylinders -1) * (heads * sectors)
6587 ;; now we need to move the 32bit result dx:ax to what the
6588 ;; BIOS wants which is cx:dx.
6589 ;; and then into CX:DX on the stack
6590 mov _int13_harddisk.CX + 2 [bp], dx
6591 mov _int13_harddisk.DX + 2 [bp], ax
6592 pop bp
6593 ASM_END
6594 SET_AH(3); // hard disk accessible
6595 SET_DISK_RET_STATUS(0); // ??? should this be 0
6596 CLEAR_CF(); // successful
6597 return;
6598 break;
6600 case 0x18: // set media type for format
6601 case 0x41: // IBM/MS
6602 case 0x42: // IBM/MS
6603 case 0x43: // IBM/MS
6604 case 0x44: // IBM/MS
6605 case 0x45: // IBM/MS lock/unlock drive
6606 case 0x46: // IBM/MS eject media
6607 case 0x47: // IBM/MS extended seek
6608 case 0x49: // IBM/MS extended media change
6609 case 0x50: // IBM/MS send packet command
6610 default:
6611 BX_INFO("int13_harddisk: unsupported AH=%02x\n", GET_AH());
6613 SET_AH(1); // code=invalid function in AH or invalid parameter
6614 SET_DISK_RET_STATUS(1);
6615 SET_CF(); /* unsuccessful */
6616 return;
6617 break;
6621 static char panic_msg_reg12h[] = "HD%d cmos reg 12h not type F\n";
6622 static char panic_msg_reg19h[] = "HD%d cmos reg %02xh not user definable type 47\n";
6624 void
6625 get_hd_geometry(drive, hd_cylinders, hd_heads, hd_sectors)
6626 Bit8u drive;
6627 Bit16u *hd_cylinders;
6628 Bit8u *hd_heads;
6629 Bit8u *hd_sectors;
6631 Bit8u hd_type;
6632 Bit16u ss;
6633 Bit16u cylinders;
6634 Bit8u iobase;
6636 ss = get_SS();
6637 if (drive == 0x80) {
6638 hd_type = inb_cmos(0x12) & 0xf0;
6639 if (hd_type != 0xf0)
6640 BX_INFO(panic_msg_reg12h,0);
6641 hd_type = inb_cmos(0x19); // HD0: extended type
6642 if (hd_type != 47)
6643 BX_INFO(panic_msg_reg19h,0,0x19);
6644 iobase = 0x1b;
6645 } else {
6646 hd_type = inb_cmos(0x12) & 0x0f;
6647 if (hd_type != 0x0f)
6648 BX_INFO(panic_msg_reg12h,1);
6649 hd_type = inb_cmos(0x1a); // HD1: extended type
6650 if (hd_type != 47)
6651 BX_INFO(panic_msg_reg19h,0,0x1a);
6652 iobase = 0x24;
6655 // cylinders
6656 cylinders = inb_cmos(iobase) | (inb_cmos(iobase+1) << 8);
6657 write_word(ss, hd_cylinders, cylinders);
6659 // heads
6660 write_byte(ss, hd_heads, inb_cmos(iobase+2));
6662 // sectors per track
6663 write_byte(ss, hd_sectors, inb_cmos(iobase+8));
6666 #endif //else BX_USE_ATADRV
6668 #if BX_SUPPORT_FLOPPY
6670 //////////////////////
6671 // FLOPPY functions //
6672 //////////////////////
6674 void floppy_reset_controller()
6676 Bit8u val8;
6678 // Reset controller
6679 val8 = inb(0x03f2);
6680 outb(0x03f2, val8 & ~0x04);
6681 outb(0x03f2, val8 | 0x04);
6683 // Wait for controller to come out of reset
6684 do {
6685 val8 = inb(0x3f4);
6686 } while ( (val8 & 0xc0) != 0x80 );
6689 void floppy_prepare_controller(drive)
6690 Bit16u drive;
6692 Bit8u val8, dor, prev_reset;
6694 // set 40:3e bit 7 to 0
6695 val8 = read_byte(0x0040, 0x003e);
6696 val8 &= 0x7f;
6697 write_byte(0x0040, 0x003e, val8);
6699 // turn on motor of selected drive, DMA & int enabled, normal operation
6700 prev_reset = inb(0x03f2) & 0x04;
6701 if (drive)
6702 dor = 0x20;
6703 else
6704 dor = 0x10;
6705 dor |= 0x0c;
6706 dor |= drive;
6707 outb(0x03f2, dor);
6709 // reset the disk motor timeout value of INT 08
6710 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
6712 // wait for drive readiness
6713 do {
6714 val8 = inb(0x3f4);
6715 } while ( (val8 & 0xc0) != 0x80 );
6717 if (prev_reset == 0) {
6718 // turn on interrupts
6719 ASM_START
6721 ASM_END
6722 // wait on 40:3e bit 7 to become 1
6723 do {
6724 val8 = read_byte(0x0040, 0x003e);
6725 } while ( (val8 & 0x80) == 0 );
6726 val8 &= 0x7f;
6727 ASM_START
6729 ASM_END
6730 write_byte(0x0040, 0x003e, val8);
6734 bx_bool
6735 floppy_media_known(drive)
6736 Bit16u drive;
6738 Bit8u val8;
6739 Bit16u media_state_offset;
6741 val8 = read_byte(0x0040, 0x003e); // diskette recal status
6742 if (drive)
6743 val8 >>= 1;
6744 val8 &= 0x01;
6745 if (val8 == 0)
6746 return(0);
6748 media_state_offset = 0x0090;
6749 if (drive)
6750 media_state_offset += 1;
6752 val8 = read_byte(0x0040, media_state_offset);
6753 val8 = (val8 >> 4) & 0x01;
6754 if (val8 == 0)
6755 return(0);
6757 // check pass, return KNOWN
6758 return(1);
6761 bx_bool
6762 floppy_media_sense(drive)
6763 Bit16u drive;
6765 bx_bool retval;
6766 Bit16u media_state_offset;
6767 Bit8u drive_type, config_data, media_state;
6769 if (floppy_drive_recal(drive) == 0) {
6770 return(0);
6773 // for now cheat and get drive type from CMOS,
6774 // assume media is same as drive type
6776 // ** config_data **
6777 // Bitfields for diskette media control:
6778 // Bit(s) Description (Table M0028)
6779 // 7-6 last data rate set by controller
6780 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6781 // 5-4 last diskette drive step rate selected
6782 // 00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
6783 // 3-2 {data rate at start of operation}
6784 // 1-0 reserved
6786 // ** media_state **
6787 // Bitfields for diskette drive media state:
6788 // Bit(s) Description (Table M0030)
6789 // 7-6 data rate
6790 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6791 // 5 double stepping required (e.g. 360kB in 1.2MB)
6792 // 4 media type established
6793 // 3 drive capable of supporting 4MB media
6794 // 2-0 on exit from BIOS, contains
6795 // 000 trying 360kB in 360kB
6796 // 001 trying 360kB in 1.2MB
6797 // 010 trying 1.2MB in 1.2MB
6798 // 011 360kB in 360kB established
6799 // 100 360kB in 1.2MB established
6800 // 101 1.2MB in 1.2MB established
6801 // 110 reserved
6802 // 111 all other formats/drives
6804 drive_type = inb_cmos(0x10);
6805 if (drive == 0)
6806 drive_type >>= 4;
6807 else
6808 drive_type &= 0x0f;
6809 if ( drive_type == 1 ) {
6810 // 360K 5.25" drive
6811 config_data = 0x00; // 0000 0000
6812 media_state = 0x25; // 0010 0101
6813 retval = 1;
6815 else if ( drive_type == 2 ) {
6816 // 1.2 MB 5.25" drive
6817 config_data = 0x00; // 0000 0000
6818 media_state = 0x25; // 0010 0101 // need double stepping??? (bit 5)
6819 retval = 1;
6821 else if ( drive_type == 3 ) {
6822 // 720K 3.5" drive
6823 config_data = 0x00; // 0000 0000 ???
6824 media_state = 0x17; // 0001 0111
6825 retval = 1;
6827 else if ( drive_type == 4 ) {
6828 // 1.44 MB 3.5" drive
6829 config_data = 0x00; // 0000 0000
6830 media_state = 0x17; // 0001 0111
6831 retval = 1;
6833 else if ( drive_type == 5 ) {
6834 // 2.88 MB 3.5" drive
6835 config_data = 0xCC; // 1100 1100
6836 media_state = 0xD7; // 1101 0111
6837 retval = 1;
6840 // Extended floppy size uses special cmos setting
6841 else if ( drive_type == 6 ) {
6842 // 160k 5.25" drive
6843 config_data = 0x00; // 0000 0000
6844 media_state = 0x27; // 0010 0111
6845 retval = 1;
6847 else if ( drive_type == 7 ) {
6848 // 180k 5.25" drive
6849 config_data = 0x00; // 0000 0000
6850 media_state = 0x27; // 0010 0111
6851 retval = 1;
6853 else if ( drive_type == 8 ) {
6854 // 320k 5.25" drive
6855 config_data = 0x00; // 0000 0000
6856 media_state = 0x27; // 0010 0111
6857 retval = 1;
6860 else {
6861 // not recognized
6862 config_data = 0x00; // 0000 0000
6863 media_state = 0x00; // 0000 0000
6864 retval = 0;
6867 if (drive == 0)
6868 media_state_offset = 0x90;
6869 else
6870 media_state_offset = 0x91;
6871 write_byte(0x0040, 0x008B, config_data);
6872 write_byte(0x0040, media_state_offset, media_state);
6874 return(retval);
6877 bx_bool
6878 floppy_drive_recal(drive)
6879 Bit16u drive;
6881 Bit8u val8;
6882 Bit16u curr_cyl_offset;
6884 floppy_prepare_controller(drive);
6886 // send Recalibrate command (2 bytes) to controller
6887 outb(0x03f5, 0x07); // 07: Recalibrate
6888 outb(0x03f5, drive); // 0=drive0, 1=drive1
6890 // turn on interrupts
6891 ASM_START
6893 ASM_END
6895 // wait on 40:3e bit 7 to become 1
6896 do {
6897 val8 = (read_byte(0x0040, 0x003e) & 0x80);
6898 } while ( val8 == 0 );
6900 val8 = 0; // separate asm from while() loop
6901 // turn off interrupts
6902 ASM_START
6904 ASM_END
6906 // set 40:3e bit 7 to 0, and calibrated bit
6907 val8 = read_byte(0x0040, 0x003e);
6908 val8 &= 0x7f;
6909 if (drive) {
6910 val8 |= 0x02; // Drive 1 calibrated
6911 curr_cyl_offset = 0x0095;
6912 } else {
6913 val8 |= 0x01; // Drive 0 calibrated
6914 curr_cyl_offset = 0x0094;
6916 write_byte(0x0040, 0x003e, val8);
6917 write_byte(0x0040, curr_cyl_offset, 0); // current cylinder is 0
6919 return(1);
6924 bx_bool
6925 floppy_drive_exists(drive)
6926 Bit16u drive;
6928 Bit8u drive_type;
6930 // check CMOS to see if drive exists
6931 drive_type = inb_cmos(0x10);
6932 if (drive == 0)
6933 drive_type >>= 4;
6934 else
6935 drive_type &= 0x0f;
6936 if ( drive_type == 0 )
6937 return(0);
6938 else
6939 return(1);
6942 void
6943 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
6944 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
6946 Bit8u drive, num_sectors, track, sector, head, status;
6947 Bit16u base_address, base_count, base_es;
6948 Bit8u page, mode_register, val8, dor;
6949 Bit8u return_status[7];
6950 Bit8u drive_type, num_floppies, ah;
6951 Bit16u es, last_addr;
6953 BX_DEBUG_INT13_FL("int13_diskette: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
6955 ah = GET_AH();
6957 switch ( ah ) {
6958 case 0x00: // diskette controller reset
6959 BX_DEBUG_INT13_FL("floppy f00\n");
6960 drive = GET_ELDL();
6961 if (drive > 1) {
6962 SET_AH(1); // invalid param
6963 set_diskette_ret_status(1);
6964 SET_CF();
6965 return;
6967 drive_type = inb_cmos(0x10);
6969 if (drive == 0)
6970 drive_type >>= 4;
6971 else
6972 drive_type &= 0x0f;
6973 if (drive_type == 0) {
6974 SET_AH(0x80); // drive not responding
6975 set_diskette_ret_status(0x80);
6976 SET_CF();
6977 return;
6979 SET_AH(0);
6980 set_diskette_ret_status(0);
6981 CLEAR_CF(); // successful
6982 set_diskette_current_cyl(drive, 0); // current cylinder
6983 return;
6985 case 0x01: // Read Diskette Status
6986 CLEAR_CF();
6987 val8 = read_byte(0x0000, 0x0441);
6988 SET_AH(val8);
6989 if (val8) {
6990 SET_CF();
6992 return;
6994 case 0x02: // Read Diskette Sectors
6995 case 0x03: // Write Diskette Sectors
6996 case 0x04: // Verify Diskette Sectors
6997 num_sectors = GET_AL();
6998 track = GET_CH();
6999 sector = GET_CL();
7000 head = GET_DH();
7001 drive = GET_ELDL();
7003 if ((drive > 1) || (head > 1) || (sector == 0) ||
7004 (num_sectors == 0) || (num_sectors > 72)) {
7005 BX_INFO("int13_diskette: read/write/verify: parameter out of range\n");
7006 SET_AH(1);
7007 set_diskette_ret_status(1);
7008 SET_AL(0); // no sectors read
7009 SET_CF(); // error occurred
7010 return;
7013 // see if drive exists
7014 if (floppy_drive_exists(drive) == 0) {
7015 SET_AH(0x80); // not responding
7016 set_diskette_ret_status(0x80);
7017 SET_AL(0); // no sectors read
7018 SET_CF(); // error occurred
7019 return;
7022 // see if media in drive, and type is known
7023 if (floppy_media_known(drive) == 0) {
7024 if (floppy_media_sense(drive) == 0) {
7025 SET_AH(0x0C); // Media type not found
7026 set_diskette_ret_status(0x0C);
7027 SET_AL(0); // no sectors read
7028 SET_CF(); // error occurred
7029 return;
7033 if (ah == 0x02) {
7034 // Read Diskette Sectors
7036 //-----------------------------------
7037 // set up DMA controller for transfer
7038 //-----------------------------------
7040 // es:bx = pointer to where to place information from diskette
7041 // port 04: DMA-1 base and current address, channel 2
7042 // port 05: DMA-1 base and current count, channel 2
7043 page = (ES >> 12); // upper 4 bits
7044 base_es = (ES << 4); // lower 16bits contributed by ES
7045 base_address = base_es + BX; // lower 16 bits of address
7046 // contributed by ES:BX
7047 if ( base_address < base_es ) {
7048 // in case of carry, adjust page by 1
7049 page++;
7051 base_count = (num_sectors * 512) - 1;
7053 // check for 64K boundary overrun
7054 last_addr = base_address + base_count;
7055 if (last_addr < base_address) {
7056 SET_AH(0x09);
7057 set_diskette_ret_status(0x09);
7058 SET_AL(0); // no sectors read
7059 SET_CF(); // error occurred
7060 return;
7063 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
7064 outb(0x000a, 0x06);
7066 BX_DEBUG_INT13_FL("clear flip-flop\n");
7067 outb(0x000c, 0x00); // clear flip-flop
7068 outb(0x0004, base_address);
7069 outb(0x0004, base_address>>8);
7070 BX_DEBUG_INT13_FL("clear flip-flop\n");
7071 outb(0x000c, 0x00); // clear flip-flop
7072 outb(0x0005, base_count);
7073 outb(0x0005, base_count>>8);
7075 // port 0b: DMA-1 Mode Register
7076 mode_register = 0x46; // single mode, increment, autoinit disable,
7077 // transfer type=write, channel 2
7078 BX_DEBUG_INT13_FL("setting mode register\n");
7079 outb(0x000b, mode_register);
7081 BX_DEBUG_INT13_FL("setting page register\n");
7082 // port 81: DMA-1 Page Register, channel 2
7083 outb(0x0081, page);
7085 BX_DEBUG_INT13_FL("unmask chan 2\n");
7086 outb(0x000a, 0x02); // unmask channel 2
7088 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
7089 outb(0x000a, 0x02);
7091 //--------------------------------------
7092 // set up floppy controller for transfer
7093 //--------------------------------------
7094 floppy_prepare_controller(drive);
7096 // send read-normal-data command (9 bytes) to controller
7097 outb(0x03f5, 0xe6); // e6: read normal data
7098 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7099 outb(0x03f5, track);
7100 outb(0x03f5, head);
7101 outb(0x03f5, sector);
7102 outb(0x03f5, 2); // 512 byte sector size
7103 outb(0x03f5, sector + num_sectors - 1); // last sector to read on track
7104 outb(0x03f5, 0); // Gap length
7105 outb(0x03f5, 0xff); // Gap length
7107 // turn on interrupts
7108 ASM_START
7110 ASM_END
7112 // wait on 40:3e bit 7 to become 1
7113 do {
7114 val8 = read_byte(0x0040, 0x0040);
7115 if (val8 == 0) {
7116 floppy_reset_controller();
7117 SET_AH(0x80); // drive not ready (timeout)
7118 set_diskette_ret_status(0x80);
7119 SET_AL(0); // no sectors read
7120 SET_CF(); // error occurred
7121 return;
7123 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7124 } while ( val8 == 0 );
7126 val8 = 0; // separate asm from while() loop
7127 // turn off interrupts
7128 ASM_START
7130 ASM_END
7132 // set 40:3e bit 7 to 0
7133 val8 = read_byte(0x0040, 0x003e);
7134 val8 &= 0x7f;
7135 write_byte(0x0040, 0x003e, val8);
7137 // check port 3f4 for accessibility to status bytes
7138 val8 = inb(0x3f4);
7139 if ( (val8 & 0xc0) != 0xc0 )
7140 BX_PANIC("int13_diskette: ctrl not ready\n");
7142 // read 7 return status bytes from controller
7143 // using loop index broken, have to unroll...
7144 return_status[0] = inb(0x3f5);
7145 return_status[1] = inb(0x3f5);
7146 return_status[2] = inb(0x3f5);
7147 return_status[3] = inb(0x3f5);
7148 return_status[4] = inb(0x3f5);
7149 return_status[5] = inb(0x3f5);
7150 return_status[6] = inb(0x3f5);
7151 // record in BIOS Data Area
7152 write_byte(0x0040, 0x0042, return_status[0]);
7153 write_byte(0x0040, 0x0043, return_status[1]);
7154 write_byte(0x0040, 0x0044, return_status[2]);
7155 write_byte(0x0040, 0x0045, return_status[3]);
7156 write_byte(0x0040, 0x0046, return_status[4]);
7157 write_byte(0x0040, 0x0047, return_status[5]);
7158 write_byte(0x0040, 0x0048, return_status[6]);
7160 if ( (return_status[0] & 0xc0) != 0 ) {
7161 SET_AH(0x20);
7162 set_diskette_ret_status(0x20);
7163 SET_AL(0); // no sectors read
7164 SET_CF(); // error occurred
7165 return;
7168 // ??? should track be new val from return_status[3] ?
7169 set_diskette_current_cyl(drive, track);
7170 // AL = number of sectors read (same value as passed)
7171 SET_AH(0x00); // success
7172 CLEAR_CF(); // success
7173 return;
7174 } else if (ah == 0x03) {
7175 // Write Diskette Sectors
7177 //-----------------------------------
7178 // set up DMA controller for transfer
7179 //-----------------------------------
7181 // es:bx = pointer to where to place information from diskette
7182 // port 04: DMA-1 base and current address, channel 2
7183 // port 05: DMA-1 base and current count, channel 2
7184 page = (ES >> 12); // upper 4 bits
7185 base_es = (ES << 4); // lower 16bits contributed by ES
7186 base_address = base_es + BX; // lower 16 bits of address
7187 // contributed by ES:BX
7188 if ( base_address < base_es ) {
7189 // in case of carry, adjust page by 1
7190 page++;
7192 base_count = (num_sectors * 512) - 1;
7194 // check for 64K boundary overrun
7195 last_addr = base_address + base_count;
7196 if (last_addr < base_address) {
7197 SET_AH(0x09);
7198 set_diskette_ret_status(0x09);
7199 SET_AL(0); // no sectors read
7200 SET_CF(); // error occurred
7201 return;
7204 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
7205 outb(0x000a, 0x06);
7207 outb(0x000c, 0x00); // clear flip-flop
7208 outb(0x0004, base_address);
7209 outb(0x0004, base_address>>8);
7210 outb(0x000c, 0x00); // clear flip-flop
7211 outb(0x0005, base_count);
7212 outb(0x0005, base_count>>8);
7214 // port 0b: DMA-1 Mode Register
7215 mode_register = 0x4a; // single mode, increment, autoinit disable,
7216 // transfer type=read, channel 2
7217 outb(0x000b, mode_register);
7219 // port 81: DMA-1 Page Register, channel 2
7220 outb(0x0081, page);
7222 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
7223 outb(0x000a, 0x02);
7225 //--------------------------------------
7226 // set up floppy controller for transfer
7227 //--------------------------------------
7228 floppy_prepare_controller(drive);
7230 // send write-normal-data command (9 bytes) to controller
7231 outb(0x03f5, 0xc5); // c5: write normal data
7232 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7233 outb(0x03f5, track);
7234 outb(0x03f5, head);
7235 outb(0x03f5, sector);
7236 outb(0x03f5, 2); // 512 byte sector size
7237 outb(0x03f5, sector + num_sectors - 1); // last sector to write on track
7238 outb(0x03f5, 0); // Gap length
7239 outb(0x03f5, 0xff); // Gap length
7241 // turn on interrupts
7242 ASM_START
7244 ASM_END
7246 // wait on 40:3e bit 7 to become 1
7247 do {
7248 val8 = read_byte(0x0040, 0x0040);
7249 if (val8 == 0) {
7250 floppy_reset_controller();
7251 SET_AH(0x80); // drive not ready (timeout)
7252 set_diskette_ret_status(0x80);
7253 SET_AL(0); // no sectors written
7254 SET_CF(); // error occurred
7255 return;
7257 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7258 } while ( val8 == 0 );
7260 val8 = 0; // separate asm from while() loop
7261 // turn off interrupts
7262 ASM_START
7264 ASM_END
7266 // set 40:3e bit 7 to 0
7267 val8 = read_byte(0x0040, 0x003e);
7268 val8 &= 0x7f;
7269 write_byte(0x0040, 0x003e, val8);
7271 // check port 3f4 for accessibility to status bytes
7272 val8 = inb(0x3f4);
7273 if ( (val8 & 0xc0) != 0xc0 )
7274 BX_PANIC("int13_diskette: ctrl not ready\n");
7276 // read 7 return status bytes from controller
7277 // using loop index broken, have to unroll...
7278 return_status[0] = inb(0x3f5);
7279 return_status[1] = inb(0x3f5);
7280 return_status[2] = inb(0x3f5);
7281 return_status[3] = inb(0x3f5);
7282 return_status[4] = inb(0x3f5);
7283 return_status[5] = inb(0x3f5);
7284 return_status[6] = inb(0x3f5);
7285 // record in BIOS Data Area
7286 write_byte(0x0040, 0x0042, return_status[0]);
7287 write_byte(0x0040, 0x0043, return_status[1]);
7288 write_byte(0x0040, 0x0044, return_status[2]);
7289 write_byte(0x0040, 0x0045, return_status[3]);
7290 write_byte(0x0040, 0x0046, return_status[4]);
7291 write_byte(0x0040, 0x0047, return_status[5]);
7292 write_byte(0x0040, 0x0048, return_status[6]);
7294 if ( (return_status[0] & 0xc0) != 0 ) {
7295 if ( (return_status[1] & 0x02) != 0 ) {
7296 // diskette not writable.
7297 // AH=status code=0x03 (tried to write on write-protected disk)
7298 // AL=number of sectors written=0
7299 AX = 0x0300;
7300 SET_CF();
7301 return;
7302 } else {
7303 BX_PANIC("int13_diskette_function: read error\n");
7307 // ??? should track be new val from return_status[3] ?
7308 set_diskette_current_cyl(drive, track);
7309 // AL = number of sectors read (same value as passed)
7310 SET_AH(0x00); // success
7311 CLEAR_CF(); // success
7312 return;
7313 } else { // if (ah == 0x04)
7314 // Verify Diskette Sectors
7316 // ??? should track be new val from return_status[3] ?
7317 set_diskette_current_cyl(drive, track);
7318 // AL = number of sectors verified (same value as passed)
7319 CLEAR_CF(); // success
7320 SET_AH(0x00); // success
7321 return;
7323 break;
7325 case 0x05: // format diskette track
7326 BX_DEBUG_INT13_FL("floppy f05\n");
7328 num_sectors = GET_AL();
7329 track = GET_CH();
7330 head = GET_DH();
7331 drive = GET_ELDL();
7333 if ((drive > 1) || (head > 1) || (track > 79) ||
7334 (num_sectors == 0) || (num_sectors > 18)) {
7335 SET_AH(1);
7336 set_diskette_ret_status(1);
7337 SET_CF(); // error occurred
7340 // see if drive exists
7341 if (floppy_drive_exists(drive) == 0) {
7342 SET_AH(0x80); // drive not responding
7343 set_diskette_ret_status(0x80);
7344 SET_CF(); // error occurred
7345 return;
7348 // see if media in drive, and type is known
7349 if (floppy_media_known(drive) == 0) {
7350 if (floppy_media_sense(drive) == 0) {
7351 SET_AH(0x0C); // Media type not found
7352 set_diskette_ret_status(0x0C);
7353 SET_AL(0); // no sectors read
7354 SET_CF(); // error occurred
7355 return;
7359 // set up DMA controller for transfer
7360 page = (ES >> 12); // upper 4 bits
7361 base_es = (ES << 4); // lower 16bits contributed by ES
7362 base_address = base_es + BX; // lower 16 bits of address
7363 // contributed by ES:BX
7364 if ( base_address < base_es ) {
7365 // in case of carry, adjust page by 1
7366 page++;
7368 base_count = (num_sectors * 4) - 1;
7370 // check for 64K boundary overrun
7371 last_addr = base_address + base_count;
7372 if (last_addr < base_address) {
7373 SET_AH(0x09);
7374 set_diskette_ret_status(0x09);
7375 SET_AL(0); // no sectors read
7376 SET_CF(); // error occurred
7377 return;
7380 outb(0x000a, 0x06);
7381 outb(0x000c, 0x00); // clear flip-flop
7382 outb(0x0004, base_address);
7383 outb(0x0004, base_address>>8);
7384 outb(0x000c, 0x00); // clear flip-flop
7385 outb(0x0005, base_count);
7386 outb(0x0005, base_count>>8);
7387 mode_register = 0x4a; // single mode, increment, autoinit disable,
7388 // transfer type=read, channel 2
7389 outb(0x000b, mode_register);
7390 // port 81: DMA-1 Page Register, channel 2
7391 outb(0x0081, page);
7392 outb(0x000a, 0x02);
7394 // set up floppy controller for transfer
7395 floppy_prepare_controller(drive);
7397 // send format-track command (6 bytes) to controller
7398 outb(0x03f5, 0x4d); // 4d: format track
7399 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7400 outb(0x03f5, 2); // 512 byte sector size
7401 outb(0x03f5, num_sectors); // number of sectors per track
7402 outb(0x03f5, 0); // Gap length
7403 outb(0x03f5, 0xf6); // Fill byte
7404 // turn on interrupts
7405 ASM_START
7407 ASM_END
7409 // wait on 40:3e bit 7 to become 1
7410 do {
7411 val8 = read_byte(0x0040, 0x0040);
7412 if (val8 == 0) {
7413 floppy_reset_controller();
7414 SET_AH(0x80); // drive not ready (timeout)
7415 set_diskette_ret_status(0x80);
7416 SET_CF(); // error occurred
7417 return;
7419 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7420 } while ( val8 == 0 );
7422 val8 = 0; // separate asm from while() loop
7423 // turn off interrupts
7424 ASM_START
7426 ASM_END
7427 // set 40:3e bit 7 to 0
7428 val8 = read_byte(0x0040, 0x003e);
7429 val8 &= 0x7f;
7430 write_byte(0x0040, 0x003e, val8);
7431 // check port 3f4 for accessibility to status bytes
7432 val8 = inb(0x3f4);
7433 if ( (val8 & 0xc0) != 0xc0 )
7434 BX_PANIC("int13_diskette: ctrl not ready\n");
7436 // read 7 return status bytes from controller
7437 // using loop index broken, have to unroll...
7438 return_status[0] = inb(0x3f5);
7439 return_status[1] = inb(0x3f5);
7440 return_status[2] = inb(0x3f5);
7441 return_status[3] = inb(0x3f5);
7442 return_status[4] = inb(0x3f5);
7443 return_status[5] = inb(0x3f5);
7444 return_status[6] = inb(0x3f5);
7445 // record in BIOS Data Area
7446 write_byte(0x0040, 0x0042, return_status[0]);
7447 write_byte(0x0040, 0x0043, return_status[1]);
7448 write_byte(0x0040, 0x0044, return_status[2]);
7449 write_byte(0x0040, 0x0045, return_status[3]);
7450 write_byte(0x0040, 0x0046, return_status[4]);
7451 write_byte(0x0040, 0x0047, return_status[5]);
7452 write_byte(0x0040, 0x0048, return_status[6]);
7454 if ( (return_status[0] & 0xc0) != 0 ) {
7455 if ( (return_status[1] & 0x02) != 0 ) {
7456 // diskette not writable.
7457 // AH=status code=0x03 (tried to write on write-protected disk)
7458 // AL=number of sectors written=0
7459 AX = 0x0300;
7460 SET_CF();
7461 return;
7462 } else {
7463 BX_PANIC("int13_diskette_function: write error\n");
7467 SET_AH(0);
7468 set_diskette_ret_status(0);
7469 set_diskette_current_cyl(drive, 0);
7470 CLEAR_CF(); // successful
7471 return;
7474 case 0x08: // read diskette drive parameters
7475 BX_DEBUG_INT13_FL("floppy f08\n");
7476 drive = GET_ELDL();
7478 if (drive > 1) {
7479 AX = 0;
7480 BX = 0;
7481 CX = 0;
7482 DX = 0;
7483 ES = 0;
7484 DI = 0;
7485 SET_DL(num_floppies);
7486 SET_CF();
7487 return;
7490 drive_type = inb_cmos(0x10);
7491 num_floppies = 0;
7492 if (drive_type & 0xf0)
7493 num_floppies++;
7494 if (drive_type & 0x0f)
7495 num_floppies++;
7497 if (drive == 0)
7498 drive_type >>= 4;
7499 else
7500 drive_type &= 0x0f;
7502 SET_BH(0);
7503 SET_BL(drive_type);
7504 SET_AH(0);
7505 SET_AL(0);
7506 SET_DL(num_floppies);
7508 switch (drive_type) {
7509 case 0: // none
7510 CX = 0;
7511 SET_DH(0); // max head #
7512 break;
7514 case 1: // 360KB, 5.25"
7515 CX = 0x2709; // 40 tracks, 9 sectors
7516 SET_DH(1); // max head #
7517 break;
7519 case 2: // 1.2MB, 5.25"
7520 CX = 0x4f0f; // 80 tracks, 15 sectors
7521 SET_DH(1); // max head #
7522 break;
7524 case 3: // 720KB, 3.5"
7525 CX = 0x4f09; // 80 tracks, 9 sectors
7526 SET_DH(1); // max head #
7527 break;
7529 case 4: // 1.44MB, 3.5"
7530 CX = 0x4f12; // 80 tracks, 18 sectors
7531 SET_DH(1); // max head #
7532 break;
7534 case 5: // 2.88MB, 3.5"
7535 CX = 0x4f24; // 80 tracks, 36 sectors
7536 SET_DH(1); // max head #
7537 break;
7539 case 6: // 160k, 5.25"
7540 CX = 0x2708; // 40 tracks, 8 sectors
7541 SET_DH(0); // max head #
7542 break;
7544 case 7: // 180k, 5.25"
7545 CX = 0x2709; // 40 tracks, 9 sectors
7546 SET_DH(0); // max head #
7547 break;
7549 case 8: // 320k, 5.25"
7550 CX = 0x2708; // 40 tracks, 8 sectors
7551 SET_DH(1); // max head #
7552 break;
7554 default: // ?
7555 BX_PANIC("floppy: int13: bad floppy type\n");
7558 /* set es & di to point to 11 byte diskette param table in ROM */
7559 ASM_START
7560 push bp
7561 mov bp, sp
7562 mov ax, #diskette_param_table2
7563 mov _int13_diskette_function.DI+2[bp], ax
7564 mov _int13_diskette_function.ES+2[bp], cs
7565 pop bp
7566 ASM_END
7567 CLEAR_CF(); // success
7568 /* disk status not changed upon success */
7569 return;
7572 case 0x15: // read diskette drive type
7573 BX_DEBUG_INT13_FL("floppy f15\n");
7574 drive = GET_ELDL();
7575 if (drive > 1) {
7576 SET_AH(0); // only 2 drives supported
7577 // set_diskette_ret_status here ???
7578 SET_CF();
7579 return;
7581 drive_type = inb_cmos(0x10);
7583 if (drive == 0)
7584 drive_type >>= 4;
7585 else
7586 drive_type &= 0x0f;
7587 CLEAR_CF(); // successful, not present
7588 if (drive_type==0) {
7589 SET_AH(0); // drive not present
7591 else {
7592 SET_AH(1); // drive present, does not support change line
7595 return;
7597 case 0x16: // get diskette change line status
7598 BX_DEBUG_INT13_FL("floppy f16\n");
7599 drive = GET_ELDL();
7600 if (drive > 1) {
7601 SET_AH(0x01); // invalid drive
7602 set_diskette_ret_status(0x01);
7603 SET_CF();
7604 return;
7607 SET_AH(0x06); // change line not supported
7608 set_diskette_ret_status(0x06);
7609 SET_CF();
7610 return;
7612 case 0x17: // set diskette type for format(old)
7613 BX_DEBUG_INT13_FL("floppy f17\n");
7614 /* not used for 1.44M floppies */
7615 SET_AH(0x01); // not supported
7616 set_diskette_ret_status(1); /* not supported */
7617 SET_CF();
7618 return;
7620 case 0x18: // set diskette type for format(new)
7621 BX_DEBUG_INT13_FL("floppy f18\n");
7622 SET_AH(0x01); // do later
7623 set_diskette_ret_status(1);
7624 SET_CF();
7625 return;
7627 default:
7628 BX_INFO("int13_diskette: unsupported AH=%02x\n", GET_AH());
7630 // if ( (ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e) ) {
7631 SET_AH(0x01); // ???
7632 set_diskette_ret_status(1);
7633 SET_CF();
7634 return;
7635 // }
7638 #else // #if BX_SUPPORT_FLOPPY
7639 void
7640 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
7641 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
7643 Bit8u val8;
7645 switch ( GET_AH() ) {
7647 case 0x01: // Read Diskette Status
7648 CLEAR_CF();
7649 val8 = read_byte(0x0000, 0x0441);
7650 SET_AH(val8);
7651 if (val8) {
7652 SET_CF();
7654 return;
7656 default:
7657 SET_CF();
7658 write_byte(0x0000, 0x0441, 0x01);
7659 SET_AH(0x01);
7662 #endif // #if BX_SUPPORT_FLOPPY
7664 void
7665 set_diskette_ret_status(value)
7666 Bit8u value;
7668 write_byte(0x0040, 0x0041, value);
7671 void
7672 set_diskette_current_cyl(drive, cyl)
7673 Bit8u drive;
7674 Bit8u cyl;
7676 if (drive > 1)
7677 BX_PANIC("set_diskette_current_cyl(): drive > 1\n");
7678 write_byte(0x0040, 0x0094+drive, cyl);
7681 void
7682 determine_floppy_media(drive)
7683 Bit16u drive;
7685 #if 0
7686 Bit8u val8, DOR, ctrl_info;
7688 ctrl_info = read_byte(0x0040, 0x008F);
7689 if (drive==1)
7690 ctrl_info >>= 4;
7691 else
7692 ctrl_info &= 0x0f;
7694 #if 0
7695 if (drive == 0) {
7696 DOR = 0x1c; // DOR: drive0 motor on, DMA&int enabled, normal op, drive select 0
7698 else {
7699 DOR = 0x2d; // DOR: drive1 motor on, DMA&int enabled, normal op, drive select 1
7701 #endif
7703 if ( (ctrl_info & 0x04) != 0x04 ) {
7704 // Drive not determined means no drive exists, done.
7705 return;
7708 #if 0
7709 // check Main Status Register for readiness
7710 val8 = inb(0x03f4) & 0x80; // Main Status Register
7711 if (val8 != 0x80)
7712 BX_PANIC("d_f_m: MRQ bit not set\n");
7714 // change line
7716 // existing BDA values
7718 // turn on drive motor
7719 outb(0x03f2, DOR); // Digital Output Register
7721 #endif
7722 BX_PANIC("d_f_m: OK so far\n");
7723 #endif
7726 void
7727 int17_function(regs, ds, iret_addr)
7728 pusha_regs_t regs; // regs pushed from PUSHA instruction
7729 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7730 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7732 Bit16u addr,timeout;
7733 Bit8u val8;
7735 ASM_START
7737 ASM_END
7739 addr = read_word(0x0040, (regs.u.r16.dx << 1) + 8);
7740 if ((regs.u.r8.ah < 3) && (regs.u.r16.dx < 3) && (addr > 0)) {
7741 timeout = read_byte(0x0040, 0x0078 + regs.u.r16.dx) << 8;
7742 if (regs.u.r8.ah == 0) {
7743 outb(addr, regs.u.r8.al);
7744 val8 = inb(addr+2);
7745 outb(addr+2, val8 | 0x01); // send strobe
7746 ASM_START
7748 ASM_END
7749 outb(addr+2, val8 & ~0x01);
7750 while (((inb(addr+1) & 0x40) == 0x40) && (timeout)) {
7751 timeout--;
7754 if (regs.u.r8.ah == 1) {
7755 val8 = inb(addr+2);
7756 outb(addr+2, val8 & ~0x04); // send init
7757 ASM_START
7759 ASM_END
7760 outb(addr+2, val8 | 0x04);
7762 val8 = inb(addr+1);
7763 regs.u.r8.ah = (val8 ^ 0x48);
7764 if (!timeout) regs.u.r8.ah |= 0x01;
7765 ClearCF(iret_addr.flags);
7766 } else {
7767 SetCF(iret_addr.flags); // Unsupported
7771 void
7772 int19_function(seq_nr)
7773 Bit16u seq_nr;
7775 Bit16u ebda_seg=read_word(0x0040,0x000E);
7776 Bit16u bootdev;
7777 Bit8u bootdrv;
7778 Bit8u bootchk;
7779 Bit16u bootseg;
7780 Bit16u bootip;
7781 Bit16u status;
7783 ipl_entry_t e;
7785 // if BX_ELTORITO_BOOT is not defined, old behavior
7786 // check bit 5 in CMOS reg 0x2d. load either 0x00 or 0x80 into DL
7787 // in preparation for the intial INT 13h (0=floppy A:, 0x80=C:)
7788 // 0: system boot sequence, first drive C: then A:
7789 // 1: system boot sequence, first drive A: then C:
7790 // else BX_ELTORITO_BOOT is defined
7791 // CMOS regs 0x3D and 0x38 contain the boot sequence:
7792 // CMOS reg 0x3D & 0x0f : 1st boot device
7793 // CMOS reg 0x3D & 0xf0 : 2nd boot device
7794 // CMOS reg 0x38 & 0xf0 : 3rd boot device
7795 // boot device codes:
7796 // 0x00 : not defined
7797 // 0x01 : first floppy
7798 // 0x02 : first harddrive
7799 // 0x03 : first cdrom
7800 // 0x04 - 0x0f : PnP expansion ROMs (e.g. Etherboot)
7801 // else : boot failure
7803 // Get the boot sequence
7804 #if BX_ELTORITO_BOOT
7805 bootdev = inb_cmos(0x3d);
7806 bootdev |= ((inb_cmos(0x38) & 0xf0) << 4);
7807 bootdev >>= 4 * seq_nr;
7808 bootdev &= 0xf;
7809 if (bootdev == 0) BX_PANIC("No bootable device.\n");
7811 /* Translate from CMOS runes to an IPL table offset by subtracting 1 */
7812 bootdev -= 1;
7813 #else
7814 if (seq_nr ==2) BX_PANIC("No more boot devices.");
7815 if (!!(inb_cmos(0x2d) & 0x20) ^ (seq_nr == 1))
7816 /* Boot from floppy if the bit is set or it's the second boot */
7817 bootdev = 0x00;
7818 else
7819 bootdev = 0x01;
7820 #endif
7822 /* Read the boot device from the IPL table */
7823 if (get_boot_vector(bootdev, &e) == 0) {
7824 BX_INFO("Invalid boot device (0x%x)\n", bootdev);
7825 return;
7828 /* Do the loading, and set up vector as a far pointer to the boot
7829 * address, and bootdrv as the boot drive */
7830 print_boot_device(e.type);
7832 switch(e.type) {
7833 case IPL_TYPE_FLOPPY: /* FDD */
7834 case IPL_TYPE_HARDDISK: /* HDD */
7836 bootdrv = (e.type == IPL_TYPE_HARDDISK) ? 0x80 : 0x00;
7837 bootseg = 0x07c0;
7838 status = 0;
7840 ASM_START
7841 push bp
7842 mov bp, sp
7843 push ax
7844 push bx
7845 push cx
7846 push dx
7848 mov dl, _int19_function.bootdrv + 2[bp]
7849 mov ax, _int19_function.bootseg + 2[bp]
7850 mov es, ax ;; segment
7851 xor bx, bx ;; offset
7852 mov ah, #0x02 ;; function 2, read diskette sector
7853 mov al, #0x01 ;; read 1 sector
7854 mov ch, #0x00 ;; track 0
7855 mov cl, #0x01 ;; sector 1
7856 mov dh, #0x00 ;; head 0
7857 int #0x13 ;; read sector
7858 jnc int19_load_done
7859 mov ax, #0x0001
7860 mov _int19_function.status + 2[bp], ax
7862 int19_load_done:
7863 pop dx
7864 pop cx
7865 pop bx
7866 pop ax
7867 pop bp
7868 ASM_END
7870 if (status != 0) {
7871 print_boot_failure(e.type, 1);
7872 return;
7875 /* Always check the signature on a HDD boot sector; on FDD, only do
7876 * the check if the CMOS doesn't tell us to skip it */
7877 if ((e.type != IPL_TYPE_FLOPPY) || !((inb_cmos(0x38) & 0x01))) {
7878 if (read_word(bootseg,0x1fe) != 0xaa55) {
7879 print_boot_failure(e.type, 0);
7880 return;
7884 /* Canonicalize bootseg:bootip */
7885 bootip = (bootseg & 0x0fff) << 4;
7886 bootseg &= 0xf000;
7887 break;
7889 #if BX_ELTORITO_BOOT
7890 case IPL_TYPE_CDROM: /* CD-ROM */
7891 status = cdrom_boot();
7893 // If failure
7894 if ( (status & 0x00ff) !=0 ) {
7895 print_cdromboot_failure(status);
7896 print_boot_failure(e.type, 1);
7897 return;
7900 bootdrv = (Bit8u)(status>>8);
7901 bootseg = read_word(ebda_seg,&EbdaData->cdemu.load_segment);
7902 /* Canonicalize bootseg:bootip */
7903 bootip = (bootseg & 0x0fff) << 4;
7904 bootseg &= 0xf000;
7905 break;
7906 #endif
7908 case IPL_TYPE_BEV: /* Expansion ROM with a Bootstrap Entry Vector (a far pointer) */
7909 bootseg = e.vector >> 16;
7910 bootip = e.vector & 0xffff;
7911 break;
7913 default: return;
7916 /* Debugging info */
7917 BX_INFO("Booting from %x:%x\n", bootseg, bootip);
7919 /* Jump to the boot vector */
7920 ASM_START
7921 mov bp, sp
7922 ;; Build an iret stack frame that will take us to the boot vector.
7923 ;; iret pops ip, then cs, then flags, so push them in the opposite order.
7924 pushf
7925 mov ax, _int19_function.bootseg + 0[bp]
7926 push ax
7927 mov ax, _int19_function.bootip + 0[bp]
7928 push ax
7929 ;; Set the magic number in ax and the boot drive in dl.
7930 mov ax, #0xaa55
7931 mov dl, _int19_function.bootdrv + 0[bp]
7932 ;; Zero some of the other registers.
7933 xor bx, bx
7934 mov ds, bx
7935 mov es, bx
7936 mov bp, bx
7937 ;; Go!
7938 iret
7939 ASM_END
7942 void
7943 int1a_function(regs, ds, iret_addr)
7944 pusha_regs_t regs; // regs pushed from PUSHA instruction
7945 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7946 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7948 Bit8u val8;
7950 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);
7952 ASM_START
7954 ASM_END
7956 switch (regs.u.r8.ah) {
7957 case 0: // get current clock count
7958 ASM_START
7960 ASM_END
7961 regs.u.r16.cx = BiosData->ticks_high;
7962 regs.u.r16.dx = BiosData->ticks_low;
7963 regs.u.r8.al = BiosData->midnight_flag;
7964 BiosData->midnight_flag = 0; // reset flag
7965 ASM_START
7967 ASM_END
7968 // AH already 0
7969 ClearCF(iret_addr.flags); // OK
7970 break;
7972 case 1: // Set Current Clock Count
7973 ASM_START
7975 ASM_END
7976 BiosData->ticks_high = regs.u.r16.cx;
7977 BiosData->ticks_low = regs.u.r16.dx;
7978 BiosData->midnight_flag = 0; // reset flag
7979 ASM_START
7981 ASM_END
7982 regs.u.r8.ah = 0;
7983 ClearCF(iret_addr.flags); // OK
7984 break;
7987 case 2: // Read CMOS Time
7988 if (rtc_updating()) {
7989 SetCF(iret_addr.flags);
7990 break;
7993 regs.u.r8.dh = inb_cmos(0x00); // Seconds
7994 regs.u.r8.cl = inb_cmos(0x02); // Minutes
7995 regs.u.r8.ch = inb_cmos(0x04); // Hours
7996 regs.u.r8.dl = inb_cmos(0x0b) & 0x01; // Stat Reg B
7997 regs.u.r8.ah = 0;
7998 regs.u.r8.al = regs.u.r8.ch;
7999 ClearCF(iret_addr.flags); // OK
8000 break;
8002 case 3: // Set CMOS Time
8003 // Using a debugger, I notice the following masking/setting
8004 // of bits in Status Register B, by setting Reg B to
8005 // a few values and getting its value after INT 1A was called.
8007 // try#1 try#2 try#3
8008 // before 1111 1101 0111 1101 0000 0000
8009 // after 0110 0010 0110 0010 0000 0010
8011 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8012 // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
8013 if (rtc_updating()) {
8014 init_rtc();
8015 // fall through as if an update were not in progress
8017 outb_cmos(0x00, regs.u.r8.dh); // Seconds
8018 outb_cmos(0x02, regs.u.r8.cl); // Minutes
8019 outb_cmos(0x04, regs.u.r8.ch); // Hours
8020 // Set Daylight Savings time enabled bit to requested value
8021 val8 = (inb_cmos(0x0b) & 0x60) | 0x02 | (regs.u.r8.dl & 0x01);
8022 // (reg B already selected)
8023 outb_cmos(0x0b, val8);
8024 regs.u.r8.ah = 0;
8025 regs.u.r8.al = val8; // val last written to Reg B
8026 ClearCF(iret_addr.flags); // OK
8027 break;
8029 case 4: // Read CMOS Date
8030 regs.u.r8.ah = 0;
8031 if (rtc_updating()) {
8032 SetCF(iret_addr.flags);
8033 break;
8035 regs.u.r8.cl = inb_cmos(0x09); // Year
8036 regs.u.r8.dh = inb_cmos(0x08); // Month
8037 regs.u.r8.dl = inb_cmos(0x07); // Day of Month
8038 regs.u.r8.ch = inb_cmos(0x32); // Century
8039 regs.u.r8.al = regs.u.r8.ch;
8040 ClearCF(iret_addr.flags); // OK
8041 break;
8043 case 5: // Set CMOS Date
8044 // Using a debugger, I notice the following masking/setting
8045 // of bits in Status Register B, by setting Reg B to
8046 // a few values and getting its value after INT 1A was called.
8048 // try#1 try#2 try#3 try#4
8049 // before 1111 1101 0111 1101 0000 0010 0000 0000
8050 // after 0110 1101 0111 1101 0000 0010 0000 0000
8052 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8053 // My assumption: RegB = (RegB & 01111111b)
8054 if (rtc_updating()) {
8055 init_rtc();
8056 SetCF(iret_addr.flags);
8057 break;
8059 outb_cmos(0x09, regs.u.r8.cl); // Year
8060 outb_cmos(0x08, regs.u.r8.dh); // Month
8061 outb_cmos(0x07, regs.u.r8.dl); // Day of Month
8062 outb_cmos(0x32, regs.u.r8.ch); // Century
8063 val8 = inb_cmos(0x0b) & 0x7f; // clear halt-clock bit
8064 outb_cmos(0x0b, val8);
8065 regs.u.r8.ah = 0;
8066 regs.u.r8.al = val8; // AL = val last written to Reg B
8067 ClearCF(iret_addr.flags); // OK
8068 break;
8070 case 6: // Set Alarm Time in CMOS
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
8076 // before 1101 1111 0101 1111 0000 0000
8077 // after 0110 1111 0111 1111 0010 0000
8079 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8080 // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
8081 val8 = inb_cmos(0x0b); // Get Status Reg B
8082 regs.u.r16.ax = 0;
8083 if (val8 & 0x20) {
8084 // Alarm interrupt enabled already
8085 SetCF(iret_addr.flags); // Error: alarm in use
8086 break;
8088 if (rtc_updating()) {
8089 init_rtc();
8090 // fall through as if an update were not in progress
8092 outb_cmos(0x01, regs.u.r8.dh); // Seconds alarm
8093 outb_cmos(0x03, regs.u.r8.cl); // Minutes alarm
8094 outb_cmos(0x05, regs.u.r8.ch); // Hours alarm
8095 outb(0xa1, inb(0xa1) & 0xfe); // enable IRQ 8
8096 // enable Status Reg B alarm bit, clear halt clock bit
8097 outb_cmos(0x0b, (val8 & 0x7f) | 0x20);
8098 ClearCF(iret_addr.flags); // OK
8099 break;
8101 case 7: // Turn off Alarm
8102 // Using a debugger, I notice the following masking/setting
8103 // of bits in Status Register B, by setting Reg B to
8104 // a few values and getting its value after INT 1A was called.
8106 // try#1 try#2 try#3 try#4
8107 // before 1111 1101 0111 1101 0010 0000 0010 0010
8108 // after 0100 0101 0101 0101 0000 0000 0000 0010
8110 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8111 // My assumption: RegB = (RegB & 01010111b)
8112 val8 = inb_cmos(0x0b); // Get Status Reg B
8113 // clear clock-halt bit, disable alarm bit
8114 outb_cmos(0x0b, val8 & 0x57); // disable alarm bit
8115 regs.u.r8.ah = 0;
8116 regs.u.r8.al = val8; // val last written to Reg B
8117 ClearCF(iret_addr.flags); // OK
8118 break;
8119 #if BX_PCIBIOS
8120 case 0xb1:
8121 // real mode PCI BIOS functions now handled in assembler code
8122 // this C code handles the error code for information only
8123 if (regs.u.r8.bl == 0xff) {
8124 BX_INFO("PCI BIOS: PCI not present\n");
8125 } else if (regs.u.r8.bl == 0x81) {
8126 BX_INFO("unsupported PCI BIOS function 0x%02x\n", regs.u.r8.al);
8127 } else if (regs.u.r8.bl == 0x83) {
8128 BX_INFO("bad PCI vendor ID %04x\n", regs.u.r16.dx);
8129 } else if (regs.u.r8.bl == 0x86) {
8130 if (regs.u.r8.al == 0x02) {
8131 BX_INFO("PCI device %04x:%04x not found at index %d\n", regs.u.r16.dx, regs.u.r16.cx, regs.u.r16.si);
8132 } else {
8133 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);
8136 regs.u.r8.ah = regs.u.r8.bl;
8137 SetCF(iret_addr.flags);
8138 break;
8139 #endif
8141 default:
8142 SetCF(iret_addr.flags); // Unsupported
8146 void
8147 int70_function(regs, ds, iret_addr)
8148 pusha_regs_t regs; // regs pushed from PUSHA instruction
8149 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
8150 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
8152 // INT 70h: IRQ 8 - CMOS RTC interrupt from periodic or alarm modes
8153 Bit8u registerB = 0, registerC = 0;
8155 // Check which modes are enabled and have occurred.
8156 registerB = inb_cmos( 0xB );
8157 registerC = inb_cmos( 0xC );
8159 if( ( registerB & 0x60 ) != 0 ) {
8160 if( ( registerC & 0x20 ) != 0 ) {
8161 // Handle Alarm Interrupt.
8162 ASM_START
8164 int #0x4a
8166 ASM_END
8168 if( ( registerC & 0x40 ) != 0 ) {
8169 // Handle Periodic Interrupt.
8171 if( read_byte( 0x40, 0xA0 ) != 0 ) {
8172 // Wait Interval (Int 15, AH=83) active.
8173 Bit32u time, toggle;
8175 time = read_dword( 0x40, 0x9C ); // Time left in microseconds.
8176 if( time < 0x3D1 ) {
8177 // Done waiting.
8178 Bit16u segment, offset;
8180 segment = read_word( 0x40, 0x98 );
8181 offset = read_word( 0x40, 0x9A );
8182 write_byte( 0x40, 0xA0, 0 ); // Turn of status byte.
8183 outb_cmos( 0xB, registerB & 0x37 ); // Clear the Periodic Interrupt.
8184 write_byte(segment, offset, read_byte(segment, offset) | 0x80 ); // Write to specified flag byte.
8185 } else {
8186 // Continue waiting.
8187 time -= 0x3D1;
8188 write_dword( 0x40, 0x9C, time );
8194 ASM_START
8195 call eoi_both_pics
8196 ASM_END
8200 ASM_START
8201 ;------------------------------------------
8202 ;- INT74h : PS/2 mouse hardware interrupt -
8203 ;------------------------------------------
8204 int74_handler:
8206 pusha
8207 push ds ;; save DS
8208 push #0x00 ;; placeholder for status
8209 push #0x00 ;; placeholder for X
8210 push #0x00 ;; placeholder for Y
8211 push #0x00 ;; placeholder for Z
8212 push #0x00 ;; placeholder for make_far_call boolean
8213 call _int74_function
8214 pop cx ;; remove make_far_call from stack
8215 jcxz int74_done
8217 ;; make far call to EBDA:0022
8218 push #0x00
8219 pop ds
8220 push 0x040E ;; push 0000:040E (opcodes 0xff, 0x36, 0x0E, 0x04)
8221 pop ds
8222 //CALL_EP(0x0022) ;; call far routine (call_Ep DS:0022 :opcodes 0xff, 0x1e, 0x22, 0x00)
8223 call far ptr[0x22]
8224 int74_done:
8226 call eoi_both_pics
8227 add sp, #8 ;; pop status, x, y, z
8229 pop ds ;; restore DS
8230 popa
8231 iret
8234 ;; This will perform an IRET, but will retain value of current CF
8235 ;; by altering flags on stack. Better than RETF #02.
8236 iret_modify_cf:
8237 jc carry_set
8238 push bp
8239 mov bp, sp
8240 and BYTE [bp + 0x06], #0xfe
8241 pop bp
8242 iret
8243 carry_set:
8244 push bp
8245 mov bp, sp
8246 or BYTE [bp + 0x06], #0x01
8247 pop bp
8248 iret
8251 ;----------------------
8252 ;- INT13h (relocated) -
8253 ;----------------------
8255 ; int13_relocated is a little bit messed up since I played with it
8256 ; I have to rewrite it:
8257 ; - call a function that detect which function to call
8258 ; - make all called C function get the same parameters list
8260 int13_relocated:
8262 #if BX_ELTORITO_BOOT
8263 ;; check for an eltorito function
8264 cmp ah,#0x4a
8265 jb int13_not_eltorito
8266 cmp ah,#0x4d
8267 ja int13_not_eltorito
8269 pusha
8270 push es
8271 push ds
8272 push ss
8273 pop ds
8275 push #int13_out
8276 jmp _int13_eltorito ;; ELDX not used
8278 int13_not_eltorito:
8279 push ax
8280 push bx
8281 push cx
8282 push dx
8284 ;; check if emulation active
8285 call _cdemu_isactive
8286 cmp al,#0x00
8287 je int13_cdemu_inactive
8289 ;; check if access to the emulated drive
8290 call _cdemu_emulated_drive
8291 pop dx
8292 push dx
8293 cmp al,dl ;; int13 on emulated drive
8294 jne int13_nocdemu
8296 pop dx
8297 pop cx
8298 pop bx
8299 pop ax
8301 pusha
8302 push es
8303 push ds
8304 push ss
8305 pop ds
8307 push #int13_out
8308 jmp _int13_cdemu ;; ELDX not used
8310 int13_nocdemu:
8311 and dl,#0xE0 ;; mask to get device class, including cdroms
8312 cmp al,dl ;; al is 0x00 or 0x80
8313 jne int13_cdemu_inactive ;; inactive for device class
8315 pop dx
8316 pop cx
8317 pop bx
8318 pop ax
8320 push ax
8321 push cx
8322 push dx
8323 push bx
8325 dec dl ;; real drive is dl - 1
8326 jmp int13_legacy
8328 int13_cdemu_inactive:
8329 pop dx
8330 pop cx
8331 pop bx
8332 pop ax
8334 #endif // BX_ELTORITO_BOOT
8336 int13_noeltorito:
8338 push ax
8339 push cx
8340 push dx
8341 push bx
8343 int13_legacy:
8345 push dx ;; push eltorito value of dx instead of sp
8347 push bp
8348 push si
8349 push di
8351 push es
8352 push ds
8353 push ss
8354 pop ds
8356 ;; now the 16-bit registers can be restored with:
8357 ;; pop ds; pop es; popa; iret
8358 ;; arguments passed to functions should be
8359 ;; DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS
8361 test dl, #0x80
8362 jnz int13_notfloppy
8364 push #int13_out
8365 jmp _int13_diskette_function
8367 int13_notfloppy:
8369 #if BX_USE_ATADRV
8371 cmp dl, #0xE0
8372 jb int13_notcdrom
8374 // ebx is modified: BSD 5.2.1 boot loader problem
8375 // someone should figure out which 32 bit register that actually are used
8377 shr ebx, #16
8378 push bx
8380 call _int13_cdrom
8382 pop bx
8383 shl ebx, #16
8385 jmp int13_out
8387 int13_notcdrom:
8389 #endif
8391 int13_disk:
8392 ;; int13_harddisk modifies high word of EAX
8393 shr eax, #16
8394 push ax
8395 call _int13_harddisk
8396 pop ax
8397 shl eax, #16
8399 int13_out:
8400 pop ds
8401 pop es
8402 popa
8403 iret
8405 ;----------
8406 ;- INT18h -
8407 ;----------
8408 int18_handler: ;; Boot Failure recovery: try the next device.
8410 ;; Reset SP and SS
8411 mov ax, #0xfffe
8412 mov sp, ax
8413 xor ax, ax
8414 mov ss, ax
8416 ;; Get the boot sequence number out of the IPL memory
8417 mov bx, #IPL_SEG
8418 mov ds, bx ;; Set segment
8419 mov bx, IPL_SEQUENCE_OFFSET ;; BX is now the sequence number
8420 inc bx ;; ++
8421 mov IPL_SEQUENCE_OFFSET, bx ;; Write it back
8422 mov ds, ax ;; and reset the segment to zero.
8424 ;; Carry on in the INT 19h handler, using the new sequence number
8425 push bx
8427 jmp int19_next_boot
8429 ;----------
8430 ;- INT19h -
8431 ;----------
8432 int19_relocated: ;; Boot function, relocated
8434 ;; int19 was beginning to be really complex, so now it
8435 ;; just calls a C function that does the work
8437 push bp
8438 mov bp, sp
8440 ;; Reset SS and SP
8441 mov ax, #0xfffe
8442 mov sp, ax
8443 xor ax, ax
8444 mov ss, ax
8446 ;; Start from the first boot device (0, in AX)
8447 mov bx, #IPL_SEG
8448 mov ds, bx ;; Set segment to write to the IPL memory
8449 mov IPL_SEQUENCE_OFFSET, ax ;; Save the sequence number
8450 mov ds, ax ;; and reset the segment.
8452 push ax
8454 int19_next_boot:
8456 ;; Call the C code for the next boot device
8457 call _int19_function
8459 ;; Boot failed: invoke the boot recovery function
8460 int #0x18
8462 ;----------
8463 ;- INT1Ch -
8464 ;----------
8465 int1c_handler: ;; User Timer Tick
8466 iret
8469 ;----------------------
8470 ;- POST: Floppy Drive -
8471 ;----------------------
8472 floppy_drive_post:
8473 xor ax, ax
8474 mov ds, ax
8476 mov al, #0x00
8477 mov 0x043e, al ;; drive 0 & 1 uncalibrated, no interrupt has occurred
8479 mov 0x043f, al ;; diskette motor status: read op, drive0, motors off
8481 mov 0x0440, al ;; diskette motor timeout counter: not active
8482 mov 0x0441, al ;; diskette controller status return code
8484 mov 0x0442, al ;; disk & diskette controller status register 0
8485 mov 0x0443, al ;; diskette controller status register 1
8486 mov 0x0444, al ;; diskette controller status register 2
8487 mov 0x0445, al ;; diskette controller cylinder number
8488 mov 0x0446, al ;; diskette controller head number
8489 mov 0x0447, al ;; diskette controller sector number
8490 mov 0x0448, al ;; diskette controller bytes written
8492 mov 0x048b, al ;; diskette configuration data
8494 ;; -----------------------------------------------------------------
8495 ;; (048F) diskette controller information
8497 mov al, #0x10 ;; get CMOS diskette drive type
8498 out 0x70, AL
8499 in AL, 0x71
8500 mov ah, al ;; save byte to AH
8502 look_drive0:
8503 shr al, #4 ;; look at top 4 bits for drive 0
8504 jz f0_missing ;; jump if no drive0
8505 mov bl, #0x07 ;; drive0 determined, multi-rate, has changed line
8506 jmp look_drive1
8507 f0_missing:
8508 mov bl, #0x00 ;; no drive0
8510 look_drive1:
8511 mov al, ah ;; restore from AH
8512 and al, #0x0f ;; look at bottom 4 bits for drive 1
8513 jz f1_missing ;; jump if no drive1
8514 or bl, #0x70 ;; drive1 determined, multi-rate, has changed line
8515 f1_missing:
8516 ;; leave high bits in BL zerod
8517 mov 0x048f, bl ;; put new val in BDA (diskette controller information)
8518 ;; -----------------------------------------------------------------
8520 mov al, #0x00
8521 mov 0x0490, al ;; diskette 0 media state
8522 mov 0x0491, al ;; diskette 1 media state
8524 ;; diskette 0,1 operational starting state
8525 ;; drive type has not been determined,
8526 ;; has no changed detection line
8527 mov 0x0492, al
8528 mov 0x0493, al
8530 mov 0x0494, al ;; diskette 0 current cylinder
8531 mov 0x0495, al ;; diskette 1 current cylinder
8533 mov al, #0x02
8534 out #0x0a, al ;; clear DMA-1 channel 2 mask bit
8536 SET_INT_VECTOR(0x1E, #0xF000, #diskette_param_table2)
8537 SET_INT_VECTOR(0x40, #0xF000, #int13_diskette)
8538 SET_INT_VECTOR(0x0E, #0xF000, #int0e_handler) ;; IRQ 6
8543 ;--------------------
8544 ;- POST: HARD DRIVE -
8545 ;--------------------
8546 ; relocated here because the primary POST area isnt big enough.
8547 hard_drive_post:
8548 // IRQ 14 = INT 76h
8549 // INT 76h calls INT 15h function ax=9100
8551 mov al, #0x0a ; 0000 1010 = reserved, disable IRQ 14
8552 mov dx, #0x03f6
8553 out dx, al
8555 xor ax, ax
8556 mov ds, ax
8557 mov 0x0474, al /* hard disk status of last operation */
8558 mov 0x0477, al /* hard disk port offset (XT only ???) */
8559 mov 0x048c, al /* hard disk status register */
8560 mov 0x048d, al /* hard disk error register */
8561 mov 0x048e, al /* hard disk task complete flag */
8562 mov al, #0x01
8563 mov 0x0475, al /* hard disk number attached */
8564 mov al, #0xc0
8565 mov 0x0476, al /* hard disk control byte */
8566 SET_INT_VECTOR(0x13, #0xF000, #int13_handler)
8567 SET_INT_VECTOR(0x76, #0xF000, #int76_handler)
8568 ;; INT 41h: hard disk 0 configuration pointer
8569 ;; INT 46h: hard disk 1 configuration pointer
8570 SET_INT_VECTOR(0x41, #EBDA_SEG, #0x003D)
8571 SET_INT_VECTOR(0x46, #EBDA_SEG, #0x004D)
8573 ;; move disk geometry data from CMOS to EBDA disk parameter table(s)
8574 mov al, #0x12
8575 out #0x70, al
8576 in al, #0x71
8577 and al, #0xf0
8578 cmp al, #0xf0
8579 je post_d0_extended
8580 jmp check_for_hd1
8581 post_d0_extended:
8582 mov al, #0x19
8583 out #0x70, al
8584 in al, #0x71
8585 cmp al, #47 ;; decimal 47 - user definable
8586 je post_d0_type47
8587 HALT(__LINE__)
8588 post_d0_type47:
8589 ;; CMOS purpose param table offset
8590 ;; 1b cylinders low 0
8591 ;; 1c cylinders high 1
8592 ;; 1d heads 2
8593 ;; 1e write pre-comp low 5
8594 ;; 1f write pre-comp high 6
8595 ;; 20 retries/bad map/heads>8 8
8596 ;; 21 landing zone low C
8597 ;; 22 landing zone high D
8598 ;; 23 sectors/track E
8600 mov ax, #EBDA_SEG
8601 mov ds, ax
8603 ;;; Filling EBDA table for hard disk 0.
8604 mov al, #0x1f
8605 out #0x70, al
8606 in al, #0x71
8607 mov ah, al
8608 mov al, #0x1e
8609 out #0x70, al
8610 in al, #0x71
8611 mov (0x003d + 0x05), ax ;; write precomp word
8613 mov al, #0x20
8614 out #0x70, al
8615 in al, #0x71
8616 mov (0x003d + 0x08), al ;; drive control byte
8618 mov al, #0x22
8619 out #0x70, al
8620 in al, #0x71
8621 mov ah, al
8622 mov al, #0x21
8623 out #0x70, al
8624 in al, #0x71
8625 mov (0x003d + 0x0C), ax ;; landing zone word
8627 mov al, #0x1c ;; get cylinders word in AX
8628 out #0x70, al
8629 in al, #0x71 ;; high byte
8630 mov ah, al
8631 mov al, #0x1b
8632 out #0x70, al
8633 in al, #0x71 ;; low byte
8634 mov bx, ax ;; BX = cylinders
8636 mov al, #0x1d
8637 out #0x70, al
8638 in al, #0x71
8639 mov cl, al ;; CL = heads
8641 mov al, #0x23
8642 out #0x70, al
8643 in al, #0x71
8644 mov dl, al ;; DL = sectors
8646 cmp bx, #1024
8647 jnbe hd0_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8649 hd0_post_physical_chs:
8650 ;; no logical CHS mapping used, just physical CHS
8651 ;; use Standard Fixed Disk Parameter Table (FDPT)
8652 mov (0x003d + 0x00), bx ;; number of physical cylinders
8653 mov (0x003d + 0x02), cl ;; number of physical heads
8654 mov (0x003d + 0x0E), dl ;; number of physical sectors
8655 jmp check_for_hd1
8657 hd0_post_logical_chs:
8658 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8659 mov (0x003d + 0x09), bx ;; number of physical cylinders
8660 mov (0x003d + 0x0b), cl ;; number of physical heads
8661 mov (0x003d + 0x04), dl ;; number of physical sectors
8662 mov (0x003d + 0x0e), dl ;; number of logical sectors (same)
8663 mov al, #0xa0
8664 mov (0x003d + 0x03), al ;; A0h signature, indicates translated table
8666 cmp bx, #2048
8667 jnbe hd0_post_above_2048
8668 ;; 1024 < c <= 2048 cylinders
8669 shr bx, #0x01
8670 shl cl, #0x01
8671 jmp hd0_post_store_logical
8673 hd0_post_above_2048:
8674 cmp bx, #4096
8675 jnbe hd0_post_above_4096
8676 ;; 2048 < c <= 4096 cylinders
8677 shr bx, #0x02
8678 shl cl, #0x02
8679 jmp hd0_post_store_logical
8681 hd0_post_above_4096:
8682 cmp bx, #8192
8683 jnbe hd0_post_above_8192
8684 ;; 4096 < c <= 8192 cylinders
8685 shr bx, #0x03
8686 shl cl, #0x03
8687 jmp hd0_post_store_logical
8689 hd0_post_above_8192:
8690 ;; 8192 < c <= 16384 cylinders
8691 shr bx, #0x04
8692 shl cl, #0x04
8694 hd0_post_store_logical:
8695 mov (0x003d + 0x00), bx ;; number of physical cylinders
8696 mov (0x003d + 0x02), cl ;; number of physical heads
8697 ;; checksum
8698 mov cl, #0x0f ;; repeat count
8699 mov si, #0x003d ;; offset to disk0 FDPT
8700 mov al, #0x00 ;; sum
8701 hd0_post_checksum_loop:
8702 add al, [si]
8703 inc si
8704 dec cl
8705 jnz hd0_post_checksum_loop
8706 not al ;; now take 2s complement
8707 inc al
8708 mov [si], al
8709 ;;; Done filling EBDA table for hard disk 0.
8712 check_for_hd1:
8713 ;; is there really a second hard disk? if not, return now
8714 mov al, #0x12
8715 out #0x70, al
8716 in al, #0x71
8717 and al, #0x0f
8718 jnz post_d1_exists
8720 post_d1_exists:
8721 ;; check that the hd type is really 0x0f.
8722 cmp al, #0x0f
8723 jz post_d1_extended
8724 HALT(__LINE__)
8725 post_d1_extended:
8726 ;; check that the extended type is 47 - user definable
8727 mov al, #0x1a
8728 out #0x70, al
8729 in al, #0x71
8730 cmp al, #47 ;; decimal 47 - user definable
8731 je post_d1_type47
8732 HALT(__LINE__)
8733 post_d1_type47:
8734 ;; Table for disk1.
8735 ;; CMOS purpose param table offset
8736 ;; 0x24 cylinders low 0
8737 ;; 0x25 cylinders high 1
8738 ;; 0x26 heads 2
8739 ;; 0x27 write pre-comp low 5
8740 ;; 0x28 write pre-comp high 6
8741 ;; 0x29 heads>8 8
8742 ;; 0x2a landing zone low C
8743 ;; 0x2b landing zone high D
8744 ;; 0x2c sectors/track E
8745 ;;; Fill EBDA table for hard disk 1.
8746 mov ax, #EBDA_SEG
8747 mov ds, ax
8748 mov al, #0x28
8749 out #0x70, al
8750 in al, #0x71
8751 mov ah, al
8752 mov al, #0x27
8753 out #0x70, al
8754 in al, #0x71
8755 mov (0x004d + 0x05), ax ;; write precomp word
8757 mov al, #0x29
8758 out #0x70, al
8759 in al, #0x71
8760 mov (0x004d + 0x08), al ;; drive control byte
8762 mov al, #0x2b
8763 out #0x70, al
8764 in al, #0x71
8765 mov ah, al
8766 mov al, #0x2a
8767 out #0x70, al
8768 in al, #0x71
8769 mov (0x004d + 0x0C), ax ;; landing zone word
8771 mov al, #0x25 ;; get cylinders word in AX
8772 out #0x70, al
8773 in al, #0x71 ;; high byte
8774 mov ah, al
8775 mov al, #0x24
8776 out #0x70, al
8777 in al, #0x71 ;; low byte
8778 mov bx, ax ;; BX = cylinders
8780 mov al, #0x26
8781 out #0x70, al
8782 in al, #0x71
8783 mov cl, al ;; CL = heads
8785 mov al, #0x2c
8786 out #0x70, al
8787 in al, #0x71
8788 mov dl, al ;; DL = sectors
8790 cmp bx, #1024
8791 jnbe hd1_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8793 hd1_post_physical_chs:
8794 ;; no logical CHS mapping used, just physical CHS
8795 ;; use Standard Fixed Disk Parameter Table (FDPT)
8796 mov (0x004d + 0x00), bx ;; number of physical cylinders
8797 mov (0x004d + 0x02), cl ;; number of physical heads
8798 mov (0x004d + 0x0E), dl ;; number of physical sectors
8801 hd1_post_logical_chs:
8802 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8803 mov (0x004d + 0x09), bx ;; number of physical cylinders
8804 mov (0x004d + 0x0b), cl ;; number of physical heads
8805 mov (0x004d + 0x04), dl ;; number of physical sectors
8806 mov (0x004d + 0x0e), dl ;; number of logical sectors (same)
8807 mov al, #0xa0
8808 mov (0x004d + 0x03), al ;; A0h signature, indicates translated table
8810 cmp bx, #2048
8811 jnbe hd1_post_above_2048
8812 ;; 1024 < c <= 2048 cylinders
8813 shr bx, #0x01
8814 shl cl, #0x01
8815 jmp hd1_post_store_logical
8817 hd1_post_above_2048:
8818 cmp bx, #4096
8819 jnbe hd1_post_above_4096
8820 ;; 2048 < c <= 4096 cylinders
8821 shr bx, #0x02
8822 shl cl, #0x02
8823 jmp hd1_post_store_logical
8825 hd1_post_above_4096:
8826 cmp bx, #8192
8827 jnbe hd1_post_above_8192
8828 ;; 4096 < c <= 8192 cylinders
8829 shr bx, #0x03
8830 shl cl, #0x03
8831 jmp hd1_post_store_logical
8833 hd1_post_above_8192:
8834 ;; 8192 < c <= 16384 cylinders
8835 shr bx, #0x04
8836 shl cl, #0x04
8838 hd1_post_store_logical:
8839 mov (0x004d + 0x00), bx ;; number of physical cylinders
8840 mov (0x004d + 0x02), cl ;; number of physical heads
8841 ;; checksum
8842 mov cl, #0x0f ;; repeat count
8843 mov si, #0x004d ;; offset to disk0 FDPT
8844 mov al, #0x00 ;; sum
8845 hd1_post_checksum_loop:
8846 add al, [si]
8847 inc si
8848 dec cl
8849 jnz hd1_post_checksum_loop
8850 not al ;; now take 2s complement
8851 inc al
8852 mov [si], al
8853 ;;; Done filling EBDA table for hard disk 1.
8857 ;--------------------
8858 ;- POST: EBDA segment
8859 ;--------------------
8860 ; relocated here because the primary POST area isnt big enough.
8861 ebda_post:
8862 #if BX_USE_EBDA
8863 mov ax, #EBDA_SEG
8864 mov ds, ax
8865 mov byte ptr [0x0], #EBDA_SIZE
8866 #endif
8867 xor ax, ax ; mov EBDA seg into 40E
8868 mov ds, ax
8869 mov word ptr [0x40E], #EBDA_SEG
8870 ret;;
8872 ;--------------------
8873 ;- POST: EOI + jmp via [0x40:67)
8874 ;--------------------
8875 ; relocated here because the primary POST area isnt big enough.
8876 eoi_jmp_post:
8877 call eoi_both_pics
8879 xor ax, ax
8880 mov ds, ax
8882 jmp far ptr [0x467]
8885 ;--------------------
8886 eoi_both_pics:
8887 mov al, #0x20
8888 out #0xA0, al ;; slave PIC EOI
8889 eoi_master_pic:
8890 mov al, #0x20
8891 out #0x20, al ;; master PIC EOI
8894 ;--------------------
8895 BcdToBin:
8896 ;; in: AL in BCD format
8897 ;; out: AL in binary format, AH will always be 0
8898 ;; trashes BX
8899 mov bl, al
8900 and bl, #0x0f ;; bl has low digit
8901 shr al, #4 ;; al has high digit
8902 mov bh, #10
8903 mul al, bh ;; multiply high digit by 10 (result in AX)
8904 add al, bl ;; then add low digit
8907 ;--------------------
8908 timer_tick_post:
8909 ;; Setup the Timer Ticks Count (0x46C:dword) and
8910 ;; Timer Ticks Roller Flag (0x470:byte)
8911 ;; The Timer Ticks Count needs to be set according to
8912 ;; the current CMOS time, as if ticks have been occurring
8913 ;; at 18.2hz since midnight up to this point. Calculating
8914 ;; this is a little complicated. Here are the factors I gather
8915 ;; regarding this. 14,318,180 hz was the original clock speed,
8916 ;; chosen so it could be divided by either 3 to drive the 5Mhz CPU
8917 ;; at the time, or 4 to drive the CGA video adapter. The div3
8918 ;; source was divided again by 4 to feed a 1.193Mhz signal to
8919 ;; the timer. With a maximum 16bit timer count, this is again
8920 ;; divided down by 65536 to 18.2hz.
8922 ;; 14,318,180 Hz clock
8923 ;; /3 = 4,772,726 Hz fed to orginal 5Mhz CPU
8924 ;; /4 = 1,193,181 Hz fed to timer
8925 ;; /65536 (maximum timer count) = 18.20650736 ticks/second
8926 ;; 1 second = 18.20650736 ticks
8927 ;; 1 minute = 1092.390442 ticks
8928 ;; 1 hour = 65543.42651 ticks
8930 ;; Given the values in the CMOS clock, one could calculate
8931 ;; the number of ticks by the following:
8932 ;; ticks = (BcdToBin(seconds) * 18.206507) +
8933 ;; (BcdToBin(minutes) * 1092.3904)
8934 ;; (BcdToBin(hours) * 65543.427)
8935 ;; To get a little more accuracy, since Im using integer
8936 ;; arithmatic, I use:
8937 ;; ticks = (BcdToBin(seconds) * 18206507) / 1000000 +
8938 ;; (BcdToBin(minutes) * 10923904) / 10000 +
8939 ;; (BcdToBin(hours) * 65543427) / 1000
8941 ;; assuming DS=0000
8943 ;; get CMOS seconds
8944 xor eax, eax ;; clear EAX
8945 mov al, #0x00
8946 out #0x70, al
8947 in al, #0x71 ;; AL has CMOS seconds in BCD
8948 call BcdToBin ;; EAX now has seconds in binary
8949 mov edx, #18206507
8950 mul eax, edx
8951 mov ebx, #1000000
8952 xor edx, edx
8953 div eax, ebx
8954 mov ecx, eax ;; ECX will accumulate total ticks
8956 ;; get CMOS minutes
8957 xor eax, eax ;; clear EAX
8958 mov al, #0x02
8959 out #0x70, al
8960 in al, #0x71 ;; AL has CMOS minutes in BCD
8961 call BcdToBin ;; EAX now has minutes in binary
8962 mov edx, #10923904
8963 mul eax, edx
8964 mov ebx, #10000
8965 xor edx, edx
8966 div eax, ebx
8967 add ecx, eax ;; add to total ticks
8969 ;; get CMOS hours
8970 xor eax, eax ;; clear EAX
8971 mov al, #0x04
8972 out #0x70, al
8973 in al, #0x71 ;; AL has CMOS hours in BCD
8974 call BcdToBin ;; EAX now has hours in binary
8975 mov edx, #65543427
8976 mul eax, edx
8977 mov ebx, #1000
8978 xor edx, edx
8979 div eax, ebx
8980 add ecx, eax ;; add to total ticks
8982 mov 0x46C, ecx ;; Timer Ticks Count
8983 xor al, al
8984 mov 0x470, al ;; Timer Ticks Rollover Flag
8987 ;--------------------
8988 int76_handler:
8989 ;; record completion in BIOS task complete flag
8990 push ax
8991 push ds
8992 mov ax, #0x0040
8993 mov ds, ax
8994 mov 0x008E, #0xff
8995 call eoi_both_pics
8996 pop ds
8997 pop ax
8998 iret
9001 ;--------------------
9002 #if BX_APM
9004 use32 386
9005 #define APM_PROT32
9006 #include "apmbios.S"
9008 use16 386
9009 #define APM_PROT16
9010 #include "apmbios.S"
9012 #define APM_REAL
9013 #include "apmbios.S"
9015 #endif
9017 ;--------------------
9018 #if BX_PCIBIOS
9019 use32 386
9020 .align 16
9021 bios32_structure:
9022 db 0x5f, 0x33, 0x32, 0x5f ;; "_32_" signature
9023 dw bios32_entry_point, 0xf ;; 32 bit physical address
9024 db 0 ;; revision level
9025 ;; length in paragraphs and checksum stored in a word to prevent errors
9026 dw (~(((bios32_entry_point >> 8) + (bios32_entry_point & 0xff) + 0x32) \
9027 & 0xff) << 8) + 0x01
9028 db 0,0,0,0,0 ;; reserved
9030 .align 16
9031 bios32_entry_point:
9032 pushfd
9033 cmp eax, #0x49435024 ;; "$PCI"
9034 jne unknown_service
9035 mov eax, #0x80000000
9036 mov dx, #0x0cf8
9037 out dx, eax
9038 mov dx, #0x0cfc
9039 in eax, dx
9040 #ifdef PCI_FIXED_HOST_BRIDGE
9041 cmp eax, #PCI_FIXED_HOST_BRIDGE
9042 jne unknown_service
9043 #else
9044 ;; say ok if a device is present
9045 cmp eax, #0xffffffff
9046 je unknown_service
9047 #endif
9048 mov ebx, #0x000f0000
9049 mov ecx, #0
9050 mov edx, #pcibios_protected
9051 xor al, al
9052 jmp bios32_end
9053 unknown_service:
9054 mov al, #0x80
9055 bios32_end:
9056 #ifdef BX_QEMU
9057 and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
9058 #endif
9059 popfd
9060 retf
9062 .align 16
9063 pcibios_protected:
9064 pushfd
9066 push esi
9067 push edi
9068 cmp al, #0x01 ;; installation check
9069 jne pci_pro_f02
9070 mov bx, #0x0210
9071 mov cx, #0
9072 mov edx, #0x20494350 ;; "PCI "
9073 mov al, #0x01
9074 jmp pci_pro_ok
9075 pci_pro_f02: ;; find pci device
9076 cmp al, #0x02
9077 jne pci_pro_f03
9078 shl ecx, #16
9079 mov cx, dx
9080 xor bx, bx
9081 mov di, #0x00
9082 pci_pro_devloop:
9083 call pci_pro_select_reg
9084 mov dx, #0x0cfc
9085 in eax, dx
9086 cmp eax, ecx
9087 jne pci_pro_nextdev
9088 cmp si, #0
9089 je pci_pro_ok
9090 dec si
9091 pci_pro_nextdev:
9092 inc bx
9093 cmp bx, #0x0100
9094 jne pci_pro_devloop
9095 mov ah, #0x86
9096 jmp pci_pro_fail
9097 pci_pro_f03: ;; find class code
9098 cmp al, #0x03
9099 jne pci_pro_f08
9100 xor bx, bx
9101 mov di, #0x08
9102 pci_pro_devloop2:
9103 call pci_pro_select_reg
9104 mov dx, #0x0cfc
9105 in eax, dx
9106 shr eax, #8
9107 cmp eax, ecx
9108 jne pci_pro_nextdev2
9109 cmp si, #0
9110 je pci_pro_ok
9111 dec si
9112 pci_pro_nextdev2:
9113 inc bx
9114 cmp bx, #0x0100
9115 jne pci_pro_devloop2
9116 mov ah, #0x86
9117 jmp pci_pro_fail
9118 pci_pro_f08: ;; read configuration byte
9119 cmp al, #0x08
9120 jne pci_pro_f09
9121 call pci_pro_select_reg
9122 push edx
9123 mov dx, di
9124 and dx, #0x03
9125 add dx, #0x0cfc
9126 in al, dx
9127 pop edx
9128 mov cl, al
9129 jmp pci_pro_ok
9130 pci_pro_f09: ;; read configuration word
9131 cmp al, #0x09
9132 jne pci_pro_f0a
9133 call pci_pro_select_reg
9134 push edx
9135 mov dx, di
9136 and dx, #0x02
9137 add dx, #0x0cfc
9138 in ax, dx
9139 pop edx
9140 mov cx, ax
9141 jmp pci_pro_ok
9142 pci_pro_f0a: ;; read configuration dword
9143 cmp al, #0x0a
9144 jne pci_pro_f0b
9145 call pci_pro_select_reg
9146 push edx
9147 mov dx, #0x0cfc
9148 in eax, dx
9149 pop edx
9150 mov ecx, eax
9151 jmp pci_pro_ok
9152 pci_pro_f0b: ;; write configuration byte
9153 cmp al, #0x0b
9154 jne pci_pro_f0c
9155 call pci_pro_select_reg
9156 push edx
9157 mov dx, di
9158 and dx, #0x03
9159 add dx, #0x0cfc
9160 mov al, cl
9161 out dx, al
9162 pop edx
9163 jmp pci_pro_ok
9164 pci_pro_f0c: ;; write configuration word
9165 cmp al, #0x0c
9166 jne pci_pro_f0d
9167 call pci_pro_select_reg
9168 push edx
9169 mov dx, di
9170 and dx, #0x02
9171 add dx, #0x0cfc
9172 mov ax, cx
9173 out dx, ax
9174 pop edx
9175 jmp pci_pro_ok
9176 pci_pro_f0d: ;; write configuration dword
9177 cmp al, #0x0d
9178 jne pci_pro_unknown
9179 call pci_pro_select_reg
9180 push edx
9181 mov dx, #0x0cfc
9182 mov eax, ecx
9183 out dx, eax
9184 pop edx
9185 jmp pci_pro_ok
9186 pci_pro_unknown:
9187 mov ah, #0x81
9188 pci_pro_fail:
9189 pop edi
9190 pop esi
9191 #ifdef BX_QEMU
9192 and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
9193 #endif
9194 popfd
9196 retf
9197 pci_pro_ok:
9198 xor ah, ah
9199 pop edi
9200 pop esi
9201 #ifdef BX_QEMU
9202 and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
9203 #endif
9204 popfd
9206 retf
9208 pci_pro_select_reg:
9209 push edx
9210 mov eax, #0x800000
9211 mov ax, bx
9212 shl eax, #8
9213 and di, #0xff
9214 or ax, di
9215 and al, #0xfc
9216 mov dx, #0x0cf8
9217 out dx, eax
9218 pop edx
9221 use16 386
9223 pcibios_real:
9224 push eax
9225 push dx
9226 mov eax, #0x80000000
9227 mov dx, #0x0cf8
9228 out dx, eax
9229 mov dx, #0x0cfc
9230 in eax, dx
9231 #ifdef PCI_FIXED_HOST_BRIDGE
9232 cmp eax, #PCI_FIXED_HOST_BRIDGE
9233 je pci_present
9234 #else
9235 ;; say ok if a device is present
9236 cmp eax, #0xffffffff
9237 jne pci_present
9238 #endif
9239 pop dx
9240 pop eax
9241 mov ah, #0xff
9244 pci_present:
9245 pop dx
9246 pop eax
9247 cmp al, #0x01 ;; installation check
9248 jne pci_real_f02
9249 mov ax, #0x0001
9250 mov bx, #0x0210
9251 mov cx, #0
9252 mov edx, #0x20494350 ;; "PCI "
9253 mov edi, #0xf0000
9254 mov di, #pcibios_protected
9257 pci_real_f02: ;; find pci device
9258 push esi
9259 push edi
9260 cmp al, #0x02
9261 jne pci_real_f03
9262 shl ecx, #16
9263 mov cx, dx
9264 xor bx, bx
9265 mov di, #0x00
9266 pci_real_devloop:
9267 call pci_real_select_reg
9268 mov dx, #0x0cfc
9269 in eax, dx
9270 cmp eax, ecx
9271 jne pci_real_nextdev
9272 cmp si, #0
9273 je pci_real_ok
9274 dec si
9275 pci_real_nextdev:
9276 inc bx
9277 cmp bx, #0x0100
9278 jne pci_real_devloop
9279 mov dx, cx
9280 shr ecx, #16
9281 mov ax, #0x8602
9282 jmp pci_real_fail
9283 pci_real_f03: ;; find class code
9284 cmp al, #0x03
9285 jne pci_real_f08
9286 xor bx, bx
9287 mov di, #0x08
9288 pci_real_devloop2:
9289 call pci_real_select_reg
9290 mov dx, #0x0cfc
9291 in eax, dx
9292 shr eax, #8
9293 cmp eax, ecx
9294 jne pci_real_nextdev2
9295 cmp si, #0
9296 je pci_real_ok
9297 dec si
9298 pci_real_nextdev2:
9299 inc bx
9300 cmp bx, #0x0100
9301 jne pci_real_devloop2
9302 mov dx, cx
9303 shr ecx, #16
9304 mov ax, #0x8603
9305 jmp pci_real_fail
9306 pci_real_f08: ;; read configuration byte
9307 cmp al, #0x08
9308 jne pci_real_f09
9309 call pci_real_select_reg
9310 push dx
9311 mov dx, di
9312 and dx, #0x03
9313 add dx, #0x0cfc
9314 in al, dx
9315 pop dx
9316 mov cl, al
9317 jmp pci_real_ok
9318 pci_real_f09: ;; read configuration word
9319 cmp al, #0x09
9320 jne pci_real_f0a
9321 call pci_real_select_reg
9322 push dx
9323 mov dx, di
9324 and dx, #0x02
9325 add dx, #0x0cfc
9326 in ax, dx
9327 pop dx
9328 mov cx, ax
9329 jmp pci_real_ok
9330 pci_real_f0a: ;; read configuration dword
9331 cmp al, #0x0a
9332 jne pci_real_f0b
9333 call pci_real_select_reg
9334 push dx
9335 mov dx, #0x0cfc
9336 in eax, dx
9337 pop dx
9338 mov ecx, eax
9339 jmp pci_real_ok
9340 pci_real_f0b: ;; write configuration byte
9341 cmp al, #0x0b
9342 jne pci_real_f0c
9343 call pci_real_select_reg
9344 push dx
9345 mov dx, di
9346 and dx, #0x03
9347 add dx, #0x0cfc
9348 mov al, cl
9349 out dx, al
9350 pop dx
9351 jmp pci_real_ok
9352 pci_real_f0c: ;; write configuration word
9353 cmp al, #0x0c
9354 jne pci_real_f0d
9355 call pci_real_select_reg
9356 push dx
9357 mov dx, di
9358 and dx, #0x02
9359 add dx, #0x0cfc
9360 mov ax, cx
9361 out dx, ax
9362 pop dx
9363 jmp pci_real_ok
9364 pci_real_f0d: ;; write configuration dword
9365 cmp al, #0x0d
9366 jne pci_real_f0e
9367 call pci_real_select_reg
9368 push dx
9369 mov dx, #0x0cfc
9370 mov eax, ecx
9371 out dx, eax
9372 pop dx
9373 jmp pci_real_ok
9374 pci_real_f0e: ;; get irq routing options
9375 cmp al, #0x0e
9376 jne pci_real_unknown
9377 SEG ES
9378 cmp word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9379 jb pci_real_too_small
9380 SEG ES
9381 mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9382 pushf
9383 push ds
9384 push es
9385 push cx
9386 push si
9387 push di
9389 mov si, #pci_routing_table_structure_start
9390 push cs
9391 pop ds
9392 SEG ES
9393 mov cx, [di+2]
9394 SEG ES
9395 mov es, [di+4]
9396 mov di, cx
9397 mov cx, #pci_routing_table_structure_end - pci_routing_table_structure_start
9399 movsb
9400 pop di
9401 pop si
9402 pop cx
9403 pop es
9404 pop ds
9405 popf
9406 mov bx, #(1 << 9) | (1 << 11) ;; irq 9 and 11 are used
9407 jmp pci_real_ok
9408 pci_real_too_small:
9409 SEG ES
9410 mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9411 mov ah, #0x89
9412 jmp pci_real_fail
9414 pci_real_unknown:
9415 mov ah, #0x81
9416 pci_real_fail:
9417 pop edi
9418 pop esi
9421 pci_real_ok:
9422 xor ah, ah
9423 pop edi
9424 pop esi
9428 pci_real_select_reg:
9429 push dx
9430 mov eax, #0x800000
9431 mov ax, bx
9432 shl eax, #8
9433 and di, #0xff
9434 or ax, di
9435 and al, #0xfc
9436 mov dx, #0x0cf8
9437 out dx, eax
9438 pop dx
9441 .align 16
9442 pci_routing_table_structure:
9443 db 0x24, 0x50, 0x49, 0x52 ;; "$PIR" signature
9444 db 0, 1 ;; version
9445 dw 32 + (6 * 16) ;; table size
9446 db 0 ;; PCI interrupt router bus
9447 db 0x08 ;; PCI interrupt router DevFunc
9448 dw 0x0000 ;; PCI exclusive IRQs
9449 dw 0x8086 ;; compatible PCI interrupt router vendor ID
9450 dw 0x7000 ;; compatible PCI interrupt router device ID
9451 dw 0,0 ;; Miniport data
9452 db 0,0,0,0,0,0,0,0,0,0,0 ;; reserved
9453 db 0x07 ;; checksum
9454 pci_routing_table_structure_start:
9455 ;; first slot entry PCI-to-ISA (embedded)
9456 db 0 ;; pci bus number
9457 db 0x08 ;; pci device number (bit 7-3)
9458 db 0x60 ;; link value INTA#: pointer into PCI2ISA config space
9459 dw 0xdef8 ;; IRQ bitmap INTA#
9460 db 0x61 ;; link value INTB#
9461 dw 0xdef8 ;; IRQ bitmap INTB#
9462 db 0x62 ;; link value INTC#
9463 dw 0xdef8 ;; IRQ bitmap INTC#
9464 db 0x63 ;; link value INTD#
9465 dw 0xdef8 ;; IRQ bitmap INTD#
9466 db 0 ;; physical slot (0 = embedded)
9467 db 0 ;; reserved
9468 ;; second slot entry: 1st PCI slot
9469 db 0 ;; pci bus number
9470 db 0x10 ;; pci device number (bit 7-3)
9471 db 0x61 ;; link value INTA#
9472 dw 0xdef8 ;; IRQ bitmap INTA#
9473 db 0x62 ;; link value INTB#
9474 dw 0xdef8 ;; IRQ bitmap INTB#
9475 db 0x63 ;; link value INTC#
9476 dw 0xdef8 ;; IRQ bitmap INTC#
9477 db 0x60 ;; link value INTD#
9478 dw 0xdef8 ;; IRQ bitmap INTD#
9479 db 1 ;; physical slot (0 = embedded)
9480 db 0 ;; reserved
9481 ;; third slot entry: 2nd PCI slot
9482 db 0 ;; pci bus number
9483 db 0x18 ;; pci device number (bit 7-3)
9484 db 0x62 ;; link value INTA#
9485 dw 0xdef8 ;; IRQ bitmap INTA#
9486 db 0x63 ;; link value INTB#
9487 dw 0xdef8 ;; IRQ bitmap INTB#
9488 db 0x60 ;; link value INTC#
9489 dw 0xdef8 ;; IRQ bitmap INTC#
9490 db 0x61 ;; link value INTD#
9491 dw 0xdef8 ;; IRQ bitmap INTD#
9492 db 2 ;; physical slot (0 = embedded)
9493 db 0 ;; reserved
9494 ;; 4th slot entry: 3rd PCI slot
9495 db 0 ;; pci bus number
9496 db 0x20 ;; pci device number (bit 7-3)
9497 db 0x63 ;; link value INTA#
9498 dw 0xdef8 ;; IRQ bitmap INTA#
9499 db 0x60 ;; link value INTB#
9500 dw 0xdef8 ;; IRQ bitmap INTB#
9501 db 0x61 ;; link value INTC#
9502 dw 0xdef8 ;; IRQ bitmap INTC#
9503 db 0x62 ;; link value INTD#
9504 dw 0xdef8 ;; IRQ bitmap INTD#
9505 db 3 ;; physical slot (0 = embedded)
9506 db 0 ;; reserved
9507 ;; 5th slot entry: 4rd PCI slot
9508 db 0 ;; pci bus number
9509 db 0x28 ;; pci device number (bit 7-3)
9510 db 0x60 ;; link value INTA#
9511 dw 0xdef8 ;; IRQ bitmap INTA#
9512 db 0x61 ;; link value INTB#
9513 dw 0xdef8 ;; IRQ bitmap INTB#
9514 db 0x62 ;; link value INTC#
9515 dw 0xdef8 ;; IRQ bitmap INTC#
9516 db 0x63 ;; link value INTD#
9517 dw 0xdef8 ;; IRQ bitmap INTD#
9518 db 4 ;; physical slot (0 = embedded)
9519 db 0 ;; reserved
9520 ;; 6th slot entry: 5rd PCI slot
9521 db 0 ;; pci bus number
9522 db 0x30 ;; pci device number (bit 7-3)
9523 db 0x61 ;; link value INTA#
9524 dw 0xdef8 ;; IRQ bitmap INTA#
9525 db 0x62 ;; link value INTB#
9526 dw 0xdef8 ;; IRQ bitmap INTB#
9527 db 0x63 ;; link value INTC#
9528 dw 0xdef8 ;; IRQ bitmap INTC#
9529 db 0x60 ;; link value INTD#
9530 dw 0xdef8 ;; IRQ bitmap INTD#
9531 db 5 ;; physical slot (0 = embedded)
9532 db 0 ;; reserved
9533 pci_routing_table_structure_end:
9535 #if !BX_ROMBIOS32
9536 pci_irq_list:
9537 db 11, 10, 9, 5;
9539 pcibios_init_sel_reg:
9540 push eax
9541 mov eax, #0x800000
9542 mov ax, bx
9543 shl eax, #8
9544 and dl, #0xfc
9545 or al, dl
9546 mov dx, #0x0cf8
9547 out dx, eax
9548 pop eax
9551 pcibios_init_iomem_bases:
9552 push bp
9553 mov bp, sp
9554 mov eax, #0xe0000000 ;; base for memory init
9555 push eax
9556 mov ax, #0xc000 ;; base for i/o init
9557 push ax
9558 mov ax, #0x0010 ;; start at base address #0
9559 push ax
9560 mov bx, #0x0008
9561 pci_init_io_loop1:
9562 mov dl, #0x00
9563 call pcibios_init_sel_reg
9564 mov dx, #0x0cfc
9565 in ax, dx
9566 cmp ax, #0xffff
9567 jz next_pci_dev
9568 mov dl, #0x04 ;; disable i/o and memory space access
9569 call pcibios_init_sel_reg
9570 mov dx, #0x0cfc
9571 in al, dx
9572 and al, #0xfc
9573 out dx, al
9574 pci_init_io_loop2:
9575 mov dl, [bp-8]
9576 call pcibios_init_sel_reg
9577 mov dx, #0x0cfc
9578 in eax, dx
9579 test al, #0x01
9580 jnz init_io_base
9581 mov ecx, eax
9582 mov eax, #0xffffffff
9583 out dx, eax
9584 in eax, dx
9585 cmp eax, ecx
9586 je next_pci_base
9587 xor eax, #0xffffffff
9588 mov ecx, eax
9589 mov eax, [bp-4]
9590 out dx, eax
9591 add eax, ecx ;; calculate next free mem base
9592 add eax, #0x01000000
9593 and eax, #0xff000000
9594 mov [bp-4], eax
9595 jmp next_pci_base
9596 init_io_base:
9597 mov cx, ax
9598 mov ax, #0xffff
9599 out dx, ax
9600 in ax, dx
9601 cmp ax, cx
9602 je next_pci_base
9603 xor ax, #0xfffe
9604 mov cx, ax
9605 mov ax, [bp-6]
9606 out dx, ax
9607 add ax, cx ;; calculate next free i/o base
9608 add ax, #0x0100
9609 and ax, #0xff00
9610 mov [bp-6], ax
9611 next_pci_base:
9612 mov al, [bp-8]
9613 add al, #0x04
9614 cmp al, #0x28
9615 je enable_iomem_space
9616 mov byte ptr[bp-8], al
9617 jmp pci_init_io_loop2
9618 enable_iomem_space:
9619 mov dl, #0x04 ;; enable i/o and memory space access if available
9620 call pcibios_init_sel_reg
9621 mov dx, #0x0cfc
9622 in al, dx
9623 or al, #0x07
9624 out dx, al
9625 next_pci_dev:
9626 mov byte ptr[bp-8], #0x10
9627 inc bx
9628 cmp bx, #0x0100
9629 jne pci_init_io_loop1
9630 mov sp, bp
9631 pop bp
9634 pcibios_init_set_elcr:
9635 push ax
9636 push cx
9637 mov dx, #0x04d0
9638 test al, #0x08
9639 jz is_master_pic
9640 inc dx
9641 and al, #0x07
9642 is_master_pic:
9643 mov cl, al
9644 mov bl, #0x01
9645 shl bl, cl
9646 in al, dx
9647 or al, bl
9648 out dx, al
9649 pop cx
9650 pop ax
9653 pcibios_init_irqs:
9654 push ds
9655 push bp
9656 mov ax, #0xf000
9657 mov ds, ax
9658 mov dx, #0x04d0 ;; reset ELCR1 + ELCR2
9659 mov al, #0x00
9660 out dx, al
9661 inc dx
9662 out dx, al
9663 mov si, #pci_routing_table_structure
9664 mov bh, [si+8]
9665 mov bl, [si+9]
9666 mov dl, #0x00
9667 call pcibios_init_sel_reg
9668 mov dx, #0x0cfc
9669 in eax, dx
9670 cmp eax, [si+12] ;; check irq router
9671 jne pci_init_end
9672 mov dl, [si+34]
9673 call pcibios_init_sel_reg
9674 push bx ;; save irq router bus + devfunc
9675 mov dx, #0x0cfc
9676 mov ax, #0x8080
9677 out dx, ax ;; reset PIRQ route control
9678 add dx, #2
9679 out dx, ax
9680 mov ax, [si+6]
9681 sub ax, #0x20
9682 shr ax, #4
9683 mov cx, ax
9684 add si, #0x20 ;; set pointer to 1st entry
9685 mov bp, sp
9686 mov ax, #pci_irq_list
9687 push ax
9688 xor ax, ax
9689 push ax
9690 pci_init_irq_loop1:
9691 mov bh, [si]
9692 mov bl, [si+1]
9693 pci_init_irq_loop2:
9694 mov dl, #0x00
9695 call pcibios_init_sel_reg
9696 mov dx, #0x0cfc
9697 in ax, dx
9698 cmp ax, #0xffff
9699 jnz pci_test_int_pin
9700 test bl, #0x07
9701 jz next_pir_entry
9702 jmp next_pci_func
9703 pci_test_int_pin:
9704 mov dl, #0x3c
9705 call pcibios_init_sel_reg
9706 mov dx, #0x0cfd
9707 in al, dx
9708 and al, #0x07
9709 jz next_pci_func
9710 dec al ;; determine pirq reg
9711 mov dl, #0x03
9712 mul al, dl
9713 add al, #0x02
9714 xor ah, ah
9715 mov bx, ax
9716 mov al, [si+bx]
9717 mov dl, al
9718 mov bx, [bp]
9719 call pcibios_init_sel_reg
9720 mov dx, #0x0cfc
9721 and al, #0x03
9722 add dl, al
9723 in al, dx
9724 cmp al, #0x80
9725 jb pirq_found
9726 mov bx, [bp-2] ;; pci irq list pointer
9727 mov al, [bx]
9728 out dx, al
9729 inc bx
9730 mov [bp-2], bx
9731 call pcibios_init_set_elcr
9732 pirq_found:
9733 mov bh, [si]
9734 mov bl, [si+1]
9735 add bl, [bp-3] ;; pci function number
9736 mov dl, #0x3c
9737 call pcibios_init_sel_reg
9738 mov dx, #0x0cfc
9739 out dx, al
9740 next_pci_func:
9741 inc byte ptr[bp-3]
9742 inc bl
9743 test bl, #0x07
9744 jnz pci_init_irq_loop2
9745 next_pir_entry:
9746 add si, #0x10
9747 mov byte ptr[bp-3], #0x00
9748 loop pci_init_irq_loop1
9749 mov sp, bp
9750 pop bx
9751 pci_init_end:
9752 pop bp
9753 pop ds
9755 #endif // BX_ROMBIOS32
9756 #endif // BX_PCIBIOS
9758 #if BX_ROMBIOS32
9759 rombios32_init:
9760 ;; save a20 and enable it
9761 in al, 0x92
9762 push ax
9763 or al, #0x02
9764 out 0x92, al
9766 ;; save SS:SP to the BDA
9767 xor ax, ax
9768 mov ds, ax
9769 mov 0x0469, ss
9770 mov 0x0467, sp
9772 SEG CS
9773 lidt [pmode_IDT_info]
9774 SEG CS
9775 lgdt [rombios32_gdt_48]
9776 ;; set PE bit in CR0
9777 mov eax, cr0
9778 or al, #0x01
9779 mov cr0, eax
9780 ;; start protected mode code: ljmpl 0x10:rombios32_init1
9781 db 0x66, 0xea
9782 dw rombios32_05
9783 dw 0x000f ;; high 16 bit address
9784 dw 0x0010
9786 use32 386
9787 rombios32_05:
9788 ;; init data segments
9789 mov eax, #0x18
9790 mov ds, ax
9791 mov es, ax
9792 mov ss, ax
9793 xor eax, eax
9794 mov fs, ax
9795 mov gs, ax
9798 ;; copy rombios32 code to ram (ram offset = 1MB)
9799 mov esi, #0xfffe0000
9800 mov edi, #0x00040000
9801 mov ecx, #0x10000 / 4
9803 movsd
9805 ;; init the stack pointer
9806 mov esp, #0x00080000
9808 ;; call rombios32 code
9809 mov eax, #0x00040000
9810 call eax
9812 ;; reset the memory (some boot loaders such as syslinux suppose
9813 ;; that the memory is set to zero)
9814 mov edi, #0x00040000
9815 mov ecx, #0x40000 / 4
9816 xor eax, eax
9818 stosd
9820 ;; return to 16 bit protected mode first
9821 db 0xea
9822 dd rombios32_10
9823 dw 0x20
9825 use16 386
9826 rombios32_10:
9827 ;; restore data segment limits to 0xffff
9828 mov ax, #0x28
9829 mov ds, ax
9830 mov es, ax
9831 mov ss, ax
9832 mov fs, ax
9833 mov gs, ax
9835 ;; reset PE bit in CR0
9836 mov eax, cr0
9837 and al, #0xFE
9838 mov cr0, eax
9840 ;; far jump to flush CPU queue after transition to real mode
9841 JMP_AP(0xf000, rombios32_real_mode)
9843 rombios32_real_mode:
9844 ;; restore IDT to normal real-mode defaults
9845 SEG CS
9846 lidt [rmode_IDT_info]
9848 xor ax, ax
9849 mov ds, ax
9850 mov es, ax
9851 mov fs, ax
9852 mov gs, ax
9854 ;; restore SS:SP from the BDA
9855 mov ss, 0x0469
9856 xor esp, esp
9857 mov sp, 0x0467
9858 ;; restore a20
9859 pop ax
9860 out 0x92, al
9863 rombios32_gdt_48:
9864 dw 0x30
9865 dw rombios32_gdt
9866 dw 0x000f
9868 rombios32_gdt:
9869 dw 0, 0, 0, 0
9870 dw 0, 0, 0, 0
9871 dw 0xffff, 0, 0x9b00, 0x00cf ; 32 bit flat code segment (0x10)
9872 dw 0xffff, 0, 0x9300, 0x00cf ; 32 bit flat data segment (0x18)
9873 dw 0xffff, 0, 0x9b0f, 0x0000 ; 16 bit code segment base=0xf0000 limit=0xffff
9874 dw 0xffff, 0, 0x9300, 0x0000 ; 16 bit data segment base=0x0 limit=0xffff
9875 #endif
9878 ; parallel port detection: base address in DX, index in BX, timeout in CL
9879 detect_parport:
9880 push dx
9881 add dx, #2
9882 in al, dx
9883 and al, #0xdf ; clear input mode
9884 out dx, al
9885 pop dx
9886 mov al, #0xaa
9887 out dx, al
9888 in al, dx
9889 cmp al, #0xaa
9890 jne no_parport
9891 push bx
9892 shl bx, #1
9893 mov [bx+0x408], dx ; Parallel I/O address
9894 pop bx
9895 mov [bx+0x478], cl ; Parallel printer timeout
9896 inc bx
9897 no_parport:
9900 ; serial port detection: base address in DX, index in BX, timeout in CL
9901 detect_serial:
9902 push dx
9903 inc dx
9904 mov al, #0x02
9905 out dx, al
9906 in al, dx
9907 cmp al, #0x02
9908 jne no_serial
9909 inc dx
9910 in al, dx
9911 cmp al, #0x02
9912 jne no_serial
9913 dec dx
9914 xor al, al
9915 out dx, al
9916 pop dx
9917 push bx
9918 shl bx, #1
9919 mov [bx+0x400], dx ; Serial I/O address
9920 pop bx
9921 mov [bx+0x47c], cl ; Serial timeout
9922 inc bx
9924 no_serial:
9925 pop dx
9928 rom_checksum:
9929 push ax
9930 push bx
9931 push cx
9932 xor ax, ax
9933 xor bx, bx
9934 xor cx, cx
9935 mov ch, [2]
9936 shl cx, #1
9937 checksum_loop:
9938 add al, [bx]
9939 inc bx
9940 loop checksum_loop
9941 and al, #0xff
9942 pop cx
9943 pop bx
9944 pop ax
9948 ;; We need a copy of this string, but we are not actually a PnP BIOS,
9949 ;; so make sure it is *not* aligned, so OSes will not see it if they scan.
9950 .align 16
9951 db 0
9952 pnp_string:
9953 .ascii "$PnP"
9956 rom_scan:
9957 ;; Scan for existence of valid expansion ROMS.
9958 ;; Video ROM: from 0xC0000..0xC7FFF in 2k increments
9959 ;; General ROM: from 0xC8000..0xDFFFF in 2k increments
9960 ;; System ROM: only 0xE0000
9962 ;; Header:
9963 ;; Offset Value
9964 ;; 0 0x55
9965 ;; 1 0xAA
9966 ;; 2 ROM length in 512-byte blocks
9967 ;; 3 ROM initialization entry point (FAR CALL)
9969 rom_scan_loop:
9970 push ax ;; Save AX
9971 mov ds, cx
9972 mov ax, #0x0004 ;; start with increment of 4 (512-byte) blocks = 2k
9973 cmp [0], #0xAA55 ;; look for signature
9974 jne rom_scan_increment
9975 call rom_checksum
9976 jnz rom_scan_increment
9977 mov al, [2] ;; change increment to ROM length in 512-byte blocks
9979 ;; We want our increment in 512-byte quantities, rounded to
9980 ;; the nearest 2k quantity, since we only scan at 2k intervals.
9981 test al, #0x03
9982 jz block_count_rounded
9983 and al, #0xfc ;; needs rounding up
9984 add al, #0x04
9985 block_count_rounded:
9987 xor bx, bx ;; Restore DS back to 0000:
9988 mov ds, bx
9989 push ax ;; Save AX
9990 push di ;; Save DI
9991 ;; Push addr of ROM entry point
9992 push cx ;; Push seg
9993 push #0x0003 ;; Push offset
9995 ;; Point ES:DI at "$PnP", which tells the ROM that we are a PnP BIOS.
9996 ;; That should stop it grabbing INT 19h; we will use its BEV instead.
9997 mov ax, #0xf000
9998 mov es, ax
9999 lea di, pnp_string
10001 mov bp, sp ;; Call ROM init routine using seg:off on stack
10002 db 0xff ;; call_far ss:[bp+0]
10003 db 0x5e
10004 db 0
10005 cli ;; In case expansion ROM BIOS turns IF on
10006 add sp, #2 ;; Pop offset value
10007 pop cx ;; Pop seg value (restore CX)
10009 ;; Look at the ROM's PnP Expansion header. Properly, we're supposed
10010 ;; to init all the ROMs and then go back and build an IPL table of
10011 ;; all the bootable devices, but we can get away with one pass.
10012 mov ds, cx ;; ROM base
10013 mov bx, 0x001a ;; 0x1A is the offset into ROM header that contains...
10014 mov ax, [bx] ;; the offset of PnP expansion header, where...
10015 cmp ax, #0x5024 ;; we look for signature "$PnP"
10016 jne no_bev
10017 mov ax, 2[bx]
10018 cmp ax, #0x506e
10019 jne no_bev
10020 mov ax, 0x1a[bx] ;; 0x1A is also the offset into the expansion header of...
10021 cmp ax, #0x0000 ;; the Bootstrap Entry Vector, or zero if there is none.
10022 je no_bev
10024 ;; Found a device that thinks it can boot the system. Record its BEV and product name string.
10025 mov di, 0x10[bx] ;; Pointer to the product name string or zero if none
10026 mov bx, #IPL_SEG ;; Go to the segment where the IPL table lives
10027 mov ds, bx
10028 mov bx, IPL_COUNT_OFFSET ;; Read the number of entries so far
10029 cmp bx, #IPL_TABLE_ENTRIES
10030 je no_bev ;; Get out if the table is full
10031 shl bx, #0x4 ;; Turn count into offset (entries are 16 bytes)
10032 mov 0[bx], #IPL_TYPE_BEV ;; This entry is a BEV device
10033 mov 6[bx], cx ;; Build a far pointer from the segment...
10034 mov 4[bx], ax ;; and the offset
10035 cmp di, #0x0000
10036 je no_prod_str
10037 mov 0xA[bx], cx ;; Build a far pointer from the segment...
10038 mov 8[bx], di ;; and the offset
10039 no_prod_str:
10040 shr bx, #0x4 ;; Turn the offset back into a count
10041 inc bx ;; We have one more entry now
10042 mov IPL_COUNT_OFFSET, bx ;; Remember that.
10044 no_bev:
10045 pop di ;; Restore DI
10046 pop ax ;; Restore AX
10047 rom_scan_increment:
10048 shl ax, #5 ;; convert 512-bytes blocks to 16-byte increments
10049 ;; because the segment selector is shifted left 4 bits.
10050 add cx, ax
10051 pop ax ;; Restore AX
10052 cmp cx, ax
10053 jbe rom_scan_loop
10055 xor ax, ax ;; Restore DS back to 0000:
10056 mov ds, ax
10059 ;; for 'C' strings and other data, insert them here with
10060 ;; a the following hack:
10061 ;; DATA_SEG_DEFS_HERE
10064 ;; the following area can be used to write dynamically generated tables
10065 .align 16
10066 bios_table_area_start:
10067 dd 0xaafb4442
10068 dd bios_table_area_end - bios_table_area_start - 8;
10070 ;--------
10071 ;- POST -
10072 ;--------
10073 .org 0xe05b ; POST Entry Point
10074 post:
10075 ;; enable cache
10076 mov eax, cr0
10077 and eax, #0x9fffffff
10078 mov cr0, eax
10080 xor ax, ax
10082 ;; first reset the DMA controllers
10083 out 0x0d,al
10084 out 0xda,al
10086 ;; then initialize the DMA controllers
10087 mov al, #0xC0
10088 out 0xD6, al ; cascade mode of channel 4 enabled
10089 mov al, #0x00
10090 out 0xD4, al ; unmask channel 4
10092 ;; Examine CMOS shutdown status.
10093 mov AL, #0x0f
10094 out 0x70, AL
10095 in AL, 0x71
10097 ;; backup status
10098 mov bl, al
10100 ;; Reset CMOS shutdown status.
10101 mov AL, #0x0f
10102 out 0x70, AL ; select CMOS register Fh
10103 mov AL, #0x00
10104 out 0x71, AL ; set shutdown action to normal
10106 ;; Examine CMOS shutdown status.
10107 mov al, bl
10109 ;; 0x00, 0x09, 0x0D+ = normal startup
10110 cmp AL, #0x00
10111 jz normal_post
10112 cmp AL, #0x0d
10113 jae normal_post
10114 cmp AL, #0x09
10115 je normal_post
10117 ;; 0x05 = eoi + jmp via [0x40:0x67] jump
10118 cmp al, #0x05
10119 je eoi_jmp_post
10121 ;; Examine CMOS shutdown status.
10122 ;; 0x01,0x02,0x03,0x04,0x06,0x07,0x08, 0x0a, 0x0b, 0x0c = Unimplemented shutdown status.
10123 push bx
10124 call _shutdown_status_panic
10126 #if 0
10127 HALT(__LINE__)
10129 ;#if 0
10130 ; 0xb0, 0x20, /* mov al, #0x20 */
10131 ; 0xe6, 0x20, /* out 0x20, al ;send EOI to PIC */
10132 ;#endif
10134 pop es
10135 pop ds
10136 popa
10137 iret
10138 #endif
10140 normal_post:
10141 ; case 0: normal startup
10144 mov ax, #0xfffe
10145 mov sp, ax
10146 xor ax, ax
10147 mov ds, ax
10148 mov ss, ax
10150 ;; zero out BIOS data area (40:00..40:ff)
10151 mov es, ax
10152 mov cx, #0x0080 ;; 128 words
10153 mov di, #0x0400
10156 stosw
10158 call _log_bios_start
10160 ;; set all interrupts to default handler
10161 xor bx, bx ;; offset index
10162 mov cx, #0x0100 ;; counter (256 interrupts)
10163 mov ax, #dummy_iret_handler
10164 mov dx, #0xF000
10166 post_default_ints:
10167 mov [bx], ax
10168 add bx, #2
10169 mov [bx], dx
10170 add bx, #2
10171 loop post_default_ints
10173 ;; set vector 0x79 to zero
10174 ;; this is used by 'gardian angel' protection system
10175 SET_INT_VECTOR(0x79, #0, #0)
10177 ;; base memory in K 40:13 (word)
10178 mov ax, #BASE_MEM_IN_K
10179 mov 0x0413, ax
10182 ;; Manufacturing Test 40:12
10183 ;; zerod out above
10185 ;; Warm Boot Flag 0040:0072
10186 ;; value of 1234h = skip memory checks
10187 ;; zerod out above
10190 ;; Printer Services vector
10191 SET_INT_VECTOR(0x17, #0xF000, #int17_handler)
10193 ;; Bootstrap failure vector
10194 SET_INT_VECTOR(0x18, #0xF000, #int18_handler)
10196 ;; Bootstrap Loader vector
10197 SET_INT_VECTOR(0x19, #0xF000, #int19_handler)
10199 ;; User Timer Tick vector
10200 SET_INT_VECTOR(0x1c, #0xF000, #int1c_handler)
10202 ;; Memory Size Check vector
10203 SET_INT_VECTOR(0x12, #0xF000, #int12_handler)
10205 ;; Equipment Configuration Check vector
10206 SET_INT_VECTOR(0x11, #0xF000, #int11_handler)
10208 ;; System Services
10209 SET_INT_VECTOR(0x15, #0xF000, #int15_handler)
10211 ;; EBDA setup
10212 call ebda_post
10214 ;; PIT setup
10215 SET_INT_VECTOR(0x08, #0xF000, #int08_handler)
10216 ;; int 1C already points at dummy_iret_handler (above)
10217 mov al, #0x34 ; timer0: binary count, 16bit count, mode 2
10218 out 0x43, al
10219 mov al, #0x00 ; maximum count of 0000H = 18.2Hz
10220 out 0x40, al
10221 out 0x40, al
10223 ;; Keyboard
10224 SET_INT_VECTOR(0x09, #0xF000, #int09_handler)
10225 SET_INT_VECTOR(0x16, #0xF000, #int16_handler)
10227 xor ax, ax
10228 mov ds, ax
10229 mov 0x0417, al /* keyboard shift flags, set 1 */
10230 mov 0x0418, al /* keyboard shift flags, set 2 */
10231 mov 0x0419, al /* keyboard alt-numpad work area */
10232 mov 0x0471, al /* keyboard ctrl-break flag */
10233 mov 0x0497, al /* keyboard status flags 4 */
10234 mov al, #0x10
10235 mov 0x0496, al /* keyboard status flags 3 */
10238 /* keyboard head of buffer pointer */
10239 mov bx, #0x001E
10240 mov 0x041A, bx
10242 /* keyboard end of buffer pointer */
10243 mov 0x041C, bx
10245 /* keyboard pointer to start of buffer */
10246 mov bx, #0x001E
10247 mov 0x0480, bx
10249 /* keyboard pointer to end of buffer */
10250 mov bx, #0x003E
10251 mov 0x0482, bx
10253 /* init the keyboard */
10254 call _keyboard_init
10256 ;; mov CMOS Equipment Byte to BDA Equipment Word
10257 mov ax, 0x0410
10258 mov al, #0x14
10259 out 0x70, al
10260 in al, 0x71
10261 mov 0x0410, ax
10264 ;; Parallel setup
10265 SET_INT_VECTOR(0x0F, #0xF000, #dummy_iret_handler)
10266 xor ax, ax
10267 mov ds, ax
10268 xor bx, bx
10269 mov cl, #0x14 ; timeout value
10270 mov dx, #0x378 ; Parallel I/O address, port 1
10271 call detect_parport
10272 mov dx, #0x278 ; Parallel I/O address, port 2
10273 call detect_parport
10274 shl bx, #0x0e
10275 mov ax, 0x410 ; Equipment word bits 14..15 determing # parallel ports
10276 and ax, #0x3fff
10277 or ax, bx ; set number of parallel ports
10278 mov 0x410, ax
10280 ;; Serial setup
10281 SET_INT_VECTOR(0x0C, #0xF000, #dummy_iret_handler)
10282 SET_INT_VECTOR(0x14, #0xF000, #int14_handler)
10283 xor bx, bx
10284 mov cl, #0x0a ; timeout value
10285 mov dx, #0x03f8 ; Serial I/O address, port 1
10286 call detect_serial
10287 mov dx, #0x02f8 ; Serial I/O address, port 2
10288 call detect_serial
10289 mov dx, #0x03e8 ; Serial I/O address, port 3
10290 call detect_serial
10291 mov dx, #0x02e8 ; Serial I/O address, port 4
10292 call detect_serial
10293 shl bx, #0x09
10294 mov ax, 0x410 ; Equipment word bits 9..11 determing # serial ports
10295 and ax, #0xf1ff
10296 or ax, bx ; set number of serial port
10297 mov 0x410, ax
10299 ;; CMOS RTC
10300 SET_INT_VECTOR(0x1A, #0xF000, #int1a_handler)
10301 SET_INT_VECTOR(0x4A, #0xF000, #dummy_iret_handler)
10302 SET_INT_VECTOR(0x70, #0xF000, #int70_handler)
10303 ;; BIOS DATA AREA 0x4CE ???
10304 call timer_tick_post
10306 ;; PS/2 mouse setup
10307 SET_INT_VECTOR(0x74, #0xF000, #int74_handler)
10309 ;; IRQ13 (FPU exception) setup
10310 SET_INT_VECTOR(0x75, #0xF000, #int75_handler)
10312 ;; Video setup
10313 SET_INT_VECTOR(0x10, #0xF000, #int10_handler)
10315 ;; PIC
10316 mov al, #0x11 ; send initialisation commands
10317 out 0x20, al
10318 out 0xa0, al
10319 mov al, #0x08
10320 out 0x21, al
10321 mov al, #0x70
10322 out 0xa1, al
10323 mov al, #0x04
10324 out 0x21, al
10325 mov al, #0x02
10326 out 0xa1, al
10327 mov al, #0x01
10328 out 0x21, al
10329 out 0xa1, al
10330 mov al, #0xb8
10331 out 0x21, AL ;master pic: unmask IRQ 0, 1, 2, 6
10332 #if BX_USE_PS2_MOUSE
10333 mov al, #0x8f
10334 #else
10335 mov al, #0x9f
10336 #endif
10337 out 0xa1, AL ;slave pic: unmask IRQ 12, 13, 14
10339 mov cx, #0xc000 ;; init vga bios
10340 mov ax, #0xc780
10341 call rom_scan
10343 call _print_bios_banner
10345 #if BX_ROMBIOS32
10346 call rombios32_init
10347 #else
10348 #if BX_PCIBIOS
10349 call pcibios_init_iomem_bases
10350 call pcibios_init_irqs
10351 #endif //BX_PCIBIOS
10352 #endif
10355 ;; Floppy setup
10357 call floppy_drive_post
10359 #if BX_USE_ATADRV
10362 ;; Hard Drive setup
10364 call hard_drive_post
10367 ;; ATA/ATAPI driver setup
10369 call _ata_init
10370 call _ata_detect
10372 #else // BX_USE_ATADRV
10375 ;; Hard Drive setup
10377 call hard_drive_post
10379 #endif // BX_USE_ATADRV
10381 #if BX_ELTORITO_BOOT
10383 ;; eltorito floppy/harddisk emulation from cd
10385 call _cdemu_init
10387 #endif // BX_ELTORITO_BOOT
10389 call _init_boot_vectors
10391 mov cx, #0xc800 ;; init option roms
10392 mov ax, #0xe000
10393 call rom_scan
10395 sti ;; enable interrupts
10396 int #0x19
10398 .org 0xe2c3 ; NMI Handler Entry Point
10399 nmi:
10400 ;; FIXME the NMI handler should not panic
10401 ;; but iret when called from int75 (fpu exception)
10402 call _nmi_handler_msg
10403 iret
10405 int75_handler:
10406 out 0xf0, al // clear irq13
10407 call eoi_both_pics // clear interrupt
10408 int 2 // legacy nmi call
10409 iret
10411 ;-------------------------------------------
10412 ;- INT 13h Fixed Disk Services Entry Point -
10413 ;-------------------------------------------
10414 .org 0xe3fe ; INT 13h Fixed Disk Services Entry Point
10415 int13_handler:
10416 //JMPL(int13_relocated)
10417 jmp int13_relocated
10419 .org 0xe401 ; Fixed Disk Parameter Table
10421 ;----------
10422 ;- INT19h -
10423 ;----------
10424 .org 0xe6f2 ; INT 19h Boot Load Service Entry Point
10425 int19_handler:
10427 jmp int19_relocated
10428 ;-------------------------------------------
10429 ;- System BIOS Configuration Data Table
10430 ;-------------------------------------------
10431 .org BIOS_CONFIG_TABLE
10432 db 0x08 ; Table size (bytes) -Lo
10433 db 0x00 ; Table size (bytes) -Hi
10434 db SYS_MODEL_ID
10435 db SYS_SUBMODEL_ID
10436 db BIOS_REVISION
10437 ; Feature byte 1
10438 ; b7: 1=DMA channel 3 used by hard disk
10439 ; b6: 1=2 interrupt controllers present
10440 ; b5: 1=RTC present
10441 ; b4: 1=BIOS calls int 15h/4Fh every key
10442 ; b3: 1=wait for extern event supported (Int 15h/41h)
10443 ; b2: 1=extended BIOS data area used
10444 ; b1: 0=AT or ESDI bus, 1=MicroChannel
10445 ; b0: 1=Dual bus (MicroChannel + ISA)
10446 db (0 << 7) | \
10447 (1 << 6) | \
10448 (1 << 5) | \
10449 (BX_CALL_INT15_4F << 4) | \
10450 (0 << 3) | \
10451 (BX_USE_EBDA << 2) | \
10452 (0 << 1) | \
10453 (0 << 0)
10454 ; Feature byte 2
10455 ; b7: 1=32-bit DMA supported
10456 ; b6: 1=int16h, function 9 supported
10457 ; b5: 1=int15h/C6h (get POS data) supported
10458 ; b4: 1=int15h/C7h (get mem map info) supported
10459 ; b3: 1=int15h/C8h (en/dis CPU) supported
10460 ; b2: 1=non-8042 kb controller
10461 ; b1: 1=data streaming supported
10462 ; b0: reserved
10463 db (0 << 7) | \
10464 (1 << 6) | \
10465 (0 << 5) | \
10466 (0 << 4) | \
10467 (0 << 3) | \
10468 (0 << 2) | \
10469 (0 << 1) | \
10470 (0 << 0)
10471 ; Feature byte 3
10472 ; b7: not used
10473 ; b6: reserved
10474 ; b5: reserved
10475 ; b4: POST supports ROM-to-RAM enable/disable
10476 ; b3: SCSI on system board
10477 ; b2: info panel installed
10478 ; b1: Initial Machine Load (IML) system - BIOS on disk
10479 ; b0: SCSI supported in IML
10480 db 0x00
10481 ; Feature byte 4
10482 ; b7: IBM private
10483 ; b6: EEPROM present
10484 ; b5-3: ABIOS presence (011 = not supported)
10485 ; b2: private
10486 ; b1: memory split above 16Mb supported
10487 ; b0: POSTEXT directly supported by POST
10488 db 0x00
10489 ; Feature byte 5 (IBM)
10490 ; b1: enhanced mouse
10491 ; b0: flash EPROM
10492 db 0x00
10496 .org 0xe729 ; Baud Rate Generator Table
10498 ;----------
10499 ;- INT14h -
10500 ;----------
10501 .org 0xe739 ; INT 14h Serial Communications Service Entry Point
10502 int14_handler:
10503 push ds
10504 pusha
10505 xor ax, ax
10506 mov ds, ax
10507 call _int14_function
10508 popa
10509 pop ds
10510 iret
10513 ;----------------------------------------
10514 ;- INT 16h Keyboard Service Entry Point -
10515 ;----------------------------------------
10516 .org 0xe82e
10517 int16_handler:
10520 push ds
10521 pushf
10522 pusha
10524 cmp ah, #0x00
10525 je int16_F00
10526 cmp ah, #0x10
10527 je int16_F00
10529 mov bx, #0xf000
10530 mov ds, bx
10531 call _int16_function
10532 popa
10533 popf
10534 pop ds
10535 jz int16_zero_set
10537 int16_zero_clear:
10538 push bp
10539 mov bp, sp
10540 //SEG SS
10541 and BYTE [bp + 0x06], #0xbf
10542 pop bp
10543 iret
10545 int16_zero_set:
10546 push bp
10547 mov bp, sp
10548 //SEG SS
10549 or BYTE [bp + 0x06], #0x40
10550 pop bp
10551 iret
10553 int16_F00:
10554 mov bx, #0x0040
10555 mov ds, bx
10557 int16_wait_for_key:
10559 mov bx, 0x001a
10560 cmp bx, 0x001c
10561 jne int16_key_found
10564 #if 0
10565 /* no key yet, call int 15h, function AX=9002 */
10566 0x50, /* push AX */
10567 0xb8, 0x02, 0x90, /* mov AX, #0x9002 */
10568 0xcd, 0x15, /* int 15h */
10569 0x58, /* pop AX */
10570 0xeb, 0xea, /* jmp WAIT_FOR_KEY */
10571 #endif
10572 jmp int16_wait_for_key
10574 int16_key_found:
10575 mov bx, #0xf000
10576 mov ds, bx
10577 call _int16_function
10578 popa
10579 popf
10580 pop ds
10581 #if 0
10582 /* notify int16 complete w/ int 15h, function AX=9102 */
10583 0x50, /* push AX */
10584 0xb8, 0x02, 0x91, /* mov AX, #0x9102 */
10585 0xcd, 0x15, /* int 15h */
10586 0x58, /* pop AX */
10587 #endif
10588 iret
10592 ;-------------------------------------------------
10593 ;- INT09h : Keyboard Hardware Service Entry Point -
10594 ;-------------------------------------------------
10595 .org 0xe987
10596 int09_handler:
10598 push ax
10600 mov al, #0xAD ;;disable keyboard
10601 out #0x64, al
10603 mov al, #0x0B
10604 out #0x20, al
10605 in al, #0x20
10606 and al, #0x02
10607 jz int09_finish
10609 in al, #0x60 ;;read key from keyboard controller
10611 push ds
10612 pusha
10613 #ifdef BX_CALL_INT15_4F
10614 mov ah, #0x4f ;; allow for keyboard intercept
10616 int #0x15
10617 jnc int09_done
10618 #endif
10620 ;; check for extended key
10621 cmp al, #0xe0
10622 jne int09_check_pause
10623 xor ax, ax
10624 mov ds, ax
10625 mov al, BYTE [0x496] ;; mf2_state |= 0x02
10626 or al, #0x02
10627 mov BYTE [0x496], al
10628 jmp int09_done
10630 int09_check_pause: ;; check for pause key
10631 cmp al, #0xe1
10632 jne int09_process_key
10633 xor ax, ax
10634 mov ds, ax
10635 mov al, BYTE [0x496] ;; mf2_state |= 0x01
10636 or al, #0x01
10637 mov BYTE [0x496], al
10638 jmp int09_done
10640 int09_process_key:
10641 mov bx, #0xf000
10642 mov ds, bx
10643 call _int09_function
10645 int09_done:
10646 popa
10647 pop ds
10649 call eoi_master_pic
10651 int09_finish:
10652 mov al, #0xAE ;;enable keyboard
10653 out #0x64, al
10654 pop ax
10655 iret
10658 ;----------------------------------------
10659 ;- INT 13h Diskette Service Entry Point -
10660 ;----------------------------------------
10661 .org 0xec59
10662 int13_diskette:
10663 jmp int13_noeltorito
10665 ;---------------------------------------------
10666 ;- INT 0Eh Diskette Hardware ISR Entry Point -
10667 ;---------------------------------------------
10668 .org 0xef57 ; INT 0Eh Diskette Hardware ISR Entry Point
10669 int0e_handler:
10670 push ax
10671 push dx
10672 mov dx, #0x03f4
10673 in al, dx
10674 and al, #0xc0
10675 cmp al, #0xc0
10676 je int0e_normal
10677 mov dx, #0x03f5
10678 mov al, #0x08 ; sense interrupt status
10679 out dx, al
10680 int0e_loop1:
10681 mov dx, #0x03f4
10682 in al, dx
10683 and al, #0xc0
10684 cmp al, #0xc0
10685 jne int0e_loop1
10686 int0e_loop2:
10687 mov dx, #0x03f5
10688 in al, dx
10689 mov dx, #0x03f4
10690 in al, dx
10691 and al, #0xc0
10692 cmp al, #0xc0
10693 je int0e_loop2
10694 int0e_normal:
10695 push ds
10696 xor ax, ax ;; segment 0000
10697 mov ds, ax
10698 call eoi_master_pic
10699 mov al, 0x043e
10700 or al, #0x80 ;; diskette interrupt has occurred
10701 mov 0x043e, al
10702 pop ds
10703 pop dx
10704 pop ax
10705 iret
10708 .org 0xefc7 ; Diskette Controller Parameter Table
10709 diskette_param_table:
10710 ;; Since no provisions are made for multiple drive types, most
10711 ;; values in this table are ignored. I set parameters for 1.44M
10712 ;; floppy here
10713 db 0xAF
10714 db 0x02 ;; head load time 0000001, DMA used
10715 db 0x25
10716 db 0x02
10717 db 18
10718 db 0x1B
10719 db 0xFF
10720 db 0x6C
10721 db 0xF6
10722 db 0x0F
10723 db 0x08
10726 ;----------------------------------------
10727 ;- INT17h : Printer Service Entry Point -
10728 ;----------------------------------------
10729 .org 0xefd2
10730 int17_handler:
10731 push ds
10732 pusha
10733 xor ax, ax
10734 mov ds, ax
10735 call _int17_function
10736 popa
10737 pop ds
10738 iret
10740 diskette_param_table2:
10741 ;; New diskette parameter table adding 3 parameters from IBM
10742 ;; Since no provisions are made for multiple drive types, most
10743 ;; values in this table are ignored. I set parameters for 1.44M
10744 ;; floppy here
10745 db 0xAF
10746 db 0x02 ;; head load time 0000001, DMA used
10747 db 0x25
10748 db 0x02
10749 db 18
10750 db 0x1B
10751 db 0xFF
10752 db 0x6C
10753 db 0xF6
10754 db 0x0F
10755 db 0x08
10756 db 79 ;; maximum track
10757 db 0 ;; data transfer rate
10758 db 4 ;; drive type in cmos
10760 .org 0xf045 ; INT 10 Functions 0-Fh Entry Point
10761 HALT(__LINE__)
10762 iret
10764 ;----------
10765 ;- INT10h -
10766 ;----------
10767 .org 0xf065 ; INT 10h Video Support Service Entry Point
10768 int10_handler:
10769 ;; dont do anything, since the VGA BIOS handles int10h requests
10770 iret
10772 .org 0xf0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
10774 ;----------
10775 ;- INT12h -
10776 ;----------
10777 .org 0xf841 ; INT 12h Memory Size Service Entry Point
10778 ; ??? different for Pentium (machine check)?
10779 int12_handler:
10780 push ds
10781 mov ax, #0x0040
10782 mov ds, ax
10783 mov ax, 0x0013
10784 pop ds
10785 iret
10787 ;----------
10788 ;- INT11h -
10789 ;----------
10790 .org 0xf84d ; INT 11h Equipment List Service Entry Point
10791 int11_handler:
10792 push ds
10793 mov ax, #0x0040
10794 mov ds, ax
10795 mov ax, 0x0010
10796 pop ds
10797 iret
10799 ;----------
10800 ;- INT15h -
10801 ;----------
10802 .org 0xf859 ; INT 15h System Services Entry Point
10803 int15_handler:
10804 pushf
10805 #if BX_APM
10806 cmp ah, #0x53
10807 je apm_call
10808 #endif
10809 push ds
10810 push es
10811 cmp ah, #0x86
10812 je int15_handler32
10813 cmp ah, #0xE8
10814 je int15_handler32
10815 pusha
10816 #if BX_USE_PS2_MOUSE
10817 cmp ah, #0xC2
10818 je int15_handler_mouse
10819 #endif
10820 call _int15_function
10821 int15_handler_mouse_ret:
10822 popa
10823 int15_handler32_ret:
10824 pop es
10825 pop ds
10826 popf
10827 jmp iret_modify_cf
10828 #if BX_APM
10829 apm_call:
10830 jmp _apmreal_entry
10831 #endif
10833 #if BX_USE_PS2_MOUSE
10834 int15_handler_mouse:
10835 call _int15_function_mouse
10836 jmp int15_handler_mouse_ret
10837 #endif
10839 int15_handler32:
10840 pushad
10841 call _int15_function32
10842 popad
10843 jmp int15_handler32_ret
10845 ;; Protected mode IDT descriptor
10847 ;; I just make the limit 0, so the machine will shutdown
10848 ;; if an exception occurs during protected mode memory
10849 ;; transfers.
10851 ;; Set base to f0000 to correspond to beginning of BIOS,
10852 ;; in case I actually define an IDT later
10853 ;; Set limit to 0
10855 pmode_IDT_info:
10856 dw 0x0000 ;; limit 15:00
10857 dw 0x0000 ;; base 15:00
10858 db 0x0f ;; base 23:16
10860 ;; Real mode IDT descriptor
10862 ;; Set to typical real-mode values.
10863 ;; base = 000000
10864 ;; limit = 03ff
10866 rmode_IDT_info:
10867 dw 0x03ff ;; limit 15:00
10868 dw 0x0000 ;; base 15:00
10869 db 0x00 ;; base 23:16
10872 ;----------
10873 ;- INT1Ah -
10874 ;----------
10875 .org 0xfe6e ; INT 1Ah Time-of-day Service Entry Point
10876 int1a_handler:
10877 #if BX_PCIBIOS
10878 cmp ah, #0xb1
10879 jne int1a_normal
10880 call pcibios_real
10881 jc pcibios_error
10882 retf 2
10883 pcibios_error:
10884 mov bl, ah
10885 mov ah, #0xb1
10886 push ds
10887 pusha
10888 mov ax, ss ; set readable descriptor to ds, for calling pcibios
10889 mov ds, ax ; on 16bit protected mode.
10890 jmp int1a_callfunction
10891 int1a_normal:
10892 #endif
10893 push ds
10894 pusha
10895 xor ax, ax
10896 mov ds, ax
10897 int1a_callfunction:
10898 call _int1a_function
10899 popa
10900 pop ds
10901 iret
10904 ;; int70h: IRQ8 - CMOS RTC
10906 int70_handler:
10907 push ds
10908 pushad
10909 xor ax, ax
10910 mov ds, ax
10911 call _int70_function
10912 popad
10913 pop ds
10914 iret
10916 ;---------
10917 ;- INT08 -
10918 ;---------
10919 .org 0xfea5 ; INT 08h System Timer ISR Entry Point
10920 int08_handler:
10922 push eax
10923 push ds
10924 xor ax, ax
10925 mov ds, ax
10927 ;; time to turn off drive(s)?
10928 mov al,0x0440
10929 or al,al
10930 jz int08_floppy_off
10931 dec al
10932 mov 0x0440,al
10933 jnz int08_floppy_off
10934 ;; turn motor(s) off
10935 push dx
10936 mov dx,#0x03f2
10937 in al,dx
10938 and al,#0xcf
10939 out dx,al
10940 pop dx
10941 int08_floppy_off:
10943 mov eax, 0x046c ;; get ticks dword
10944 inc eax
10946 ;; compare eax to one days worth of timer ticks at 18.2 hz
10947 cmp eax, #0x001800B0
10948 jb int08_store_ticks
10949 ;; there has been a midnight rollover at this point
10950 xor eax, eax ;; zero out counter
10951 inc BYTE 0x0470 ;; increment rollover flag
10953 int08_store_ticks:
10954 mov 0x046c, eax ;; store new ticks dword
10955 ;; chain to user timer tick INT #0x1c
10956 //pushf
10957 //;; call_ep [ds:loc]
10958 //CALL_EP( 0x1c << 2 )
10959 int #0x1c
10961 call eoi_master_pic
10962 pop ds
10963 pop eax
10964 iret
10966 .org 0xfef3 ; Initial Interrupt Vector Offsets Loaded by POST
10969 .org 0xff00
10970 .ascii BIOS_COPYRIGHT_STRING
10972 ;------------------------------------------------
10973 ;- IRET Instruction for Dummy Interrupt Handler -
10974 ;------------------------------------------------
10975 .org 0xff53 ; IRET Instruction for Dummy Interrupt Handler
10976 dummy_iret_handler:
10977 iret
10979 .org 0xff54 ; INT 05h Print Screen Service Entry Point
10980 HALT(__LINE__)
10981 iret
10983 .org 0xfff0 ; Power-up Entry Point
10984 jmp 0xf000:post
10986 .org 0xfff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
10987 .ascii BIOS_BUILD_DATE
10989 .org 0xfffe ; System Model ID
10990 db SYS_MODEL_ID
10991 db 0x00 ; filler
10993 .org 0xfa6e ;; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
10994 ASM_END
10996 * This font comes from the fntcol16.zip package (c) by Joseph Gil
10997 * found at ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip
10998 * This font is public domain
11000 static Bit8u vgafont8[128*8]=
11002 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
11003 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e,
11004 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e,
11005 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00,
11006 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00,
11007 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c,
11008 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c,
11009 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00,
11010 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff,
11011 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
11012 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff,
11013 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78,
11014 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18,
11015 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0,
11016 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0,
11017 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99,
11018 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00,
11019 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00,
11020 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18,
11021 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
11022 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00,
11023 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78,
11024 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00,
11025 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff,
11026 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00,
11027 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
11028 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00,
11029 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
11030 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00,
11031 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00,
11032 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00,
11033 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00,
11034 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
11035 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00,
11036 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
11037 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00,
11038 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00,
11039 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00,
11040 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00,
11041 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
11042 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00,
11043 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
11044 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
11045 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00,
11046 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60,
11047 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
11048 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00,
11049 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00,
11050 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00,
11051 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00,
11052 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00,
11053 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00,
11054 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00,
11055 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00,
11056 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00,
11057 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00,
11058 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00,
11059 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00,
11060 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00,
11061 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60,
11062 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00,
11063 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00,
11064 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00,
11065 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00,
11066 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00,
11067 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00,
11068 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00,
11069 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00,
11070 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00,
11071 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00,
11072 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00,
11073 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00,
11074 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00,
11075 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
11076 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00,
11077 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00,
11078 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00,
11079 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00,
11080 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00,
11081 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00,
11082 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
11083 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00,
11084 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00,
11085 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00,
11086 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
11087 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00,
11088 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
11089 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00,
11090 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00,
11091 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00,
11092 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00,
11093 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
11094 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00,
11095 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
11096 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
11097 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
11098 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
11099 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00,
11100 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00,
11101 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00,
11102 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00,
11103 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
11104 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00,
11105 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
11106 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00,
11107 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
11108 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78,
11109 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00,
11110 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
11111 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00,
11112 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
11113 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
11114 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0,
11115 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e,
11116 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00,
11117 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00,
11118 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00,
11119 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
11120 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
11121 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00,
11122 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00,
11123 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
11124 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00,
11125 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00,
11126 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
11127 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00,
11128 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
11129 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00,
11132 ASM_START
11133 .org 0xcc00
11134 bios_table_area_end:
11135 // bcc-generated data will be placed here
11136 ASM_END