- implemented LBA48 support (since BIOS functions are limited to 32 bit, the
[gplbios.git] / rombios.c
blobc35d773b1dd88bcd0d6b2ab730066f1ca5108a54
1 /////////////////////////////////////////////////////////////////////////
2 // $Id$
3 /////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (C) 2002 MandrakeSoft S.A.
6 //
7 // MandrakeSoft S.A.
8 // 43, rue d'Aboukir
9 // 75002 Paris - France
10 // http://www.linux-mandrake.com/
11 // http://www.mandrakesoft.com/
13 // This library is free software; you can redistribute it and/or
14 // modify it under the terms of the GNU Lesser General Public
15 // License as published by the Free Software Foundation; either
16 // version 2 of the License, or (at your option) any later version.
18 // This library is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 // Lesser General Public License for more details.
23 // You should have received a copy of the GNU Lesser General Public
24 // License along with this library; if not, write to the Free Software
25 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27 // ROM BIOS for use with Bochs/Plex86/QEMU emulation environment
30 // ROM BIOS compatability entry points:
31 // ===================================
32 // $e05b ; POST Entry Point
33 // $e2c3 ; NMI Handler Entry Point
34 // $e3fe ; INT 13h Fixed Disk Services Entry Point
35 // $e401 ; Fixed Disk Parameter Table
36 // $e6f2 ; INT 19h Boot Load Service Entry Point
37 // $e6f5 ; Configuration Data Table
38 // $e729 ; Baud Rate Generator Table
39 // $e739 ; INT 14h Serial Communications Service Entry Point
40 // $e82e ; INT 16h Keyboard Service Entry Point
41 // $e987 ; INT 09h Keyboard Service Entry Point
42 // $ec59 ; INT 13h Diskette Service Entry Point
43 // $ef57 ; INT 0Eh Diskette Hardware ISR Entry Point
44 // $efc7 ; Diskette Controller Parameter Table
45 // $efd2 ; INT 17h Printer Service Entry Point
46 // $f045 ; INT 10 Functions 0-Fh Entry Point
47 // $f065 ; INT 10h Video Support Service Entry Point
48 // $f0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
49 // $f841 ; INT 12h Memory Size Service Entry Point
50 // $f84d ; INT 11h Equipment List Service Entry Point
51 // $f859 ; INT 15h System Services Entry Point
52 // $fa6e ; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
53 // $fe6e ; INT 1Ah Time-of-day Service Entry Point
54 // $fea5 ; INT 08h System Timer ISR Entry Point
55 // $fef3 ; Initial Interrupt Vector Offsets Loaded by POST
56 // $ff53 ; IRET Instruction for Dummy Interrupt Handler
57 // $ff54 ; INT 05h Print Screen Service Entry Point
58 // $fff0 ; Power-up Entry Point
59 // $fff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
60 // $fffe ; System Model ID
62 // NOTES for ATA/ATAPI driver (cbbochs@free.fr)
63 // Features
64 // - supports up to 4 ATA interfaces
65 // - device/geometry detection
66 // - 16bits/32bits device access
67 // - pchs/lba access
68 // - datain/dataout/packet command support
70 // NOTES for El-Torito Boot (cbbochs@free.fr)
71 // - CD-ROM booting is only available if ATA/ATAPI Driver is available
72 // - Current code is only able to boot mono-session cds
73 // - Current code can not boot and emulate a hard-disk
74 // the bios will panic otherwise
75 // - Current code also use memory in EBDA segement.
76 // - I used cmos byte 0x3D to store extended information on boot-device
77 // - Code has to be modified modified to handle multiple cdrom drives
78 // - Here are the cdrom boot failure codes:
79 // 1 : no atapi device found
80 // 2 : no atapi cdrom found
81 // 3 : can not read cd - BRVD
82 // 4 : cd is not eltorito (BRVD)
83 // 5 : cd is not eltorito (ISO TAG)
84 // 6 : cd is not eltorito (ELTORITO TAG)
85 // 7 : can not read cd - boot catalog
86 // 8 : boot catalog : bad header
87 // 9 : boot catalog : bad platform
88 // 10 : boot catalog : bad signature
89 // 11 : boot catalog : bootable flag not set
90 // 12 : can not read cd - boot image
92 // ATA driver
93 // - EBDA segment.
94 // I used memory starting at 0x121 in the segment
95 // - the translation policy is defined in cmos regs 0x39 & 0x3a
97 // TODO :
99 // int74
100 // - needs to be reworked. Uses direct [bp] offsets. (?)
102 // int13:
103 // - f04 (verify sectors) isn't complete (?)
104 // - f02/03/04 should set current cyl,etc in BDA (?)
105 // - rewrite int13_relocated & clean up int13 entry code
107 // NOTES:
108 // - NMI access (bit7 of addr written to 70h)
110 // ATA driver
111 // - should handle the "don't detect" bit (cmos regs 0x3b & 0x3c)
112 // - could send the multiple-sector read/write commands
114 // El-Torito
115 // - Emulate a Hard-disk (currently only diskette can be emulated) see "FIXME ElTorito Harddisk"
116 // - Implement remaining int13_cdemu functions (as defined by El-Torito specs)
117 // - cdrom drive is hardcoded to ide 0 device 1 in several places. see "FIXME ElTorito Hardcoded"
118 // - int13 Fix DL when emulating a cd. In that case DL is decremented before calling real int13.
119 // This is ok. But DL should be reincremented afterwards.
120 // - Fix all "FIXME ElTorito Various"
121 // - should be able to boot any cdrom instead of the first one
123 // BCC Bug: find a generic way to handle the bug of #asm after an "if" (fixed in 0.16.7)
125 #include "rombios.h"
127 #define DEBUG_ATA 0
128 #define DEBUG_INT13_HD 0
129 #define DEBUG_INT13_CD 0
130 #define DEBUG_INT13_ET 0
131 #define DEBUG_INT13_FL 0
132 #define DEBUG_INT15 0
133 #define DEBUG_INT16 0
134 #define DEBUG_INT1A 0
135 #define DEBUG_INT74 0
136 #define DEBUG_APM 0
138 #define BX_CPU 3
139 #define BX_USE_PS2_MOUSE 1
140 #define BX_CALL_INT15_4F 1
141 #define BX_USE_EBDA 1
142 #define BX_SUPPORT_FLOPPY 1
143 #define BX_FLOPPY_ON_CNT 37 /* 2 seconds */
144 #define BX_PCIBIOS 1
145 #define BX_APM 1
147 #define BX_USE_ATADRV 1
148 #define BX_ELTORITO_BOOT 1
150 #define BX_MAX_ATA_INTERFACES 4
151 #define BX_MAX_ATA_DEVICES (BX_MAX_ATA_INTERFACES*2)
153 #define BX_VIRTUAL_PORTS 1 /* normal output to Bochs ports */
154 #define BX_DEBUG_SERIAL 0 /* output to COM1 */
156 /* model byte 0xFC = AT */
157 #define SYS_MODEL_ID 0xFC
158 #define SYS_SUBMODEL_ID 0x00
159 #define BIOS_REVISION 1
160 #define BIOS_CONFIG_TABLE 0xe6f5
162 #ifndef BIOS_BUILD_DATE
163 # define BIOS_BUILD_DATE "06/23/99"
164 #endif
166 // 1K of base memory used for Extended Bios Data Area (EBDA)
167 // EBDA is used for PS/2 mouse support, and IDE BIOS, etc.
168 #define EBDA_SEG 0x9FC0
169 #define EBDA_SIZE 1 // In KiB
170 #define BASE_MEM_IN_K (640 - EBDA_SIZE)
172 /* 256 bytes at 0x9ff00 -- 0x9ffff is used for the IPL boot table. */
173 #define IPL_SEG 0x9ff0
174 #define IPL_TABLE_OFFSET 0x0000
175 #define IPL_TABLE_ENTRIES 8
176 #define IPL_COUNT_OFFSET 0x0080 /* u16: number of valid table entries */
177 #define IPL_SEQUENCE_OFFSET 0x0082 /* u16: next boot device */
178 #define IPL_SIZE 0xff
179 #define IPL_TYPE_FLOPPY 0x01
180 #define IPL_TYPE_HARDDISK 0x02
181 #define IPL_TYPE_CDROM 0x03
182 #define IPL_TYPE_BEV 0x80
184 // Define the application NAME
185 #if defined(BX_QEMU)
186 # define BX_APPNAME "QEMU"
187 #elif defined(PLEX86)
188 # define BX_APPNAME "Plex86"
189 #else
190 # define BX_APPNAME "Bochs"
191 #endif
193 // Sanity Checks
194 #if BX_USE_ATADRV && BX_CPU<3
195 # error The ATA/ATAPI Driver can only to be used with a 386+ cpu
196 #endif
197 #if BX_USE_ATADRV && !BX_USE_EBDA
198 # error ATA/ATAPI Driver can only be used if EBDA is available
199 #endif
200 #if BX_ELTORITO_BOOT && !BX_USE_ATADRV
201 # error El-Torito Boot can only be use if ATA/ATAPI Driver is available
202 #endif
203 #if BX_PCIBIOS && BX_CPU<3
204 # error PCI BIOS can only be used with 386+ cpu
205 #endif
206 #if BX_APM && BX_CPU<3
207 # error APM BIOS can only be used with 386+ cpu
208 #endif
210 // define this if you want to make PCIBIOS working on a specific bridge only
211 // undef enables PCIBIOS when at least one PCI device is found
212 // i440FX is emulated by Bochs and QEMU
213 #define PCI_FIXED_HOST_BRIDGE 0x12378086 ;; i440FX PCI bridge
215 // #20 is dec 20
216 // #$20 is hex 20 = 32
217 // #0x20 is hex 20 = 32
218 // LDA #$20
219 // JSR $E820
220 // LDD .i,S
221 // JSR $C682
222 // mov al, #$20
224 // all hex literals should be prefixed with '0x'
225 // grep "#[0-9a-fA-F][0-9a-fA-F]" rombios.c
226 // no mov SEG-REG, #value, must mov register into seg-reg
227 // grep -i "mov[ ]*.s" rombios.c
229 // This is for compiling with gcc2 and gcc3
230 #define ASM_START #asm
231 #define ASM_END #endasm
233 ASM_START
234 .rom
236 .org 0x0000
238 #if BX_CPU >= 3
239 use16 386
240 #else
241 use16 286
242 #endif
244 MACRO HALT
245 ;; the HALT macro is called with the line number of the HALT call.
246 ;; The line number is then sent to the PANIC_PORT, causing Bochs/Plex
247 ;; to print a BX_PANIC message. This will normally halt the simulation
248 ;; with a message such as "BIOS panic at rombios.c, line 4091".
249 ;; However, users can choose to make panics non-fatal and continue.
250 #if BX_VIRTUAL_PORTS
251 mov dx,#PANIC_PORT
252 mov ax,#?1
253 out dx,ax
254 #else
255 mov dx,#0x80
256 mov ax,#?1
257 out dx,al
258 #endif
259 MEND
261 MACRO JMP_AP
262 db 0xea
263 dw ?2
264 dw ?1
265 MEND
267 MACRO SET_INT_VECTOR
268 mov ax, ?3
269 mov ?1*4, ax
270 mov ax, ?2
271 mov ?1*4+2, ax
272 MEND
274 ASM_END
276 typedef unsigned char Bit8u;
277 typedef unsigned short Bit16u;
278 typedef unsigned short bx_bool;
279 typedef unsigned long Bit32u;
282 void memsetb(seg,offset,value,count);
283 void memcpyb(dseg,doffset,sseg,soffset,count);
284 void memcpyd(dseg,doffset,sseg,soffset,count);
286 // memset of count bytes
287 void
288 memsetb(seg,offset,value,count)
289 Bit16u seg;
290 Bit16u offset;
291 Bit16u value;
292 Bit16u count;
294 ASM_START
295 push bp
296 mov bp, sp
298 push ax
299 push cx
300 push es
301 push di
303 mov cx, 10[bp] ; count
304 test cx, cx
305 je memsetb_end
306 mov ax, 4[bp] ; segment
307 mov es, ax
308 mov ax, 6[bp] ; offset
309 mov di, ax
310 mov al, 8[bp] ; value
313 stosb
315 memsetb_end:
316 pop di
317 pop es
318 pop cx
319 pop ax
321 pop bp
322 ASM_END
325 // memcpy of count bytes
326 void
327 memcpyb(dseg,doffset,sseg,soffset,count)
328 Bit16u dseg;
329 Bit16u doffset;
330 Bit16u sseg;
331 Bit16u soffset;
332 Bit16u count;
334 ASM_START
335 push bp
336 mov bp, sp
338 push ax
339 push cx
340 push es
341 push di
342 push ds
343 push si
345 mov cx, 12[bp] ; count
346 test cx, cx
347 je memcpyb_end
348 mov ax, 4[bp] ; dsegment
349 mov es, ax
350 mov ax, 6[bp] ; doffset
351 mov di, ax
352 mov ax, 8[bp] ; ssegment
353 mov ds, ax
354 mov ax, 10[bp] ; soffset
355 mov si, ax
358 movsb
360 memcpyb_end:
361 pop si
362 pop ds
363 pop di
364 pop es
365 pop cx
366 pop ax
368 pop bp
369 ASM_END
372 // memcpy of count dword
373 void
374 memcpyd(dseg,doffset,sseg,soffset,count)
375 Bit16u dseg;
376 Bit16u doffset;
377 Bit16u sseg;
378 Bit16u soffset;
379 Bit16u count;
381 ASM_START
382 push bp
383 mov bp, sp
385 push ax
386 push cx
387 push es
388 push di
389 push ds
390 push si
392 mov cx, 12[bp] ; count
393 test cx, cx
394 je memcpyd_end
395 mov ax, 4[bp] ; dsegment
396 mov es, ax
397 mov ax, 6[bp] ; doffset
398 mov di, ax
399 mov ax, 8[bp] ; ssegment
400 mov ds, ax
401 mov ax, 10[bp] ; soffset
402 mov si, ax
405 movsd
407 memcpyd_end:
408 pop si
409 pop ds
410 pop di
411 pop es
412 pop cx
413 pop ax
415 pop bp
416 ASM_END
419 // read_dword and write_dword functions
420 static Bit32u read_dword();
421 static void write_dword();
423 Bit32u
424 read_dword(seg, offset)
425 Bit16u seg;
426 Bit16u offset;
428 ASM_START
429 push bp
430 mov bp, sp
432 push bx
433 push ds
434 mov ax, 4[bp] ; segment
435 mov ds, ax
436 mov bx, 6[bp] ; offset
437 mov ax, [bx]
438 add bx, #2
439 mov dx, [bx]
440 ;; ax = return value (word)
441 ;; dx = return value (word)
442 pop ds
443 pop bx
445 pop bp
446 ASM_END
449 void
450 write_dword(seg, offset, data)
451 Bit16u seg;
452 Bit16u offset;
453 Bit32u data;
455 ASM_START
456 push bp
457 mov bp, sp
459 push ax
460 push bx
461 push ds
462 mov ax, 4[bp] ; segment
463 mov ds, ax
464 mov bx, 6[bp] ; offset
465 mov ax, 8[bp] ; data word
466 mov [bx], ax ; write data word
467 add bx, #2
468 mov ax, 10[bp] ; data word
469 mov [bx], ax ; write data word
470 pop ds
471 pop bx
472 pop ax
474 pop bp
475 ASM_END
478 // Bit32u (unsigned long) and long helper functions
479 ASM_START
481 ;; and function
482 landl:
483 landul:
484 SEG SS
485 and ax,[di]
486 SEG SS
487 and bx,2[di]
490 ;; add function
491 laddl:
492 laddul:
493 SEG SS
494 add ax,[di]
495 SEG SS
496 adc bx,2[di]
499 ;; cmp function
500 lcmpl:
501 lcmpul:
502 and eax, #0x0000FFFF
503 shl ebx, #16
504 or eax, ebx
505 shr ebx, #16
506 SEG SS
507 cmp eax, dword ptr [di]
510 ;; sub function
511 lsubl:
512 lsubul:
513 SEG SS
514 sub ax,[di]
515 SEG SS
516 sbb bx,2[di]
519 ;; mul function
520 lmull:
521 lmulul:
522 and eax, #0x0000FFFF
523 shl ebx, #16
524 or eax, ebx
525 SEG SS
526 mul eax, dword ptr [di]
527 mov ebx, eax
528 shr ebx, #16
531 ;; dec function
532 ldecl:
533 ldecul:
534 SEG SS
535 dec dword ptr [bx]
538 ;; or function
539 lorl:
540 lorul:
541 SEG SS
542 or ax,[di]
543 SEG SS
544 or bx,2[di]
547 ;; inc function
548 lincl:
549 lincul:
550 SEG SS
551 inc dword ptr [bx]
554 ;; tst function
555 ltstl:
556 ltstul:
557 and eax, #0x0000FFFF
558 shl ebx, #16
559 or eax, ebx
560 shr ebx, #16
561 test eax, eax
564 ;; sr function
565 lsrul:
566 mov cx,di
567 jcxz lsr_exit
568 and eax, #0x0000FFFF
569 shl ebx, #16
570 or eax, ebx
571 lsr_loop:
572 shr eax, #1
573 loop lsr_loop
574 mov ebx, eax
575 shr ebx, #16
576 lsr_exit:
579 ;; sl function
580 lsll:
581 lslul:
582 mov cx,di
583 jcxz lsl_exit
584 and eax, #0x0000FFFF
585 shl ebx, #16
586 or eax, ebx
587 lsl_loop:
588 shl eax, #1
589 loop lsl_loop
590 mov ebx, eax
591 shr ebx, #16
592 lsl_exit:
595 idiv_:
597 idiv bx
600 idiv_u:
601 xor dx,dx
602 div bx
605 ldivul:
606 and eax, #0x0000FFFF
607 shl ebx, #16
608 or eax, ebx
609 xor edx, edx
610 SEG SS
611 mov bx, 2[di]
612 shl ebx, #16
613 SEG SS
614 mov bx, [di]
615 div ebx
616 mov ebx, eax
617 shr ebx, #16
620 ASM_END
622 // for access to RAM area which is used by interrupt vectors
623 // and BIOS Data Area
625 typedef struct {
626 unsigned char filler1[0x400];
627 unsigned char filler2[0x6c];
628 Bit16u ticks_low;
629 Bit16u ticks_high;
630 Bit8u midnight_flag;
631 } bios_data_t;
633 #define BiosData ((bios_data_t *) 0)
635 #if BX_USE_ATADRV
636 typedef struct {
637 Bit16u heads; // # heads
638 Bit16u cylinders; // # cylinders
639 Bit16u spt; // # sectors / track
640 } chs_t;
642 // DPTE definition
643 typedef struct {
644 Bit16u iobase1;
645 Bit16u iobase2;
646 Bit8u prefix;
647 Bit8u unused;
648 Bit8u irq;
649 Bit8u blkcount;
650 Bit8u dma;
651 Bit8u pio;
652 Bit16u options;
653 Bit16u reserved;
654 Bit8u revision;
655 Bit8u checksum;
656 } dpte_t;
658 typedef struct {
659 Bit8u iface; // ISA or PCI
660 Bit16u iobase1; // IO Base 1
661 Bit16u iobase2; // IO Base 2
662 Bit8u irq; // IRQ
663 } ata_channel_t;
665 typedef struct {
666 Bit8u type; // Detected type of ata (ata/atapi/none/unknown)
667 Bit8u device; // Detected type of attached devices (hd/cd/none)
668 Bit8u removable; // Removable device flag
669 Bit8u lock; // Locks for removable devices
670 Bit8u mode; // transfer mode : PIO 16/32 bits - IRQ - ISADMA - PCIDMA
671 Bit16u blksize; // block size
673 Bit8u translation; // type of translation
674 chs_t lchs; // Logical CHS
675 chs_t pchs; // Physical CHS
677 Bit32u sectors; // Total sectors count
678 } ata_device_t;
680 typedef struct {
681 // ATA channels info
682 ata_channel_t channels[BX_MAX_ATA_INTERFACES];
684 // ATA devices info
685 ata_device_t devices[BX_MAX_ATA_DEVICES];
687 // map between (bios hd id - 0x80) and ata channels
688 Bit8u hdcount, hdidmap[BX_MAX_ATA_DEVICES];
690 // map between (bios cd id - 0xE0) and ata channels
691 Bit8u cdcount, cdidmap[BX_MAX_ATA_DEVICES];
693 // Buffer for DPTE table
694 dpte_t dpte;
696 // Count of transferred sectors and bytes
697 Bit16u trsfsectors;
698 Bit32u trsfbytes;
700 } ata_t;
702 #if BX_ELTORITO_BOOT
703 // ElTorito Device Emulation data
704 typedef struct {
705 Bit8u active;
706 Bit8u media;
707 Bit8u emulated_drive;
708 Bit8u controller_index;
709 Bit16u device_spec;
710 Bit32u ilba;
711 Bit16u buffer_segment;
712 Bit16u load_segment;
713 Bit16u sector_count;
715 // Virtual device
716 chs_t vdevice;
717 } cdemu_t;
718 #endif // BX_ELTORITO_BOOT
720 // for access to EBDA area
721 // The EBDA structure should conform to
722 // http://www.frontiernet.net/~fys/rombios.htm document
723 // I made the ata and cdemu structs begin at 0x121 in the EBDA seg
724 // EBDA must be at most 768 bytes; it lives at 0x9fc00, and the boot
725 // device tables are at 0x9ff00 -- 0x9ffff
726 typedef struct {
727 unsigned char filler1[0x3D];
729 // FDPT - Can be splitted in data members if needed
730 unsigned char fdpt0[0x10];
731 unsigned char fdpt1[0x10];
733 unsigned char filler2[0xC4];
735 // ATA Driver data
736 ata_t ata;
738 #if BX_ELTORITO_BOOT
739 // El Torito Emulation data
740 cdemu_t cdemu;
741 #endif // BX_ELTORITO_BOOT
743 } ebda_data_t;
745 #define EbdaData ((ebda_data_t *) 0)
747 // for access to the int13ext structure
748 typedef struct {
749 Bit8u size;
750 Bit8u reserved;
751 Bit16u count;
752 Bit16u offset;
753 Bit16u segment;
754 Bit32u lba1;
755 Bit32u lba2;
756 } int13ext_t;
758 #define Int13Ext ((int13ext_t *) 0)
760 // Disk Physical Table definition
761 typedef struct {
762 Bit16u size;
763 Bit16u infos;
764 Bit32u cylinders;
765 Bit32u heads;
766 Bit32u spt;
767 Bit32u sector_count1;
768 Bit32u sector_count2;
769 Bit16u blksize;
770 Bit16u dpte_offset;
771 Bit16u dpte_segment;
772 Bit16u key;
773 Bit8u dpi_length;
774 Bit8u reserved1;
775 Bit16u reserved2;
776 Bit8u host_bus[4];
777 Bit8u iface_type[8];
778 Bit8u iface_path[8];
779 Bit8u device_path[8];
780 Bit8u reserved3;
781 Bit8u checksum;
782 } dpt_t;
784 #define Int13DPT ((dpt_t *) 0)
786 #endif // BX_USE_ATADRV
788 typedef struct {
789 union {
790 struct {
791 Bit16u di, si, bp, sp;
792 Bit16u bx, dx, cx, ax;
793 } r16;
794 struct {
795 Bit16u filler[4];
796 Bit8u bl, bh, dl, dh, cl, ch, al, ah;
797 } r8;
798 } u;
799 } pusha_regs_t;
801 typedef struct {
802 union {
803 struct {
804 Bit32u edi, esi, ebp, esp;
805 Bit32u ebx, edx, ecx, eax;
806 } r32;
807 struct {
808 Bit16u di, filler1, si, filler2, bp, filler3, sp, filler4;
809 Bit16u bx, filler5, dx, filler6, cx, filler7, ax, filler8;
810 } r16;
811 struct {
812 Bit32u filler[4];
813 Bit8u bl, bh;
814 Bit16u filler1;
815 Bit8u dl, dh;
816 Bit16u filler2;
817 Bit8u cl, ch;
818 Bit16u filler3;
819 Bit8u al, ah;
820 Bit16u filler4;
821 } r8;
822 } u;
823 } pushad_regs_t;
825 typedef struct {
826 union {
827 struct {
828 Bit16u flags;
829 } r16;
830 struct {
831 Bit8u flagsl;
832 Bit8u flagsh;
833 } r8;
834 } u;
835 } flags_t;
837 #define SetCF(x) x.u.r8.flagsl |= 0x01
838 #define SetZF(x) x.u.r8.flagsl |= 0x40
839 #define ClearCF(x) x.u.r8.flagsl &= 0xfe
840 #define ClearZF(x) x.u.r8.flagsl &= 0xbf
841 #define GetCF(x) (x.u.r8.flagsl & 0x01)
843 typedef struct {
844 Bit16u ip;
845 Bit16u cs;
846 flags_t flags;
847 } iret_addr_t;
849 typedef struct {
850 Bit16u type;
851 Bit16u flags;
852 Bit32u vector;
853 Bit32u description;
854 Bit32u reserved;
855 } ipl_entry_t;
859 static Bit8u inb();
860 static Bit8u inb_cmos();
861 static void outb();
862 static void outb_cmos();
863 static Bit16u inw();
864 static void outw();
865 static void init_rtc();
866 static bx_bool rtc_updating();
868 static Bit8u read_byte();
869 static Bit16u read_word();
870 static void write_byte();
871 static void write_word();
872 static void bios_printf();
874 static Bit8u inhibit_mouse_int_and_events();
875 static void enable_mouse_int_and_events();
876 static Bit8u send_to_mouse_ctrl();
877 static Bit8u get_mouse_data();
878 static void set_kbd_command_byte();
880 static void int09_function();
881 static void int13_harddisk();
882 static void int13_cdrom();
883 static void int13_cdemu();
884 static void int13_eltorito();
885 static void int13_diskette_function();
886 static void int14_function();
887 static void int15_function();
888 static void int16_function();
889 static void int17_function();
890 static void int19_function();
891 static void int1a_function();
892 static void int70_function();
893 static void int74_function();
894 static Bit16u get_CS();
895 static Bit16u get_SS();
896 static unsigned int enqueue_key();
897 static unsigned int dequeue_key();
898 static void get_hd_geometry();
899 static void set_diskette_ret_status();
900 static void set_diskette_current_cyl();
901 static void determine_floppy_media();
902 static bx_bool floppy_drive_exists();
903 static bx_bool floppy_drive_recal();
904 static bx_bool floppy_media_known();
905 static bx_bool floppy_media_sense();
906 static bx_bool set_enable_a20();
907 static void debugger_on();
908 static void debugger_off();
909 static void keyboard_init();
910 static void keyboard_panic();
911 static void shutdown_status_panic();
912 static void nmi_handler_msg();
914 static void print_bios_banner();
915 static void print_boot_device();
916 static void print_boot_failure();
917 static void print_cdromboot_failure();
919 # if BX_USE_ATADRV
921 // ATA / ATAPI driver
922 void ata_init();
923 void ata_detect();
924 void ata_reset();
926 Bit16u ata_cmd_non_data();
927 Bit16u ata_cmd_data_in();
928 Bit16u ata_cmd_data_out();
929 Bit16u ata_cmd_packet();
931 Bit16u atapi_get_sense();
932 Bit16u atapi_is_ready();
933 Bit16u atapi_is_cdrom();
935 #endif // BX_USE_ATADRV
937 #if BX_ELTORITO_BOOT
939 void cdemu_init();
940 Bit8u cdemu_isactive();
941 Bit8u cdemu_emulated_drive();
943 Bit16u cdrom_boot();
945 #endif // BX_ELTORITO_BOOT
947 static char bios_cvs_version_string[] = "$Revision$ $Date$";
949 #define BIOS_COPYRIGHT_STRING "(c) 2002 MandrakeSoft S.A. Written by Kevin Lawton & the Bochs team."
951 #if DEBUG_ATA
952 # define BX_DEBUG_ATA(a...) BX_DEBUG(a)
953 #else
954 # define BX_DEBUG_ATA(a...)
955 #endif
956 #if DEBUG_INT13_HD
957 # define BX_DEBUG_INT13_HD(a...) BX_DEBUG(a)
958 #else
959 # define BX_DEBUG_INT13_HD(a...)
960 #endif
961 #if DEBUG_INT13_CD
962 # define BX_DEBUG_INT13_CD(a...) BX_DEBUG(a)
963 #else
964 # define BX_DEBUG_INT13_CD(a...)
965 #endif
966 #if DEBUG_INT13_ET
967 # define BX_DEBUG_INT13_ET(a...) BX_DEBUG(a)
968 #else
969 # define BX_DEBUG_INT13_ET(a...)
970 #endif
971 #if DEBUG_INT13_FL
972 # define BX_DEBUG_INT13_FL(a...) BX_DEBUG(a)
973 #else
974 # define BX_DEBUG_INT13_FL(a...)
975 #endif
976 #if DEBUG_INT15
977 # define BX_DEBUG_INT15(a...) BX_DEBUG(a)
978 #else
979 # define BX_DEBUG_INT15(a...)
980 #endif
981 #if DEBUG_INT16
982 # define BX_DEBUG_INT16(a...) BX_DEBUG(a)
983 #else
984 # define BX_DEBUG_INT16(a...)
985 #endif
986 #if DEBUG_INT1A
987 # define BX_DEBUG_INT1A(a...) BX_DEBUG(a)
988 #else
989 # define BX_DEBUG_INT1A(a...)
990 #endif
991 #if DEBUG_INT74
992 # define BX_DEBUG_INT74(a...) BX_DEBUG(a)
993 #else
994 # define BX_DEBUG_INT74(a...)
995 #endif
997 #define SET_AL(val8) AX = ((AX & 0xff00) | (val8))
998 #define SET_BL(val8) BX = ((BX & 0xff00) | (val8))
999 #define SET_CL(val8) CX = ((CX & 0xff00) | (val8))
1000 #define SET_DL(val8) DX = ((DX & 0xff00) | (val8))
1001 #define SET_AH(val8) AX = ((AX & 0x00ff) | ((val8) << 8))
1002 #define SET_BH(val8) BX = ((BX & 0x00ff) | ((val8) << 8))
1003 #define SET_CH(val8) CX = ((CX & 0x00ff) | ((val8) << 8))
1004 #define SET_DH(val8) DX = ((DX & 0x00ff) | ((val8) << 8))
1006 #define GET_AL() ( AX & 0x00ff )
1007 #define GET_BL() ( BX & 0x00ff )
1008 #define GET_CL() ( CX & 0x00ff )
1009 #define GET_DL() ( DX & 0x00ff )
1010 #define GET_AH() ( AX >> 8 )
1011 #define GET_BH() ( BX >> 8 )
1012 #define GET_CH() ( CX >> 8 )
1013 #define GET_DH() ( DX >> 8 )
1015 #define GET_ELDL() ( ELDX & 0x00ff )
1016 #define GET_ELDH() ( ELDX >> 8 )
1018 #define SET_CF() FLAGS |= 0x0001
1019 #define CLEAR_CF() FLAGS &= 0xfffe
1020 #define GET_CF() (FLAGS & 0x0001)
1022 #define SET_ZF() FLAGS |= 0x0040
1023 #define CLEAR_ZF() FLAGS &= 0xffbf
1024 #define GET_ZF() (FLAGS & 0x0040)
1026 #define UNSUPPORTED_FUNCTION 0x86
1028 #define none 0
1029 #define MAX_SCAN_CODE 0x58
1031 static struct {
1032 Bit16u normal;
1033 Bit16u shift;
1034 Bit16u control;
1035 Bit16u alt;
1036 Bit8u lock_flags;
1037 } scan_to_scanascii[MAX_SCAN_CODE + 1] = {
1038 { none, none, none, none, none },
1039 { 0x011b, 0x011b, 0x011b, 0x0100, none }, /* escape */
1040 { 0x0231, 0x0221, none, 0x7800, none }, /* 1! */
1041 { 0x0332, 0x0340, 0x0300, 0x7900, none }, /* 2@ */
1042 { 0x0433, 0x0423, none, 0x7a00, none }, /* 3# */
1043 { 0x0534, 0x0524, none, 0x7b00, none }, /* 4$ */
1044 { 0x0635, 0x0625, none, 0x7c00, none }, /* 5% */
1045 { 0x0736, 0x075e, 0x071e, 0x7d00, none }, /* 6^ */
1046 { 0x0837, 0x0826, none, 0x7e00, none }, /* 7& */
1047 { 0x0938, 0x092a, none, 0x7f00, none }, /* 8* */
1048 { 0x0a39, 0x0a28, none, 0x8000, none }, /* 9( */
1049 { 0x0b30, 0x0b29, none, 0x8100, none }, /* 0) */
1050 { 0x0c2d, 0x0c5f, 0x0c1f, 0x8200, none }, /* -_ */
1051 { 0x0d3d, 0x0d2b, none, 0x8300, none }, /* =+ */
1052 { 0x0e08, 0x0e08, 0x0e7f, none, none }, /* backspace */
1053 { 0x0f09, 0x0f00, none, none, none }, /* tab */
1054 { 0x1071, 0x1051, 0x1011, 0x1000, 0x40 }, /* Q */
1055 { 0x1177, 0x1157, 0x1117, 0x1100, 0x40 }, /* W */
1056 { 0x1265, 0x1245, 0x1205, 0x1200, 0x40 }, /* E */
1057 { 0x1372, 0x1352, 0x1312, 0x1300, 0x40 }, /* R */
1058 { 0x1474, 0x1454, 0x1414, 0x1400, 0x40 }, /* T */
1059 { 0x1579, 0x1559, 0x1519, 0x1500, 0x40 }, /* Y */
1060 { 0x1675, 0x1655, 0x1615, 0x1600, 0x40 }, /* U */
1061 { 0x1769, 0x1749, 0x1709, 0x1700, 0x40 }, /* I */
1062 { 0x186f, 0x184f, 0x180f, 0x1800, 0x40 }, /* O */
1063 { 0x1970, 0x1950, 0x1910, 0x1900, 0x40 }, /* P */
1064 { 0x1a5b, 0x1a7b, 0x1a1b, none, none }, /* [{ */
1065 { 0x1b5d, 0x1b7d, 0x1b1d, none, none }, /* ]} */
1066 { 0x1c0d, 0x1c0d, 0x1c0a, none, none }, /* Enter */
1067 { none, none, none, none, none }, /* L Ctrl */
1068 { 0x1e61, 0x1e41, 0x1e01, 0x1e00, 0x40 }, /* A */
1069 { 0x1f73, 0x1f53, 0x1f13, 0x1f00, 0x40 }, /* S */
1070 { 0x2064, 0x2044, 0x2004, 0x2000, 0x40 }, /* D */
1071 { 0x2166, 0x2146, 0x2106, 0x2100, 0x40 }, /* F */
1072 { 0x2267, 0x2247, 0x2207, 0x2200, 0x40 }, /* G */
1073 { 0x2368, 0x2348, 0x2308, 0x2300, 0x40 }, /* H */
1074 { 0x246a, 0x244a, 0x240a, 0x2400, 0x40 }, /* J */
1075 { 0x256b, 0x254b, 0x250b, 0x2500, 0x40 }, /* K */
1076 { 0x266c, 0x264c, 0x260c, 0x2600, 0x40 }, /* L */
1077 { 0x273b, 0x273a, none, none, none }, /* ;: */
1078 { 0x2827, 0x2822, none, none, none }, /* '" */
1079 { 0x2960, 0x297e, none, none, none }, /* `~ */
1080 { none, none, none, none, none }, /* L shift */
1081 { 0x2b5c, 0x2b7c, 0x2b1c, none, none }, /* |\ */
1082 { 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00, 0x40 }, /* Z */
1083 { 0x2d78, 0x2d58, 0x2d18, 0x2d00, 0x40 }, /* X */
1084 { 0x2e63, 0x2e43, 0x2e03, 0x2e00, 0x40 }, /* C */
1085 { 0x2f76, 0x2f56, 0x2f16, 0x2f00, 0x40 }, /* V */
1086 { 0x3062, 0x3042, 0x3002, 0x3000, 0x40 }, /* B */
1087 { 0x316e, 0x314e, 0x310e, 0x3100, 0x40 }, /* N */
1088 { 0x326d, 0x324d, 0x320d, 0x3200, 0x40 }, /* M */
1089 { 0x332c, 0x333c, none, none, none }, /* ,< */
1090 { 0x342e, 0x343e, none, none, none }, /* .> */
1091 { 0x352f, 0x353f, none, none, none }, /* /? */
1092 { none, none, none, none, none }, /* R Shift */
1093 { 0x372a, 0x372a, none, none, none }, /* * */
1094 { none, none, none, none, none }, /* L Alt */
1095 { 0x3920, 0x3920, 0x3920, 0x3920, none }, /* space */
1096 { none, none, none, none, none }, /* caps lock */
1097 { 0x3b00, 0x5400, 0x5e00, 0x6800, none }, /* F1 */
1098 { 0x3c00, 0x5500, 0x5f00, 0x6900, none }, /* F2 */
1099 { 0x3d00, 0x5600, 0x6000, 0x6a00, none }, /* F3 */
1100 { 0x3e00, 0x5700, 0x6100, 0x6b00, none }, /* F4 */
1101 { 0x3f00, 0x5800, 0x6200, 0x6c00, none }, /* F5 */
1102 { 0x4000, 0x5900, 0x6300, 0x6d00, none }, /* F6 */
1103 { 0x4100, 0x5a00, 0x6400, 0x6e00, none }, /* F7 */
1104 { 0x4200, 0x5b00, 0x6500, 0x6f00, none }, /* F8 */
1105 { 0x4300, 0x5c00, 0x6600, 0x7000, none }, /* F9 */
1106 { 0x4400, 0x5d00, 0x6700, 0x7100, none }, /* F10 */
1107 { none, none, none, none, none }, /* Num Lock */
1108 { none, none, none, none, none }, /* Scroll Lock */
1109 { 0x4700, 0x4737, 0x7700, none, 0x20 }, /* 7 Home */
1110 { 0x4800, 0x4838, none, none, 0x20 }, /* 8 UP */
1111 { 0x4900, 0x4939, 0x8400, none, 0x20 }, /* 9 PgUp */
1112 { 0x4a2d, 0x4a2d, none, none, none }, /* - */
1113 { 0x4b00, 0x4b34, 0x7300, none, 0x20 }, /* 4 Left */
1114 { 0x4c00, 0x4c35, none, none, 0x20 }, /* 5 */
1115 { 0x4d00, 0x4d36, 0x7400, none, 0x20 }, /* 6 Right */
1116 { 0x4e2b, 0x4e2b, none, none, none }, /* + */
1117 { 0x4f00, 0x4f31, 0x7500, none, 0x20 }, /* 1 End */
1118 { 0x5000, 0x5032, none, none, 0x20 }, /* 2 Down */
1119 { 0x5100, 0x5133, 0x7600, none, 0x20 }, /* 3 PgDn */
1120 { 0x5200, 0x5230, none, none, 0x20 }, /* 0 Ins */
1121 { 0x5300, 0x532e, none, none, 0x20 }, /* Del */
1122 { none, none, none, none, none },
1123 { none, none, none, none, none },
1124 { 0x565c, 0x567c, none, none, none }, /* \| */
1125 { 0x5700, 0x5700, none, none, none }, /* F11 */
1126 { 0x5800, 0x5800, none, none, none } /* F12 */
1129 Bit8u
1130 inb(port)
1131 Bit16u port;
1133 ASM_START
1134 push bp
1135 mov bp, sp
1137 push dx
1138 mov dx, 4[bp]
1139 in al, dx
1140 pop dx
1142 pop bp
1143 ASM_END
1146 #if BX_USE_ATADRV
1147 Bit16u
1148 inw(port)
1149 Bit16u port;
1151 ASM_START
1152 push bp
1153 mov bp, sp
1155 push dx
1156 mov dx, 4[bp]
1157 in ax, dx
1158 pop dx
1160 pop bp
1161 ASM_END
1163 #endif
1165 void
1166 outb(port, val)
1167 Bit16u port;
1168 Bit8u val;
1170 ASM_START
1171 push bp
1172 mov bp, sp
1174 push ax
1175 push dx
1176 mov dx, 4[bp]
1177 mov al, 6[bp]
1178 out dx, al
1179 pop dx
1180 pop ax
1182 pop bp
1183 ASM_END
1186 #if BX_USE_ATADRV
1187 void
1188 outw(port, val)
1189 Bit16u port;
1190 Bit16u val;
1192 ASM_START
1193 push bp
1194 mov bp, sp
1196 push ax
1197 push dx
1198 mov dx, 4[bp]
1199 mov ax, 6[bp]
1200 out dx, ax
1201 pop dx
1202 pop ax
1204 pop bp
1205 ASM_END
1207 #endif
1209 void
1210 outb_cmos(cmos_reg, val)
1211 Bit8u cmos_reg;
1212 Bit8u val;
1214 ASM_START
1215 push bp
1216 mov bp, sp
1218 mov al, 4[bp] ;; cmos_reg
1219 out 0x70, al
1220 mov al, 6[bp] ;; val
1221 out 0x71, al
1223 pop bp
1224 ASM_END
1227 Bit8u
1228 inb_cmos(cmos_reg)
1229 Bit8u cmos_reg;
1231 ASM_START
1232 push bp
1233 mov bp, sp
1235 mov al, 4[bp] ;; cmos_reg
1236 out 0x70, al
1237 in al, 0x71
1239 pop bp
1240 ASM_END
1243 void
1244 init_rtc()
1246 outb_cmos(0x0a, 0x26);
1247 outb_cmos(0x0b, 0x02);
1248 inb_cmos(0x0c);
1249 inb_cmos(0x0d);
1252 bx_bool
1253 rtc_updating()
1255 // This function checks to see if the update-in-progress bit
1256 // is set in CMOS Status Register A. If not, it returns 0.
1257 // If it is set, it tries to wait until there is a transition
1258 // to 0, and will return 0 if such a transition occurs. A 1
1259 // is returned only after timing out. The maximum period
1260 // that this bit should be set is constrained to 244useconds.
1261 // The count I use below guarantees coverage or more than
1262 // this time, with any reasonable IPS setting.
1264 Bit16u count;
1266 count = 25000;
1267 while (--count != 0) {
1268 if ( (inb_cmos(0x0a) & 0x80) == 0 )
1269 return(0);
1271 return(1); // update-in-progress never transitioned to 0
1275 Bit8u
1276 read_byte(seg, offset)
1277 Bit16u seg;
1278 Bit16u offset;
1280 ASM_START
1281 push bp
1282 mov bp, sp
1284 push bx
1285 push ds
1286 mov ax, 4[bp] ; segment
1287 mov ds, ax
1288 mov bx, 6[bp] ; offset
1289 mov al, [bx]
1290 ;; al = return value (byte)
1291 pop ds
1292 pop bx
1294 pop bp
1295 ASM_END
1298 Bit16u
1299 read_word(seg, offset)
1300 Bit16u seg;
1301 Bit16u offset;
1303 ASM_START
1304 push bp
1305 mov bp, sp
1307 push bx
1308 push ds
1309 mov ax, 4[bp] ; segment
1310 mov ds, ax
1311 mov bx, 6[bp] ; offset
1312 mov ax, [bx]
1313 ;; ax = return value (word)
1314 pop ds
1315 pop bx
1317 pop bp
1318 ASM_END
1321 void
1322 write_byte(seg, offset, data)
1323 Bit16u seg;
1324 Bit16u offset;
1325 Bit8u data;
1327 ASM_START
1328 push bp
1329 mov bp, sp
1331 push ax
1332 push bx
1333 push ds
1334 mov ax, 4[bp] ; segment
1335 mov ds, ax
1336 mov bx, 6[bp] ; offset
1337 mov al, 8[bp] ; data byte
1338 mov [bx], al ; write data byte
1339 pop ds
1340 pop bx
1341 pop ax
1343 pop bp
1344 ASM_END
1347 void
1348 write_word(seg, offset, data)
1349 Bit16u seg;
1350 Bit16u offset;
1351 Bit16u data;
1353 ASM_START
1354 push bp
1355 mov bp, sp
1357 push ax
1358 push bx
1359 push ds
1360 mov ax, 4[bp] ; segment
1361 mov ds, ax
1362 mov bx, 6[bp] ; offset
1363 mov ax, 8[bp] ; data word
1364 mov [bx], ax ; write data word
1365 pop ds
1366 pop bx
1367 pop ax
1369 pop bp
1370 ASM_END
1373 Bit16u
1374 get_CS()
1376 ASM_START
1377 mov ax, cs
1378 ASM_END
1381 Bit16u
1382 get_SS()
1384 ASM_START
1385 mov ax, ss
1386 ASM_END
1389 #if BX_DEBUG_SERIAL
1390 /* serial debug port*/
1391 #define BX_DEBUG_PORT 0x03f8
1393 /* data */
1394 #define UART_RBR 0x00
1395 #define UART_THR 0x00
1397 /* control */
1398 #define UART_IER 0x01
1399 #define UART_IIR 0x02
1400 #define UART_FCR 0x02
1401 #define UART_LCR 0x03
1402 #define UART_MCR 0x04
1403 #define UART_DLL 0x00
1404 #define UART_DLM 0x01
1406 /* status */
1407 #define UART_LSR 0x05
1408 #define UART_MSR 0x06
1409 #define UART_SCR 0x07
1411 int uart_can_tx_byte(base_port)
1412 Bit16u base_port;
1414 return inb(base_port + UART_LSR) & 0x20;
1417 void uart_wait_to_tx_byte(base_port)
1418 Bit16u base_port;
1420 while (!uart_can_tx_byte(base_port));
1423 void uart_wait_until_sent(base_port)
1424 Bit16u base_port;
1426 while (!(inb(base_port + UART_LSR) & 0x40));
1429 void uart_tx_byte(base_port, data)
1430 Bit16u base_port;
1431 Bit8u data;
1433 uart_wait_to_tx_byte(base_port);
1434 outb(base_port + UART_THR, data);
1435 uart_wait_until_sent(base_port);
1437 #endif
1439 void
1440 wrch(c)
1441 Bit8u c;
1443 ASM_START
1444 push bp
1445 mov bp, sp
1447 push bx
1448 mov ah, #0x0e
1449 mov al, 4[bp]
1450 xor bx,bx
1451 int #0x10
1452 pop bx
1454 pop bp
1455 ASM_END
1458 void
1459 send(action, c)
1460 Bit16u action;
1461 Bit8u c;
1463 #if BX_DEBUG_SERIAL
1464 if (c == '\n') uart_tx_byte(BX_DEBUG_PORT, '\r');
1465 uart_tx_byte(BX_DEBUG_PORT, c);
1466 #endif
1467 #if BX_VIRTUAL_PORTS
1468 if (action & BIOS_PRINTF_DEBUG) outb(DEBUG_PORT, c);
1469 if (action & BIOS_PRINTF_INFO) outb(INFO_PORT, c);
1470 #endif
1471 if (action & BIOS_PRINTF_SCREEN) {
1472 if (c == '\n') wrch('\r');
1473 wrch(c);
1477 void
1478 put_int(action, val, width, neg)
1479 Bit16u action;
1480 short val, width;
1481 bx_bool neg;
1483 short nval = val / 10;
1484 if (nval)
1485 put_int(action, nval, width - 1, neg);
1486 else {
1487 while (--width > 0) send(action, ' ');
1488 if (neg) send(action, '-');
1490 send(action, val - (nval * 10) + '0');
1493 void
1494 put_uint(action, val, width, neg)
1495 Bit16u action;
1496 unsigned short val;
1497 short width;
1498 bx_bool neg;
1500 unsigned short nval = val / 10;
1501 if (nval)
1502 put_uint(action, nval, width - 1, neg);
1503 else {
1504 while (--width > 0) send(action, ' ');
1505 if (neg) send(action, '-');
1507 send(action, val - (nval * 10) + '0');
1510 void
1511 put_luint(action, val, width, neg)
1512 Bit16u action;
1513 unsigned long val;
1514 short width;
1515 bx_bool neg;
1517 unsigned long nval = val / 10;
1518 if (nval)
1519 put_luint(action, nval, width - 1, neg);
1520 else {
1521 while (--width > 0) send(action, ' ');
1522 if (neg) send(action, '-');
1524 send(action, val - (nval * 10) + '0');
1527 void put_str(action, s)
1528 Bit16u action;
1529 Bit8u *s;
1531 Bit8u c;
1532 if (!s)
1533 s = "<NULL>";
1535 while (c = read_byte(get_CS(), s)) {
1536 send(action, c);
1537 s++;
1541 //--------------------------------------------------------------------------
1542 // bios_printf()
1543 // A compact variable argument printf function.
1545 // Supports %[format_width][length]format
1546 // where format can be x,X,u,d,s,c
1547 // and the optional length modifier is l (ell)
1548 //--------------------------------------------------------------------------
1549 void
1550 bios_printf(action, s)
1551 Bit16u action;
1552 Bit8u *s;
1554 Bit8u c, format_char;
1555 bx_bool in_format;
1556 short i;
1557 Bit16u *arg_ptr;
1558 Bit16u arg_seg, arg, nibble, hibyte, shift_count, format_width, hexadd;
1560 arg_ptr = &s;
1561 arg_seg = get_SS();
1563 in_format = 0;
1564 format_width = 0;
1566 if ((action & BIOS_PRINTF_DEBHALT) == BIOS_PRINTF_DEBHALT) {
1567 #if BX_VIRTUAL_PORTS
1568 outb(PANIC_PORT2, 0x00);
1569 #endif
1570 bios_printf (BIOS_PRINTF_SCREEN, "FATAL: ");
1573 while (c = read_byte(get_CS(), s)) {
1574 if ( c == '%' ) {
1575 in_format = 1;
1576 format_width = 0;
1578 else if (in_format) {
1579 if ( (c>='0') && (c<='9') ) {
1580 format_width = (format_width * 10) + (c - '0');
1582 else {
1583 arg_ptr++; // increment to next arg
1584 arg = read_word(arg_seg, arg_ptr);
1585 if (c == 'x' || c == 'X') {
1586 if (format_width == 0)
1587 format_width = 4;
1588 if (c == 'x')
1589 hexadd = 'a';
1590 else
1591 hexadd = 'A';
1592 for (i=format_width-1; i>=0; i--) {
1593 nibble = (arg >> (4 * i)) & 0x000f;
1594 send (action, (nibble<=9)? (nibble+'0') : (nibble-10+hexadd));
1597 else if (c == 'u') {
1598 put_uint(action, arg, format_width, 0);
1600 else if (c == 'l') {
1601 s++;
1602 c = read_byte(get_CS(), s); /* is it ld,lx,lu? */
1603 arg_ptr++; /* increment to next arg */
1604 hibyte = read_word(arg_seg, arg_ptr);
1605 if (c == 'd') {
1606 if (hibyte & 0x8000)
1607 put_luint(action, 0L-(((Bit32u) hibyte << 16) | arg), format_width-1, 1);
1608 else
1609 put_luint(action, ((Bit32u) hibyte << 16) | arg, format_width, 0);
1611 else if (c == 'u') {
1612 put_luint(action, ((Bit32u) hibyte << 16) | arg, format_width, 0);
1614 else if (c == 'x' || c == 'X')
1616 if (format_width == 0)
1617 format_width = 8;
1618 if (c == 'x')
1619 hexadd = 'a';
1620 else
1621 hexadd = 'A';
1622 for (i=format_width-1; i>=0; i--) {
1623 nibble = ((((Bit32u) hibyte <<16) | arg) >> (4 * i)) & 0x000f;
1624 send (action, (nibble<=9)? (nibble+'0') : (nibble-10+hexadd));
1628 else if (c == 'd') {
1629 if (arg & 0x8000)
1630 put_int(action, -arg, format_width - 1, 1);
1631 else
1632 put_int(action, arg, format_width, 0);
1634 else if (c == 's') {
1635 put_str(action, arg);
1637 else if (c == 'c') {
1638 send(action, arg);
1640 else
1641 BX_PANIC("bios_printf: unknown format\n");
1642 in_format = 0;
1645 else {
1646 send(action, c);
1648 s ++;
1651 if (action & BIOS_PRINTF_HALT) {
1652 // freeze in a busy loop.
1653 ASM_START
1655 halt2_loop:
1657 jmp halt2_loop
1658 ASM_END
1662 //--------------------------------------------------------------------------
1663 // keyboard_init
1664 //--------------------------------------------------------------------------
1665 // this file is based on LinuxBIOS implementation of keyboard.c
1666 // could convert to #asm to gain space
1667 void
1668 keyboard_init()
1670 Bit16u max;
1672 /* ------------------- Flush buffers ------------------------*/
1673 /* Wait until buffer is empty */
1674 max=0xffff;
1675 while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
1677 /* flush incoming keys */
1678 max=0x2000;
1679 while (--max > 0) {
1680 outb(0x80, 0x00);
1681 if (inb(0x64) & 0x01) {
1682 inb(0x60);
1683 max = 0x2000;
1687 // Due to timer issues, and if the IPS setting is > 15000000,
1688 // the incoming keys might not be flushed here. That will
1689 // cause a panic a few lines below. See sourceforge bug report :
1690 // [ 642031 ] FATAL: Keyboard RESET error:993
1692 /* ------------------- controller side ----------------------*/
1693 /* send cmd = 0xAA, self test 8042 */
1694 outb(0x64, 0xaa);
1696 /* Wait until buffer is empty */
1697 max=0xffff;
1698 while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
1699 if (max==0x0) keyboard_panic(00);
1701 /* Wait for data */
1702 max=0xffff;
1703 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x01);
1704 if (max==0x0) keyboard_panic(01);
1706 /* read self-test result, 0x55 should be returned from 0x60 */
1707 if ((inb(0x60) != 0x55)){
1708 keyboard_panic(991);
1711 /* send cmd = 0xAB, keyboard interface test */
1712 outb(0x64,0xab);
1714 /* Wait until buffer is empty */
1715 max=0xffff;
1716 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x10);
1717 if (max==0x0) keyboard_panic(10);
1719 /* Wait for data */
1720 max=0xffff;
1721 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x11);
1722 if (max==0x0) keyboard_panic(11);
1724 /* read keyboard interface test result, */
1725 /* 0x00 should be returned form 0x60 */
1726 if ((inb(0x60) != 0x00)) {
1727 keyboard_panic(992);
1730 /* Enable Keyboard clock */
1731 outb(0x64,0xae);
1732 outb(0x64,0xa8);
1734 /* ------------------- keyboard side ------------------------*/
1735 /* reset kerboard and self test (keyboard side) */
1736 outb(0x60, 0xff);
1738 /* Wait until buffer is empty */
1739 max=0xffff;
1740 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x20);
1741 if (max==0x0) keyboard_panic(20);
1743 /* Wait for data */
1744 max=0xffff;
1745 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x21);
1746 if (max==0x0) keyboard_panic(21);
1748 /* keyboard should return ACK */
1749 if ((inb(0x60) != 0xfa)) {
1750 keyboard_panic(993);
1753 /* Wait for data */
1754 max=0xffff;
1755 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x31);
1756 if (max==0x0) keyboard_panic(31);
1758 if ((inb(0x60) != 0xaa)) {
1759 keyboard_panic(994);
1762 /* Disable keyboard */
1763 outb(0x60, 0xf5);
1765 /* Wait until buffer is empty */
1766 max=0xffff;
1767 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x40);
1768 if (max==0x0) keyboard_panic(40);
1770 /* Wait for data */
1771 max=0xffff;
1772 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x41);
1773 if (max==0x0) keyboard_panic(41);
1775 /* keyboard should return ACK */
1776 if ((inb(0x60) != 0xfa)) {
1777 keyboard_panic(995);
1780 /* Write Keyboard Mode */
1781 outb(0x64, 0x60);
1783 /* Wait until buffer is empty */
1784 max=0xffff;
1785 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x50);
1786 if (max==0x0) keyboard_panic(50);
1788 /* send cmd: scan code convert, disable mouse, enable IRQ 1 */
1789 outb(0x60, 0x61);
1791 /* Wait until buffer is empty */
1792 max=0xffff;
1793 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x60);
1794 if (max==0x0) keyboard_panic(60);
1796 /* Enable keyboard */
1797 outb(0x60, 0xf4);
1799 /* Wait until buffer is empty */
1800 max=0xffff;
1801 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x70);
1802 if (max==0x0) keyboard_panic(70);
1804 /* Wait for data */
1805 max=0xffff;
1806 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x71);
1807 if (max==0x0) keyboard_panic(70);
1809 /* keyboard should return ACK */
1810 if ((inb(0x60) != 0xfa)) {
1811 keyboard_panic(996);
1814 outb(0x80, 0x77);
1817 //--------------------------------------------------------------------------
1818 // keyboard_panic
1819 //--------------------------------------------------------------------------
1820 void
1821 keyboard_panic(status)
1822 Bit16u status;
1824 // If you're getting a 993 keyboard panic here,
1825 // please see the comment in keyboard_init
1827 BX_PANIC("Keyboard error:%u\n",status);
1830 //--------------------------------------------------------------------------
1831 // shutdown_status_panic
1832 // called when the shutdown statsu is not implemented, displays the status
1833 //--------------------------------------------------------------------------
1834 void
1835 shutdown_status_panic(status)
1836 Bit16u status;
1838 BX_PANIC("Unimplemented shutdown status: %02x\n",(Bit8u)status);
1841 //--------------------------------------------------------------------------
1842 // print_bios_banner
1843 // displays a the bios version
1844 //--------------------------------------------------------------------------
1845 void
1846 print_bios_banner()
1848 printf(BX_APPNAME" BIOS - build: %s\n%s\nOptions: ",
1849 BIOS_BUILD_DATE, bios_cvs_version_string);
1850 printf(
1851 #if BX_APM
1852 "apmbios "
1853 #endif
1854 #if BX_PCIBIOS
1855 "pcibios "
1856 #endif
1857 #if BX_ELTORITO_BOOT
1858 "eltorito "
1859 #endif
1860 #if BX_ROMBIOS32
1861 "rombios32 "
1862 #endif
1863 "\n\n");
1866 //--------------------------------------------------------------------------
1867 // BIOS Boot Specification 1.0.1 compatibility
1869 // Very basic support for the BIOS Boot Specification, which allows expansion
1870 // ROMs to register themselves as boot devices, instead of just stealing the
1871 // INT 19h boot vector.
1873 // This is a hack: to do it properly requires a proper PnP BIOS and we aren't
1874 // one; we just lie to the option ROMs to make them behave correctly.
1875 // We also don't support letting option ROMs register as bootable disk
1876 // drives (BCVs), only as bootable devices (BEVs).
1878 // http://www.phoenix.com/en/Customer+Services/White+Papers-Specs/pc+industry+specifications.htm
1879 //--------------------------------------------------------------------------
1882 static void
1883 init_boot_vectors()
1885 ipl_entry_t e;
1886 Bit16u count = 0;
1887 Bit16u ss = get_SS();
1889 /* Clear out the IPL table. */
1890 memsetb(IPL_SEG, IPL_TABLE_OFFSET, 0, IPL_SIZE);
1892 /* Floppy drive */
1893 e.type = IPL_TYPE_FLOPPY; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
1894 memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
1895 count++;
1897 /* First HDD */
1898 e.type = IPL_TYPE_HARDDISK; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
1899 memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
1900 count++;
1902 #if BX_ELTORITO_BOOT
1903 /* CDROM */
1904 e.type = IPL_TYPE_CDROM; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
1905 memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
1906 count++;
1907 #endif
1909 /* Remember how many devices we have */
1910 write_word(IPL_SEG, IPL_COUNT_OFFSET, count);
1911 /* Not tried booting anything yet */
1912 write_word(IPL_SEG, IPL_SEQUENCE_OFFSET, 0xffff);
1915 static Bit8u
1916 get_boot_vector(i, e)
1917 Bit16u i; ipl_entry_t *e;
1919 Bit16u count;
1920 Bit16u ss = get_SS();
1921 /* Get the count of boot devices, and refuse to overrun the array */
1922 count = read_word(IPL_SEG, IPL_COUNT_OFFSET);
1923 if (i >= count) return 0;
1924 /* OK to read this device */
1925 memcpyb(ss, e, IPL_SEG, IPL_TABLE_OFFSET + i * sizeof (*e), sizeof (*e));
1926 return 1;
1930 //--------------------------------------------------------------------------
1931 // print_boot_device
1932 // displays the boot device
1933 //--------------------------------------------------------------------------
1935 static char drivetypes[][10]={"", "Floppy","Hard Disk","CD-Rom", "Network"};
1937 void
1938 print_boot_device(type)
1939 Bit16u type;
1941 /* NIC appears as type 0x80 */
1942 if (type == IPL_TYPE_BEV) type = 0x4;
1943 if (type == 0 || type > 0x4) BX_PANIC("Bad drive type\n");
1944 printf("Booting from %s...\n", drivetypes[type]);
1947 //--------------------------------------------------------------------------
1948 // print_boot_failure
1949 // displays the reason why boot failed
1950 //--------------------------------------------------------------------------
1951 void
1952 print_boot_failure(type, reason)
1953 Bit16u type; Bit8u reason;
1955 if (type == 0 || type > 0x3) BX_PANIC("Bad drive type\n");
1957 printf("Boot from %s failed", drivetypes[type]);
1958 if (type < 4) {
1959 /* Report the reason too */
1960 if (reason==0)
1961 printf(": not a bootable disk");
1962 else
1963 printf(": could not read the boot disk");
1965 printf("\n");
1968 //--------------------------------------------------------------------------
1969 // print_cdromboot_failure
1970 // displays the reason why boot failed
1971 //--------------------------------------------------------------------------
1972 void
1973 print_cdromboot_failure( code )
1974 Bit16u code;
1976 bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, "CDROM boot failure code : %04x\n",code);
1978 return;
1981 void
1982 nmi_handler_msg()
1984 BX_PANIC("NMI Handler called\n");
1987 void
1988 int18_panic_msg()
1990 BX_PANIC("INT18: BOOT FAILURE\n");
1993 void
1994 log_bios_start()
1996 #if BX_DEBUG_SERIAL
1997 outb(BX_DEBUG_PORT+UART_LCR, 0x03); /* setup for serial logging: 8N1 */
1998 #endif
1999 BX_INFO("%s\n", bios_cvs_version_string);
2002 bx_bool
2003 set_enable_a20(val)
2004 bx_bool val;
2006 Bit8u oldval;
2008 // Use PS2 System Control port A to set A20 enable
2010 // get current setting first
2011 oldval = inb(0x92);
2013 // change A20 status
2014 if (val)
2015 outb(0x92, oldval | 0x02);
2016 else
2017 outb(0x92, oldval & 0xfd);
2019 return((oldval & 0x02) != 0);
2022 void
2023 debugger_on()
2025 outb(0xfedc, 0x01);
2028 void
2029 debugger_off()
2031 outb(0xfedc, 0x00);
2034 #if BX_USE_ATADRV
2036 // ---------------------------------------------------------------------------
2037 // Start of ATA/ATAPI Driver
2038 // ---------------------------------------------------------------------------
2040 // Global defines -- ATA register and register bits.
2041 // command block & control block regs
2042 #define ATA_CB_DATA 0 // data reg in/out pio_base_addr1+0
2043 #define ATA_CB_ERR 1 // error in pio_base_addr1+1
2044 #define ATA_CB_FR 1 // feature reg out pio_base_addr1+1
2045 #define ATA_CB_SC 2 // sector count in/out pio_base_addr1+2
2046 #define ATA_CB_SN 3 // sector number in/out pio_base_addr1+3
2047 #define ATA_CB_CL 4 // cylinder low in/out pio_base_addr1+4
2048 #define ATA_CB_CH 5 // cylinder high in/out pio_base_addr1+5
2049 #define ATA_CB_DH 6 // device head in/out pio_base_addr1+6
2050 #define ATA_CB_STAT 7 // primary status in pio_base_addr1+7
2051 #define ATA_CB_CMD 7 // command out pio_base_addr1+7
2052 #define ATA_CB_ASTAT 6 // alternate status in pio_base_addr2+6
2053 #define ATA_CB_DC 6 // device control out pio_base_addr2+6
2054 #define ATA_CB_DA 7 // device address in pio_base_addr2+7
2056 #define ATA_CB_ER_ICRC 0x80 // ATA Ultra DMA bad CRC
2057 #define ATA_CB_ER_BBK 0x80 // ATA bad block
2058 #define ATA_CB_ER_UNC 0x40 // ATA uncorrected error
2059 #define ATA_CB_ER_MC 0x20 // ATA media change
2060 #define ATA_CB_ER_IDNF 0x10 // ATA id not found
2061 #define ATA_CB_ER_MCR 0x08 // ATA media change request
2062 #define ATA_CB_ER_ABRT 0x04 // ATA command aborted
2063 #define ATA_CB_ER_NTK0 0x02 // ATA track 0 not found
2064 #define ATA_CB_ER_NDAM 0x01 // ATA address mark not found
2066 #define ATA_CB_ER_P_SNSKEY 0xf0 // ATAPI sense key (mask)
2067 #define ATA_CB_ER_P_MCR 0x08 // ATAPI Media Change Request
2068 #define ATA_CB_ER_P_ABRT 0x04 // ATAPI command abort
2069 #define ATA_CB_ER_P_EOM 0x02 // ATAPI End of Media
2070 #define ATA_CB_ER_P_ILI 0x01 // ATAPI Illegal Length Indication
2072 // ATAPI Interrupt Reason bits in the Sector Count reg (CB_SC)
2073 #define ATA_CB_SC_P_TAG 0xf8 // ATAPI tag (mask)
2074 #define ATA_CB_SC_P_REL 0x04 // ATAPI release
2075 #define ATA_CB_SC_P_IO 0x02 // ATAPI I/O
2076 #define ATA_CB_SC_P_CD 0x01 // ATAPI C/D
2078 // bits 7-4 of the device/head (CB_DH) reg
2079 #define ATA_CB_DH_DEV0 0xa0 // select device 0
2080 #define ATA_CB_DH_DEV1 0xb0 // select device 1
2081 #define ATA_CB_DH_LBA 0x40 // use LBA
2083 // status reg (CB_STAT and CB_ASTAT) bits
2084 #define ATA_CB_STAT_BSY 0x80 // busy
2085 #define ATA_CB_STAT_RDY 0x40 // ready
2086 #define ATA_CB_STAT_DF 0x20 // device fault
2087 #define ATA_CB_STAT_WFT 0x20 // write fault (old name)
2088 #define ATA_CB_STAT_SKC 0x10 // seek complete
2089 #define ATA_CB_STAT_SERV 0x10 // service
2090 #define ATA_CB_STAT_DRQ 0x08 // data request
2091 #define ATA_CB_STAT_CORR 0x04 // corrected
2092 #define ATA_CB_STAT_IDX 0x02 // index
2093 #define ATA_CB_STAT_ERR 0x01 // error (ATA)
2094 #define ATA_CB_STAT_CHK 0x01 // check (ATAPI)
2096 // device control reg (CB_DC) bits
2097 #define ATA_CB_DC_HD15 0x08 // bit should always be set to one
2098 #define ATA_CB_DC_SRST 0x04 // soft reset
2099 #define ATA_CB_DC_NIEN 0x02 // disable interrupts
2101 // Most mandtory and optional ATA commands (from ATA-3),
2102 #define ATA_CMD_CFA_ERASE_SECTORS 0xC0
2103 #define ATA_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03
2104 #define ATA_CMD_CFA_TRANSLATE_SECTOR 0x87
2105 #define ATA_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD
2106 #define ATA_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38
2107 #define ATA_CMD_CHECK_POWER_MODE1 0xE5
2108 #define ATA_CMD_CHECK_POWER_MODE2 0x98
2109 #define ATA_CMD_DEVICE_RESET 0x08
2110 #define ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90
2111 #define ATA_CMD_FLUSH_CACHE 0xE7
2112 #define ATA_CMD_FORMAT_TRACK 0x50
2113 #define ATA_CMD_IDENTIFY_DEVICE 0xEC
2114 #define ATA_CMD_IDENTIFY_DEVICE_PACKET 0xA1
2115 #define ATA_CMD_IDENTIFY_PACKET_DEVICE 0xA1
2116 #define ATA_CMD_IDLE1 0xE3
2117 #define ATA_CMD_IDLE2 0x97
2118 #define ATA_CMD_IDLE_IMMEDIATE1 0xE1
2119 #define ATA_CMD_IDLE_IMMEDIATE2 0x95
2120 #define ATA_CMD_INITIALIZE_DRIVE_PARAMETERS 0x91
2121 #define ATA_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91
2122 #define ATA_CMD_NOP 0x00
2123 #define ATA_CMD_PACKET 0xA0
2124 #define ATA_CMD_READ_BUFFER 0xE4
2125 #define ATA_CMD_READ_DMA 0xC8
2126 #define ATA_CMD_READ_DMA_QUEUED 0xC7
2127 #define ATA_CMD_READ_MULTIPLE 0xC4
2128 #define ATA_CMD_READ_SECTORS 0x20
2129 #define ATA_CMD_READ_VERIFY_SECTORS 0x40
2130 #define ATA_CMD_RECALIBRATE 0x10
2131 #define ATA_CMD_REQUEST_SENSE 0x03
2132 #define ATA_CMD_SEEK 0x70
2133 #define ATA_CMD_SET_FEATURES 0xEF
2134 #define ATA_CMD_SET_MULTIPLE_MODE 0xC6
2135 #define ATA_CMD_SLEEP1 0xE6
2136 #define ATA_CMD_SLEEP2 0x99
2137 #define ATA_CMD_STANDBY1 0xE2
2138 #define ATA_CMD_STANDBY2 0x96
2139 #define ATA_CMD_STANDBY_IMMEDIATE1 0xE0
2140 #define ATA_CMD_STANDBY_IMMEDIATE2 0x94
2141 #define ATA_CMD_WRITE_BUFFER 0xE8
2142 #define ATA_CMD_WRITE_DMA 0xCA
2143 #define ATA_CMD_WRITE_DMA_QUEUED 0xCC
2144 #define ATA_CMD_WRITE_MULTIPLE 0xC5
2145 #define ATA_CMD_WRITE_SECTORS 0x30
2146 #define ATA_CMD_WRITE_VERIFY 0x3C
2148 #define ATA_IFACE_NONE 0x00
2149 #define ATA_IFACE_ISA 0x00
2150 #define ATA_IFACE_PCI 0x01
2152 #define ATA_TYPE_NONE 0x00
2153 #define ATA_TYPE_UNKNOWN 0x01
2154 #define ATA_TYPE_ATA 0x02
2155 #define ATA_TYPE_ATAPI 0x03
2157 #define ATA_DEVICE_NONE 0x00
2158 #define ATA_DEVICE_HD 0xFF
2159 #define ATA_DEVICE_CDROM 0x05
2161 #define ATA_MODE_NONE 0x00
2162 #define ATA_MODE_PIO16 0x00
2163 #define ATA_MODE_PIO32 0x01
2164 #define ATA_MODE_ISADMA 0x02
2165 #define ATA_MODE_PCIDMA 0x03
2166 #define ATA_MODE_USEIRQ 0x10
2168 #define ATA_TRANSLATION_NONE 0
2169 #define ATA_TRANSLATION_LBA 1
2170 #define ATA_TRANSLATION_LARGE 2
2171 #define ATA_TRANSLATION_RECHS 3
2173 #define ATA_DATA_NO 0x00
2174 #define ATA_DATA_IN 0x01
2175 #define ATA_DATA_OUT 0x02
2177 // ---------------------------------------------------------------------------
2178 // ATA/ATAPI driver : initialization
2179 // ---------------------------------------------------------------------------
2180 void ata_init( )
2182 Bit16u ebda_seg=read_word(0x0040,0x000E);
2183 Bit8u channel, device;
2185 // Channels info init.
2186 for (channel=0; channel<BX_MAX_ATA_INTERFACES; channel++) {
2187 write_byte(ebda_seg,&EbdaData->ata.channels[channel].iface,ATA_IFACE_NONE);
2188 write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1,0x0);
2189 write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2,0x0);
2190 write_byte(ebda_seg,&EbdaData->ata.channels[channel].irq,0);
2193 // Devices info init.
2194 for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2195 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
2196 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_NONE);
2197 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable,0);
2198 write_byte(ebda_seg,&EbdaData->ata.devices[device].lock,0);
2199 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode,ATA_MODE_NONE);
2200 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize,0);
2201 write_byte(ebda_seg,&EbdaData->ata.devices[device].translation,ATA_TRANSLATION_NONE);
2202 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads,0);
2203 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders,0);
2204 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt,0);
2205 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads,0);
2206 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders,0);
2207 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt,0);
2209 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors,0L);
2212 // hdidmap and cdidmap init.
2213 for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2214 write_byte(ebda_seg,&EbdaData->ata.hdidmap[device],BX_MAX_ATA_DEVICES);
2215 write_byte(ebda_seg,&EbdaData->ata.cdidmap[device],BX_MAX_ATA_DEVICES);
2218 write_byte(ebda_seg,&EbdaData->ata.hdcount,0);
2219 write_byte(ebda_seg,&EbdaData->ata.cdcount,0);
2222 #define TIMEOUT 0
2223 #define BSY 1
2224 #define NOT_BSY 2
2225 #define NOT_BSY_DRQ 3
2226 #define NOT_BSY_NOT_DRQ 4
2227 #define NOT_BSY_RDY 5
2229 #define IDE_TIMEOUT 32000u //32 seconds max for IDE ops
2231 int await_ide();
2232 static int await_ide(when_done,base,timeout)
2233 Bit8u when_done;
2234 Bit16u base;
2235 Bit16u timeout;
2237 Bit32u time=0,last=0;
2238 Bit16u status;
2239 Bit8u result;
2240 status = inb(base + ATA_CB_STAT); // for the times you're supposed to throw one away
2241 for(;;) {
2242 status = inb(base+ATA_CB_STAT);
2243 time++;
2244 if (when_done == BSY)
2245 result = status & ATA_CB_STAT_BSY;
2246 else if (when_done == NOT_BSY)
2247 result = !(status & ATA_CB_STAT_BSY);
2248 else if (when_done == NOT_BSY_DRQ)
2249 result = !(status & ATA_CB_STAT_BSY) && (status & ATA_CB_STAT_DRQ);
2250 else if (when_done == NOT_BSY_NOT_DRQ)
2251 result = !(status & ATA_CB_STAT_BSY) && !(status & ATA_CB_STAT_DRQ);
2252 else if (when_done == NOT_BSY_RDY)
2253 result = !(status & ATA_CB_STAT_BSY) && (status & ATA_CB_STAT_RDY);
2254 else if (when_done == TIMEOUT)
2255 result = 0;
2257 if (result) return 0;
2258 if (time>>16 != last) // mod 2048 each 16 ms
2260 last = time >>16;
2261 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);
2263 if (status & ATA_CB_STAT_ERR)
2265 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);
2266 return -1;
2268 if ((timeout == 0) || ((time>>11) > timeout)) break;
2270 BX_INFO("IDE time out\n");
2271 return -1;
2274 // ---------------------------------------------------------------------------
2275 // ATA/ATAPI driver : device detection
2276 // ---------------------------------------------------------------------------
2278 void ata_detect( )
2280 Bit16u ebda_seg=read_word(0x0040,0x000E);
2281 Bit8u hdcount, cdcount, device, type;
2282 Bit8u buffer[0x0200];
2284 #if BX_MAX_ATA_INTERFACES > 0
2285 write_byte(ebda_seg,&EbdaData->ata.channels[0].iface,ATA_IFACE_ISA);
2286 write_word(ebda_seg,&EbdaData->ata.channels[0].iobase1,0x1f0);
2287 write_word(ebda_seg,&EbdaData->ata.channels[0].iobase2,0x3f0);
2288 write_byte(ebda_seg,&EbdaData->ata.channels[0].irq,14);
2289 #endif
2290 #if BX_MAX_ATA_INTERFACES > 1
2291 write_byte(ebda_seg,&EbdaData->ata.channels[1].iface,ATA_IFACE_ISA);
2292 write_word(ebda_seg,&EbdaData->ata.channels[1].iobase1,0x170);
2293 write_word(ebda_seg,&EbdaData->ata.channels[1].iobase2,0x370);
2294 write_byte(ebda_seg,&EbdaData->ata.channels[1].irq,15);
2295 #endif
2296 #if BX_MAX_ATA_INTERFACES > 2
2297 write_byte(ebda_seg,&EbdaData->ata.channels[2].iface,ATA_IFACE_ISA);
2298 write_word(ebda_seg,&EbdaData->ata.channels[2].iobase1,0x1e8);
2299 write_word(ebda_seg,&EbdaData->ata.channels[2].iobase2,0x3e0);
2300 write_byte(ebda_seg,&EbdaData->ata.channels[2].irq,12);
2301 #endif
2302 #if BX_MAX_ATA_INTERFACES > 3
2303 write_byte(ebda_seg,&EbdaData->ata.channels[3].iface,ATA_IFACE_ISA);
2304 write_word(ebda_seg,&EbdaData->ata.channels[3].iobase1,0x168);
2305 write_word(ebda_seg,&EbdaData->ata.channels[3].iobase2,0x360);
2306 write_byte(ebda_seg,&EbdaData->ata.channels[3].irq,11);
2307 #endif
2308 #if BX_MAX_ATA_INTERFACES > 4
2309 #error Please fill the ATA interface informations
2310 #endif
2312 // Device detection
2313 hdcount=cdcount=0;
2315 for(device=0; device<BX_MAX_ATA_DEVICES; device++) {
2316 Bit16u iobase1, iobase2;
2317 Bit8u channel, slave, shift;
2318 Bit8u sc, sn, cl, ch, st;
2320 channel = device / 2;
2321 slave = device % 2;
2323 iobase1 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1);
2324 iobase2 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2);
2326 // Disable interrupts
2327 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2329 // Look for device
2330 outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2331 outb(iobase1+ATA_CB_SC, 0x55);
2332 outb(iobase1+ATA_CB_SN, 0xaa);
2333 outb(iobase1+ATA_CB_SC, 0xaa);
2334 outb(iobase1+ATA_CB_SN, 0x55);
2335 outb(iobase1+ATA_CB_SC, 0x55);
2336 outb(iobase1+ATA_CB_SN, 0xaa);
2338 // If we found something
2339 sc = inb(iobase1+ATA_CB_SC);
2340 sn = inb(iobase1+ATA_CB_SN);
2342 if ( (sc == 0x55) && (sn == 0xaa) ) {
2343 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_UNKNOWN);
2345 // reset the channel
2346 ata_reset(device);
2348 // check for ATA or ATAPI
2349 outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2350 sc = inb(iobase1+ATA_CB_SC);
2351 sn = inb(iobase1+ATA_CB_SN);
2352 if ((sc==0x01) && (sn==0x01)) {
2353 cl = inb(iobase1+ATA_CB_CL);
2354 ch = inb(iobase1+ATA_CB_CH);
2355 st = inb(iobase1+ATA_CB_STAT);
2357 if ((cl==0x14) && (ch==0xeb)) {
2358 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATAPI);
2359 } else if ((cl==0x00) && (ch==0x00) && (st!=0x00)) {
2360 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATA);
2361 } else if ((cl==0xff) && (ch==0xff)) {
2362 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
2367 type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type);
2369 // Now we send a IDENTIFY command to ATA device
2370 if(type == ATA_TYPE_ATA) {
2371 Bit32u sectors;
2372 Bit16u cylinders, heads, spt, blksize;
2373 Bit8u translation, removable, mode;
2375 //Temporary values to do the transfer
2376 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2377 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2379 if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE, 1, 0, 0, 0, 0L, get_SS(),buffer) !=0 )
2380 BX_PANIC("ata-detect: Failed to detect ATA device\n");
2382 removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2383 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2384 blksize = read_word(get_SS(),buffer+10);
2386 cylinders = read_word(get_SS(),buffer+(1*2)); // word 1
2387 heads = read_word(get_SS(),buffer+(3*2)); // word 3
2388 spt = read_word(get_SS(),buffer+(6*2)); // word 6
2390 sectors = read_dword(get_SS(),buffer+(60*2)); // word 60 and word 61
2392 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2393 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2394 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2395 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2396 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads, heads);
2397 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders, cylinders);
2398 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt, spt);
2399 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors, sectors);
2400 BX_INFO("ata%d-%d: PCHS=%u/%d/%d translation=", channel, slave,cylinders, heads, spt);
2402 translation = inb_cmos(0x39 + channel/2);
2403 for (shift=device%4; shift>0; shift--) translation >>= 2;
2404 translation &= 0x03;
2406 write_byte(ebda_seg,&EbdaData->ata.devices[device].translation, translation);
2408 switch (translation) {
2409 case ATA_TRANSLATION_NONE:
2410 BX_INFO("none");
2411 break;
2412 case ATA_TRANSLATION_LBA:
2413 BX_INFO("lba");
2414 break;
2415 case ATA_TRANSLATION_LARGE:
2416 BX_INFO("large");
2417 break;
2418 case ATA_TRANSLATION_RECHS:
2419 BX_INFO("r-echs");
2420 break;
2422 switch (translation) {
2423 case ATA_TRANSLATION_NONE:
2424 break;
2425 case ATA_TRANSLATION_LBA:
2426 spt = 63;
2427 sectors /= 63;
2428 heads = sectors / 1024;
2429 if (heads>128) heads = 255;
2430 else if (heads>64) heads = 128;
2431 else if (heads>32) heads = 64;
2432 else if (heads>16) heads = 32;
2433 else heads=16;
2434 cylinders = sectors / heads;
2435 break;
2436 case ATA_TRANSLATION_RECHS:
2437 // Take care not to overflow
2438 if (heads==16) {
2439 if(cylinders>61439) cylinders=61439;
2440 heads=15;
2441 cylinders = (Bit16u)((Bit32u)(cylinders)*16/15);
2443 // then go through the large bitshift process
2444 case ATA_TRANSLATION_LARGE:
2445 while(cylinders > 1024) {
2446 cylinders >>= 1;
2447 heads <<= 1;
2449 // If we max out the head count
2450 if (heads > 127) break;
2452 break;
2454 // clip to 1024 cylinders in lchs
2455 if (cylinders > 1024) cylinders=1024;
2456 BX_INFO(" LCHS=%d/%d/%d\n", cylinders, heads, spt);
2458 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads, heads);
2459 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders, cylinders);
2460 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt, spt);
2462 // fill hdidmap
2463 write_byte(ebda_seg,&EbdaData->ata.hdidmap[hdcount], device);
2464 hdcount++;
2467 // Now we send a IDENTIFY command to ATAPI device
2468 if(type == ATA_TYPE_ATAPI) {
2470 Bit8u type, removable, mode;
2471 Bit16u blksize;
2473 //Temporary values to do the transfer
2474 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_CDROM);
2475 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2477 if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE_PACKET, 1, 0, 0, 0, 0L, get_SS(),buffer) != 0)
2478 BX_PANIC("ata-detect: Failed to detect ATAPI device\n");
2480 type = read_byte(get_SS(),buffer+1) & 0x1f;
2481 removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2482 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2483 blksize = 2048;
2485 write_byte(ebda_seg,&EbdaData->ata.devices[device].device, type);
2486 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2487 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2488 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2490 // fill cdidmap
2491 write_byte(ebda_seg,&EbdaData->ata.cdidmap[cdcount], device);
2492 cdcount++;
2496 Bit32u sizeinmb;
2497 Bit16u ataversion;
2498 Bit8u c, i, version, model[41];
2500 switch (type) {
2501 case ATA_TYPE_ATA:
2502 sizeinmb = read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors);
2503 sizeinmb >>= 11;
2504 case ATA_TYPE_ATAPI:
2505 // Read ATA/ATAPI version
2506 ataversion=((Bit16u)(read_byte(get_SS(),buffer+161))<<8)|read_byte(get_SS(),buffer+160);
2507 for(version=15;version>0;version--) {
2508 if((ataversion&(1<<version))!=0)
2509 break;
2512 // Read model name
2513 for(i=0;i<20;i++){
2514 write_byte(get_SS(),model+(i*2),read_byte(get_SS(),buffer+(i*2)+54+1));
2515 write_byte(get_SS(),model+(i*2)+1,read_byte(get_SS(),buffer+(i*2)+54));
2518 // Reformat
2519 write_byte(get_SS(),model+40,0x00);
2520 for(i=39;i>0;i--){
2521 if(read_byte(get_SS(),model+i)==0x20)
2522 write_byte(get_SS(),model+i,0x00);
2523 else break;
2525 break;
2528 switch (type) {
2529 case ATA_TYPE_ATA:
2530 printf("ata%d %s: ",channel,slave?" slave":"master");
2531 i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
2532 if (sizeinmb < (1UL<<16))
2533 printf(" ATA-%d Hard-Disk (%4u MBytes)\n", version, (Bit16u)sizeinmb);
2534 else
2535 printf(" ATA-%d Hard-Disk (%4u GBytes)\n", version, (Bit16u)(sizeinmb>>10));
2536 break;
2537 case ATA_TYPE_ATAPI:
2538 printf("ata%d %s: ",channel,slave?" slave":"master");
2539 i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
2540 if(read_byte(ebda_seg,&EbdaData->ata.devices[device].device)==ATA_DEVICE_CDROM)
2541 printf(" ATAPI-%d CD-Rom/DVD-Rom\n",version);
2542 else
2543 printf(" ATAPI-%d Device\n",version);
2544 break;
2545 case ATA_TYPE_UNKNOWN:
2546 printf("ata%d %s: Unknown device\n",channel,slave?" slave":"master");
2547 break;
2552 // Store the devices counts
2553 write_byte(ebda_seg,&EbdaData->ata.hdcount, hdcount);
2554 write_byte(ebda_seg,&EbdaData->ata.cdcount, cdcount);
2555 write_byte(0x40,0x75, hdcount);
2557 printf("\n");
2559 // FIXME : should use bios=cmos|auto|disable bits
2560 // FIXME : should know about translation bits
2561 // FIXME : move hard_drive_post here
2565 // ---------------------------------------------------------------------------
2566 // ATA/ATAPI driver : software reset
2567 // ---------------------------------------------------------------------------
2568 // ATA-3
2569 // 8.2.1 Software reset - Device 0
2571 void ata_reset(device)
2572 Bit16u device;
2574 Bit16u ebda_seg=read_word(0x0040,0x000E);
2575 Bit16u iobase1, iobase2;
2576 Bit8u channel, slave, sn, sc;
2577 Bit8u type;
2578 Bit16u max;
2580 channel = device / 2;
2581 slave = device % 2;
2583 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2584 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2586 // Reset
2588 // 8.2.1 (a) -- set SRST in DC
2589 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST);
2591 // 8.2.1 (b) -- wait for BSY
2592 await_ide(BSY, iobase1, 20);
2594 // 8.2.1 (f) -- clear SRST
2595 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2597 type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type);
2598 if (type != ATA_TYPE_NONE) {
2600 // 8.2.1 (g) -- check for sc==sn==0x01
2601 // select device
2602 outb(iobase1+ATA_CB_DH, slave?ATA_CB_DH_DEV1:ATA_CB_DH_DEV0);
2603 sc = inb(iobase1+ATA_CB_SC);
2604 sn = inb(iobase1+ATA_CB_SN);
2606 if ( (sc==0x01) && (sn==0x01) ) {
2607 if (type == ATA_TYPE_ATA) //ATA
2608 await_ide(NOT_BSY_RDY, iobase1, IDE_TIMEOUT);
2609 else //ATAPI
2610 await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
2613 // 8.2.1 (h) -- wait for not BSY
2614 await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
2617 // Enable interrupts
2618 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2621 // ---------------------------------------------------------------------------
2622 // ATA/ATAPI driver : execute a non data command
2623 // ---------------------------------------------------------------------------
2625 Bit16u ata_cmd_non_data()
2626 {return 0;}
2628 // ---------------------------------------------------------------------------
2629 // ATA/ATAPI driver : execute a data-in command
2630 // ---------------------------------------------------------------------------
2631 // returns
2632 // 0 : no error
2633 // 1 : BUSY bit set
2634 // 2 : read error
2635 // 3 : expected DRQ=1
2636 // 4 : no sectors left to read/verify
2637 // 5 : more sectors to read/verify
2638 // 6 : no sectors left to write
2639 // 7 : more sectors to write
2640 Bit16u ata_cmd_data_in(device, command, count, cylinder, head, sector, lba, segment, offset)
2641 Bit16u device, command, count, cylinder, head, sector, segment, offset;
2642 Bit32u lba;
2644 Bit16u ebda_seg=read_word(0x0040,0x000E);
2645 Bit16u iobase1, iobase2, blksize;
2646 Bit8u channel, slave;
2647 Bit8u status, current, mode;
2649 channel = device / 2;
2650 slave = device % 2;
2652 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2653 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2654 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2655 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
2656 if (mode == ATA_MODE_PIO32) blksize>>=2;
2657 else blksize>>=1;
2659 // Reset count of transferred data
2660 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2661 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2662 current = 0;
2664 status = inb(iobase1 + ATA_CB_STAT);
2665 if (status & ATA_CB_STAT_BSY) return 1;
2667 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2669 // sector will be 0 only on lba access. Convert to lba-chs
2670 if (sector == 0) {
2671 if ((count >= 1 << 8) || (lba + count >= 1UL << 28)) {
2672 outb(iobase1 + ATA_CB_FR, 0x00);
2673 outb(iobase1 + ATA_CB_SC, (count >> 8) & 0xff);
2674 outb(iobase1 + ATA_CB_SN, lba >> 24);
2675 outb(iobase1 + ATA_CB_CL, 0);
2676 outb(iobase1 + ATA_CB_CH, 0);
2677 command |= 0x04;
2678 count &= (1UL << 8) - 1;
2679 lba &= (1UL << 24) - 1;
2681 sector = (Bit16u) (lba & 0x000000ffL);
2682 cylinder = (Bit16u) ((lba>>8) & 0x0000ffffL);
2683 head = ((Bit16u) ((lba>>24) & 0x0000000fL)) | ATA_CB_DH_LBA;
2686 outb(iobase1 + ATA_CB_FR, 0x00);
2687 outb(iobase1 + ATA_CB_SC, count);
2688 outb(iobase1 + ATA_CB_SN, sector);
2689 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
2690 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
2691 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
2692 outb(iobase1 + ATA_CB_CMD, command);
2694 await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
2695 status = inb(iobase1 + ATA_CB_STAT);
2697 if (status & ATA_CB_STAT_ERR) {
2698 BX_DEBUG_ATA("ata_cmd_data_in : read error\n");
2699 return 2;
2700 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2701 BX_DEBUG_ATA("ata_cmd_data_in : DRQ not set (status %02x)\n", (unsigned) status);
2702 return 3;
2705 // FIXME : move seg/off translation here
2707 ASM_START
2708 sti ;; enable higher priority interrupts
2709 ASM_END
2711 while (1) {
2713 ASM_START
2714 push bp
2715 mov bp, sp
2716 mov di, _ata_cmd_data_in.offset + 2[bp]
2717 mov ax, _ata_cmd_data_in.segment + 2[bp]
2718 mov cx, _ata_cmd_data_in.blksize + 2[bp]
2720 ;; adjust if there will be an overrun. 2K max sector size
2721 cmp di, #0xf800 ;;
2722 jbe ata_in_no_adjust
2724 ata_in_adjust:
2725 sub di, #0x0800 ;; sub 2 kbytes from offset
2726 add ax, #0x0080 ;; add 2 Kbytes to segment
2728 ata_in_no_adjust:
2729 mov es, ax ;; segment in es
2731 mov dx, _ata_cmd_data_in.iobase1 + 2[bp] ;; ATA data read port
2733 mov ah, _ata_cmd_data_in.mode + 2[bp]
2734 cmp ah, #ATA_MODE_PIO32
2735 je ata_in_32
2737 ata_in_16:
2739 insw ;; CX words transfered from port(DX) to ES:[DI]
2740 jmp ata_in_done
2742 ata_in_32:
2744 insd ;; CX dwords transfered from port(DX) to ES:[DI]
2746 ata_in_done:
2747 mov _ata_cmd_data_in.offset + 2[bp], di
2748 mov _ata_cmd_data_in.segment + 2[bp], es
2749 pop bp
2750 ASM_END
2752 current++;
2753 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
2754 count--;
2755 await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
2756 status = inb(iobase1 + ATA_CB_STAT);
2757 if (count == 0) {
2758 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2759 != ATA_CB_STAT_RDY ) {
2760 BX_DEBUG_ATA("ata_cmd_data_in : no sectors left (status %02x)\n", (unsigned) status);
2761 return 4;
2763 break;
2765 else {
2766 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2767 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2768 BX_DEBUG_ATA("ata_cmd_data_in : more sectors left (status %02x)\n", (unsigned) status);
2769 return 5;
2771 continue;
2774 // Enable interrupts
2775 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2776 return 0;
2779 // ---------------------------------------------------------------------------
2780 // ATA/ATAPI driver : execute a data-out command
2781 // ---------------------------------------------------------------------------
2782 // returns
2783 // 0 : no error
2784 // 1 : BUSY bit set
2785 // 2 : read error
2786 // 3 : expected DRQ=1
2787 // 4 : no sectors left to read/verify
2788 // 5 : more sectors to read/verify
2789 // 6 : no sectors left to write
2790 // 7 : more sectors to write
2791 Bit16u ata_cmd_data_out(device, command, count, cylinder, head, sector, lba, segment, offset)
2792 Bit16u device, command, count, cylinder, head, sector, segment, offset;
2793 Bit32u lba;
2795 Bit16u ebda_seg=read_word(0x0040,0x000E);
2796 Bit16u iobase1, iobase2, blksize;
2797 Bit8u channel, slave;
2798 Bit8u status, current, mode;
2800 channel = device / 2;
2801 slave = device % 2;
2803 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2804 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2805 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2806 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
2807 if (mode == ATA_MODE_PIO32) blksize>>=2;
2808 else blksize>>=1;
2810 // Reset count of transferred data
2811 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2812 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2813 current = 0;
2815 status = inb(iobase1 + ATA_CB_STAT);
2816 if (status & ATA_CB_STAT_BSY) return 1;
2818 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2820 // sector will be 0 only on lba access. Convert to lba-chs
2821 if (sector == 0) {
2822 if ((count >= 1 << 8) || (lba + count >= 1UL << 28)) {
2823 outb(iobase1 + ATA_CB_FR, 0x00);
2824 outb(iobase1 + ATA_CB_SC, (count >> 8) & 0xff);
2825 outb(iobase1 + ATA_CB_SN, lba >> 24);
2826 outb(iobase1 + ATA_CB_CL, 0);
2827 outb(iobase1 + ATA_CB_CH, 0);
2828 command |= 0x04;
2829 count &= (1UL << 8) - 1;
2830 lba &= (1UL << 24) - 1;
2832 sector = (Bit16u) (lba & 0x000000ffL);
2833 cylinder = (Bit16u) ((lba>>8) & 0x0000ffffL);
2834 head = ((Bit16u) ((lba>>24) & 0x0000000fL)) | ATA_CB_DH_LBA;
2837 outb(iobase1 + ATA_CB_FR, 0x00);
2838 outb(iobase1 + ATA_CB_SC, count);
2839 outb(iobase1 + ATA_CB_SN, sector);
2840 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
2841 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
2842 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
2843 outb(iobase1 + ATA_CB_CMD, command);
2845 await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
2846 status = inb(iobase1 + ATA_CB_STAT);
2848 if (status & ATA_CB_STAT_ERR) {
2849 BX_DEBUG_ATA("ata_cmd_data_out : read error\n");
2850 return 2;
2851 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2852 BX_DEBUG_ATA("ata_cmd_data_out : DRQ not set (status %02x)\n", (unsigned) status);
2853 return 3;
2856 // FIXME : move seg/off translation here
2858 ASM_START
2859 sti ;; enable higher priority interrupts
2860 ASM_END
2862 while (1) {
2864 ASM_START
2865 push bp
2866 mov bp, sp
2867 mov si, _ata_cmd_data_out.offset + 2[bp]
2868 mov ax, _ata_cmd_data_out.segment + 2[bp]
2869 mov cx, _ata_cmd_data_out.blksize + 2[bp]
2871 ;; adjust if there will be an overrun. 2K max sector size
2872 cmp si, #0xf800 ;;
2873 jbe ata_out_no_adjust
2875 ata_out_adjust:
2876 sub si, #0x0800 ;; sub 2 kbytes from offset
2877 add ax, #0x0080 ;; add 2 Kbytes to segment
2879 ata_out_no_adjust:
2880 mov es, ax ;; segment in es
2882 mov dx, _ata_cmd_data_out.iobase1 + 2[bp] ;; ATA data write port
2884 mov ah, _ata_cmd_data_out.mode + 2[bp]
2885 cmp ah, #ATA_MODE_PIO32
2886 je ata_out_32
2888 ata_out_16:
2889 seg ES
2891 outsw ;; CX words transfered from port(DX) to ES:[SI]
2892 jmp ata_out_done
2894 ata_out_32:
2895 seg ES
2897 outsd ;; CX dwords transfered from port(DX) to ES:[SI]
2899 ata_out_done:
2900 mov _ata_cmd_data_out.offset + 2[bp], si
2901 mov _ata_cmd_data_out.segment + 2[bp], es
2902 pop bp
2903 ASM_END
2905 current++;
2906 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
2907 count--;
2908 status = inb(iobase1 + ATA_CB_STAT);
2909 if (count == 0) {
2910 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2911 != ATA_CB_STAT_RDY ) {
2912 BX_DEBUG_ATA("ata_cmd_data_out : no sectors left (status %02x)\n", (unsigned) status);
2913 return 6;
2915 break;
2917 else {
2918 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2919 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2920 BX_DEBUG_ATA("ata_cmd_data_out : more sectors left (status %02x)\n", (unsigned) status);
2921 return 7;
2923 continue;
2926 // Enable interrupts
2927 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2928 return 0;
2931 // ---------------------------------------------------------------------------
2932 // ATA/ATAPI driver : execute a packet command
2933 // ---------------------------------------------------------------------------
2934 // returns
2935 // 0 : no error
2936 // 1 : error in parameters
2937 // 2 : BUSY bit set
2938 // 3 : error
2939 // 4 : not ready
2940 Bit16u ata_cmd_packet(device, cmdlen, cmdseg, cmdoff, header, length, inout, bufseg, bufoff)
2941 Bit8u cmdlen,inout;
2942 Bit16u device,cmdseg, cmdoff, bufseg, bufoff;
2943 Bit16u header;
2944 Bit32u length;
2946 Bit16u ebda_seg=read_word(0x0040,0x000E);
2947 Bit16u iobase1, iobase2;
2948 Bit16u lcount, lbefore, lafter, count;
2949 Bit8u channel, slave;
2950 Bit8u status, mode, lmode;
2951 Bit32u total, transfer;
2953 channel = device / 2;
2954 slave = device % 2;
2956 // Data out is not supported yet
2957 if (inout == ATA_DATA_OUT) {
2958 BX_INFO("ata_cmd_packet: DATA_OUT not supported yet\n");
2959 return 1;
2962 // The header length must be even
2963 if (header & 1) {
2964 BX_DEBUG_ATA("ata_cmd_packet : header must be even (%04x)\n",header);
2965 return 1;
2968 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2969 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2970 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2971 transfer= 0L;
2973 if (cmdlen < 12) cmdlen=12;
2974 if (cmdlen > 12) cmdlen=16;
2975 cmdlen>>=1;
2977 // Reset count of transferred data
2978 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2979 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2981 status = inb(iobase1 + ATA_CB_STAT);
2982 if (status & ATA_CB_STAT_BSY) return 2;
2984 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2985 outb(iobase1 + ATA_CB_FR, 0x00);
2986 outb(iobase1 + ATA_CB_SC, 0x00);
2987 outb(iobase1 + ATA_CB_SN, 0x00);
2988 outb(iobase1 + ATA_CB_CL, 0xfff0 & 0x00ff);
2989 outb(iobase1 + ATA_CB_CH, 0xfff0 >> 8);
2990 outb(iobase1 + ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2991 outb(iobase1 + ATA_CB_CMD, ATA_CMD_PACKET);
2993 // Device should ok to receive command
2994 await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
2995 status = inb(iobase1 + ATA_CB_STAT);
2997 if (status & ATA_CB_STAT_ERR) {
2998 BX_DEBUG_ATA("ata_cmd_packet : error, status is %02x\n",status);
2999 return 3;
3000 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
3001 BX_DEBUG_ATA("ata_cmd_packet : DRQ not set (status %02x)\n", (unsigned) status);
3002 return 4;
3005 // Normalize address
3006 cmdseg += (cmdoff / 16);
3007 cmdoff %= 16;
3009 // Send command to device
3010 ASM_START
3011 sti ;; enable higher priority interrupts
3013 push bp
3014 mov bp, sp
3016 mov si, _ata_cmd_packet.cmdoff + 2[bp]
3017 mov ax, _ata_cmd_packet.cmdseg + 2[bp]
3018 mov cx, _ata_cmd_packet.cmdlen + 2[bp]
3019 mov es, ax ;; segment in es
3021 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data write port
3023 seg ES
3025 outsw ;; CX words transfered from port(DX) to ES:[SI]
3027 pop bp
3028 ASM_END
3030 if (inout == ATA_DATA_NO) {
3031 await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
3032 status = inb(iobase1 + ATA_CB_STAT);
3034 else {
3035 Bit16u loops = 0;
3036 Bit8u sc;
3037 while (1) {
3039 if (loops == 0) {//first time through
3040 status = inb(iobase2 + ATA_CB_ASTAT);
3041 await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
3043 else
3044 await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
3045 loops++;
3047 status = inb(iobase1 + ATA_CB_STAT);
3048 sc = inb(iobase1 + ATA_CB_SC);
3050 // Check if command completed
3051 if(((inb(iobase1 + ATA_CB_SC)&0x7)==0x3) &&
3052 ((status & (ATA_CB_STAT_RDY | ATA_CB_STAT_ERR)) == ATA_CB_STAT_RDY)) break;
3054 if (status & ATA_CB_STAT_ERR) {
3055 BX_DEBUG_ATA("ata_cmd_packet : error (status %02x)\n",status);
3056 return 3;
3059 // Normalize address
3060 bufseg += (bufoff / 16);
3061 bufoff %= 16;
3063 // Get the byte count
3064 lcount = ((Bit16u)(inb(iobase1 + ATA_CB_CH))<<8)+inb(iobase1 + ATA_CB_CL);
3066 // adjust to read what we want
3067 if(header>lcount) {
3068 lbefore=lcount;
3069 header-=lcount;
3070 lcount=0;
3072 else {
3073 lbefore=header;
3074 header=0;
3075 lcount-=lbefore;
3078 if(lcount>length) {
3079 lafter=lcount-length;
3080 lcount=length;
3081 length=0;
3083 else {
3084 lafter=0;
3085 length-=lcount;
3088 // Save byte count
3089 count = lcount;
3091 BX_DEBUG_ATA("Trying to read %04x bytes (%04x %04x %04x) ",lbefore+lcount+lafter,lbefore,lcount,lafter);
3092 BX_DEBUG_ATA("to 0x%04x:0x%04x\n",bufseg,bufoff);
3094 // If counts not dividable by 4, use 16bits mode
3095 lmode = mode;
3096 if (lbefore & 0x03) lmode=ATA_MODE_PIO16;
3097 if (lcount & 0x03) lmode=ATA_MODE_PIO16;
3098 if (lafter & 0x03) lmode=ATA_MODE_PIO16;
3100 // adds an extra byte if count are odd. before is always even
3101 if (lcount & 0x01) {
3102 lcount+=1;
3103 if ((lafter > 0) && (lafter & 0x01)) {
3104 lafter-=1;
3108 if (lmode == ATA_MODE_PIO32) {
3109 lcount>>=2; lbefore>>=2; lafter>>=2;
3111 else {
3112 lcount>>=1; lbefore>>=1; lafter>>=1;
3115 ; // FIXME bcc bug
3117 ASM_START
3118 push bp
3119 mov bp, sp
3121 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data read port
3123 mov cx, _ata_cmd_packet.lbefore + 2[bp]
3124 jcxz ata_packet_no_before
3126 mov ah, _ata_cmd_packet.lmode + 2[bp]
3127 cmp ah, #ATA_MODE_PIO32
3128 je ata_packet_in_before_32
3130 ata_packet_in_before_16:
3131 in ax, dx
3132 loop ata_packet_in_before_16
3133 jmp ata_packet_no_before
3135 ata_packet_in_before_32:
3136 push eax
3137 ata_packet_in_before_32_loop:
3138 in eax, dx
3139 loop ata_packet_in_before_32_loop
3140 pop eax
3142 ata_packet_no_before:
3143 mov cx, _ata_cmd_packet.lcount + 2[bp]
3144 jcxz ata_packet_after
3146 mov di, _ata_cmd_packet.bufoff + 2[bp]
3147 mov ax, _ata_cmd_packet.bufseg + 2[bp]
3148 mov es, ax
3150 mov ah, _ata_cmd_packet.lmode + 2[bp]
3151 cmp ah, #ATA_MODE_PIO32
3152 je ata_packet_in_32
3154 ata_packet_in_16:
3156 insw ;; CX words transfered tp port(DX) to ES:[DI]
3157 jmp ata_packet_after
3159 ata_packet_in_32:
3161 insd ;; CX dwords transfered to port(DX) to ES:[DI]
3163 ata_packet_after:
3164 mov cx, _ata_cmd_packet.lafter + 2[bp]
3165 jcxz ata_packet_done
3167 mov ah, _ata_cmd_packet.lmode + 2[bp]
3168 cmp ah, #ATA_MODE_PIO32
3169 je ata_packet_in_after_32
3171 ata_packet_in_after_16:
3172 in ax, dx
3173 loop ata_packet_in_after_16
3174 jmp ata_packet_done
3176 ata_packet_in_after_32:
3177 push eax
3178 ata_packet_in_after_32_loop:
3179 in eax, dx
3180 loop ata_packet_in_after_32_loop
3181 pop eax
3183 ata_packet_done:
3184 pop bp
3185 ASM_END
3187 // Compute new buffer address
3188 bufoff += count;
3190 // Save transferred bytes count
3191 transfer += count;
3192 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,transfer);
3196 // Final check, device must be ready
3197 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
3198 != ATA_CB_STAT_RDY ) {
3199 BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", (unsigned) status);
3200 return 4;
3203 // Enable interrupts
3204 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
3205 return 0;
3208 // ---------------------------------------------------------------------------
3209 // End of ATA/ATAPI Driver
3210 // ---------------------------------------------------------------------------
3212 // ---------------------------------------------------------------------------
3213 // Start of ATA/ATAPI generic functions
3214 // ---------------------------------------------------------------------------
3216 Bit16u
3217 atapi_get_sense(device, seg, asc, ascq)
3218 Bit16u device;
3220 Bit8u atacmd[12];
3221 Bit8u buffer[18];
3222 Bit8u i;
3224 memsetb(get_SS(),atacmd,0,12);
3226 // Request SENSE
3227 atacmd[0]=ATA_CMD_REQUEST_SENSE;
3228 atacmd[4]=sizeof(buffer);
3229 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 18L, ATA_DATA_IN, get_SS(), buffer) != 0)
3230 return 0x0002;
3232 write_byte(seg,asc,buffer[12]);
3233 write_byte(seg,ascq,buffer[13]);
3235 return 0;
3238 Bit16u
3239 atapi_is_ready(device)
3240 Bit16u device;
3242 Bit8u packet[12];
3243 Bit8u buf[8];
3244 Bit32u block_len;
3245 Bit32u sectors;
3246 Bit32u timeout; //measured in ms
3247 Bit32u time;
3248 Bit8u asc, ascq;
3249 Bit8u in_progress;
3250 Bit16u ebda_seg = read_word(0x0040,0x000E);
3251 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI) {
3252 printf("not implemented for non-ATAPI device\n");
3253 return -1;
3256 BX_DEBUG_ATA("ata_detect_medium: begin\n");
3257 memsetb(get_SS(),packet, 0, sizeof packet);
3258 packet[0] = 0x25; /* READ CAPACITY */
3260 /* Retry READ CAPACITY 50 times unless MEDIUM NOT PRESENT
3261 * is reported by the device. If the device reports "IN PROGRESS",
3262 * 30 seconds is added. */
3263 timeout = 5000;
3264 time = 0;
3265 in_progress = 0;
3266 while (time < timeout) {
3267 if (ata_cmd_packet(device, sizeof(packet), get_SS(), packet, 0, 8L, ATA_DATA_IN, get_SS(), buf) == 0)
3268 goto ok;
3270 if (atapi_get_sense(device, get_SS(), &asc, &ascq) == 0) {
3271 if (asc == 0x3a) { /* MEDIUM NOT PRESENT */
3272 BX_DEBUG_ATA("Device reports MEDIUM NOT PRESENT\n");
3273 return -1;
3276 if (asc == 0x04 && ascq == 0x01 && !in_progress) {
3277 /* IN PROGRESS OF BECOMING READY */
3278 printf("Waiting for device to detect medium... ");
3279 /* Allow 30 seconds more */
3280 timeout = 30000;
3281 in_progress = 1;
3284 time += 100;
3286 BX_DEBUG_ATA("read capacity failed\n");
3287 return -1;
3290 block_len = (Bit32u) buf[4] << 24
3291 | (Bit32u) buf[5] << 16
3292 | (Bit32u) buf[6] << 8
3293 | (Bit32u) buf[7] << 0;
3294 BX_DEBUG_ATA("block_len=%u\n", block_len);
3296 if (block_len!= 2048 && block_len!= 512)
3298 printf("Unsupported sector size %u\n", block_len);
3299 return -1;
3301 write_dword(ebda_seg,&EbdaData->ata.devices[device].blksize, block_len);
3303 sectors = (Bit32u) buf[0] << 24
3304 | (Bit32u) buf[1] << 16
3305 | (Bit32u) buf[2] << 8
3306 | (Bit32u) buf[3] << 0;
3308 BX_DEBUG_ATA("sectors=%u\n", sectors);
3309 if (block_len == 2048)
3310 sectors <<= 2; /* # of sectors in 512-byte "soft" sector */
3311 if (sectors != read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors))
3312 printf("%dMB medium detected\n", sectors>>(20-9));
3313 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors, sectors);
3314 return 0;
3317 Bit16u
3318 atapi_is_cdrom(device)
3319 Bit8u device;
3321 Bit16u ebda_seg=read_word(0x0040,0x000E);
3323 if (device >= BX_MAX_ATA_DEVICES)
3324 return 0;
3326 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI)
3327 return 0;
3329 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].device) != ATA_DEVICE_CDROM)
3330 return 0;
3332 return 1;
3335 // ---------------------------------------------------------------------------
3336 // End of ATA/ATAPI generic functions
3337 // ---------------------------------------------------------------------------
3339 #endif // BX_USE_ATADRV
3341 #if BX_ELTORITO_BOOT
3343 // ---------------------------------------------------------------------------
3344 // Start of El-Torito boot functions
3345 // ---------------------------------------------------------------------------
3347 void
3348 cdemu_init()
3350 Bit16u ebda_seg=read_word(0x0040,0x000E);
3352 // the only important data is this one for now
3353 write_byte(ebda_seg,&EbdaData->cdemu.active,0x00);
3356 Bit8u
3357 cdemu_isactive()
3359 Bit16u ebda_seg=read_word(0x0040,0x000E);
3361 return(read_byte(ebda_seg,&EbdaData->cdemu.active));
3364 Bit8u
3365 cdemu_emulated_drive()
3367 Bit16u ebda_seg=read_word(0x0040,0x000E);
3369 return(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
3372 static char isotag[6]="CD001";
3373 static char eltorito[24]="EL TORITO SPECIFICATION";
3375 // Returns ah: emulated drive, al: error code
3377 Bit16u
3378 cdrom_boot()
3380 Bit16u ebda_seg=read_word(0x0040,0x000E);
3381 Bit8u atacmd[12], buffer[2048];
3382 Bit32u lba;
3383 Bit16u boot_segment, nbsectors, i, error;
3384 Bit8u device;
3386 // Find out the first cdrom
3387 for (device=0; device<BX_MAX_ATA_DEVICES;device++) {
3388 if (atapi_is_cdrom(device)) break;
3391 if(error = atapi_is_ready(device) != 0)
3392 BX_INFO("ata_is_ready returned %d\n",error);
3394 // if not found
3395 if(device >= BX_MAX_ATA_DEVICES) return 2;
3397 // Read the Boot Record Volume Descriptor
3398 memsetb(get_SS(),atacmd,0,12);
3399 atacmd[0]=0x28; // READ command
3400 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3401 atacmd[8]=(0x01 & 0x00ff); // Sectors
3402 atacmd[2]=(0x11 & 0xff000000) >> 24; // LBA
3403 atacmd[3]=(0x11 & 0x00ff0000) >> 16;
3404 atacmd[4]=(0x11 & 0x0000ff00) >> 8;
3405 atacmd[5]=(0x11 & 0x000000ff);
3406 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3407 return 3;
3409 // Validity checks
3410 if(buffer[0]!=0)return 4;
3411 for(i=0;i<5;i++){
3412 if(buffer[1+i]!=read_byte(0xf000,&isotag[i]))return 5;
3414 for(i=0;i<23;i++)
3415 if(buffer[7+i]!=read_byte(0xf000,&eltorito[i]))return 6;
3417 // ok, now we calculate the Boot catalog address
3418 lba=buffer[0x4A]*0x1000000+buffer[0x49]*0x10000+buffer[0x48]*0x100+buffer[0x47];
3420 // And we read the Boot Catalog
3421 memsetb(get_SS(),atacmd,0,12);
3422 atacmd[0]=0x28; // READ command
3423 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3424 atacmd[8]=(0x01 & 0x00ff); // Sectors
3425 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3426 atacmd[3]=(lba & 0x00ff0000) >> 16;
3427 atacmd[4]=(lba & 0x0000ff00) >> 8;
3428 atacmd[5]=(lba & 0x000000ff);
3429 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3430 return 7;
3432 // Validation entry
3433 if(buffer[0x00]!=0x01)return 8; // Header
3434 if(buffer[0x01]!=0x00)return 9; // Platform
3435 if(buffer[0x1E]!=0x55)return 10; // key 1
3436 if(buffer[0x1F]!=0xAA)return 10; // key 2
3438 // Initial/Default Entry
3439 if(buffer[0x20]!=0x88)return 11; // Bootable
3441 write_byte(ebda_seg,&EbdaData->cdemu.media,buffer[0x21]);
3442 if(buffer[0x21]==0){
3443 // FIXME ElTorito Hardcoded. cdrom is hardcoded as device 0xE0.
3444 // Win2000 cd boot needs to know it booted from cd
3445 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0xE0);
3447 else if(buffer[0x21]<4)
3448 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x00);
3449 else
3450 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x80);
3452 write_byte(ebda_seg,&EbdaData->cdemu.controller_index,device/2);
3453 write_byte(ebda_seg,&EbdaData->cdemu.device_spec,device%2);
3455 boot_segment=buffer[0x23]*0x100+buffer[0x22];
3456 if(boot_segment==0x0000)boot_segment=0x07C0;
3458 write_word(ebda_seg,&EbdaData->cdemu.load_segment,boot_segment);
3459 write_word(ebda_seg,&EbdaData->cdemu.buffer_segment,0x0000);
3461 nbsectors=buffer[0x27]*0x100+buffer[0x26];
3462 write_word(ebda_seg,&EbdaData->cdemu.sector_count,nbsectors);
3464 lba=buffer[0x2B]*0x1000000+buffer[0x2A]*0x10000+buffer[0x29]*0x100+buffer[0x28];
3465 write_dword(ebda_seg,&EbdaData->cdemu.ilba,lba);
3467 // And we read the image in memory
3468 memsetb(get_SS(),atacmd,0,12);
3469 atacmd[0]=0x28; // READ command
3470 atacmd[7]=((1+(nbsectors-1)/4) & 0xff00) >> 8; // Sectors
3471 atacmd[8]=((1+(nbsectors-1)/4) & 0x00ff); // Sectors
3472 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3473 atacmd[3]=(lba & 0x00ff0000) >> 16;
3474 atacmd[4]=(lba & 0x0000ff00) >> 8;
3475 atacmd[5]=(lba & 0x000000ff);
3476 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, nbsectors*512L, ATA_DATA_IN, boot_segment,0)) != 0)
3477 return 12;
3479 // Remember the media type
3480 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
3481 case 0x01: // 1.2M floppy
3482 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,15);
3483 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3484 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3485 break;
3486 case 0x02: // 1.44M floppy
3487 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,18);
3488 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3489 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3490 break;
3491 case 0x03: // 2.88M floppy
3492 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,36);
3493 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3494 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3495 break;
3496 case 0x04: // Harddrive
3497 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,read_byte(boot_segment,446+6)&0x3f);
3498 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,
3499 (read_byte(boot_segment,446+6)<<2) + read_byte(boot_segment,446+7) + 1);
3500 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,read_byte(boot_segment,446+5) + 1);
3501 break;
3504 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0) {
3505 // Increase bios installed hardware number of devices
3506 if(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)==0x00)
3507 write_byte(0x40,0x10,read_byte(0x40,0x10)|0x41);
3508 else
3509 write_byte(ebda_seg, &EbdaData->ata.hdcount, read_byte(ebda_seg, &EbdaData->ata.hdcount) + 1);
3513 // everything is ok, so from now on, the emulation is active
3514 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0)
3515 write_byte(ebda_seg,&EbdaData->cdemu.active,0x01);
3517 // return the boot drive + no error
3518 return (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)*0x100)+0;
3521 // ---------------------------------------------------------------------------
3522 // End of El-Torito boot functions
3523 // ---------------------------------------------------------------------------
3524 #endif // BX_ELTORITO_BOOT
3526 void
3527 int14_function(regs, ds, iret_addr)
3528 pusha_regs_t regs; // regs pushed from PUSHA instruction
3529 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
3530 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
3532 Bit16u addr,timer,val16;
3533 Bit8u timeout;
3535 ASM_START
3537 ASM_END
3539 addr = read_word(0x0040, (regs.u.r16.dx << 1));
3540 timeout = read_byte(0x0040, 0x007C + regs.u.r16.dx);
3541 if ((regs.u.r16.dx < 4) && (addr > 0)) {
3542 switch (regs.u.r8.ah) {
3543 case 0:
3544 outb(addr+3, inb(addr+3) | 0x80);
3545 if (regs.u.r8.al & 0xE0 == 0) {
3546 outb(addr, 0x17);
3547 outb(addr+1, 0x04);
3548 } else {
3549 val16 = 0x600 >> ((regs.u.r8.al & 0xE0) >> 5);
3550 outb(addr, val16 & 0xFF);
3551 outb(addr+1, val16 >> 8);
3553 outb(addr+3, regs.u.r8.al & 0x1F);
3554 regs.u.r8.ah = inb(addr+5);
3555 regs.u.r8.al = inb(addr+6);
3556 ClearCF(iret_addr.flags);
3557 break;
3558 case 1:
3559 timer = read_word(0x0040, 0x006C);
3560 while (((inb(addr+5) & 0x60) != 0x60) && (timeout)) {
3561 val16 = read_word(0x0040, 0x006C);
3562 if (val16 != timer) {
3563 timer = val16;
3564 timeout--;
3567 if (timeout) outb(addr, regs.u.r8.al);
3568 regs.u.r8.ah = inb(addr+5);
3569 if (!timeout) regs.u.r8.ah |= 0x80;
3570 ClearCF(iret_addr.flags);
3571 break;
3572 case 2:
3573 timer = read_word(0x0040, 0x006C);
3574 while (((inb(addr+5) & 0x01) == 0) && (timeout)) {
3575 val16 = read_word(0x0040, 0x006C);
3576 if (val16 != timer) {
3577 timer = val16;
3578 timeout--;
3581 if (timeout) {
3582 regs.u.r8.ah = 0;
3583 regs.u.r8.al = inb(addr);
3584 } else {
3585 regs.u.r8.ah = inb(addr+5);
3587 ClearCF(iret_addr.flags);
3588 break;
3589 case 3:
3590 regs.u.r8.ah = inb(addr+5);
3591 regs.u.r8.al = inb(addr+6);
3592 ClearCF(iret_addr.flags);
3593 break;
3594 default:
3595 SetCF(iret_addr.flags); // Unsupported
3597 } else {
3598 SetCF(iret_addr.flags); // Unsupported
3602 void
3603 int15_function(regs, ES, DS, FLAGS)
3604 pusha_regs_t regs; // REGS pushed via pusha
3605 Bit16u ES, DS, FLAGS;
3607 Bit16u ebda_seg=read_word(0x0040,0x000E);
3608 bx_bool prev_a20_enable;
3609 Bit16u base15_00;
3610 Bit8u base23_16;
3611 Bit16u ss;
3612 Bit16u CX,DX;
3614 Bit16u bRegister;
3615 Bit8u irqDisable;
3617 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3619 switch (regs.u.r8.ah) {
3620 case 0x24: /* A20 Control */
3621 switch (regs.u.r8.al) {
3622 case 0x00:
3623 set_enable_a20(0);
3624 CLEAR_CF();
3625 regs.u.r8.ah = 0;
3626 break;
3627 case 0x01:
3628 set_enable_a20(1);
3629 CLEAR_CF();
3630 regs.u.r8.ah = 0;
3631 break;
3632 case 0x02:
3633 regs.u.r8.al = (inb(0x92) >> 1) & 0x01;
3634 CLEAR_CF();
3635 regs.u.r8.ah = 0;
3636 break;
3637 case 0x03:
3638 CLEAR_CF();
3639 regs.u.r8.ah = 0;
3640 regs.u.r16.bx = 3;
3641 break;
3642 default:
3643 BX_INFO("int15: Func 24h, subfunc %02xh, A20 gate control not supported\n", (unsigned) regs.u.r8.al);
3644 SET_CF();
3645 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3647 break;
3649 case 0x41:
3650 SET_CF();
3651 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3652 break;
3654 case 0x4f:
3655 /* keyboard intercept */
3656 #if BX_CPU < 2
3657 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3658 #else
3659 // nop
3660 #endif
3661 SET_CF();
3662 break;
3664 case 0x52: // removable media eject
3665 CLEAR_CF();
3666 regs.u.r8.ah = 0; // "ok ejection may proceed"
3667 break;
3669 case 0x83: {
3670 if( regs.u.r8.al == 0 ) {
3671 // Set Interval requested.
3672 if( ( read_byte( 0x40, 0xA0 ) & 1 ) == 0 ) {
3673 // Interval not already set.
3674 write_byte( 0x40, 0xA0, 1 ); // Set status byte.
3675 write_word( 0x40, 0x98, ES ); // Byte location, segment
3676 write_word( 0x40, 0x9A, regs.u.r16.bx ); // Byte location, offset
3677 write_word( 0x40, 0x9C, regs.u.r16.dx ); // Low word, delay
3678 write_word( 0x40, 0x9E, regs.u.r16.cx ); // High word, delay.
3679 CLEAR_CF( );
3680 irqDisable = inb( 0xA1 );
3681 outb( 0xA1, irqDisable & 0xFE );
3682 bRegister = inb_cmos( 0xB ); // Unmask IRQ8 so INT70 will get through.
3683 outb_cmos( 0xB, bRegister | 0x40 ); // Turn on the Periodic Interrupt timer
3684 } else {
3685 // Interval already set.
3686 BX_DEBUG_INT15("int15: Func 83h, failed, already waiting.\n" );
3687 SET_CF();
3688 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3690 } else if( regs.u.r8.al == 1 ) {
3691 // Clear Interval requested
3692 write_byte( 0x40, 0xA0, 0 ); // Clear status byte
3693 CLEAR_CF( );
3694 bRegister = inb_cmos( 0xB );
3695 outb_cmos( 0xB, bRegister & ~0x40 ); // Turn off the Periodic Interrupt timer
3696 } else {
3697 BX_DEBUG_INT15("int15: Func 83h, failed.\n" );
3698 SET_CF();
3699 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3700 regs.u.r8.al--;
3703 break;
3706 case 0x87:
3707 #if BX_CPU < 3
3708 # error "Int15 function 87h not supported on < 80386"
3709 #endif
3710 // +++ should probably have descriptor checks
3711 // +++ should have exception handlers
3713 // turn off interrupts
3714 ASM_START
3716 ASM_END
3718 prev_a20_enable = set_enable_a20(1); // enable A20 line
3720 // 128K max of transfer on 386+ ???
3721 // source == destination ???
3723 // ES:SI points to descriptor table
3724 // offset use initially comments
3725 // ==============================================
3726 // 00..07 Unused zeros Null descriptor
3727 // 08..0f GDT zeros filled in by BIOS
3728 // 10..17 source ssssssss source of data
3729 // 18..1f dest dddddddd destination of data
3730 // 20..27 CS zeros filled in by BIOS
3731 // 28..2f SS zeros filled in by BIOS
3733 //es:si
3734 //eeee0
3735 //0ssss
3736 //-----
3738 // check for access rights of source & dest here
3740 // Initialize GDT descriptor
3741 base15_00 = (ES << 4) + regs.u.r16.si;
3742 base23_16 = ES >> 12;
3743 if (base15_00 < (ES<<4))
3744 base23_16++;
3745 write_word(ES, regs.u.r16.si+0x08+0, 47); // limit 15:00 = 6 * 8bytes/descriptor
3746 write_word(ES, regs.u.r16.si+0x08+2, base15_00);// base 15:00
3747 write_byte(ES, regs.u.r16.si+0x08+4, base23_16);// base 23:16
3748 write_byte(ES, regs.u.r16.si+0x08+5, 0x93); // access
3749 write_word(ES, regs.u.r16.si+0x08+6, 0x0000); // base 31:24/reserved/limit 19:16
3751 // Initialize CS descriptor
3752 write_word(ES, regs.u.r16.si+0x20+0, 0xffff);// limit 15:00 = normal 64K limit
3753 write_word(ES, regs.u.r16.si+0x20+2, 0x0000);// base 15:00
3754 write_byte(ES, regs.u.r16.si+0x20+4, 0x000f);// base 23:16
3755 write_byte(ES, regs.u.r16.si+0x20+5, 0x9b); // access
3756 write_word(ES, regs.u.r16.si+0x20+6, 0x0000);// base 31:24/reserved/limit 19:16
3758 // Initialize SS descriptor
3759 ss = get_SS();
3760 base15_00 = ss << 4;
3761 base23_16 = ss >> 12;
3762 write_word(ES, regs.u.r16.si+0x28+0, 0xffff); // limit 15:00 = normal 64K limit
3763 write_word(ES, regs.u.r16.si+0x28+2, base15_00);// base 15:00
3764 write_byte(ES, regs.u.r16.si+0x28+4, base23_16);// base 23:16
3765 write_byte(ES, regs.u.r16.si+0x28+5, 0x93); // access
3766 write_word(ES, regs.u.r16.si+0x28+6, 0x0000); // base 31:24/reserved/limit 19:16
3768 CX = regs.u.r16.cx;
3769 ASM_START
3770 // Compile generates locals offset info relative to SP.
3771 // Get CX (word count) from stack.
3772 mov bx, sp
3773 SEG SS
3774 mov cx, _int15_function.CX [bx]
3776 // since we need to set SS:SP, save them to the BDA
3777 // for future restore
3778 push eax
3779 xor eax, eax
3780 mov ds, ax
3781 mov 0x0469, ss
3782 mov 0x0467, sp
3784 SEG ES
3785 lgdt [si + 0x08]
3786 SEG CS
3787 lidt [pmode_IDT_info]
3788 ;; perhaps do something with IDT here
3790 ;; set PE bit in CR0
3791 mov eax, cr0
3792 or al, #0x01
3793 mov cr0, eax
3794 ;; far jump to flush CPU queue after transition to protected mode
3795 JMP_AP(0x0020, protected_mode)
3797 protected_mode:
3798 ;; GDT points to valid descriptor table, now load SS, DS, ES
3799 mov ax, #0x28 ;; 101 000 = 5th descriptor in table, TI=GDT, RPL=00
3800 mov ss, ax
3801 mov ax, #0x10 ;; 010 000 = 2nd descriptor in table, TI=GDT, RPL=00
3802 mov ds, ax
3803 mov ax, #0x18 ;; 011 000 = 3rd descriptor in table, TI=GDT, RPL=00
3804 mov es, ax
3805 xor si, si
3806 xor di, di
3809 movsw ;; move CX words from DS:SI to ES:DI
3811 ;; make sure DS and ES limits are 64KB
3812 mov ax, #0x28
3813 mov ds, ax
3814 mov es, ax
3816 ;; reset PG bit in CR0 ???
3817 mov eax, cr0
3818 and al, #0xFE
3819 mov cr0, eax
3821 ;; far jump to flush CPU queue after transition to real mode
3822 JMP_AP(0xf000, real_mode)
3824 real_mode:
3825 ;; restore IDT to normal real-mode defaults
3826 SEG CS
3827 lidt [rmode_IDT_info]
3829 // restore SS:SP from the BDA
3830 xor ax, ax
3831 mov ds, ax
3832 mov ss, 0x0469
3833 mov sp, 0x0467
3834 pop eax
3835 ASM_END
3837 set_enable_a20(prev_a20_enable);
3839 // turn back on interrupts
3840 ASM_START
3842 ASM_END
3844 regs.u.r8.ah = 0;
3845 CLEAR_CF();
3846 break;
3849 case 0x88:
3850 // Get the amount of extended memory (above 1M)
3851 #if BX_CPU < 2
3852 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3853 SET_CF();
3854 #else
3855 regs.u.r8.al = inb_cmos(0x30);
3856 regs.u.r8.ah = inb_cmos(0x31);
3858 // According to Ralf Brown's interrupt the limit should be 15M,
3859 // but real machines mostly return max. 63M.
3860 if(regs.u.r16.ax > 0xffc0)
3861 regs.u.r16.ax = 0xffc0;
3863 CLEAR_CF();
3864 #endif
3865 break;
3867 case 0x90:
3868 /* Device busy interrupt. Called by Int 16h when no key available */
3869 break;
3871 case 0x91:
3872 /* Interrupt complete. Called by Int 16h when key becomes available */
3873 break;
3875 case 0xbf:
3876 BX_INFO("*** int 15h function AH=bf not yet supported!\n");
3877 SET_CF();
3878 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3879 break;
3881 case 0xC0:
3882 #if 0
3883 SET_CF();
3884 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3885 break;
3886 #endif
3887 CLEAR_CF();
3888 regs.u.r8.ah = 0;
3889 regs.u.r16.bx = BIOS_CONFIG_TABLE;
3890 ES = 0xF000;
3891 break;
3893 case 0xc1:
3894 ES = ebda_seg;
3895 CLEAR_CF();
3896 break;
3898 case 0xd8:
3899 bios_printf(BIOS_PRINTF_DEBUG, "EISA BIOS not present\n");
3900 SET_CF();
3901 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3902 break;
3904 default:
3905 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
3906 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
3907 SET_CF();
3908 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3909 break;
3913 #if BX_USE_PS2_MOUSE
3914 void
3915 int15_function_mouse(regs, ES, DS, FLAGS)
3916 pusha_regs_t regs; // REGS pushed via pusha
3917 Bit16u ES, DS, FLAGS;
3919 Bit16u ebda_seg=read_word(0x0040,0x000E);
3920 Bit8u mouse_flags_1, mouse_flags_2;
3921 Bit16u mouse_driver_seg;
3922 Bit16u mouse_driver_offset;
3923 Bit8u comm_byte, prev_command_byte;
3924 Bit8u ret, mouse_data1, mouse_data2, mouse_data3;
3926 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3928 switch (regs.u.r8.ah) {
3929 case 0xC2:
3930 // Return Codes status in AH
3931 // =========================
3932 // 00: success
3933 // 01: invalid subfunction (AL > 7)
3934 // 02: invalid input value (out of allowable range)
3935 // 03: interface error
3936 // 04: resend command received from mouse controller,
3937 // device driver should attempt command again
3938 // 05: cannot enable mouse, since no far call has been installed
3939 // 80/86: mouse service not implemented
3941 switch (regs.u.r8.al) {
3942 case 0: // Disable/Enable Mouse
3943 BX_DEBUG_INT15("case 0:\n");
3944 switch (regs.u.r8.bh) {
3945 case 0: // Disable Mouse
3946 BX_DEBUG_INT15("case 0: disable mouse\n");
3947 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3948 ret = send_to_mouse_ctrl(0xF5); // disable mouse command
3949 if (ret == 0) {
3950 ret = get_mouse_data(&mouse_data1);
3951 if ( (ret == 0) || (mouse_data1 == 0xFA) ) {
3952 CLEAR_CF();
3953 regs.u.r8.ah = 0;
3954 return;
3958 // error
3959 SET_CF();
3960 regs.u.r8.ah = ret;
3961 return;
3962 break;
3964 case 1: // Enable Mouse
3965 BX_DEBUG_INT15("case 1: enable mouse\n");
3966 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
3967 if ( (mouse_flags_2 & 0x80) == 0 ) {
3968 BX_DEBUG_INT15("INT 15h C2 Enable Mouse, no far call handler\n");
3969 SET_CF(); // error
3970 regs.u.r8.ah = 5; // no far call installed
3971 return;
3973 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3974 ret = send_to_mouse_ctrl(0xF4); // enable mouse command
3975 if (ret == 0) {
3976 ret = get_mouse_data(&mouse_data1);
3977 if ( (ret == 0) && (mouse_data1 == 0xFA) ) {
3978 enable_mouse_int_and_events(); // turn IRQ12 and packet generation on
3979 CLEAR_CF();
3980 regs.u.r8.ah = 0;
3981 return;
3984 SET_CF();
3985 regs.u.r8.ah = ret;
3986 return;
3988 default: // invalid subfunction
3989 BX_DEBUG_INT15("INT 15h C2 AL=0, BH=%02x\n", (unsigned) regs.u.r8.bh);
3990 SET_CF(); // error
3991 regs.u.r8.ah = 1; // invalid subfunction
3992 return;
3994 break;
3996 case 1: // Reset Mouse
3997 case 5: // Initialize Mouse
3998 BX_DEBUG_INT15("case 1 or 5:\n");
3999 if (regs.u.r8.al == 5) {
4000 if (regs.u.r8.bh != 3) {
4001 SET_CF();
4002 regs.u.r8.ah = 0x02; // invalid input
4003 return;
4005 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4006 mouse_flags_2 = (mouse_flags_2 & 0x00) | regs.u.r8.bh;
4007 mouse_flags_1 = 0x00;
4008 write_byte(ebda_seg, 0x0026, mouse_flags_1);
4009 write_byte(ebda_seg, 0x0027, mouse_flags_2);
4012 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4013 ret = send_to_mouse_ctrl(0xFF); // reset mouse command
4014 if (ret == 0) {
4015 ret = get_mouse_data(&mouse_data3);
4016 // if no mouse attached, it will return RESEND
4017 if (mouse_data3 == 0xfe) {
4018 SET_CF();
4019 return;
4021 if (mouse_data3 != 0xfa)
4022 BX_PANIC("Mouse reset returned %02x (should be ack)\n", (unsigned)mouse_data3);
4023 if ( ret == 0 ) {
4024 ret = get_mouse_data(&mouse_data1);
4025 if ( ret == 0 ) {
4026 ret = get_mouse_data(&mouse_data2);
4027 if ( ret == 0 ) {
4028 // turn IRQ12 and packet generation on
4029 enable_mouse_int_and_events();
4030 CLEAR_CF();
4031 regs.u.r8.ah = 0;
4032 regs.u.r8.bl = mouse_data1;
4033 regs.u.r8.bh = mouse_data2;
4034 return;
4040 // error
4041 SET_CF();
4042 regs.u.r8.ah = ret;
4043 return;
4045 case 2: // Set Sample Rate
4046 BX_DEBUG_INT15("case 2:\n");
4047 switch (regs.u.r8.bh) {
4048 case 0: mouse_data1 = 10; break; // 10 reports/sec
4049 case 1: mouse_data1 = 20; break; // 20 reports/sec
4050 case 2: mouse_data1 = 40; break; // 40 reports/sec
4051 case 3: mouse_data1 = 60; break; // 60 reports/sec
4052 case 4: mouse_data1 = 80; break; // 80 reports/sec
4053 case 5: mouse_data1 = 100; break; // 100 reports/sec (default)
4054 case 6: mouse_data1 = 200; break; // 200 reports/sec
4055 default: mouse_data1 = 0;
4057 if (mouse_data1 > 0) {
4058 ret = send_to_mouse_ctrl(0xF3); // set sample rate command
4059 if (ret == 0) {
4060 ret = get_mouse_data(&mouse_data2);
4061 ret = send_to_mouse_ctrl(mouse_data1);
4062 ret = get_mouse_data(&mouse_data2);
4063 CLEAR_CF();
4064 regs.u.r8.ah = 0;
4065 } else {
4066 // error
4067 SET_CF();
4068 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4070 } else {
4071 // error
4072 SET_CF();
4073 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4075 break;
4077 case 3: // Set Resolution
4078 BX_DEBUG_INT15("case 3:\n");
4079 // BH:
4080 // 0 = 25 dpi, 1 count per millimeter
4081 // 1 = 50 dpi, 2 counts per millimeter
4082 // 2 = 100 dpi, 4 counts per millimeter
4083 // 3 = 200 dpi, 8 counts per millimeter
4084 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4085 if (regs.u.r8.bh < 4) {
4086 ret = send_to_mouse_ctrl(0xE8); // set resolution command
4087 if (ret == 0) {
4088 ret = get_mouse_data(&mouse_data1);
4089 if (mouse_data1 != 0xfa)
4090 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
4091 ret = send_to_mouse_ctrl(regs.u.r8.bh);
4092 ret = get_mouse_data(&mouse_data1);
4093 if (mouse_data1 != 0xfa)
4094 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
4095 CLEAR_CF();
4096 regs.u.r8.ah = 0;
4097 } else {
4098 // error
4099 SET_CF();
4100 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4102 } else {
4103 // error
4104 SET_CF();
4105 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4107 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4108 break;
4110 case 4: // Get Device ID
4111 BX_DEBUG_INT15("case 4:\n");
4112 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4113 ret = send_to_mouse_ctrl(0xF2); // get mouse ID command
4114 if (ret == 0) {
4115 ret = get_mouse_data(&mouse_data1);
4116 ret = get_mouse_data(&mouse_data2);
4117 CLEAR_CF();
4118 regs.u.r8.ah = 0;
4119 regs.u.r8.bh = mouse_data2;
4120 } else {
4121 // error
4122 SET_CF();
4123 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4125 break;
4127 case 6: // Return Status & Set Scaling Factor...
4128 BX_DEBUG_INT15("case 6:\n");
4129 switch (regs.u.r8.bh) {
4130 case 0: // Return Status
4131 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4132 ret = send_to_mouse_ctrl(0xE9); // get mouse info command
4133 if (ret == 0) {
4134 ret = get_mouse_data(&mouse_data1);
4135 if (mouse_data1 != 0xfa)
4136 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
4137 if (ret == 0) {
4138 ret = get_mouse_data(&mouse_data1);
4139 if ( ret == 0 ) {
4140 ret = get_mouse_data(&mouse_data2);
4141 if ( ret == 0 ) {
4142 ret = get_mouse_data(&mouse_data3);
4143 if ( ret == 0 ) {
4144 CLEAR_CF();
4145 regs.u.r8.ah = 0;
4146 regs.u.r8.bl = mouse_data1;
4147 regs.u.r8.cl = mouse_data2;
4148 regs.u.r8.dl = mouse_data3;
4149 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4150 return;
4157 // error
4158 SET_CF();
4159 regs.u.r8.ah = ret;
4160 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4161 return;
4163 case 1: // Set Scaling Factor to 1:1
4164 case 2: // Set Scaling Factor to 2:1
4165 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4166 if (regs.u.r8.bh == 1) {
4167 ret = send_to_mouse_ctrl(0xE6);
4168 } else {
4169 ret = send_to_mouse_ctrl(0xE7);
4171 if (ret == 0) {
4172 get_mouse_data(&mouse_data1);
4173 ret = (mouse_data1 != 0xFA);
4175 if (ret == 0) {
4176 CLEAR_CF();
4177 regs.u.r8.ah = 0;
4178 } else {
4179 // error
4180 SET_CF();
4181 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4183 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4184 break;
4186 default:
4187 BX_PANIC("INT 15h C2 AL=6, BH=%02x\n", (unsigned) regs.u.r8.bh);
4189 break;
4191 case 7: // Set Mouse Handler Address
4192 BX_DEBUG_INT15("case 7:\n");
4193 mouse_driver_seg = ES;
4194 mouse_driver_offset = regs.u.r16.bx;
4195 write_word(ebda_seg, 0x0022, mouse_driver_offset);
4196 write_word(ebda_seg, 0x0024, mouse_driver_seg);
4197 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4198 if (mouse_driver_offset == 0 && mouse_driver_seg == 0) {
4199 /* remove handler */
4200 if ( (mouse_flags_2 & 0x80) != 0 ) {
4201 mouse_flags_2 &= ~0x80;
4202 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4205 else {
4206 /* install handler */
4207 mouse_flags_2 |= 0x80;
4209 write_byte(ebda_seg, 0x0027, mouse_flags_2);
4210 CLEAR_CF();
4211 regs.u.r8.ah = 0;
4212 break;
4214 default:
4215 BX_DEBUG_INT15("case default:\n");
4216 regs.u.r8.ah = 1; // invalid function
4217 SET_CF();
4219 break;
4221 default:
4222 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4223 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4224 SET_CF();
4225 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4226 break;
4229 #endif
4232 void set_e820_range(ES, DI, start, end, type)
4233 Bit16u ES;
4234 Bit16u DI;
4235 Bit32u start;
4236 Bit32u end;
4237 Bit16u type;
4239 write_word(ES, DI, start);
4240 write_word(ES, DI+2, start >> 16);
4241 write_word(ES, DI+4, 0x00);
4242 write_word(ES, DI+6, 0x00);
4244 end -= start;
4245 write_word(ES, DI+8, end);
4246 write_word(ES, DI+10, end >> 16);
4247 write_word(ES, DI+12, 0x0000);
4248 write_word(ES, DI+14, 0x0000);
4250 write_word(ES, DI+16, type);
4251 write_word(ES, DI+18, 0x0);
4254 void
4255 int15_function32(regs, ES, DS, FLAGS)
4256 pushad_regs_t regs; // REGS pushed via pushad
4257 Bit16u ES, DS, FLAGS;
4259 Bit32u extended_memory_size=0; // 64bits long
4260 Bit16u CX,DX;
4262 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
4264 switch (regs.u.r8.ah) {
4265 case 0x86:
4266 // Wait for CX:DX microseconds. currently using the
4267 // refresh request port 0x61 bit4, toggling every 15usec
4269 CX = regs.u.r16.cx;
4270 DX = regs.u.r16.dx;
4272 ASM_START
4275 ;; Get the count in eax
4276 mov bx, sp
4277 SEG SS
4278 mov ax, _int15_function32.CX [bx]
4279 shl eax, #16
4280 SEG SS
4281 mov ax, _int15_function32.DX [bx]
4283 ;; convert to numbers of 15usec ticks
4284 mov ebx, #15
4285 xor edx, edx
4286 div eax, ebx
4287 mov ecx, eax
4289 ;; wait for ecx number of refresh requests
4290 in al, #0x61
4291 and al,#0x10
4292 mov ah, al
4294 or ecx, ecx
4295 je int1586_tick_end
4296 int1586_tick:
4297 in al, #0x61
4298 and al,#0x10
4299 cmp al, ah
4300 je int1586_tick
4301 mov ah, al
4302 dec ecx
4303 jnz int1586_tick
4304 int1586_tick_end:
4305 ASM_END
4307 break;
4309 case 0xe8:
4310 switch(regs.u.r8.al)
4312 case 0x20: // coded by osmaker aka K.J.
4313 if(regs.u.r32.edx == 0x534D4150)
4315 extended_memory_size = inb_cmos(0x35);
4316 extended_memory_size <<= 8;
4317 extended_memory_size |= inb_cmos(0x34);
4318 extended_memory_size *= 64;
4319 // greater than EFF00000???
4320 if(extended_memory_size > 0x3bc000) {
4321 extended_memory_size = 0x3bc000; // everything after this is reserved memory until we get to 0x100000000
4323 extended_memory_size *= 1024;
4324 extended_memory_size += (16L * 1024 * 1024);
4326 if(extended_memory_size <= (16L * 1024 * 1024)) {
4327 extended_memory_size = inb_cmos(0x31);
4328 extended_memory_size <<= 8;
4329 extended_memory_size |= inb_cmos(0x30);
4330 extended_memory_size *= 1024;
4333 switch(regs.u.r16.bx)
4335 case 0:
4336 set_e820_range(ES, regs.u.r16.di,
4337 0x0000000L, 0x0009fc00L, 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, 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, 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, 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, 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 /* 256KB BIOS area at the end of 4 GB */
4384 set_e820_range(ES, regs.u.r16.di,
4385 0xfffc0000L, 0x00000000L, 2);
4386 regs.u.r32.ebx = 0;
4387 regs.u.r32.eax = 0x534D4150;
4388 regs.u.r32.ecx = 0x14;
4389 CLEAR_CF();
4390 return;
4391 default: /* AX=E820, DX=534D4150, BX unrecognized */
4392 goto int15_unimplemented;
4393 break;
4395 } else {
4396 // if DX != 0x534D4150)
4397 goto int15_unimplemented;
4399 break;
4401 case 0x01:
4402 // do we have any reason to fail here ?
4403 CLEAR_CF();
4405 // my real system sets ax and bx to 0
4406 // this is confirmed by Ralph Brown list
4407 // but syslinux v1.48 is known to behave
4408 // strangely if ax is set to 0
4409 // regs.u.r16.ax = 0;
4410 // regs.u.r16.bx = 0;
4412 // Get the amount of extended memory (above 1M)
4413 regs.u.r8.cl = inb_cmos(0x30);
4414 regs.u.r8.ch = inb_cmos(0x31);
4416 // limit to 15M
4417 if(regs.u.r16.cx > 0x3c00)
4419 regs.u.r16.cx = 0x3c00;
4422 // Get the amount of extended memory above 16M in 64k blocs
4423 regs.u.r8.dl = inb_cmos(0x34);
4424 regs.u.r8.dh = inb_cmos(0x35);
4426 // Set configured memory equal to extended memory
4427 regs.u.r16.ax = regs.u.r16.cx;
4428 regs.u.r16.bx = regs.u.r16.dx;
4429 break;
4430 default: /* AH=0xE8?? but not implemented */
4431 goto int15_unimplemented;
4433 break;
4434 int15_unimplemented:
4435 // fall into the default
4436 default:
4437 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4438 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4439 SET_CF();
4440 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4441 break;
4445 void
4446 int16_function(DI, SI, BP, SP, BX, DX, CX, AX, FLAGS)
4447 Bit16u DI, SI, BP, SP, BX, DX, CX, AX, FLAGS;
4449 Bit8u scan_code, ascii_code, shift_flags, led_flags, count;
4450 Bit16u kbd_code, max;
4452 BX_DEBUG_INT16("int16: AX=%04x BX=%04x CX=%04x DX=%04x \n", AX, BX, CX, DX);
4454 shift_flags = read_byte(0x0040, 0x17);
4455 led_flags = read_byte(0x0040, 0x97);
4456 if ((((shift_flags >> 4) & 0x07) ^ (led_flags & 0x07)) != 0) {
4457 ASM_START
4459 ASM_END
4460 outb(0x60, 0xed);
4461 while ((inb(0x64) & 0x01) == 0) outb(0x80, 0x21);
4462 if ((inb(0x60) == 0xfa)) {
4463 led_flags &= 0xf8;
4464 led_flags |= ((shift_flags >> 4) & 0x07);
4465 outb(0x60, led_flags & 0x07);
4466 while ((inb(0x64) & 0x01) == 0) outb(0x80, 0x21);
4467 inb(0x60);
4468 write_byte(0x0040, 0x97, led_flags);
4470 ASM_START
4472 ASM_END
4475 switch (GET_AH()) {
4476 case 0x00: /* read keyboard input */
4478 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4479 BX_PANIC("KBD: int16h: out of keyboard input\n");
4481 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4482 else if (ascii_code == 0xE0) ascii_code = 0;
4483 AX = (scan_code << 8) | ascii_code;
4484 break;
4486 case 0x01: /* check keyboard status */
4487 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4488 SET_ZF();
4489 return;
4491 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4492 else if (ascii_code == 0xE0) ascii_code = 0;
4493 AX = (scan_code << 8) | ascii_code;
4494 CLEAR_ZF();
4495 break;
4497 case 0x02: /* get shift flag status */
4498 shift_flags = read_byte(0x0040, 0x17);
4499 SET_AL(shift_flags);
4500 break;
4502 case 0x05: /* store key-stroke into buffer */
4503 if ( !enqueue_key(GET_CH(), GET_CL()) ) {
4504 SET_AL(1);
4506 else {
4507 SET_AL(0);
4509 break;
4511 case 0x09: /* GET KEYBOARD FUNCTIONALITY */
4512 // bit Bochs Description
4513 // 7 0 reserved
4514 // 6 0 INT 16/AH=20h-22h supported (122-key keyboard support)
4515 // 5 1 INT 16/AH=10h-12h supported (enhanced keyboard support)
4516 // 4 1 INT 16/AH=0Ah supported
4517 // 3 0 INT 16/AX=0306h supported
4518 // 2 0 INT 16/AX=0305h supported
4519 // 1 0 INT 16/AX=0304h supported
4520 // 0 0 INT 16/AX=0300h supported
4522 SET_AL(0x30);
4523 break;
4525 case 0x0A: /* GET KEYBOARD ID */
4526 count = 2;
4527 kbd_code = 0x0;
4528 outb(0x60, 0xf2);
4529 /* Wait for data */
4530 max=0xffff;
4531 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4532 if (max>0x0) {
4533 if ((inb(0x60) == 0xfa)) {
4534 do {
4535 max=0xffff;
4536 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4537 if (max>0x0) {
4538 kbd_code >>= 8;
4539 kbd_code |= (inb(0x60) << 8);
4541 } while (--count>0);
4544 BX=kbd_code;
4545 break;
4547 case 0x10: /* read MF-II keyboard input */
4549 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4550 BX_PANIC("KBD: int16h: out of keyboard input\n");
4552 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4553 AX = (scan_code << 8) | ascii_code;
4554 break;
4556 case 0x11: /* check MF-II keyboard status */
4557 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4558 SET_ZF();
4559 return;
4561 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4562 AX = (scan_code << 8) | ascii_code;
4563 CLEAR_ZF();
4564 break;
4566 case 0x12: /* get extended keyboard status */
4567 shift_flags = read_byte(0x0040, 0x17);
4568 SET_AL(shift_flags);
4569 shift_flags = read_byte(0x0040, 0x18) & 0x73;
4570 shift_flags |= read_byte(0x0040, 0x96) & 0x0c;
4571 SET_AH(shift_flags);
4572 BX_DEBUG_INT16("int16: func 12 sending %04x\n",AX);
4573 break;
4575 case 0x92: /* keyboard capability check called by DOS 5.0+ keyb */
4576 SET_AH(0x80); // function int16 ah=0x10-0x12 supported
4577 break;
4579 case 0xA2: /* 122 keys capability check called by DOS 5.0+ keyb */
4580 // don't change AH : function int16 ah=0x20-0x22 NOT supported
4581 break;
4583 case 0x6F:
4584 if (GET_AL() == 0x08)
4585 SET_AH(0x02); // unsupported, aka normal keyboard
4587 default:
4588 BX_INFO("KBD: unsupported int 16h function %02x\n", GET_AH());
4592 unsigned int
4593 dequeue_key(scan_code, ascii_code, incr)
4594 Bit8u *scan_code;
4595 Bit8u *ascii_code;
4596 unsigned int incr;
4598 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail;
4599 Bit16u ss;
4600 Bit8u acode, scode;
4602 #if BX_CPU < 2
4603 buffer_start = 0x001E;
4604 buffer_end = 0x003E;
4605 #else
4606 buffer_start = read_word(0x0040, 0x0080);
4607 buffer_end = read_word(0x0040, 0x0082);
4608 #endif
4610 buffer_head = read_word(0x0040, 0x001a);
4611 buffer_tail = read_word(0x0040, 0x001c);
4613 if (buffer_head != buffer_tail) {
4614 ss = get_SS();
4615 acode = read_byte(0x0040, buffer_head);
4616 scode = read_byte(0x0040, buffer_head+1);
4617 write_byte(ss, ascii_code, acode);
4618 write_byte(ss, scan_code, scode);
4620 if (incr) {
4621 buffer_head += 2;
4622 if (buffer_head >= buffer_end)
4623 buffer_head = buffer_start;
4624 write_word(0x0040, 0x001a, buffer_head);
4626 return(1);
4628 else {
4629 return(0);
4633 static char panic_msg_keyb_buffer_full[] = "%s: keyboard input buffer full\n";
4635 Bit8u
4636 inhibit_mouse_int_and_events()
4638 Bit8u command_byte, prev_command_byte;
4640 // Turn off IRQ generation and aux data line
4641 if ( inb(0x64) & 0x02 )
4642 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4643 outb(0x64, 0x20); // get command byte
4644 while ( (inb(0x64) & 0x01) != 0x01 );
4645 prev_command_byte = inb(0x60);
4646 command_byte = prev_command_byte;
4647 //while ( (inb(0x64) & 0x02) );
4648 if ( inb(0x64) & 0x02 )
4649 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4650 command_byte &= 0xfd; // turn off IRQ 12 generation
4651 command_byte |= 0x20; // disable mouse serial clock line
4652 outb(0x64, 0x60); // write command byte
4653 outb(0x60, command_byte);
4654 return(prev_command_byte);
4657 void
4658 enable_mouse_int_and_events()
4660 Bit8u command_byte;
4662 // Turn on IRQ generation and aux data line
4663 if ( inb(0x64) & 0x02 )
4664 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4665 outb(0x64, 0x20); // get command byte
4666 while ( (inb(0x64) & 0x01) != 0x01 );
4667 command_byte = inb(0x60);
4668 //while ( (inb(0x64) & 0x02) );
4669 if ( inb(0x64) & 0x02 )
4670 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4671 command_byte |= 0x02; // turn on IRQ 12 generation
4672 command_byte &= 0xdf; // enable mouse serial clock line
4673 outb(0x64, 0x60); // write command byte
4674 outb(0x60, command_byte);
4677 Bit8u
4678 send_to_mouse_ctrl(sendbyte)
4679 Bit8u sendbyte;
4681 Bit8u response;
4683 // wait for chance to write to ctrl
4684 if ( inb(0x64) & 0x02 )
4685 BX_PANIC(panic_msg_keyb_buffer_full,"sendmouse");
4686 outb(0x64, 0xD4);
4687 outb(0x60, sendbyte);
4688 return(0);
4692 Bit8u
4693 get_mouse_data(data)
4694 Bit8u *data;
4696 Bit8u response;
4697 Bit16u ss;
4699 while ( (inb(0x64) & 0x21) != 0x21 ) {
4702 response = inb(0x60);
4704 ss = get_SS();
4705 write_byte(ss, data, response);
4706 return(0);
4709 void
4710 set_kbd_command_byte(command_byte)
4711 Bit8u command_byte;
4713 if ( inb(0x64) & 0x02 )
4714 BX_PANIC(panic_msg_keyb_buffer_full,"setkbdcomm");
4715 outb(0x64, 0xD4);
4717 outb(0x64, 0x60); // write command byte
4718 outb(0x60, command_byte);
4721 void
4722 int09_function(DI, SI, BP, SP, BX, DX, CX, AX)
4723 Bit16u DI, SI, BP, SP, BX, DX, CX, AX;
4725 Bit8u scancode, asciicode, shift_flags;
4726 Bit8u mf2_flags, mf2_state;
4729 // DS has been set to F000 before call
4733 scancode = GET_AL();
4735 if (scancode == 0) {
4736 BX_INFO("KBD: int09 handler: AL=0\n");
4737 return;
4741 shift_flags = read_byte(0x0040, 0x17);
4742 mf2_flags = read_byte(0x0040, 0x18);
4743 mf2_state = read_byte(0x0040, 0x96);
4744 asciicode = 0;
4746 switch (scancode) {
4747 case 0x3a: /* Caps Lock press */
4748 shift_flags ^= 0x40;
4749 write_byte(0x0040, 0x17, shift_flags);
4750 mf2_flags |= 0x40;
4751 write_byte(0x0040, 0x18, mf2_flags);
4752 break;
4753 case 0xba: /* Caps Lock release */
4754 mf2_flags &= ~0x40;
4755 write_byte(0x0040, 0x18, mf2_flags);
4756 break;
4758 case 0x2a: /* L Shift press */
4759 shift_flags |= 0x02;
4760 write_byte(0x0040, 0x17, shift_flags);
4761 break;
4762 case 0xaa: /* L Shift release */
4763 shift_flags &= ~0x02;
4764 write_byte(0x0040, 0x17, shift_flags);
4765 break;
4767 case 0x36: /* R Shift press */
4768 shift_flags |= 0x01;
4769 write_byte(0x0040, 0x17, shift_flags);
4770 break;
4771 case 0xb6: /* R Shift release */
4772 shift_flags &= ~0x01;
4773 write_byte(0x0040, 0x17, shift_flags);
4774 break;
4776 case 0x1d: /* Ctrl press */
4777 if ((mf2_state & 0x01) == 0) {
4778 shift_flags |= 0x04;
4779 write_byte(0x0040, 0x17, shift_flags);
4780 if (mf2_state & 0x02) {
4781 mf2_state |= 0x04;
4782 write_byte(0x0040, 0x96, mf2_state);
4783 } else {
4784 mf2_flags |= 0x01;
4785 write_byte(0x0040, 0x18, mf2_flags);
4788 break;
4789 case 0x9d: /* Ctrl release */
4790 if ((mf2_state & 0x01) == 0) {
4791 shift_flags &= ~0x04;
4792 write_byte(0x0040, 0x17, shift_flags);
4793 if (mf2_state & 0x02) {
4794 mf2_state &= ~0x04;
4795 write_byte(0x0040, 0x96, mf2_state);
4796 } else {
4797 mf2_flags &= ~0x01;
4798 write_byte(0x0040, 0x18, mf2_flags);
4801 break;
4803 case 0x38: /* Alt press */
4804 shift_flags |= 0x08;
4805 write_byte(0x0040, 0x17, shift_flags);
4806 if (mf2_state & 0x02) {
4807 mf2_state |= 0x08;
4808 write_byte(0x0040, 0x96, mf2_state);
4809 } else {
4810 mf2_flags |= 0x02;
4811 write_byte(0x0040, 0x18, mf2_flags);
4813 break;
4814 case 0xb8: /* Alt release */
4815 shift_flags &= ~0x08;
4816 write_byte(0x0040, 0x17, shift_flags);
4817 if (mf2_state & 0x02) {
4818 mf2_state &= ~0x08;
4819 write_byte(0x0040, 0x96, mf2_state);
4820 } else {
4821 mf2_flags &= ~0x02;
4822 write_byte(0x0040, 0x18, mf2_flags);
4824 break;
4826 case 0x45: /* Num Lock press */
4827 if ((mf2_state & 0x03) == 0) {
4828 mf2_flags |= 0x20;
4829 write_byte(0x0040, 0x18, mf2_flags);
4830 shift_flags ^= 0x20;
4831 write_byte(0x0040, 0x17, shift_flags);
4833 break;
4834 case 0xc5: /* Num Lock release */
4835 if ((mf2_state & 0x03) == 0) {
4836 mf2_flags &= ~0x20;
4837 write_byte(0x0040, 0x18, mf2_flags);
4839 break;
4841 case 0x46: /* Scroll Lock press */
4842 mf2_flags |= 0x10;
4843 write_byte(0x0040, 0x18, mf2_flags);
4844 shift_flags ^= 0x10;
4845 write_byte(0x0040, 0x17, shift_flags);
4846 break;
4848 case 0xc6: /* Scroll Lock release */
4849 mf2_flags &= ~0x10;
4850 write_byte(0x0040, 0x18, mf2_flags);
4851 break;
4853 default:
4854 if (scancode & 0x80) {
4855 break; /* toss key releases ... */
4857 if (scancode > MAX_SCAN_CODE) {
4858 BX_INFO("KBD: int09h_handler(): unknown scancode read: 0x%02x!\n", scancode);
4859 return;
4861 if (shift_flags & 0x08) { /* ALT */
4862 asciicode = scan_to_scanascii[scancode].alt;
4863 scancode = scan_to_scanascii[scancode].alt >> 8;
4864 } else if (shift_flags & 0x04) { /* CONTROL */
4865 asciicode = scan_to_scanascii[scancode].control;
4866 scancode = scan_to_scanascii[scancode].control >> 8;
4867 } else if (((mf2_state & 0x02) > 0) && ((scancode >= 0x47) && (scancode <= 0x53))) {
4868 /* extended keys handling */
4869 asciicode = 0xe0;
4870 scancode = scan_to_scanascii[scancode].normal >> 8;
4871 } else if (shift_flags & 0x03) { /* LSHIFT + RSHIFT */
4872 /* check if lock state should be ignored
4873 * because a SHIFT key are pressed */
4875 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
4876 asciicode = scan_to_scanascii[scancode].normal;
4877 scancode = scan_to_scanascii[scancode].normal >> 8;
4878 } else {
4879 asciicode = scan_to_scanascii[scancode].shift;
4880 scancode = scan_to_scanascii[scancode].shift >> 8;
4882 } else {
4883 /* check if lock is on */
4884 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
4885 asciicode = scan_to_scanascii[scancode].shift;
4886 scancode = scan_to_scanascii[scancode].shift >> 8;
4887 } else {
4888 asciicode = scan_to_scanascii[scancode].normal;
4889 scancode = scan_to_scanascii[scancode].normal >> 8;
4892 if (scancode==0 && asciicode==0) {
4893 BX_INFO("KBD: int09h_handler(): scancode & asciicode are zero?\n");
4895 enqueue_key(scancode, asciicode);
4896 break;
4898 if ((scancode & 0x7f) != 0x1d) {
4899 mf2_state &= ~0x01;
4901 mf2_state &= ~0x02;
4902 write_byte(0x0040, 0x96, mf2_state);
4905 unsigned int
4906 enqueue_key(scan_code, ascii_code)
4907 Bit8u scan_code, ascii_code;
4909 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail, temp_tail;
4911 #if BX_CPU < 2
4912 buffer_start = 0x001E;
4913 buffer_end = 0x003E;
4914 #else
4915 buffer_start = read_word(0x0040, 0x0080);
4916 buffer_end = read_word(0x0040, 0x0082);
4917 #endif
4919 buffer_head = read_word(0x0040, 0x001A);
4920 buffer_tail = read_word(0x0040, 0x001C);
4922 temp_tail = buffer_tail;
4923 buffer_tail += 2;
4924 if (buffer_tail >= buffer_end)
4925 buffer_tail = buffer_start;
4927 if (buffer_tail == buffer_head) {
4928 return(0);
4931 write_byte(0x0040, temp_tail, ascii_code);
4932 write_byte(0x0040, temp_tail+1, scan_code);
4933 write_word(0x0040, 0x001C, buffer_tail);
4934 return(1);
4938 void
4939 int74_function(make_farcall, Z, Y, X, status)
4940 Bit16u make_farcall, Z, Y, X, status;
4942 Bit16u ebda_seg=read_word(0x0040,0x000E);
4943 Bit8u in_byte, index, package_count;
4944 Bit8u mouse_flags_1, mouse_flags_2;
4946 BX_DEBUG_INT74("entering int74_function\n");
4947 make_farcall = 0;
4949 in_byte = inb(0x64);
4950 if ( (in_byte & 0x21) != 0x21 ) {
4951 return;
4953 in_byte = inb(0x60);
4954 BX_DEBUG_INT74("int74: read byte %02x\n", in_byte);
4956 mouse_flags_1 = read_byte(ebda_seg, 0x0026);
4957 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4959 if ( (mouse_flags_2 & 0x80) != 0x80 ) {
4960 return;
4963 package_count = mouse_flags_2 & 0x07;
4964 index = mouse_flags_1 & 0x07;
4965 write_byte(ebda_seg, 0x28 + index, in_byte);
4967 if ( (index+1) >= package_count ) {
4968 BX_DEBUG_INT74("int74_function: make_farcall=1\n");
4969 status = read_byte(ebda_seg, 0x0028 + 0);
4970 X = read_byte(ebda_seg, 0x0028 + 1);
4971 Y = read_byte(ebda_seg, 0x0028 + 2);
4972 Z = 0;
4973 mouse_flags_1 = 0;
4974 // check if far call handler installed
4975 if (mouse_flags_2 & 0x80)
4976 make_farcall = 1;
4978 else {
4979 mouse_flags_1++;
4981 write_byte(ebda_seg, 0x0026, mouse_flags_1);
4984 #define SET_DISK_RET_STATUS(status) write_byte(0x0040, 0x0074, status)
4986 #if BX_USE_ATADRV
4988 void
4989 int13_harddisk(EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
4990 Bit16u EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
4992 Bit32u lba;
4993 Bit16u ebda_seg=read_word(0x0040,0x000E);
4994 Bit16u cylinder, head, sector;
4995 Bit16u segment, offset;
4996 Bit16u npc, nph, npspt, nlc, nlh, nlspt;
4997 Bit16u size, count;
4998 Bit8u device, status;
5000 BX_DEBUG_INT13_HD("int13_harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5002 write_byte(0x0040, 0x008e, 0); // clear completion flag
5004 // basic check : device has to be defined
5005 if ( (GET_ELDL() < 0x80) || (GET_ELDL() >= 0x80 + BX_MAX_ATA_DEVICES) ) {
5006 BX_INFO("int13_harddisk: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
5007 goto int13_fail;
5010 // Get the ata channel
5011 device=read_byte(ebda_seg,&EbdaData->ata.hdidmap[GET_ELDL()-0x80]);
5013 // basic check : device has to be valid
5014 if (device >= BX_MAX_ATA_DEVICES) {
5015 BX_INFO("int13_harddisk: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
5016 goto int13_fail;
5019 switch (GET_AH()) {
5021 case 0x00: /* disk controller reset */
5022 ata_reset (device);
5023 goto int13_success;
5024 break;
5026 case 0x01: /* read disk status */
5027 status = read_byte(0x0040, 0x0074);
5028 SET_AH(status);
5029 SET_DISK_RET_STATUS(0);
5030 /* set CF if error status read */
5031 if (status) goto int13_fail_nostatus;
5032 else goto int13_success_noah;
5033 break;
5035 case 0x02: // read disk sectors
5036 case 0x03: // write disk sectors
5037 case 0x04: // verify disk sectors
5039 count = GET_AL();
5040 cylinder = GET_CH();
5041 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
5042 sector = (GET_CL() & 0x3f);
5043 head = GET_DH();
5045 segment = ES;
5046 offset = BX;
5048 if ((count > 128) || (count == 0) || (sector == 0)) {
5049 BX_INFO("int13_harddisk: function %02x, parameter out of range!\n",GET_AH());
5050 goto int13_fail;
5053 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
5054 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
5055 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
5057 // sanity check on cyl heads, sec
5058 if( (cylinder >= nlc) || (head >= nlh) || (sector > nlspt )) {
5059 BX_INFO("int13_harddisk: function %02x, parameters out of range %04x/%04x/%04x!\n", GET_AH(), cylinder, head, sector);
5060 goto int13_fail;
5063 // FIXME verify
5064 if ( GET_AH() == 0x04 ) goto int13_success;
5066 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
5067 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
5069 // if needed, translate lchs to lba, and execute command
5070 if ( (nph != nlh) || (npspt != nlspt)) {
5071 lba = ((((Bit32u)cylinder * (Bit32u)nlh) + (Bit32u)head) * (Bit32u)nlspt) + (Bit32u)sector - 1;
5072 sector = 0; // this forces the command to be lba
5075 if ( GET_AH() == 0x02 )
5076 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, cylinder, head, sector, lba, segment, offset);
5077 else
5078 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, cylinder, head, sector, lba, segment, offset);
5080 // Set nb of sector transferred
5081 SET_AL(read_word(ebda_seg, &EbdaData->ata.trsfsectors));
5083 if (status != 0) {
5084 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
5085 SET_AH(0x0c);
5086 goto int13_fail_noah;
5089 goto int13_success;
5090 break;
5092 case 0x05: /* format disk track */
5093 BX_INFO("format disk track called\n");
5094 goto int13_success;
5095 return;
5096 break;
5098 case 0x08: /* read disk drive parameters */
5100 // Get logical geometry from table
5101 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
5102 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
5103 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
5104 count = read_byte(ebda_seg, &EbdaData->ata.hdcount);
5106 nlc = nlc - 2; /* 0 based , last sector not used */
5107 SET_AL(0);
5108 SET_CH(nlc & 0xff);
5109 SET_CL(((nlc >> 2) & 0xc0) | (nlspt & 0x3f));
5110 SET_DH(nlh - 1);
5111 SET_DL(count); /* FIXME returns 0, 1, or n hard drives */
5113 // FIXME should set ES & DI
5115 goto int13_success;
5116 break;
5118 case 0x10: /* check drive ready */
5119 // should look at 40:8E also???
5121 // Read the status from controller
5122 status = inb(read_word(ebda_seg, &EbdaData->ata.channels[device/2].iobase1) + ATA_CB_STAT);
5123 if ( (status & ( ATA_CB_STAT_BSY | ATA_CB_STAT_RDY )) == ATA_CB_STAT_RDY ) {
5124 goto int13_success;
5126 else {
5127 SET_AH(0xAA);
5128 goto int13_fail_noah;
5130 break;
5132 case 0x15: /* read disk drive size */
5134 // Get logical geometry from table
5135 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
5136 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
5137 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
5139 // Compute sector count seen by int13
5140 lba = (Bit32u)(nlc - 1) * (Bit32u)nlh * (Bit32u)nlspt;
5141 CX = lba >> 16;
5142 DX = lba & 0xffff;
5144 SET_AH(3); // hard disk accessible
5145 goto int13_success_noah;
5146 break;
5148 case 0x41: // IBM/MS installation check
5149 BX=0xaa55; // install check
5150 SET_AH(0x30); // EDD 3.0
5151 CX=0x0007; // ext disk access and edd, removable supported
5152 goto int13_success_noah;
5153 break;
5155 case 0x42: // IBM/MS extended read
5156 case 0x43: // IBM/MS extended write
5157 case 0x44: // IBM/MS verify
5158 case 0x47: // IBM/MS extended seek
5160 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
5161 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
5162 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
5164 // Can't use 64 bits lba
5165 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
5166 if (lba != 0L) {
5167 BX_PANIC("int13_harddisk: function %02x. Can't use 64bits lba\n",GET_AH());
5168 goto int13_fail;
5171 // Get 32 bits lba and check
5172 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5173 if (lba >= read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors) ) {
5174 BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH());
5175 goto int13_fail;
5178 // If verify or seek
5179 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
5180 goto int13_success;
5182 // Execute the command
5183 if ( GET_AH() == 0x42 )
5184 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, 0, 0, 0, lba, segment, offset);
5185 else
5186 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, 0, 0, 0, lba, segment, offset);
5188 count=read_word(ebda_seg, &EbdaData->ata.trsfsectors);
5189 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5191 if (status != 0) {
5192 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
5193 SET_AH(0x0c);
5194 goto int13_fail_noah;
5197 goto int13_success;
5198 break;
5200 case 0x45: // IBM/MS lock/unlock drive
5201 case 0x49: // IBM/MS extended media change
5202 goto int13_success; // Always success for HD
5203 break;
5205 case 0x46: // IBM/MS eject media
5206 SET_AH(0xb2); // Volume Not Removable
5207 goto int13_fail_noah; // Always fail for HD
5208 break;
5210 case 0x48: // IBM/MS get drive parameters
5211 size=read_word(DS,SI+(Bit16u)&Int13DPT->size);
5213 // Buffer is too small
5214 if(size < 0x1a)
5215 goto int13_fail;
5217 // EDD 1.x
5218 if(size >= 0x1a) {
5219 Bit16u blksize;
5221 npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
5222 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
5223 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
5224 lba = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors);
5225 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5227 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5228 if ((lba/npspt)/nph > 0x3fff)
5230 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x00); // geometry is invalid
5231 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0x3fff);
5233 else
5235 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x02); // geometry is valid
5236 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, (Bit32u)npc);
5238 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, (Bit32u)nph);
5239 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, (Bit32u)npspt);
5240 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, lba); // FIXME should be Bit64
5241 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0L);
5242 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5245 // EDD 2.x
5246 if(size >= 0x1e) {
5247 Bit8u channel, dev, irq, mode, checksum, i, translation;
5248 Bit16u iobase1, iobase2, options;
5250 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5252 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5253 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5255 // Fill in dpte
5256 channel = device / 2;
5257 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5258 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5259 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5260 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5261 translation = read_byte(ebda_seg, &EbdaData->ata.devices[device].translation);
5263 options = (translation==ATA_TRANSLATION_NONE?0:1)<<3; // chs translation
5264 options |= (1<<4); // lba translation
5265 options |= (mode==ATA_MODE_PIO32?1:0)<<7;
5266 options |= (translation==ATA_TRANSLATION_LBA?1:0)<<9;
5267 options |= (translation==ATA_TRANSLATION_RECHS?3:0)<<9;
5269 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5270 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2 + ATA_CB_DC);
5271 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5272 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5273 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5274 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5275 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5276 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5277 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5278 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5279 if (size >=0x42)
5280 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5281 else
5282 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x10);
5284 checksum=0;
5285 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, ((Bit8u*)(&EbdaData->ata.dpte)) + i);
5286 checksum = ~checksum;
5287 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5290 // EDD 3.x
5291 if(size >= 0x42) {
5292 Bit8u channel, iface, checksum, i;
5293 Bit16u iobase1;
5295 channel = device / 2;
5296 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5297 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5299 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5300 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5301 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5302 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5303 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5305 if (iface==ATA_IFACE_ISA) {
5306 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5307 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5308 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5309 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5311 else {
5312 // FIXME PCI
5314 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5315 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5316 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5317 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5319 if (iface==ATA_IFACE_ISA) {
5320 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5321 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5322 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5324 else {
5325 // FIXME PCI
5327 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5328 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5329 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5330 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5332 checksum=0;
5333 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5334 checksum = ~checksum;
5335 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5338 goto int13_success;
5339 break;
5341 case 0x4e: // // IBM/MS set hardware configuration
5342 // DMA, prefetch, PIO maximum not supported
5343 switch (GET_AL()) {
5344 case 0x01:
5345 case 0x03:
5346 case 0x04:
5347 case 0x06:
5348 goto int13_success;
5349 break;
5350 default :
5351 goto int13_fail;
5353 break;
5355 case 0x09: /* initialize drive parameters */
5356 case 0x0c: /* seek to specified cylinder */
5357 case 0x0d: /* alternate disk reset */
5358 case 0x11: /* recalibrate */
5359 case 0x14: /* controller internal diagnostic */
5360 BX_INFO("int13_harddisk: function %02xh unimplemented, returns success\n", GET_AH());
5361 goto int13_success;
5362 break;
5364 case 0x0a: /* read disk sectors with ECC */
5365 case 0x0b: /* write disk sectors with ECC */
5366 case 0x18: // set media type for format
5367 case 0x50: // IBM/MS send packet command
5368 default:
5369 BX_INFO("int13_harddisk: function %02xh unsupported, returns fail\n", GET_AH());
5370 goto int13_fail;
5371 break;
5374 int13_fail:
5375 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5376 int13_fail_noah:
5377 SET_DISK_RET_STATUS(GET_AH());
5378 int13_fail_nostatus:
5379 SET_CF(); // error occurred
5380 return;
5382 int13_success:
5383 SET_AH(0x00); // no error
5384 int13_success_noah:
5385 SET_DISK_RET_STATUS(0x00);
5386 CLEAR_CF(); // no error
5387 return;
5390 // ---------------------------------------------------------------------------
5391 // Start of int13 for cdrom
5392 // ---------------------------------------------------------------------------
5394 void
5395 int13_cdrom(EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
5396 Bit16u EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
5398 Bit16u ebda_seg=read_word(0x0040,0x000E);
5399 Bit8u device, status, locks;
5400 Bit8u atacmd[12];
5401 Bit32u lba;
5402 Bit16u count, segment, offset, i, size;
5404 BX_DEBUG_INT13_CD("int13_cdrom: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5406 SET_DISK_RET_STATUS(0x00);
5408 /* basic check : device should be 0xE0+ */
5409 if( (GET_ELDL() < 0xE0) || (GET_ELDL() >= 0xE0+BX_MAX_ATA_DEVICES) ) {
5410 BX_INFO("int13_cdrom: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
5411 goto int13_fail;
5414 // Get the ata channel
5415 device=read_byte(ebda_seg,&EbdaData->ata.cdidmap[GET_ELDL()-0xE0]);
5417 /* basic check : device has to be valid */
5418 if (device >= BX_MAX_ATA_DEVICES) {
5419 BX_INFO("int13_cdrom: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
5420 goto int13_fail;
5423 switch (GET_AH()) {
5425 // all those functions return SUCCESS
5426 case 0x00: /* disk controller reset */
5427 case 0x09: /* initialize drive parameters */
5428 case 0x0c: /* seek to specified cylinder */
5429 case 0x0d: /* alternate disk reset */
5430 case 0x10: /* check drive ready */
5431 case 0x11: /* recalibrate */
5432 case 0x14: /* controller internal diagnostic */
5433 case 0x16: /* detect disk change */
5434 goto int13_success;
5435 break;
5437 // all those functions return disk write-protected
5438 case 0x03: /* write disk sectors */
5439 case 0x05: /* format disk track */
5440 case 0x43: // IBM/MS extended write
5441 SET_AH(0x03);
5442 goto int13_fail_noah;
5443 break;
5445 case 0x01: /* read disk status */
5446 status = read_byte(0x0040, 0x0074);
5447 SET_AH(status);
5448 SET_DISK_RET_STATUS(0);
5450 /* set CF if error status read */
5451 if (status) goto int13_fail_nostatus;
5452 else goto int13_success_noah;
5453 break;
5455 case 0x15: /* read disk drive size */
5456 SET_AH(0x02);
5457 goto int13_fail_noah;
5458 break;
5460 case 0x41: // IBM/MS installation check
5461 BX=0xaa55; // install check
5462 SET_AH(0x30); // EDD 2.1
5463 CX=0x0007; // ext disk access, removable and edd
5464 goto int13_success_noah;
5465 break;
5467 case 0x42: // IBM/MS extended read
5468 case 0x44: // IBM/MS verify sectors
5469 case 0x47: // IBM/MS extended seek
5471 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
5472 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
5473 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
5475 // Can't use 64 bits lba
5476 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
5477 if (lba != 0L) {
5478 BX_PANIC("int13_cdrom: function %02x. Can't use 64bits lba\n",GET_AH());
5479 goto int13_fail;
5482 // Get 32 bits lba
5483 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5485 // If verify or seek
5486 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
5487 goto int13_success;
5489 memsetb(get_SS(),atacmd,0,12);
5490 atacmd[0]=0x28; // READ command
5491 atacmd[7]=(count & 0xff00) >> 8; // Sectors
5492 atacmd[8]=(count & 0x00ff); // Sectors
5493 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
5494 atacmd[3]=(lba & 0x00ff0000) >> 16;
5495 atacmd[4]=(lba & 0x0000ff00) >> 8;
5496 atacmd[5]=(lba & 0x000000ff);
5497 status = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, count*2048L, ATA_DATA_IN, segment,offset);
5499 count = (Bit16u)(read_dword(ebda_seg, &EbdaData->ata.trsfbytes) >> 11);
5500 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5502 if (status != 0) {
5503 BX_INFO("int13_cdrom: function %02x, status %02x !\n",GET_AH(),status);
5504 SET_AH(0x0c);
5505 goto int13_fail_noah;
5508 goto int13_success;
5509 break;
5511 case 0x45: // IBM/MS lock/unlock drive
5512 if (GET_AL() > 2) goto int13_fail;
5514 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5516 switch (GET_AL()) {
5517 case 0 : // lock
5518 if (locks == 0xff) {
5519 SET_AH(0xb4);
5520 SET_AL(1);
5521 goto int13_fail_noah;
5523 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, ++locks);
5524 SET_AL(1);
5525 break;
5526 case 1 : // unlock
5527 if (locks == 0x00) {
5528 SET_AH(0xb0);
5529 SET_AL(0);
5530 goto int13_fail_noah;
5532 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, --locks);
5533 SET_AL(locks==0?0:1);
5534 break;
5535 case 2 : // status
5536 SET_AL(locks==0?0:1);
5537 break;
5539 goto int13_success;
5540 break;
5542 case 0x46: // IBM/MS eject media
5543 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5545 if (locks != 0) {
5546 SET_AH(0xb1); // media locked
5547 goto int13_fail_noah;
5549 // FIXME should handle 0x31 no media in device
5550 // FIXME should handle 0xb5 valid request failed
5552 // Call removable media eject
5553 ASM_START
5554 push bp
5555 mov bp, sp
5557 mov ah, #0x52
5558 int #0x15
5559 mov _int13_cdrom.status + 2[bp], ah
5560 jnc int13_cdrom_rme_end
5561 mov _int13_cdrom.status, #1
5562 int13_cdrom_rme_end:
5563 pop bp
5564 ASM_END
5566 if (status != 0) {
5567 SET_AH(0xb1); // media locked
5568 goto int13_fail_noah;
5571 goto int13_success;
5572 break;
5574 case 0x48: // IBM/MS get drive parameters
5575 size = read_word(DS,SI+(Bit16u)&Int13Ext->size);
5577 // Buffer is too small
5578 if(size < 0x1a)
5579 goto int13_fail;
5581 // EDD 1.x
5582 if(size >= 0x1a) {
5583 Bit16u cylinders, heads, spt, blksize;
5585 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5587 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5588 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x74); // removable, media change, lockable, max values
5589 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0xffffffff);
5590 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, 0xffffffff);
5591 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, 0xffffffff);
5592 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, 0xffffffff); // FIXME should be Bit64
5593 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0xffffffff);
5594 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5597 // EDD 2.x
5598 if(size >= 0x1e) {
5599 Bit8u channel, dev, irq, mode, checksum, i;
5600 Bit16u iobase1, iobase2, options;
5602 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5604 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5605 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5607 // Fill in dpte
5608 channel = device / 2;
5609 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5610 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5611 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5612 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5614 // FIXME atapi device
5615 options = (1<<4); // lba translation
5616 options |= (1<<5); // removable device
5617 options |= (1<<6); // atapi device
5618 options |= (mode==ATA_MODE_PIO32?1:0<<7);
5620 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5621 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2 + ATA_CB_DC);
5622 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5623 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5624 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5625 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5626 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5627 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5628 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5629 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5630 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5632 checksum=0;
5633 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, ((Bit8u*)(&EbdaData->ata.dpte)) + i);
5634 checksum = ~checksum;
5635 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5638 // EDD 3.x
5639 if(size >= 0x42) {
5640 Bit8u channel, iface, checksum, i;
5641 Bit16u iobase1;
5643 channel = device / 2;
5644 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5645 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5647 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5648 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5649 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5650 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5651 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5653 if (iface==ATA_IFACE_ISA) {
5654 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5655 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5656 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5657 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5659 else {
5660 // FIXME PCI
5662 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5663 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5664 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5665 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5667 if (iface==ATA_IFACE_ISA) {
5668 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5669 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5670 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5672 else {
5673 // FIXME PCI
5675 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5676 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5677 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5678 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5680 checksum=0;
5681 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5682 checksum = ~checksum;
5683 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5686 goto int13_success;
5687 break;
5689 case 0x49: // IBM/MS extended media change
5690 // always send changed ??
5691 SET_AH(06);
5692 goto int13_fail_nostatus;
5693 break;
5695 case 0x4e: // // IBM/MS set hardware configuration
5696 // DMA, prefetch, PIO maximum not supported
5697 switch (GET_AL()) {
5698 case 0x01:
5699 case 0x03:
5700 case 0x04:
5701 case 0x06:
5702 goto int13_success;
5703 break;
5704 default :
5705 goto int13_fail;
5707 break;
5709 // all those functions return unimplemented
5710 case 0x02: /* read sectors */
5711 case 0x04: /* verify sectors */
5712 case 0x08: /* read disk drive parameters */
5713 case 0x0a: /* read disk sectors with ECC */
5714 case 0x0b: /* write disk sectors with ECC */
5715 case 0x18: /* set media type for format */
5716 case 0x50: // ? - send packet command
5717 default:
5718 BX_INFO("int13_cdrom: unsupported AH=%02x\n", GET_AH());
5719 goto int13_fail;
5720 break;
5723 int13_fail:
5724 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5725 int13_fail_noah:
5726 SET_DISK_RET_STATUS(GET_AH());
5727 int13_fail_nostatus:
5728 SET_CF(); // error occurred
5729 return;
5731 int13_success:
5732 SET_AH(0x00); // no error
5733 int13_success_noah:
5734 SET_DISK_RET_STATUS(0x00);
5735 CLEAR_CF(); // no error
5736 return;
5739 // ---------------------------------------------------------------------------
5740 // End of int13 for cdrom
5741 // ---------------------------------------------------------------------------
5743 #if BX_ELTORITO_BOOT
5744 // ---------------------------------------------------------------------------
5745 // Start of int13 for eltorito functions
5746 // ---------------------------------------------------------------------------
5748 void
5749 int13_eltorito(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
5750 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
5752 Bit16u ebda_seg=read_word(0x0040,0x000E);
5754 BX_DEBUG_INT13_ET("int13_eltorito: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5755 // BX_DEBUG_INT13_ET("int13_eltorito: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
5757 switch (GET_AH()) {
5759 // FIXME ElTorito Various. Should be implemented
5760 case 0x4a: // ElTorito - Initiate disk emu
5761 case 0x4c: // ElTorito - Initiate disk emu and boot
5762 case 0x4d: // ElTorito - Return Boot catalog
5763 BX_PANIC("Int13 eltorito call with AX=%04x. Please report\n",AX);
5764 goto int13_fail;
5765 break;
5767 case 0x4b: // ElTorito - Terminate disk emu
5768 // FIXME ElTorito Hardcoded
5769 write_byte(DS,SI+0x00,0x13);
5770 write_byte(DS,SI+0x01,read_byte(ebda_seg,&EbdaData->cdemu.media));
5771 write_byte(DS,SI+0x02,read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
5772 write_byte(DS,SI+0x03,read_byte(ebda_seg,&EbdaData->cdemu.controller_index));
5773 write_dword(DS,SI+0x04,read_dword(ebda_seg,&EbdaData->cdemu.ilba));
5774 write_word(DS,SI+0x08,read_word(ebda_seg,&EbdaData->cdemu.device_spec));
5775 write_word(DS,SI+0x0a,read_word(ebda_seg,&EbdaData->cdemu.buffer_segment));
5776 write_word(DS,SI+0x0c,read_word(ebda_seg,&EbdaData->cdemu.load_segment));
5777 write_word(DS,SI+0x0e,read_word(ebda_seg,&EbdaData->cdemu.sector_count));
5778 write_byte(DS,SI+0x10,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.cylinders));
5779 write_byte(DS,SI+0x11,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.spt));
5780 write_byte(DS,SI+0x12,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.heads));
5782 // If we have to terminate emulation
5783 if(GET_AL() == 0x00) {
5784 // FIXME ElTorito Various. Should be handled accordingly to spec
5785 write_byte(ebda_seg,&EbdaData->cdemu.active, 0x00); // bye bye
5788 goto int13_success;
5789 break;
5791 default:
5792 BX_INFO("int13_eltorito: unsupported AH=%02x\n", GET_AH());
5793 goto int13_fail;
5794 break;
5797 int13_fail:
5798 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5799 SET_DISK_RET_STATUS(GET_AH());
5800 SET_CF(); // error occurred
5801 return;
5803 int13_success:
5804 SET_AH(0x00); // no error
5805 SET_DISK_RET_STATUS(0x00);
5806 CLEAR_CF(); // no error
5807 return;
5810 // ---------------------------------------------------------------------------
5811 // End of int13 for eltorito functions
5812 // ---------------------------------------------------------------------------
5814 // ---------------------------------------------------------------------------
5815 // Start of int13 when emulating a device from the cd
5816 // ---------------------------------------------------------------------------
5818 void
5819 int13_cdemu(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
5820 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
5822 Bit16u ebda_seg=read_word(0x0040,0x000E);
5823 Bit8u device, status;
5824 Bit16u vheads, vspt, vcylinders;
5825 Bit16u head, sector, cylinder, nbsectors;
5826 Bit32u vlba, ilba, slba, elba;
5827 Bit16u before, segment, offset;
5828 Bit8u atacmd[12];
5830 BX_DEBUG_INT13_ET("int13_cdemu: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5832 /* at this point, we are emulating a floppy/harddisk */
5834 // Recompute the device number
5835 device = read_byte(ebda_seg,&EbdaData->cdemu.controller_index) * 2;
5836 device += read_byte(ebda_seg,&EbdaData->cdemu.device_spec);
5838 SET_DISK_RET_STATUS(0x00);
5840 /* basic checks : emulation should be active, dl should equal the emulated drive */
5841 if( (read_byte(ebda_seg,&EbdaData->cdemu.active) ==0 )
5842 || (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive ) != GET_DL())) {
5843 BX_INFO("int13_cdemu: function %02x, emulation not active for DL= %02x\n", GET_AH(), GET_DL());
5844 goto int13_fail;
5847 switch (GET_AH()) {
5849 // all those functions return SUCCESS
5850 case 0x00: /* disk controller reset */
5851 case 0x09: /* initialize drive parameters */
5852 case 0x0c: /* seek to specified cylinder */
5853 case 0x0d: /* alternate disk reset */ // FIXME ElTorito Various. should really reset ?
5854 case 0x10: /* check drive ready */ // FIXME ElTorito Various. should check if ready ?
5855 case 0x11: /* recalibrate */
5856 case 0x14: /* controller internal diagnostic */
5857 case 0x16: /* detect disk change */
5858 goto int13_success;
5859 break;
5861 // all those functions return disk write-protected
5862 case 0x03: /* write disk sectors */
5863 case 0x05: /* format disk track */
5864 SET_AH(0x03);
5865 goto int13_fail_noah;
5866 break;
5868 case 0x01: /* read disk status */
5869 status=read_byte(0x0040, 0x0074);
5870 SET_AH(status);
5871 SET_DISK_RET_STATUS(0);
5873 /* set CF if error status read */
5874 if (status) goto int13_fail_nostatus;
5875 else goto int13_success_noah;
5876 break;
5878 case 0x02: // read disk sectors
5879 case 0x04: // verify disk sectors
5880 vspt = read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
5881 vcylinders = read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders);
5882 vheads = read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads);
5884 ilba = read_dword(ebda_seg,&EbdaData->cdemu.ilba);
5886 sector = GET_CL() & 0x003f;
5887 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
5888 head = GET_DH();
5889 nbsectors = GET_AL();
5890 segment = ES;
5891 offset = BX;
5893 // no sector to read ?
5894 if(nbsectors==0) goto int13_success;
5896 // sanity checks sco openserver needs this!
5897 if ((sector > vspt)
5898 || (cylinder >= vcylinders)
5899 || (head >= vheads)) {
5900 goto int13_fail;
5903 // After controls, verify do nothing
5904 if (GET_AH() == 0x04) goto int13_success;
5906 segment = ES+(BX / 16);
5907 offset = BX % 16;
5909 // calculate the virtual lba inside the image
5910 vlba=((((Bit32u)cylinder*(Bit32u)vheads)+(Bit32u)head)*(Bit32u)vspt)+((Bit32u)(sector-1));
5912 // In advance so we don't loose the count
5913 SET_AL(nbsectors);
5915 // start lba on cd
5916 slba = (Bit32u)vlba/4;
5917 before= (Bit16u)vlba%4;
5919 // end lba on cd
5920 elba = (Bit32u)(vlba+nbsectors-1)/4;
5922 memsetb(get_SS(),atacmd,0,12);
5923 atacmd[0]=0x28; // READ command
5924 atacmd[7]=((Bit16u)(elba-slba+1) & 0xff00) >> 8; // Sectors
5925 atacmd[8]=((Bit16u)(elba-slba+1) & 0x00ff); // Sectors
5926 atacmd[2]=(ilba+slba & 0xff000000) >> 24; // LBA
5927 atacmd[3]=(ilba+slba & 0x00ff0000) >> 16;
5928 atacmd[4]=(ilba+slba & 0x0000ff00) >> 8;
5929 atacmd[5]=(ilba+slba & 0x000000ff);
5930 if((status = ata_cmd_packet(device, 12, get_SS(), atacmd, before*512, nbsectors*512L, ATA_DATA_IN, segment,offset)) != 0) {
5931 BX_INFO("int13_cdemu: function %02x, error %02x !\n",GET_AH(),status);
5932 SET_AH(0x02);
5933 SET_AL(0);
5934 goto int13_fail_noah;
5937 goto int13_success;
5938 break;
5940 case 0x08: /* read disk drive parameters */
5941 vspt=read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
5942 vcylinders=read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders) - 1;
5943 vheads=read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads) - 1;
5945 SET_AL( 0x00 );
5946 SET_BL( 0x00 );
5947 SET_CH( vcylinders & 0xff );
5948 SET_CL((( vcylinders >> 2) & 0xc0) | ( vspt & 0x3f ));
5949 SET_DH( vheads );
5950 SET_DL( 0x02 ); // FIXME ElTorito Various. should send the real count of drives 1 or 2
5951 // FIXME ElTorito Harddisk. should send the HD count
5953 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
5954 case 0x01: SET_BL( 0x02 ); break;
5955 case 0x02: SET_BL( 0x04 ); break;
5956 case 0x03: SET_BL( 0x06 ); break;
5959 ASM_START
5960 push bp
5961 mov bp, sp
5962 mov ax, #diskette_param_table2
5963 mov _int13_cdemu.DI+2[bp], ax
5964 mov _int13_cdemu.ES+2[bp], cs
5965 pop bp
5966 ASM_END
5967 goto int13_success;
5968 break;
5970 case 0x15: /* read disk drive size */
5971 // FIXME ElTorito Harddisk. What geometry to send ?
5972 SET_AH(0x03);
5973 goto int13_success_noah;
5974 break;
5976 // all those functions return unimplemented
5977 case 0x0a: /* read disk sectors with ECC */
5978 case 0x0b: /* write disk sectors with ECC */
5979 case 0x18: /* set media type for format */
5980 case 0x41: // IBM/MS installation check
5981 // FIXME ElTorito Harddisk. Darwin would like to use EDD
5982 case 0x42: // IBM/MS extended read
5983 case 0x43: // IBM/MS extended write
5984 case 0x44: // IBM/MS verify sectors
5985 case 0x45: // IBM/MS lock/unlock drive
5986 case 0x46: // IBM/MS eject media
5987 case 0x47: // IBM/MS extended seek
5988 case 0x48: // IBM/MS get drive parameters
5989 case 0x49: // IBM/MS extended media change
5990 case 0x4e: // ? - set hardware configuration
5991 case 0x50: // ? - send packet command
5992 default:
5993 BX_INFO("int13_cdemu function AH=%02x unsupported, returns fail\n", GET_AH());
5994 goto int13_fail;
5995 break;
5998 int13_fail:
5999 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
6000 int13_fail_noah:
6001 SET_DISK_RET_STATUS(GET_AH());
6002 int13_fail_nostatus:
6003 SET_CF(); // error occurred
6004 return;
6006 int13_success:
6007 SET_AH(0x00); // no error
6008 int13_success_noah:
6009 SET_DISK_RET_STATUS(0x00);
6010 CLEAR_CF(); // no error
6011 return;
6014 // ---------------------------------------------------------------------------
6015 // End of int13 when emulating a device from the cd
6016 // ---------------------------------------------------------------------------
6018 #endif // BX_ELTORITO_BOOT
6020 #else //BX_USE_ATADRV
6022 void
6023 outLBA(cylinder,hd_heads,head,hd_sectors,sector,dl)
6024 Bit16u cylinder;
6025 Bit16u hd_heads;
6026 Bit16u head;
6027 Bit16u hd_sectors;
6028 Bit16u sector;
6029 Bit16u dl;
6031 ASM_START
6032 push bp
6033 mov bp, sp
6034 push eax
6035 push ebx
6036 push edx
6037 xor eax,eax
6038 mov ax,4[bp] // cylinder
6039 xor ebx,ebx
6040 mov bl,6[bp] // hd_heads
6041 imul ebx
6043 mov bl,8[bp] // head
6044 add eax,ebx
6045 mov bl,10[bp] // hd_sectors
6046 imul ebx
6047 mov bl,12[bp] // sector
6048 add eax,ebx
6050 dec eax
6051 mov dx,#0x1f3
6052 out dx,al
6053 mov dx,#0x1f4
6054 mov al,ah
6055 out dx,al
6056 shr eax,#16
6057 mov dx,#0x1f5
6058 out dx,al
6059 and ah,#0xf
6060 mov bl,14[bp] // dl
6061 and bl,#1
6062 shl bl,#4
6063 or ah,bl
6064 or ah,#0xe0
6065 mov al,ah
6066 mov dx,#0x01f6
6067 out dx,al
6068 pop edx
6069 pop ebx
6070 pop eax
6071 pop bp
6072 ASM_END
6075 void
6076 int13_harddisk(EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
6077 Bit16u EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
6079 Bit8u drive, num_sectors, sector, head, status, mod;
6080 Bit8u drive_map;
6081 Bit8u n_drives;
6082 Bit16u cyl_mod, ax;
6083 Bit16u max_cylinder, cylinder, total_sectors;
6084 Bit16u hd_cylinders;
6085 Bit8u hd_heads, hd_sectors;
6086 Bit16u val16;
6087 Bit8u sector_count;
6088 unsigned int i;
6089 Bit16u tempbx;
6090 Bit16u dpsize;
6092 Bit16u count, segment, offset;
6093 Bit32u lba;
6094 Bit16u error;
6096 BX_DEBUG_INT13_HD("int13 harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
6098 write_byte(0x0040, 0x008e, 0); // clear completion flag
6100 /* at this point, DL is >= 0x80 to be passed from the floppy int13h
6101 handler code */
6102 /* check how many disks first (cmos reg 0x12), return an error if
6103 drive not present */
6104 drive_map = inb_cmos(0x12);
6105 drive_map = (((drive_map & 0xf0)==0) ? 0 : 1) |
6106 (((drive_map & 0x0f)==0) ? 0 : 2);
6107 n_drives = (drive_map==0) ? 0 :
6108 ((drive_map==3) ? 2 : 1);
6110 if (!(drive_map & (1<<(GET_ELDL()&0x7f)))) { /* allow 0, 1, or 2 disks */
6111 SET_AH(0x01);
6112 SET_DISK_RET_STATUS(0x01);
6113 SET_CF(); /* error occurred */
6114 return;
6117 switch (GET_AH()) {
6119 case 0x00: /* disk controller reset */
6120 BX_DEBUG_INT13_HD("int13_f00\n");
6122 SET_AH(0);
6123 SET_DISK_RET_STATUS(0);
6124 set_diskette_ret_status(0);
6125 set_diskette_current_cyl(0, 0); /* current cylinder, diskette 1 */
6126 set_diskette_current_cyl(1, 0); /* current cylinder, diskette 2 */
6127 CLEAR_CF(); /* successful */
6128 return;
6129 break;
6131 case 0x01: /* read disk status */
6132 BX_DEBUG_INT13_HD("int13_f01\n");
6133 status = read_byte(0x0040, 0x0074);
6134 SET_AH(status);
6135 SET_DISK_RET_STATUS(0);
6136 /* set CF if error status read */
6137 if (status) SET_CF();
6138 else CLEAR_CF();
6139 return;
6140 break;
6142 case 0x04: // verify disk sectors
6143 case 0x02: // read disk sectors
6144 drive = GET_ELDL();
6145 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6147 num_sectors = GET_AL();
6148 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
6149 sector = (GET_CL() & 0x3f);
6150 head = GET_DH();
6153 if (hd_cylinders > 1024) {
6154 if (hd_cylinders <= 2048) {
6155 cylinder <<= 1;
6157 else if (hd_cylinders <= 4096) {
6158 cylinder <<= 2;
6160 else if (hd_cylinders <= 8192) {
6161 cylinder <<= 3;
6163 else { // hd_cylinders <= 16384
6164 cylinder <<= 4;
6167 ax = head / hd_heads;
6168 cyl_mod = ax & 0xff;
6169 head = ax >> 8;
6170 cylinder |= cyl_mod;
6173 if ( (cylinder >= hd_cylinders) ||
6174 (sector > hd_sectors) ||
6175 (head >= hd_heads) ) {
6176 SET_AH(1);
6177 SET_DISK_RET_STATUS(1);
6178 SET_CF(); /* error occurred */
6179 return;
6182 if ( (num_sectors > 128) || (num_sectors == 0) )
6183 BX_PANIC("int13_harddisk: num_sectors out of range!\n");
6185 if (head > 15)
6186 BX_PANIC("hard drive BIOS:(read/verify) head > 15\n");
6188 if ( GET_AH() == 0x04 ) {
6189 SET_AH(0);
6190 SET_DISK_RET_STATUS(0);
6191 CLEAR_CF();
6192 return;
6195 status = inb(0x1f7);
6196 if (status & 0x80) {
6197 BX_PANIC("hard drive BIOS:(read/verify) BUSY bit set\n");
6199 outb(0x01f2, num_sectors);
6200 /* activate LBA? (tomv) */
6201 if (hd_heads > 16) {
6202 BX_DEBUG_INT13_HD("CHS: %x %x %x\n", cylinder, head, sector);
6203 outLBA(cylinder,hd_heads,head,hd_sectors,sector,drive);
6205 else {
6206 outb(0x01f3, sector);
6207 outb(0x01f4, cylinder & 0x00ff);
6208 outb(0x01f5, cylinder >> 8);
6209 outb(0x01f6, 0xa0 | ((drive & 0x01)<<4) | (head & 0x0f));
6211 outb(0x01f7, 0x20);
6213 while (1) {
6214 status = inb(0x1f7);
6215 if ( !(status & 0x80) ) break;
6218 if (status & 0x01) {
6219 BX_PANIC("hard drive BIOS:(read/verify) read error\n");
6220 } else if ( !(status & 0x08) ) {
6221 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
6222 BX_PANIC("hard drive BIOS:(read/verify) expected DRQ=1\n");
6225 sector_count = 0;
6226 tempbx = BX;
6228 ASM_START
6229 sti ;; enable higher priority interrupts
6230 ASM_END
6232 while (1) {
6233 ASM_START
6234 ;; store temp bx in real DI register
6235 push bp
6236 mov bp, sp
6237 mov di, _int13_harddisk.tempbx + 2 [bp]
6238 pop bp
6240 ;; adjust if there will be an overrun
6241 cmp di, #0xfe00
6242 jbe i13_f02_no_adjust
6243 i13_f02_adjust:
6244 sub di, #0x0200 ; sub 512 bytes from offset
6245 mov ax, es
6246 add ax, #0x0020 ; add 512 to segment
6247 mov es, ax
6249 i13_f02_no_adjust:
6250 mov cx, #0x0100 ;; counter (256 words = 512b)
6251 mov dx, #0x01f0 ;; AT data read port
6254 insw ;; CX words transfered from port(DX) to ES:[DI]
6256 i13_f02_done:
6257 ;; store real DI register back to temp bx
6258 push bp
6259 mov bp, sp
6260 mov _int13_harddisk.tempbx + 2 [bp], di
6261 pop bp
6262 ASM_END
6264 sector_count++;
6265 num_sectors--;
6266 if (num_sectors == 0) {
6267 status = inb(0x1f7);
6268 if ( (status & 0xc9) != 0x40 )
6269 BX_PANIC("no sectors left to read/verify, status is %02x\n", (unsigned) status);
6270 break;
6272 else {
6273 status = inb(0x1f7);
6274 if ( (status & 0xc9) != 0x48 )
6275 BX_PANIC("more sectors left to read/verify, status is %02x\n", (unsigned) status);
6276 continue;
6280 SET_AH(0);
6281 SET_DISK_RET_STATUS(0);
6282 SET_AL(sector_count);
6283 CLEAR_CF(); /* successful */
6284 return;
6285 break;
6288 case 0x03: /* write disk sectors */
6289 BX_DEBUG_INT13_HD("int13_f03\n");
6290 drive = GET_ELDL ();
6291 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6293 num_sectors = GET_AL();
6294 cylinder = GET_CH();
6295 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
6296 sector = (GET_CL() & 0x3f);
6297 head = GET_DH();
6299 if (hd_cylinders > 1024) {
6300 if (hd_cylinders <= 2048) {
6301 cylinder <<= 1;
6303 else if (hd_cylinders <= 4096) {
6304 cylinder <<= 2;
6306 else if (hd_cylinders <= 8192) {
6307 cylinder <<= 3;
6309 else { // hd_cylinders <= 16384
6310 cylinder <<= 4;
6313 ax = head / hd_heads;
6314 cyl_mod = ax & 0xff;
6315 head = ax >> 8;
6316 cylinder |= cyl_mod;
6319 if ( (cylinder >= hd_cylinders) ||
6320 (sector > hd_sectors) ||
6321 (head >= hd_heads) ) {
6322 SET_AH( 1);
6323 SET_DISK_RET_STATUS(1);
6324 SET_CF(); /* error occurred */
6325 return;
6328 if ( (num_sectors > 128) || (num_sectors == 0) )
6329 BX_PANIC("int13_harddisk: num_sectors out of range!\n");
6331 if (head > 15)
6332 BX_PANIC("hard drive BIOS:(read) head > 15\n");
6334 status = inb(0x1f7);
6335 if (status & 0x80) {
6336 BX_PANIC("hard drive BIOS:(read) BUSY bit set\n");
6338 // should check for Drive Ready Bit also in status reg
6339 outb(0x01f2, num_sectors);
6341 /* activate LBA? (tomv) */
6342 if (hd_heads > 16) {
6343 BX_DEBUG_INT13_HD("CHS (write): %x %x %x\n", cylinder, head, sector);
6344 outLBA(cylinder,hd_heads,head,hd_sectors,sector,GET_ELDL());
6346 else {
6347 outb(0x01f3, sector);
6348 outb(0x01f4, cylinder & 0x00ff);
6349 outb(0x01f5, cylinder >> 8);
6350 outb(0x01f6, 0xa0 | ((GET_ELDL() & 0x01)<<4) | (head & 0x0f));
6352 outb(0x01f7, 0x30);
6354 // wait for busy bit to turn off after seeking
6355 while (1) {
6356 status = inb(0x1f7);
6357 if ( !(status & 0x80) ) break;
6360 if ( !(status & 0x08) ) {
6361 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
6362 BX_PANIC("hard drive BIOS:(write) data-request bit not set\n");
6365 sector_count = 0;
6366 tempbx = BX;
6368 ASM_START
6369 sti ;; enable higher priority interrupts
6370 ASM_END
6372 while (1) {
6373 ASM_START
6374 ;; store temp bx in real SI register
6375 push bp
6376 mov bp, sp
6377 mov si, _int13_harddisk.tempbx + 2 [bp]
6378 pop bp
6380 ;; adjust if there will be an overrun
6381 cmp si, #0xfe00
6382 jbe i13_f03_no_adjust
6383 i13_f03_adjust:
6384 sub si, #0x0200 ; sub 512 bytes from offset
6385 mov ax, es
6386 add ax, #0x0020 ; add 512 to segment
6387 mov es, ax
6389 i13_f03_no_adjust:
6390 mov cx, #0x0100 ;; counter (256 words = 512b)
6391 mov dx, #0x01f0 ;; AT data read port
6393 seg ES
6395 outsw ;; CX words tranfered from ES:[SI] to port(DX)
6397 ;; store real SI register back to temp bx
6398 push bp
6399 mov bp, sp
6400 mov _int13_harddisk.tempbx + 2 [bp], si
6401 pop bp
6402 ASM_END
6404 sector_count++;
6405 num_sectors--;
6406 if (num_sectors == 0) {
6407 status = inb(0x1f7);
6408 if ( (status & 0xe9) != 0x40 )
6409 BX_PANIC("no sectors left to write, status is %02x\n", (unsigned) status);
6410 break;
6412 else {
6413 status = inb(0x1f7);
6414 if ( (status & 0xc9) != 0x48 )
6415 BX_PANIC("more sectors left to write, status is %02x\n", (unsigned) status);
6416 continue;
6420 SET_AH(0);
6421 SET_DISK_RET_STATUS(0);
6422 SET_AL(sector_count);
6423 CLEAR_CF(); /* successful */
6424 return;
6425 break;
6427 case 0x05: /* format disk track */
6428 BX_DEBUG_INT13_HD("int13_f05\n");
6429 BX_PANIC("format disk track called\n");
6430 /* nop */
6431 SET_AH(0);
6432 SET_DISK_RET_STATUS(0);
6433 CLEAR_CF(); /* successful */
6434 return;
6435 break;
6437 case 0x08: /* read disk drive parameters */
6438 BX_DEBUG_INT13_HD("int13_f08\n");
6440 drive = GET_ELDL ();
6441 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6443 // translate CHS
6445 if (hd_cylinders <= 1024) {
6446 // hd_cylinders >>= 0;
6447 // hd_heads <<= 0;
6449 else if (hd_cylinders <= 2048) {
6450 hd_cylinders >>= 1;
6451 hd_heads <<= 1;
6453 else if (hd_cylinders <= 4096) {
6454 hd_cylinders >>= 2;
6455 hd_heads <<= 2;
6457 else if (hd_cylinders <= 8192) {
6458 hd_cylinders >>= 3;
6459 hd_heads <<= 3;
6461 else { // hd_cylinders <= 16384
6462 hd_cylinders >>= 4;
6463 hd_heads <<= 4;
6466 max_cylinder = hd_cylinders - 2; /* 0 based */
6467 SET_AL(0);
6468 SET_CH(max_cylinder & 0xff);
6469 SET_CL(((max_cylinder >> 2) & 0xc0) | (hd_sectors & 0x3f));
6470 SET_DH(hd_heads - 1);
6471 SET_DL(n_drives); /* returns 0, 1, or 2 hard drives */
6472 SET_AH(0);
6473 SET_DISK_RET_STATUS(0);
6474 CLEAR_CF(); /* successful */
6476 return;
6477 break;
6479 case 0x09: /* initialize drive parameters */
6480 BX_DEBUG_INT13_HD("int13_f09\n");
6481 SET_AH(0);
6482 SET_DISK_RET_STATUS(0);
6483 CLEAR_CF(); /* successful */
6484 return;
6485 break;
6487 case 0x0a: /* read disk sectors with ECC */
6488 BX_DEBUG_INT13_HD("int13_f0a\n");
6489 case 0x0b: /* write disk sectors with ECC */
6490 BX_DEBUG_INT13_HD("int13_f0b\n");
6491 BX_PANIC("int13h Functions 0Ah & 0Bh not implemented!\n");
6492 return;
6493 break;
6495 case 0x0c: /* seek to specified cylinder */
6496 BX_DEBUG_INT13_HD("int13_f0c\n");
6497 BX_INFO("int13h function 0ch (seek) not implemented!\n");
6498 SET_AH(0);
6499 SET_DISK_RET_STATUS(0);
6500 CLEAR_CF(); /* successful */
6501 return;
6502 break;
6504 case 0x0d: /* alternate disk reset */
6505 BX_DEBUG_INT13_HD("int13_f0d\n");
6506 SET_AH(0);
6507 SET_DISK_RET_STATUS(0);
6508 CLEAR_CF(); /* successful */
6509 return;
6510 break;
6512 case 0x10: /* check drive ready */
6513 BX_DEBUG_INT13_HD("int13_f10\n");
6514 //SET_AH(0);
6515 //SET_DISK_RET_STATUS(0);
6516 //CLEAR_CF(); /* successful */
6517 //return;
6518 //break;
6520 // should look at 40:8E also???
6521 status = inb(0x01f7);
6522 if ( (status & 0xc0) == 0x40 ) {
6523 SET_AH(0);
6524 SET_DISK_RET_STATUS(0);
6525 CLEAR_CF(); // drive ready
6526 return;
6528 else {
6529 SET_AH(0xAA);
6530 SET_DISK_RET_STATUS(0xAA);
6531 SET_CF(); // not ready
6532 return;
6534 break;
6536 case 0x11: /* recalibrate */
6537 BX_DEBUG_INT13_HD("int13_f11\n");
6538 SET_AH(0);
6539 SET_DISK_RET_STATUS(0);
6540 CLEAR_CF(); /* successful */
6541 return;
6542 break;
6544 case 0x14: /* controller internal diagnostic */
6545 BX_DEBUG_INT13_HD("int13_f14\n");
6546 SET_AH(0);
6547 SET_DISK_RET_STATUS(0);
6548 CLEAR_CF(); /* successful */
6549 SET_AL(0);
6550 return;
6551 break;
6553 case 0x15: /* read disk drive size */
6554 drive = GET_ELDL();
6555 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6556 ASM_START
6557 push bp
6558 mov bp, sp
6559 mov al, _int13_harddisk.hd_heads + 2 [bp]
6560 mov ah, _int13_harddisk.hd_sectors + 2 [bp]
6561 mul al, ah ;; ax = heads * sectors
6562 mov bx, _int13_harddisk.hd_cylinders + 2 [bp]
6563 dec bx ;; use (cylinders - 1) ???
6564 mul ax, bx ;; dx:ax = (cylinders -1) * (heads * sectors)
6565 ;; now we need to move the 32bit result dx:ax to what the
6566 ;; BIOS wants which is cx:dx.
6567 ;; and then into CX:DX on the stack
6568 mov _int13_harddisk.CX + 2 [bp], dx
6569 mov _int13_harddisk.DX + 2 [bp], ax
6570 pop bp
6571 ASM_END
6572 SET_AH(3); // hard disk accessible
6573 SET_DISK_RET_STATUS(0); // ??? should this be 0
6574 CLEAR_CF(); // successful
6575 return;
6576 break;
6578 case 0x18: // set media type for format
6579 case 0x41: // IBM/MS
6580 case 0x42: // IBM/MS
6581 case 0x43: // IBM/MS
6582 case 0x44: // IBM/MS
6583 case 0x45: // IBM/MS lock/unlock drive
6584 case 0x46: // IBM/MS eject media
6585 case 0x47: // IBM/MS extended seek
6586 case 0x49: // IBM/MS extended media change
6587 case 0x50: // IBM/MS send packet command
6588 default:
6589 BX_INFO("int13_harddisk: unsupported AH=%02x\n", GET_AH());
6591 SET_AH(1); // code=invalid function in AH or invalid parameter
6592 SET_DISK_RET_STATUS(1);
6593 SET_CF(); /* unsuccessful */
6594 return;
6595 break;
6599 static char panic_msg_reg12h[] = "HD%d cmos reg 12h not type F\n";
6600 static char panic_msg_reg19h[] = "HD%d cmos reg %02xh not user definable type 47\n";
6602 void
6603 get_hd_geometry(drive, hd_cylinders, hd_heads, hd_sectors)
6604 Bit8u drive;
6605 Bit16u *hd_cylinders;
6606 Bit8u *hd_heads;
6607 Bit8u *hd_sectors;
6609 Bit8u hd_type;
6610 Bit16u ss;
6611 Bit16u cylinders;
6612 Bit8u iobase;
6614 ss = get_SS();
6615 if (drive == 0x80) {
6616 hd_type = inb_cmos(0x12) & 0xf0;
6617 if (hd_type != 0xf0)
6618 BX_INFO(panic_msg_reg12h,0);
6619 hd_type = inb_cmos(0x19); // HD0: extended type
6620 if (hd_type != 47)
6621 BX_INFO(panic_msg_reg19h,0,0x19);
6622 iobase = 0x1b;
6623 } else {
6624 hd_type = inb_cmos(0x12) & 0x0f;
6625 if (hd_type != 0x0f)
6626 BX_INFO(panic_msg_reg12h,1);
6627 hd_type = inb_cmos(0x1a); // HD0: extended type
6628 if (hd_type != 47)
6629 BX_INFO(panic_msg_reg19h,0,0x1a);
6630 iobase = 0x24;
6633 // cylinders
6634 cylinders = inb_cmos(iobase) | (inb_cmos(iobase+1) << 8);
6635 write_word(ss, hd_cylinders, cylinders);
6637 // heads
6638 write_byte(ss, hd_heads, inb_cmos(iobase+2));
6640 // sectors per track
6641 write_byte(ss, hd_sectors, inb_cmos(iobase+8));
6644 #endif //else BX_USE_ATADRV
6646 #if BX_SUPPORT_FLOPPY
6648 //////////////////////
6649 // FLOPPY functions //
6650 //////////////////////
6652 void floppy_reset_controller()
6654 Bit8u val8;
6656 // Reset controller
6657 val8 = inb(0x03f2);
6658 outb(0x03f2, val8 & ~0x04);
6659 outb(0x03f2, val8 | 0x04);
6661 // Wait for controller to come out of reset
6662 do {
6663 val8 = inb(0x3f4);
6664 } while ( (val8 & 0xc0) != 0x80 );
6667 void floppy_prepare_controller(drive)
6668 Bit16u drive;
6670 Bit8u val8, dor, prev_reset;
6672 // set 40:3e bit 7 to 0
6673 val8 = read_byte(0x0040, 0x003e);
6674 val8 &= 0x7f;
6675 write_byte(0x0040, 0x003e, val8);
6677 // turn on motor of selected drive, DMA & int enabled, normal operation
6678 prev_reset = inb(0x03f2) & 0x04;
6679 if (drive)
6680 dor = 0x20;
6681 else
6682 dor = 0x10;
6683 dor |= 0x0c;
6684 dor |= drive;
6685 outb(0x03f2, dor);
6687 // reset the disk motor timeout value of INT 08
6688 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
6690 // wait for drive readiness
6691 do {
6692 val8 = inb(0x3f4);
6693 } while ( (val8 & 0xc0) != 0x80 );
6695 if (prev_reset == 0) {
6696 // turn on interrupts
6697 ASM_START
6699 ASM_END
6700 // wait on 40:3e bit 7 to become 1
6701 do {
6702 val8 = read_byte(0x0040, 0x003e);
6703 } while ( (val8 & 0x80) == 0 );
6704 val8 &= 0x7f;
6705 ASM_START
6707 ASM_END
6708 write_byte(0x0040, 0x003e, val8);
6712 bx_bool
6713 floppy_media_known(drive)
6714 Bit16u drive;
6716 Bit8u val8;
6717 Bit16u media_state_offset;
6719 val8 = read_byte(0x0040, 0x003e); // diskette recal status
6720 if (drive)
6721 val8 >>= 1;
6722 val8 &= 0x01;
6723 if (val8 == 0)
6724 return(0);
6726 media_state_offset = 0x0090;
6727 if (drive)
6728 media_state_offset += 1;
6730 val8 = read_byte(0x0040, media_state_offset);
6731 val8 = (val8 >> 4) & 0x01;
6732 if (val8 == 0)
6733 return(0);
6735 // check pass, return KNOWN
6736 return(1);
6739 bx_bool
6740 floppy_media_sense(drive)
6741 Bit16u drive;
6743 bx_bool retval;
6744 Bit16u media_state_offset;
6745 Bit8u drive_type, config_data, media_state;
6747 if (floppy_drive_recal(drive) == 0) {
6748 return(0);
6751 // for now cheat and get drive type from CMOS,
6752 // assume media is same as drive type
6754 // ** config_data **
6755 // Bitfields for diskette media control:
6756 // Bit(s) Description (Table M0028)
6757 // 7-6 last data rate set by controller
6758 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6759 // 5-4 last diskette drive step rate selected
6760 // 00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
6761 // 3-2 {data rate at start of operation}
6762 // 1-0 reserved
6764 // ** media_state **
6765 // Bitfields for diskette drive media state:
6766 // Bit(s) Description (Table M0030)
6767 // 7-6 data rate
6768 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6769 // 5 double stepping required (e.g. 360kB in 1.2MB)
6770 // 4 media type established
6771 // 3 drive capable of supporting 4MB media
6772 // 2-0 on exit from BIOS, contains
6773 // 000 trying 360kB in 360kB
6774 // 001 trying 360kB in 1.2MB
6775 // 010 trying 1.2MB in 1.2MB
6776 // 011 360kB in 360kB established
6777 // 100 360kB in 1.2MB established
6778 // 101 1.2MB in 1.2MB established
6779 // 110 reserved
6780 // 111 all other formats/drives
6782 drive_type = inb_cmos(0x10);
6783 if (drive == 0)
6784 drive_type >>= 4;
6785 else
6786 drive_type &= 0x0f;
6787 if ( drive_type == 1 ) {
6788 // 360K 5.25" drive
6789 config_data = 0x00; // 0000 0000
6790 media_state = 0x25; // 0010 0101
6791 retval = 1;
6793 else if ( drive_type == 2 ) {
6794 // 1.2 MB 5.25" drive
6795 config_data = 0x00; // 0000 0000
6796 media_state = 0x25; // 0010 0101 // need double stepping??? (bit 5)
6797 retval = 1;
6799 else if ( drive_type == 3 ) {
6800 // 720K 3.5" drive
6801 config_data = 0x00; // 0000 0000 ???
6802 media_state = 0x17; // 0001 0111
6803 retval = 1;
6805 else if ( drive_type == 4 ) {
6806 // 1.44 MB 3.5" drive
6807 config_data = 0x00; // 0000 0000
6808 media_state = 0x17; // 0001 0111
6809 retval = 1;
6811 else if ( drive_type == 5 ) {
6812 // 2.88 MB 3.5" drive
6813 config_data = 0xCC; // 1100 1100
6814 media_state = 0xD7; // 1101 0111
6815 retval = 1;
6818 // Extended floppy size uses special cmos setting
6819 else if ( drive_type == 6 ) {
6820 // 160k 5.25" drive
6821 config_data = 0x00; // 0000 0000
6822 media_state = 0x27; // 0010 0111
6823 retval = 1;
6825 else if ( drive_type == 7 ) {
6826 // 180k 5.25" drive
6827 config_data = 0x00; // 0000 0000
6828 media_state = 0x27; // 0010 0111
6829 retval = 1;
6831 else if ( drive_type == 8 ) {
6832 // 320k 5.25" drive
6833 config_data = 0x00; // 0000 0000
6834 media_state = 0x27; // 0010 0111
6835 retval = 1;
6838 else {
6839 // not recognized
6840 config_data = 0x00; // 0000 0000
6841 media_state = 0x00; // 0000 0000
6842 retval = 0;
6845 if (drive == 0)
6846 media_state_offset = 0x90;
6847 else
6848 media_state_offset = 0x91;
6849 write_byte(0x0040, 0x008B, config_data);
6850 write_byte(0x0040, media_state_offset, media_state);
6852 return(retval);
6855 bx_bool
6856 floppy_drive_recal(drive)
6857 Bit16u drive;
6859 Bit8u val8;
6860 Bit16u curr_cyl_offset;
6862 floppy_prepare_controller(drive);
6864 // send Recalibrate command (2 bytes) to controller
6865 outb(0x03f5, 0x07); // 07: Recalibrate
6866 outb(0x03f5, drive); // 0=drive0, 1=drive1
6868 // turn on interrupts
6869 ASM_START
6871 ASM_END
6873 // wait on 40:3e bit 7 to become 1
6874 do {
6875 val8 = (read_byte(0x0040, 0x003e) & 0x80);
6876 } while ( val8 == 0 );
6878 val8 = 0; // separate asm from while() loop
6879 // turn off interrupts
6880 ASM_START
6882 ASM_END
6884 // set 40:3e bit 7 to 0, and calibrated bit
6885 val8 = read_byte(0x0040, 0x003e);
6886 val8 &= 0x7f;
6887 if (drive) {
6888 val8 |= 0x02; // Drive 1 calibrated
6889 curr_cyl_offset = 0x0095;
6890 } else {
6891 val8 |= 0x01; // Drive 0 calibrated
6892 curr_cyl_offset = 0x0094;
6894 write_byte(0x0040, 0x003e, val8);
6895 write_byte(0x0040, curr_cyl_offset, 0); // current cylinder is 0
6897 return(1);
6902 bx_bool
6903 floppy_drive_exists(drive)
6904 Bit16u drive;
6906 Bit8u drive_type;
6908 // check CMOS to see if drive exists
6909 drive_type = inb_cmos(0x10);
6910 if (drive == 0)
6911 drive_type >>= 4;
6912 else
6913 drive_type &= 0x0f;
6914 if ( drive_type == 0 )
6915 return(0);
6916 else
6917 return(1);
6920 void
6921 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
6922 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
6924 Bit8u drive, num_sectors, track, sector, head, status;
6925 Bit16u base_address, base_count, base_es;
6926 Bit8u page, mode_register, val8, dor;
6927 Bit8u return_status[7];
6928 Bit8u drive_type, num_floppies, ah;
6929 Bit16u es, last_addr;
6931 BX_DEBUG_INT13_FL("int13_diskette: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
6933 ah = GET_AH();
6935 switch ( ah ) {
6936 case 0x00: // diskette controller reset
6937 BX_DEBUG_INT13_FL("floppy f00\n");
6938 drive = GET_ELDL();
6939 if (drive > 1) {
6940 SET_AH(1); // invalid param
6941 set_diskette_ret_status(1);
6942 SET_CF();
6943 return;
6945 drive_type = inb_cmos(0x10);
6947 if (drive == 0)
6948 drive_type >>= 4;
6949 else
6950 drive_type &= 0x0f;
6951 if (drive_type == 0) {
6952 SET_AH(0x80); // drive not responding
6953 set_diskette_ret_status(0x80);
6954 SET_CF();
6955 return;
6957 SET_AH(0);
6958 set_diskette_ret_status(0);
6959 CLEAR_CF(); // successful
6960 set_diskette_current_cyl(drive, 0); // current cylinder
6961 return;
6963 case 0x01: // Read Diskette Status
6964 CLEAR_CF();
6965 val8 = read_byte(0x0000, 0x0441);
6966 SET_AH(val8);
6967 if (val8) {
6968 SET_CF();
6970 return;
6972 case 0x02: // Read Diskette Sectors
6973 case 0x03: // Write Diskette Sectors
6974 case 0x04: // Verify Diskette Sectors
6975 num_sectors = GET_AL();
6976 track = GET_CH();
6977 sector = GET_CL();
6978 head = GET_DH();
6979 drive = GET_ELDL();
6981 if ((drive > 1) || (head > 1) || (sector == 0) ||
6982 (num_sectors == 0) || (num_sectors > 72)) {
6983 BX_INFO("int13_diskette: read/write/verify: parameter out of range\n");
6984 SET_AH(1);
6985 set_diskette_ret_status(1);
6986 SET_AL(0); // no sectors read
6987 SET_CF(); // error occurred
6988 return;
6991 // see if drive exists
6992 if (floppy_drive_exists(drive) == 0) {
6993 SET_AH(0x80); // not responding
6994 set_diskette_ret_status(0x80);
6995 SET_AL(0); // no sectors read
6996 SET_CF(); // error occurred
6997 return;
7000 // see if media in drive, and type is known
7001 if (floppy_media_known(drive) == 0) {
7002 if (floppy_media_sense(drive) == 0) {
7003 SET_AH(0x0C); // Media type not found
7004 set_diskette_ret_status(0x0C);
7005 SET_AL(0); // no sectors read
7006 SET_CF(); // error occurred
7007 return;
7011 if (ah == 0x02) {
7012 // Read Diskette Sectors
7014 //-----------------------------------
7015 // set up DMA controller for transfer
7016 //-----------------------------------
7018 // es:bx = pointer to where to place information from diskette
7019 // port 04: DMA-1 base and current address, channel 2
7020 // port 05: DMA-1 base and current count, channel 2
7021 page = (ES >> 12); // upper 4 bits
7022 base_es = (ES << 4); // lower 16bits contributed by ES
7023 base_address = base_es + BX; // lower 16 bits of address
7024 // contributed by ES:BX
7025 if ( base_address < base_es ) {
7026 // in case of carry, adjust page by 1
7027 page++;
7029 base_count = (num_sectors * 512) - 1;
7031 // check for 64K boundary overrun
7032 last_addr = base_address + base_count;
7033 if (last_addr < base_address) {
7034 SET_AH(0x09);
7035 set_diskette_ret_status(0x09);
7036 SET_AL(0); // no sectors read
7037 SET_CF(); // error occurred
7038 return;
7041 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
7042 outb(0x000a, 0x06);
7044 BX_DEBUG_INT13_FL("clear flip-flop\n");
7045 outb(0x000c, 0x00); // clear flip-flop
7046 outb(0x0004, base_address);
7047 outb(0x0004, base_address>>8);
7048 BX_DEBUG_INT13_FL("clear flip-flop\n");
7049 outb(0x000c, 0x00); // clear flip-flop
7050 outb(0x0005, base_count);
7051 outb(0x0005, base_count>>8);
7053 // port 0b: DMA-1 Mode Register
7054 mode_register = 0x46; // single mode, increment, autoinit disable,
7055 // transfer type=write, channel 2
7056 BX_DEBUG_INT13_FL("setting mode register\n");
7057 outb(0x000b, mode_register);
7059 BX_DEBUG_INT13_FL("setting page register\n");
7060 // port 81: DMA-1 Page Register, channel 2
7061 outb(0x0081, page);
7063 BX_DEBUG_INT13_FL("unmask chan 2\n");
7064 outb(0x000a, 0x02); // unmask channel 2
7066 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
7067 outb(0x000a, 0x02);
7069 //--------------------------------------
7070 // set up floppy controller for transfer
7071 //--------------------------------------
7072 floppy_prepare_controller(drive);
7074 // send read-normal-data command (9 bytes) to controller
7075 outb(0x03f5, 0xe6); // e6: read normal data
7076 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7077 outb(0x03f5, track);
7078 outb(0x03f5, head);
7079 outb(0x03f5, sector);
7080 outb(0x03f5, 2); // 512 byte sector size
7081 outb(0x03f5, sector + num_sectors - 1); // last sector to read on track
7082 outb(0x03f5, 0); // Gap length
7083 outb(0x03f5, 0xff); // Gap length
7085 // turn on interrupts
7086 ASM_START
7088 ASM_END
7090 // wait on 40:3e bit 7 to become 1
7091 do {
7092 val8 = read_byte(0x0040, 0x0040);
7093 if (val8 == 0) {
7094 floppy_reset_controller();
7095 SET_AH(0x80); // drive not ready (timeout)
7096 set_diskette_ret_status(0x80);
7097 SET_AL(0); // no sectors read
7098 SET_CF(); // error occurred
7099 return;
7101 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7102 } while ( val8 == 0 );
7104 val8 = 0; // separate asm from while() loop
7105 // turn off interrupts
7106 ASM_START
7108 ASM_END
7110 // set 40:3e bit 7 to 0
7111 val8 = read_byte(0x0040, 0x003e);
7112 val8 &= 0x7f;
7113 write_byte(0x0040, 0x003e, val8);
7115 // check port 3f4 for accessibility to status bytes
7116 val8 = inb(0x3f4);
7117 if ( (val8 & 0xc0) != 0xc0 )
7118 BX_PANIC("int13_diskette: ctrl not ready\n");
7120 // read 7 return status bytes from controller
7121 // using loop index broken, have to unroll...
7122 return_status[0] = inb(0x3f5);
7123 return_status[1] = inb(0x3f5);
7124 return_status[2] = inb(0x3f5);
7125 return_status[3] = inb(0x3f5);
7126 return_status[4] = inb(0x3f5);
7127 return_status[5] = inb(0x3f5);
7128 return_status[6] = inb(0x3f5);
7129 // record in BIOS Data Area
7130 write_byte(0x0040, 0x0042, return_status[0]);
7131 write_byte(0x0040, 0x0043, return_status[1]);
7132 write_byte(0x0040, 0x0044, return_status[2]);
7133 write_byte(0x0040, 0x0045, return_status[3]);
7134 write_byte(0x0040, 0x0046, return_status[4]);
7135 write_byte(0x0040, 0x0047, return_status[5]);
7136 write_byte(0x0040, 0x0048, return_status[6]);
7138 if ( (return_status[0] & 0xc0) != 0 ) {
7139 SET_AH(0x20);
7140 set_diskette_ret_status(0x20);
7141 SET_AL(0); // no sectors read
7142 SET_CF(); // error occurred
7143 return;
7146 // ??? should track be new val from return_status[3] ?
7147 set_diskette_current_cyl(drive, track);
7148 // AL = number of sectors read (same value as passed)
7149 SET_AH(0x00); // success
7150 CLEAR_CF(); // success
7151 return;
7152 } else if (ah == 0x03) {
7153 // Write Diskette Sectors
7155 //-----------------------------------
7156 // set up DMA controller for transfer
7157 //-----------------------------------
7159 // es:bx = pointer to where to place information from diskette
7160 // port 04: DMA-1 base and current address, channel 2
7161 // port 05: DMA-1 base and current count, channel 2
7162 page = (ES >> 12); // upper 4 bits
7163 base_es = (ES << 4); // lower 16bits contributed by ES
7164 base_address = base_es + BX; // lower 16 bits of address
7165 // contributed by ES:BX
7166 if ( base_address < base_es ) {
7167 // in case of carry, adjust page by 1
7168 page++;
7170 base_count = (num_sectors * 512) - 1;
7172 // check for 64K boundary overrun
7173 last_addr = base_address + base_count;
7174 if (last_addr < base_address) {
7175 SET_AH(0x09);
7176 set_diskette_ret_status(0x09);
7177 SET_AL(0); // no sectors read
7178 SET_CF(); // error occurred
7179 return;
7182 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
7183 outb(0x000a, 0x06);
7185 outb(0x000c, 0x00); // clear flip-flop
7186 outb(0x0004, base_address);
7187 outb(0x0004, base_address>>8);
7188 outb(0x000c, 0x00); // clear flip-flop
7189 outb(0x0005, base_count);
7190 outb(0x0005, base_count>>8);
7192 // port 0b: DMA-1 Mode Register
7193 mode_register = 0x4a; // single mode, increment, autoinit disable,
7194 // transfer type=read, channel 2
7195 outb(0x000b, mode_register);
7197 // port 81: DMA-1 Page Register, channel 2
7198 outb(0x0081, page);
7200 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
7201 outb(0x000a, 0x02);
7203 //--------------------------------------
7204 // set up floppy controller for transfer
7205 //--------------------------------------
7206 floppy_prepare_controller(drive);
7208 // send write-normal-data command (9 bytes) to controller
7209 outb(0x03f5, 0xc5); // c5: write normal data
7210 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7211 outb(0x03f5, track);
7212 outb(0x03f5, head);
7213 outb(0x03f5, sector);
7214 outb(0x03f5, 2); // 512 byte sector size
7215 outb(0x03f5, sector + num_sectors - 1); // last sector to write on track
7216 outb(0x03f5, 0); // Gap length
7217 outb(0x03f5, 0xff); // Gap length
7219 // turn on interrupts
7220 ASM_START
7222 ASM_END
7224 // wait on 40:3e bit 7 to become 1
7225 do {
7226 val8 = read_byte(0x0040, 0x0040);
7227 if (val8 == 0) {
7228 floppy_reset_controller();
7229 SET_AH(0x80); // drive not ready (timeout)
7230 set_diskette_ret_status(0x80);
7231 SET_AL(0); // no sectors written
7232 SET_CF(); // error occurred
7233 return;
7235 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7236 } while ( val8 == 0 );
7238 val8 = 0; // separate asm from while() loop
7239 // turn off interrupts
7240 ASM_START
7242 ASM_END
7244 // set 40:3e bit 7 to 0
7245 val8 = read_byte(0x0040, 0x003e);
7246 val8 &= 0x7f;
7247 write_byte(0x0040, 0x003e, val8);
7249 // check port 3f4 for accessibility to status bytes
7250 val8 = inb(0x3f4);
7251 if ( (val8 & 0xc0) != 0xc0 )
7252 BX_PANIC("int13_diskette: ctrl not ready\n");
7254 // read 7 return status bytes from controller
7255 // using loop index broken, have to unroll...
7256 return_status[0] = inb(0x3f5);
7257 return_status[1] = inb(0x3f5);
7258 return_status[2] = inb(0x3f5);
7259 return_status[3] = inb(0x3f5);
7260 return_status[4] = inb(0x3f5);
7261 return_status[5] = inb(0x3f5);
7262 return_status[6] = inb(0x3f5);
7263 // record in BIOS Data Area
7264 write_byte(0x0040, 0x0042, return_status[0]);
7265 write_byte(0x0040, 0x0043, return_status[1]);
7266 write_byte(0x0040, 0x0044, return_status[2]);
7267 write_byte(0x0040, 0x0045, return_status[3]);
7268 write_byte(0x0040, 0x0046, return_status[4]);
7269 write_byte(0x0040, 0x0047, return_status[5]);
7270 write_byte(0x0040, 0x0048, return_status[6]);
7272 if ( (return_status[0] & 0xc0) != 0 ) {
7273 if ( (return_status[1] & 0x02) != 0 ) {
7274 // diskette not writable.
7275 // AH=status code=0x03 (tried to write on write-protected disk)
7276 // AL=number of sectors written=0
7277 AX = 0x0300;
7278 SET_CF();
7279 return;
7280 } else {
7281 BX_PANIC("int13_diskette_function: read error\n");
7285 // ??? should track be new val from return_status[3] ?
7286 set_diskette_current_cyl(drive, track);
7287 // AL = number of sectors read (same value as passed)
7288 SET_AH(0x00); // success
7289 CLEAR_CF(); // success
7290 return;
7291 } else { // if (ah == 0x04)
7292 // Verify Diskette Sectors
7294 // ??? should track be new val from return_status[3] ?
7295 set_diskette_current_cyl(drive, track);
7296 // AL = number of sectors verified (same value as passed)
7297 CLEAR_CF(); // success
7298 SET_AH(0x00); // success
7299 return;
7301 break;
7303 case 0x05: // format diskette track
7304 BX_DEBUG_INT13_FL("floppy f05\n");
7306 num_sectors = GET_AL();
7307 track = GET_CH();
7308 head = GET_DH();
7309 drive = GET_ELDL();
7311 if ((drive > 1) || (head > 1) || (track > 79) ||
7312 (num_sectors == 0) || (num_sectors > 18)) {
7313 SET_AH(1);
7314 set_diskette_ret_status(1);
7315 SET_CF(); // error occurred
7318 // see if drive exists
7319 if (floppy_drive_exists(drive) == 0) {
7320 SET_AH(0x80); // drive not responding
7321 set_diskette_ret_status(0x80);
7322 SET_CF(); // error occurred
7323 return;
7326 // see if media in drive, and type is known
7327 if (floppy_media_known(drive) == 0) {
7328 if (floppy_media_sense(drive) == 0) {
7329 SET_AH(0x0C); // Media type not found
7330 set_diskette_ret_status(0x0C);
7331 SET_AL(0); // no sectors read
7332 SET_CF(); // error occurred
7333 return;
7337 // set up DMA controller for transfer
7338 page = (ES >> 12); // upper 4 bits
7339 base_es = (ES << 4); // lower 16bits contributed by ES
7340 base_address = base_es + BX; // lower 16 bits of address
7341 // contributed by ES:BX
7342 if ( base_address < base_es ) {
7343 // in case of carry, adjust page by 1
7344 page++;
7346 base_count = (num_sectors * 4) - 1;
7348 // check for 64K boundary overrun
7349 last_addr = base_address + base_count;
7350 if (last_addr < base_address) {
7351 SET_AH(0x09);
7352 set_diskette_ret_status(0x09);
7353 SET_AL(0); // no sectors read
7354 SET_CF(); // error occurred
7355 return;
7358 outb(0x000a, 0x06);
7359 outb(0x000c, 0x00); // clear flip-flop
7360 outb(0x0004, base_address);
7361 outb(0x0004, base_address>>8);
7362 outb(0x000c, 0x00); // clear flip-flop
7363 outb(0x0005, base_count);
7364 outb(0x0005, base_count>>8);
7365 mode_register = 0x4a; // single mode, increment, autoinit disable,
7366 // transfer type=read, channel 2
7367 outb(0x000b, mode_register);
7368 // port 81: DMA-1 Page Register, channel 2
7369 outb(0x0081, page);
7370 outb(0x000a, 0x02);
7372 // set up floppy controller for transfer
7373 floppy_prepare_controller(drive);
7375 // send format-track command (6 bytes) to controller
7376 outb(0x03f5, 0x4d); // 4d: format track
7377 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7378 outb(0x03f5, 2); // 512 byte sector size
7379 outb(0x03f5, num_sectors); // number of sectors per track
7380 outb(0x03f5, 0); // Gap length
7381 outb(0x03f5, 0xf6); // Fill byte
7382 // turn on interrupts
7383 ASM_START
7385 ASM_END
7387 // wait on 40:3e bit 7 to become 1
7388 do {
7389 val8 = read_byte(0x0040, 0x0040);
7390 if (val8 == 0) {
7391 floppy_reset_controller();
7392 SET_AH(0x80); // drive not ready (timeout)
7393 set_diskette_ret_status(0x80);
7394 SET_CF(); // error occurred
7395 return;
7397 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7398 } while ( val8 == 0 );
7400 val8 = 0; // separate asm from while() loop
7401 // turn off interrupts
7402 ASM_START
7404 ASM_END
7405 // set 40:3e bit 7 to 0
7406 val8 = read_byte(0x0040, 0x003e);
7407 val8 &= 0x7f;
7408 write_byte(0x0040, 0x003e, val8);
7409 // check port 3f4 for accessibility to status bytes
7410 val8 = inb(0x3f4);
7411 if ( (val8 & 0xc0) != 0xc0 )
7412 BX_PANIC("int13_diskette: ctrl not ready\n");
7414 // read 7 return status bytes from controller
7415 // using loop index broken, have to unroll...
7416 return_status[0] = inb(0x3f5);
7417 return_status[1] = inb(0x3f5);
7418 return_status[2] = inb(0x3f5);
7419 return_status[3] = inb(0x3f5);
7420 return_status[4] = inb(0x3f5);
7421 return_status[5] = inb(0x3f5);
7422 return_status[6] = inb(0x3f5);
7423 // record in BIOS Data Area
7424 write_byte(0x0040, 0x0042, return_status[0]);
7425 write_byte(0x0040, 0x0043, return_status[1]);
7426 write_byte(0x0040, 0x0044, return_status[2]);
7427 write_byte(0x0040, 0x0045, return_status[3]);
7428 write_byte(0x0040, 0x0046, return_status[4]);
7429 write_byte(0x0040, 0x0047, return_status[5]);
7430 write_byte(0x0040, 0x0048, return_status[6]);
7432 if ( (return_status[0] & 0xc0) != 0 ) {
7433 if ( (return_status[1] & 0x02) != 0 ) {
7434 // diskette not writable.
7435 // AH=status code=0x03 (tried to write on write-protected disk)
7436 // AL=number of sectors written=0
7437 AX = 0x0300;
7438 SET_CF();
7439 return;
7440 } else {
7441 BX_PANIC("int13_diskette_function: write error\n");
7445 SET_AH(0);
7446 set_diskette_ret_status(0);
7447 set_diskette_current_cyl(drive, 0);
7448 CLEAR_CF(); // successful
7449 return;
7452 case 0x08: // read diskette drive parameters
7453 BX_DEBUG_INT13_FL("floppy f08\n");
7454 drive = GET_ELDL();
7456 if (drive > 1) {
7457 AX = 0;
7458 BX = 0;
7459 CX = 0;
7460 DX = 0;
7461 ES = 0;
7462 DI = 0;
7463 SET_DL(num_floppies);
7464 SET_CF();
7465 return;
7468 drive_type = inb_cmos(0x10);
7469 num_floppies = 0;
7470 if (drive_type & 0xf0)
7471 num_floppies++;
7472 if (drive_type & 0x0f)
7473 num_floppies++;
7475 if (drive == 0)
7476 drive_type >>= 4;
7477 else
7478 drive_type &= 0x0f;
7480 SET_BH(0);
7481 SET_BL(drive_type);
7482 SET_AH(0);
7483 SET_AL(0);
7484 SET_DL(num_floppies);
7486 switch (drive_type) {
7487 case 0: // none
7488 CX = 0;
7489 SET_DH(0); // max head #
7490 break;
7492 case 1: // 360KB, 5.25"
7493 CX = 0x2709; // 40 tracks, 9 sectors
7494 SET_DH(1); // max head #
7495 break;
7497 case 2: // 1.2MB, 5.25"
7498 CX = 0x4f0f; // 80 tracks, 15 sectors
7499 SET_DH(1); // max head #
7500 break;
7502 case 3: // 720KB, 3.5"
7503 CX = 0x4f09; // 80 tracks, 9 sectors
7504 SET_DH(1); // max head #
7505 break;
7507 case 4: // 1.44MB, 3.5"
7508 CX = 0x4f12; // 80 tracks, 18 sectors
7509 SET_DH(1); // max head #
7510 break;
7512 case 5: // 2.88MB, 3.5"
7513 CX = 0x4f24; // 80 tracks, 36 sectors
7514 SET_DH(1); // max head #
7515 break;
7517 case 6: // 160k, 5.25"
7518 CX = 0x2708; // 40 tracks, 8 sectors
7519 SET_DH(0); // max head #
7520 break;
7522 case 7: // 180k, 5.25"
7523 CX = 0x2709; // 40 tracks, 9 sectors
7524 SET_DH(0); // max head #
7525 break;
7527 case 8: // 320k, 5.25"
7528 CX = 0x2708; // 40 tracks, 8 sectors
7529 SET_DH(1); // max head #
7530 break;
7532 default: // ?
7533 BX_PANIC("floppy: int13: bad floppy type\n");
7536 /* set es & di to point to 11 byte diskette param table in ROM */
7537 ASM_START
7538 push bp
7539 mov bp, sp
7540 mov ax, #diskette_param_table2
7541 mov _int13_diskette_function.DI+2[bp], ax
7542 mov _int13_diskette_function.ES+2[bp], cs
7543 pop bp
7544 ASM_END
7545 CLEAR_CF(); // success
7546 /* disk status not changed upon success */
7547 return;
7550 case 0x15: // read diskette drive type
7551 BX_DEBUG_INT13_FL("floppy f15\n");
7552 drive = GET_ELDL();
7553 if (drive > 1) {
7554 SET_AH(0); // only 2 drives supported
7555 // set_diskette_ret_status here ???
7556 SET_CF();
7557 return;
7559 drive_type = inb_cmos(0x10);
7561 if (drive == 0)
7562 drive_type >>= 4;
7563 else
7564 drive_type &= 0x0f;
7565 CLEAR_CF(); // successful, not present
7566 if (drive_type==0) {
7567 SET_AH(0); // drive not present
7569 else {
7570 SET_AH(1); // drive present, does not support change line
7573 return;
7575 case 0x16: // get diskette change line status
7576 BX_DEBUG_INT13_FL("floppy f16\n");
7577 drive = GET_ELDL();
7578 if (drive > 1) {
7579 SET_AH(0x01); // invalid drive
7580 set_diskette_ret_status(0x01);
7581 SET_CF();
7582 return;
7585 SET_AH(0x06); // change line not supported
7586 set_diskette_ret_status(0x06);
7587 SET_CF();
7588 return;
7590 case 0x17: // set diskette type for format(old)
7591 BX_DEBUG_INT13_FL("floppy f17\n");
7592 /* not used for 1.44M floppies */
7593 SET_AH(0x01); // not supported
7594 set_diskette_ret_status(1); /* not supported */
7595 SET_CF();
7596 return;
7598 case 0x18: // set diskette type for format(new)
7599 BX_DEBUG_INT13_FL("floppy f18\n");
7600 SET_AH(0x01); // do later
7601 set_diskette_ret_status(1);
7602 SET_CF();
7603 return;
7605 default:
7606 BX_INFO("int13_diskette: unsupported AH=%02x\n", GET_AH());
7608 // if ( (ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e) ) {
7609 SET_AH(0x01); // ???
7610 set_diskette_ret_status(1);
7611 SET_CF();
7612 return;
7613 // }
7616 #else // #if BX_SUPPORT_FLOPPY
7617 void
7618 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
7619 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
7621 Bit8u val8;
7623 switch ( GET_AH() ) {
7625 case 0x01: // Read Diskette Status
7626 CLEAR_CF();
7627 val8 = read_byte(0x0000, 0x0441);
7628 SET_AH(val8);
7629 if (val8) {
7630 SET_CF();
7632 return;
7634 default:
7635 SET_CF();
7636 write_byte(0x0000, 0x0441, 0x01);
7637 SET_AH(0x01);
7640 #endif // #if BX_SUPPORT_FLOPPY
7642 void
7643 set_diskette_ret_status(value)
7644 Bit8u value;
7646 write_byte(0x0040, 0x0041, value);
7649 void
7650 set_diskette_current_cyl(drive, cyl)
7651 Bit8u drive;
7652 Bit8u cyl;
7654 if (drive > 1)
7655 BX_PANIC("set_diskette_current_cyl(): drive > 1\n");
7656 write_byte(0x0040, 0x0094+drive, cyl);
7659 void
7660 determine_floppy_media(drive)
7661 Bit16u drive;
7663 #if 0
7664 Bit8u val8, DOR, ctrl_info;
7666 ctrl_info = read_byte(0x0040, 0x008F);
7667 if (drive==1)
7668 ctrl_info >>= 4;
7669 else
7670 ctrl_info &= 0x0f;
7672 #if 0
7673 if (drive == 0) {
7674 DOR = 0x1c; // DOR: drive0 motor on, DMA&int enabled, normal op, drive select 0
7676 else {
7677 DOR = 0x2d; // DOR: drive1 motor on, DMA&int enabled, normal op, drive select 1
7679 #endif
7681 if ( (ctrl_info & 0x04) != 0x04 ) {
7682 // Drive not determined means no drive exists, done.
7683 return;
7686 #if 0
7687 // check Main Status Register for readiness
7688 val8 = inb(0x03f4) & 0x80; // Main Status Register
7689 if (val8 != 0x80)
7690 BX_PANIC("d_f_m: MRQ bit not set\n");
7692 // change line
7694 // existing BDA values
7696 // turn on drive motor
7697 outb(0x03f2, DOR); // Digital Output Register
7699 #endif
7700 BX_PANIC("d_f_m: OK so far\n");
7701 #endif
7704 void
7705 int17_function(regs, ds, iret_addr)
7706 pusha_regs_t regs; // regs pushed from PUSHA instruction
7707 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7708 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7710 Bit16u addr,timeout;
7711 Bit8u val8;
7713 ASM_START
7715 ASM_END
7717 addr = read_word(0x0040, (regs.u.r16.dx << 1) + 8);
7718 if ((regs.u.r8.ah < 3) && (regs.u.r16.dx < 3) && (addr > 0)) {
7719 timeout = read_byte(0x0040, 0x0078 + regs.u.r16.dx) << 8;
7720 if (regs.u.r8.ah == 0) {
7721 outb(addr, regs.u.r8.al);
7722 val8 = inb(addr+2);
7723 outb(addr+2, val8 | 0x01); // send strobe
7724 ASM_START
7726 ASM_END
7727 outb(addr+2, val8 & ~0x01);
7728 while (((inb(addr+1) & 0x40) == 0x40) && (timeout)) {
7729 timeout--;
7732 if (regs.u.r8.ah == 1) {
7733 val8 = inb(addr+2);
7734 outb(addr+2, val8 & ~0x04); // send init
7735 ASM_START
7737 ASM_END
7738 outb(addr+2, val8 | 0x04);
7740 val8 = inb(addr+1);
7741 regs.u.r8.ah = (val8 ^ 0x48);
7742 if (!timeout) regs.u.r8.ah |= 0x01;
7743 ClearCF(iret_addr.flags);
7744 } else {
7745 SetCF(iret_addr.flags); // Unsupported
7749 void
7750 int19_function(seq_nr)
7751 Bit16u seq_nr;
7753 Bit16u ebda_seg=read_word(0x0040,0x000E);
7754 Bit16u bootdev;
7755 Bit8u bootdrv;
7756 Bit8u bootchk;
7757 Bit16u bootseg;
7758 Bit16u bootip;
7759 Bit16u status;
7761 ipl_entry_t e;
7763 // if BX_ELTORITO_BOOT is not defined, old behavior
7764 // check bit 5 in CMOS reg 0x2d. load either 0x00 or 0x80 into DL
7765 // in preparation for the intial INT 13h (0=floppy A:, 0x80=C:)
7766 // 0: system boot sequence, first drive C: then A:
7767 // 1: system boot sequence, first drive A: then C:
7768 // else BX_ELTORITO_BOOT is defined
7769 // CMOS regs 0x3D and 0x38 contain the boot sequence:
7770 // CMOS reg 0x3D & 0x0f : 1st boot device
7771 // CMOS reg 0x3D & 0xf0 : 2nd boot device
7772 // CMOS reg 0x38 & 0xf0 : 3rd boot device
7773 // boot device codes:
7774 // 0x00 : not defined
7775 // 0x01 : first floppy
7776 // 0x02 : first harddrive
7777 // 0x03 : first cdrom
7778 // 0x04 - 0x0f : PnP expansion ROMs (e.g. Etherboot)
7779 // else : boot failure
7781 // Get the boot sequence
7782 #if BX_ELTORITO_BOOT
7783 bootdev = inb_cmos(0x3d);
7784 bootdev |= ((inb_cmos(0x38) & 0xf0) << 4);
7785 bootdev >>= 4 * seq_nr;
7786 bootdev &= 0xf;
7787 if (bootdev == 0) BX_PANIC("No bootable device.\n");
7789 /* Translate from CMOS runes to an IPL table offset by subtracting 1 */
7790 bootdev -= 1;
7791 #else
7792 if (seq_nr ==2) BX_PANIC("No more boot devices.");
7793 if (!!(inb_cmos(0x2d) & 0x20) ^ (seq_nr == 1))
7794 /* Boot from floppy if the bit is set or it's the second boot */
7795 bootdev = 0x00;
7796 else
7797 bootdev = 0x01;
7798 #endif
7800 /* Read the boot device from the IPL table */
7801 if (get_boot_vector(bootdev, &e) == 0) {
7802 BX_INFO("Invalid boot device (0x%x)\n", bootdev);
7803 return;
7806 /* Do the loading, and set up vector as a far pointer to the boot
7807 * address, and bootdrv as the boot drive */
7808 print_boot_device(e.type);
7810 switch(e.type) {
7811 case IPL_TYPE_FLOPPY: /* FDD */
7812 case IPL_TYPE_HARDDISK: /* HDD */
7814 bootdrv = (e.type == IPL_TYPE_HARDDISK) ? 0x80 : 0x00;
7815 bootseg = 0x07c0;
7816 status = 0;
7818 ASM_START
7819 push bp
7820 mov bp, sp
7821 push ax
7822 push bx
7823 push cx
7824 push dx
7826 mov dl, _int19_function.bootdrv + 2[bp]
7827 mov ax, _int19_function.bootseg + 2[bp]
7828 mov es, ax ;; segment
7829 xor bx, bx ;; offset
7830 mov ah, #0x02 ;; function 2, read diskette sector
7831 mov al, #0x01 ;; read 1 sector
7832 mov ch, #0x00 ;; track 0
7833 mov cl, #0x01 ;; sector 1
7834 mov dh, #0x00 ;; head 0
7835 int #0x13 ;; read sector
7836 jnc int19_load_done
7837 mov ax, #0x0001
7838 mov _int19_function.status + 2[bp], ax
7840 int19_load_done:
7841 pop dx
7842 pop cx
7843 pop bx
7844 pop ax
7845 pop bp
7846 ASM_END
7848 if (status != 0) {
7849 print_boot_failure(e.type, 1);
7850 return;
7853 /* Always check the signature on a HDD boot sector; on FDD, only do
7854 * the check if the CMOS doesn't tell us to skip it */
7855 if ((e.type != IPL_TYPE_FLOPPY) || !((inb_cmos(0x38) & 0x01))) {
7856 if (read_word(bootseg,0x1fe) != 0xaa55) {
7857 print_boot_failure(e.type, 0);
7858 return;
7862 /* Canonicalize bootseg:bootip */
7863 bootip = (bootseg & 0x0fff) << 4;
7864 bootseg &= 0xf000;
7865 break;
7867 #if BX_ELTORITO_BOOT
7868 case IPL_TYPE_CDROM: /* CD-ROM */
7869 status = cdrom_boot();
7871 // If failure
7872 if ( (status & 0x00ff) !=0 ) {
7873 print_cdromboot_failure(status);
7874 print_boot_failure(e.type, 1);
7875 return;
7878 bootdrv = (Bit8u)(status>>8);
7879 bootseg = read_word(ebda_seg,&EbdaData->cdemu.load_segment);
7880 /* Canonicalize bootseg:bootip */
7881 bootip = (bootseg & 0x0fff) << 4;
7882 bootseg &= 0xf000;
7883 break;
7884 #endif
7886 case IPL_TYPE_BEV: /* Expansion ROM with a Bootstrap Entry Vector (a far pointer) */
7887 bootseg = e.vector >> 16;
7888 bootip = e.vector & 0xffff;
7889 break;
7891 default: return;
7894 /* Debugging info */
7895 BX_INFO("Booting from %x:%x\n", bootseg, bootip);
7897 /* Jump to the boot vector */
7898 ASM_START
7899 mov bp, sp
7900 ;; Build an iret stack frame that will take us to the boot vector.
7901 ;; iret pops ip, then cs, then flags, so push them in the opposite order.
7902 pushf
7903 mov ax, _int19_function.bootseg + 0[bp]
7904 push ax
7905 mov ax, _int19_function.bootip + 0[bp]
7906 push ax
7907 ;; Set the magic number in ax and the boot drive in dl.
7908 mov ax, #0xaa55
7909 mov dl, _int19_function.bootdrv + 0[bp]
7910 ;; Zero some of the other registers.
7911 xor bx, bx
7912 mov ds, bx
7913 mov es, bx
7914 mov bp, bx
7915 ;; Go!
7916 iret
7917 ASM_END
7920 void
7921 int1a_function(regs, ds, iret_addr)
7922 pusha_regs_t regs; // regs pushed from PUSHA instruction
7923 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7924 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7926 Bit8u val8;
7928 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);
7930 ASM_START
7932 ASM_END
7934 switch (regs.u.r8.ah) {
7935 case 0: // get current clock count
7936 ASM_START
7938 ASM_END
7939 regs.u.r16.cx = BiosData->ticks_high;
7940 regs.u.r16.dx = BiosData->ticks_low;
7941 regs.u.r8.al = BiosData->midnight_flag;
7942 BiosData->midnight_flag = 0; // reset flag
7943 ASM_START
7945 ASM_END
7946 // AH already 0
7947 ClearCF(iret_addr.flags); // OK
7948 break;
7950 case 1: // Set Current Clock Count
7951 ASM_START
7953 ASM_END
7954 BiosData->ticks_high = regs.u.r16.cx;
7955 BiosData->ticks_low = regs.u.r16.dx;
7956 BiosData->midnight_flag = 0; // reset flag
7957 ASM_START
7959 ASM_END
7960 regs.u.r8.ah = 0;
7961 ClearCF(iret_addr.flags); // OK
7962 break;
7965 case 2: // Read CMOS Time
7966 if (rtc_updating()) {
7967 SetCF(iret_addr.flags);
7968 break;
7971 regs.u.r8.dh = inb_cmos(0x00); // Seconds
7972 regs.u.r8.cl = inb_cmos(0x02); // Minutes
7973 regs.u.r8.ch = inb_cmos(0x04); // Hours
7974 regs.u.r8.dl = inb_cmos(0x0b) & 0x01; // Stat Reg B
7975 regs.u.r8.ah = 0;
7976 regs.u.r8.al = regs.u.r8.ch;
7977 ClearCF(iret_addr.flags); // OK
7978 break;
7980 case 3: // Set CMOS Time
7981 // Using a debugger, I notice the following masking/setting
7982 // of bits in Status Register B, by setting Reg B to
7983 // a few values and getting its value after INT 1A was called.
7985 // try#1 try#2 try#3
7986 // before 1111 1101 0111 1101 0000 0000
7987 // after 0110 0010 0110 0010 0000 0010
7989 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7990 // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
7991 if (rtc_updating()) {
7992 init_rtc();
7993 // fall through as if an update were not in progress
7995 outb_cmos(0x00, regs.u.r8.dh); // Seconds
7996 outb_cmos(0x02, regs.u.r8.cl); // Minutes
7997 outb_cmos(0x04, regs.u.r8.ch); // Hours
7998 // Set Daylight Savings time enabled bit to requested value
7999 val8 = (inb_cmos(0x0b) & 0x60) | 0x02 | (regs.u.r8.dl & 0x01);
8000 // (reg B already selected)
8001 outb_cmos(0x0b, val8);
8002 regs.u.r8.ah = 0;
8003 regs.u.r8.al = val8; // val last written to Reg B
8004 ClearCF(iret_addr.flags); // OK
8005 break;
8007 case 4: // Read CMOS Date
8008 regs.u.r8.ah = 0;
8009 if (rtc_updating()) {
8010 SetCF(iret_addr.flags);
8011 break;
8013 regs.u.r8.cl = inb_cmos(0x09); // Year
8014 regs.u.r8.dh = inb_cmos(0x08); // Month
8015 regs.u.r8.dl = inb_cmos(0x07); // Day of Month
8016 regs.u.r8.ch = inb_cmos(0x32); // Century
8017 regs.u.r8.al = regs.u.r8.ch;
8018 ClearCF(iret_addr.flags); // OK
8019 break;
8021 case 5: // Set CMOS Date
8022 // Using a debugger, I notice the following masking/setting
8023 // of bits in Status Register B, by setting Reg B to
8024 // a few values and getting its value after INT 1A was called.
8026 // try#1 try#2 try#3 try#4
8027 // before 1111 1101 0111 1101 0000 0010 0000 0000
8028 // after 0110 1101 0111 1101 0000 0010 0000 0000
8030 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8031 // My assumption: RegB = (RegB & 01111111b)
8032 if (rtc_updating()) {
8033 init_rtc();
8034 SetCF(iret_addr.flags);
8035 break;
8037 outb_cmos(0x09, regs.u.r8.cl); // Year
8038 outb_cmos(0x08, regs.u.r8.dh); // Month
8039 outb_cmos(0x07, regs.u.r8.dl); // Day of Month
8040 outb_cmos(0x32, regs.u.r8.ch); // Century
8041 val8 = inb_cmos(0x0b) & 0x7f; // clear halt-clock bit
8042 outb_cmos(0x0b, val8);
8043 regs.u.r8.ah = 0;
8044 regs.u.r8.al = val8; // AL = val last written to Reg B
8045 ClearCF(iret_addr.flags); // OK
8046 break;
8048 case 6: // Set Alarm Time in CMOS
8049 // Using a debugger, I notice the following masking/setting
8050 // of bits in Status Register B, by setting Reg B to
8051 // a few values and getting its value after INT 1A was called.
8053 // try#1 try#2 try#3
8054 // before 1101 1111 0101 1111 0000 0000
8055 // after 0110 1111 0111 1111 0010 0000
8057 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8058 // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
8059 val8 = inb_cmos(0x0b); // Get Status Reg B
8060 regs.u.r16.ax = 0;
8061 if (val8 & 0x20) {
8062 // Alarm interrupt enabled already
8063 SetCF(iret_addr.flags); // Error: alarm in use
8064 break;
8066 if (rtc_updating()) {
8067 init_rtc();
8068 // fall through as if an update were not in progress
8070 outb_cmos(0x01, regs.u.r8.dh); // Seconds alarm
8071 outb_cmos(0x03, regs.u.r8.cl); // Minutes alarm
8072 outb_cmos(0x05, regs.u.r8.ch); // Hours alarm
8073 outb(0xa1, inb(0xa1) & 0xfe); // enable IRQ 8
8074 // enable Status Reg B alarm bit, clear halt clock bit
8075 outb_cmos(0x0b, (val8 & 0x7f) | 0x20);
8076 ClearCF(iret_addr.flags); // OK
8077 break;
8079 case 7: // Turn off Alarm
8080 // Using a debugger, I notice the following masking/setting
8081 // of bits in Status Register B, by setting Reg B to
8082 // a few values and getting its value after INT 1A was called.
8084 // try#1 try#2 try#3 try#4
8085 // before 1111 1101 0111 1101 0010 0000 0010 0010
8086 // after 0100 0101 0101 0101 0000 0000 0000 0010
8088 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8089 // My assumption: RegB = (RegB & 01010111b)
8090 val8 = inb_cmos(0x0b); // Get Status Reg B
8091 // clear clock-halt bit, disable alarm bit
8092 outb_cmos(0x0b, val8 & 0x57); // disable alarm bit
8093 regs.u.r8.ah = 0;
8094 regs.u.r8.al = val8; // val last written to Reg B
8095 ClearCF(iret_addr.flags); // OK
8096 break;
8097 #if BX_PCIBIOS
8098 case 0xb1:
8099 // real mode PCI BIOS functions now handled in assembler code
8100 // this C code handles the error code for information only
8101 if (regs.u.r8.bl == 0xff) {
8102 BX_INFO("PCI BIOS: PCI not present\n");
8103 } else if (regs.u.r8.bl == 0x81) {
8104 BX_INFO("unsupported PCI BIOS function 0x%02x\n", regs.u.r8.al);
8105 } else if (regs.u.r8.bl == 0x83) {
8106 BX_INFO("bad PCI vendor ID %04x\n", regs.u.r16.dx);
8107 } else if (regs.u.r8.bl == 0x86) {
8108 if (regs.u.r8.al == 0x02) {
8109 BX_INFO("PCI device %04x:%04x not found at index %d\n", regs.u.r16.dx, regs.u.r16.cx, regs.u.r16.si);
8110 } else {
8111 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);
8114 regs.u.r8.ah = regs.u.r8.bl;
8115 SetCF(iret_addr.flags);
8116 break;
8117 #endif
8119 default:
8120 SetCF(iret_addr.flags); // Unsupported
8124 void
8125 int70_function(regs, ds, iret_addr)
8126 pusha_regs_t regs; // regs pushed from PUSHA instruction
8127 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
8128 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
8130 // INT 70h: IRQ 8 - CMOS RTC interrupt from periodic or alarm modes
8131 Bit8u registerB = 0, registerC = 0;
8133 // Check which modes are enabled and have occurred.
8134 registerB = inb_cmos( 0xB );
8135 registerC = inb_cmos( 0xC );
8137 if( ( registerB & 0x60 ) != 0 ) {
8138 if( ( registerC & 0x20 ) != 0 ) {
8139 // Handle Alarm Interrupt.
8140 ASM_START
8142 int #0x4a
8144 ASM_END
8146 if( ( registerC & 0x40 ) != 0 ) {
8147 // Handle Periodic Interrupt.
8149 if( read_byte( 0x40, 0xA0 ) != 0 ) {
8150 // Wait Interval (Int 15, AH=83) active.
8151 Bit32u time, toggle;
8153 time = read_dword( 0x40, 0x9C ); // Time left in microseconds.
8154 if( time < 0x3D1 ) {
8155 // Done waiting.
8156 Bit16u segment, offset;
8158 segment = read_word( 0x40, 0x98 );
8159 offset = read_word( 0x40, 0x9A );
8160 write_byte( 0x40, 0xA0, 0 ); // Turn of status byte.
8161 outb_cmos( 0xB, registerB & 0x37 ); // Clear the Periodic Interrupt.
8162 write_byte(segment, offset, read_byte(segment, offset) | 0x80 ); // Write to specified flag byte.
8163 } else {
8164 // Continue waiting.
8165 time -= 0x3D1;
8166 write_dword( 0x40, 0x9C, time );
8172 ASM_START
8173 call eoi_both_pics
8174 ASM_END
8178 ASM_START
8179 ;------------------------------------------
8180 ;- INT74h : PS/2 mouse hardware interrupt -
8181 ;------------------------------------------
8182 int74_handler:
8184 pusha
8185 push ds ;; save DS
8186 push #0x00 ;; placeholder for status
8187 push #0x00 ;; placeholder for X
8188 push #0x00 ;; placeholder for Y
8189 push #0x00 ;; placeholder for Z
8190 push #0x00 ;; placeholder for make_far_call boolean
8191 call _int74_function
8192 pop cx ;; remove make_far_call from stack
8193 jcxz int74_done
8195 ;; make far call to EBDA:0022
8196 push #0x00
8197 pop ds
8198 push 0x040E ;; push 0000:040E (opcodes 0xff, 0x36, 0x0E, 0x04)
8199 pop ds
8200 //CALL_EP(0x0022) ;; call far routine (call_Ep DS:0022 :opcodes 0xff, 0x1e, 0x22, 0x00)
8201 call far ptr[0x22]
8202 int74_done:
8204 call eoi_both_pics
8205 add sp, #8 ;; pop status, x, y, z
8207 pop ds ;; restore DS
8208 popa
8209 iret
8212 ;; This will perform an IRET, but will retain value of current CF
8213 ;; by altering flags on stack. Better than RETF #02.
8214 iret_modify_cf:
8215 jc carry_set
8216 push bp
8217 mov bp, sp
8218 and BYTE [bp + 0x06], #0xfe
8219 pop bp
8220 iret
8221 carry_set:
8222 push bp
8223 mov bp, sp
8224 or BYTE [bp + 0x06], #0x01
8225 pop bp
8226 iret
8229 ;----------------------
8230 ;- INT13h (relocated) -
8231 ;----------------------
8233 ; int13_relocated is a little bit messed up since I played with it
8234 ; I have to rewrite it:
8235 ; - call a function that detect which function to call
8236 ; - make all called C function get the same parameters list
8238 int13_relocated:
8240 #if BX_ELTORITO_BOOT
8241 ;; check for an eltorito function
8242 cmp ah,#0x4a
8243 jb int13_not_eltorito
8244 cmp ah,#0x4d
8245 ja int13_not_eltorito
8247 pusha
8248 push es
8249 push ds
8250 push ss
8251 pop ds
8253 push #int13_out
8254 jmp _int13_eltorito ;; ELDX not used
8256 int13_not_eltorito:
8257 push ax
8258 push bx
8259 push cx
8260 push dx
8262 ;; check if emulation active
8263 call _cdemu_isactive
8264 cmp al,#0x00
8265 je int13_cdemu_inactive
8267 ;; check if access to the emulated drive
8268 call _cdemu_emulated_drive
8269 pop dx
8270 push dx
8271 cmp al,dl ;; int13 on emulated drive
8272 jne int13_nocdemu
8274 pop dx
8275 pop cx
8276 pop bx
8277 pop ax
8279 pusha
8280 push es
8281 push ds
8282 push ss
8283 pop ds
8285 push #int13_out
8286 jmp _int13_cdemu ;; ELDX not used
8288 int13_nocdemu:
8289 and dl,#0xE0 ;; mask to get device class, including cdroms
8290 cmp al,dl ;; al is 0x00 or 0x80
8291 jne int13_cdemu_inactive ;; inactive for device class
8293 pop dx
8294 pop cx
8295 pop bx
8296 pop ax
8298 push ax
8299 push cx
8300 push dx
8301 push bx
8303 dec dl ;; real drive is dl - 1
8304 jmp int13_legacy
8306 int13_cdemu_inactive:
8307 pop dx
8308 pop cx
8309 pop bx
8310 pop ax
8312 #endif // BX_ELTORITO_BOOT
8314 int13_noeltorito:
8316 push ax
8317 push cx
8318 push dx
8319 push bx
8321 int13_legacy:
8323 push dx ;; push eltorito value of dx instead of sp
8325 push bp
8326 push si
8327 push di
8329 push es
8330 push ds
8331 push ss
8332 pop ds
8334 ;; now the 16-bit registers can be restored with:
8335 ;; pop ds; pop es; popa; iret
8336 ;; arguments passed to functions should be
8337 ;; DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS
8339 test dl, #0x80
8340 jnz int13_notfloppy
8342 push #int13_out
8343 jmp _int13_diskette_function
8345 int13_notfloppy:
8347 #if BX_USE_ATADRV
8349 cmp dl, #0xE0
8350 jb int13_notcdrom
8352 // ebx is modified: BSD 5.2.1 boot loader problem
8353 // someone should figure out which 32 bit register that actually are used
8355 shr ebx, #16
8356 push bx
8358 call _int13_cdrom
8360 pop bx
8361 shl ebx, #16
8363 jmp int13_out
8365 int13_notcdrom:
8367 #endif
8369 int13_disk:
8370 ;; int13_harddisk modifies high word of EAX
8371 shr eax, #16
8372 push ax
8373 call _int13_harddisk
8374 pop ax
8375 shl eax, #16
8377 int13_out:
8378 pop ds
8379 pop es
8380 popa
8381 iret
8383 ;----------
8384 ;- INT18h -
8385 ;----------
8386 int18_handler: ;; Boot Failure recovery: try the next device.
8388 ;; Reset SP and SS
8389 mov ax, #0xfffe
8390 mov sp, ax
8391 xor ax, ax
8392 mov ss, ax
8394 ;; Get the boot sequence number out of the IPL memory
8395 mov bx, #IPL_SEG
8396 mov ds, bx ;; Set segment
8397 mov bx, IPL_SEQUENCE_OFFSET ;; BX is now the sequence number
8398 inc bx ;; ++
8399 mov IPL_SEQUENCE_OFFSET, bx ;; Write it back
8400 mov ds, ax ;; and reset the segment to zero.
8402 ;; Carry on in the INT 19h handler, using the new sequence number
8403 push bx
8405 jmp int19_next_boot
8407 ;----------
8408 ;- INT19h -
8409 ;----------
8410 int19_relocated: ;; Boot function, relocated
8412 ;; int19 was beginning to be really complex, so now it
8413 ;; just calls a C function that does the work
8415 push bp
8416 mov bp, sp
8418 ;; Reset SS and SP
8419 mov ax, #0xfffe
8420 mov sp, ax
8421 xor ax, ax
8422 mov ss, ax
8424 ;; Start from the first boot device (0, in AX)
8425 mov bx, #IPL_SEG
8426 mov ds, bx ;; Set segment to write to the IPL memory
8427 mov IPL_SEQUENCE_OFFSET, ax ;; Save the sequence number
8428 mov ds, ax ;; and reset the segment.
8430 push ax
8432 int19_next_boot:
8434 ;; Call the C code for the next boot device
8435 call _int19_function
8437 ;; Boot failed: invoke the boot recovery function
8438 int #0x18
8440 ;----------
8441 ;- INT1Ch -
8442 ;----------
8443 int1c_handler: ;; User Timer Tick
8444 iret
8447 ;----------------------
8448 ;- POST: Floppy Drive -
8449 ;----------------------
8450 floppy_drive_post:
8451 xor ax, ax
8452 mov ds, ax
8454 mov al, #0x00
8455 mov 0x043e, al ;; drive 0 & 1 uncalibrated, no interrupt has occurred
8457 mov 0x043f, al ;; diskette motor status: read op, drive0, motors off
8459 mov 0x0440, al ;; diskette motor timeout counter: not active
8460 mov 0x0441, al ;; diskette controller status return code
8462 mov 0x0442, al ;; disk & diskette controller status register 0
8463 mov 0x0443, al ;; diskette controller status register 1
8464 mov 0x0444, al ;; diskette controller status register 2
8465 mov 0x0445, al ;; diskette controller cylinder number
8466 mov 0x0446, al ;; diskette controller head number
8467 mov 0x0447, al ;; diskette controller sector number
8468 mov 0x0448, al ;; diskette controller bytes written
8470 mov 0x048b, al ;; diskette configuration data
8472 ;; -----------------------------------------------------------------
8473 ;; (048F) diskette controller information
8475 mov al, #0x10 ;; get CMOS diskette drive type
8476 out 0x70, AL
8477 in AL, 0x71
8478 mov ah, al ;; save byte to AH
8480 look_drive0:
8481 shr al, #4 ;; look at top 4 bits for drive 0
8482 jz f0_missing ;; jump if no drive0
8483 mov bl, #0x07 ;; drive0 determined, multi-rate, has changed line
8484 jmp look_drive1
8485 f0_missing:
8486 mov bl, #0x00 ;; no drive0
8488 look_drive1:
8489 mov al, ah ;; restore from AH
8490 and al, #0x0f ;; look at bottom 4 bits for drive 1
8491 jz f1_missing ;; jump if no drive1
8492 or bl, #0x70 ;; drive1 determined, multi-rate, has changed line
8493 f1_missing:
8494 ;; leave high bits in BL zerod
8495 mov 0x048f, bl ;; put new val in BDA (diskette controller information)
8496 ;; -----------------------------------------------------------------
8498 mov al, #0x00
8499 mov 0x0490, al ;; diskette 0 media state
8500 mov 0x0491, al ;; diskette 1 media state
8502 ;; diskette 0,1 operational starting state
8503 ;; drive type has not been determined,
8504 ;; has no changed detection line
8505 mov 0x0492, al
8506 mov 0x0493, al
8508 mov 0x0494, al ;; diskette 0 current cylinder
8509 mov 0x0495, al ;; diskette 1 current cylinder
8511 mov al, #0x02
8512 out #0x0a, al ;; clear DMA-1 channel 2 mask bit
8514 SET_INT_VECTOR(0x1E, #0xF000, #diskette_param_table2)
8515 SET_INT_VECTOR(0x40, #0xF000, #int13_diskette)
8516 SET_INT_VECTOR(0x0E, #0xF000, #int0e_handler) ;; IRQ 6
8521 ;--------------------
8522 ;- POST: HARD DRIVE -
8523 ;--------------------
8524 ; relocated here because the primary POST area isnt big enough.
8525 hard_drive_post:
8526 // IRQ 14 = INT 76h
8527 // INT 76h calls INT 15h function ax=9100
8529 mov al, #0x0a ; 0000 1010 = reserved, disable IRQ 14
8530 mov dx, #0x03f6
8531 out dx, al
8533 xor ax, ax
8534 mov ds, ax
8535 mov 0x0474, al /* hard disk status of last operation */
8536 mov 0x0477, al /* hard disk port offset (XT only ???) */
8537 mov 0x048c, al /* hard disk status register */
8538 mov 0x048d, al /* hard disk error register */
8539 mov 0x048e, al /* hard disk task complete flag */
8540 mov al, #0x01
8541 mov 0x0475, al /* hard disk number attached */
8542 mov al, #0xc0
8543 mov 0x0476, al /* hard disk control byte */
8544 SET_INT_VECTOR(0x13, #0xF000, #int13_handler)
8545 SET_INT_VECTOR(0x76, #0xF000, #int76_handler)
8546 ;; INT 41h: hard disk 0 configuration pointer
8547 ;; INT 46h: hard disk 1 configuration pointer
8548 SET_INT_VECTOR(0x41, #EBDA_SEG, #0x003D)
8549 SET_INT_VECTOR(0x46, #EBDA_SEG, #0x004D)
8551 ;; move disk geometry data from CMOS to EBDA disk parameter table(s)
8552 mov al, #0x12
8553 out #0x70, al
8554 in al, #0x71
8555 and al, #0xf0
8556 cmp al, #0xf0
8557 je post_d0_extended
8558 jmp check_for_hd1
8559 post_d0_extended:
8560 mov al, #0x19
8561 out #0x70, al
8562 in al, #0x71
8563 cmp al, #47 ;; decimal 47 - user definable
8564 je post_d0_type47
8565 HALT(__LINE__)
8566 post_d0_type47:
8567 ;; CMOS purpose param table offset
8568 ;; 1b cylinders low 0
8569 ;; 1c cylinders high 1
8570 ;; 1d heads 2
8571 ;; 1e write pre-comp low 5
8572 ;; 1f write pre-comp high 6
8573 ;; 20 retries/bad map/heads>8 8
8574 ;; 21 landing zone low C
8575 ;; 22 landing zone high D
8576 ;; 23 sectors/track E
8578 mov ax, #EBDA_SEG
8579 mov ds, ax
8581 ;;; Filling EBDA table for hard disk 0.
8582 mov al, #0x1f
8583 out #0x70, al
8584 in al, #0x71
8585 mov ah, al
8586 mov al, #0x1e
8587 out #0x70, al
8588 in al, #0x71
8589 mov (0x003d + 0x05), ax ;; write precomp word
8591 mov al, #0x20
8592 out #0x70, al
8593 in al, #0x71
8594 mov (0x003d + 0x08), al ;; drive control byte
8596 mov al, #0x22
8597 out #0x70, al
8598 in al, #0x71
8599 mov ah, al
8600 mov al, #0x21
8601 out #0x70, al
8602 in al, #0x71
8603 mov (0x003d + 0x0C), ax ;; landing zone word
8605 mov al, #0x1c ;; get cylinders word in AX
8606 out #0x70, al
8607 in al, #0x71 ;; high byte
8608 mov ah, al
8609 mov al, #0x1b
8610 out #0x70, al
8611 in al, #0x71 ;; low byte
8612 mov bx, ax ;; BX = cylinders
8614 mov al, #0x1d
8615 out #0x70, al
8616 in al, #0x71
8617 mov cl, al ;; CL = heads
8619 mov al, #0x23
8620 out #0x70, al
8621 in al, #0x71
8622 mov dl, al ;; DL = sectors
8624 cmp bx, #1024
8625 jnbe hd0_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8627 hd0_post_physical_chs:
8628 ;; no logical CHS mapping used, just physical CHS
8629 ;; use Standard Fixed Disk Parameter Table (FDPT)
8630 mov (0x003d + 0x00), bx ;; number of physical cylinders
8631 mov (0x003d + 0x02), cl ;; number of physical heads
8632 mov (0x003d + 0x0E), dl ;; number of physical sectors
8633 jmp check_for_hd1
8635 hd0_post_logical_chs:
8636 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8637 mov (0x003d + 0x09), bx ;; number of physical cylinders
8638 mov (0x003d + 0x0b), cl ;; number of physical heads
8639 mov (0x003d + 0x04), dl ;; number of physical sectors
8640 mov (0x003d + 0x0e), dl ;; number of logical sectors (same)
8641 mov al, #0xa0
8642 mov (0x003d + 0x03), al ;; A0h signature, indicates translated table
8644 cmp bx, #2048
8645 jnbe hd0_post_above_2048
8646 ;; 1024 < c <= 2048 cylinders
8647 shr bx, #0x01
8648 shl cl, #0x01
8649 jmp hd0_post_store_logical
8651 hd0_post_above_2048:
8652 cmp bx, #4096
8653 jnbe hd0_post_above_4096
8654 ;; 2048 < c <= 4096 cylinders
8655 shr bx, #0x02
8656 shl cl, #0x02
8657 jmp hd0_post_store_logical
8659 hd0_post_above_4096:
8660 cmp bx, #8192
8661 jnbe hd0_post_above_8192
8662 ;; 4096 < c <= 8192 cylinders
8663 shr bx, #0x03
8664 shl cl, #0x03
8665 jmp hd0_post_store_logical
8667 hd0_post_above_8192:
8668 ;; 8192 < c <= 16384 cylinders
8669 shr bx, #0x04
8670 shl cl, #0x04
8672 hd0_post_store_logical:
8673 mov (0x003d + 0x00), bx ;; number of physical cylinders
8674 mov (0x003d + 0x02), cl ;; number of physical heads
8675 ;; checksum
8676 mov cl, #0x0f ;; repeat count
8677 mov si, #0x003d ;; offset to disk0 FDPT
8678 mov al, #0x00 ;; sum
8679 hd0_post_checksum_loop:
8680 add al, [si]
8681 inc si
8682 dec cl
8683 jnz hd0_post_checksum_loop
8684 not al ;; now take 2s complement
8685 inc al
8686 mov [si], al
8687 ;;; Done filling EBDA table for hard disk 0.
8690 check_for_hd1:
8691 ;; is there really a second hard disk? if not, return now
8692 mov al, #0x12
8693 out #0x70, al
8694 in al, #0x71
8695 and al, #0x0f
8696 jnz post_d1_exists
8698 post_d1_exists:
8699 ;; check that the hd type is really 0x0f.
8700 cmp al, #0x0f
8701 jz post_d1_extended
8702 HALT(__LINE__)
8703 post_d1_extended:
8704 ;; check that the extended type is 47 - user definable
8705 mov al, #0x1a
8706 out #0x70, al
8707 in al, #0x71
8708 cmp al, #47 ;; decimal 47 - user definable
8709 je post_d1_type47
8710 HALT(__LINE__)
8711 post_d1_type47:
8712 ;; Table for disk1.
8713 ;; CMOS purpose param table offset
8714 ;; 0x24 cylinders low 0
8715 ;; 0x25 cylinders high 1
8716 ;; 0x26 heads 2
8717 ;; 0x27 write pre-comp low 5
8718 ;; 0x28 write pre-comp high 6
8719 ;; 0x29 heads>8 8
8720 ;; 0x2a landing zone low C
8721 ;; 0x2b landing zone high D
8722 ;; 0x2c sectors/track E
8723 ;;; Fill EBDA table for hard disk 1.
8724 mov ax, #EBDA_SEG
8725 mov ds, ax
8726 mov al, #0x28
8727 out #0x70, al
8728 in al, #0x71
8729 mov ah, al
8730 mov al, #0x27
8731 out #0x70, al
8732 in al, #0x71
8733 mov (0x004d + 0x05), ax ;; write precomp word
8735 mov al, #0x29
8736 out #0x70, al
8737 in al, #0x71
8738 mov (0x004d + 0x08), al ;; drive control byte
8740 mov al, #0x2b
8741 out #0x70, al
8742 in al, #0x71
8743 mov ah, al
8744 mov al, #0x2a
8745 out #0x70, al
8746 in al, #0x71
8747 mov (0x004d + 0x0C), ax ;; landing zone word
8749 mov al, #0x25 ;; get cylinders word in AX
8750 out #0x70, al
8751 in al, #0x71 ;; high byte
8752 mov ah, al
8753 mov al, #0x24
8754 out #0x70, al
8755 in al, #0x71 ;; low byte
8756 mov bx, ax ;; BX = cylinders
8758 mov al, #0x26
8759 out #0x70, al
8760 in al, #0x71
8761 mov cl, al ;; CL = heads
8763 mov al, #0x2c
8764 out #0x70, al
8765 in al, #0x71
8766 mov dl, al ;; DL = sectors
8768 cmp bx, #1024
8769 jnbe hd1_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8771 hd1_post_physical_chs:
8772 ;; no logical CHS mapping used, just physical CHS
8773 ;; use Standard Fixed Disk Parameter Table (FDPT)
8774 mov (0x004d + 0x00), bx ;; number of physical cylinders
8775 mov (0x004d + 0x02), cl ;; number of physical heads
8776 mov (0x004d + 0x0E), dl ;; number of physical sectors
8779 hd1_post_logical_chs:
8780 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8781 mov (0x004d + 0x09), bx ;; number of physical cylinders
8782 mov (0x004d + 0x0b), cl ;; number of physical heads
8783 mov (0x004d + 0x04), dl ;; number of physical sectors
8784 mov (0x004d + 0x0e), dl ;; number of logical sectors (same)
8785 mov al, #0xa0
8786 mov (0x004d + 0x03), al ;; A0h signature, indicates translated table
8788 cmp bx, #2048
8789 jnbe hd1_post_above_2048
8790 ;; 1024 < c <= 2048 cylinders
8791 shr bx, #0x01
8792 shl cl, #0x01
8793 jmp hd1_post_store_logical
8795 hd1_post_above_2048:
8796 cmp bx, #4096
8797 jnbe hd1_post_above_4096
8798 ;; 2048 < c <= 4096 cylinders
8799 shr bx, #0x02
8800 shl cl, #0x02
8801 jmp hd1_post_store_logical
8803 hd1_post_above_4096:
8804 cmp bx, #8192
8805 jnbe hd1_post_above_8192
8806 ;; 4096 < c <= 8192 cylinders
8807 shr bx, #0x03
8808 shl cl, #0x03
8809 jmp hd1_post_store_logical
8811 hd1_post_above_8192:
8812 ;; 8192 < c <= 16384 cylinders
8813 shr bx, #0x04
8814 shl cl, #0x04
8816 hd1_post_store_logical:
8817 mov (0x004d + 0x00), bx ;; number of physical cylinders
8818 mov (0x004d + 0x02), cl ;; number of physical heads
8819 ;; checksum
8820 mov cl, #0x0f ;; repeat count
8821 mov si, #0x004d ;; offset to disk0 FDPT
8822 mov al, #0x00 ;; sum
8823 hd1_post_checksum_loop:
8824 add al, [si]
8825 inc si
8826 dec cl
8827 jnz hd1_post_checksum_loop
8828 not al ;; now take 2s complement
8829 inc al
8830 mov [si], al
8831 ;;; Done filling EBDA table for hard disk 1.
8835 ;--------------------
8836 ;- POST: EBDA segment
8837 ;--------------------
8838 ; relocated here because the primary POST area isnt big enough.
8839 ebda_post:
8840 #if BX_USE_EBDA
8841 mov ax, #EBDA_SEG
8842 mov ds, ax
8843 mov byte ptr [0x0], #EBDA_SIZE
8844 #endif
8845 xor ax, ax ; mov EBDA seg into 40E
8846 mov ds, ax
8847 mov word ptr [0x40E], #EBDA_SEG
8848 ret;;
8850 ;--------------------
8851 ;- POST: EOI + jmp via [0x40:67)
8852 ;--------------------
8853 ; relocated here because the primary POST area isnt big enough.
8854 eoi_jmp_post:
8855 call eoi_both_pics
8857 xor ax, ax
8858 mov ds, ax
8860 jmp far ptr [0x467]
8863 ;--------------------
8864 eoi_both_pics:
8865 mov al, #0x20
8866 out #0xA0, al ;; slave PIC EOI
8867 eoi_master_pic:
8868 mov al, #0x20
8869 out #0x20, al ;; master PIC EOI
8872 ;--------------------
8873 BcdToBin:
8874 ;; in: AL in BCD format
8875 ;; out: AL in binary format, AH will always be 0
8876 ;; trashes BX
8877 mov bl, al
8878 and bl, #0x0f ;; bl has low digit
8879 shr al, #4 ;; al has high digit
8880 mov bh, #10
8881 mul al, bh ;; multiply high digit by 10 (result in AX)
8882 add al, bl ;; then add low digit
8885 ;--------------------
8886 timer_tick_post:
8887 ;; Setup the Timer Ticks Count (0x46C:dword) and
8888 ;; Timer Ticks Roller Flag (0x470:byte)
8889 ;; The Timer Ticks Count needs to be set according to
8890 ;; the current CMOS time, as if ticks have been occurring
8891 ;; at 18.2hz since midnight up to this point. Calculating
8892 ;; this is a little complicated. Here are the factors I gather
8893 ;; regarding this. 14,318,180 hz was the original clock speed,
8894 ;; chosen so it could be divided by either 3 to drive the 5Mhz CPU
8895 ;; at the time, or 4 to drive the CGA video adapter. The div3
8896 ;; source was divided again by 4 to feed a 1.193Mhz signal to
8897 ;; the timer. With a maximum 16bit timer count, this is again
8898 ;; divided down by 65536 to 18.2hz.
8900 ;; 14,318,180 Hz clock
8901 ;; /3 = 4,772,726 Hz fed to orginal 5Mhz CPU
8902 ;; /4 = 1,193,181 Hz fed to timer
8903 ;; /65536 (maximum timer count) = 18.20650736 ticks/second
8904 ;; 1 second = 18.20650736 ticks
8905 ;; 1 minute = 1092.390442 ticks
8906 ;; 1 hour = 65543.42651 ticks
8908 ;; Given the values in the CMOS clock, one could calculate
8909 ;; the number of ticks by the following:
8910 ;; ticks = (BcdToBin(seconds) * 18.206507) +
8911 ;; (BcdToBin(minutes) * 1092.3904)
8912 ;; (BcdToBin(hours) * 65543.427)
8913 ;; To get a little more accuracy, since Im using integer
8914 ;; arithmatic, I use:
8915 ;; ticks = (BcdToBin(seconds) * 18206507) / 1000000 +
8916 ;; (BcdToBin(minutes) * 10923904) / 10000 +
8917 ;; (BcdToBin(hours) * 65543427) / 1000
8919 ;; assuming DS=0000
8921 ;; get CMOS seconds
8922 xor eax, eax ;; clear EAX
8923 mov al, #0x00
8924 out #0x70, al
8925 in al, #0x71 ;; AL has CMOS seconds in BCD
8926 call BcdToBin ;; EAX now has seconds in binary
8927 mov edx, #18206507
8928 mul eax, edx
8929 mov ebx, #1000000
8930 xor edx, edx
8931 div eax, ebx
8932 mov ecx, eax ;; ECX will accumulate total ticks
8934 ;; get CMOS minutes
8935 xor eax, eax ;; clear EAX
8936 mov al, #0x02
8937 out #0x70, al
8938 in al, #0x71 ;; AL has CMOS minutes in BCD
8939 call BcdToBin ;; EAX now has minutes in binary
8940 mov edx, #10923904
8941 mul eax, edx
8942 mov ebx, #10000
8943 xor edx, edx
8944 div eax, ebx
8945 add ecx, eax ;; add to total ticks
8947 ;; get CMOS hours
8948 xor eax, eax ;; clear EAX
8949 mov al, #0x04
8950 out #0x70, al
8951 in al, #0x71 ;; AL has CMOS hours in BCD
8952 call BcdToBin ;; EAX now has hours in binary
8953 mov edx, #65543427
8954 mul eax, edx
8955 mov ebx, #1000
8956 xor edx, edx
8957 div eax, ebx
8958 add ecx, eax ;; add to total ticks
8960 mov 0x46C, ecx ;; Timer Ticks Count
8961 xor al, al
8962 mov 0x470, al ;; Timer Ticks Rollover Flag
8965 ;--------------------
8966 int76_handler:
8967 ;; record completion in BIOS task complete flag
8968 push ax
8969 push ds
8970 mov ax, #0x0040
8971 mov ds, ax
8972 mov 0x008E, #0xff
8973 call eoi_both_pics
8974 pop ds
8975 pop ax
8976 iret
8979 ;--------------------
8980 #if BX_APM
8982 use32 386
8983 #define APM_PROT32
8984 #include "apmbios.S"
8986 use16 386
8987 #define APM_PROT16
8988 #include "apmbios.S"
8990 #define APM_REAL
8991 #include "apmbios.S"
8993 #endif
8995 ;--------------------
8996 #if BX_PCIBIOS
8997 use32 386
8998 .align 16
8999 bios32_structure:
9000 db 0x5f, 0x33, 0x32, 0x5f ;; "_32_" signature
9001 dw bios32_entry_point, 0xf ;; 32 bit physical address
9002 db 0 ;; revision level
9003 ;; length in paragraphs and checksum stored in a word to prevent errors
9004 dw (~(((bios32_entry_point >> 8) + (bios32_entry_point & 0xff) + 0x32) \
9005 & 0xff) << 8) + 0x01
9006 db 0,0,0,0,0 ;; reserved
9008 .align 16
9009 bios32_entry_point:
9010 pushfd
9011 cmp eax, #0x49435024 ;; "$PCI"
9012 jne unknown_service
9013 mov eax, #0x80000000
9014 mov dx, #0x0cf8
9015 out dx, eax
9016 mov dx, #0x0cfc
9017 in eax, dx
9018 #ifdef PCI_FIXED_HOST_BRIDGE
9019 cmp eax, #PCI_FIXED_HOST_BRIDGE
9020 jne unknown_service
9021 #else
9022 ;; say ok if a device is present
9023 cmp eax, #0xffffffff
9024 je unknown_service
9025 #endif
9026 mov ebx, #0x000f0000
9027 mov ecx, #0
9028 mov edx, #pcibios_protected
9029 xor al, al
9030 jmp bios32_end
9031 unknown_service:
9032 mov al, #0x80
9033 bios32_end:
9034 #ifdef BX_QEMU
9035 and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
9036 #endif
9037 popfd
9038 retf
9040 .align 16
9041 pcibios_protected:
9042 pushfd
9044 push esi
9045 push edi
9046 cmp al, #0x01 ;; installation check
9047 jne pci_pro_f02
9048 mov bx, #0x0210
9049 mov cx, #0
9050 mov edx, #0x20494350 ;; "PCI "
9051 mov al, #0x01
9052 jmp pci_pro_ok
9053 pci_pro_f02: ;; find pci device
9054 cmp al, #0x02
9055 jne pci_pro_f03
9056 shl ecx, #16
9057 mov cx, dx
9058 xor bx, bx
9059 mov di, #0x00
9060 pci_pro_devloop:
9061 call pci_pro_select_reg
9062 mov dx, #0x0cfc
9063 in eax, dx
9064 cmp eax, ecx
9065 jne pci_pro_nextdev
9066 cmp si, #0
9067 je pci_pro_ok
9068 dec si
9069 pci_pro_nextdev:
9070 inc bx
9071 cmp bx, #0x0100
9072 jne pci_pro_devloop
9073 mov ah, #0x86
9074 jmp pci_pro_fail
9075 pci_pro_f03: ;; find class code
9076 cmp al, #0x03
9077 jne pci_pro_f08
9078 xor bx, bx
9079 mov di, #0x08
9080 pci_pro_devloop2:
9081 call pci_pro_select_reg
9082 mov dx, #0x0cfc
9083 in eax, dx
9084 shr eax, #8
9085 cmp eax, ecx
9086 jne pci_pro_nextdev2
9087 cmp si, #0
9088 je pci_pro_ok
9089 dec si
9090 pci_pro_nextdev2:
9091 inc bx
9092 cmp bx, #0x0100
9093 jne pci_pro_devloop2
9094 mov ah, #0x86
9095 jmp pci_pro_fail
9096 pci_pro_f08: ;; read configuration byte
9097 cmp al, #0x08
9098 jne pci_pro_f09
9099 call pci_pro_select_reg
9100 push edx
9101 mov dx, di
9102 and dx, #0x03
9103 add dx, #0x0cfc
9104 in al, dx
9105 pop edx
9106 mov cl, al
9107 jmp pci_pro_ok
9108 pci_pro_f09: ;; read configuration word
9109 cmp al, #0x09
9110 jne pci_pro_f0a
9111 call pci_pro_select_reg
9112 push edx
9113 mov dx, di
9114 and dx, #0x02
9115 add dx, #0x0cfc
9116 in ax, dx
9117 pop edx
9118 mov cx, ax
9119 jmp pci_pro_ok
9120 pci_pro_f0a: ;; read configuration dword
9121 cmp al, #0x0a
9122 jne pci_pro_f0b
9123 call pci_pro_select_reg
9124 push edx
9125 mov dx, #0x0cfc
9126 in eax, dx
9127 pop edx
9128 mov ecx, eax
9129 jmp pci_pro_ok
9130 pci_pro_f0b: ;; write configuration byte
9131 cmp al, #0x0b
9132 jne pci_pro_f0c
9133 call pci_pro_select_reg
9134 push edx
9135 mov dx, di
9136 and dx, #0x03
9137 add dx, #0x0cfc
9138 mov al, cl
9139 out dx, al
9140 pop edx
9141 jmp pci_pro_ok
9142 pci_pro_f0c: ;; write configuration word
9143 cmp al, #0x0c
9144 jne pci_pro_f0d
9145 call pci_pro_select_reg
9146 push edx
9147 mov dx, di
9148 and dx, #0x02
9149 add dx, #0x0cfc
9150 mov ax, cx
9151 out dx, ax
9152 pop edx
9153 jmp pci_pro_ok
9154 pci_pro_f0d: ;; write configuration dword
9155 cmp al, #0x0d
9156 jne pci_pro_unknown
9157 call pci_pro_select_reg
9158 push edx
9159 mov dx, #0x0cfc
9160 mov eax, ecx
9161 out dx, eax
9162 pop edx
9163 jmp pci_pro_ok
9164 pci_pro_unknown:
9165 mov ah, #0x81
9166 pci_pro_fail:
9167 pop edi
9168 pop esi
9169 #ifdef BX_QEMU
9170 and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
9171 #endif
9172 popfd
9174 retf
9175 pci_pro_ok:
9176 xor ah, ah
9177 pop edi
9178 pop esi
9179 #ifdef BX_QEMU
9180 and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
9181 #endif
9182 popfd
9184 retf
9186 pci_pro_select_reg:
9187 push edx
9188 mov eax, #0x800000
9189 mov ax, bx
9190 shl eax, #8
9191 and di, #0xff
9192 or ax, di
9193 and al, #0xfc
9194 mov dx, #0x0cf8
9195 out dx, eax
9196 pop edx
9199 use16 386
9201 pcibios_real:
9202 push eax
9203 push dx
9204 mov eax, #0x80000000
9205 mov dx, #0x0cf8
9206 out dx, eax
9207 mov dx, #0x0cfc
9208 in eax, dx
9209 #ifdef PCI_FIXED_HOST_BRIDGE
9210 cmp eax, #PCI_FIXED_HOST_BRIDGE
9211 je pci_present
9212 #else
9213 ;; say ok if a device is present
9214 cmp eax, #0xffffffff
9215 jne pci_present
9216 #endif
9217 pop dx
9218 pop eax
9219 mov ah, #0xff
9222 pci_present:
9223 pop dx
9224 pop eax
9225 cmp al, #0x01 ;; installation check
9226 jne pci_real_f02
9227 mov ax, #0x0001
9228 mov bx, #0x0210
9229 mov cx, #0
9230 mov edx, #0x20494350 ;; "PCI "
9231 mov edi, #0xf0000
9232 mov di, #pcibios_protected
9235 pci_real_f02: ;; find pci device
9236 push esi
9237 push edi
9238 cmp al, #0x02
9239 jne pci_real_f03
9240 shl ecx, #16
9241 mov cx, dx
9242 xor bx, bx
9243 mov di, #0x00
9244 pci_real_devloop:
9245 call pci_real_select_reg
9246 mov dx, #0x0cfc
9247 in eax, dx
9248 cmp eax, ecx
9249 jne pci_real_nextdev
9250 cmp si, #0
9251 je pci_real_ok
9252 dec si
9253 pci_real_nextdev:
9254 inc bx
9255 cmp bx, #0x0100
9256 jne pci_real_devloop
9257 mov dx, cx
9258 shr ecx, #16
9259 mov ax, #0x8602
9260 jmp pci_real_fail
9261 pci_real_f03: ;; find class code
9262 cmp al, #0x03
9263 jne pci_real_f08
9264 xor bx, bx
9265 mov di, #0x08
9266 pci_real_devloop2:
9267 call pci_real_select_reg
9268 mov dx, #0x0cfc
9269 in eax, dx
9270 shr eax, #8
9271 cmp eax, ecx
9272 jne pci_real_nextdev2
9273 cmp si, #0
9274 je pci_real_ok
9275 dec si
9276 pci_real_nextdev2:
9277 inc bx
9278 cmp bx, #0x0100
9279 jne pci_real_devloop2
9280 mov dx, cx
9281 shr ecx, #16
9282 mov ax, #0x8603
9283 jmp pci_real_fail
9284 pci_real_f08: ;; read configuration byte
9285 cmp al, #0x08
9286 jne pci_real_f09
9287 call pci_real_select_reg
9288 push dx
9289 mov dx, di
9290 and dx, #0x03
9291 add dx, #0x0cfc
9292 in al, dx
9293 pop dx
9294 mov cl, al
9295 jmp pci_real_ok
9296 pci_real_f09: ;; read configuration word
9297 cmp al, #0x09
9298 jne pci_real_f0a
9299 call pci_real_select_reg
9300 push dx
9301 mov dx, di
9302 and dx, #0x02
9303 add dx, #0x0cfc
9304 in ax, dx
9305 pop dx
9306 mov cx, ax
9307 jmp pci_real_ok
9308 pci_real_f0a: ;; read configuration dword
9309 cmp al, #0x0a
9310 jne pci_real_f0b
9311 call pci_real_select_reg
9312 push dx
9313 mov dx, #0x0cfc
9314 in eax, dx
9315 pop dx
9316 mov ecx, eax
9317 jmp pci_real_ok
9318 pci_real_f0b: ;; write configuration byte
9319 cmp al, #0x0b
9320 jne pci_real_f0c
9321 call pci_real_select_reg
9322 push dx
9323 mov dx, di
9324 and dx, #0x03
9325 add dx, #0x0cfc
9326 mov al, cl
9327 out dx, al
9328 pop dx
9329 jmp pci_real_ok
9330 pci_real_f0c: ;; write configuration word
9331 cmp al, #0x0c
9332 jne pci_real_f0d
9333 call pci_real_select_reg
9334 push dx
9335 mov dx, di
9336 and dx, #0x02
9337 add dx, #0x0cfc
9338 mov ax, cx
9339 out dx, ax
9340 pop dx
9341 jmp pci_real_ok
9342 pci_real_f0d: ;; write configuration dword
9343 cmp al, #0x0d
9344 jne pci_real_f0e
9345 call pci_real_select_reg
9346 push dx
9347 mov dx, #0x0cfc
9348 mov eax, ecx
9349 out dx, eax
9350 pop dx
9351 jmp pci_real_ok
9352 pci_real_f0e: ;; get irq routing options
9353 cmp al, #0x0e
9354 jne pci_real_unknown
9355 SEG ES
9356 cmp word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9357 jb pci_real_too_small
9358 SEG ES
9359 mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9360 pushf
9361 push ds
9362 push es
9363 push cx
9364 push si
9365 push di
9367 mov si, #pci_routing_table_structure_start
9368 push cs
9369 pop ds
9370 SEG ES
9371 mov cx, [di+2]
9372 SEG ES
9373 mov es, [di+4]
9374 mov di, cx
9375 mov cx, #pci_routing_table_structure_end - pci_routing_table_structure_start
9377 movsb
9378 pop di
9379 pop si
9380 pop cx
9381 pop es
9382 pop ds
9383 popf
9384 mov bx, #(1 << 9) | (1 << 11) ;; irq 9 and 11 are used
9385 jmp pci_real_ok
9386 pci_real_too_small:
9387 SEG ES
9388 mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9389 mov ah, #0x89
9390 jmp pci_real_fail
9392 pci_real_unknown:
9393 mov ah, #0x81
9394 pci_real_fail:
9395 pop edi
9396 pop esi
9399 pci_real_ok:
9400 xor ah, ah
9401 pop edi
9402 pop esi
9406 pci_real_select_reg:
9407 push dx
9408 mov eax, #0x800000
9409 mov ax, bx
9410 shl eax, #8
9411 and di, #0xff
9412 or ax, di
9413 and al, #0xfc
9414 mov dx, #0x0cf8
9415 out dx, eax
9416 pop dx
9419 .align 16
9420 pci_routing_table_structure:
9421 db 0x24, 0x50, 0x49, 0x52 ;; "$PIR" signature
9422 db 0, 1 ;; version
9423 dw 32 + (6 * 16) ;; table size
9424 db 0 ;; PCI interrupt router bus
9425 db 0x08 ;; PCI interrupt router DevFunc
9426 dw 0x0000 ;; PCI exclusive IRQs
9427 dw 0x8086 ;; compatible PCI interrupt router vendor ID
9428 dw 0x7000 ;; compatible PCI interrupt router device ID
9429 dw 0,0 ;; Miniport data
9430 db 0,0,0,0,0,0,0,0,0,0,0 ;; reserved
9431 db 0x07 ;; checksum
9432 pci_routing_table_structure_start:
9433 ;; first slot entry PCI-to-ISA (embedded)
9434 db 0 ;; pci bus number
9435 db 0x08 ;; pci device number (bit 7-3)
9436 db 0x60 ;; link value INTA#: pointer into PCI2ISA config space
9437 dw 0xdef8 ;; IRQ bitmap INTA#
9438 db 0x61 ;; link value INTB#
9439 dw 0xdef8 ;; IRQ bitmap INTB#
9440 db 0x62 ;; link value INTC#
9441 dw 0xdef8 ;; IRQ bitmap INTC#
9442 db 0x63 ;; link value INTD#
9443 dw 0xdef8 ;; IRQ bitmap INTD#
9444 db 0 ;; physical slot (0 = embedded)
9445 db 0 ;; reserved
9446 ;; second slot entry: 1st PCI slot
9447 db 0 ;; pci bus number
9448 db 0x10 ;; pci device number (bit 7-3)
9449 db 0x61 ;; link value INTA#
9450 dw 0xdef8 ;; IRQ bitmap INTA#
9451 db 0x62 ;; link value INTB#
9452 dw 0xdef8 ;; IRQ bitmap INTB#
9453 db 0x63 ;; link value INTC#
9454 dw 0xdef8 ;; IRQ bitmap INTC#
9455 db 0x60 ;; link value INTD#
9456 dw 0xdef8 ;; IRQ bitmap INTD#
9457 db 1 ;; physical slot (0 = embedded)
9458 db 0 ;; reserved
9459 ;; third slot entry: 2nd PCI slot
9460 db 0 ;; pci bus number
9461 db 0x18 ;; pci device number (bit 7-3)
9462 db 0x62 ;; link value INTA#
9463 dw 0xdef8 ;; IRQ bitmap INTA#
9464 db 0x63 ;; link value INTB#
9465 dw 0xdef8 ;; IRQ bitmap INTB#
9466 db 0x60 ;; link value INTC#
9467 dw 0xdef8 ;; IRQ bitmap INTC#
9468 db 0x61 ;; link value INTD#
9469 dw 0xdef8 ;; IRQ bitmap INTD#
9470 db 2 ;; physical slot (0 = embedded)
9471 db 0 ;; reserved
9472 ;; 4th slot entry: 3rd PCI slot
9473 db 0 ;; pci bus number
9474 db 0x20 ;; pci device number (bit 7-3)
9475 db 0x63 ;; link value INTA#
9476 dw 0xdef8 ;; IRQ bitmap INTA#
9477 db 0x60 ;; link value INTB#
9478 dw 0xdef8 ;; IRQ bitmap INTB#
9479 db 0x61 ;; link value INTC#
9480 dw 0xdef8 ;; IRQ bitmap INTC#
9481 db 0x62 ;; link value INTD#
9482 dw 0xdef8 ;; IRQ bitmap INTD#
9483 db 3 ;; physical slot (0 = embedded)
9484 db 0 ;; reserved
9485 ;; 5th slot entry: 4rd PCI slot
9486 db 0 ;; pci bus number
9487 db 0x28 ;; pci device number (bit 7-3)
9488 db 0x60 ;; link value INTA#
9489 dw 0xdef8 ;; IRQ bitmap INTA#
9490 db 0x61 ;; link value INTB#
9491 dw 0xdef8 ;; IRQ bitmap INTB#
9492 db 0x62 ;; link value INTC#
9493 dw 0xdef8 ;; IRQ bitmap INTC#
9494 db 0x63 ;; link value INTD#
9495 dw 0xdef8 ;; IRQ bitmap INTD#
9496 db 4 ;; physical slot (0 = embedded)
9497 db 0 ;; reserved
9498 ;; 6th slot entry: 5rd PCI slot
9499 db 0 ;; pci bus number
9500 db 0x30 ;; pci device number (bit 7-3)
9501 db 0x61 ;; link value INTA#
9502 dw 0xdef8 ;; IRQ bitmap INTA#
9503 db 0x62 ;; link value INTB#
9504 dw 0xdef8 ;; IRQ bitmap INTB#
9505 db 0x63 ;; link value INTC#
9506 dw 0xdef8 ;; IRQ bitmap INTC#
9507 db 0x60 ;; link value INTD#
9508 dw 0xdef8 ;; IRQ bitmap INTD#
9509 db 5 ;; physical slot (0 = embedded)
9510 db 0 ;; reserved
9511 pci_routing_table_structure_end:
9513 #if !BX_ROMBIOS32
9514 pci_irq_list:
9515 db 11, 10, 9, 5;
9517 pcibios_init_sel_reg:
9518 push eax
9519 mov eax, #0x800000
9520 mov ax, bx
9521 shl eax, #8
9522 and dl, #0xfc
9523 or al, dl
9524 mov dx, #0x0cf8
9525 out dx, eax
9526 pop eax
9529 pcibios_init_iomem_bases:
9530 push bp
9531 mov bp, sp
9532 mov eax, #0xe0000000 ;; base for memory init
9533 push eax
9534 mov ax, #0xc000 ;; base for i/o init
9535 push ax
9536 mov ax, #0x0010 ;; start at base address #0
9537 push ax
9538 mov bx, #0x0008
9539 pci_init_io_loop1:
9540 mov dl, #0x00
9541 call pcibios_init_sel_reg
9542 mov dx, #0x0cfc
9543 in ax, dx
9544 cmp ax, #0xffff
9545 jz next_pci_dev
9546 mov dl, #0x04 ;; disable i/o and memory space access
9547 call pcibios_init_sel_reg
9548 mov dx, #0x0cfc
9549 in al, dx
9550 and al, #0xfc
9551 out dx, al
9552 pci_init_io_loop2:
9553 mov dl, [bp-8]
9554 call pcibios_init_sel_reg
9555 mov dx, #0x0cfc
9556 in eax, dx
9557 test al, #0x01
9558 jnz init_io_base
9559 mov ecx, eax
9560 mov eax, #0xffffffff
9561 out dx, eax
9562 in eax, dx
9563 cmp eax, ecx
9564 je next_pci_base
9565 xor eax, #0xffffffff
9566 mov ecx, eax
9567 mov eax, [bp-4]
9568 out dx, eax
9569 add eax, ecx ;; calculate next free mem base
9570 add eax, #0x01000000
9571 and eax, #0xff000000
9572 mov [bp-4], eax
9573 jmp next_pci_base
9574 init_io_base:
9575 mov cx, ax
9576 mov ax, #0xffff
9577 out dx, ax
9578 in ax, dx
9579 cmp ax, cx
9580 je next_pci_base
9581 xor ax, #0xfffe
9582 mov cx, ax
9583 mov ax, [bp-6]
9584 out dx, ax
9585 add ax, cx ;; calculate next free i/o base
9586 add ax, #0x0100
9587 and ax, #0xff00
9588 mov [bp-6], ax
9589 next_pci_base:
9590 mov al, [bp-8]
9591 add al, #0x04
9592 cmp al, #0x28
9593 je enable_iomem_space
9594 mov byte ptr[bp-8], al
9595 jmp pci_init_io_loop2
9596 enable_iomem_space:
9597 mov dl, #0x04 ;; enable i/o and memory space access if available
9598 call pcibios_init_sel_reg
9599 mov dx, #0x0cfc
9600 in al, dx
9601 or al, #0x07
9602 out dx, al
9603 next_pci_dev:
9604 mov byte ptr[bp-8], #0x10
9605 inc bx
9606 cmp bx, #0x0100
9607 jne pci_init_io_loop1
9608 mov sp, bp
9609 pop bp
9612 pcibios_init_set_elcr:
9613 push ax
9614 push cx
9615 mov dx, #0x04d0
9616 test al, #0x08
9617 jz is_master_pic
9618 inc dx
9619 and al, #0x07
9620 is_master_pic:
9621 mov cl, al
9622 mov bl, #0x01
9623 shl bl, cl
9624 in al, dx
9625 or al, bl
9626 out dx, al
9627 pop cx
9628 pop ax
9631 pcibios_init_irqs:
9632 push ds
9633 push bp
9634 mov ax, #0xf000
9635 mov ds, ax
9636 mov dx, #0x04d0 ;; reset ELCR1 + ELCR2
9637 mov al, #0x00
9638 out dx, al
9639 inc dx
9640 out dx, al
9641 mov si, #pci_routing_table_structure
9642 mov bh, [si+8]
9643 mov bl, [si+9]
9644 mov dl, #0x00
9645 call pcibios_init_sel_reg
9646 mov dx, #0x0cfc
9647 in eax, dx
9648 cmp eax, [si+12] ;; check irq router
9649 jne pci_init_end
9650 mov dl, [si+34]
9651 call pcibios_init_sel_reg
9652 push bx ;; save irq router bus + devfunc
9653 mov dx, #0x0cfc
9654 mov ax, #0x8080
9655 out dx, ax ;; reset PIRQ route control
9656 add dx, #2
9657 out dx, ax
9658 mov ax, [si+6]
9659 sub ax, #0x20
9660 shr ax, #4
9661 mov cx, ax
9662 add si, #0x20 ;; set pointer to 1st entry
9663 mov bp, sp
9664 mov ax, #pci_irq_list
9665 push ax
9666 xor ax, ax
9667 push ax
9668 pci_init_irq_loop1:
9669 mov bh, [si]
9670 mov bl, [si+1]
9671 pci_init_irq_loop2:
9672 mov dl, #0x00
9673 call pcibios_init_sel_reg
9674 mov dx, #0x0cfc
9675 in ax, dx
9676 cmp ax, #0xffff
9677 jnz pci_test_int_pin
9678 test bl, #0x07
9679 jz next_pir_entry
9680 jmp next_pci_func
9681 pci_test_int_pin:
9682 mov dl, #0x3c
9683 call pcibios_init_sel_reg
9684 mov dx, #0x0cfd
9685 in al, dx
9686 and al, #0x07
9687 jz next_pci_func
9688 dec al ;; determine pirq reg
9689 mov dl, #0x03
9690 mul al, dl
9691 add al, #0x02
9692 xor ah, ah
9693 mov bx, ax
9694 mov al, [si+bx]
9695 mov dl, al
9696 mov bx, [bp]
9697 call pcibios_init_sel_reg
9698 mov dx, #0x0cfc
9699 and al, #0x03
9700 add dl, al
9701 in al, dx
9702 cmp al, #0x80
9703 jb pirq_found
9704 mov bx, [bp-2] ;; pci irq list pointer
9705 mov al, [bx]
9706 out dx, al
9707 inc bx
9708 mov [bp-2], bx
9709 call pcibios_init_set_elcr
9710 pirq_found:
9711 mov bh, [si]
9712 mov bl, [si+1]
9713 add bl, [bp-3] ;; pci function number
9714 mov dl, #0x3c
9715 call pcibios_init_sel_reg
9716 mov dx, #0x0cfc
9717 out dx, al
9718 next_pci_func:
9719 inc byte ptr[bp-3]
9720 inc bl
9721 test bl, #0x07
9722 jnz pci_init_irq_loop2
9723 next_pir_entry:
9724 add si, #0x10
9725 mov byte ptr[bp-3], #0x00
9726 loop pci_init_irq_loop1
9727 mov sp, bp
9728 pop bx
9729 pci_init_end:
9730 pop bp
9731 pop ds
9733 #endif // BX_ROMBIOS32
9734 #endif // BX_PCIBIOS
9736 #if BX_ROMBIOS32
9737 rombios32_init:
9738 ;; save a20 and enable it
9739 in al, 0x92
9740 push ax
9741 or al, #0x02
9742 out 0x92, al
9744 ;; save SS:SP to the BDA
9745 xor ax, ax
9746 mov ds, ax
9747 mov 0x0469, ss
9748 mov 0x0467, sp
9750 SEG CS
9751 lidt [pmode_IDT_info]
9752 SEG CS
9753 lgdt [rombios32_gdt_48]
9754 ;; set PE bit in CR0
9755 mov eax, cr0
9756 or al, #0x01
9757 mov cr0, eax
9758 ;; start protected mode code: ljmpl 0x10:rombios32_init1
9759 db 0x66, 0xea
9760 dw rombios32_05
9761 dw 0x000f ;; high 16 bit address
9762 dw 0x0010
9764 use32 386
9765 rombios32_05:
9766 ;; init data segments
9767 mov eax, #0x18
9768 mov ds, ax
9769 mov es, ax
9770 mov ss, ax
9771 xor eax, eax
9772 mov fs, ax
9773 mov gs, ax
9776 ;; copy rombios32 code to ram (ram offset = 1MB)
9777 mov esi, #0xfffe0000
9778 mov edi, #0x00040000
9779 mov ecx, #0x10000 / 4
9781 movsd
9783 ;; init the stack pointer
9784 mov esp, #0x00080000
9786 ;; call rombios32 code
9787 mov eax, #0x00040000
9788 call eax
9790 ;; reset the memory (some boot loaders such as syslinux suppose
9791 ;; that the memory is set to zero)
9792 mov edi, #0x00040000
9793 mov ecx, #0x40000 / 4
9794 xor eax, eax
9796 stosd
9798 ;; return to 16 bit protected mode first
9799 db 0xea
9800 dd rombios32_10
9801 dw 0x20
9803 use16 386
9804 rombios32_10:
9805 ;; restore data segment limits to 0xffff
9806 mov ax, #0x28
9807 mov ds, ax
9808 mov es, ax
9809 mov ss, ax
9810 mov fs, ax
9811 mov gs, ax
9813 ;; reset PE bit in CR0
9814 mov eax, cr0
9815 and al, #0xFE
9816 mov cr0, eax
9818 ;; far jump to flush CPU queue after transition to real mode
9819 JMP_AP(0xf000, rombios32_real_mode)
9821 rombios32_real_mode:
9822 ;; restore IDT to normal real-mode defaults
9823 SEG CS
9824 lidt [rmode_IDT_info]
9826 xor ax, ax
9827 mov ds, ax
9828 mov es, ax
9829 mov fs, ax
9830 mov gs, ax
9832 ;; restore SS:SP from the BDA
9833 mov ss, 0x0469
9834 xor esp, esp
9835 mov sp, 0x0467
9836 ;; restore a20
9837 pop ax
9838 out 0x92, al
9841 rombios32_gdt_48:
9842 dw 0x30
9843 dw rombios32_gdt
9844 dw 0x000f
9846 rombios32_gdt:
9847 dw 0, 0, 0, 0
9848 dw 0, 0, 0, 0
9849 dw 0xffff, 0, 0x9b00, 0x00cf ; 32 bit flat code segment (0x10)
9850 dw 0xffff, 0, 0x9300, 0x00cf ; 32 bit flat data segment (0x18)
9851 dw 0xffff, 0, 0x9b0f, 0x0000 ; 16 bit code segment base=0xf0000 limit=0xffff
9852 dw 0xffff, 0, 0x9300, 0x0000 ; 16 bit data segment base=0x0 limit=0xffff
9853 #endif
9856 ; parallel port detection: base address in DX, index in BX, timeout in CL
9857 detect_parport:
9858 push dx
9859 add dx, #2
9860 in al, dx
9861 and al, #0xdf ; clear input mode
9862 out dx, al
9863 pop dx
9864 mov al, #0xaa
9865 out dx, al
9866 in al, dx
9867 cmp al, #0xaa
9868 jne no_parport
9869 push bx
9870 shl bx, #1
9871 mov [bx+0x408], dx ; Parallel I/O address
9872 pop bx
9873 mov [bx+0x478], cl ; Parallel printer timeout
9874 inc bx
9875 no_parport:
9878 ; serial port detection: base address in DX, index in BX, timeout in CL
9879 detect_serial:
9880 push dx
9881 inc dx
9882 mov al, #0x02
9883 out dx, al
9884 in al, dx
9885 cmp al, #0x02
9886 jne no_serial
9887 inc dx
9888 in al, dx
9889 cmp al, #0x02
9890 jne no_serial
9891 dec dx
9892 xor al, al
9893 out dx, al
9894 pop dx
9895 push bx
9896 shl bx, #1
9897 mov [bx+0x400], dx ; Serial I/O address
9898 pop bx
9899 mov [bx+0x47c], cl ; Serial timeout
9900 inc bx
9902 no_serial:
9903 pop dx
9906 rom_checksum:
9907 push ax
9908 push bx
9909 push cx
9910 xor ax, ax
9911 xor bx, bx
9912 xor cx, cx
9913 mov ch, [2]
9914 shl cx, #1
9915 checksum_loop:
9916 add al, [bx]
9917 inc bx
9918 loop checksum_loop
9919 and al, #0xff
9920 pop cx
9921 pop bx
9922 pop ax
9926 ;; We need a copy of this string, but we are not actually a PnP BIOS,
9927 ;; so make sure it is *not* aligned, so OSes will not see it if they scan.
9928 .align 16
9929 db 0
9930 pnp_string:
9931 .ascii "$PnP"
9934 rom_scan:
9935 ;; Scan for existence of valid expansion ROMS.
9936 ;; Video ROM: from 0xC0000..0xC7FFF in 2k increments
9937 ;; General ROM: from 0xC8000..0xDFFFF in 2k increments
9938 ;; System ROM: only 0xE0000
9940 ;; Header:
9941 ;; Offset Value
9942 ;; 0 0x55
9943 ;; 1 0xAA
9944 ;; 2 ROM length in 512-byte blocks
9945 ;; 3 ROM initialization entry point (FAR CALL)
9947 rom_scan_loop:
9948 push ax ;; Save AX
9949 mov ds, cx
9950 mov ax, #0x0004 ;; start with increment of 4 (512-byte) blocks = 2k
9951 cmp [0], #0xAA55 ;; look for signature
9952 jne rom_scan_increment
9953 call rom_checksum
9954 jnz rom_scan_increment
9955 mov al, [2] ;; change increment to ROM length in 512-byte blocks
9957 ;; We want our increment in 512-byte quantities, rounded to
9958 ;; the nearest 2k quantity, since we only scan at 2k intervals.
9959 test al, #0x03
9960 jz block_count_rounded
9961 and al, #0xfc ;; needs rounding up
9962 add al, #0x04
9963 block_count_rounded:
9965 xor bx, bx ;; Restore DS back to 0000:
9966 mov ds, bx
9967 push ax ;; Save AX
9968 push di ;; Save DI
9969 ;; Push addr of ROM entry point
9970 push cx ;; Push seg
9971 push #0x0003 ;; Push offset
9973 ;; Point ES:DI at "$PnP", which tells the ROM that we are a PnP BIOS.
9974 ;; That should stop it grabbing INT 19h; we will use its BEV instead.
9975 mov ax, #0xf000
9976 mov es, ax
9977 lea di, pnp_string
9979 mov bp, sp ;; Call ROM init routine using seg:off on stack
9980 db 0xff ;; call_far ss:[bp+0]
9981 db 0x5e
9982 db 0
9983 cli ;; In case expansion ROM BIOS turns IF on
9984 add sp, #2 ;; Pop offset value
9985 pop cx ;; Pop seg value (restore CX)
9987 ;; Look at the ROM's PnP Expansion header. Properly, we're supposed
9988 ;; to init all the ROMs and then go back and build an IPL table of
9989 ;; all the bootable devices, but we can get away with one pass.
9990 mov ds, cx ;; ROM base
9991 mov bx, 0x001a ;; 0x1A is the offset into ROM header that contains...
9992 mov ax, [bx] ;; the offset of PnP expansion header, where...
9993 cmp ax, #0x5024 ;; we look for signature "$PnP"
9994 jne no_bev
9995 mov ax, 2[bx]
9996 cmp ax, #0x506e
9997 jne no_bev
9998 mov ax, 0x1a[bx] ;; 0x1A is also the offset into the expansion header of...
9999 cmp ax, #0x0000 ;; the Bootstrap Entry Vector, or zero if there is none.
10000 je no_bev
10002 ;; Found a device that thinks it can boot the system. Record its BEV and product name string.
10003 mov di, 0x10[bx] ;; Pointer to the product name string or zero if none
10004 mov bx, #IPL_SEG ;; Go to the segment where the IPL table lives
10005 mov ds, bx
10006 mov bx, IPL_COUNT_OFFSET ;; Read the number of entries so far
10007 cmp bx, #IPL_TABLE_ENTRIES
10008 je no_bev ;; Get out if the table is full
10009 shl bx, #0x4 ;; Turn count into offset (entries are 16 bytes)
10010 mov 0[bx], #IPL_TYPE_BEV ;; This entry is a BEV device
10011 mov 6[bx], cx ;; Build a far pointer from the segment...
10012 mov 4[bx], ax ;; and the offset
10013 cmp di, #0x0000
10014 je no_prod_str
10015 mov 0xA[bx], cx ;; Build a far pointer from the segment...
10016 mov 8[bx], di ;; and the offset
10017 no_prod_str:
10018 shr bx, #0x4 ;; Turn the offset back into a count
10019 inc bx ;; We have one more entry now
10020 mov IPL_COUNT_OFFSET, bx ;; Remember that.
10022 no_bev:
10023 pop di ;; Restore DI
10024 pop ax ;; Restore AX
10025 rom_scan_increment:
10026 shl ax, #5 ;; convert 512-bytes blocks to 16-byte increments
10027 ;; because the segment selector is shifted left 4 bits.
10028 add cx, ax
10029 pop ax ;; Restore AX
10030 cmp cx, ax
10031 jbe rom_scan_loop
10033 xor ax, ax ;; Restore DS back to 0000:
10034 mov ds, ax
10037 ;; for 'C' strings and other data, insert them here with
10038 ;; a the following hack:
10039 ;; DATA_SEG_DEFS_HERE
10042 ;; the following area can be used to write dynamically generated tables
10043 .align 16
10044 bios_table_area_start:
10045 dd 0xaafb4442
10046 dd bios_table_area_end - bios_table_area_start - 8;
10048 ;--------
10049 ;- POST -
10050 ;--------
10051 .org 0xe05b ; POST Entry Point
10052 bios_table_area_end:
10053 post:
10055 xor ax, ax
10057 ;; first reset the DMA controllers
10058 out 0x0d,al
10059 out 0xda,al
10061 ;; then initialize the DMA controllers
10062 mov al, #0xC0
10063 out 0xD6, al ; cascade mode of channel 4 enabled
10064 mov al, #0x00
10065 out 0xD4, al ; unmask channel 4
10067 ;; Examine CMOS shutdown status.
10068 mov AL, #0x0f
10069 out 0x70, AL
10070 in AL, 0x71
10072 ;; backup status
10073 mov bl, al
10075 ;; Reset CMOS shutdown status.
10076 mov AL, #0x0f
10077 out 0x70, AL ; select CMOS register Fh
10078 mov AL, #0x00
10079 out 0x71, AL ; set shutdown action to normal
10081 ;; Examine CMOS shutdown status.
10082 mov al, bl
10084 ;; 0x00, 0x09, 0x0D+ = normal startup
10085 cmp AL, #0x00
10086 jz normal_post
10087 cmp AL, #0x0d
10088 jae normal_post
10089 cmp AL, #0x09
10090 je normal_post
10092 ;; 0x05 = eoi + jmp via [0x40:0x67] jump
10093 cmp al, #0x05
10094 je eoi_jmp_post
10096 ;; Examine CMOS shutdown status.
10097 ;; 0x01,0x02,0x03,0x04,0x06,0x07,0x08, 0x0a, 0x0b, 0x0c = Unimplemented shutdown status.
10098 push bx
10099 call _shutdown_status_panic
10101 #if 0
10102 HALT(__LINE__)
10104 ;#if 0
10105 ; 0xb0, 0x20, /* mov al, #0x20 */
10106 ; 0xe6, 0x20, /* out 0x20, al ;send EOI to PIC */
10107 ;#endif
10109 pop es
10110 pop ds
10111 popa
10112 iret
10113 #endif
10115 normal_post:
10116 ; case 0: normal startup
10119 mov ax, #0xfffe
10120 mov sp, ax
10121 xor ax, ax
10122 mov ds, ax
10123 mov ss, ax
10125 ;; zero out BIOS data area (40:00..40:ff)
10126 mov es, ax
10127 mov cx, #0x0080 ;; 128 words
10128 mov di, #0x0400
10131 stosw
10133 call _log_bios_start
10135 ;; set all interrupts to default handler
10136 xor bx, bx ;; offset index
10137 mov cx, #0x0100 ;; counter (256 interrupts)
10138 mov ax, #dummy_iret_handler
10139 mov dx, #0xF000
10141 post_default_ints:
10142 mov [bx], ax
10143 add bx, #2
10144 mov [bx], dx
10145 add bx, #2
10146 loop post_default_ints
10148 ;; set vector 0x79 to zero
10149 ;; this is used by 'gardian angel' protection system
10150 SET_INT_VECTOR(0x79, #0, #0)
10152 ;; base memory in K 40:13 (word)
10153 mov ax, #BASE_MEM_IN_K
10154 mov 0x0413, ax
10157 ;; Manufacturing Test 40:12
10158 ;; zerod out above
10160 ;; Warm Boot Flag 0040:0072
10161 ;; value of 1234h = skip memory checks
10162 ;; zerod out above
10165 ;; Printer Services vector
10166 SET_INT_VECTOR(0x17, #0xF000, #int17_handler)
10168 ;; Bootstrap failure vector
10169 SET_INT_VECTOR(0x18, #0xF000, #int18_handler)
10171 ;; Bootstrap Loader vector
10172 SET_INT_VECTOR(0x19, #0xF000, #int19_handler)
10174 ;; User Timer Tick vector
10175 SET_INT_VECTOR(0x1c, #0xF000, #int1c_handler)
10177 ;; Memory Size Check vector
10178 SET_INT_VECTOR(0x12, #0xF000, #int12_handler)
10180 ;; Equipment Configuration Check vector
10181 SET_INT_VECTOR(0x11, #0xF000, #int11_handler)
10183 ;; System Services
10184 SET_INT_VECTOR(0x15, #0xF000, #int15_handler)
10186 ;; EBDA setup
10187 call ebda_post
10189 ;; PIT setup
10190 SET_INT_VECTOR(0x08, #0xF000, #int08_handler)
10191 ;; int 1C already points at dummy_iret_handler (above)
10192 mov al, #0x34 ; timer0: binary count, 16bit count, mode 2
10193 out 0x43, al
10194 mov al, #0x00 ; maximum count of 0000H = 18.2Hz
10195 out 0x40, al
10196 out 0x40, al
10198 ;; Keyboard
10199 SET_INT_VECTOR(0x09, #0xF000, #int09_handler)
10200 SET_INT_VECTOR(0x16, #0xF000, #int16_handler)
10202 xor ax, ax
10203 mov ds, ax
10204 mov 0x0417, al /* keyboard shift flags, set 1 */
10205 mov 0x0418, al /* keyboard shift flags, set 2 */
10206 mov 0x0419, al /* keyboard alt-numpad work area */
10207 mov 0x0471, al /* keyboard ctrl-break flag */
10208 mov 0x0497, al /* keyboard status flags 4 */
10209 mov al, #0x10
10210 mov 0x0496, al /* keyboard status flags 3 */
10213 /* keyboard head of buffer pointer */
10214 mov bx, #0x001E
10215 mov 0x041A, bx
10217 /* keyboard end of buffer pointer */
10218 mov 0x041C, bx
10220 /* keyboard pointer to start of buffer */
10221 mov bx, #0x001E
10222 mov 0x0480, bx
10224 /* keyboard pointer to end of buffer */
10225 mov bx, #0x003E
10226 mov 0x0482, bx
10228 /* init the keyboard */
10229 call _keyboard_init
10231 ;; mov CMOS Equipment Byte to BDA Equipment Word
10232 mov ax, 0x0410
10233 mov al, #0x14
10234 out 0x70, al
10235 in al, 0x71
10236 mov 0x0410, ax
10239 ;; Parallel setup
10240 SET_INT_VECTOR(0x0F, #0xF000, #dummy_iret_handler)
10241 xor ax, ax
10242 mov ds, ax
10243 xor bx, bx
10244 mov cl, #0x14 ; timeout value
10245 mov dx, #0x378 ; Parallel I/O address, port 1
10246 call detect_parport
10247 mov dx, #0x278 ; Parallel I/O address, port 2
10248 call detect_parport
10249 shl bx, #0x0e
10250 mov ax, 0x410 ; Equipment word bits 14..15 determing # parallel ports
10251 and ax, #0x3fff
10252 or ax, bx ; set number of parallel ports
10253 mov 0x410, ax
10255 ;; Serial setup
10256 SET_INT_VECTOR(0x0C, #0xF000, #dummy_iret_handler)
10257 SET_INT_VECTOR(0x14, #0xF000, #int14_handler)
10258 xor bx, bx
10259 mov cl, #0x0a ; timeout value
10260 mov dx, #0x03f8 ; Serial I/O address, port 1
10261 call detect_serial
10262 mov dx, #0x02f8 ; Serial I/O address, port 2
10263 call detect_serial
10264 mov dx, #0x03e8 ; Serial I/O address, port 3
10265 call detect_serial
10266 mov dx, #0x02e8 ; Serial I/O address, port 4
10267 call detect_serial
10268 shl bx, #0x09
10269 mov ax, 0x410 ; Equipment word bits 9..11 determing # serial ports
10270 and ax, #0xf1ff
10271 or ax, bx ; set number of serial port
10272 mov 0x410, ax
10274 ;; CMOS RTC
10275 SET_INT_VECTOR(0x1A, #0xF000, #int1a_handler)
10276 SET_INT_VECTOR(0x4A, #0xF000, #dummy_iret_handler)
10277 SET_INT_VECTOR(0x70, #0xF000, #int70_handler)
10278 ;; BIOS DATA AREA 0x4CE ???
10279 call timer_tick_post
10281 ;; PS/2 mouse setup
10282 SET_INT_VECTOR(0x74, #0xF000, #int74_handler)
10284 ;; IRQ13 (FPU exception) setup
10285 SET_INT_VECTOR(0x75, #0xF000, #int75_handler)
10287 ;; Video setup
10288 SET_INT_VECTOR(0x10, #0xF000, #int10_handler)
10290 ;; PIC
10291 mov al, #0x11 ; send initialisation commands
10292 out 0x20, al
10293 out 0xa0, al
10294 mov al, #0x08
10295 out 0x21, al
10296 mov al, #0x70
10297 out 0xa1, al
10298 mov al, #0x04
10299 out 0x21, al
10300 mov al, #0x02
10301 out 0xa1, al
10302 mov al, #0x01
10303 out 0x21, al
10304 out 0xa1, al
10305 mov al, #0xb8
10306 out 0x21, AL ;master pic: unmask IRQ 0, 1, 2, 6
10307 #if BX_USE_PS2_MOUSE
10308 mov al, #0x8f
10309 #else
10310 mov al, #0x9f
10311 #endif
10312 out 0xa1, AL ;slave pic: unmask IRQ 12, 13, 14
10314 mov cx, #0xc000 ;; init vga bios
10315 mov ax, #0xc780
10316 call rom_scan
10318 call _print_bios_banner
10320 #if BX_ROMBIOS32
10321 call rombios32_init
10322 #else
10323 #if BX_PCIBIOS
10324 call pcibios_init_iomem_bases
10325 call pcibios_init_irqs
10326 #endif //BX_PCIBIOS
10327 #endif
10330 ;; Floppy setup
10332 call floppy_drive_post
10334 #if BX_USE_ATADRV
10337 ;; Hard Drive setup
10339 call hard_drive_post
10342 ;; ATA/ATAPI driver setup
10344 call _ata_init
10345 call _ata_detect
10347 #else // BX_USE_ATADRV
10350 ;; Hard Drive setup
10352 call hard_drive_post
10354 #endif // BX_USE_ATADRV
10356 #if BX_ELTORITO_BOOT
10358 ;; eltorito floppy/harddisk emulation from cd
10360 call _cdemu_init
10362 #endif // BX_ELTORITO_BOOT
10364 call _init_boot_vectors
10366 mov cx, #0xc800 ;; init option roms
10367 mov ax, #0xe000
10368 call rom_scan
10370 sti ;; enable interrupts
10371 int #0x19
10373 .org 0xe2c3 ; NMI Handler Entry Point
10374 nmi:
10375 ;; FIXME the NMI handler should not panic
10376 ;; but iret when called from int75 (fpu exception)
10377 call _nmi_handler_msg
10378 iret
10380 int75_handler:
10381 out 0xf0, al // clear irq13
10382 call eoi_both_pics // clear interrupt
10383 int 2 // legacy nmi call
10384 iret
10386 ;-------------------------------------------
10387 ;- INT 13h Fixed Disk Services Entry Point -
10388 ;-------------------------------------------
10389 .org 0xe3fe ; INT 13h Fixed Disk Services Entry Point
10390 int13_handler:
10391 //JMPL(int13_relocated)
10392 jmp int13_relocated
10394 .org 0xe401 ; Fixed Disk Parameter Table
10396 ;----------
10397 ;- INT19h -
10398 ;----------
10399 .org 0xe6f2 ; INT 19h Boot Load Service Entry Point
10400 int19_handler:
10402 jmp int19_relocated
10403 ;-------------------------------------------
10404 ;- System BIOS Configuration Data Table
10405 ;-------------------------------------------
10406 .org BIOS_CONFIG_TABLE
10407 db 0x08 ; Table size (bytes) -Lo
10408 db 0x00 ; Table size (bytes) -Hi
10409 db SYS_MODEL_ID
10410 db SYS_SUBMODEL_ID
10411 db BIOS_REVISION
10412 ; Feature byte 1
10413 ; b7: 1=DMA channel 3 used by hard disk
10414 ; b6: 1=2 interrupt controllers present
10415 ; b5: 1=RTC present
10416 ; b4: 1=BIOS calls int 15h/4Fh every key
10417 ; b3: 1=wait for extern event supported (Int 15h/41h)
10418 ; b2: 1=extended BIOS data area used
10419 ; b1: 0=AT or ESDI bus, 1=MicroChannel
10420 ; b0: 1=Dual bus (MicroChannel + ISA)
10421 db (0 << 7) | \
10422 (1 << 6) | \
10423 (1 << 5) | \
10424 (BX_CALL_INT15_4F << 4) | \
10425 (0 << 3) | \
10426 (BX_USE_EBDA << 2) | \
10427 (0 << 1) | \
10428 (0 << 0)
10429 ; Feature byte 2
10430 ; b7: 1=32-bit DMA supported
10431 ; b6: 1=int16h, function 9 supported
10432 ; b5: 1=int15h/C6h (get POS data) supported
10433 ; b4: 1=int15h/C7h (get mem map info) supported
10434 ; b3: 1=int15h/C8h (en/dis CPU) supported
10435 ; b2: 1=non-8042 kb controller
10436 ; b1: 1=data streaming supported
10437 ; b0: reserved
10438 db (0 << 7) | \
10439 (1 << 6) | \
10440 (0 << 5) | \
10441 (0 << 4) | \
10442 (0 << 3) | \
10443 (0 << 2) | \
10444 (0 << 1) | \
10445 (0 << 0)
10446 ; Feature byte 3
10447 ; b7: not used
10448 ; b6: reserved
10449 ; b5: reserved
10450 ; b4: POST supports ROM-to-RAM enable/disable
10451 ; b3: SCSI on system board
10452 ; b2: info panel installed
10453 ; b1: Initial Machine Load (IML) system - BIOS on disk
10454 ; b0: SCSI supported in IML
10455 db 0x00
10456 ; Feature byte 4
10457 ; b7: IBM private
10458 ; b6: EEPROM present
10459 ; b5-3: ABIOS presence (011 = not supported)
10460 ; b2: private
10461 ; b1: memory split above 16Mb supported
10462 ; b0: POSTEXT directly supported by POST
10463 db 0x00
10464 ; Feature byte 5 (IBM)
10465 ; b1: enhanced mouse
10466 ; b0: flash EPROM
10467 db 0x00
10471 .org 0xe729 ; Baud Rate Generator Table
10473 ;----------
10474 ;- INT14h -
10475 ;----------
10476 .org 0xe739 ; INT 14h Serial Communications Service Entry Point
10477 int14_handler:
10478 push ds
10479 pusha
10480 xor ax, ax
10481 mov ds, ax
10482 call _int14_function
10483 popa
10484 pop ds
10485 iret
10488 ;----------------------------------------
10489 ;- INT 16h Keyboard Service Entry Point -
10490 ;----------------------------------------
10491 .org 0xe82e
10492 int16_handler:
10495 push ds
10496 pushf
10497 pusha
10499 cmp ah, #0x00
10500 je int16_F00
10501 cmp ah, #0x10
10502 je int16_F00
10504 mov bx, #0xf000
10505 mov ds, bx
10506 call _int16_function
10507 popa
10508 popf
10509 pop ds
10510 jz int16_zero_set
10512 int16_zero_clear:
10513 push bp
10514 mov bp, sp
10515 //SEG SS
10516 and BYTE [bp + 0x06], #0xbf
10517 pop bp
10518 iret
10520 int16_zero_set:
10521 push bp
10522 mov bp, sp
10523 //SEG SS
10524 or BYTE [bp + 0x06], #0x40
10525 pop bp
10526 iret
10528 int16_F00:
10529 mov bx, #0x0040
10530 mov ds, bx
10532 int16_wait_for_key:
10534 mov bx, 0x001a
10535 cmp bx, 0x001c
10536 jne int16_key_found
10539 #if 0
10540 /* no key yet, call int 15h, function AX=9002 */
10541 0x50, /* push AX */
10542 0xb8, 0x02, 0x90, /* mov AX, #0x9002 */
10543 0xcd, 0x15, /* int 15h */
10544 0x58, /* pop AX */
10545 0xeb, 0xea, /* jmp WAIT_FOR_KEY */
10546 #endif
10547 jmp int16_wait_for_key
10549 int16_key_found:
10550 mov bx, #0xf000
10551 mov ds, bx
10552 call _int16_function
10553 popa
10554 popf
10555 pop ds
10556 #if 0
10557 /* notify int16 complete w/ int 15h, function AX=9102 */
10558 0x50, /* push AX */
10559 0xb8, 0x02, 0x91, /* mov AX, #0x9102 */
10560 0xcd, 0x15, /* int 15h */
10561 0x58, /* pop AX */
10562 #endif
10563 iret
10567 ;-------------------------------------------------
10568 ;- INT09h : Keyboard Hardware Service Entry Point -
10569 ;-------------------------------------------------
10570 .org 0xe987
10571 int09_handler:
10573 push ax
10575 mov al, #0xAD ;;disable keyboard
10576 out #0x64, al
10578 mov al, #0x0B
10579 out #0x20, al
10580 in al, #0x20
10581 and al, #0x02
10582 jz int09_finish
10584 in al, #0x60 ;;read key from keyboard controller
10586 push ds
10587 pusha
10588 #ifdef BX_CALL_INT15_4F
10589 mov ah, #0x4f ;; allow for keyboard intercept
10591 int #0x15
10592 jnc int09_done
10593 #endif
10595 ;; check for extended key
10596 cmp al, #0xe0
10597 jne int09_check_pause
10598 xor ax, ax
10599 mov ds, ax
10600 mov al, BYTE [0x496] ;; mf2_state |= 0x02
10601 or al, #0x02
10602 mov BYTE [0x496], al
10603 jmp int09_done
10605 int09_check_pause: ;; check for pause key
10606 cmp al, #0xe1
10607 jne int09_process_key
10608 xor ax, ax
10609 mov ds, ax
10610 mov al, BYTE [0x496] ;; mf2_state |= 0x01
10611 or al, #0x01
10612 mov BYTE [0x496], al
10613 jmp int09_done
10615 int09_process_key:
10616 mov bx, #0xf000
10617 mov ds, bx
10618 call _int09_function
10620 int09_done:
10621 popa
10622 pop ds
10624 call eoi_master_pic
10626 int09_finish:
10627 mov al, #0xAE ;;enable keyboard
10628 out #0x64, al
10629 pop ax
10630 iret
10633 ;----------------------------------------
10634 ;- INT 13h Diskette Service Entry Point -
10635 ;----------------------------------------
10636 .org 0xec59
10637 int13_diskette:
10638 jmp int13_noeltorito
10640 ;---------------------------------------------
10641 ;- INT 0Eh Diskette Hardware ISR Entry Point -
10642 ;---------------------------------------------
10643 .org 0xef57 ; INT 0Eh Diskette Hardware ISR Entry Point
10644 int0e_handler:
10645 push ax
10646 push dx
10647 mov dx, #0x03f4
10648 in al, dx
10649 and al, #0xc0
10650 cmp al, #0xc0
10651 je int0e_normal
10652 mov dx, #0x03f5
10653 mov al, #0x08 ; sense interrupt status
10654 out dx, al
10655 int0e_loop1:
10656 mov dx, #0x03f4
10657 in al, dx
10658 and al, #0xc0
10659 cmp al, #0xc0
10660 jne int0e_loop1
10661 int0e_loop2:
10662 mov dx, #0x03f5
10663 in al, dx
10664 mov dx, #0x03f4
10665 in al, dx
10666 and al, #0xc0
10667 cmp al, #0xc0
10668 je int0e_loop2
10669 int0e_normal:
10670 push ds
10671 xor ax, ax ;; segment 0000
10672 mov ds, ax
10673 call eoi_master_pic
10674 mov al, 0x043e
10675 or al, #0x80 ;; diskette interrupt has occurred
10676 mov 0x043e, al
10677 pop ds
10678 pop dx
10679 pop ax
10680 iret
10683 .org 0xefc7 ; Diskette Controller Parameter Table
10684 diskette_param_table:
10685 ;; Since no provisions are made for multiple drive types, most
10686 ;; values in this table are ignored. I set parameters for 1.44M
10687 ;; floppy here
10688 db 0xAF
10689 db 0x02 ;; head load time 0000001, DMA used
10690 db 0x25
10691 db 0x02
10692 db 18
10693 db 0x1B
10694 db 0xFF
10695 db 0x6C
10696 db 0xF6
10697 db 0x0F
10698 db 0x08
10701 ;----------------------------------------
10702 ;- INT17h : Printer Service Entry Point -
10703 ;----------------------------------------
10704 .org 0xefd2
10705 int17_handler:
10706 push ds
10707 pusha
10708 xor ax, ax
10709 mov ds, ax
10710 call _int17_function
10711 popa
10712 pop ds
10713 iret
10715 diskette_param_table2:
10716 ;; New diskette parameter table adding 3 parameters from IBM
10717 ;; Since no provisions are made for multiple drive types, most
10718 ;; values in this table are ignored. I set parameters for 1.44M
10719 ;; floppy here
10720 db 0xAF
10721 db 0x02 ;; head load time 0000001, DMA used
10722 db 0x25
10723 db 0x02
10724 db 18
10725 db 0x1B
10726 db 0xFF
10727 db 0x6C
10728 db 0xF6
10729 db 0x0F
10730 db 0x08
10731 db 79 ;; maximum track
10732 db 0 ;; data transfer rate
10733 db 4 ;; drive type in cmos
10735 .org 0xf045 ; INT 10 Functions 0-Fh Entry Point
10736 HALT(__LINE__)
10737 iret
10739 ;----------
10740 ;- INT10h -
10741 ;----------
10742 .org 0xf065 ; INT 10h Video Support Service Entry Point
10743 int10_handler:
10744 ;; dont do anything, since the VGA BIOS handles int10h requests
10745 iret
10747 .org 0xf0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
10749 ;----------
10750 ;- INT12h -
10751 ;----------
10752 .org 0xf841 ; INT 12h Memory Size Service Entry Point
10753 ; ??? different for Pentium (machine check)?
10754 int12_handler:
10755 push ds
10756 mov ax, #0x0040
10757 mov ds, ax
10758 mov ax, 0x0013
10759 pop ds
10760 iret
10762 ;----------
10763 ;- INT11h -
10764 ;----------
10765 .org 0xf84d ; INT 11h Equipment List Service Entry Point
10766 int11_handler:
10767 push ds
10768 mov ax, #0x0040
10769 mov ds, ax
10770 mov ax, 0x0010
10771 pop ds
10772 iret
10774 ;----------
10775 ;- INT15h -
10776 ;----------
10777 .org 0xf859 ; INT 15h System Services Entry Point
10778 int15_handler:
10779 pushf
10780 #if BX_APM
10781 cmp ah, #0x53
10782 je apm_call
10783 #endif
10784 push ds
10785 push es
10786 cmp ah, #0x86
10787 je int15_handler32
10788 cmp ah, #0xE8
10789 je int15_handler32
10790 pusha
10791 #if BX_USE_PS2_MOUSE
10792 cmp ah, #0xC2
10793 je int15_handler_mouse
10794 #endif
10795 call _int15_function
10796 int15_handler_mouse_ret:
10797 popa
10798 int15_handler32_ret:
10799 pop es
10800 pop ds
10801 popf
10802 jmp iret_modify_cf
10803 #if BX_APM
10804 apm_call:
10805 jmp _apmreal_entry
10806 #endif
10808 #if BX_USE_PS2_MOUSE
10809 int15_handler_mouse:
10810 call _int15_function_mouse
10811 jmp int15_handler_mouse_ret
10812 #endif
10814 int15_handler32:
10815 pushad
10816 call _int15_function32
10817 popad
10818 jmp int15_handler32_ret
10820 ;; Protected mode IDT descriptor
10822 ;; I just make the limit 0, so the machine will shutdown
10823 ;; if an exception occurs during protected mode memory
10824 ;; transfers.
10826 ;; Set base to f0000 to correspond to beginning of BIOS,
10827 ;; in case I actually define an IDT later
10828 ;; Set limit to 0
10830 pmode_IDT_info:
10831 dw 0x0000 ;; limit 15:00
10832 dw 0x0000 ;; base 15:00
10833 db 0x0f ;; base 23:16
10835 ;; Real mode IDT descriptor
10837 ;; Set to typical real-mode values.
10838 ;; base = 000000
10839 ;; limit = 03ff
10841 rmode_IDT_info:
10842 dw 0x03ff ;; limit 15:00
10843 dw 0x0000 ;; base 15:00
10844 db 0x00 ;; base 23:16
10847 ;----------
10848 ;- INT1Ah -
10849 ;----------
10850 .org 0xfe6e ; INT 1Ah Time-of-day Service Entry Point
10851 int1a_handler:
10852 #if BX_PCIBIOS
10853 cmp ah, #0xb1
10854 jne int1a_normal
10855 call pcibios_real
10856 jc pcibios_error
10857 retf 2
10858 pcibios_error:
10859 mov bl, ah
10860 mov ah, #0xb1
10861 push ds
10862 pusha
10863 mov ax, ss ; set readable descriptor to ds, for calling pcibios
10864 mov ds, ax ; on 16bit protected mode.
10865 jmp int1a_callfunction
10866 int1a_normal:
10867 #endif
10868 push ds
10869 pusha
10870 xor ax, ax
10871 mov ds, ax
10872 int1a_callfunction:
10873 call _int1a_function
10874 popa
10875 pop ds
10876 iret
10879 ;; int70h: IRQ8 - CMOS RTC
10881 int70_handler:
10882 push ds
10883 pushad
10884 xor ax, ax
10885 mov ds, ax
10886 call _int70_function
10887 popad
10888 pop ds
10889 iret
10891 ;---------
10892 ;- INT08 -
10893 ;---------
10894 .org 0xfea5 ; INT 08h System Timer ISR Entry Point
10895 int08_handler:
10897 push eax
10898 push ds
10899 xor ax, ax
10900 mov ds, ax
10902 ;; time to turn off drive(s)?
10903 mov al,0x0440
10904 or al,al
10905 jz int08_floppy_off
10906 dec al
10907 mov 0x0440,al
10908 jnz int08_floppy_off
10909 ;; turn motor(s) off
10910 push dx
10911 mov dx,#0x03f2
10912 in al,dx
10913 and al,#0xcf
10914 out dx,al
10915 pop dx
10916 int08_floppy_off:
10918 mov eax, 0x046c ;; get ticks dword
10919 inc eax
10921 ;; compare eax to one days worth of timer ticks at 18.2 hz
10922 cmp eax, #0x001800B0
10923 jb int08_store_ticks
10924 ;; there has been a midnight rollover at this point
10925 xor eax, eax ;; zero out counter
10926 inc BYTE 0x0470 ;; increment rollover flag
10928 int08_store_ticks:
10929 mov 0x046c, eax ;; store new ticks dword
10930 ;; chain to user timer tick INT #0x1c
10931 //pushf
10932 //;; call_ep [ds:loc]
10933 //CALL_EP( 0x1c << 2 )
10934 int #0x1c
10936 call eoi_master_pic
10937 pop ds
10938 pop eax
10939 iret
10941 .org 0xfef3 ; Initial Interrupt Vector Offsets Loaded by POST
10944 .org 0xff00
10945 .ascii BIOS_COPYRIGHT_STRING
10947 ;------------------------------------------------
10948 ;- IRET Instruction for Dummy Interrupt Handler -
10949 ;------------------------------------------------
10950 .org 0xff53 ; IRET Instruction for Dummy Interrupt Handler
10951 dummy_iret_handler:
10952 iret
10954 .org 0xff54 ; INT 05h Print Screen Service Entry Point
10955 HALT(__LINE__)
10956 iret
10958 .org 0xfff0 ; Power-up Entry Point
10959 jmp 0xf000:post
10961 .org 0xfff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
10962 .ascii BIOS_BUILD_DATE
10964 .org 0xfffe ; System Model ID
10965 db SYS_MODEL_ID
10966 db 0x00 ; filler
10968 .org 0xfa6e ;; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
10969 ASM_END
10971 * This font comes from the fntcol16.zip package (c) by Joseph Gil
10972 * found at ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip
10973 * This font is public domain
10975 static Bit8u vgafont8[128*8]=
10977 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
10978 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e,
10979 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e,
10980 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00,
10981 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00,
10982 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c,
10983 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c,
10984 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00,
10985 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff,
10986 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
10987 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff,
10988 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78,
10989 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18,
10990 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0,
10991 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0,
10992 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99,
10993 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00,
10994 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00,
10995 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18,
10996 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
10997 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00,
10998 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78,
10999 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00,
11000 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff,
11001 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00,
11002 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
11003 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00,
11004 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
11005 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00,
11006 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00,
11007 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00,
11008 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00,
11009 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
11010 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00,
11011 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
11012 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00,
11013 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00,
11014 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00,
11015 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00,
11016 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
11017 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00,
11018 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
11019 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
11020 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00,
11021 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60,
11022 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
11023 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00,
11024 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00,
11025 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00,
11026 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00,
11027 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00,
11028 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00,
11029 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00,
11030 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00,
11031 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00,
11032 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00,
11033 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00,
11034 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00,
11035 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00,
11036 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60,
11037 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00,
11038 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00,
11039 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00,
11040 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00,
11041 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00,
11042 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00,
11043 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00,
11044 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00,
11045 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00,
11046 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00,
11047 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00,
11048 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00,
11049 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00,
11050 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
11051 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00,
11052 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00,
11053 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00,
11054 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00,
11055 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00,
11056 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00,
11057 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
11058 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00,
11059 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00,
11060 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00,
11061 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
11062 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00,
11063 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
11064 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00,
11065 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00,
11066 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00,
11067 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00,
11068 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
11069 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00,
11070 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
11071 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
11072 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
11073 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
11074 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00,
11075 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00,
11076 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00,
11077 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00,
11078 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
11079 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00,
11080 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
11081 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00,
11082 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
11083 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78,
11084 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00,
11085 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
11086 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00,
11087 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
11088 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
11089 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0,
11090 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e,
11091 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00,
11092 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00,
11093 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00,
11094 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
11095 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
11096 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00,
11097 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00,
11098 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
11099 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00,
11100 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00,
11101 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
11102 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00,
11103 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
11104 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00,
11107 ASM_START
11108 .org 0xcc00
11109 // bcc-generated data will be placed here
11110 ASM_END