- inline asm optimizations by Stanislav
[bochs-mirror.git] / bios / rombios.c
blob6e3dadb9bbd8398d44a040326a9dd913fca25015
1 /////////////////////////////////////////////////////////////////////////
2 // $Id: rombios.c,v 1.191 2007/12/01 19:26:46 vruppert Exp $
3 /////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (C) 2002 MandrakeSoft S.A.
6 //
7 // MandrakeSoft S.A.
8 // 43, rue d'Aboukir
9 // 75002 Paris - France
10 // http://www.linux-mandrake.com/
11 // http://www.mandrakesoft.com/
13 // This library is free software; you can redistribute it and/or
14 // modify it under the terms of the GNU Lesser General Public
15 // License as published by the Free Software Foundation; either
16 // version 2 of the License, or (at your option) any later version.
18 // This library is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 // Lesser General Public License for more details.
23 // You should have received a copy of the GNU Lesser General Public
24 // License along with this library; if not, write to the Free Software
25 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27 // ROM BIOS for use with Bochs/Plex86/QEMU emulation environment
30 // ROM BIOS compatability entry points:
31 // ===================================
32 // $e05b ; POST Entry Point
33 // $e2c3 ; NMI Handler Entry Point
34 // $e3fe ; INT 13h Fixed Disk Services Entry Point
35 // $e401 ; Fixed Disk Parameter Table
36 // $e6f2 ; INT 19h Boot Load Service Entry Point
37 // $e6f5 ; Configuration Data Table
38 // $e729 ; Baud Rate Generator Table
39 // $e739 ; INT 14h Serial Communications Service Entry Point
40 // $e82e ; INT 16h Keyboard Service Entry Point
41 // $e987 ; INT 09h Keyboard Service Entry Point
42 // $ec59 ; INT 13h Diskette Service Entry Point
43 // $ef57 ; INT 0Eh Diskette Hardware ISR Entry Point
44 // $efc7 ; Diskette Controller Parameter Table
45 // $efd2 ; INT 17h Printer Service Entry Point
46 // $f045 ; INT 10 Functions 0-Fh Entry Point
47 // $f065 ; INT 10h Video Support Service Entry Point
48 // $f0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
49 // $f841 ; INT 12h Memory Size Service Entry Point
50 // $f84d ; INT 11h Equipment List Service Entry Point
51 // $f859 ; INT 15h System Services Entry Point
52 // $fa6e ; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
53 // $fe6e ; INT 1Ah Time-of-day Service Entry Point
54 // $fea5 ; INT 08h System Timer ISR Entry Point
55 // $fef3 ; Initial Interrupt Vector Offsets Loaded by POST
56 // $ff53 ; IRET Instruction for Dummy Interrupt Handler
57 // $ff54 ; INT 05h Print Screen Service Entry Point
58 // $fff0 ; Power-up Entry Point
59 // $fff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
60 // $fffe ; System Model ID
62 // NOTES for ATA/ATAPI driver (cbbochs@free.fr)
63 // Features
64 // - supports up to 4 ATA interfaces
65 // - device/geometry detection
66 // - 16bits/32bits device access
67 // - pchs/lba access
68 // - datain/dataout/packet command support
70 // NOTES for El-Torito Boot (cbbochs@free.fr)
71 // - CD-ROM booting is only available if ATA/ATAPI Driver is available
72 // - Current code is only able to boot mono-session cds
73 // - Current code can not boot and emulate a hard-disk
74 // the bios will panic otherwise
75 // - Current code also use memory in EBDA segement.
76 // - I used cmos byte 0x3D to store extended information on boot-device
77 // - Code has to be modified modified to handle multiple cdrom drives
78 // - Here are the cdrom boot failure codes:
79 // 1 : no atapi device found
80 // 2 : no atapi cdrom found
81 // 3 : can not read cd - BRVD
82 // 4 : cd is not eltorito (BRVD)
83 // 5 : cd is not eltorito (ISO TAG)
84 // 6 : cd is not eltorito (ELTORITO TAG)
85 // 7 : can not read cd - boot catalog
86 // 8 : boot catalog : bad header
87 // 9 : boot catalog : bad platform
88 // 10 : boot catalog : bad signature
89 // 11 : boot catalog : bootable flag not set
90 // 12 : can not read cd - boot image
92 // ATA driver
93 // - EBDA segment.
94 // I used memory starting at 0x121 in the segment
95 // - the translation policy is defined in cmos regs 0x39 & 0x3a
97 // TODO :
99 // int74
100 // - needs to be reworked. Uses direct [bp] offsets. (?)
102 // int13:
103 // - f04 (verify sectors) isn't complete (?)
104 // - f02/03/04 should set current cyl,etc in BDA (?)
105 // - rewrite int13_relocated & clean up int13 entry code
107 // NOTES:
108 // - NMI access (bit7 of addr written to 70h)
110 // ATA driver
111 // - should handle the "don't detect" bit (cmos regs 0x3b & 0x3c)
112 // - could send the multiple-sector read/write commands
114 // El-Torito
115 // - Emulate a Hard-disk (currently only diskette can be emulated) see "FIXME ElTorito Harddisk"
116 // - Implement remaining int13_cdemu functions (as defined by El-Torito specs)
117 // - cdrom drive is hardcoded to ide 0 device 1 in several places. see "FIXME ElTorito Hardcoded"
118 // - int13 Fix DL when emulating a cd. In that case DL is decremented before calling real int13.
119 // This is ok. But DL should be reincremented afterwards.
120 // - Fix all "FIXME ElTorito Various"
121 // - should be able to boot any cdrom instead of the first one
123 // BCC Bug: find a generic way to handle the bug of #asm after an "if" (fixed in 0.16.7)
125 #include "rombios.h"
127 #define DEBUG_ATA 0
128 #define DEBUG_INT13_HD 0
129 #define DEBUG_INT13_CD 0
130 #define DEBUG_INT13_ET 0
131 #define DEBUG_INT13_FL 0
132 #define DEBUG_INT15 0
133 #define DEBUG_INT16 0
134 #define DEBUG_INT1A 0
135 #define DEBUG_INT74 0
136 #define DEBUG_APM 0
138 #define BX_CPU 3
139 #define BX_USE_PS2_MOUSE 1
140 #define BX_CALL_INT15_4F 1
141 #define BX_USE_EBDA 1
142 #define BX_SUPPORT_FLOPPY 1
143 #define BX_FLOPPY_ON_CNT 37 /* 2 seconds */
144 #define BX_PCIBIOS 1
145 #define BX_APM 1
147 #define BX_USE_ATADRV 1
148 #define BX_ELTORITO_BOOT 1
150 #define BX_MAX_ATA_INTERFACES 4
151 #define BX_MAX_ATA_DEVICES (BX_MAX_ATA_INTERFACES*2)
153 #define BX_VIRTUAL_PORTS 1 /* normal output to Bochs ports */
154 #define BX_DEBUG_SERIAL 0 /* output to COM1 */
156 /* model byte 0xFC = AT */
157 #define SYS_MODEL_ID 0xFC
158 #define SYS_SUBMODEL_ID 0x00
159 #define BIOS_REVISION 1
160 #define BIOS_CONFIG_TABLE 0xe6f5
162 #ifndef BIOS_BUILD_DATE
163 # define BIOS_BUILD_DATE "06/23/99"
164 #endif
166 // 1K of base memory used for Extended Bios Data Area (EBDA)
167 // EBDA is used for PS/2 mouse support, and IDE BIOS, etc.
168 #define EBDA_SEG 0x9FC0
169 #define EBDA_SIZE 1 // In KiB
170 #define BASE_MEM_IN_K (640 - EBDA_SIZE)
172 /* 256 bytes at 0x9ff00 -- 0x9ffff is used for the IPL boot table. */
173 #define IPL_SEG 0x9ff0
174 #define IPL_TABLE_OFFSET 0x0000
175 #define IPL_TABLE_ENTRIES 8
176 #define IPL_COUNT_OFFSET 0x0080 /* u16: number of valid table entries */
177 #define IPL_SEQUENCE_OFFSET 0x0082 /* u16: next boot device */
179 // Define the application NAME
180 #if defined(BX_QEMU)
181 # define BX_APPNAME "QEMU"
182 #elif defined(PLEX86)
183 # define BX_APPNAME "Plex86"
184 #else
185 # define BX_APPNAME "Bochs"
186 #endif
188 // Sanity Checks
189 #if BX_USE_ATADRV && BX_CPU<3
190 # error The ATA/ATAPI Driver can only to be used with a 386+ cpu
191 #endif
192 #if BX_USE_ATADRV && !BX_USE_EBDA
193 # error ATA/ATAPI Driver can only be used if EBDA is available
194 #endif
195 #if BX_ELTORITO_BOOT && !BX_USE_ATADRV
196 # error El-Torito Boot can only be use if ATA/ATAPI Driver is available
197 #endif
198 #if BX_PCIBIOS && BX_CPU<3
199 # error PCI BIOS can only be used with 386+ cpu
200 #endif
201 #if BX_APM && BX_CPU<3
202 # error APM BIOS can only be used with 386+ cpu
203 #endif
205 // define this if you want to make PCIBIOS working on a specific bridge only
206 // undef enables PCIBIOS when at least one PCI device is found
207 // i440FX is emulated by Bochs and QEMU
208 #define PCI_FIXED_HOST_BRIDGE 0x12378086 ;; i440FX PCI bridge
210 // #20 is dec 20
211 // #$20 is hex 20 = 32
212 // #0x20 is hex 20 = 32
213 // LDA #$20
214 // JSR $E820
215 // LDD .i,S
216 // JSR $C682
217 // mov al, #$20
219 // all hex literals should be prefixed with '0x'
220 // grep "#[0-9a-fA-F][0-9a-fA-F]" rombios.c
221 // no mov SEG-REG, #value, must mov register into seg-reg
222 // grep -i "mov[ ]*.s" rombios.c
224 // This is for compiling with gcc2 and gcc3
225 #define ASM_START #asm
226 #define ASM_END #endasm
228 ASM_START
229 .rom
231 .org 0x0000
233 #if BX_CPU >= 3
234 use16 386
235 #else
236 use16 286
237 #endif
239 MACRO HALT
240 ;; the HALT macro is called with the line number of the HALT call.
241 ;; The line number is then sent to the PANIC_PORT, causing Bochs/Plex
242 ;; to print a BX_PANIC message. This will normally halt the simulation
243 ;; with a message such as "BIOS panic at rombios.c, line 4091".
244 ;; However, users can choose to make panics non-fatal and continue.
245 #if BX_VIRTUAL_PORTS
246 mov dx,#PANIC_PORT
247 mov ax,#?1
248 out dx,ax
249 #else
250 mov dx,#0x80
251 mov ax,#?1
252 out dx,al
253 #endif
254 MEND
256 MACRO JMP_AP
257 db 0xea
258 dw ?2
259 dw ?1
260 MEND
262 MACRO SET_INT_VECTOR
263 mov ax, ?3
264 mov ?1*4, ax
265 mov ax, ?2
266 mov ?1*4+2, ax
267 MEND
269 ASM_END
271 typedef unsigned char Bit8u;
272 typedef unsigned short Bit16u;
273 typedef unsigned short bx_bool;
274 typedef unsigned long Bit32u;
277 void memsetb(seg,offset,value,count);
278 void memcpyb(dseg,doffset,sseg,soffset,count);
279 void memcpyd(dseg,doffset,sseg,soffset,count);
281 // memset of count bytes
282 void
283 memsetb(seg,offset,value,count)
284 Bit16u seg;
285 Bit16u offset;
286 Bit16u value;
287 Bit16u count;
289 ASM_START
290 push bp
291 mov bp, sp
293 push ax
294 push cx
295 push es
296 push di
298 mov cx, 10[bp] ; count
299 test cx, cx
300 je memsetb_end
301 mov ax, 4[bp] ; segment
302 mov es, ax
303 mov ax, 6[bp] ; offset
304 mov di, ax
305 mov al, 8[bp] ; value
308 stosb
310 memsetb_end:
311 pop di
312 pop es
313 pop cx
314 pop ax
316 pop bp
317 ASM_END
320 // memcpy of count bytes
321 void
322 memcpyb(dseg,doffset,sseg,soffset,count)
323 Bit16u dseg;
324 Bit16u doffset;
325 Bit16u sseg;
326 Bit16u soffset;
327 Bit16u count;
329 ASM_START
330 push bp
331 mov bp, sp
333 push ax
334 push cx
335 push es
336 push di
337 push ds
338 push si
340 mov cx, 12[bp] ; count
341 test cx, cx
342 je memcpyb_end
343 mov ax, 4[bp] ; dsegment
344 mov es, ax
345 mov ax, 6[bp] ; doffset
346 mov di, ax
347 mov ax, 8[bp] ; ssegment
348 mov ds, ax
349 mov ax, 10[bp] ; soffset
350 mov si, ax
353 movsb
355 memcpyb_end:
356 pop si
357 pop ds
358 pop di
359 pop es
360 pop cx
361 pop ax
363 pop bp
364 ASM_END
367 // memcpy of count dword
368 void
369 memcpyd(dseg,doffset,sseg,soffset,count)
370 Bit16u dseg;
371 Bit16u doffset;
372 Bit16u sseg;
373 Bit16u soffset;
374 Bit16u count;
376 ASM_START
377 push bp
378 mov bp, sp
380 push ax
381 push cx
382 push es
383 push di
384 push ds
385 push si
387 mov cx, 12[bp] ; count
388 test cx, cx
389 je memcpyd_end
390 mov ax, 4[bp] ; dsegment
391 mov es, ax
392 mov ax, 6[bp] ; doffset
393 mov di, ax
394 mov ax, 8[bp] ; ssegment
395 mov ds, ax
396 mov ax, 10[bp] ; soffset
397 mov si, ax
400 movsd
402 memcpyd_end:
403 pop si
404 pop ds
405 pop di
406 pop es
407 pop cx
408 pop ax
410 pop bp
411 ASM_END
414 // read_dword and write_dword functions
415 static Bit32u read_dword();
416 static void write_dword();
418 Bit32u
419 read_dword(seg, offset)
420 Bit16u seg;
421 Bit16u offset;
423 ASM_START
424 push bp
425 mov bp, sp
427 push bx
428 push ds
429 mov ax, 4[bp] ; segment
430 mov ds, ax
431 mov bx, 6[bp] ; offset
432 mov ax, [bx]
433 add bx, 2
434 mov dx, [bx]
435 ;; ax = return value (word)
436 ;; dx = return value (word)
437 pop ds
438 pop bx
440 pop bp
441 ASM_END
444 void
445 write_dword(seg, offset, data)
446 Bit16u seg;
447 Bit16u offset;
448 Bit32u data;
450 ASM_START
451 push bp
452 mov bp, sp
454 push ax
455 push bx
456 push ds
457 mov ax, 4[bp] ; segment
458 mov ds, ax
459 mov bx, 6[bp] ; offset
460 mov ax, 8[bp] ; data word
461 mov [bx], ax ; write data word
462 add bx, 2
463 mov ax, 10[bp] ; data word
464 mov [bx], ax ; write data word
465 pop ds
466 pop bx
467 pop ax
469 pop bp
470 ASM_END
473 // Bit32u (unsigned long) and long helper functions
474 ASM_START
476 ;; and function
477 landl:
478 landul:
479 SEG SS
480 and ax,[di]
481 SEG SS
482 and bx,2[di]
485 ;; add function
486 laddl:
487 laddul:
488 SEG SS
489 add ax,[di]
490 SEG SS
491 adc bx,2[di]
494 ;; cmp function
495 lcmpl:
496 lcmpul:
497 and eax, #0x0000FFFF
498 shl ebx, #16
499 or eax, ebx
500 shr ebx, #16
501 SEG SS
502 cmp eax, dword ptr [di]
505 ;; sub function
506 lsubl:
507 lsubul:
508 SEG SS
509 sub ax,[di]
510 SEG SS
511 sbb bx,2[di]
514 ;; mul function
515 lmull:
516 lmulul:
517 and eax, #0x0000FFFF
518 shl ebx, #16
519 or eax, ebx
520 SEG SS
521 mul eax, dword ptr [di]
522 mov ebx, eax
523 shr ebx, #16
526 ;; dec function
527 ldecl:
528 ldecul:
529 SEG SS
530 dec dword ptr [bx]
533 ;; or function
534 lorl:
535 lorul:
536 SEG SS
537 or ax,[di]
538 SEG SS
539 or bx,2[di]
542 ;; inc function
543 lincl:
544 lincul:
545 SEG SS
546 inc dword ptr [bx]
549 ;; tst function
550 ltstl:
551 ltstul:
552 and eax, #0x0000FFFF
553 shl ebx, #16
554 or eax, ebx
555 shr ebx, #16
556 test eax, eax
559 ;; sr function
560 lsrul:
561 mov cx,di
562 jcxz lsr_exit
563 and eax, #0x0000FFFF
564 shl ebx, #16
565 or eax, ebx
566 lsr_loop:
567 shr eax, #1
568 loop lsr_loop
569 mov ebx, eax
570 shr ebx, #16
571 lsr_exit:
574 ;; sl function
575 lsll:
576 lslul:
577 mov cx,di
578 jcxz lsl_exit
579 and eax, #0x0000FFFF
580 shl ebx, #16
581 or eax, ebx
582 lsl_loop:
583 shl eax, #1
584 loop lsl_loop
585 mov ebx, eax
586 shr ebx, #16
587 lsl_exit:
590 idiv_:
592 idiv bx
595 idiv_u:
596 xor dx,dx
597 div bx
600 ldivul:
601 and eax, #0x0000FFFF
602 shl ebx, #16
603 or eax, ebx
604 xor edx, edx
605 SEG SS
606 mov bx, 2[di]
607 shl ebx, #16
608 SEG SS
609 mov bx, [di]
610 div ebx
611 mov ebx, eax
612 shr ebx, #16
615 ASM_END
617 // for access to RAM area which is used by interrupt vectors
618 // and BIOS Data Area
620 typedef struct {
621 unsigned char filler1[0x400];
622 unsigned char filler2[0x6c];
623 Bit16u ticks_low;
624 Bit16u ticks_high;
625 Bit8u midnight_flag;
626 } bios_data_t;
628 #define BiosData ((bios_data_t *) 0)
630 #if BX_USE_ATADRV
631 typedef struct {
632 Bit16u heads; // # heads
633 Bit16u cylinders; // # cylinders
634 Bit16u spt; // # sectors / track
635 } chs_t;
637 // DPTE definition
638 typedef struct {
639 Bit16u iobase1;
640 Bit16u iobase2;
641 Bit8u prefix;
642 Bit8u unused;
643 Bit8u irq;
644 Bit8u blkcount;
645 Bit8u dma;
646 Bit8u pio;
647 Bit16u options;
648 Bit16u reserved;
649 Bit8u revision;
650 Bit8u checksum;
651 } dpte_t;
653 typedef struct {
654 Bit8u iface; // ISA or PCI
655 Bit16u iobase1; // IO Base 1
656 Bit16u iobase2; // IO Base 2
657 Bit8u irq; // IRQ
658 } ata_channel_t;
660 typedef struct {
661 Bit8u type; // Detected type of ata (ata/atapi/none/unknown)
662 Bit8u device; // Detected type of attached devices (hd/cd/none)
663 Bit8u removable; // Removable device flag
664 Bit8u lock; // Locks for removable devices
665 Bit8u mode; // transfer mode : PIO 16/32 bits - IRQ - ISADMA - PCIDMA
666 Bit16u blksize; // block size
668 Bit8u translation; // type of translation
669 chs_t lchs; // Logical CHS
670 chs_t pchs; // Physical CHS
672 Bit32u sectors; // Total sectors count
673 } ata_device_t;
675 typedef struct {
676 // ATA channels info
677 ata_channel_t channels[BX_MAX_ATA_INTERFACES];
679 // ATA devices info
680 ata_device_t devices[BX_MAX_ATA_DEVICES];
682 // map between (bios hd id - 0x80) and ata channels
683 Bit8u hdcount, hdidmap[BX_MAX_ATA_DEVICES];
685 // map between (bios cd id - 0xE0) and ata channels
686 Bit8u cdcount, cdidmap[BX_MAX_ATA_DEVICES];
688 // Buffer for DPTE table
689 dpte_t dpte;
691 // Count of transferred sectors and bytes
692 Bit16u trsfsectors;
693 Bit32u trsfbytes;
695 } ata_t;
697 #if BX_ELTORITO_BOOT
698 // ElTorito Device Emulation data
699 typedef struct {
700 Bit8u active;
701 Bit8u media;
702 Bit8u emulated_drive;
703 Bit8u controller_index;
704 Bit16u device_spec;
705 Bit32u ilba;
706 Bit16u buffer_segment;
707 Bit16u load_segment;
708 Bit16u sector_count;
710 // Virtual device
711 chs_t vdevice;
712 } cdemu_t;
713 #endif // BX_ELTORITO_BOOT
715 // for access to EBDA area
716 // The EBDA structure should conform to
717 // http://www.frontiernet.net/~fys/rombios.htm document
718 // I made the ata and cdemu structs begin at 0x121 in the EBDA seg
719 // EBDA must be at most 768 bytes; it lives at 0x9fc00, and the boot
720 // device tables are at 0x9ff00 -- 0x9ffff
721 typedef struct {
722 unsigned char filler1[0x3D];
724 // FDPT - Can be splitted in data members if needed
725 unsigned char fdpt0[0x10];
726 unsigned char fdpt1[0x10];
728 unsigned char filler2[0xC4];
730 // ATA Driver data
731 ata_t ata;
733 #if BX_ELTORITO_BOOT
734 // El Torito Emulation data
735 cdemu_t cdemu;
736 #endif // BX_ELTORITO_BOOT
738 } ebda_data_t;
740 #define EbdaData ((ebda_data_t *) 0)
742 // for access to the int13ext structure
743 typedef struct {
744 Bit8u size;
745 Bit8u reserved;
746 Bit16u count;
747 Bit16u offset;
748 Bit16u segment;
749 Bit32u lba1;
750 Bit32u lba2;
751 } int13ext_t;
753 #define Int13Ext ((int13ext_t *) 0)
755 // Disk Physical Table definition
756 typedef struct {
757 Bit16u size;
758 Bit16u infos;
759 Bit32u cylinders;
760 Bit32u heads;
761 Bit32u spt;
762 Bit32u sector_count1;
763 Bit32u sector_count2;
764 Bit16u blksize;
765 Bit16u dpte_offset;
766 Bit16u dpte_segment;
767 Bit16u key;
768 Bit8u dpi_length;
769 Bit8u reserved1;
770 Bit16u reserved2;
771 Bit8u host_bus[4];
772 Bit8u iface_type[8];
773 Bit8u iface_path[8];
774 Bit8u device_path[8];
775 Bit8u reserved3;
776 Bit8u checksum;
777 } dpt_t;
779 #define Int13DPT ((dpt_t *) 0)
781 #endif // BX_USE_ATADRV
783 typedef struct {
784 union {
785 struct {
786 Bit16u di, si, bp, sp;
787 Bit16u bx, dx, cx, ax;
788 } r16;
789 struct {
790 Bit16u filler[4];
791 Bit8u bl, bh, dl, dh, cl, ch, al, ah;
792 } r8;
793 } u;
794 } pusha_regs_t;
796 typedef struct {
797 union {
798 struct {
799 Bit32u edi, esi, ebp, esp;
800 Bit32u ebx, edx, ecx, eax;
801 } r32;
802 struct {
803 Bit16u di, filler1, si, filler2, bp, filler3, sp, filler4;
804 Bit16u bx, filler5, dx, filler6, cx, filler7, ax, filler8;
805 } r16;
806 struct {
807 Bit32u filler[4];
808 Bit8u bl, bh;
809 Bit16u filler1;
810 Bit8u dl, dh;
811 Bit16u filler2;
812 Bit8u cl, ch;
813 Bit16u filler3;
814 Bit8u al, ah;
815 Bit16u filler4;
816 } r8;
817 } u;
818 } pushad_regs_t;
820 typedef struct {
821 union {
822 struct {
823 Bit16u flags;
824 } r16;
825 struct {
826 Bit8u flagsl;
827 Bit8u flagsh;
828 } r8;
829 } u;
830 } flags_t;
832 #define SetCF(x) x.u.r8.flagsl |= 0x01
833 #define SetZF(x) x.u.r8.flagsl |= 0x40
834 #define ClearCF(x) x.u.r8.flagsl &= 0xfe
835 #define ClearZF(x) x.u.r8.flagsl &= 0xbf
836 #define GetCF(x) (x.u.r8.flagsl & 0x01)
838 typedef struct {
839 Bit16u ip;
840 Bit16u cs;
841 flags_t flags;
842 } iret_addr_t;
844 typedef struct {
845 Bit16u type;
846 Bit16u flags;
847 Bit32u vector;
848 Bit32u description;
849 Bit32u reserved;
850 } ipl_entry_t;
854 static Bit8u inb();
855 static Bit8u inb_cmos();
856 static void outb();
857 static void outb_cmos();
858 static Bit16u inw();
859 static void outw();
860 static void init_rtc();
861 static bx_bool rtc_updating();
863 static Bit8u read_byte();
864 static Bit16u read_word();
865 static void write_byte();
866 static void write_word();
867 static void bios_printf();
869 static Bit8u inhibit_mouse_int_and_events();
870 static void enable_mouse_int_and_events();
871 static Bit8u send_to_mouse_ctrl();
872 static Bit8u get_mouse_data();
873 static void set_kbd_command_byte();
875 static void int09_function();
876 static void int13_harddisk();
877 static void int13_cdrom();
878 static void int13_cdemu();
879 static void int13_eltorito();
880 static void int13_diskette_function();
881 static void int14_function();
882 static void int15_function();
883 static void int16_function();
884 static void int17_function();
885 static void int19_function();
886 static void int1a_function();
887 static void int70_function();
888 static void int74_function();
889 static Bit16u get_CS();
890 static Bit16u get_SS();
891 static unsigned int enqueue_key();
892 static unsigned int dequeue_key();
893 static void get_hd_geometry();
894 static void set_diskette_ret_status();
895 static void set_diskette_current_cyl();
896 static void determine_floppy_media();
897 static bx_bool floppy_drive_exists();
898 static bx_bool floppy_drive_recal();
899 static bx_bool floppy_media_known();
900 static bx_bool floppy_media_sense();
901 static bx_bool set_enable_a20();
902 static void debugger_on();
903 static void debugger_off();
904 static void keyboard_init();
905 static void keyboard_panic();
906 static void shutdown_status_panic();
907 static void nmi_handler_msg();
909 static void print_bios_banner();
910 static void print_boot_device();
911 static void print_boot_failure();
912 static void print_cdromboot_failure();
914 # if BX_USE_ATADRV
916 // ATA / ATAPI driver
917 void ata_init();
918 void ata_detect();
919 void ata_reset();
921 Bit16u ata_cmd_non_data();
922 Bit16u ata_cmd_data_in();
923 Bit16u ata_cmd_data_out();
924 Bit16u ata_cmd_packet();
926 Bit16u atapi_get_sense();
927 Bit16u atapi_is_ready();
928 Bit16u atapi_is_cdrom();
930 #endif // BX_USE_ATADRV
932 #if BX_ELTORITO_BOOT
934 void cdemu_init();
935 Bit8u cdemu_isactive();
936 Bit8u cdemu_emulated_drive();
938 Bit16u cdrom_boot();
940 #endif // BX_ELTORITO_BOOT
942 static char bios_cvs_version_string[] = "$Revision: 1.191 $ $Date: 2007/12/01 19:26:46 $";
944 #define BIOS_COPYRIGHT_STRING "(c) 2002 MandrakeSoft S.A. Written by Kevin Lawton & the Bochs team."
946 #if DEBUG_ATA
947 # define BX_DEBUG_ATA(a...) BX_DEBUG(a)
948 #else
949 # define BX_DEBUG_ATA(a...)
950 #endif
951 #if DEBUG_INT13_HD
952 # define BX_DEBUG_INT13_HD(a...) BX_DEBUG(a)
953 #else
954 # define BX_DEBUG_INT13_HD(a...)
955 #endif
956 #if DEBUG_INT13_CD
957 # define BX_DEBUG_INT13_CD(a...) BX_DEBUG(a)
958 #else
959 # define BX_DEBUG_INT13_CD(a...)
960 #endif
961 #if DEBUG_INT13_ET
962 # define BX_DEBUG_INT13_ET(a...) BX_DEBUG(a)
963 #else
964 # define BX_DEBUG_INT13_ET(a...)
965 #endif
966 #if DEBUG_INT13_FL
967 # define BX_DEBUG_INT13_FL(a...) BX_DEBUG(a)
968 #else
969 # define BX_DEBUG_INT13_FL(a...)
970 #endif
971 #if DEBUG_INT15
972 # define BX_DEBUG_INT15(a...) BX_DEBUG(a)
973 #else
974 # define BX_DEBUG_INT15(a...)
975 #endif
976 #if DEBUG_INT16
977 # define BX_DEBUG_INT16(a...) BX_DEBUG(a)
978 #else
979 # define BX_DEBUG_INT16(a...)
980 #endif
981 #if DEBUG_INT1A
982 # define BX_DEBUG_INT1A(a...) BX_DEBUG(a)
983 #else
984 # define BX_DEBUG_INT1A(a...)
985 #endif
986 #if DEBUG_INT74
987 # define BX_DEBUG_INT74(a...) BX_DEBUG(a)
988 #else
989 # define BX_DEBUG_INT74(a...)
990 #endif
992 #define SET_AL(val8) AX = ((AX & 0xff00) | (val8))
993 #define SET_BL(val8) BX = ((BX & 0xff00) | (val8))
994 #define SET_CL(val8) CX = ((CX & 0xff00) | (val8))
995 #define SET_DL(val8) DX = ((DX & 0xff00) | (val8))
996 #define SET_AH(val8) AX = ((AX & 0x00ff) | ((val8) << 8))
997 #define SET_BH(val8) BX = ((BX & 0x00ff) | ((val8) << 8))
998 #define SET_CH(val8) CX = ((CX & 0x00ff) | ((val8) << 8))
999 #define SET_DH(val8) DX = ((DX & 0x00ff) | ((val8) << 8))
1001 #define GET_AL() ( AX & 0x00ff )
1002 #define GET_BL() ( BX & 0x00ff )
1003 #define GET_CL() ( CX & 0x00ff )
1004 #define GET_DL() ( DX & 0x00ff )
1005 #define GET_AH() ( AX >> 8 )
1006 #define GET_BH() ( BX >> 8 )
1007 #define GET_CH() ( CX >> 8 )
1008 #define GET_DH() ( DX >> 8 )
1010 #define GET_ELDL() ( ELDX & 0x00ff )
1011 #define GET_ELDH() ( ELDX >> 8 )
1013 #define SET_CF() FLAGS |= 0x0001
1014 #define CLEAR_CF() FLAGS &= 0xfffe
1015 #define GET_CF() (FLAGS & 0x0001)
1017 #define SET_ZF() FLAGS |= 0x0040
1018 #define CLEAR_ZF() FLAGS &= 0xffbf
1019 #define GET_ZF() (FLAGS & 0x0040)
1021 #define UNSUPPORTED_FUNCTION 0x86
1023 #define none 0
1024 #define MAX_SCAN_CODE 0x58
1026 static struct {
1027 Bit16u normal;
1028 Bit16u shift;
1029 Bit16u control;
1030 Bit16u alt;
1031 Bit8u lock_flags;
1032 } scan_to_scanascii[MAX_SCAN_CODE + 1] = {
1033 { none, none, none, none, none },
1034 { 0x011b, 0x011b, 0x011b, 0x0100, none }, /* escape */
1035 { 0x0231, 0x0221, none, 0x7800, none }, /* 1! */
1036 { 0x0332, 0x0340, 0x0300, 0x7900, none }, /* 2@ */
1037 { 0x0433, 0x0423, none, 0x7a00, none }, /* 3# */
1038 { 0x0534, 0x0524, none, 0x7b00, none }, /* 4$ */
1039 { 0x0635, 0x0625, none, 0x7c00, none }, /* 5% */
1040 { 0x0736, 0x075e, 0x071e, 0x7d00, none }, /* 6^ */
1041 { 0x0837, 0x0826, none, 0x7e00, none }, /* 7& */
1042 { 0x0938, 0x092a, none, 0x7f00, none }, /* 8* */
1043 { 0x0a39, 0x0a28, none, 0x8000, none }, /* 9( */
1044 { 0x0b30, 0x0b29, none, 0x8100, none }, /* 0) */
1045 { 0x0c2d, 0x0c5f, 0x0c1f, 0x8200, none }, /* -_ */
1046 { 0x0d3d, 0x0d2b, none, 0x8300, none }, /* =+ */
1047 { 0x0e08, 0x0e08, 0x0e7f, none, none }, /* backspace */
1048 { 0x0f09, 0x0f00, none, none, none }, /* tab */
1049 { 0x1071, 0x1051, 0x1011, 0x1000, 0x40 }, /* Q */
1050 { 0x1177, 0x1157, 0x1117, 0x1100, 0x40 }, /* W */
1051 { 0x1265, 0x1245, 0x1205, 0x1200, 0x40 }, /* E */
1052 { 0x1372, 0x1352, 0x1312, 0x1300, 0x40 }, /* R */
1053 { 0x1474, 0x1454, 0x1414, 0x1400, 0x40 }, /* T */
1054 { 0x1579, 0x1559, 0x1519, 0x1500, 0x40 }, /* Y */
1055 { 0x1675, 0x1655, 0x1615, 0x1600, 0x40 }, /* U */
1056 { 0x1769, 0x1749, 0x1709, 0x1700, 0x40 }, /* I */
1057 { 0x186f, 0x184f, 0x180f, 0x1800, 0x40 }, /* O */
1058 { 0x1970, 0x1950, 0x1910, 0x1900, 0x40 }, /* P */
1059 { 0x1a5b, 0x1a7b, 0x1a1b, none, none }, /* [{ */
1060 { 0x1b5d, 0x1b7d, 0x1b1d, none, none }, /* ]} */
1061 { 0x1c0d, 0x1c0d, 0x1c0a, none, none }, /* Enter */
1062 { none, none, none, none, none }, /* L Ctrl */
1063 { 0x1e61, 0x1e41, 0x1e01, 0x1e00, 0x40 }, /* A */
1064 { 0x1f73, 0x1f53, 0x1f13, 0x1f00, 0x40 }, /* S */
1065 { 0x2064, 0x2044, 0x2004, 0x2000, 0x40 }, /* D */
1066 { 0x2166, 0x2146, 0x2106, 0x2100, 0x40 }, /* F */
1067 { 0x2267, 0x2247, 0x2207, 0x2200, 0x40 }, /* G */
1068 { 0x2368, 0x2348, 0x2308, 0x2300, 0x40 }, /* H */
1069 { 0x246a, 0x244a, 0x240a, 0x2400, 0x40 }, /* J */
1070 { 0x256b, 0x254b, 0x250b, 0x2500, 0x40 }, /* K */
1071 { 0x266c, 0x264c, 0x260c, 0x2600, 0x40 }, /* L */
1072 { 0x273b, 0x273a, none, none, none }, /* ;: */
1073 { 0x2827, 0x2822, none, none, none }, /* '" */
1074 { 0x2960, 0x297e, none, none, none }, /* `~ */
1075 { none, none, none, none, none }, /* L shift */
1076 { 0x2b5c, 0x2b7c, 0x2b1c, none, none }, /* |\ */
1077 { 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00, 0x40 }, /* Z */
1078 { 0x2d78, 0x2d58, 0x2d18, 0x2d00, 0x40 }, /* X */
1079 { 0x2e63, 0x2e43, 0x2e03, 0x2e00, 0x40 }, /* C */
1080 { 0x2f76, 0x2f56, 0x2f16, 0x2f00, 0x40 }, /* V */
1081 { 0x3062, 0x3042, 0x3002, 0x3000, 0x40 }, /* B */
1082 { 0x316e, 0x314e, 0x310e, 0x3100, 0x40 }, /* N */
1083 { 0x326d, 0x324d, 0x320d, 0x3200, 0x40 }, /* M */
1084 { 0x332c, 0x333c, none, none, none }, /* ,< */
1085 { 0x342e, 0x343e, none, none, none }, /* .> */
1086 { 0x352f, 0x353f, none, none, none }, /* /? */
1087 { none, none, none, none, none }, /* R Shift */
1088 { 0x372a, 0x372a, none, none, none }, /* * */
1089 { none, none, none, none, none }, /* L Alt */
1090 { 0x3920, 0x3920, 0x3920, 0x3920, none }, /* space */
1091 { none, none, none, none, none }, /* caps lock */
1092 { 0x3b00, 0x5400, 0x5e00, 0x6800, none }, /* F1 */
1093 { 0x3c00, 0x5500, 0x5f00, 0x6900, none }, /* F2 */
1094 { 0x3d00, 0x5600, 0x6000, 0x6a00, none }, /* F3 */
1095 { 0x3e00, 0x5700, 0x6100, 0x6b00, none }, /* F4 */
1096 { 0x3f00, 0x5800, 0x6200, 0x6c00, none }, /* F5 */
1097 { 0x4000, 0x5900, 0x6300, 0x6d00, none }, /* F6 */
1098 { 0x4100, 0x5a00, 0x6400, 0x6e00, none }, /* F7 */
1099 { 0x4200, 0x5b00, 0x6500, 0x6f00, none }, /* F8 */
1100 { 0x4300, 0x5c00, 0x6600, 0x7000, none }, /* F9 */
1101 { 0x4400, 0x5d00, 0x6700, 0x7100, none }, /* F10 */
1102 { none, none, none, none, none }, /* Num Lock */
1103 { none, none, none, none, none }, /* Scroll Lock */
1104 { 0x4700, 0x4737, 0x7700, none, 0x20 }, /* 7 Home */
1105 { 0x4800, 0x4838, none, none, 0x20 }, /* 8 UP */
1106 { 0x4900, 0x4939, 0x8400, none, 0x20 }, /* 9 PgUp */
1107 { 0x4a2d, 0x4a2d, none, none, none }, /* - */
1108 { 0x4b00, 0x4b34, 0x7300, none, 0x20 }, /* 4 Left */
1109 { 0x4c00, 0x4c35, none, none, 0x20 }, /* 5 */
1110 { 0x4d00, 0x4d36, 0x7400, none, 0x20 }, /* 6 Right */
1111 { 0x4e2b, 0x4e2b, none, none, none }, /* + */
1112 { 0x4f00, 0x4f31, 0x7500, none, 0x20 }, /* 1 End */
1113 { 0x5000, 0x5032, none, none, 0x20 }, /* 2 Down */
1114 { 0x5100, 0x5133, 0x7600, none, 0x20 }, /* 3 PgDn */
1115 { 0x5200, 0x5230, none, none, 0x20 }, /* 0 Ins */
1116 { 0x5300, 0x532e, none, none, 0x20 }, /* Del */
1117 { none, none, none, none, none },
1118 { none, none, none, none, none },
1119 { 0x565c, 0x567c, none, none, none }, /* \| */
1120 { 0x5700, 0x5700, none, none, none }, /* F11 */
1121 { 0x5800, 0x5800, none, none, none } /* F12 */
1124 Bit8u
1125 inb(port)
1126 Bit16u port;
1128 ASM_START
1129 push bp
1130 mov bp, sp
1132 push dx
1133 mov dx, 4[bp]
1134 in al, dx
1135 pop dx
1137 pop bp
1138 ASM_END
1141 #if BX_USE_ATADRV
1142 Bit16u
1143 inw(port)
1144 Bit16u port;
1146 ASM_START
1147 push bp
1148 mov bp, sp
1150 push dx
1151 mov dx, 4[bp]
1152 in ax, dx
1153 pop dx
1155 pop bp
1156 ASM_END
1158 #endif
1160 void
1161 outb(port, val)
1162 Bit16u port;
1163 Bit8u val;
1165 ASM_START
1166 push bp
1167 mov bp, sp
1169 push ax
1170 push dx
1171 mov dx, 4[bp]
1172 mov al, 6[bp]
1173 out dx, al
1174 pop dx
1175 pop ax
1177 pop bp
1178 ASM_END
1181 #if BX_USE_ATADRV
1182 void
1183 outw(port, val)
1184 Bit16u port;
1185 Bit16u val;
1187 ASM_START
1188 push bp
1189 mov bp, sp
1191 push ax
1192 push dx
1193 mov dx, 4[bp]
1194 mov ax, 6[bp]
1195 out dx, ax
1196 pop dx
1197 pop ax
1199 pop bp
1200 ASM_END
1202 #endif
1204 void
1205 outb_cmos(cmos_reg, val)
1206 Bit8u cmos_reg;
1207 Bit8u val;
1209 ASM_START
1210 push bp
1211 mov bp, sp
1213 mov al, 4[bp] ;; cmos_reg
1214 out 0x70, al
1215 mov al, 6[bp] ;; val
1216 out 0x71, al
1218 pop bp
1219 ASM_END
1222 Bit8u
1223 inb_cmos(cmos_reg)
1224 Bit8u cmos_reg;
1226 ASM_START
1227 push bp
1228 mov bp, sp
1230 mov al, 4[bp] ;; cmos_reg
1231 out 0x70, al
1232 in al, 0x71
1234 pop bp
1235 ASM_END
1238 void
1239 init_rtc()
1241 outb_cmos(0x0a, 0x26);
1242 outb_cmos(0x0b, 0x02);
1243 inb_cmos(0x0c);
1244 inb_cmos(0x0d);
1247 bx_bool
1248 rtc_updating()
1250 // This function checks to see if the update-in-progress bit
1251 // is set in CMOS Status Register A. If not, it returns 0.
1252 // If it is set, it tries to wait until there is a transition
1253 // to 0, and will return 0 if such a transition occurs. A 1
1254 // is returned only after timing out. The maximum period
1255 // that this bit should be set is constrained to 244useconds.
1256 // The count I use below guarantees coverage or more than
1257 // this time, with any reasonable IPS setting.
1259 Bit16u count;
1261 count = 25000;
1262 while (--count != 0) {
1263 if ( (inb_cmos(0x0a) & 0x80) == 0 )
1264 return(0);
1266 return(1); // update-in-progress never transitioned to 0
1270 Bit8u
1271 read_byte(seg, offset)
1272 Bit16u seg;
1273 Bit16u offset;
1275 ASM_START
1276 push bp
1277 mov bp, sp
1279 push bx
1280 push ds
1281 mov ax, 4[bp] ; segment
1282 mov ds, ax
1283 mov bx, 6[bp] ; offset
1284 mov al, [bx]
1285 ;; al = return value (byte)
1286 pop ds
1287 pop bx
1289 pop bp
1290 ASM_END
1293 Bit16u
1294 read_word(seg, offset)
1295 Bit16u seg;
1296 Bit16u offset;
1298 ASM_START
1299 push bp
1300 mov bp, sp
1302 push bx
1303 push ds
1304 mov ax, 4[bp] ; segment
1305 mov ds, ax
1306 mov bx, 6[bp] ; offset
1307 mov ax, [bx]
1308 ;; ax = return value (word)
1309 pop ds
1310 pop bx
1312 pop bp
1313 ASM_END
1316 void
1317 write_byte(seg, offset, data)
1318 Bit16u seg;
1319 Bit16u offset;
1320 Bit8u data;
1322 ASM_START
1323 push bp
1324 mov bp, sp
1326 push ax
1327 push bx
1328 push ds
1329 mov ax, 4[bp] ; segment
1330 mov ds, ax
1331 mov bx, 6[bp] ; offset
1332 mov al, 8[bp] ; data byte
1333 mov [bx], al ; write data byte
1334 pop ds
1335 pop bx
1336 pop ax
1338 pop bp
1339 ASM_END
1342 void
1343 write_word(seg, offset, data)
1344 Bit16u seg;
1345 Bit16u offset;
1346 Bit16u data;
1348 ASM_START
1349 push bp
1350 mov bp, sp
1352 push ax
1353 push bx
1354 push ds
1355 mov ax, 4[bp] ; segment
1356 mov ds, ax
1357 mov bx, 6[bp] ; offset
1358 mov ax, 8[bp] ; data word
1359 mov [bx], ax ; write data word
1360 pop ds
1361 pop bx
1362 pop ax
1364 pop bp
1365 ASM_END
1368 Bit16u
1369 get_CS()
1371 ASM_START
1372 mov ax, cs
1373 ASM_END
1376 Bit16u
1377 get_SS()
1379 ASM_START
1380 mov ax, ss
1381 ASM_END
1384 #if BX_DEBUG_SERIAL
1385 /* serial debug port*/
1386 #define BX_DEBUG_PORT 0x03f8
1388 /* data */
1389 #define UART_RBR 0x00
1390 #define UART_THR 0x00
1392 /* control */
1393 #define UART_IER 0x01
1394 #define UART_IIR 0x02
1395 #define UART_FCR 0x02
1396 #define UART_LCR 0x03
1397 #define UART_MCR 0x04
1398 #define UART_DLL 0x00
1399 #define UART_DLM 0x01
1401 /* status */
1402 #define UART_LSR 0x05
1403 #define UART_MSR 0x06
1404 #define UART_SCR 0x07
1406 int uart_can_tx_byte(base_port)
1407 Bit16u base_port;
1409 return inb(base_port + UART_LSR) & 0x20;
1412 void uart_wait_to_tx_byte(base_port)
1413 Bit16u base_port;
1415 while (!uart_can_tx_byte(base_port));
1418 void uart_wait_until_sent(base_port)
1419 Bit16u base_port;
1421 while (!(inb(base_port + UART_LSR) & 0x40));
1424 void uart_tx_byte(base_port, data)
1425 Bit16u base_port;
1426 Bit8u data;
1428 uart_wait_to_tx_byte(base_port);
1429 outb(base_port + UART_THR, data);
1430 uart_wait_until_sent(base_port);
1432 #endif
1434 void
1435 wrch(c)
1436 Bit8u c;
1438 ASM_START
1439 push bp
1440 mov bp, sp
1442 push bx
1443 mov ah, #0x0e
1444 mov al, 4[bp]
1445 xor bx,bx
1446 int #0x10
1447 pop bx
1449 pop bp
1450 ASM_END
1453 void
1454 send(action, c)
1455 Bit16u action;
1456 Bit8u c;
1458 #if BX_DEBUG_SERIAL
1459 if (c == '\n') uart_tx_byte(BX_DEBUG_PORT, '\r');
1460 uart_tx_byte(BX_DEBUG_PORT, c);
1461 #endif
1462 #if BX_VIRTUAL_PORTS
1463 if (action & BIOS_PRINTF_DEBUG) outb(DEBUG_PORT, c);
1464 if (action & BIOS_PRINTF_INFO) outb(INFO_PORT, c);
1465 #endif
1466 if (action & BIOS_PRINTF_SCREEN) {
1467 if (c == '\n') wrch('\r');
1468 wrch(c);
1472 void
1473 put_int(action, val, width, neg)
1474 Bit16u action;
1475 short val, width;
1476 bx_bool neg;
1478 short nval = val / 10;
1479 if (nval)
1480 put_int(action, nval, width - 1, neg);
1481 else {
1482 while (--width > 0) send(action, ' ');
1483 if (neg) send(action, '-');
1485 send(action, val - (nval * 10) + '0');
1488 void
1489 put_uint(action, val, width, neg)
1490 Bit16u action;
1491 unsigned short val;
1492 short width;
1493 bx_bool neg;
1495 unsigned short nval = val / 10;
1496 if (nval)
1497 put_uint(action, nval, width - 1, neg);
1498 else {
1499 while (--width > 0) send(action, ' ');
1500 if (neg) send(action, '-');
1502 send(action, val - (nval * 10) + '0');
1505 void
1506 put_luint(action, val, width, neg)
1507 Bit16u action;
1508 unsigned long val;
1509 short width;
1510 bx_bool neg;
1512 unsigned long nval = val / 10;
1513 if (nval)
1514 put_luint(action, nval, width - 1, neg);
1515 else {
1516 while (--width > 0) send(action, ' ');
1517 if (neg) send(action, '-');
1519 send(action, val - (nval * 10) + '0');
1522 void put_str(action, s)
1523 Bit16u action;
1524 Bit8u *s;
1526 Bit8u c;
1527 if (!s)
1528 s = "<NULL>";
1530 while (c = read_byte(get_CS(), s)) {
1531 send(action, c);
1532 s++;
1536 //--------------------------------------------------------------------------
1537 // bios_printf()
1538 // A compact variable argument printf function.
1540 // Supports %[format_width][length]format
1541 // where format can be x,X,u,d,s,c
1542 // and the optional length modifier is l (ell)
1543 //--------------------------------------------------------------------------
1544 void
1545 bios_printf(action, s)
1546 Bit16u action;
1547 Bit8u *s;
1549 Bit8u c, format_char;
1550 bx_bool in_format;
1551 short i;
1552 Bit16u *arg_ptr;
1553 Bit16u arg_seg, arg, nibble, hibyte, shift_count, format_width, hexadd;
1555 arg_ptr = &s;
1556 arg_seg = get_SS();
1558 in_format = 0;
1559 format_width = 0;
1561 if ((action & BIOS_PRINTF_DEBHALT) == BIOS_PRINTF_DEBHALT) {
1562 #if BX_VIRTUAL_PORTS
1563 outb(PANIC_PORT2, 0x00);
1564 #endif
1565 bios_printf (BIOS_PRINTF_SCREEN, "FATAL: ");
1568 while (c = read_byte(get_CS(), s)) {
1569 if ( c == '%' ) {
1570 in_format = 1;
1571 format_width = 0;
1573 else if (in_format) {
1574 if ( (c>='0') && (c<='9') ) {
1575 format_width = (format_width * 10) + (c - '0');
1577 else {
1578 arg_ptr++; // increment to next arg
1579 arg = read_word(arg_seg, arg_ptr);
1580 if (c == 'x' || c == 'X') {
1581 if (format_width == 0)
1582 format_width = 4;
1583 if (c == 'x')
1584 hexadd = 'a';
1585 else
1586 hexadd = 'A';
1587 for (i=format_width-1; i>=0; i--) {
1588 nibble = (arg >> (4 * i)) & 0x000f;
1589 send (action, (nibble<=9)? (nibble+'0') : (nibble-10+hexadd));
1592 else if (c == 'u') {
1593 put_uint(action, arg, format_width, 0);
1595 else if (c == 'l') {
1596 s++;
1597 c = read_byte(get_CS(), s); /* is it ld,lx,lu? */
1598 arg_ptr++; /* increment to next arg */
1599 hibyte = read_word(arg_seg, arg_ptr);
1600 if (c == 'd') {
1601 if (hibyte & 0x8000)
1602 put_luint(action, 0L-(((Bit32u) hibyte << 16) | arg), format_width-1, 1);
1603 else
1604 put_luint(action, ((Bit32u) hibyte << 16) | arg, format_width, 0);
1606 else if (c == 'u') {
1607 put_luint(action, ((Bit32u) hibyte << 16) | arg, format_width, 0);
1609 else if (c == 'x' || c == 'X')
1611 if (format_width == 0)
1612 format_width = 8;
1613 if (c == 'x')
1614 hexadd = 'a';
1615 else
1616 hexadd = 'A';
1617 for (i=format_width-1; i>=0; i--) {
1618 nibble = ((((Bit32u) hibyte <<16) | arg) >> (4 * i)) & 0x000f;
1619 send (action, (nibble<=9)? (nibble+'0') : (nibble-10+hexadd));
1623 else if (c == 'd') {
1624 if (arg & 0x8000)
1625 put_int(action, -arg, format_width - 1, 1);
1626 else
1627 put_int(action, arg, format_width, 0);
1629 else if (c == 's') {
1630 put_str(action, arg);
1632 else if (c == 'c') {
1633 send(action, arg);
1635 else
1636 BX_PANIC("bios_printf: unknown format\n");
1637 in_format = 0;
1640 else {
1641 send(action, c);
1643 s ++;
1646 if (action & BIOS_PRINTF_HALT) {
1647 // freeze in a busy loop.
1648 ASM_START
1650 halt2_loop:
1652 jmp halt2_loop
1653 ASM_END
1657 //--------------------------------------------------------------------------
1658 // keyboard_init
1659 //--------------------------------------------------------------------------
1660 // this file is based on LinuxBIOS implementation of keyboard.c
1661 // could convert to #asm to gain space
1662 void
1663 keyboard_init()
1665 Bit16u max;
1667 /* ------------------- Flush buffers ------------------------*/
1668 /* Wait until buffer is empty */
1669 max=0xffff;
1670 while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
1672 /* flush incoming keys */
1673 max=0x2000;
1674 while (--max > 0) {
1675 outb(0x80, 0x00);
1676 if (inb(0x64) & 0x01) {
1677 inb(0x60);
1678 max = 0x2000;
1682 // Due to timer issues, and if the IPS setting is > 15000000,
1683 // the incoming keys might not be flushed here. That will
1684 // cause a panic a few lines below. See sourceforge bug report :
1685 // [ 642031 ] FATAL: Keyboard RESET error:993
1687 /* ------------------- controller side ----------------------*/
1688 /* send cmd = 0xAA, self test 8042 */
1689 outb(0x64, 0xaa);
1691 /* Wait until buffer is empty */
1692 max=0xffff;
1693 while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
1694 if (max==0x0) keyboard_panic(00);
1696 /* Wait for data */
1697 max=0xffff;
1698 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x01);
1699 if (max==0x0) keyboard_panic(01);
1701 /* read self-test result, 0x55 should be returned from 0x60 */
1702 if ((inb(0x60) != 0x55)){
1703 keyboard_panic(991);
1706 /* send cmd = 0xAB, keyboard interface test */
1707 outb(0x64,0xab);
1709 /* Wait until buffer is empty */
1710 max=0xffff;
1711 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x10);
1712 if (max==0x0) keyboard_panic(10);
1714 /* Wait for data */
1715 max=0xffff;
1716 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x11);
1717 if (max==0x0) keyboard_panic(11);
1719 /* read keyboard interface test result, */
1720 /* 0x00 should be returned form 0x60 */
1721 if ((inb(0x60) != 0x00)) {
1722 keyboard_panic(992);
1725 /* Enable Keyboard clock */
1726 outb(0x64,0xae);
1727 outb(0x64,0xa8);
1729 /* ------------------- keyboard side ------------------------*/
1730 /* reset kerboard and self test (keyboard side) */
1731 outb(0x60, 0xff);
1733 /* Wait until buffer is empty */
1734 max=0xffff;
1735 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x20);
1736 if (max==0x0) keyboard_panic(20);
1738 /* Wait for data */
1739 max=0xffff;
1740 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x21);
1741 if (max==0x0) keyboard_panic(21);
1743 /* keyboard should return ACK */
1744 if ((inb(0x60) != 0xfa)) {
1745 keyboard_panic(993);
1748 /* Wait for data */
1749 max=0xffff;
1750 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x31);
1751 if (max==0x0) keyboard_panic(31);
1753 if ((inb(0x60) != 0xaa)) {
1754 keyboard_panic(994);
1757 /* Disable keyboard */
1758 outb(0x60, 0xf5);
1760 /* Wait until buffer is empty */
1761 max=0xffff;
1762 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x40);
1763 if (max==0x0) keyboard_panic(40);
1765 /* Wait for data */
1766 max=0xffff;
1767 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x41);
1768 if (max==0x0) keyboard_panic(41);
1770 /* keyboard should return ACK */
1771 if ((inb(0x60) != 0xfa)) {
1772 keyboard_panic(995);
1775 /* Write Keyboard Mode */
1776 outb(0x64, 0x60);
1778 /* Wait until buffer is empty */
1779 max=0xffff;
1780 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x50);
1781 if (max==0x0) keyboard_panic(50);
1783 /* send cmd: scan code convert, disable mouse, enable IRQ 1 */
1784 outb(0x60, 0x61);
1786 /* Wait until buffer is empty */
1787 max=0xffff;
1788 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x60);
1789 if (max==0x0) keyboard_panic(60);
1791 /* Enable keyboard */
1792 outb(0x60, 0xf4);
1794 /* Wait until buffer is empty */
1795 max=0xffff;
1796 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x70);
1797 if (max==0x0) keyboard_panic(70);
1799 /* Wait for data */
1800 max=0xffff;
1801 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x71);
1802 if (max==0x0) keyboard_panic(70);
1804 /* keyboard should return ACK */
1805 if ((inb(0x60) != 0xfa)) {
1806 keyboard_panic(996);
1809 outb(0x80, 0x77);
1812 //--------------------------------------------------------------------------
1813 // keyboard_panic
1814 //--------------------------------------------------------------------------
1815 void
1816 keyboard_panic(status)
1817 Bit16u status;
1819 // If you're getting a 993 keyboard panic here,
1820 // please see the comment in keyboard_init
1822 BX_PANIC("Keyboard error:%u\n",status);
1825 //--------------------------------------------------------------------------
1826 // shutdown_status_panic
1827 // called when the shutdown statsu is not implemented, displays the status
1828 //--------------------------------------------------------------------------
1829 void
1830 shutdown_status_panic(status)
1831 Bit16u status;
1833 BX_PANIC("Unimplemented shutdown status: %02x\n",(Bit8u)status);
1836 //--------------------------------------------------------------------------
1837 // print_bios_banner
1838 // displays a the bios version
1839 //--------------------------------------------------------------------------
1840 void
1841 print_bios_banner()
1843 printf(BX_APPNAME" BIOS - build: %s\n%s\nOptions: ",
1844 BIOS_BUILD_DATE, bios_cvs_version_string);
1845 printf(
1846 #if BX_APM
1847 "apmbios "
1848 #endif
1849 #if BX_PCIBIOS
1850 "pcibios "
1851 #endif
1852 #if BX_ELTORITO_BOOT
1853 "eltorito "
1854 #endif
1855 #if BX_ROMBIOS32
1856 "rombios32 "
1857 #endif
1858 "\n\n");
1861 //--------------------------------------------------------------------------
1862 // BIOS Boot Specification 1.0.1 compatibility
1864 // Very basic support for the BIOS Boot Specification, which allows expansion
1865 // ROMs to register themselves as boot devices, instead of just stealing the
1866 // INT 19h boot vector.
1868 // This is a hack: to do it properly requires a proper PnP BIOS and we aren't
1869 // one; we just lie to the option ROMs to make them behave correctly.
1870 // We also don't support letting option ROMs register as bootable disk
1871 // drives (BCVs), only as bootable devices (BEVs).
1873 // http://www.phoenix.com/en/Customer+Services/White+Papers-Specs/pc+industry+specifications.htm
1874 //--------------------------------------------------------------------------
1877 static void
1878 init_boot_vectors()
1880 ipl_entry_t e;
1881 Bit16u count = 0;
1882 Bit16u ss = get_SS();
1884 /* Clear out the IPL table. */
1885 memsetb(IPL_SEG, IPL_TABLE_OFFSET, 0, 0xff);
1887 /* Floppy drive */
1888 e.type = 1; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
1889 memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
1890 count++;
1892 /* First HDD */
1893 e.type = 2; 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 #if BX_ELTORITO_BOOT
1898 /* CDROM */
1899 e.type = 3; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
1900 memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
1901 count++;
1902 #endif
1904 /* Remember how many devices we have */
1905 write_word(IPL_SEG, IPL_COUNT_OFFSET, count);
1906 /* Not tried booting anything yet */
1907 write_word(IPL_SEG, IPL_SEQUENCE_OFFSET, 0xffff);
1910 static Bit8u
1911 get_boot_vector(i, e)
1912 Bit16u i; ipl_entry_t *e;
1914 Bit16u count;
1915 Bit16u ss = get_SS();
1916 /* Get the count of boot devices, and refuse to overrun the array */
1917 count = read_word(IPL_SEG, IPL_COUNT_OFFSET);
1918 if (i >= count) return 0;
1919 /* OK to read this device */
1920 memcpyb(ss, e, IPL_SEG, IPL_TABLE_OFFSET + i * sizeof (*e), sizeof (*e));
1921 return 1;
1925 //--------------------------------------------------------------------------
1926 // print_boot_device
1927 // displays the boot device
1928 //--------------------------------------------------------------------------
1930 static char drivetypes[][10]={"", "Floppy","Hard Disk","CD-Rom", "Network"};
1932 void
1933 print_boot_device(type)
1934 Bit16u type;
1936 /* NIC appears as type 0x80 */
1937 if (type == 0x80 ) type = 0x4;
1938 if (type == 0 || type > 0x4) BX_PANIC("Bad drive type\n");
1939 printf("Booting from %s...\n", drivetypes[type]);
1942 //--------------------------------------------------------------------------
1943 // print_boot_failure
1944 // displays the reason why boot failed
1945 //--------------------------------------------------------------------------
1946 void
1947 print_boot_failure(type, reason)
1948 Bit16u type; Bit8u reason;
1950 if (type == 0 || type > 0x3) BX_PANIC("Bad drive type\n");
1952 printf("Boot from %s failed", drivetypes[type]);
1953 if (type < 4) {
1954 /* Report the reason too */
1955 if (reason==0)
1956 printf(": not a bootable disk");
1957 else
1958 printf(": could not read the boot disk");
1960 printf("\n");
1963 //--------------------------------------------------------------------------
1964 // print_cdromboot_failure
1965 // displays the reason why boot failed
1966 //--------------------------------------------------------------------------
1967 void
1968 print_cdromboot_failure( code )
1969 Bit16u code;
1971 bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, "CDROM boot failure code : %04x\n",code);
1973 return;
1976 void
1977 nmi_handler_msg()
1979 BX_PANIC("NMI Handler called\n");
1982 void
1983 int18_panic_msg()
1985 BX_PANIC("INT18: BOOT FAILURE\n");
1988 void
1989 log_bios_start()
1991 #if BX_DEBUG_SERIAL
1992 outb(BX_DEBUG_PORT+UART_LCR, 0x03); /* setup for serial logging: 8N1 */
1993 #endif
1994 BX_INFO("%s\n", bios_cvs_version_string);
1997 bx_bool
1998 set_enable_a20(val)
1999 bx_bool val;
2001 Bit8u oldval;
2003 // Use PS2 System Control port A to set A20 enable
2005 // get current setting first
2006 oldval = inb(0x92);
2008 // change A20 status
2009 if (val)
2010 outb(0x92, oldval | 0x02);
2011 else
2012 outb(0x92, oldval & 0xfd);
2014 return((oldval & 0x02) != 0);
2017 void
2018 debugger_on()
2020 outb(0xfedc, 0x01);
2023 void
2024 debugger_off()
2026 outb(0xfedc, 0x00);
2029 #if BX_USE_ATADRV
2031 // ---------------------------------------------------------------------------
2032 // Start of ATA/ATAPI Driver
2033 // ---------------------------------------------------------------------------
2035 // Global defines -- ATA register and register bits.
2036 // command block & control block regs
2037 #define ATA_CB_DATA 0 // data reg in/out pio_base_addr1+0
2038 #define ATA_CB_ERR 1 // error in pio_base_addr1+1
2039 #define ATA_CB_FR 1 // feature reg out pio_base_addr1+1
2040 #define ATA_CB_SC 2 // sector count in/out pio_base_addr1+2
2041 #define ATA_CB_SN 3 // sector number in/out pio_base_addr1+3
2042 #define ATA_CB_CL 4 // cylinder low in/out pio_base_addr1+4
2043 #define ATA_CB_CH 5 // cylinder high in/out pio_base_addr1+5
2044 #define ATA_CB_DH 6 // device head in/out pio_base_addr1+6
2045 #define ATA_CB_STAT 7 // primary status in pio_base_addr1+7
2046 #define ATA_CB_CMD 7 // command out pio_base_addr1+7
2047 #define ATA_CB_ASTAT 6 // alternate status in pio_base_addr2+6
2048 #define ATA_CB_DC 6 // device control out pio_base_addr2+6
2049 #define ATA_CB_DA 7 // device address in pio_base_addr2+7
2051 #define ATA_CB_ER_ICRC 0x80 // ATA Ultra DMA bad CRC
2052 #define ATA_CB_ER_BBK 0x80 // ATA bad block
2053 #define ATA_CB_ER_UNC 0x40 // ATA uncorrected error
2054 #define ATA_CB_ER_MC 0x20 // ATA media change
2055 #define ATA_CB_ER_IDNF 0x10 // ATA id not found
2056 #define ATA_CB_ER_MCR 0x08 // ATA media change request
2057 #define ATA_CB_ER_ABRT 0x04 // ATA command aborted
2058 #define ATA_CB_ER_NTK0 0x02 // ATA track 0 not found
2059 #define ATA_CB_ER_NDAM 0x01 // ATA address mark not found
2061 #define ATA_CB_ER_P_SNSKEY 0xf0 // ATAPI sense key (mask)
2062 #define ATA_CB_ER_P_MCR 0x08 // ATAPI Media Change Request
2063 #define ATA_CB_ER_P_ABRT 0x04 // ATAPI command abort
2064 #define ATA_CB_ER_P_EOM 0x02 // ATAPI End of Media
2065 #define ATA_CB_ER_P_ILI 0x01 // ATAPI Illegal Length Indication
2067 // ATAPI Interrupt Reason bits in the Sector Count reg (CB_SC)
2068 #define ATA_CB_SC_P_TAG 0xf8 // ATAPI tag (mask)
2069 #define ATA_CB_SC_P_REL 0x04 // ATAPI release
2070 #define ATA_CB_SC_P_IO 0x02 // ATAPI I/O
2071 #define ATA_CB_SC_P_CD 0x01 // ATAPI C/D
2073 // bits 7-4 of the device/head (CB_DH) reg
2074 #define ATA_CB_DH_DEV0 0xa0 // select device 0
2075 #define ATA_CB_DH_DEV1 0xb0 // select device 1
2076 #define ATA_CB_DH_LBA 0x40 // use LBA
2078 // status reg (CB_STAT and CB_ASTAT) bits
2079 #define ATA_CB_STAT_BSY 0x80 // busy
2080 #define ATA_CB_STAT_RDY 0x40 // ready
2081 #define ATA_CB_STAT_DF 0x20 // device fault
2082 #define ATA_CB_STAT_WFT 0x20 // write fault (old name)
2083 #define ATA_CB_STAT_SKC 0x10 // seek complete
2084 #define ATA_CB_STAT_SERV 0x10 // service
2085 #define ATA_CB_STAT_DRQ 0x08 // data request
2086 #define ATA_CB_STAT_CORR 0x04 // corrected
2087 #define ATA_CB_STAT_IDX 0x02 // index
2088 #define ATA_CB_STAT_ERR 0x01 // error (ATA)
2089 #define ATA_CB_STAT_CHK 0x01 // check (ATAPI)
2091 // device control reg (CB_DC) bits
2092 #define ATA_CB_DC_HD15 0x08 // bit should always be set to one
2093 #define ATA_CB_DC_SRST 0x04 // soft reset
2094 #define ATA_CB_DC_NIEN 0x02 // disable interrupts
2096 // Most mandtory and optional ATA commands (from ATA-3),
2097 #define ATA_CMD_CFA_ERASE_SECTORS 0xC0
2098 #define ATA_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03
2099 #define ATA_CMD_CFA_TRANSLATE_SECTOR 0x87
2100 #define ATA_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD
2101 #define ATA_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38
2102 #define ATA_CMD_CHECK_POWER_MODE1 0xE5
2103 #define ATA_CMD_CHECK_POWER_MODE2 0x98
2104 #define ATA_CMD_DEVICE_RESET 0x08
2105 #define ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90
2106 #define ATA_CMD_FLUSH_CACHE 0xE7
2107 #define ATA_CMD_FORMAT_TRACK 0x50
2108 #define ATA_CMD_IDENTIFY_DEVICE 0xEC
2109 #define ATA_CMD_IDENTIFY_DEVICE_PACKET 0xA1
2110 #define ATA_CMD_IDENTIFY_PACKET_DEVICE 0xA1
2111 #define ATA_CMD_IDLE1 0xE3
2112 #define ATA_CMD_IDLE2 0x97
2113 #define ATA_CMD_IDLE_IMMEDIATE1 0xE1
2114 #define ATA_CMD_IDLE_IMMEDIATE2 0x95
2115 #define ATA_CMD_INITIALIZE_DRIVE_PARAMETERS 0x91
2116 #define ATA_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91
2117 #define ATA_CMD_NOP 0x00
2118 #define ATA_CMD_PACKET 0xA0
2119 #define ATA_CMD_READ_BUFFER 0xE4
2120 #define ATA_CMD_READ_DMA 0xC8
2121 #define ATA_CMD_READ_DMA_QUEUED 0xC7
2122 #define ATA_CMD_READ_MULTIPLE 0xC4
2123 #define ATA_CMD_READ_SECTORS 0x20
2124 #define ATA_CMD_READ_VERIFY_SECTORS 0x40
2125 #define ATA_CMD_RECALIBRATE 0x10
2126 #define ATA_CMD_REQUEST_SENSE 0x03
2127 #define ATA_CMD_SEEK 0x70
2128 #define ATA_CMD_SET_FEATURES 0xEF
2129 #define ATA_CMD_SET_MULTIPLE_MODE 0xC6
2130 #define ATA_CMD_SLEEP1 0xE6
2131 #define ATA_CMD_SLEEP2 0x99
2132 #define ATA_CMD_STANDBY1 0xE2
2133 #define ATA_CMD_STANDBY2 0x96
2134 #define ATA_CMD_STANDBY_IMMEDIATE1 0xE0
2135 #define ATA_CMD_STANDBY_IMMEDIATE2 0x94
2136 #define ATA_CMD_WRITE_BUFFER 0xE8
2137 #define ATA_CMD_WRITE_DMA 0xCA
2138 #define ATA_CMD_WRITE_DMA_QUEUED 0xCC
2139 #define ATA_CMD_WRITE_MULTIPLE 0xC5
2140 #define ATA_CMD_WRITE_SECTORS 0x30
2141 #define ATA_CMD_WRITE_VERIFY 0x3C
2143 #define ATA_IFACE_NONE 0x00
2144 #define ATA_IFACE_ISA 0x00
2145 #define ATA_IFACE_PCI 0x01
2147 #define ATA_TYPE_NONE 0x00
2148 #define ATA_TYPE_UNKNOWN 0x01
2149 #define ATA_TYPE_ATA 0x02
2150 #define ATA_TYPE_ATAPI 0x03
2152 #define ATA_DEVICE_NONE 0x00
2153 #define ATA_DEVICE_HD 0xFF
2154 #define ATA_DEVICE_CDROM 0x05
2156 #define ATA_MODE_NONE 0x00
2157 #define ATA_MODE_PIO16 0x00
2158 #define ATA_MODE_PIO32 0x01
2159 #define ATA_MODE_ISADMA 0x02
2160 #define ATA_MODE_PCIDMA 0x03
2161 #define ATA_MODE_USEIRQ 0x10
2163 #define ATA_TRANSLATION_NONE 0
2164 #define ATA_TRANSLATION_LBA 1
2165 #define ATA_TRANSLATION_LARGE 2
2166 #define ATA_TRANSLATION_RECHS 3
2168 #define ATA_DATA_NO 0x00
2169 #define ATA_DATA_IN 0x01
2170 #define ATA_DATA_OUT 0x02
2172 // ---------------------------------------------------------------------------
2173 // ATA/ATAPI driver : initialization
2174 // ---------------------------------------------------------------------------
2175 void ata_init( )
2177 Bit16u ebda_seg=read_word(0x0040,0x000E);
2178 Bit8u channel, device;
2180 // Channels info init.
2181 for (channel=0; channel<BX_MAX_ATA_INTERFACES; channel++) {
2182 write_byte(ebda_seg,&EbdaData->ata.channels[channel].iface,ATA_IFACE_NONE);
2183 write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1,0x0);
2184 write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2,0x0);
2185 write_byte(ebda_seg,&EbdaData->ata.channels[channel].irq,0);
2188 // Devices info init.
2189 for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2190 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
2191 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_NONE);
2192 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable,0);
2193 write_byte(ebda_seg,&EbdaData->ata.devices[device].lock,0);
2194 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode,ATA_MODE_NONE);
2195 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize,0);
2196 write_byte(ebda_seg,&EbdaData->ata.devices[device].translation,ATA_TRANSLATION_NONE);
2197 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads,0);
2198 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders,0);
2199 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt,0);
2200 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads,0);
2201 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders,0);
2202 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt,0);
2204 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors,0L);
2207 // hdidmap and cdidmap init.
2208 for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2209 write_byte(ebda_seg,&EbdaData->ata.hdidmap[device],BX_MAX_ATA_DEVICES);
2210 write_byte(ebda_seg,&EbdaData->ata.cdidmap[device],BX_MAX_ATA_DEVICES);
2213 write_byte(ebda_seg,&EbdaData->ata.hdcount,0);
2214 write_byte(ebda_seg,&EbdaData->ata.cdcount,0);
2217 #define TIMEOUT 0
2218 #define BSY 1
2219 #define NOT_BSY 2
2220 #define NOT_BSY_DRQ 3
2221 #define NOT_BSY_NOT_DRQ 4
2222 #define NOT_BSY_RDY 5
2224 #define IDE_TIMEOUT 32000u //32 seconds max for IDE ops
2226 int await_ide();
2227 static int await_ide(when_done,base,timeout)
2228 Bit8u when_done;
2229 Bit16u base;
2230 Bit16u timeout;
2232 Bit32u time=0,last=0;
2233 Bit16u status;
2234 Bit8u result;
2235 status = inb(base + ATA_CB_STAT); // for the times you're supposed to throw one away
2236 for(;;) {
2237 status = inb(base+ATA_CB_STAT);
2238 time++;
2239 if (when_done == BSY)
2240 result = status & ATA_CB_STAT_BSY;
2241 else if (when_done == NOT_BSY)
2242 result = !(status & ATA_CB_STAT_BSY);
2243 else if (when_done == NOT_BSY_DRQ)
2244 result = !(status & ATA_CB_STAT_BSY) && (status & ATA_CB_STAT_DRQ);
2245 else if (when_done == NOT_BSY_NOT_DRQ)
2246 result = !(status & ATA_CB_STAT_BSY) && !(status & ATA_CB_STAT_DRQ);
2247 else if (when_done == NOT_BSY_RDY)
2248 result = !(status & ATA_CB_STAT_BSY) && (status & ATA_CB_STAT_RDY);
2249 else if (when_done == TIMEOUT)
2250 result = 0;
2252 if (result) return 0;
2253 if (time>>16 != last) // mod 2048 each 16 ms
2255 last = time >>16;
2256 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);
2258 if (status & ATA_CB_STAT_ERR)
2260 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);
2261 return -1;
2263 if ((timeout == 0) || ((time>>11) > timeout)) break;
2265 BX_INFO("IDE time out\n");
2266 return -1;
2269 // ---------------------------------------------------------------------------
2270 // ATA/ATAPI driver : device detection
2271 // ---------------------------------------------------------------------------
2273 void ata_detect( )
2275 Bit16u ebda_seg=read_word(0x0040,0x000E);
2276 Bit8u hdcount, cdcount, device, type;
2277 Bit8u buffer[0x0200];
2279 #if BX_MAX_ATA_INTERFACES > 0
2280 write_byte(ebda_seg,&EbdaData->ata.channels[0].iface,ATA_IFACE_ISA);
2281 write_word(ebda_seg,&EbdaData->ata.channels[0].iobase1,0x1f0);
2282 write_word(ebda_seg,&EbdaData->ata.channels[0].iobase2,0x3f0);
2283 write_byte(ebda_seg,&EbdaData->ata.channels[0].irq,14);
2284 #endif
2285 #if BX_MAX_ATA_INTERFACES > 1
2286 write_byte(ebda_seg,&EbdaData->ata.channels[1].iface,ATA_IFACE_ISA);
2287 write_word(ebda_seg,&EbdaData->ata.channels[1].iobase1,0x170);
2288 write_word(ebda_seg,&EbdaData->ata.channels[1].iobase2,0x370);
2289 write_byte(ebda_seg,&EbdaData->ata.channels[1].irq,15);
2290 #endif
2291 #if BX_MAX_ATA_INTERFACES > 2
2292 write_byte(ebda_seg,&EbdaData->ata.channels[2].iface,ATA_IFACE_ISA);
2293 write_word(ebda_seg,&EbdaData->ata.channels[2].iobase1,0x1e8);
2294 write_word(ebda_seg,&EbdaData->ata.channels[2].iobase2,0x3e0);
2295 write_byte(ebda_seg,&EbdaData->ata.channels[2].irq,12);
2296 #endif
2297 #if BX_MAX_ATA_INTERFACES > 3
2298 write_byte(ebda_seg,&EbdaData->ata.channels[3].iface,ATA_IFACE_ISA);
2299 write_word(ebda_seg,&EbdaData->ata.channels[3].iobase1,0x168);
2300 write_word(ebda_seg,&EbdaData->ata.channels[3].iobase2,0x360);
2301 write_byte(ebda_seg,&EbdaData->ata.channels[3].irq,11);
2302 #endif
2303 #if BX_MAX_ATA_INTERFACES > 4
2304 #error Please fill the ATA interface informations
2305 #endif
2307 // Device detection
2308 hdcount=cdcount=0;
2310 for(device=0; device<BX_MAX_ATA_DEVICES; device++) {
2311 Bit16u iobase1, iobase2;
2312 Bit8u channel, slave, shift;
2313 Bit8u sc, sn, cl, ch, st;
2315 channel = device / 2;
2316 slave = device % 2;
2318 iobase1 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1);
2319 iobase2 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2);
2321 // Disable interrupts
2322 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2324 // Look for device
2325 outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2326 outb(iobase1+ATA_CB_SC, 0x55);
2327 outb(iobase1+ATA_CB_SN, 0xaa);
2328 outb(iobase1+ATA_CB_SC, 0xaa);
2329 outb(iobase1+ATA_CB_SN, 0x55);
2330 outb(iobase1+ATA_CB_SC, 0x55);
2331 outb(iobase1+ATA_CB_SN, 0xaa);
2333 // If we found something
2334 sc = inb(iobase1+ATA_CB_SC);
2335 sn = inb(iobase1+ATA_CB_SN);
2337 if ( (sc == 0x55) && (sn == 0xaa) ) {
2338 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_UNKNOWN);
2340 // reset the channel
2341 ata_reset(device);
2343 // check for ATA or ATAPI
2344 outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2345 sc = inb(iobase1+ATA_CB_SC);
2346 sn = inb(iobase1+ATA_CB_SN);
2347 if ((sc==0x01) && (sn==0x01)) {
2348 cl = inb(iobase1+ATA_CB_CL);
2349 ch = inb(iobase1+ATA_CB_CH);
2350 st = inb(iobase1+ATA_CB_STAT);
2352 if ((cl==0x14) && (ch==0xeb)) {
2353 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATAPI);
2354 } else if ((cl==0x00) && (ch==0x00) && (st!=0x00)) {
2355 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATA);
2356 } else if ((cl==0xff) && (ch==0xff)) {
2357 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
2362 type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type);
2364 // Now we send a IDENTIFY command to ATA device
2365 if(type == ATA_TYPE_ATA) {
2366 Bit32u sectors;
2367 Bit16u cylinders, heads, spt, blksize;
2368 Bit8u translation, removable, mode;
2370 //Temporary values to do the transfer
2371 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2372 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2374 if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE, 1, 0, 0, 0, 0L, get_SS(),buffer) !=0 )
2375 BX_PANIC("ata-detect: Failed to detect ATA device\n");
2377 removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2378 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2379 blksize = read_word(get_SS(),buffer+10);
2381 cylinders = read_word(get_SS(),buffer+(1*2)); // word 1
2382 heads = read_word(get_SS(),buffer+(3*2)); // word 3
2383 spt = read_word(get_SS(),buffer+(6*2)); // word 6
2385 sectors = read_dword(get_SS(),buffer+(60*2)); // word 60 and word 61
2387 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2388 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2389 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2390 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2391 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads, heads);
2392 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders, cylinders);
2393 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt, spt);
2394 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors, sectors);
2395 BX_INFO("ata%d-%d: PCHS=%u/%d/%d translation=", channel, slave,cylinders, heads, spt);
2397 translation = inb_cmos(0x39 + channel/2);
2398 for (shift=device%4; shift>0; shift--) translation >>= 2;
2399 translation &= 0x03;
2401 write_byte(ebda_seg,&EbdaData->ata.devices[device].translation, translation);
2403 switch (translation) {
2404 case ATA_TRANSLATION_NONE:
2405 BX_INFO("none");
2406 break;
2407 case ATA_TRANSLATION_LBA:
2408 BX_INFO("lba");
2409 break;
2410 case ATA_TRANSLATION_LARGE:
2411 BX_INFO("large");
2412 break;
2413 case ATA_TRANSLATION_RECHS:
2414 BX_INFO("r-echs");
2415 break;
2417 switch (translation) {
2418 case ATA_TRANSLATION_NONE:
2419 break;
2420 case ATA_TRANSLATION_LBA:
2421 spt = 63;
2422 sectors /= 63;
2423 heads = sectors / 1024;
2424 if (heads>128) heads = 255;
2425 else if (heads>64) heads = 128;
2426 else if (heads>32) heads = 64;
2427 else if (heads>16) heads = 32;
2428 else heads=16;
2429 cylinders = sectors / heads;
2430 break;
2431 case ATA_TRANSLATION_RECHS:
2432 // Take care not to overflow
2433 if (heads==16) {
2434 if(cylinders>61439) cylinders=61439;
2435 heads=15;
2436 cylinders = (Bit16u)((Bit32u)(cylinders)*16/15);
2438 // then go through the large bitshift process
2439 case ATA_TRANSLATION_LARGE:
2440 while(cylinders > 1024) {
2441 cylinders >>= 1;
2442 heads <<= 1;
2444 // If we max out the head count
2445 if (heads > 127) break;
2447 break;
2449 // clip to 1024 cylinders in lchs
2450 if (cylinders > 1024) cylinders=1024;
2451 BX_INFO(" LCHS=%d/%d/%d\n", cylinders, heads, spt);
2453 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads, heads);
2454 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders, cylinders);
2455 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt, spt);
2457 // fill hdidmap
2458 write_byte(ebda_seg,&EbdaData->ata.hdidmap[hdcount], device);
2459 hdcount++;
2462 // Now we send a IDENTIFY command to ATAPI device
2463 if(type == ATA_TYPE_ATAPI) {
2465 Bit8u type, removable, mode;
2466 Bit16u blksize;
2468 //Temporary values to do the transfer
2469 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_CDROM);
2470 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2472 if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE_PACKET, 1, 0, 0, 0, 0L, get_SS(),buffer) != 0)
2473 BX_PANIC("ata-detect: Failed to detect ATAPI device\n");
2475 type = read_byte(get_SS(),buffer+1) & 0x1f;
2476 removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2477 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2478 blksize = 2048;
2480 write_byte(ebda_seg,&EbdaData->ata.devices[device].device, type);
2481 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2482 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2483 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2485 // fill cdidmap
2486 write_byte(ebda_seg,&EbdaData->ata.cdidmap[cdcount], device);
2487 cdcount++;
2491 Bit32u sizeinmb;
2492 Bit16u ataversion;
2493 Bit8u c, i, version, model[41];
2495 switch (type) {
2496 case ATA_TYPE_ATA:
2497 sizeinmb = read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors);
2498 sizeinmb >>= 11;
2499 case ATA_TYPE_ATAPI:
2500 // Read ATA/ATAPI version
2501 ataversion=((Bit16u)(read_byte(get_SS(),buffer+161))<<8)|read_byte(get_SS(),buffer+160);
2502 for(version=15;version>0;version--) {
2503 if((ataversion&(1<<version))!=0)
2504 break;
2507 // Read model name
2508 for(i=0;i<20;i++){
2509 write_byte(get_SS(),model+(i*2),read_byte(get_SS(),buffer+(i*2)+54+1));
2510 write_byte(get_SS(),model+(i*2)+1,read_byte(get_SS(),buffer+(i*2)+54));
2513 // Reformat
2514 write_byte(get_SS(),model+40,0x00);
2515 for(i=39;i>0;i--){
2516 if(read_byte(get_SS(),model+i)==0x20)
2517 write_byte(get_SS(),model+i,0x00);
2518 else break;
2520 break;
2523 switch (type) {
2524 case ATA_TYPE_ATA:
2525 printf("ata%d %s: ",channel,slave?" slave":"master");
2526 i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
2527 if (sizeinmb < (1UL<<16))
2528 printf(" ATA-%d Hard-Disk (%4u MBytes)\n", version, (Bit16u)sizeinmb);
2529 else
2530 printf(" ATA-%d Hard-Disk (%4u GBytes)\n", version, (Bit16u)(sizeinmb>>10));
2531 break;
2532 case ATA_TYPE_ATAPI:
2533 printf("ata%d %s: ",channel,slave?" slave":"master");
2534 i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
2535 if(read_byte(ebda_seg,&EbdaData->ata.devices[device].device)==ATA_DEVICE_CDROM)
2536 printf(" ATAPI-%d CD-Rom/DVD-Rom\n",version);
2537 else
2538 printf(" ATAPI-%d Device\n",version);
2539 break;
2540 case ATA_TYPE_UNKNOWN:
2541 printf("ata%d %s: Unknown device\n",channel,slave?" slave":"master");
2542 break;
2547 // Store the devices counts
2548 write_byte(ebda_seg,&EbdaData->ata.hdcount, hdcount);
2549 write_byte(ebda_seg,&EbdaData->ata.cdcount, cdcount);
2550 write_byte(0x40,0x75, hdcount);
2552 printf("\n");
2554 // FIXME : should use bios=cmos|auto|disable bits
2555 // FIXME : should know about translation bits
2556 // FIXME : move hard_drive_post here
2560 // ---------------------------------------------------------------------------
2561 // ATA/ATAPI driver : software reset
2562 // ---------------------------------------------------------------------------
2563 // ATA-3
2564 // 8.2.1 Software reset - Device 0
2566 void ata_reset(device)
2567 Bit16u device;
2569 Bit16u ebda_seg=read_word(0x0040,0x000E);
2570 Bit16u iobase1, iobase2;
2571 Bit8u channel, slave, sn, sc;
2572 Bit8u type;
2573 Bit16u max;
2575 channel = device / 2;
2576 slave = device % 2;
2578 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2579 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2581 // Reset
2583 // 8.2.1 (a) -- set SRST in DC
2584 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST);
2586 // 8.2.1 (b) -- wait for BSY
2587 if (await_ide(BSY, iobase1, 20)) return;
2589 // 8.2.1 (f) -- clear SRST
2590 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2592 type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type);
2593 if (type != ATA_TYPE_NONE) {
2595 // 8.2.1 (g) -- check for sc==sn==0x01
2596 // select device
2597 outb(iobase1+ATA_CB_DH, slave?ATA_CB_DH_DEV1:ATA_CB_DH_DEV0);
2598 sc = inb(iobase1+ATA_CB_SC);
2599 sn = inb(iobase1+ATA_CB_SN);
2601 if ( (sc==0x01) && (sn==0x01) ) {
2602 if (type == ATA_TYPE_ATA) //ATA
2603 await_ide(NOT_BSY_RDY, iobase1, IDE_TIMEOUT);
2604 else //ATAPI
2605 await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
2608 // 8.2.1 (h) -- wait for not BSY
2609 await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
2612 // Enable interrupts
2613 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2616 // ---------------------------------------------------------------------------
2617 // ATA/ATAPI driver : execute a non data command
2618 // ---------------------------------------------------------------------------
2620 Bit16u ata_cmd_non_data()
2621 {return 0;}
2623 // ---------------------------------------------------------------------------
2624 // ATA/ATAPI driver : execute a data-in command
2625 // ---------------------------------------------------------------------------
2626 // returns
2627 // 0 : no error
2628 // 1 : BUSY bit set
2629 // 2 : read error
2630 // 3 : expected DRQ=1
2631 // 4 : no sectors left to read/verify
2632 // 5 : more sectors to read/verify
2633 // 6 : no sectors left to write
2634 // 7 : more sectors to write
2635 Bit16u ata_cmd_data_in(device, command, count, cylinder, head, sector, lba, segment, offset)
2636 Bit16u device, command, count, cylinder, head, sector, segment, offset;
2637 Bit32u lba;
2639 Bit16u ebda_seg=read_word(0x0040,0x000E);
2640 Bit16u iobase1, iobase2, blksize;
2641 Bit8u channel, slave;
2642 Bit8u status, current, mode;
2644 channel = device / 2;
2645 slave = device % 2;
2647 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2648 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2649 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2650 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
2651 if (mode == ATA_MODE_PIO32) blksize>>=2;
2652 else blksize>>=1;
2654 //!!FIXME!! this only works up to 28-bit LBA
2655 // sector will be 0 only on lba access. Convert to lba-chs
2656 if (sector == 0) {
2657 sector = (Bit16u) (lba & 0x000000ffL);
2658 cylinder = (Bit16u) ((lba>>8) & 0x0000ffffL);
2659 head = ((Bit16u) ((lba>>24) & 0x0000000fL)) | ATA_CB_DH_LBA;
2662 // Reset count of transferred data
2663 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2664 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2665 current = 0;
2667 status = inb(iobase1 + ATA_CB_STAT);
2668 if (status & ATA_CB_STAT_BSY) return 1;
2670 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2671 outb(iobase1 + ATA_CB_FR, 0x00);
2672 outb(iobase1 + ATA_CB_SC, count);
2673 outb(iobase1 + ATA_CB_SN, sector);
2674 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
2675 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
2676 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
2677 outb(iobase1 + ATA_CB_CMD, command);
2679 await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
2680 status = inb(iobase1 + ATA_CB_STAT);
2682 if (status & ATA_CB_STAT_ERR) {
2683 BX_DEBUG_ATA("ata_cmd_data_in : read error\n");
2684 return 2;
2685 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2686 BX_DEBUG_ATA("ata_cmd_data_in : DRQ not set (status %02x)\n", (unsigned) status);
2687 return 3;
2690 // FIXME : move seg/off translation here
2692 ASM_START
2693 sti ;; enable higher priority interrupts
2694 ASM_END
2696 while (1) {
2698 ASM_START
2699 push bp
2700 mov bp, sp
2701 mov di, _ata_cmd_data_in.offset + 2[bp]
2702 mov ax, _ata_cmd_data_in.segment + 2[bp]
2703 mov cx, _ata_cmd_data_in.blksize + 2[bp]
2705 ;; adjust if there will be an overrun. 2K max sector size
2706 cmp di, #0xf800 ;;
2707 jbe ata_in_no_adjust
2709 ata_in_adjust:
2710 sub di, #0x0800 ;; sub 2 kbytes from offset
2711 add ax, #0x0080 ;; add 2 Kbytes to segment
2713 ata_in_no_adjust:
2714 mov es, ax ;; segment in es
2716 mov dx, _ata_cmd_data_in.iobase1 + 2[bp] ;; ATA data read port
2718 mov ah, _ata_cmd_data_in.mode + 2[bp]
2719 cmp ah, #ATA_MODE_PIO32
2720 je ata_in_32
2722 ata_in_16:
2724 insw ;; CX words transfered from port(DX) to ES:[DI]
2725 jmp ata_in_done
2727 ata_in_32:
2729 insd ;; CX dwords transfered from port(DX) to ES:[DI]
2731 ata_in_done:
2732 mov _ata_cmd_data_in.offset + 2[bp], di
2733 mov _ata_cmd_data_in.segment + 2[bp], es
2734 pop bp
2735 ASM_END
2737 current++;
2738 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
2739 count--;
2740 await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
2741 status = inb(iobase1 + ATA_CB_STAT);
2742 if (count == 0) {
2743 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2744 != ATA_CB_STAT_RDY ) {
2745 BX_DEBUG_ATA("ata_cmd_data_in : no sectors left (status %02x)\n", (unsigned) status);
2746 return 4;
2748 break;
2750 else {
2751 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2752 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2753 BX_DEBUG_ATA("ata_cmd_data_in : more sectors left (status %02x)\n", (unsigned) status);
2754 return 5;
2756 continue;
2759 // Enable interrupts
2760 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2761 return 0;
2764 // ---------------------------------------------------------------------------
2765 // ATA/ATAPI driver : execute a data-out command
2766 // ---------------------------------------------------------------------------
2767 // returns
2768 // 0 : no error
2769 // 1 : BUSY bit set
2770 // 2 : read error
2771 // 3 : expected DRQ=1
2772 // 4 : no sectors left to read/verify
2773 // 5 : more sectors to read/verify
2774 // 6 : no sectors left to write
2775 // 7 : more sectors to write
2776 Bit16u ata_cmd_data_out(device, command, count, cylinder, head, sector, lba, segment, offset)
2777 Bit16u device, command, count, cylinder, head, sector, segment, offset;
2778 Bit32u lba;
2780 Bit16u ebda_seg=read_word(0x0040,0x000E);
2781 Bit16u iobase1, iobase2, blksize;
2782 Bit8u channel, slave;
2783 Bit8u status, current, mode;
2785 channel = device / 2;
2786 slave = device % 2;
2788 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2789 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2790 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2791 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
2792 if (mode == ATA_MODE_PIO32) blksize>>=2;
2793 else blksize>>=1;
2795 //!!FIXME!! this only works up to 28-bit LBA
2796 // sector will be 0 only on lba access. Convert to lba-chs
2797 if (sector == 0) {
2798 sector = (Bit16u) (lba & 0x000000ffL);
2799 cylinder = (Bit16u) ((lba>>8) & 0x0000ffffL);
2800 head = ((Bit16u) ((lba>>24) & 0x0000000fL)) | ATA_CB_DH_LBA;
2803 // Reset count of transferred data
2804 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2805 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2806 current = 0;
2808 status = inb(iobase1 + ATA_CB_STAT);
2809 if (status & ATA_CB_STAT_BSY) return 1;
2811 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2812 outb(iobase1 + ATA_CB_FR, 0x00);
2813 outb(iobase1 + ATA_CB_SC, count);
2814 outb(iobase1 + ATA_CB_SN, sector);
2815 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
2816 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
2817 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
2818 outb(iobase1 + ATA_CB_CMD, command);
2820 await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
2821 status = inb(iobase1 + ATA_CB_STAT);
2823 if (status & ATA_CB_STAT_ERR) {
2824 BX_DEBUG_ATA("ata_cmd_data_out : read error\n");
2825 return 2;
2826 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2827 BX_DEBUG_ATA("ata_cmd_data_out : DRQ not set (status %02x)\n", (unsigned) status);
2828 return 3;
2831 // FIXME : move seg/off translation here
2833 ASM_START
2834 sti ;; enable higher priority interrupts
2835 ASM_END
2837 while (1) {
2839 ASM_START
2840 push bp
2841 mov bp, sp
2842 mov si, _ata_cmd_data_out.offset + 2[bp]
2843 mov ax, _ata_cmd_data_out.segment + 2[bp]
2844 mov cx, _ata_cmd_data_out.blksize + 2[bp]
2846 ;; adjust if there will be an overrun. 2K max sector size
2847 cmp si, #0xf800 ;;
2848 jbe ata_out_no_adjust
2850 ata_out_adjust:
2851 sub si, #0x0800 ;; sub 2 kbytes from offset
2852 add ax, #0x0080 ;; add 2 Kbytes to segment
2854 ata_out_no_adjust:
2855 mov es, ax ;; segment in es
2857 mov dx, _ata_cmd_data_out.iobase1 + 2[bp] ;; ATA data write port
2859 mov ah, _ata_cmd_data_out.mode + 2[bp]
2860 cmp ah, #ATA_MODE_PIO32
2861 je ata_out_32
2863 ata_out_16:
2864 seg ES
2866 outsw ;; CX words transfered from port(DX) to ES:[SI]
2867 jmp ata_out_done
2869 ata_out_32:
2870 seg ES
2872 outsd ;; CX dwords transfered from port(DX) to ES:[SI]
2874 ata_out_done:
2875 mov _ata_cmd_data_out.offset + 2[bp], si
2876 mov _ata_cmd_data_out.segment + 2[bp], es
2877 pop bp
2878 ASM_END
2880 current++;
2881 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
2882 count--;
2883 status = inb(iobase1 + ATA_CB_STAT);
2884 if (count == 0) {
2885 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2886 != ATA_CB_STAT_RDY ) {
2887 BX_DEBUG_ATA("ata_cmd_data_out : no sectors left (status %02x)\n", (unsigned) status);
2888 return 6;
2890 break;
2892 else {
2893 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2894 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2895 BX_DEBUG_ATA("ata_cmd_data_out : more sectors left (status %02x)\n", (unsigned) status);
2896 return 7;
2898 continue;
2901 // Enable interrupts
2902 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2903 return 0;
2906 // ---------------------------------------------------------------------------
2907 // ATA/ATAPI driver : execute a packet command
2908 // ---------------------------------------------------------------------------
2909 // returns
2910 // 0 : no error
2911 // 1 : error in parameters
2912 // 2 : BUSY bit set
2913 // 3 : error
2914 // 4 : not ready
2915 Bit16u ata_cmd_packet(device, cmdlen, cmdseg, cmdoff, header, length, inout, bufseg, bufoff)
2916 Bit8u cmdlen,inout;
2917 Bit16u device,cmdseg, cmdoff, bufseg, bufoff;
2918 Bit16u header;
2919 Bit32u length;
2921 Bit16u ebda_seg=read_word(0x0040,0x000E);
2922 Bit16u iobase1, iobase2;
2923 Bit16u lcount, lbefore, lafter, count;
2924 Bit8u channel, slave;
2925 Bit8u status, mode, lmode;
2926 Bit32u total, transfer;
2928 channel = device / 2;
2929 slave = device % 2;
2931 // Data out is not supported yet
2932 if (inout == ATA_DATA_OUT) {
2933 BX_INFO("ata_cmd_packet: DATA_OUT not supported yet\n");
2934 return 1;
2937 // The header length must be even
2938 if (header & 1) {
2939 BX_DEBUG_ATA("ata_cmd_packet : header must be even (%04x)\n",header);
2940 return 1;
2943 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2944 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2945 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2946 transfer= 0L;
2948 if (cmdlen < 12) cmdlen=12;
2949 if (cmdlen > 12) cmdlen=16;
2950 cmdlen>>=1;
2952 // Reset count of transferred data
2953 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2954 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2956 status = inb(iobase1 + ATA_CB_STAT);
2957 if (status & ATA_CB_STAT_BSY) return 2;
2959 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2960 outb(iobase1 + ATA_CB_FR, 0x00);
2961 outb(iobase1 + ATA_CB_SC, 0x00);
2962 outb(iobase1 + ATA_CB_SN, 0x00);
2963 outb(iobase1 + ATA_CB_CL, 0xfff0 & 0x00ff);
2964 outb(iobase1 + ATA_CB_CH, 0xfff0 >> 8);
2965 outb(iobase1 + ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2966 outb(iobase1 + ATA_CB_CMD, ATA_CMD_PACKET);
2968 // Device should ok to receive command
2969 await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
2970 status = inb(iobase1 + ATA_CB_STAT);
2972 if (status & ATA_CB_STAT_ERR) {
2973 BX_DEBUG_ATA("ata_cmd_packet : error, status is %02x\n",status);
2974 return 3;
2975 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2976 BX_DEBUG_ATA("ata_cmd_packet : DRQ not set (status %02x)\n", (unsigned) status);
2977 return 4;
2980 // Normalize address
2981 cmdseg += (cmdoff / 16);
2982 cmdoff %= 16;
2984 // Send command to device
2985 ASM_START
2986 sti ;; enable higher priority interrupts
2988 push bp
2989 mov bp, sp
2991 mov si, _ata_cmd_packet.cmdoff + 2[bp]
2992 mov ax, _ata_cmd_packet.cmdseg + 2[bp]
2993 mov cx, _ata_cmd_packet.cmdlen + 2[bp]
2994 mov es, ax ;; segment in es
2996 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data write port
2998 seg ES
3000 outsw ;; CX words transfered from port(DX) to ES:[SI]
3002 pop bp
3003 ASM_END
3005 if (inout == ATA_DATA_NO) {
3006 await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
3007 status = inb(iobase1 + ATA_CB_STAT);
3009 else {
3010 Bit16u loops = 0;
3011 Bit8u sc;
3012 while (1) {
3014 if (loops == 0) {//first time through
3015 status = inb(iobase2 + ATA_CB_ASTAT);
3016 await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
3018 else
3019 await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
3020 loops++;
3022 status = inb(iobase1 + ATA_CB_STAT);
3023 sc = inb(iobase1 + ATA_CB_SC);
3025 // Check if command completed
3026 if(((inb(iobase1 + ATA_CB_SC)&0x7)==0x3) &&
3027 ((status & (ATA_CB_STAT_RDY | ATA_CB_STAT_ERR)) == ATA_CB_STAT_RDY)) break;
3029 if (status & ATA_CB_STAT_ERR) {
3030 BX_DEBUG_ATA("ata_cmd_packet : error (status %02x)\n",status);
3031 return 3;
3034 // Normalize address
3035 bufseg += (bufoff / 16);
3036 bufoff %= 16;
3038 // Get the byte count
3039 lcount = ((Bit16u)(inb(iobase1 + ATA_CB_CH))<<8)+inb(iobase1 + ATA_CB_CL);
3041 // adjust to read what we want
3042 if(header>lcount) {
3043 lbefore=lcount;
3044 header-=lcount;
3045 lcount=0;
3047 else {
3048 lbefore=header;
3049 header=0;
3050 lcount-=lbefore;
3053 if(lcount>length) {
3054 lafter=lcount-length;
3055 lcount=length;
3056 length=0;
3058 else {
3059 lafter=0;
3060 length-=lcount;
3063 // Save byte count
3064 count = lcount;
3066 BX_DEBUG_ATA("Trying to read %04x bytes (%04x %04x %04x) ",lbefore+lcount+lafter,lbefore,lcount,lafter);
3067 BX_DEBUG_ATA("to 0x%04x:0x%04x\n",bufseg,bufoff);
3069 // If counts not dividable by 4, use 16bits mode
3070 lmode = mode;
3071 if (lbefore & 0x03) lmode=ATA_MODE_PIO16;
3072 if (lcount & 0x03) lmode=ATA_MODE_PIO16;
3073 if (lafter & 0x03) lmode=ATA_MODE_PIO16;
3075 // adds an extra byte if count are odd. before is always even
3076 if (lcount & 0x01) {
3077 lcount+=1;
3078 if ((lafter > 0) && (lafter & 0x01)) {
3079 lafter-=1;
3083 if (lmode == ATA_MODE_PIO32) {
3084 lcount>>=2; lbefore>>=2; lafter>>=2;
3086 else {
3087 lcount>>=1; lbefore>>=1; lafter>>=1;
3090 ; // FIXME bcc bug
3092 ASM_START
3093 push bp
3094 mov bp, sp
3096 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data read port
3098 mov cx, _ata_cmd_packet.lbefore + 2[bp]
3099 jcxz ata_packet_no_before
3101 mov ah, _ata_cmd_packet.lmode + 2[bp]
3102 cmp ah, #ATA_MODE_PIO32
3103 je ata_packet_in_before_32
3105 ata_packet_in_before_16:
3106 in ax, dx
3107 loop ata_packet_in_before_16
3108 jmp ata_packet_no_before
3110 ata_packet_in_before_32:
3111 push eax
3112 ata_packet_in_before_32_loop:
3113 in eax, dx
3114 loop ata_packet_in_before_32_loop
3115 pop eax
3117 ata_packet_no_before:
3118 mov cx, _ata_cmd_packet.lcount + 2[bp]
3119 jcxz ata_packet_after
3121 mov di, _ata_cmd_packet.bufoff + 2[bp]
3122 mov ax, _ata_cmd_packet.bufseg + 2[bp]
3123 mov es, ax
3125 mov ah, _ata_cmd_packet.lmode + 2[bp]
3126 cmp ah, #ATA_MODE_PIO32
3127 je ata_packet_in_32
3129 ata_packet_in_16:
3131 insw ;; CX words transfered tp port(DX) to ES:[DI]
3132 jmp ata_packet_after
3134 ata_packet_in_32:
3136 insd ;; CX dwords transfered to port(DX) to ES:[DI]
3138 ata_packet_after:
3139 mov cx, _ata_cmd_packet.lafter + 2[bp]
3140 jcxz ata_packet_done
3142 mov ah, _ata_cmd_packet.lmode + 2[bp]
3143 cmp ah, #ATA_MODE_PIO32
3144 je ata_packet_in_after_32
3146 ata_packet_in_after_16:
3147 in ax, dx
3148 loop ata_packet_in_after_16
3149 jmp ata_packet_done
3151 ata_packet_in_after_32:
3152 push eax
3153 ata_packet_in_after_32_loop:
3154 in eax, dx
3155 loop ata_packet_in_after_32_loop
3156 pop eax
3158 ata_packet_done:
3159 pop bp
3160 ASM_END
3162 // Compute new buffer address
3163 bufoff += count;
3165 // Save transferred bytes count
3166 transfer += count;
3167 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,transfer);
3171 // Final check, device must be ready
3172 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
3173 != ATA_CB_STAT_RDY ) {
3174 BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", (unsigned) status);
3175 return 4;
3178 // Enable interrupts
3179 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
3180 return 0;
3183 // ---------------------------------------------------------------------------
3184 // End of ATA/ATAPI Driver
3185 // ---------------------------------------------------------------------------
3187 // ---------------------------------------------------------------------------
3188 // Start of ATA/ATAPI generic functions
3189 // ---------------------------------------------------------------------------
3191 Bit16u
3192 atapi_get_sense(device, seg, asc, ascq)
3193 Bit16u device;
3195 Bit8u atacmd[12];
3196 Bit8u buffer[18];
3197 Bit8u i;
3199 memsetb(get_SS(),atacmd,0,12);
3201 // Request SENSE
3202 atacmd[0]=ATA_CMD_REQUEST_SENSE;
3203 atacmd[4]=sizeof(buffer);
3204 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 18L, ATA_DATA_IN, get_SS(), buffer) != 0)
3205 return 0x0002;
3207 write_byte(seg,asc,buffer[12]);
3208 write_byte(seg,ascq,buffer[13]);
3210 return 0;
3213 Bit16u
3214 atapi_is_ready(device)
3215 Bit16u device;
3217 Bit8u packet[12];
3218 Bit8u buf[8];
3219 Bit32u block_len;
3220 Bit32u sectors;
3221 Bit32u timeout; //measured in ms
3222 Bit32u time;
3223 Bit8u asc, ascq;
3224 Bit8u in_progress;
3225 Bit16u ebda_seg = read_word(0x0040,0x000E);
3226 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI) {
3227 printf("not implemented for non-ATAPI device\n");
3228 return -1;
3231 BX_DEBUG_ATA("ata_detect_medium: begin\n");
3232 memsetb(get_SS(),packet, 0, sizeof packet);
3233 packet[0] = 0x25; /* READ CAPACITY */
3235 /* Retry READ CAPACITY 50 times unless MEDIUM NOT PRESENT
3236 * is reported by the device. If the device reports "IN PROGRESS",
3237 * 30 seconds is added. */
3238 timeout = 5000;
3239 time = 0;
3240 in_progress = 0;
3241 while (time < timeout) {
3242 if (ata_cmd_packet(device, sizeof(packet), get_SS(), packet, 0, 8L, ATA_DATA_IN, get_SS(), buf) == 0)
3243 goto ok;
3245 if (atapi_get_sense(device, get_SS(), &asc, &ascq) == 0) {
3246 if (asc == 0x3a) { /* MEDIUM NOT PRESENT */
3247 BX_DEBUG_ATA("Device reports MEDIUM NOT PRESENT\n");
3248 return -1;
3251 if (asc == 0x04 && ascq == 0x01 && !in_progress) {
3252 /* IN PROGRESS OF BECOMING READY */
3253 printf("Waiting for device to detect medium... ");
3254 /* Allow 30 seconds more */
3255 timeout = 30000;
3256 in_progress = 1;
3259 time += 100;
3261 BX_DEBUG_ATA("read capacity failed\n");
3262 return -1;
3265 block_len = (Bit32u) buf[4] << 24
3266 | (Bit32u) buf[5] << 16
3267 | (Bit32u) buf[6] << 8
3268 | (Bit32u) buf[7] << 0;
3269 BX_DEBUG_ATA("block_len=%u\n", block_len);
3271 if (block_len!= 2048 && block_len!= 512)
3273 printf("Unsupported sector size %u\n", block_len);
3274 return -1;
3276 write_dword(ebda_seg,&EbdaData->ata.devices[device].blksize, block_len);
3278 sectors = (Bit32u) buf[0] << 24
3279 | (Bit32u) buf[1] << 16
3280 | (Bit32u) buf[2] << 8
3281 | (Bit32u) buf[3] << 0;
3283 BX_DEBUG_ATA("sectors=%u\n", sectors);
3284 if (block_len == 2048)
3285 sectors <<= 2; /* # of sectors in 512-byte "soft" sector */
3286 if (sectors != read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors))
3287 printf("%dMB medium detected\n", sectors>>(20-9));
3288 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors, sectors);
3289 return 0;
3292 Bit16u
3293 atapi_is_cdrom(device)
3294 Bit8u device;
3296 Bit16u ebda_seg=read_word(0x0040,0x000E);
3298 if (device >= BX_MAX_ATA_DEVICES)
3299 return 0;
3301 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI)
3302 return 0;
3304 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].device) != ATA_DEVICE_CDROM)
3305 return 0;
3307 return 1;
3310 // ---------------------------------------------------------------------------
3311 // End of ATA/ATAPI generic functions
3312 // ---------------------------------------------------------------------------
3314 #endif // BX_USE_ATADRV
3316 #if BX_ELTORITO_BOOT
3318 // ---------------------------------------------------------------------------
3319 // Start of El-Torito boot functions
3320 // ---------------------------------------------------------------------------
3322 void
3323 cdemu_init()
3325 Bit16u ebda_seg=read_word(0x0040,0x000E);
3327 // the only important data is this one for now
3328 write_byte(ebda_seg,&EbdaData->cdemu.active,0x00);
3331 Bit8u
3332 cdemu_isactive()
3334 Bit16u ebda_seg=read_word(0x0040,0x000E);
3336 return(read_byte(ebda_seg,&EbdaData->cdemu.active));
3339 Bit8u
3340 cdemu_emulated_drive()
3342 Bit16u ebda_seg=read_word(0x0040,0x000E);
3344 return(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
3347 static char isotag[6]="CD001";
3348 static char eltorito[24]="EL TORITO SPECIFICATION";
3350 // Returns ah: emulated drive, al: error code
3352 Bit16u
3353 cdrom_boot()
3355 Bit16u ebda_seg=read_word(0x0040,0x000E);
3356 Bit8u atacmd[12], buffer[2048];
3357 Bit32u lba;
3358 Bit16u boot_segment, nbsectors, i, error;
3359 Bit8u device;
3361 // Find out the first cdrom
3362 for (device=0; device<BX_MAX_ATA_DEVICES;device++) {
3363 if (atapi_is_cdrom(device)) break;
3366 if(error = atapi_is_ready(device) != 0)
3367 BX_INFO("ata_is_ready returned %d\n",error);
3369 // if not found
3370 if(device >= BX_MAX_ATA_DEVICES) return 2;
3372 // Read the Boot Record Volume Descriptor
3373 memsetb(get_SS(),atacmd,0,12);
3374 atacmd[0]=0x28; // READ command
3375 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3376 atacmd[8]=(0x01 & 0x00ff); // Sectors
3377 atacmd[2]=(0x11 & 0xff000000) >> 24; // LBA
3378 atacmd[3]=(0x11 & 0x00ff0000) >> 16;
3379 atacmd[4]=(0x11 & 0x0000ff00) >> 8;
3380 atacmd[5]=(0x11 & 0x000000ff);
3381 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3382 return 3;
3384 // Validity checks
3385 if(buffer[0]!=0)return 4;
3386 for(i=0;i<5;i++){
3387 if(buffer[1+i]!=read_byte(0xf000,&isotag[i]))return 5;
3389 for(i=0;i<23;i++)
3390 if(buffer[7+i]!=read_byte(0xf000,&eltorito[i]))return 6;
3392 // ok, now we calculate the Boot catalog address
3393 lba=buffer[0x4A]*0x1000000+buffer[0x49]*0x10000+buffer[0x48]*0x100+buffer[0x47];
3395 // And we read the Boot Catalog
3396 memsetb(get_SS(),atacmd,0,12);
3397 atacmd[0]=0x28; // READ command
3398 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3399 atacmd[8]=(0x01 & 0x00ff); // Sectors
3400 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3401 atacmd[3]=(lba & 0x00ff0000) >> 16;
3402 atacmd[4]=(lba & 0x0000ff00) >> 8;
3403 atacmd[5]=(lba & 0x000000ff);
3404 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3405 return 7;
3407 // Validation entry
3408 if(buffer[0x00]!=0x01)return 8; // Header
3409 if(buffer[0x01]!=0x00)return 9; // Platform
3410 if(buffer[0x1E]!=0x55)return 10; // key 1
3411 if(buffer[0x1F]!=0xAA)return 10; // key 2
3413 // Initial/Default Entry
3414 if(buffer[0x20]!=0x88)return 11; // Bootable
3416 write_byte(ebda_seg,&EbdaData->cdemu.media,buffer[0x21]);
3417 if(buffer[0x21]==0){
3418 // FIXME ElTorito Hardcoded. cdrom is hardcoded as device 0xE0.
3419 // Win2000 cd boot needs to know it booted from cd
3420 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0xE0);
3422 else if(buffer[0x21]<4)
3423 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x00);
3424 else
3425 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x80);
3427 write_byte(ebda_seg,&EbdaData->cdemu.controller_index,device/2);
3428 write_byte(ebda_seg,&EbdaData->cdemu.device_spec,device%2);
3430 boot_segment=buffer[0x23]*0x100+buffer[0x22];
3431 if(boot_segment==0x0000)boot_segment=0x07C0;
3433 write_word(ebda_seg,&EbdaData->cdemu.load_segment,boot_segment);
3434 write_word(ebda_seg,&EbdaData->cdemu.buffer_segment,0x0000);
3436 nbsectors=buffer[0x27]*0x100+buffer[0x26];
3437 write_word(ebda_seg,&EbdaData->cdemu.sector_count,nbsectors);
3439 lba=buffer[0x2B]*0x1000000+buffer[0x2A]*0x10000+buffer[0x29]*0x100+buffer[0x28];
3440 write_dword(ebda_seg,&EbdaData->cdemu.ilba,lba);
3442 // And we read the image in memory
3443 memsetb(get_SS(),atacmd,0,12);
3444 atacmd[0]=0x28; // READ command
3445 atacmd[7]=((1+(nbsectors-1)/4) & 0xff00) >> 8; // Sectors
3446 atacmd[8]=((1+(nbsectors-1)/4) & 0x00ff); // Sectors
3447 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3448 atacmd[3]=(lba & 0x00ff0000) >> 16;
3449 atacmd[4]=(lba & 0x0000ff00) >> 8;
3450 atacmd[5]=(lba & 0x000000ff);
3451 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, nbsectors*512L, ATA_DATA_IN, boot_segment,0)) != 0)
3452 return 12;
3454 // Remember the media type
3455 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
3456 case 0x01: // 1.2M floppy
3457 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,15);
3458 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3459 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3460 break;
3461 case 0x02: // 1.44M floppy
3462 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,18);
3463 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3464 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3465 break;
3466 case 0x03: // 2.88M floppy
3467 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,36);
3468 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3469 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3470 break;
3471 case 0x04: // Harddrive
3472 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,read_byte(boot_segment,446+6)&0x3f);
3473 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,
3474 (read_byte(boot_segment,446+6)<<2) + read_byte(boot_segment,446+7) + 1);
3475 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,read_byte(boot_segment,446+5) + 1);
3476 break;
3479 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0) {
3480 // Increase bios installed hardware number of devices
3481 if(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)==0x00)
3482 write_byte(0x40,0x10,read_byte(0x40,0x10)|0x41);
3483 else
3484 write_byte(ebda_seg, &EbdaData->ata.hdcount, read_byte(ebda_seg, &EbdaData->ata.hdcount) + 1);
3488 // everything is ok, so from now on, the emulation is active
3489 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0)
3490 write_byte(ebda_seg,&EbdaData->cdemu.active,0x01);
3492 // return the boot drive + no error
3493 return (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)*0x100)+0;
3496 // ---------------------------------------------------------------------------
3497 // End of El-Torito boot functions
3498 // ---------------------------------------------------------------------------
3499 #endif // BX_ELTORITO_BOOT
3501 void
3502 int14_function(regs, ds, iret_addr)
3503 pusha_regs_t regs; // regs pushed from PUSHA instruction
3504 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
3505 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
3507 Bit16u addr,timer,val16;
3508 Bit8u timeout;
3510 ASM_START
3512 ASM_END
3514 addr = read_word(0x0040, (regs.u.r16.dx << 1));
3515 timeout = read_byte(0x0040, 0x007C + regs.u.r16.dx);
3516 if ((regs.u.r16.dx < 4) && (addr > 0)) {
3517 switch (regs.u.r8.ah) {
3518 case 0:
3519 outb(addr+3, inb(addr+3) | 0x80);
3520 if (regs.u.r8.al & 0xE0 == 0) {
3521 outb(addr, 0x17);
3522 outb(addr+1, 0x04);
3523 } else {
3524 val16 = 0x600 >> ((regs.u.r8.al & 0xE0) >> 5);
3525 outb(addr, val16 & 0xFF);
3526 outb(addr+1, val16 >> 8);
3528 outb(addr+3, regs.u.r8.al & 0x1F);
3529 regs.u.r8.ah = inb(addr+5);
3530 regs.u.r8.al = inb(addr+6);
3531 ClearCF(iret_addr.flags);
3532 break;
3533 case 1:
3534 timer = read_word(0x0040, 0x006C);
3535 while (((inb(addr+5) & 0x60) != 0x60) && (timeout)) {
3536 val16 = read_word(0x0040, 0x006C);
3537 if (val16 != timer) {
3538 timer = val16;
3539 timeout--;
3542 if (timeout) outb(addr, regs.u.r8.al);
3543 regs.u.r8.ah = inb(addr+5);
3544 if (!timeout) regs.u.r8.ah |= 0x80;
3545 ClearCF(iret_addr.flags);
3546 break;
3547 case 2:
3548 timer = read_word(0x0040, 0x006C);
3549 while (((inb(addr+5) & 0x01) == 0) && (timeout)) {
3550 val16 = read_word(0x0040, 0x006C);
3551 if (val16 != timer) {
3552 timer = val16;
3553 timeout--;
3556 if (timeout) {
3557 regs.u.r8.ah = 0;
3558 regs.u.r8.al = inb(addr);
3559 } else {
3560 regs.u.r8.ah = inb(addr+5);
3562 ClearCF(iret_addr.flags);
3563 break;
3564 case 3:
3565 regs.u.r8.ah = inb(addr+5);
3566 regs.u.r8.al = inb(addr+6);
3567 ClearCF(iret_addr.flags);
3568 break;
3569 default:
3570 SetCF(iret_addr.flags); // Unsupported
3572 } else {
3573 SetCF(iret_addr.flags); // Unsupported
3577 void
3578 int15_function(regs, ES, DS, FLAGS)
3579 pusha_regs_t regs; // REGS pushed via pusha
3580 Bit16u ES, DS, FLAGS;
3582 Bit16u ebda_seg=read_word(0x0040,0x000E);
3583 bx_bool prev_a20_enable;
3584 Bit16u base15_00;
3585 Bit8u base23_16;
3586 Bit16u ss;
3587 Bit16u CX,DX;
3589 Bit16u bRegister;
3590 Bit8u irqDisable;
3592 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3594 switch (regs.u.r8.ah) {
3595 case 0x24: /* A20 Control */
3596 switch (regs.u.r8.al) {
3597 case 0x00:
3598 set_enable_a20(0);
3599 CLEAR_CF();
3600 regs.u.r8.ah = 0;
3601 break;
3602 case 0x01:
3603 set_enable_a20(1);
3604 CLEAR_CF();
3605 regs.u.r8.ah = 0;
3606 break;
3607 case 0x02:
3608 regs.u.r8.al = (inb(0x92) >> 1) & 0x01;
3609 CLEAR_CF();
3610 regs.u.r8.ah = 0;
3611 break;
3612 case 0x03:
3613 CLEAR_CF();
3614 regs.u.r8.ah = 0;
3615 regs.u.r16.bx = 3;
3616 break;
3617 default:
3618 BX_INFO("int15: Func 24h, subfunc %02xh, A20 gate control not supported\n", (unsigned) regs.u.r8.al);
3619 SET_CF();
3620 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3622 break;
3624 case 0x41:
3625 SET_CF();
3626 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3627 break;
3629 case 0x4f:
3630 /* keyboard intercept */
3631 #if BX_CPU < 2
3632 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3633 #else
3634 // nop
3635 #endif
3636 SET_CF();
3637 break;
3639 case 0x52: // removable media eject
3640 CLEAR_CF();
3641 regs.u.r8.ah = 0; // "ok ejection may proceed"
3642 break;
3644 case 0x83: {
3645 if( regs.u.r8.al == 0 ) {
3646 // Set Interval requested.
3647 if( ( read_byte( 0x40, 0xA0 ) & 1 ) == 0 ) {
3648 // Interval not already set.
3649 write_byte( 0x40, 0xA0, 1 ); // Set status byte.
3650 write_word( 0x40, 0x98, ES ); // Byte location, segment
3651 write_word( 0x40, 0x9A, regs.u.r16.bx ); // Byte location, offset
3652 write_word( 0x40, 0x9C, regs.u.r16.dx ); // Low word, delay
3653 write_word( 0x40, 0x9E, regs.u.r16.cx ); // High word, delay.
3654 CLEAR_CF( );
3655 irqDisable = inb( 0xA1 );
3656 outb( 0xA1, irqDisable & 0xFE );
3657 bRegister = inb_cmos( 0xB ); // Unmask IRQ8 so INT70 will get through.
3658 outb_cmos( 0xB, bRegister | 0x40 ); // Turn on the Periodic Interrupt timer
3659 } else {
3660 // Interval already set.
3661 BX_DEBUG_INT15("int15: Func 83h, failed, already waiting.\n" );
3662 SET_CF();
3663 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3665 } else if( regs.u.r8.al == 1 ) {
3666 // Clear Interval requested
3667 write_byte( 0x40, 0xA0, 0 ); // Clear status byte
3668 CLEAR_CF( );
3669 bRegister = inb_cmos( 0xB );
3670 outb_cmos( 0xB, bRegister & ~0x40 ); // Turn off the Periodic Interrupt timer
3671 } else {
3672 BX_DEBUG_INT15("int15: Func 83h, failed.\n" );
3673 SET_CF();
3674 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3675 regs.u.r8.al--;
3678 break;
3681 case 0x87:
3682 #if BX_CPU < 3
3683 # error "Int15 function 87h not supported on < 80386"
3684 #endif
3685 // +++ should probably have descriptor checks
3686 // +++ should have exception handlers
3688 // turn off interrupts
3689 ASM_START
3691 ASM_END
3693 prev_a20_enable = set_enable_a20(1); // enable A20 line
3695 // 128K max of transfer on 386+ ???
3696 // source == destination ???
3698 // ES:SI points to descriptor table
3699 // offset use initially comments
3700 // ==============================================
3701 // 00..07 Unused zeros Null descriptor
3702 // 08..0f GDT zeros filled in by BIOS
3703 // 10..17 source ssssssss source of data
3704 // 18..1f dest dddddddd destination of data
3705 // 20..27 CS zeros filled in by BIOS
3706 // 28..2f SS zeros filled in by BIOS
3708 //es:si
3709 //eeee0
3710 //0ssss
3711 //-----
3713 // check for access rights of source & dest here
3715 // Initialize GDT descriptor
3716 base15_00 = (ES << 4) + regs.u.r16.si;
3717 base23_16 = ES >> 12;
3718 if (base15_00 < (ES<<4))
3719 base23_16++;
3720 write_word(ES, regs.u.r16.si+0x08+0, 47); // limit 15:00 = 6 * 8bytes/descriptor
3721 write_word(ES, regs.u.r16.si+0x08+2, base15_00);// base 15:00
3722 write_byte(ES, regs.u.r16.si+0x08+4, base23_16);// base 23:16
3723 write_byte(ES, regs.u.r16.si+0x08+5, 0x93); // access
3724 write_word(ES, regs.u.r16.si+0x08+6, 0x0000); // base 31:24/reserved/limit 19:16
3726 // Initialize CS descriptor
3727 write_word(ES, regs.u.r16.si+0x20+0, 0xffff);// limit 15:00 = normal 64K limit
3728 write_word(ES, regs.u.r16.si+0x20+2, 0x0000);// base 15:00
3729 write_byte(ES, regs.u.r16.si+0x20+4, 0x000f);// base 23:16
3730 write_byte(ES, regs.u.r16.si+0x20+5, 0x9b); // access
3731 write_word(ES, regs.u.r16.si+0x20+6, 0x0000);// base 31:24/reserved/limit 19:16
3733 // Initialize SS descriptor
3734 ss = get_SS();
3735 base15_00 = ss << 4;
3736 base23_16 = ss >> 12;
3737 write_word(ES, regs.u.r16.si+0x28+0, 0xffff); // limit 15:00 = normal 64K limit
3738 write_word(ES, regs.u.r16.si+0x28+2, base15_00);// base 15:00
3739 write_byte(ES, regs.u.r16.si+0x28+4, base23_16);// base 23:16
3740 write_byte(ES, regs.u.r16.si+0x28+5, 0x93); // access
3741 write_word(ES, regs.u.r16.si+0x28+6, 0x0000); // base 31:24/reserved/limit 19:16
3743 CX = regs.u.r16.cx;
3744 ASM_START
3745 // Compile generates locals offset info relative to SP.
3746 // Get CX (word count) from stack.
3747 mov bx, sp
3748 SEG SS
3749 mov cx, _int15_function.CX [bx]
3751 // since we need to set SS:SP, save them to the BDA
3752 // for future restore
3753 push eax
3754 xor eax, eax
3755 mov ds, ax
3756 mov 0x0469, ss
3757 mov 0x0467, sp
3759 SEG ES
3760 lgdt [si + 0x08]
3761 SEG CS
3762 lidt [pmode_IDT_info]
3763 ;; perhaps do something with IDT here
3765 ;; set PE bit in CR0
3766 mov eax, cr0
3767 or al, #0x01
3768 mov cr0, eax
3769 ;; far jump to flush CPU queue after transition to protected mode
3770 JMP_AP(0x0020, protected_mode)
3772 protected_mode:
3773 ;; GDT points to valid descriptor table, now load SS, DS, ES
3774 mov ax, #0x28 ;; 101 000 = 5th descriptor in table, TI=GDT, RPL=00
3775 mov ss, ax
3776 mov ax, #0x10 ;; 010 000 = 2nd descriptor in table, TI=GDT, RPL=00
3777 mov ds, ax
3778 mov ax, #0x18 ;; 011 000 = 3rd descriptor in table, TI=GDT, RPL=00
3779 mov es, ax
3780 xor si, si
3781 xor di, di
3784 movsw ;; move CX words from DS:SI to ES:DI
3786 ;; make sure DS and ES limits are 64KB
3787 mov ax, #0x28
3788 mov ds, ax
3789 mov es, ax
3791 ;; reset PG bit in CR0 ???
3792 mov eax, cr0
3793 and al, #0xFE
3794 mov cr0, eax
3796 ;; far jump to flush CPU queue after transition to real mode
3797 JMP_AP(0xf000, real_mode)
3799 real_mode:
3800 ;; restore IDT to normal real-mode defaults
3801 SEG CS
3802 lidt [rmode_IDT_info]
3804 // restore SS:SP from the BDA
3805 xor ax, ax
3806 mov ds, ax
3807 mov ss, 0x0469
3808 mov sp, 0x0467
3809 pop eax
3810 ASM_END
3812 set_enable_a20(prev_a20_enable);
3814 // turn back on interrupts
3815 ASM_START
3817 ASM_END
3819 regs.u.r8.ah = 0;
3820 CLEAR_CF();
3821 break;
3824 case 0x88:
3825 // Get the amount of extended memory (above 1M)
3826 #if BX_CPU < 2
3827 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3828 SET_CF();
3829 #else
3830 regs.u.r8.al = inb_cmos(0x30);
3831 regs.u.r8.ah = inb_cmos(0x31);
3833 // According to Ralf Brown's interrupt the limit should be 15M,
3834 // but real machines mostly return max. 63M.
3835 if(regs.u.r16.ax > 0xffc0)
3836 regs.u.r16.ax = 0xffc0;
3838 CLEAR_CF();
3839 #endif
3840 break;
3842 case 0x90:
3843 /* Device busy interrupt. Called by Int 16h when no key available */
3844 break;
3846 case 0x91:
3847 /* Interrupt complete. Called by Int 16h when key becomes available */
3848 break;
3850 case 0xbf:
3851 BX_INFO("*** int 15h function AH=bf not yet supported!\n");
3852 SET_CF();
3853 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3854 break;
3856 case 0xC0:
3857 #if 0
3858 SET_CF();
3859 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3860 break;
3861 #endif
3862 CLEAR_CF();
3863 regs.u.r8.ah = 0;
3864 regs.u.r16.bx = BIOS_CONFIG_TABLE;
3865 ES = 0xF000;
3866 break;
3868 case 0xc1:
3869 ES = ebda_seg;
3870 CLEAR_CF();
3871 break;
3873 case 0xd8:
3874 bios_printf(BIOS_PRINTF_DEBUG, "EISA BIOS not present\n");
3875 SET_CF();
3876 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3877 break;
3879 default:
3880 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
3881 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
3882 SET_CF();
3883 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3884 break;
3888 #if BX_USE_PS2_MOUSE
3889 void
3890 int15_function_mouse(regs, ES, DS, FLAGS)
3891 pusha_regs_t regs; // REGS pushed via pusha
3892 Bit16u ES, DS, FLAGS;
3894 Bit16u ebda_seg=read_word(0x0040,0x000E);
3895 Bit8u mouse_flags_1, mouse_flags_2;
3896 Bit16u mouse_driver_seg;
3897 Bit16u mouse_driver_offset;
3898 Bit8u comm_byte, prev_command_byte;
3899 Bit8u ret, mouse_data1, mouse_data2, mouse_data3;
3901 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3903 switch (regs.u.r8.ah) {
3904 case 0xC2:
3905 // Return Codes status in AH
3906 // =========================
3907 // 00: success
3908 // 01: invalid subfunction (AL > 7)
3909 // 02: invalid input value (out of allowable range)
3910 // 03: interface error
3911 // 04: resend command received from mouse controller,
3912 // device driver should attempt command again
3913 // 05: cannot enable mouse, since no far call has been installed
3914 // 80/86: mouse service not implemented
3916 switch (regs.u.r8.al) {
3917 case 0: // Disable/Enable Mouse
3918 BX_DEBUG_INT15("case 0:\n");
3919 switch (regs.u.r8.bh) {
3920 case 0: // Disable Mouse
3921 BX_DEBUG_INT15("case 0: disable mouse\n");
3922 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3923 ret = send_to_mouse_ctrl(0xF5); // disable mouse command
3924 if (ret == 0) {
3925 ret = get_mouse_data(&mouse_data1);
3926 if ( (ret == 0) || (mouse_data1 == 0xFA) ) {
3927 CLEAR_CF();
3928 regs.u.r8.ah = 0;
3929 return;
3933 // error
3934 SET_CF();
3935 regs.u.r8.ah = ret;
3936 return;
3937 break;
3939 case 1: // Enable Mouse
3940 BX_DEBUG_INT15("case 1: enable mouse\n");
3941 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
3942 if ( (mouse_flags_2 & 0x80) == 0 ) {
3943 BX_DEBUG_INT15("INT 15h C2 Enable Mouse, no far call handler\n");
3944 SET_CF(); // error
3945 regs.u.r8.ah = 5; // no far call installed
3946 return;
3948 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3949 ret = send_to_mouse_ctrl(0xF4); // enable mouse command
3950 if (ret == 0) {
3951 ret = get_mouse_data(&mouse_data1);
3952 if ( (ret == 0) && (mouse_data1 == 0xFA) ) {
3953 enable_mouse_int_and_events(); // turn IRQ12 and packet generation on
3954 CLEAR_CF();
3955 regs.u.r8.ah = 0;
3956 return;
3959 SET_CF();
3960 regs.u.r8.ah = ret;
3961 return;
3963 default: // invalid subfunction
3964 BX_DEBUG_INT15("INT 15h C2 AL=0, BH=%02x\n", (unsigned) regs.u.r8.bh);
3965 SET_CF(); // error
3966 regs.u.r8.ah = 1; // invalid subfunction
3967 return;
3969 break;
3971 case 1: // Reset Mouse
3972 case 5: // Initialize Mouse
3973 BX_DEBUG_INT15("case 1 or 5:\n");
3974 if (regs.u.r8.al == 5) {
3975 if (regs.u.r8.bh != 3) {
3976 SET_CF();
3977 regs.u.r8.ah = 0x02; // invalid input
3978 return;
3980 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
3981 mouse_flags_2 = (mouse_flags_2 & 0x00) | regs.u.r8.bh;
3982 mouse_flags_1 = 0x00;
3983 write_byte(ebda_seg, 0x0026, mouse_flags_1);
3984 write_byte(ebda_seg, 0x0027, mouse_flags_2);
3987 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3988 ret = send_to_mouse_ctrl(0xFF); // reset mouse command
3989 if (ret == 0) {
3990 ret = get_mouse_data(&mouse_data3);
3991 // if no mouse attached, it will return RESEND
3992 if (mouse_data3 == 0xfe) {
3993 SET_CF();
3994 return;
3996 if (mouse_data3 != 0xfa)
3997 BX_PANIC("Mouse reset returned %02x (should be ack)\n", (unsigned)mouse_data3);
3998 if ( ret == 0 ) {
3999 ret = get_mouse_data(&mouse_data1);
4000 if ( ret == 0 ) {
4001 ret = get_mouse_data(&mouse_data2);
4002 if ( ret == 0 ) {
4003 // turn IRQ12 and packet generation on
4004 enable_mouse_int_and_events();
4005 CLEAR_CF();
4006 regs.u.r8.ah = 0;
4007 regs.u.r8.bl = mouse_data1;
4008 regs.u.r8.bh = mouse_data2;
4009 return;
4015 // error
4016 SET_CF();
4017 regs.u.r8.ah = ret;
4018 return;
4020 case 2: // Set Sample Rate
4021 BX_DEBUG_INT15("case 2:\n");
4022 switch (regs.u.r8.bh) {
4023 case 0: mouse_data1 = 10; break; // 10 reports/sec
4024 case 1: mouse_data1 = 20; break; // 20 reports/sec
4025 case 2: mouse_data1 = 40; break; // 40 reports/sec
4026 case 3: mouse_data1 = 60; break; // 60 reports/sec
4027 case 4: mouse_data1 = 80; break; // 80 reports/sec
4028 case 5: mouse_data1 = 100; break; // 100 reports/sec (default)
4029 case 6: mouse_data1 = 200; break; // 200 reports/sec
4030 default: mouse_data1 = 0;
4032 if (mouse_data1 > 0) {
4033 ret = send_to_mouse_ctrl(0xF3); // set sample rate command
4034 if (ret == 0) {
4035 ret = get_mouse_data(&mouse_data2);
4036 ret = send_to_mouse_ctrl(mouse_data1);
4037 ret = get_mouse_data(&mouse_data2);
4038 CLEAR_CF();
4039 regs.u.r8.ah = 0;
4040 } else {
4041 // error
4042 SET_CF();
4043 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4045 } else {
4046 // error
4047 SET_CF();
4048 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4050 break;
4052 case 3: // Set Resolution
4053 BX_DEBUG_INT15("case 3:\n");
4054 // BH:
4055 // 0 = 25 dpi, 1 count per millimeter
4056 // 1 = 50 dpi, 2 counts per millimeter
4057 // 2 = 100 dpi, 4 counts per millimeter
4058 // 3 = 200 dpi, 8 counts per millimeter
4059 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4060 if (regs.u.r8.bh < 4) {
4061 ret = send_to_mouse_ctrl(0xE8); // set resolution command
4062 if (ret == 0) {
4063 ret = get_mouse_data(&mouse_data1);
4064 if (mouse_data1 != 0xfa)
4065 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
4066 ret = send_to_mouse_ctrl(regs.u.r8.bh);
4067 ret = get_mouse_data(&mouse_data1);
4068 if (mouse_data1 != 0xfa)
4069 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
4070 CLEAR_CF();
4071 regs.u.r8.ah = 0;
4072 } else {
4073 // error
4074 SET_CF();
4075 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4077 } else {
4078 // error
4079 SET_CF();
4080 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4082 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4083 break;
4085 case 4: // Get Device ID
4086 BX_DEBUG_INT15("case 4:\n");
4087 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4088 ret = send_to_mouse_ctrl(0xF2); // get mouse ID command
4089 if (ret == 0) {
4090 ret = get_mouse_data(&mouse_data1);
4091 ret = get_mouse_data(&mouse_data2);
4092 CLEAR_CF();
4093 regs.u.r8.ah = 0;
4094 regs.u.r8.bh = mouse_data2;
4095 } else {
4096 // error
4097 SET_CF();
4098 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4100 break;
4102 case 6: // Return Status & Set Scaling Factor...
4103 BX_DEBUG_INT15("case 6:\n");
4104 switch (regs.u.r8.bh) {
4105 case 0: // Return Status
4106 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4107 ret = send_to_mouse_ctrl(0xE9); // get mouse info command
4108 if (ret == 0) {
4109 ret = get_mouse_data(&mouse_data1);
4110 if (mouse_data1 != 0xfa)
4111 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
4112 if (ret == 0) {
4113 ret = get_mouse_data(&mouse_data1);
4114 if ( ret == 0 ) {
4115 ret = get_mouse_data(&mouse_data2);
4116 if ( ret == 0 ) {
4117 ret = get_mouse_data(&mouse_data3);
4118 if ( ret == 0 ) {
4119 CLEAR_CF();
4120 regs.u.r8.ah = 0;
4121 regs.u.r8.bl = mouse_data1;
4122 regs.u.r8.cl = mouse_data2;
4123 regs.u.r8.dl = mouse_data3;
4124 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4125 return;
4132 // error
4133 SET_CF();
4134 regs.u.r8.ah = ret;
4135 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4136 return;
4138 case 1: // Set Scaling Factor to 1:1
4139 case 2: // Set Scaling Factor to 2:1
4140 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4141 if (regs.u.r8.bh == 1) {
4142 ret = send_to_mouse_ctrl(0xE6);
4143 } else {
4144 ret = send_to_mouse_ctrl(0xE7);
4146 if (ret == 0) {
4147 get_mouse_data(&mouse_data1);
4148 ret = (mouse_data1 != 0xFA);
4150 if (ret == 0) {
4151 CLEAR_CF();
4152 regs.u.r8.ah = 0;
4153 } else {
4154 // error
4155 SET_CF();
4156 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4158 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4159 break;
4161 default:
4162 BX_PANIC("INT 15h C2 AL=6, BH=%02x\n", (unsigned) regs.u.r8.bh);
4164 break;
4166 case 7: // Set Mouse Handler Address
4167 BX_DEBUG_INT15("case 7:\n");
4168 mouse_driver_seg = ES;
4169 mouse_driver_offset = regs.u.r16.bx;
4170 write_word(ebda_seg, 0x0022, mouse_driver_offset);
4171 write_word(ebda_seg, 0x0024, mouse_driver_seg);
4172 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4173 if (mouse_driver_offset == 0 && mouse_driver_seg == 0) {
4174 /* remove handler */
4175 if ( (mouse_flags_2 & 0x80) != 0 ) {
4176 mouse_flags_2 &= ~0x80;
4177 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4180 else {
4181 /* install handler */
4182 mouse_flags_2 |= 0x80;
4184 write_byte(ebda_seg, 0x0027, mouse_flags_2);
4185 CLEAR_CF();
4186 regs.u.r8.ah = 0;
4187 break;
4189 default:
4190 BX_DEBUG_INT15("case default:\n");
4191 regs.u.r8.ah = 1; // invalid function
4192 SET_CF();
4194 break;
4196 default:
4197 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4198 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4199 SET_CF();
4200 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4201 break;
4204 #endif
4207 void set_e820_range(ES, DI, start, end, type)
4208 Bit16u ES;
4209 Bit16u DI;
4210 Bit32u start;
4211 Bit32u end;
4212 Bit16u type;
4214 write_word(ES, DI, start);
4215 write_word(ES, DI+2, start >> 16);
4216 write_word(ES, DI+4, 0x00);
4217 write_word(ES, DI+6, 0x00);
4219 end -= start;
4220 write_word(ES, DI+8, end);
4221 write_word(ES, DI+10, end >> 16);
4222 write_word(ES, DI+12, 0x0000);
4223 write_word(ES, DI+14, 0x0000);
4225 write_word(ES, DI+16, type);
4226 write_word(ES, DI+18, 0x0);
4229 void
4230 int15_function32(regs, ES, DS, FLAGS)
4231 pushad_regs_t regs; // REGS pushed via pushad
4232 Bit16u ES, DS, FLAGS;
4234 Bit32u extended_memory_size=0; // 64bits long
4235 Bit16u CX,DX;
4237 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
4239 switch (regs.u.r8.ah) {
4240 case 0x86:
4241 // Wait for CX:DX microseconds. currently using the
4242 // refresh request port 0x61 bit4, toggling every 15usec
4244 CX = regs.u.r16.cx;
4245 DX = regs.u.r16.dx;
4247 ASM_START
4250 ;; Get the count in eax
4251 mov bx, sp
4252 SEG SS
4253 mov ax, _int15_function32.CX [bx]
4254 shl eax, #16
4255 SEG SS
4256 mov ax, _int15_function32.DX [bx]
4258 ;; convert to numbers of 15usec ticks
4259 mov ebx, #15
4260 xor edx, edx
4261 div eax, ebx
4262 mov ecx, eax
4264 ;; wait for ecx number of refresh requests
4265 in al, #0x61
4266 and al,#0x10
4267 mov ah, al
4269 or ecx, ecx
4270 je int1586_tick_end
4271 int1586_tick:
4272 in al, #0x61
4273 and al,#0x10
4274 cmp al, ah
4275 je int1586_tick
4276 mov ah, al
4277 dec ecx
4278 jnz int1586_tick
4279 int1586_tick_end:
4280 ASM_END
4282 break;
4284 case 0xe8:
4285 switch(regs.u.r8.al)
4287 case 0x20: // coded by osmaker aka K.J.
4288 if(regs.u.r32.edx == 0x534D4150)
4290 extended_memory_size = inb_cmos(0x35);
4291 extended_memory_size <<= 8;
4292 extended_memory_size |= inb_cmos(0x34);
4293 extended_memory_size *= 64;
4294 // greater than EFF00000???
4295 if(extended_memory_size > 0x3bc000) {
4296 extended_memory_size = 0x3bc000; // everything after this is reserved memory until we get to 0x100000000
4298 extended_memory_size *= 1024;
4299 extended_memory_size += (16L * 1024 * 1024);
4301 if(extended_memory_size <= (16L * 1024 * 1024)) {
4302 extended_memory_size = inb_cmos(0x31);
4303 extended_memory_size <<= 8;
4304 extended_memory_size |= inb_cmos(0x30);
4305 extended_memory_size *= 1024;
4308 switch(regs.u.r16.bx)
4310 case 0:
4311 set_e820_range(ES, regs.u.r16.di,
4312 0x0000000L, 0x0009fc00L, 1);
4313 regs.u.r32.ebx = 1;
4314 regs.u.r32.eax = 0x534D4150;
4315 regs.u.r32.ecx = 0x14;
4316 CLEAR_CF();
4317 return;
4318 break;
4319 case 1:
4320 set_e820_range(ES, regs.u.r16.di,
4321 0x0009fc00L, 0x000a0000L, 2);
4322 regs.u.r32.ebx = 2;
4323 regs.u.r32.eax = 0x534D4150;
4324 regs.u.r32.ecx = 0x14;
4325 CLEAR_CF();
4326 return;
4327 break;
4328 case 2:
4329 set_e820_range(ES, regs.u.r16.di,
4330 0x000e8000L, 0x00100000L, 2);
4331 regs.u.r32.ebx = 3;
4332 regs.u.r32.eax = 0x534D4150;
4333 regs.u.r32.ecx = 0x14;
4334 CLEAR_CF();
4335 return;
4336 break;
4337 case 3:
4338 set_e820_range(ES, regs.u.r16.di,
4339 0x00100000L,
4340 extended_memory_size - ACPI_DATA_SIZE, 1);
4341 regs.u.r32.ebx = 4;
4342 regs.u.r32.eax = 0x534D4150;
4343 regs.u.r32.ecx = 0x14;
4344 CLEAR_CF();
4345 return;
4346 break;
4347 case 4:
4348 set_e820_range(ES, regs.u.r16.di,
4349 extended_memory_size - ACPI_DATA_SIZE,
4350 extended_memory_size, 3); // ACPI RAM
4351 regs.u.r32.ebx = 5;
4352 regs.u.r32.eax = 0x534D4150;
4353 regs.u.r32.ecx = 0x14;
4354 CLEAR_CF();
4355 return;
4356 break;
4357 case 5:
4358 /* 256KB BIOS area at the end of 4 GB */
4359 set_e820_range(ES, regs.u.r16.di,
4360 0xfffc0000L, 0x00000000L, 2);
4361 regs.u.r32.ebx = 0;
4362 regs.u.r32.eax = 0x534D4150;
4363 regs.u.r32.ecx = 0x14;
4364 CLEAR_CF();
4365 return;
4366 default: /* AX=E820, DX=534D4150, BX unrecognized */
4367 goto int15_unimplemented;
4368 break;
4370 } else {
4371 // if DX != 0x534D4150)
4372 goto int15_unimplemented;
4374 break;
4376 case 0x01:
4377 // do we have any reason to fail here ?
4378 CLEAR_CF();
4380 // my real system sets ax and bx to 0
4381 // this is confirmed by Ralph Brown list
4382 // but syslinux v1.48 is known to behave
4383 // strangely if ax is set to 0
4384 // regs.u.r16.ax = 0;
4385 // regs.u.r16.bx = 0;
4387 // Get the amount of extended memory (above 1M)
4388 regs.u.r8.cl = inb_cmos(0x30);
4389 regs.u.r8.ch = inb_cmos(0x31);
4391 // limit to 15M
4392 if(regs.u.r16.cx > 0x3c00)
4394 regs.u.r16.cx = 0x3c00;
4397 // Get the amount of extended memory above 16M in 64k blocs
4398 regs.u.r8.dl = inb_cmos(0x34);
4399 regs.u.r8.dh = inb_cmos(0x35);
4401 // Set configured memory equal to extended memory
4402 regs.u.r16.ax = regs.u.r16.cx;
4403 regs.u.r16.bx = regs.u.r16.dx;
4404 break;
4405 default: /* AH=0xE8?? but not implemented */
4406 goto int15_unimplemented;
4408 break;
4409 int15_unimplemented:
4410 // fall into the default
4411 default:
4412 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4413 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4414 SET_CF();
4415 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4416 break;
4420 void
4421 int16_function(DI, SI, BP, SP, BX, DX, CX, AX, FLAGS)
4422 Bit16u DI, SI, BP, SP, BX, DX, CX, AX, FLAGS;
4424 Bit8u scan_code, ascii_code, shift_flags, led_flags, count;
4425 Bit16u kbd_code, max;
4427 BX_DEBUG_INT16("int16: AX=%04x BX=%04x CX=%04x DX=%04x \n", AX, BX, CX, DX);
4429 shift_flags = read_byte(0x0040, 0x17);
4430 led_flags = read_byte(0x0040, 0x97);
4431 if ((((shift_flags >> 4) & 0x07) ^ (led_flags & 0x07)) != 0) {
4432 ASM_START
4434 ASM_END
4435 outb(0x60, 0xed);
4436 while ((inb(0x64) & 0x01) == 0) outb(0x80, 0x21);
4437 if ((inb(0x60) == 0xfa)) {
4438 led_flags &= 0xf8;
4439 led_flags |= ((shift_flags >> 4) & 0x07);
4440 outb(0x60, led_flags & 0x07);
4441 while ((inb(0x64) & 0x01) == 0) outb(0x80, 0x21);
4442 inb(0x60);
4443 write_byte(0x0040, 0x97, led_flags);
4445 ASM_START
4447 ASM_END
4450 switch (GET_AH()) {
4451 case 0x00: /* read keyboard input */
4453 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4454 BX_PANIC("KBD: int16h: out of keyboard input\n");
4456 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4457 else if (ascii_code == 0xE0) ascii_code = 0;
4458 AX = (scan_code << 8) | ascii_code;
4459 break;
4461 case 0x01: /* check keyboard status */
4462 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4463 SET_ZF();
4464 return;
4466 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4467 else if (ascii_code == 0xE0) ascii_code = 0;
4468 AX = (scan_code << 8) | ascii_code;
4469 CLEAR_ZF();
4470 break;
4472 case 0x02: /* get shift flag status */
4473 shift_flags = read_byte(0x0040, 0x17);
4474 SET_AL(shift_flags);
4475 break;
4477 case 0x05: /* store key-stroke into buffer */
4478 if ( !enqueue_key(GET_CH(), GET_CL()) ) {
4479 SET_AL(1);
4481 else {
4482 SET_AL(0);
4484 break;
4486 case 0x09: /* GET KEYBOARD FUNCTIONALITY */
4487 // bit Bochs Description
4488 // 7 0 reserved
4489 // 6 0 INT 16/AH=20h-22h supported (122-key keyboard support)
4490 // 5 1 INT 16/AH=10h-12h supported (enhanced keyboard support)
4491 // 4 1 INT 16/AH=0Ah supported
4492 // 3 0 INT 16/AX=0306h supported
4493 // 2 0 INT 16/AX=0305h supported
4494 // 1 0 INT 16/AX=0304h supported
4495 // 0 0 INT 16/AX=0300h supported
4497 SET_AL(0x30);
4498 break;
4500 case 0x0A: /* GET KEYBOARD ID */
4501 count = 2;
4502 kbd_code = 0x0;
4503 outb(0x60, 0xf2);
4504 /* Wait for data */
4505 max=0xffff;
4506 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4507 if (max>0x0) {
4508 if ((inb(0x60) == 0xfa)) {
4509 do {
4510 max=0xffff;
4511 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4512 if (max>0x0) {
4513 kbd_code >>= 8;
4514 kbd_code |= (inb(0x60) << 8);
4516 } while (--count>0);
4519 BX=kbd_code;
4520 break;
4522 case 0x10: /* read MF-II keyboard input */
4524 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4525 BX_PANIC("KBD: int16h: out of keyboard input\n");
4527 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4528 AX = (scan_code << 8) | ascii_code;
4529 break;
4531 case 0x11: /* check MF-II keyboard status */
4532 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4533 SET_ZF();
4534 return;
4536 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4537 AX = (scan_code << 8) | ascii_code;
4538 CLEAR_ZF();
4539 break;
4541 case 0x12: /* get extended keyboard status */
4542 shift_flags = read_byte(0x0040, 0x17);
4543 SET_AL(shift_flags);
4544 shift_flags = read_byte(0x0040, 0x18) & 0x73;
4545 shift_flags |= read_byte(0x0040, 0x96) & 0x0c;
4546 SET_AH(shift_flags);
4547 BX_DEBUG_INT16("int16: func 12 sending %04x\n",AX);
4548 break;
4550 case 0x92: /* keyboard capability check called by DOS 5.0+ keyb */
4551 SET_AH(0x80); // function int16 ah=0x10-0x12 supported
4552 break;
4554 case 0xA2: /* 122 keys capability check called by DOS 5.0+ keyb */
4555 // don't change AH : function int16 ah=0x20-0x22 NOT supported
4556 break;
4558 case 0x6F:
4559 if (GET_AL() == 0x08)
4560 SET_AH(0x02); // unsupported, aka normal keyboard
4562 default:
4563 BX_INFO("KBD: unsupported int 16h function %02x\n", GET_AH());
4567 unsigned int
4568 dequeue_key(scan_code, ascii_code, incr)
4569 Bit8u *scan_code;
4570 Bit8u *ascii_code;
4571 unsigned int incr;
4573 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail;
4574 Bit16u ss;
4575 Bit8u acode, scode;
4577 #if BX_CPU < 2
4578 buffer_start = 0x001E;
4579 buffer_end = 0x003E;
4580 #else
4581 buffer_start = read_word(0x0040, 0x0080);
4582 buffer_end = read_word(0x0040, 0x0082);
4583 #endif
4585 buffer_head = read_word(0x0040, 0x001a);
4586 buffer_tail = read_word(0x0040, 0x001c);
4588 if (buffer_head != buffer_tail) {
4589 ss = get_SS();
4590 acode = read_byte(0x0040, buffer_head);
4591 scode = read_byte(0x0040, buffer_head+1);
4592 write_byte(ss, ascii_code, acode);
4593 write_byte(ss, scan_code, scode);
4595 if (incr) {
4596 buffer_head += 2;
4597 if (buffer_head >= buffer_end)
4598 buffer_head = buffer_start;
4599 write_word(0x0040, 0x001a, buffer_head);
4601 return(1);
4603 else {
4604 return(0);
4608 static char panic_msg_keyb_buffer_full[] = "%s: keyboard input buffer full\n";
4610 Bit8u
4611 inhibit_mouse_int_and_events()
4613 Bit8u command_byte, prev_command_byte;
4615 // Turn off IRQ generation and aux data line
4616 if ( inb(0x64) & 0x02 )
4617 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4618 outb(0x64, 0x20); // get command byte
4619 while ( (inb(0x64) & 0x01) != 0x01 );
4620 prev_command_byte = inb(0x60);
4621 command_byte = prev_command_byte;
4622 //while ( (inb(0x64) & 0x02) );
4623 if ( inb(0x64) & 0x02 )
4624 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4625 command_byte &= 0xfd; // turn off IRQ 12 generation
4626 command_byte |= 0x20; // disable mouse serial clock line
4627 outb(0x64, 0x60); // write command byte
4628 outb(0x60, command_byte);
4629 return(prev_command_byte);
4632 void
4633 enable_mouse_int_and_events()
4635 Bit8u command_byte;
4637 // Turn on IRQ generation and aux data line
4638 if ( inb(0x64) & 0x02 )
4639 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4640 outb(0x64, 0x20); // get command byte
4641 while ( (inb(0x64) & 0x01) != 0x01 );
4642 command_byte = inb(0x60);
4643 //while ( (inb(0x64) & 0x02) );
4644 if ( inb(0x64) & 0x02 )
4645 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4646 command_byte |= 0x02; // turn on IRQ 12 generation
4647 command_byte &= 0xdf; // enable mouse serial clock line
4648 outb(0x64, 0x60); // write command byte
4649 outb(0x60, command_byte);
4652 Bit8u
4653 send_to_mouse_ctrl(sendbyte)
4654 Bit8u sendbyte;
4656 Bit8u response;
4658 // wait for chance to write to ctrl
4659 if ( inb(0x64) & 0x02 )
4660 BX_PANIC(panic_msg_keyb_buffer_full,"sendmouse");
4661 outb(0x64, 0xD4);
4662 outb(0x60, sendbyte);
4663 return(0);
4667 Bit8u
4668 get_mouse_data(data)
4669 Bit8u *data;
4671 Bit8u response;
4672 Bit16u ss;
4674 while ( (inb(0x64) & 0x21) != 0x21 ) {
4677 response = inb(0x60);
4679 ss = get_SS();
4680 write_byte(ss, data, response);
4681 return(0);
4684 void
4685 set_kbd_command_byte(command_byte)
4686 Bit8u command_byte;
4688 if ( inb(0x64) & 0x02 )
4689 BX_PANIC(panic_msg_keyb_buffer_full,"setkbdcomm");
4690 outb(0x64, 0xD4);
4692 outb(0x64, 0x60); // write command byte
4693 outb(0x60, command_byte);
4696 void
4697 int09_function(DI, SI, BP, SP, BX, DX, CX, AX)
4698 Bit16u DI, SI, BP, SP, BX, DX, CX, AX;
4700 Bit8u scancode, asciicode, shift_flags;
4701 Bit8u mf2_flags, mf2_state;
4704 // DS has been set to F000 before call
4708 scancode = GET_AL();
4710 if (scancode == 0) {
4711 BX_INFO("KBD: int09 handler: AL=0\n");
4712 return;
4716 shift_flags = read_byte(0x0040, 0x17);
4717 mf2_flags = read_byte(0x0040, 0x18);
4718 mf2_state = read_byte(0x0040, 0x96);
4719 asciicode = 0;
4721 switch (scancode) {
4722 case 0x3a: /* Caps Lock press */
4723 shift_flags ^= 0x40;
4724 write_byte(0x0040, 0x17, shift_flags);
4725 mf2_flags |= 0x40;
4726 write_byte(0x0040, 0x18, mf2_flags);
4727 break;
4728 case 0xba: /* Caps Lock release */
4729 mf2_flags &= ~0x40;
4730 write_byte(0x0040, 0x18, mf2_flags);
4731 break;
4733 case 0x2a: /* L Shift press */
4734 shift_flags |= 0x02;
4735 write_byte(0x0040, 0x17, shift_flags);
4736 break;
4737 case 0xaa: /* L Shift release */
4738 shift_flags &= ~0x02;
4739 write_byte(0x0040, 0x17, shift_flags);
4740 break;
4742 case 0x36: /* R Shift press */
4743 shift_flags |= 0x01;
4744 write_byte(0x0040, 0x17, shift_flags);
4745 break;
4746 case 0xb6: /* R Shift release */
4747 shift_flags &= ~0x01;
4748 write_byte(0x0040, 0x17, shift_flags);
4749 break;
4751 case 0x1d: /* Ctrl press */
4752 if ((mf2_state & 0x01) == 0) {
4753 shift_flags |= 0x04;
4754 write_byte(0x0040, 0x17, shift_flags);
4755 if (mf2_state & 0x02) {
4756 mf2_state |= 0x04;
4757 write_byte(0x0040, 0x96, mf2_state);
4758 } else {
4759 mf2_flags |= 0x01;
4760 write_byte(0x0040, 0x18, mf2_flags);
4763 break;
4764 case 0x9d: /* Ctrl release */
4765 if ((mf2_state & 0x01) == 0) {
4766 shift_flags &= ~0x04;
4767 write_byte(0x0040, 0x17, shift_flags);
4768 if (mf2_state & 0x02) {
4769 mf2_state &= ~0x04;
4770 write_byte(0x0040, 0x96, mf2_state);
4771 } else {
4772 mf2_flags &= ~0x01;
4773 write_byte(0x0040, 0x18, mf2_flags);
4776 break;
4778 case 0x38: /* Alt press */
4779 shift_flags |= 0x08;
4780 write_byte(0x0040, 0x17, shift_flags);
4781 if (mf2_state & 0x02) {
4782 mf2_state |= 0x08;
4783 write_byte(0x0040, 0x96, mf2_state);
4784 } else {
4785 mf2_flags |= 0x02;
4786 write_byte(0x0040, 0x18, mf2_flags);
4788 break;
4789 case 0xb8: /* Alt release */
4790 shift_flags &= ~0x08;
4791 write_byte(0x0040, 0x17, shift_flags);
4792 if (mf2_state & 0x02) {
4793 mf2_state &= ~0x08;
4794 write_byte(0x0040, 0x96, mf2_state);
4795 } else {
4796 mf2_flags &= ~0x02;
4797 write_byte(0x0040, 0x18, mf2_flags);
4799 break;
4801 case 0x45: /* Num Lock press */
4802 if ((mf2_state & 0x03) == 0) {
4803 mf2_flags |= 0x20;
4804 write_byte(0x0040, 0x18, mf2_flags);
4805 shift_flags ^= 0x20;
4806 write_byte(0x0040, 0x17, shift_flags);
4808 break;
4809 case 0xc5: /* Num Lock release */
4810 if ((mf2_state & 0x03) == 0) {
4811 mf2_flags &= ~0x20;
4812 write_byte(0x0040, 0x18, mf2_flags);
4814 break;
4816 case 0x46: /* Scroll Lock press */
4817 mf2_flags |= 0x10;
4818 write_byte(0x0040, 0x18, mf2_flags);
4819 shift_flags ^= 0x10;
4820 write_byte(0x0040, 0x17, shift_flags);
4821 break;
4823 case 0xc6: /* Scroll Lock release */
4824 mf2_flags &= ~0x10;
4825 write_byte(0x0040, 0x18, mf2_flags);
4826 break;
4828 default:
4829 if (scancode & 0x80) {
4830 break; /* toss key releases ... */
4832 if (scancode > MAX_SCAN_CODE) {
4833 BX_INFO("KBD: int09h_handler(): unknown scancode read: 0x%02x!\n", scancode);
4834 return;
4836 if (shift_flags & 0x08) { /* ALT */
4837 asciicode = scan_to_scanascii[scancode].alt;
4838 scancode = scan_to_scanascii[scancode].alt >> 8;
4839 } else if (shift_flags & 0x04) { /* CONTROL */
4840 asciicode = scan_to_scanascii[scancode].control;
4841 scancode = scan_to_scanascii[scancode].control >> 8;
4842 } else if (((mf2_state & 0x02) > 0) && ((scancode >= 0x47) && (scancode <= 0x53))) {
4843 /* extended keys handling */
4844 asciicode = 0xe0;
4845 scancode = scan_to_scanascii[scancode].normal >> 8;
4846 } else if (shift_flags & 0x03) { /* LSHIFT + RSHIFT */
4847 /* check if lock state should be ignored
4848 * because a SHIFT key are pressed */
4850 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
4851 asciicode = scan_to_scanascii[scancode].normal;
4852 scancode = scan_to_scanascii[scancode].normal >> 8;
4853 } else {
4854 asciicode = scan_to_scanascii[scancode].shift;
4855 scancode = scan_to_scanascii[scancode].shift >> 8;
4857 } else {
4858 /* check if lock is on */
4859 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
4860 asciicode = scan_to_scanascii[scancode].shift;
4861 scancode = scan_to_scanascii[scancode].shift >> 8;
4862 } else {
4863 asciicode = scan_to_scanascii[scancode].normal;
4864 scancode = scan_to_scanascii[scancode].normal >> 8;
4867 if (scancode==0 && asciicode==0) {
4868 BX_INFO("KBD: int09h_handler(): scancode & asciicode are zero?\n");
4870 enqueue_key(scancode, asciicode);
4871 break;
4873 if ((scancode & 0x7f) != 0x1d) {
4874 mf2_state &= ~0x01;
4876 mf2_state &= ~0x02;
4877 write_byte(0x0040, 0x96, mf2_state);
4880 unsigned int
4881 enqueue_key(scan_code, ascii_code)
4882 Bit8u scan_code, ascii_code;
4884 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail, temp_tail;
4886 #if BX_CPU < 2
4887 buffer_start = 0x001E;
4888 buffer_end = 0x003E;
4889 #else
4890 buffer_start = read_word(0x0040, 0x0080);
4891 buffer_end = read_word(0x0040, 0x0082);
4892 #endif
4894 buffer_head = read_word(0x0040, 0x001A);
4895 buffer_tail = read_word(0x0040, 0x001C);
4897 temp_tail = buffer_tail;
4898 buffer_tail += 2;
4899 if (buffer_tail >= buffer_end)
4900 buffer_tail = buffer_start;
4902 if (buffer_tail == buffer_head) {
4903 return(0);
4906 write_byte(0x0040, temp_tail, ascii_code);
4907 write_byte(0x0040, temp_tail+1, scan_code);
4908 write_word(0x0040, 0x001C, buffer_tail);
4909 return(1);
4913 void
4914 int74_function(make_farcall, Z, Y, X, status)
4915 Bit16u make_farcall, Z, Y, X, status;
4917 Bit16u ebda_seg=read_word(0x0040,0x000E);
4918 Bit8u in_byte, index, package_count;
4919 Bit8u mouse_flags_1, mouse_flags_2;
4921 BX_DEBUG_INT74("entering int74_function\n");
4922 make_farcall = 0;
4924 in_byte = inb(0x64);
4925 if ( (in_byte & 0x21) != 0x21 ) {
4926 return;
4928 in_byte = inb(0x60);
4929 BX_DEBUG_INT74("int74: read byte %02x\n", in_byte);
4931 mouse_flags_1 = read_byte(ebda_seg, 0x0026);
4932 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4934 if ( (mouse_flags_2 & 0x80) != 0x80 ) {
4935 return;
4938 package_count = mouse_flags_2 & 0x07;
4939 index = mouse_flags_1 & 0x07;
4940 write_byte(ebda_seg, 0x28 + index, in_byte);
4942 if ( (index+1) >= package_count ) {
4943 BX_DEBUG_INT74("int74_function: make_farcall=1\n");
4944 status = read_byte(ebda_seg, 0x0028 + 0);
4945 X = read_byte(ebda_seg, 0x0028 + 1);
4946 Y = read_byte(ebda_seg, 0x0028 + 2);
4947 Z = 0;
4948 mouse_flags_1 = 0;
4949 // check if far call handler installed
4950 if (mouse_flags_2 & 0x80)
4951 make_farcall = 1;
4953 else {
4954 mouse_flags_1++;
4956 write_byte(ebda_seg, 0x0026, mouse_flags_1);
4959 #define SET_DISK_RET_STATUS(status) write_byte(0x0040, 0x0074, status)
4961 #if BX_USE_ATADRV
4963 void
4964 int13_harddisk(EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
4965 Bit16u EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
4967 Bit32u lba;
4968 Bit16u ebda_seg=read_word(0x0040,0x000E);
4969 Bit16u cylinder, head, sector;
4970 Bit16u segment, offset;
4971 Bit16u npc, nph, npspt, nlc, nlh, nlspt;
4972 Bit16u size, count;
4973 Bit8u device, status;
4975 BX_DEBUG_INT13_HD("int13_harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
4977 write_byte(0x0040, 0x008e, 0); // clear completion flag
4979 // basic check : device has to be defined
4980 if ( (GET_ELDL() < 0x80) || (GET_ELDL() >= 0x80 + BX_MAX_ATA_DEVICES) ) {
4981 BX_INFO("int13_harddisk: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
4982 goto int13_fail;
4985 // Get the ata channel
4986 device=read_byte(ebda_seg,&EbdaData->ata.hdidmap[GET_ELDL()-0x80]);
4988 // basic check : device has to be valid
4989 if (device >= BX_MAX_ATA_DEVICES) {
4990 BX_INFO("int13_harddisk: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
4991 goto int13_fail;
4994 switch (GET_AH()) {
4996 case 0x00: /* disk controller reset */
4997 ata_reset (device);
4998 goto int13_success;
4999 break;
5001 case 0x01: /* read disk status */
5002 status = read_byte(0x0040, 0x0074);
5003 SET_AH(status);
5004 SET_DISK_RET_STATUS(0);
5005 /* set CF if error status read */
5006 if (status) goto int13_fail_nostatus;
5007 else goto int13_success_noah;
5008 break;
5010 case 0x02: // read disk sectors
5011 case 0x03: // write disk sectors
5012 case 0x04: // verify disk sectors
5014 count = GET_AL();
5015 cylinder = GET_CH();
5016 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
5017 sector = (GET_CL() & 0x3f);
5018 head = GET_DH();
5020 segment = ES;
5021 offset = BX;
5023 if ((count > 128) || (count == 0) || (sector == 0)) {
5024 BX_INFO("int13_harddisk: function %02x, parameter out of range!\n",GET_AH());
5025 goto int13_fail;
5028 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
5029 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
5030 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
5032 // sanity check on cyl heads, sec
5033 if( (cylinder >= nlc) || (head >= nlh) || (sector > nlspt )) {
5034 BX_INFO("int13_harddisk: function %02x, parameters out of range %04x/%04x/%04x!\n", GET_AH(), cylinder, head, sector);
5035 goto int13_fail;
5038 // FIXME verify
5039 if ( GET_AH() == 0x04 ) goto int13_success;
5041 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
5042 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
5044 // if needed, translate lchs to lba, and execute command
5045 if ( (nph != nlh) || (npspt != nlspt)) {
5046 lba = ((((Bit32u)cylinder * (Bit32u)nlh) + (Bit32u)head) * (Bit32u)nlspt) + (Bit32u)sector - 1;
5047 sector = 0; // this forces the command to be lba
5050 if ( GET_AH() == 0x02 )
5051 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, cylinder, head, sector, lba, segment, offset);
5052 else
5053 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, cylinder, head, sector, lba, segment, offset);
5055 // Set nb of sector transferred
5056 SET_AL(read_word(ebda_seg, &EbdaData->ata.trsfsectors));
5058 if (status != 0) {
5059 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
5060 SET_AH(0x0c);
5061 goto int13_fail_noah;
5064 goto int13_success;
5065 break;
5067 case 0x05: /* format disk track */
5068 BX_INFO("format disk track called\n");
5069 goto int13_success;
5070 return;
5071 break;
5073 case 0x08: /* read disk drive parameters */
5075 // Get logical geometry from table
5076 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
5077 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
5078 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
5079 count = read_byte(ebda_seg, &EbdaData->ata.hdcount);
5081 nlc = nlc - 2; /* 0 based , last sector not used */
5082 SET_AL(0);
5083 SET_CH(nlc & 0xff);
5084 SET_CL(((nlc >> 2) & 0xc0) | (nlspt & 0x3f));
5085 SET_DH(nlh - 1);
5086 SET_DL(count); /* FIXME returns 0, 1, or n hard drives */
5088 // FIXME should set ES & DI
5090 goto int13_success;
5091 break;
5093 case 0x10: /* check drive ready */
5094 // should look at 40:8E also???
5096 // Read the status from controller
5097 status = inb(read_word(ebda_seg, &EbdaData->ata.channels[device/2].iobase1) + ATA_CB_STAT);
5098 if ( (status & ( ATA_CB_STAT_BSY | ATA_CB_STAT_RDY )) == ATA_CB_STAT_RDY ) {
5099 goto int13_success;
5101 else {
5102 SET_AH(0xAA);
5103 goto int13_fail_noah;
5105 break;
5107 case 0x15: /* read disk drive size */
5109 // Get logical geometry from table
5110 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
5111 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
5112 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
5114 // Compute sector count seen by int13
5115 lba = (Bit32u)(nlc - 1) * (Bit32u)nlh * (Bit32u)nlspt;
5116 CX = lba >> 16;
5117 DX = lba & 0xffff;
5119 SET_AH(3); // hard disk accessible
5120 goto int13_success_noah;
5121 break;
5123 case 0x41: // IBM/MS installation check
5124 BX=0xaa55; // install check
5125 SET_AH(0x30); // EDD 3.0
5126 CX=0x0007; // ext disk access and edd, removable supported
5127 goto int13_success_noah;
5128 break;
5130 case 0x42: // IBM/MS extended read
5131 case 0x43: // IBM/MS extended write
5132 case 0x44: // IBM/MS verify
5133 case 0x47: // IBM/MS extended seek
5135 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
5136 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
5137 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
5139 // Can't use 64 bits lba
5140 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
5141 if (lba != 0L) {
5142 BX_PANIC("int13_harddisk: function %02x. Can't use 64bits lba\n",GET_AH());
5143 goto int13_fail;
5146 // Get 32 bits lba and check
5147 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5148 if (lba >= read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors) ) {
5149 BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH());
5150 goto int13_fail;
5153 // If verify or seek
5154 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
5155 goto int13_success;
5157 // Execute the command
5158 if ( GET_AH() == 0x42 )
5159 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, 0, 0, 0, lba, segment, offset);
5160 else
5161 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, 0, 0, 0, lba, segment, offset);
5163 count=read_word(ebda_seg, &EbdaData->ata.trsfsectors);
5164 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5166 if (status != 0) {
5167 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
5168 SET_AH(0x0c);
5169 goto int13_fail_noah;
5172 goto int13_success;
5173 break;
5175 case 0x45: // IBM/MS lock/unlock drive
5176 case 0x49: // IBM/MS extended media change
5177 goto int13_success; // Always success for HD
5178 break;
5180 case 0x46: // IBM/MS eject media
5181 SET_AH(0xb2); // Volume Not Removable
5182 goto int13_fail_noah; // Always fail for HD
5183 break;
5185 case 0x48: // IBM/MS get drive parameters
5186 size=read_word(DS,SI+(Bit16u)&Int13DPT->size);
5188 // Buffer is too small
5189 if(size < 0x1a)
5190 goto int13_fail;
5192 // EDD 1.x
5193 if(size >= 0x1a) {
5194 Bit16u blksize;
5196 npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
5197 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
5198 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
5199 lba = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors);
5200 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5202 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5203 if ((lba/npspt)/nph > 0x3fff)
5205 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x00); // geometry is invalid
5206 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0x3fff);
5208 else
5210 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x02); // geometry is valid
5211 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, (Bit32u)npc);
5213 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, (Bit32u)nph);
5214 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, (Bit32u)npspt);
5215 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, lba); // FIXME should be Bit64
5216 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0L);
5217 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5220 // EDD 2.x
5221 if(size >= 0x1e) {
5222 Bit8u channel, dev, irq, mode, checksum, i, translation;
5223 Bit16u iobase1, iobase2, options;
5225 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5227 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5228 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5230 // Fill in dpte
5231 channel = device / 2;
5232 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5233 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5234 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5235 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5236 translation = read_byte(ebda_seg, &EbdaData->ata.devices[device].translation);
5238 options = (translation==ATA_TRANSLATION_NONE?0:1)<<3; // chs translation
5239 options |= (1<<4); // lba translation
5240 options |= (mode==ATA_MODE_PIO32?1:0)<<7;
5241 options |= (translation==ATA_TRANSLATION_LBA?1:0)<<9;
5242 options |= (translation==ATA_TRANSLATION_RECHS?3:0)<<9;
5244 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5245 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2 + ATA_CB_DC);
5246 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5247 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5248 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5249 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5250 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5251 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5252 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5253 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5254 if (size >=0x42)
5255 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5256 else
5257 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x10);
5259 checksum=0;
5260 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, ((Bit8u*)(&EbdaData->ata.dpte)) + i);
5261 checksum = ~checksum;
5262 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5265 // EDD 3.x
5266 if(size >= 0x42) {
5267 Bit8u channel, iface, checksum, i;
5268 Bit16u iobase1;
5270 channel = device / 2;
5271 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5272 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5274 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5275 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5276 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5277 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5278 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5280 if (iface==ATA_IFACE_ISA) {
5281 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5282 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5283 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5284 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5286 else {
5287 // FIXME PCI
5289 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5290 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5291 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5292 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5294 if (iface==ATA_IFACE_ISA) {
5295 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5296 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5297 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5299 else {
5300 // FIXME PCI
5302 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5303 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5304 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5305 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5307 checksum=0;
5308 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5309 checksum = ~checksum;
5310 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5313 goto int13_success;
5314 break;
5316 case 0x4e: // // IBM/MS set hardware configuration
5317 // DMA, prefetch, PIO maximum not supported
5318 switch (GET_AL()) {
5319 case 0x01:
5320 case 0x03:
5321 case 0x04:
5322 case 0x06:
5323 goto int13_success;
5324 break;
5325 default :
5326 goto int13_fail;
5328 break;
5330 case 0x09: /* initialize drive parameters */
5331 case 0x0c: /* seek to specified cylinder */
5332 case 0x0d: /* alternate disk reset */
5333 case 0x11: /* recalibrate */
5334 case 0x14: /* controller internal diagnostic */
5335 BX_INFO("int13_harddisk: function %02xh unimplemented, returns success\n", GET_AH());
5336 goto int13_success;
5337 break;
5339 case 0x0a: /* read disk sectors with ECC */
5340 case 0x0b: /* write disk sectors with ECC */
5341 case 0x18: // set media type for format
5342 case 0x50: // IBM/MS send packet command
5343 default:
5344 BX_INFO("int13_harddisk: function %02xh unsupported, returns fail\n", GET_AH());
5345 goto int13_fail;
5346 break;
5349 int13_fail:
5350 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5351 int13_fail_noah:
5352 SET_DISK_RET_STATUS(GET_AH());
5353 int13_fail_nostatus:
5354 SET_CF(); // error occurred
5355 return;
5357 int13_success:
5358 SET_AH(0x00); // no error
5359 int13_success_noah:
5360 SET_DISK_RET_STATUS(0x00);
5361 CLEAR_CF(); // no error
5362 return;
5365 // ---------------------------------------------------------------------------
5366 // Start of int13 for cdrom
5367 // ---------------------------------------------------------------------------
5369 void
5370 int13_cdrom(EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
5371 Bit16u EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
5373 Bit16u ebda_seg=read_word(0x0040,0x000E);
5374 Bit8u device, status, locks;
5375 Bit8u atacmd[12];
5376 Bit32u lba;
5377 Bit16u count, segment, offset, i, size;
5379 BX_DEBUG_INT13_CD("int13_cdrom: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5381 SET_DISK_RET_STATUS(0x00);
5383 /* basic check : device should be 0xE0+ */
5384 if( (GET_ELDL() < 0xE0) || (GET_ELDL() >= 0xE0+BX_MAX_ATA_DEVICES) ) {
5385 BX_INFO("int13_cdrom: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
5386 goto int13_fail;
5389 // Get the ata channel
5390 device=read_byte(ebda_seg,&EbdaData->ata.cdidmap[GET_ELDL()-0xE0]);
5392 /* basic check : device has to be valid */
5393 if (device >= BX_MAX_ATA_DEVICES) {
5394 BX_INFO("int13_cdrom: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
5395 goto int13_fail;
5398 switch (GET_AH()) {
5400 // all those functions return SUCCESS
5401 case 0x00: /* disk controller reset */
5402 case 0x09: /* initialize drive parameters */
5403 case 0x0c: /* seek to specified cylinder */
5404 case 0x0d: /* alternate disk reset */
5405 case 0x10: /* check drive ready */
5406 case 0x11: /* recalibrate */
5407 case 0x14: /* controller internal diagnostic */
5408 case 0x16: /* detect disk change */
5409 goto int13_success;
5410 break;
5412 // all those functions return disk write-protected
5413 case 0x03: /* write disk sectors */
5414 case 0x05: /* format disk track */
5415 case 0x43: // IBM/MS extended write
5416 SET_AH(0x03);
5417 goto int13_fail_noah;
5418 break;
5420 case 0x01: /* read disk status */
5421 status = read_byte(0x0040, 0x0074);
5422 SET_AH(status);
5423 SET_DISK_RET_STATUS(0);
5425 /* set CF if error status read */
5426 if (status) goto int13_fail_nostatus;
5427 else goto int13_success_noah;
5428 break;
5430 case 0x15: /* read disk drive size */
5431 SET_AH(0x02);
5432 goto int13_fail_noah;
5433 break;
5435 case 0x41: // IBM/MS installation check
5436 BX=0xaa55; // install check
5437 SET_AH(0x30); // EDD 2.1
5438 CX=0x0007; // ext disk access, removable and edd
5439 goto int13_success_noah;
5440 break;
5442 case 0x42: // IBM/MS extended read
5443 case 0x44: // IBM/MS verify sectors
5444 case 0x47: // IBM/MS extended seek
5446 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
5447 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
5448 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
5450 // Can't use 64 bits lba
5451 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
5452 if (lba != 0L) {
5453 BX_PANIC("int13_cdrom: function %02x. Can't use 64bits lba\n",GET_AH());
5454 goto int13_fail;
5457 // Get 32 bits lba
5458 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5460 // If verify or seek
5461 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
5462 goto int13_success;
5464 memsetb(get_SS(),atacmd,0,12);
5465 atacmd[0]=0x28; // READ command
5466 atacmd[7]=(count & 0xff00) >> 8; // Sectors
5467 atacmd[8]=(count & 0x00ff); // Sectors
5468 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
5469 atacmd[3]=(lba & 0x00ff0000) >> 16;
5470 atacmd[4]=(lba & 0x0000ff00) >> 8;
5471 atacmd[5]=(lba & 0x000000ff);
5472 status = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, count*2048L, ATA_DATA_IN, segment,offset);
5474 count = (Bit16u)(read_dword(ebda_seg, &EbdaData->ata.trsfbytes) >> 11);
5475 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5477 if (status != 0) {
5478 BX_INFO("int13_cdrom: function %02x, status %02x !\n",GET_AH(),status);
5479 SET_AH(0x0c);
5480 goto int13_fail_noah;
5483 goto int13_success;
5484 break;
5486 case 0x45: // IBM/MS lock/unlock drive
5487 if (GET_AL() > 2) goto int13_fail;
5489 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5491 switch (GET_AL()) {
5492 case 0 : // lock
5493 if (locks == 0xff) {
5494 SET_AH(0xb4);
5495 SET_AL(1);
5496 goto int13_fail_noah;
5498 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, ++locks);
5499 SET_AL(1);
5500 break;
5501 case 1 : // unlock
5502 if (locks == 0x00) {
5503 SET_AH(0xb0);
5504 SET_AL(0);
5505 goto int13_fail_noah;
5507 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, --locks);
5508 SET_AL(locks==0?0:1);
5509 break;
5510 case 2 : // status
5511 SET_AL(locks==0?0:1);
5512 break;
5514 goto int13_success;
5515 break;
5517 case 0x46: // IBM/MS eject media
5518 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5520 if (locks != 0) {
5521 SET_AH(0xb1); // media locked
5522 goto int13_fail_noah;
5524 // FIXME should handle 0x31 no media in device
5525 // FIXME should handle 0xb5 valid request failed
5527 // Call removable media eject
5528 ASM_START
5529 push bp
5530 mov bp, sp
5532 mov ah, #0x52
5533 int #0x15
5534 mov _int13_cdrom.status + 2[bp], ah
5535 jnc int13_cdrom_rme_end
5536 mov _int13_cdrom.status, #1
5537 int13_cdrom_rme_end:
5538 pop bp
5539 ASM_END
5541 if (status != 0) {
5542 SET_AH(0xb1); // media locked
5543 goto int13_fail_noah;
5546 goto int13_success;
5547 break;
5549 case 0x48: // IBM/MS get drive parameters
5550 size = read_word(DS,SI+(Bit16u)&Int13Ext->size);
5552 // Buffer is too small
5553 if(size < 0x1a)
5554 goto int13_fail;
5556 // EDD 1.x
5557 if(size >= 0x1a) {
5558 Bit16u cylinders, heads, spt, blksize;
5560 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5562 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5563 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x74); // removable, media change, lockable, max values
5564 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0xffffffff);
5565 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, 0xffffffff);
5566 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, 0xffffffff);
5567 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, 0xffffffff); // FIXME should be Bit64
5568 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0xffffffff);
5569 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5572 // EDD 2.x
5573 if(size >= 0x1e) {
5574 Bit8u channel, dev, irq, mode, checksum, i;
5575 Bit16u iobase1, iobase2, options;
5577 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5579 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5580 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5582 // Fill in dpte
5583 channel = device / 2;
5584 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5585 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5586 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5587 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5589 // FIXME atapi device
5590 options = (1<<4); // lba translation
5591 options |= (1<<5); // removable device
5592 options |= (1<<6); // atapi device
5593 options |= (mode==ATA_MODE_PIO32?1:0<<7);
5595 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5596 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2 + ATA_CB_DC);
5597 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5598 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5599 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5600 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5601 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5602 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5603 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5604 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5605 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5607 checksum=0;
5608 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, ((Bit8u*)(&EbdaData->ata.dpte)) + i);
5609 checksum = ~checksum;
5610 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5613 // EDD 3.x
5614 if(size >= 0x42) {
5615 Bit8u channel, iface, checksum, i;
5616 Bit16u iobase1;
5618 channel = device / 2;
5619 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5620 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5622 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5623 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5624 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5625 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5626 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5628 if (iface==ATA_IFACE_ISA) {
5629 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5630 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5631 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5632 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5634 else {
5635 // FIXME PCI
5637 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5638 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5639 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5640 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5642 if (iface==ATA_IFACE_ISA) {
5643 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5644 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5645 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5647 else {
5648 // FIXME PCI
5650 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5651 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5652 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5653 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5655 checksum=0;
5656 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5657 checksum = ~checksum;
5658 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5661 goto int13_success;
5662 break;
5664 case 0x49: // IBM/MS extended media change
5665 // always send changed ??
5666 SET_AH(06);
5667 goto int13_fail_nostatus;
5668 break;
5670 case 0x4e: // // IBM/MS set hardware configuration
5671 // DMA, prefetch, PIO maximum not supported
5672 switch (GET_AL()) {
5673 case 0x01:
5674 case 0x03:
5675 case 0x04:
5676 case 0x06:
5677 goto int13_success;
5678 break;
5679 default :
5680 goto int13_fail;
5682 break;
5684 // all those functions return unimplemented
5685 case 0x02: /* read sectors */
5686 case 0x04: /* verify sectors */
5687 case 0x08: /* read disk drive parameters */
5688 case 0x0a: /* read disk sectors with ECC */
5689 case 0x0b: /* write disk sectors with ECC */
5690 case 0x18: /* set media type for format */
5691 case 0x50: // ? - send packet command
5692 default:
5693 BX_INFO("int13_cdrom: unsupported AH=%02x\n", GET_AH());
5694 goto int13_fail;
5695 break;
5698 int13_fail:
5699 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5700 int13_fail_noah:
5701 SET_DISK_RET_STATUS(GET_AH());
5702 int13_fail_nostatus:
5703 SET_CF(); // error occurred
5704 return;
5706 int13_success:
5707 SET_AH(0x00); // no error
5708 int13_success_noah:
5709 SET_DISK_RET_STATUS(0x00);
5710 CLEAR_CF(); // no error
5711 return;
5714 // ---------------------------------------------------------------------------
5715 // End of int13 for cdrom
5716 // ---------------------------------------------------------------------------
5718 #if BX_ELTORITO_BOOT
5719 // ---------------------------------------------------------------------------
5720 // Start of int13 for eltorito functions
5721 // ---------------------------------------------------------------------------
5723 void
5724 int13_eltorito(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
5725 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
5727 Bit16u ebda_seg=read_word(0x0040,0x000E);
5729 BX_DEBUG_INT13_ET("int13_eltorito: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5730 // BX_DEBUG_INT13_ET("int13_eltorito: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
5732 switch (GET_AH()) {
5734 // FIXME ElTorito Various. Should be implemented
5735 case 0x4a: // ElTorito - Initiate disk emu
5736 case 0x4c: // ElTorito - Initiate disk emu and boot
5737 case 0x4d: // ElTorito - Return Boot catalog
5738 BX_PANIC("Int13 eltorito call with AX=%04x. Please report\n",AX);
5739 goto int13_fail;
5740 break;
5742 case 0x4b: // ElTorito - Terminate disk emu
5743 // FIXME ElTorito Hardcoded
5744 write_byte(DS,SI+0x00,0x13);
5745 write_byte(DS,SI+0x01,read_byte(ebda_seg,&EbdaData->cdemu.media));
5746 write_byte(DS,SI+0x02,read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
5747 write_byte(DS,SI+0x03,read_byte(ebda_seg,&EbdaData->cdemu.controller_index));
5748 write_dword(DS,SI+0x04,read_dword(ebda_seg,&EbdaData->cdemu.ilba));
5749 write_word(DS,SI+0x08,read_word(ebda_seg,&EbdaData->cdemu.device_spec));
5750 write_word(DS,SI+0x0a,read_word(ebda_seg,&EbdaData->cdemu.buffer_segment));
5751 write_word(DS,SI+0x0c,read_word(ebda_seg,&EbdaData->cdemu.load_segment));
5752 write_word(DS,SI+0x0e,read_word(ebda_seg,&EbdaData->cdemu.sector_count));
5753 write_byte(DS,SI+0x10,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.cylinders));
5754 write_byte(DS,SI+0x11,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.spt));
5755 write_byte(DS,SI+0x12,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.heads));
5757 // If we have to terminate emulation
5758 if(GET_AL() == 0x00) {
5759 // FIXME ElTorito Various. Should be handled accordingly to spec
5760 write_byte(ebda_seg,&EbdaData->cdemu.active, 0x00); // bye bye
5763 goto int13_success;
5764 break;
5766 default:
5767 BX_INFO("int13_eltorito: unsupported AH=%02x\n", GET_AH());
5768 goto int13_fail;
5769 break;
5772 int13_fail:
5773 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5774 SET_DISK_RET_STATUS(GET_AH());
5775 SET_CF(); // error occurred
5776 return;
5778 int13_success:
5779 SET_AH(0x00); // no error
5780 SET_DISK_RET_STATUS(0x00);
5781 CLEAR_CF(); // no error
5782 return;
5785 // ---------------------------------------------------------------------------
5786 // End of int13 for eltorito functions
5787 // ---------------------------------------------------------------------------
5789 // ---------------------------------------------------------------------------
5790 // Start of int13 when emulating a device from the cd
5791 // ---------------------------------------------------------------------------
5793 void
5794 int13_cdemu(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
5795 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
5797 Bit16u ebda_seg=read_word(0x0040,0x000E);
5798 Bit8u device, status;
5799 Bit16u vheads, vspt, vcylinders;
5800 Bit16u head, sector, cylinder, nbsectors;
5801 Bit32u vlba, ilba, slba, elba;
5802 Bit16u before, segment, offset;
5803 Bit8u atacmd[12];
5805 BX_DEBUG_INT13_ET("int13_cdemu: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5807 /* at this point, we are emulating a floppy/harddisk */
5809 // Recompute the device number
5810 device = read_byte(ebda_seg,&EbdaData->cdemu.controller_index) * 2;
5811 device += read_byte(ebda_seg,&EbdaData->cdemu.device_spec);
5813 SET_DISK_RET_STATUS(0x00);
5815 /* basic checks : emulation should be active, dl should equal the emulated drive */
5816 if( (read_byte(ebda_seg,&EbdaData->cdemu.active) ==0 )
5817 || (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive ) != GET_DL())) {
5818 BX_INFO("int13_cdemu: function %02x, emulation not active for DL= %02x\n", GET_AH(), GET_DL());
5819 goto int13_fail;
5822 switch (GET_AH()) {
5824 // all those functions return SUCCESS
5825 case 0x00: /* disk controller reset */
5826 case 0x09: /* initialize drive parameters */
5827 case 0x0c: /* seek to specified cylinder */
5828 case 0x0d: /* alternate disk reset */ // FIXME ElTorito Various. should really reset ?
5829 case 0x10: /* check drive ready */ // FIXME ElTorito Various. should check if ready ?
5830 case 0x11: /* recalibrate */
5831 case 0x14: /* controller internal diagnostic */
5832 case 0x16: /* detect disk change */
5833 goto int13_success;
5834 break;
5836 // all those functions return disk write-protected
5837 case 0x03: /* write disk sectors */
5838 case 0x05: /* format disk track */
5839 SET_AH(0x03);
5840 goto int13_fail_noah;
5841 break;
5843 case 0x01: /* read disk status */
5844 status=read_byte(0x0040, 0x0074);
5845 SET_AH(status);
5846 SET_DISK_RET_STATUS(0);
5848 /* set CF if error status read */
5849 if (status) goto int13_fail_nostatus;
5850 else goto int13_success_noah;
5851 break;
5853 case 0x02: // read disk sectors
5854 case 0x04: // verify disk sectors
5855 vspt = read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
5856 vcylinders = read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders);
5857 vheads = read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads);
5859 ilba = read_dword(ebda_seg,&EbdaData->cdemu.ilba);
5861 sector = GET_CL() & 0x003f;
5862 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
5863 head = GET_DH();
5864 nbsectors = GET_AL();
5865 segment = ES;
5866 offset = BX;
5868 // no sector to read ?
5869 if(nbsectors==0) goto int13_success;
5871 // sanity checks sco openserver needs this!
5872 if ((sector > vspt)
5873 || (cylinder >= vcylinders)
5874 || (head >= vheads)) {
5875 goto int13_fail;
5878 // After controls, verify do nothing
5879 if (GET_AH() == 0x04) goto int13_success;
5881 segment = ES+(BX / 16);
5882 offset = BX % 16;
5884 // calculate the virtual lba inside the image
5885 vlba=((((Bit32u)cylinder*(Bit32u)vheads)+(Bit32u)head)*(Bit32u)vspt)+((Bit32u)(sector-1));
5887 // In advance so we don't loose the count
5888 SET_AL(nbsectors);
5890 // start lba on cd
5891 slba = (Bit32u)vlba/4;
5892 before= (Bit16u)vlba%4;
5894 // end lba on cd
5895 elba = (Bit32u)(vlba+nbsectors-1)/4;
5897 memsetb(get_SS(),atacmd,0,12);
5898 atacmd[0]=0x28; // READ command
5899 atacmd[7]=((Bit16u)(elba-slba+1) & 0xff00) >> 8; // Sectors
5900 atacmd[8]=((Bit16u)(elba-slba+1) & 0x00ff); // Sectors
5901 atacmd[2]=(ilba+slba & 0xff000000) >> 24; // LBA
5902 atacmd[3]=(ilba+slba & 0x00ff0000) >> 16;
5903 atacmd[4]=(ilba+slba & 0x0000ff00) >> 8;
5904 atacmd[5]=(ilba+slba & 0x000000ff);
5905 if((status = ata_cmd_packet(device, 12, get_SS(), atacmd, before*512, nbsectors*512L, ATA_DATA_IN, segment,offset)) != 0) {
5906 BX_INFO("int13_cdemu: function %02x, error %02x !\n",GET_AH(),status);
5907 SET_AH(0x02);
5908 SET_AL(0);
5909 goto int13_fail_noah;
5912 goto int13_success;
5913 break;
5915 case 0x08: /* read disk drive parameters */
5916 vspt=read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
5917 vcylinders=read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders) - 1;
5918 vheads=read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads) - 1;
5920 SET_AL( 0x00 );
5921 SET_BL( 0x00 );
5922 SET_CH( vcylinders & 0xff );
5923 SET_CL((( vcylinders >> 2) & 0xc0) | ( vspt & 0x3f ));
5924 SET_DH( vheads );
5925 SET_DL( 0x02 ); // FIXME ElTorito Various. should send the real count of drives 1 or 2
5926 // FIXME ElTorito Harddisk. should send the HD count
5928 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
5929 case 0x01: SET_BL( 0x02 ); break;
5930 case 0x02: SET_BL( 0x04 ); break;
5931 case 0x03: SET_BL( 0x06 ); break;
5934 ASM_START
5935 push bp
5936 mov bp, sp
5937 mov ax, #diskette_param_table2
5938 mov _int13_cdemu.DI+2[bp], ax
5939 mov _int13_cdemu.ES+2[bp], cs
5940 pop bp
5941 ASM_END
5942 goto int13_success;
5943 break;
5945 case 0x15: /* read disk drive size */
5946 // FIXME ElTorito Harddisk. What geometry to send ?
5947 SET_AH(0x03);
5948 goto int13_success_noah;
5949 break;
5951 // all those functions return unimplemented
5952 case 0x0a: /* read disk sectors with ECC */
5953 case 0x0b: /* write disk sectors with ECC */
5954 case 0x18: /* set media type for format */
5955 case 0x41: // IBM/MS installation check
5956 // FIXME ElTorito Harddisk. Darwin would like to use EDD
5957 case 0x42: // IBM/MS extended read
5958 case 0x43: // IBM/MS extended write
5959 case 0x44: // IBM/MS verify sectors
5960 case 0x45: // IBM/MS lock/unlock drive
5961 case 0x46: // IBM/MS eject media
5962 case 0x47: // IBM/MS extended seek
5963 case 0x48: // IBM/MS get drive parameters
5964 case 0x49: // IBM/MS extended media change
5965 case 0x4e: // ? - set hardware configuration
5966 case 0x50: // ? - send packet command
5967 default:
5968 BX_INFO("int13_cdemu function AH=%02x unsupported, returns fail\n", GET_AH());
5969 goto int13_fail;
5970 break;
5973 int13_fail:
5974 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5975 int13_fail_noah:
5976 SET_DISK_RET_STATUS(GET_AH());
5977 int13_fail_nostatus:
5978 SET_CF(); // error occurred
5979 return;
5981 int13_success:
5982 SET_AH(0x00); // no error
5983 int13_success_noah:
5984 SET_DISK_RET_STATUS(0x00);
5985 CLEAR_CF(); // no error
5986 return;
5989 // ---------------------------------------------------------------------------
5990 // End of int13 when emulating a device from the cd
5991 // ---------------------------------------------------------------------------
5993 #endif // BX_ELTORITO_BOOT
5995 #else //BX_USE_ATADRV
5997 void
5998 outLBA(cylinder,hd_heads,head,hd_sectors,sector,dl)
5999 Bit16u cylinder;
6000 Bit16u hd_heads;
6001 Bit16u head;
6002 Bit16u hd_sectors;
6003 Bit16u sector;
6004 Bit16u dl;
6006 ASM_START
6007 push bp
6008 mov bp, sp
6009 push eax
6010 push ebx
6011 push edx
6012 xor eax,eax
6013 mov ax,4[bp] // cylinder
6014 xor ebx,ebx
6015 mov bl,6[bp] // hd_heads
6016 imul ebx
6018 mov bl,8[bp] // head
6019 add eax,ebx
6020 mov bl,10[bp] // hd_sectors
6021 imul ebx
6022 mov bl,12[bp] // sector
6023 add eax,ebx
6025 dec eax
6026 mov dx,#0x1f3
6027 out dx,al
6028 mov dx,#0x1f4
6029 mov al,ah
6030 out dx,al
6031 shr eax,#16
6032 mov dx,#0x1f5
6033 out dx,al
6034 and ah,#0xf
6035 mov bl,14[bp] // dl
6036 and bl,#1
6037 shl bl,#4
6038 or ah,bl
6039 or ah,#0xe0
6040 mov al,ah
6041 mov dx,#0x01f6
6042 out dx,al
6043 pop edx
6044 pop ebx
6045 pop eax
6046 pop bp
6047 ASM_END
6050 void
6051 int13_harddisk(EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
6052 Bit16u EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
6054 Bit8u drive, num_sectors, sector, head, status, mod;
6055 Bit8u drive_map;
6056 Bit8u n_drives;
6057 Bit16u cyl_mod, ax;
6058 Bit16u max_cylinder, cylinder, total_sectors;
6059 Bit16u hd_cylinders;
6060 Bit8u hd_heads, hd_sectors;
6061 Bit16u val16;
6062 Bit8u sector_count;
6063 unsigned int i;
6064 Bit16u tempbx;
6065 Bit16u dpsize;
6067 Bit16u count, segment, offset;
6068 Bit32u lba;
6069 Bit16u error;
6071 BX_DEBUG_INT13_HD("int13 harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
6073 write_byte(0x0040, 0x008e, 0); // clear completion flag
6075 /* at this point, DL is >= 0x80 to be passed from the floppy int13h
6076 handler code */
6077 /* check how many disks first (cmos reg 0x12), return an error if
6078 drive not present */
6079 drive_map = inb_cmos(0x12);
6080 drive_map = (((drive_map & 0xf0)==0) ? 0 : 1) |
6081 (((drive_map & 0x0f)==0) ? 0 : 2);
6082 n_drives = (drive_map==0) ? 0 :
6083 ((drive_map==3) ? 2 : 1);
6085 if (!(drive_map & (1<<(GET_ELDL()&0x7f)))) { /* allow 0, 1, or 2 disks */
6086 SET_AH(0x01);
6087 SET_DISK_RET_STATUS(0x01);
6088 SET_CF(); /* error occurred */
6089 return;
6092 switch (GET_AH()) {
6094 case 0x00: /* disk controller reset */
6095 BX_DEBUG_INT13_HD("int13_f00\n");
6097 SET_AH(0);
6098 SET_DISK_RET_STATUS(0);
6099 set_diskette_ret_status(0);
6100 set_diskette_current_cyl(0, 0); /* current cylinder, diskette 1 */
6101 set_diskette_current_cyl(1, 0); /* current cylinder, diskette 2 */
6102 CLEAR_CF(); /* successful */
6103 return;
6104 break;
6106 case 0x01: /* read disk status */
6107 BX_DEBUG_INT13_HD("int13_f01\n");
6108 status = read_byte(0x0040, 0x0074);
6109 SET_AH(status);
6110 SET_DISK_RET_STATUS(0);
6111 /* set CF if error status read */
6112 if (status) SET_CF();
6113 else CLEAR_CF();
6114 return;
6115 break;
6117 case 0x04: // verify disk sectors
6118 case 0x02: // read disk sectors
6119 drive = GET_ELDL();
6120 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6122 num_sectors = GET_AL();
6123 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
6124 sector = (GET_CL() & 0x3f);
6125 head = GET_DH();
6128 if (hd_cylinders > 1024) {
6129 if (hd_cylinders <= 2048) {
6130 cylinder <<= 1;
6132 else if (hd_cylinders <= 4096) {
6133 cylinder <<= 2;
6135 else if (hd_cylinders <= 8192) {
6136 cylinder <<= 3;
6138 else { // hd_cylinders <= 16384
6139 cylinder <<= 4;
6142 ax = head / hd_heads;
6143 cyl_mod = ax & 0xff;
6144 head = ax >> 8;
6145 cylinder |= cyl_mod;
6148 if ( (cylinder >= hd_cylinders) ||
6149 (sector > hd_sectors) ||
6150 (head >= hd_heads) ) {
6151 SET_AH(1);
6152 SET_DISK_RET_STATUS(1);
6153 SET_CF(); /* error occurred */
6154 return;
6157 if ( (num_sectors > 128) || (num_sectors == 0) )
6158 BX_PANIC("int13_harddisk: num_sectors out of range!\n");
6160 if (head > 15)
6161 BX_PANIC("hard drive BIOS:(read/verify) head > 15\n");
6163 if ( GET_AH() == 0x04 ) {
6164 SET_AH(0);
6165 SET_DISK_RET_STATUS(0);
6166 CLEAR_CF();
6167 return;
6170 status = inb(0x1f7);
6171 if (status & 0x80) {
6172 BX_PANIC("hard drive BIOS:(read/verify) BUSY bit set\n");
6174 outb(0x01f2, num_sectors);
6175 /* activate LBA? (tomv) */
6176 if (hd_heads > 16) {
6177 BX_DEBUG_INT13_HD("CHS: %x %x %x\n", cylinder, head, sector);
6178 outLBA(cylinder,hd_heads,head,hd_sectors,sector,drive);
6180 else {
6181 outb(0x01f3, sector);
6182 outb(0x01f4, cylinder & 0x00ff);
6183 outb(0x01f5, cylinder >> 8);
6184 outb(0x01f6, 0xa0 | ((drive & 0x01)<<4) | (head & 0x0f));
6186 outb(0x01f7, 0x20);
6188 while (1) {
6189 status = inb(0x1f7);
6190 if ( !(status & 0x80) ) break;
6193 if (status & 0x01) {
6194 BX_PANIC("hard drive BIOS:(read/verify) read error\n");
6195 } else if ( !(status & 0x08) ) {
6196 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
6197 BX_PANIC("hard drive BIOS:(read/verify) expected DRQ=1\n");
6200 sector_count = 0;
6201 tempbx = BX;
6203 ASM_START
6204 sti ;; enable higher priority interrupts
6205 ASM_END
6207 while (1) {
6208 ASM_START
6209 ;; store temp bx in real DI register
6210 push bp
6211 mov bp, sp
6212 mov di, _int13_harddisk.tempbx + 2 [bp]
6213 pop bp
6215 ;; adjust if there will be an overrun
6216 cmp di, #0xfe00
6217 jbe i13_f02_no_adjust
6218 i13_f02_adjust:
6219 sub di, #0x0200 ; sub 512 bytes from offset
6220 mov ax, es
6221 add ax, #0x0020 ; add 512 to segment
6222 mov es, ax
6224 i13_f02_no_adjust:
6225 mov cx, #0x0100 ;; counter (256 words = 512b)
6226 mov dx, #0x01f0 ;; AT data read port
6229 insw ;; CX words transfered from port(DX) to ES:[DI]
6231 i13_f02_done:
6232 ;; store real DI register back to temp bx
6233 push bp
6234 mov bp, sp
6235 mov _int13_harddisk.tempbx + 2 [bp], di
6236 pop bp
6237 ASM_END
6239 sector_count++;
6240 num_sectors--;
6241 if (num_sectors == 0) {
6242 status = inb(0x1f7);
6243 if ( (status & 0xc9) != 0x40 )
6244 BX_PANIC("no sectors left to read/verify, status is %02x\n", (unsigned) status);
6245 break;
6247 else {
6248 status = inb(0x1f7);
6249 if ( (status & 0xc9) != 0x48 )
6250 BX_PANIC("more sectors left to read/verify, status is %02x\n", (unsigned) status);
6251 continue;
6255 SET_AH(0);
6256 SET_DISK_RET_STATUS(0);
6257 SET_AL(sector_count);
6258 CLEAR_CF(); /* successful */
6259 return;
6260 break;
6263 case 0x03: /* write disk sectors */
6264 BX_DEBUG_INT13_HD("int13_f03\n");
6265 drive = GET_ELDL ();
6266 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6268 num_sectors = GET_AL();
6269 cylinder = GET_CH();
6270 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
6271 sector = (GET_CL() & 0x3f);
6272 head = GET_DH();
6274 if (hd_cylinders > 1024) {
6275 if (hd_cylinders <= 2048) {
6276 cylinder <<= 1;
6278 else if (hd_cylinders <= 4096) {
6279 cylinder <<= 2;
6281 else if (hd_cylinders <= 8192) {
6282 cylinder <<= 3;
6284 else { // hd_cylinders <= 16384
6285 cylinder <<= 4;
6288 ax = head / hd_heads;
6289 cyl_mod = ax & 0xff;
6290 head = ax >> 8;
6291 cylinder |= cyl_mod;
6294 if ( (cylinder >= hd_cylinders) ||
6295 (sector > hd_sectors) ||
6296 (head >= hd_heads) ) {
6297 SET_AH( 1);
6298 SET_DISK_RET_STATUS(1);
6299 SET_CF(); /* error occurred */
6300 return;
6303 if ( (num_sectors > 128) || (num_sectors == 0) )
6304 BX_PANIC("int13_harddisk: num_sectors out of range!\n");
6306 if (head > 15)
6307 BX_PANIC("hard drive BIOS:(read) head > 15\n");
6309 status = inb(0x1f7);
6310 if (status & 0x80) {
6311 BX_PANIC("hard drive BIOS:(read) BUSY bit set\n");
6313 // should check for Drive Ready Bit also in status reg
6314 outb(0x01f2, num_sectors);
6316 /* activate LBA? (tomv) */
6317 if (hd_heads > 16) {
6318 BX_DEBUG_INT13_HD("CHS (write): %x %x %x\n", cylinder, head, sector);
6319 outLBA(cylinder,hd_heads,head,hd_sectors,sector,GET_ELDL());
6321 else {
6322 outb(0x01f3, sector);
6323 outb(0x01f4, cylinder & 0x00ff);
6324 outb(0x01f5, cylinder >> 8);
6325 outb(0x01f6, 0xa0 | ((GET_ELDL() & 0x01)<<4) | (head & 0x0f));
6327 outb(0x01f7, 0x30);
6329 // wait for busy bit to turn off after seeking
6330 while (1) {
6331 status = inb(0x1f7);
6332 if ( !(status & 0x80) ) break;
6335 if ( !(status & 0x08) ) {
6336 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
6337 BX_PANIC("hard drive BIOS:(write) data-request bit not set\n");
6340 sector_count = 0;
6341 tempbx = BX;
6343 ASM_START
6344 sti ;; enable higher priority interrupts
6345 ASM_END
6347 while (1) {
6348 ASM_START
6349 ;; store temp bx in real SI register
6350 push bp
6351 mov bp, sp
6352 mov si, _int13_harddisk.tempbx + 2 [bp]
6353 pop bp
6355 ;; adjust if there will be an overrun
6356 cmp si, #0xfe00
6357 jbe i13_f03_no_adjust
6358 i13_f03_adjust:
6359 sub si, #0x0200 ; sub 512 bytes from offset
6360 mov ax, es
6361 add ax, #0x0020 ; add 512 to segment
6362 mov es, ax
6364 i13_f03_no_adjust:
6365 mov cx, #0x0100 ;; counter (256 words = 512b)
6366 mov dx, #0x01f0 ;; AT data read port
6368 seg ES
6370 outsw ;; CX words tranfered from ES:[SI] to port(DX)
6372 ;; store real SI register back to temp bx
6373 push bp
6374 mov bp, sp
6375 mov _int13_harddisk.tempbx + 2 [bp], si
6376 pop bp
6377 ASM_END
6379 sector_count++;
6380 num_sectors--;
6381 if (num_sectors == 0) {
6382 status = inb(0x1f7);
6383 if ( (status & 0xe9) != 0x40 )
6384 BX_PANIC("no sectors left to write, status is %02x\n", (unsigned) status);
6385 break;
6387 else {
6388 status = inb(0x1f7);
6389 if ( (status & 0xc9) != 0x48 )
6390 BX_PANIC("more sectors left to write, status is %02x\n", (unsigned) status);
6391 continue;
6395 SET_AH(0);
6396 SET_DISK_RET_STATUS(0);
6397 SET_AL(sector_count);
6398 CLEAR_CF(); /* successful */
6399 return;
6400 break;
6402 case 0x05: /* format disk track */
6403 BX_DEBUG_INT13_HD("int13_f05\n");
6404 BX_PANIC("format disk track called\n");
6405 /* nop */
6406 SET_AH(0);
6407 SET_DISK_RET_STATUS(0);
6408 CLEAR_CF(); /* successful */
6409 return;
6410 break;
6412 case 0x08: /* read disk drive parameters */
6413 BX_DEBUG_INT13_HD("int13_f08\n");
6415 drive = GET_ELDL ();
6416 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6418 // translate CHS
6420 if (hd_cylinders <= 1024) {
6421 // hd_cylinders >>= 0;
6422 // hd_heads <<= 0;
6424 else if (hd_cylinders <= 2048) {
6425 hd_cylinders >>= 1;
6426 hd_heads <<= 1;
6428 else if (hd_cylinders <= 4096) {
6429 hd_cylinders >>= 2;
6430 hd_heads <<= 2;
6432 else if (hd_cylinders <= 8192) {
6433 hd_cylinders >>= 3;
6434 hd_heads <<= 3;
6436 else { // hd_cylinders <= 16384
6437 hd_cylinders >>= 4;
6438 hd_heads <<= 4;
6441 max_cylinder = hd_cylinders - 2; /* 0 based */
6442 SET_AL(0);
6443 SET_CH(max_cylinder & 0xff);
6444 SET_CL(((max_cylinder >> 2) & 0xc0) | (hd_sectors & 0x3f));
6445 SET_DH(hd_heads - 1);
6446 SET_DL(n_drives); /* returns 0, 1, or 2 hard drives */
6447 SET_AH(0);
6448 SET_DISK_RET_STATUS(0);
6449 CLEAR_CF(); /* successful */
6451 return;
6452 break;
6454 case 0x09: /* initialize drive parameters */
6455 BX_DEBUG_INT13_HD("int13_f09\n");
6456 SET_AH(0);
6457 SET_DISK_RET_STATUS(0);
6458 CLEAR_CF(); /* successful */
6459 return;
6460 break;
6462 case 0x0a: /* read disk sectors with ECC */
6463 BX_DEBUG_INT13_HD("int13_f0a\n");
6464 case 0x0b: /* write disk sectors with ECC */
6465 BX_DEBUG_INT13_HD("int13_f0b\n");
6466 BX_PANIC("int13h Functions 0Ah & 0Bh not implemented!\n");
6467 return;
6468 break;
6470 case 0x0c: /* seek to specified cylinder */
6471 BX_DEBUG_INT13_HD("int13_f0c\n");
6472 BX_INFO("int13h function 0ch (seek) not implemented!\n");
6473 SET_AH(0);
6474 SET_DISK_RET_STATUS(0);
6475 CLEAR_CF(); /* successful */
6476 return;
6477 break;
6479 case 0x0d: /* alternate disk reset */
6480 BX_DEBUG_INT13_HD("int13_f0d\n");
6481 SET_AH(0);
6482 SET_DISK_RET_STATUS(0);
6483 CLEAR_CF(); /* successful */
6484 return;
6485 break;
6487 case 0x10: /* check drive ready */
6488 BX_DEBUG_INT13_HD("int13_f10\n");
6489 //SET_AH(0);
6490 //SET_DISK_RET_STATUS(0);
6491 //CLEAR_CF(); /* successful */
6492 //return;
6493 //break;
6495 // should look at 40:8E also???
6496 status = inb(0x01f7);
6497 if ( (status & 0xc0) == 0x40 ) {
6498 SET_AH(0);
6499 SET_DISK_RET_STATUS(0);
6500 CLEAR_CF(); // drive ready
6501 return;
6503 else {
6504 SET_AH(0xAA);
6505 SET_DISK_RET_STATUS(0xAA);
6506 SET_CF(); // not ready
6507 return;
6509 break;
6511 case 0x11: /* recalibrate */
6512 BX_DEBUG_INT13_HD("int13_f11\n");
6513 SET_AH(0);
6514 SET_DISK_RET_STATUS(0);
6515 CLEAR_CF(); /* successful */
6516 return;
6517 break;
6519 case 0x14: /* controller internal diagnostic */
6520 BX_DEBUG_INT13_HD("int13_f14\n");
6521 SET_AH(0);
6522 SET_DISK_RET_STATUS(0);
6523 CLEAR_CF(); /* successful */
6524 SET_AL(0);
6525 return;
6526 break;
6528 case 0x15: /* read disk drive size */
6529 drive = GET_ELDL();
6530 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6531 ASM_START
6532 push bp
6533 mov bp, sp
6534 mov al, _int13_harddisk.hd_heads + 2 [bp]
6535 mov ah, _int13_harddisk.hd_sectors + 2 [bp]
6536 mul al, ah ;; ax = heads * sectors
6537 mov bx, _int13_harddisk.hd_cylinders + 2 [bp]
6538 dec bx ;; use (cylinders - 1) ???
6539 mul ax, bx ;; dx:ax = (cylinders -1) * (heads * sectors)
6540 ;; now we need to move the 32bit result dx:ax to what the
6541 ;; BIOS wants which is cx:dx.
6542 ;; and then into CX:DX on the stack
6543 mov _int13_harddisk.CX + 2 [bp], dx
6544 mov _int13_harddisk.DX + 2 [bp], ax
6545 pop bp
6546 ASM_END
6547 SET_AH(3); // hard disk accessible
6548 SET_DISK_RET_STATUS(0); // ??? should this be 0
6549 CLEAR_CF(); // successful
6550 return;
6551 break;
6553 case 0x18: // set media type for format
6554 case 0x41: // IBM/MS
6555 case 0x42: // IBM/MS
6556 case 0x43: // IBM/MS
6557 case 0x44: // IBM/MS
6558 case 0x45: // IBM/MS lock/unlock drive
6559 case 0x46: // IBM/MS eject media
6560 case 0x47: // IBM/MS extended seek
6561 case 0x49: // IBM/MS extended media change
6562 case 0x50: // IBM/MS send packet command
6563 default:
6564 BX_INFO("int13_harddisk: unsupported AH=%02x\n", GET_AH());
6566 SET_AH(1); // code=invalid function in AH or invalid parameter
6567 SET_DISK_RET_STATUS(1);
6568 SET_CF(); /* unsuccessful */
6569 return;
6570 break;
6574 static char panic_msg_reg12h[] = "HD%d cmos reg 12h not type F\n";
6575 static char panic_msg_reg19h[] = "HD%d cmos reg %02xh not user definable type 47\n";
6577 void
6578 get_hd_geometry(drive, hd_cylinders, hd_heads, hd_sectors)
6579 Bit8u drive;
6580 Bit16u *hd_cylinders;
6581 Bit8u *hd_heads;
6582 Bit8u *hd_sectors;
6584 Bit8u hd_type;
6585 Bit16u ss;
6586 Bit16u cylinders;
6587 Bit8u iobase;
6589 ss = get_SS();
6590 if (drive == 0x80) {
6591 hd_type = inb_cmos(0x12) & 0xf0;
6592 if (hd_type != 0xf0)
6593 BX_INFO(panic_msg_reg12h,0);
6594 hd_type = inb_cmos(0x19); // HD0: extended type
6595 if (hd_type != 47)
6596 BX_INFO(panic_msg_reg19h,0,0x19);
6597 iobase = 0x1b;
6598 } else {
6599 hd_type = inb_cmos(0x12) & 0x0f;
6600 if (hd_type != 0x0f)
6601 BX_INFO(panic_msg_reg12h,1);
6602 hd_type = inb_cmos(0x1a); // HD0: extended type
6603 if (hd_type != 47)
6604 BX_INFO(panic_msg_reg19h,0,0x1a);
6605 iobase = 0x24;
6608 // cylinders
6609 cylinders = inb_cmos(iobase) | (inb_cmos(iobase+1) << 8);
6610 write_word(ss, hd_cylinders, cylinders);
6612 // heads
6613 write_byte(ss, hd_heads, inb_cmos(iobase+2));
6615 // sectors per track
6616 write_byte(ss, hd_sectors, inb_cmos(iobase+8));
6619 #endif //else BX_USE_ATADRV
6621 #if BX_SUPPORT_FLOPPY
6623 //////////////////////
6624 // FLOPPY functions //
6625 //////////////////////
6627 void floppy_reset_controller()
6629 Bit8u val8;
6631 // Reset controller
6632 val8 = inb(0x03f2);
6633 outb(0x03f2, val8 & ~0x04);
6634 outb(0x03f2, val8 | 0x04);
6636 // Wait for controller to come out of reset
6637 do {
6638 val8 = inb(0x3f4);
6639 } while ( (val8 & 0xc0) != 0x80 );
6642 void floppy_prepare_controller(drive)
6643 Bit16u drive;
6645 Bit8u val8, dor, prev_reset;
6647 // set 40:3e bit 7 to 0
6648 val8 = read_byte(0x0040, 0x003e);
6649 val8 &= 0x7f;
6650 write_byte(0x0040, 0x003e, val8);
6652 // turn on motor of selected drive, DMA & int enabled, normal operation
6653 prev_reset = inb(0x03f2) & 0x04;
6654 if (drive)
6655 dor = 0x20;
6656 else
6657 dor = 0x10;
6658 dor |= 0x0c;
6659 dor |= drive;
6660 outb(0x03f2, dor);
6662 // reset the disk motor timeout value of INT 08
6663 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
6665 // wait for drive readiness
6666 do {
6667 val8 = inb(0x3f4);
6668 } while ( (val8 & 0xc0) != 0x80 );
6670 if (prev_reset == 0) {
6671 // turn on interrupts
6672 ASM_START
6674 ASM_END
6675 // wait on 40:3e bit 7 to become 1
6676 do {
6677 val8 = read_byte(0x0040, 0x003e);
6678 } while ( (val8 & 0x80) == 0 );
6679 val8 &= 0x7f;
6680 ASM_START
6682 ASM_END
6683 write_byte(0x0040, 0x003e, val8);
6687 bx_bool
6688 floppy_media_known(drive)
6689 Bit16u drive;
6691 Bit8u val8;
6692 Bit16u media_state_offset;
6694 val8 = read_byte(0x0040, 0x003e); // diskette recal status
6695 if (drive)
6696 val8 >>= 1;
6697 val8 &= 0x01;
6698 if (val8 == 0)
6699 return(0);
6701 media_state_offset = 0x0090;
6702 if (drive)
6703 media_state_offset += 1;
6705 val8 = read_byte(0x0040, media_state_offset);
6706 val8 = (val8 >> 4) & 0x01;
6707 if (val8 == 0)
6708 return(0);
6710 // check pass, return KNOWN
6711 return(1);
6714 bx_bool
6715 floppy_media_sense(drive)
6716 Bit16u drive;
6718 bx_bool retval;
6719 Bit16u media_state_offset;
6720 Bit8u drive_type, config_data, media_state;
6722 if (floppy_drive_recal(drive) == 0) {
6723 return(0);
6726 // for now cheat and get drive type from CMOS,
6727 // assume media is same as drive type
6729 // ** config_data **
6730 // Bitfields for diskette media control:
6731 // Bit(s) Description (Table M0028)
6732 // 7-6 last data rate set by controller
6733 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6734 // 5-4 last diskette drive step rate selected
6735 // 00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
6736 // 3-2 {data rate at start of operation}
6737 // 1-0 reserved
6739 // ** media_state **
6740 // Bitfields for diskette drive media state:
6741 // Bit(s) Description (Table M0030)
6742 // 7-6 data rate
6743 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6744 // 5 double stepping required (e.g. 360kB in 1.2MB)
6745 // 4 media type established
6746 // 3 drive capable of supporting 4MB media
6747 // 2-0 on exit from BIOS, contains
6748 // 000 trying 360kB in 360kB
6749 // 001 trying 360kB in 1.2MB
6750 // 010 trying 1.2MB in 1.2MB
6751 // 011 360kB in 360kB established
6752 // 100 360kB in 1.2MB established
6753 // 101 1.2MB in 1.2MB established
6754 // 110 reserved
6755 // 111 all other formats/drives
6757 drive_type = inb_cmos(0x10);
6758 if (drive == 0)
6759 drive_type >>= 4;
6760 else
6761 drive_type &= 0x0f;
6762 if ( drive_type == 1 ) {
6763 // 360K 5.25" drive
6764 config_data = 0x00; // 0000 0000
6765 media_state = 0x25; // 0010 0101
6766 retval = 1;
6768 else if ( drive_type == 2 ) {
6769 // 1.2 MB 5.25" drive
6770 config_data = 0x00; // 0000 0000
6771 media_state = 0x25; // 0010 0101 // need double stepping??? (bit 5)
6772 retval = 1;
6774 else if ( drive_type == 3 ) {
6775 // 720K 3.5" drive
6776 config_data = 0x00; // 0000 0000 ???
6777 media_state = 0x17; // 0001 0111
6778 retval = 1;
6780 else if ( drive_type == 4 ) {
6781 // 1.44 MB 3.5" drive
6782 config_data = 0x00; // 0000 0000
6783 media_state = 0x17; // 0001 0111
6784 retval = 1;
6786 else if ( drive_type == 5 ) {
6787 // 2.88 MB 3.5" drive
6788 config_data = 0xCC; // 1100 1100
6789 media_state = 0xD7; // 1101 0111
6790 retval = 1;
6793 // Extended floppy size uses special cmos setting
6794 else if ( drive_type == 6 ) {
6795 // 160k 5.25" drive
6796 config_data = 0x00; // 0000 0000
6797 media_state = 0x27; // 0010 0111
6798 retval = 1;
6800 else if ( drive_type == 7 ) {
6801 // 180k 5.25" drive
6802 config_data = 0x00; // 0000 0000
6803 media_state = 0x27; // 0010 0111
6804 retval = 1;
6806 else if ( drive_type == 8 ) {
6807 // 320k 5.25" drive
6808 config_data = 0x00; // 0000 0000
6809 media_state = 0x27; // 0010 0111
6810 retval = 1;
6813 else {
6814 // not recognized
6815 config_data = 0x00; // 0000 0000
6816 media_state = 0x00; // 0000 0000
6817 retval = 0;
6820 if (drive == 0)
6821 media_state_offset = 0x90;
6822 else
6823 media_state_offset = 0x91;
6824 write_byte(0x0040, 0x008B, config_data);
6825 write_byte(0x0040, media_state_offset, media_state);
6827 return(retval);
6830 bx_bool
6831 floppy_drive_recal(drive)
6832 Bit16u drive;
6834 Bit8u val8;
6835 Bit16u curr_cyl_offset;
6837 floppy_prepare_controller(drive);
6839 // send Recalibrate command (2 bytes) to controller
6840 outb(0x03f5, 0x07); // 07: Recalibrate
6841 outb(0x03f5, drive); // 0=drive0, 1=drive1
6843 // turn on interrupts
6844 ASM_START
6846 ASM_END
6848 // wait on 40:3e bit 7 to become 1
6849 do {
6850 val8 = (read_byte(0x0040, 0x003e) & 0x80);
6851 } while ( val8 == 0 );
6853 val8 = 0; // separate asm from while() loop
6854 // turn off interrupts
6855 ASM_START
6857 ASM_END
6859 // set 40:3e bit 7 to 0, and calibrated bit
6860 val8 = read_byte(0x0040, 0x003e);
6861 val8 &= 0x7f;
6862 if (drive) {
6863 val8 |= 0x02; // Drive 1 calibrated
6864 curr_cyl_offset = 0x0095;
6865 } else {
6866 val8 |= 0x01; // Drive 0 calibrated
6867 curr_cyl_offset = 0x0094;
6869 write_byte(0x0040, 0x003e, val8);
6870 write_byte(0x0040, curr_cyl_offset, 0); // current cylinder is 0
6872 return(1);
6877 bx_bool
6878 floppy_drive_exists(drive)
6879 Bit16u drive;
6881 Bit8u drive_type;
6883 // check CMOS to see if drive exists
6884 drive_type = inb_cmos(0x10);
6885 if (drive == 0)
6886 drive_type >>= 4;
6887 else
6888 drive_type &= 0x0f;
6889 if ( drive_type == 0 )
6890 return(0);
6891 else
6892 return(1);
6895 void
6896 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
6897 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
6899 Bit8u drive, num_sectors, track, sector, head, status;
6900 Bit16u base_address, base_count, base_es;
6901 Bit8u page, mode_register, val8, dor;
6902 Bit8u return_status[7];
6903 Bit8u drive_type, num_floppies, ah;
6904 Bit16u es, last_addr;
6906 BX_DEBUG_INT13_FL("int13_diskette: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
6908 ah = GET_AH();
6910 switch ( ah ) {
6911 case 0x00: // diskette controller reset
6912 BX_DEBUG_INT13_FL("floppy f00\n");
6913 drive = GET_ELDL();
6914 if (drive > 1) {
6915 SET_AH(1); // invalid param
6916 set_diskette_ret_status(1);
6917 SET_CF();
6918 return;
6920 drive_type = inb_cmos(0x10);
6922 if (drive == 0)
6923 drive_type >>= 4;
6924 else
6925 drive_type &= 0x0f;
6926 if (drive_type == 0) {
6927 SET_AH(0x80); // drive not responding
6928 set_diskette_ret_status(0x80);
6929 SET_CF();
6930 return;
6932 SET_AH(0);
6933 set_diskette_ret_status(0);
6934 CLEAR_CF(); // successful
6935 set_diskette_current_cyl(drive, 0); // current cylinder
6936 return;
6938 case 0x01: // Read Diskette Status
6939 CLEAR_CF();
6940 val8 = read_byte(0x0000, 0x0441);
6941 SET_AH(val8);
6942 if (val8) {
6943 SET_CF();
6945 return;
6947 case 0x02: // Read Diskette Sectors
6948 case 0x03: // Write Diskette Sectors
6949 case 0x04: // Verify Diskette Sectors
6950 num_sectors = GET_AL();
6951 track = GET_CH();
6952 sector = GET_CL();
6953 head = GET_DH();
6954 drive = GET_ELDL();
6956 if ((drive > 1) || (head > 1) || (sector == 0) ||
6957 (num_sectors == 0) || (num_sectors > 72)) {
6958 BX_INFO("int13_diskette: read/write/verify: parameter out of range\n");
6959 SET_AH(1);
6960 set_diskette_ret_status(1);
6961 SET_AL(0); // no sectors read
6962 SET_CF(); // error occurred
6963 return;
6966 // see if drive exists
6967 if (floppy_drive_exists(drive) == 0) {
6968 SET_AH(0x80); // not responding
6969 set_diskette_ret_status(0x80);
6970 SET_AL(0); // no sectors read
6971 SET_CF(); // error occurred
6972 return;
6975 // see if media in drive, and type is known
6976 if (floppy_media_known(drive) == 0) {
6977 if (floppy_media_sense(drive) == 0) {
6978 SET_AH(0x0C); // Media type not found
6979 set_diskette_ret_status(0x0C);
6980 SET_AL(0); // no sectors read
6981 SET_CF(); // error occurred
6982 return;
6986 if (ah == 0x02) {
6987 // Read Diskette Sectors
6989 //-----------------------------------
6990 // set up DMA controller for transfer
6991 //-----------------------------------
6993 // es:bx = pointer to where to place information from diskette
6994 // port 04: DMA-1 base and current address, channel 2
6995 // port 05: DMA-1 base and current count, channel 2
6996 page = (ES >> 12); // upper 4 bits
6997 base_es = (ES << 4); // lower 16bits contributed by ES
6998 base_address = base_es + BX; // lower 16 bits of address
6999 // contributed by ES:BX
7000 if ( base_address < base_es ) {
7001 // in case of carry, adjust page by 1
7002 page++;
7004 base_count = (num_sectors * 512) - 1;
7006 // check for 64K boundary overrun
7007 last_addr = base_address + base_count;
7008 if (last_addr < base_address) {
7009 SET_AH(0x09);
7010 set_diskette_ret_status(0x09);
7011 SET_AL(0); // no sectors read
7012 SET_CF(); // error occurred
7013 return;
7016 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
7017 outb(0x000a, 0x06);
7019 BX_DEBUG_INT13_FL("clear flip-flop\n");
7020 outb(0x000c, 0x00); // clear flip-flop
7021 outb(0x0004, base_address);
7022 outb(0x0004, base_address>>8);
7023 BX_DEBUG_INT13_FL("clear flip-flop\n");
7024 outb(0x000c, 0x00); // clear flip-flop
7025 outb(0x0005, base_count);
7026 outb(0x0005, base_count>>8);
7028 // port 0b: DMA-1 Mode Register
7029 mode_register = 0x46; // single mode, increment, autoinit disable,
7030 // transfer type=write, channel 2
7031 BX_DEBUG_INT13_FL("setting mode register\n");
7032 outb(0x000b, mode_register);
7034 BX_DEBUG_INT13_FL("setting page register\n");
7035 // port 81: DMA-1 Page Register, channel 2
7036 outb(0x0081, page);
7038 BX_DEBUG_INT13_FL("unmask chan 2\n");
7039 outb(0x000a, 0x02); // unmask channel 2
7041 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
7042 outb(0x000a, 0x02);
7044 //--------------------------------------
7045 // set up floppy controller for transfer
7046 //--------------------------------------
7047 floppy_prepare_controller(drive);
7049 // send read-normal-data command (9 bytes) to controller
7050 outb(0x03f5, 0xe6); // e6: read normal data
7051 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7052 outb(0x03f5, track);
7053 outb(0x03f5, head);
7054 outb(0x03f5, sector);
7055 outb(0x03f5, 2); // 512 byte sector size
7056 outb(0x03f5, sector + num_sectors - 1); // last sector to read on track
7057 outb(0x03f5, 0); // Gap length
7058 outb(0x03f5, 0xff); // Gap length
7060 // turn on interrupts
7061 ASM_START
7063 ASM_END
7065 // wait on 40:3e bit 7 to become 1
7066 do {
7067 val8 = read_byte(0x0040, 0x0040);
7068 if (val8 == 0) {
7069 floppy_reset_controller();
7070 SET_AH(0x80); // drive not ready (timeout)
7071 set_diskette_ret_status(0x80);
7072 SET_AL(0); // no sectors read
7073 SET_CF(); // error occurred
7074 return;
7076 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7077 } while ( val8 == 0 );
7079 val8 = 0; // separate asm from while() loop
7080 // turn off interrupts
7081 ASM_START
7083 ASM_END
7085 // set 40:3e bit 7 to 0
7086 val8 = read_byte(0x0040, 0x003e);
7087 val8 &= 0x7f;
7088 write_byte(0x0040, 0x003e, val8);
7090 // check port 3f4 for accessibility to status bytes
7091 val8 = inb(0x3f4);
7092 if ( (val8 & 0xc0) != 0xc0 )
7093 BX_PANIC("int13_diskette: ctrl not ready\n");
7095 // read 7 return status bytes from controller
7096 // using loop index broken, have to unroll...
7097 return_status[0] = inb(0x3f5);
7098 return_status[1] = inb(0x3f5);
7099 return_status[2] = inb(0x3f5);
7100 return_status[3] = inb(0x3f5);
7101 return_status[4] = inb(0x3f5);
7102 return_status[5] = inb(0x3f5);
7103 return_status[6] = inb(0x3f5);
7104 // record in BIOS Data Area
7105 write_byte(0x0040, 0x0042, return_status[0]);
7106 write_byte(0x0040, 0x0043, return_status[1]);
7107 write_byte(0x0040, 0x0044, return_status[2]);
7108 write_byte(0x0040, 0x0045, return_status[3]);
7109 write_byte(0x0040, 0x0046, return_status[4]);
7110 write_byte(0x0040, 0x0047, return_status[5]);
7111 write_byte(0x0040, 0x0048, return_status[6]);
7113 if ( (return_status[0] & 0xc0) != 0 ) {
7114 SET_AH(0x20);
7115 set_diskette_ret_status(0x20);
7116 SET_AL(0); // no sectors read
7117 SET_CF(); // error occurred
7118 return;
7121 // ??? should track be new val from return_status[3] ?
7122 set_diskette_current_cyl(drive, track);
7123 // AL = number of sectors read (same value as passed)
7124 SET_AH(0x00); // success
7125 CLEAR_CF(); // success
7126 return;
7127 } else if (ah == 0x03) {
7128 // Write Diskette Sectors
7130 //-----------------------------------
7131 // set up DMA controller for transfer
7132 //-----------------------------------
7134 // es:bx = pointer to where to place information from diskette
7135 // port 04: DMA-1 base and current address, channel 2
7136 // port 05: DMA-1 base and current count, channel 2
7137 page = (ES >> 12); // upper 4 bits
7138 base_es = (ES << 4); // lower 16bits contributed by ES
7139 base_address = base_es + BX; // lower 16 bits of address
7140 // contributed by ES:BX
7141 if ( base_address < base_es ) {
7142 // in case of carry, adjust page by 1
7143 page++;
7145 base_count = (num_sectors * 512) - 1;
7147 // check for 64K boundary overrun
7148 last_addr = base_address + base_count;
7149 if (last_addr < base_address) {
7150 SET_AH(0x09);
7151 set_diskette_ret_status(0x09);
7152 SET_AL(0); // no sectors read
7153 SET_CF(); // error occurred
7154 return;
7157 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
7158 outb(0x000a, 0x06);
7160 outb(0x000c, 0x00); // clear flip-flop
7161 outb(0x0004, base_address);
7162 outb(0x0004, base_address>>8);
7163 outb(0x000c, 0x00); // clear flip-flop
7164 outb(0x0005, base_count);
7165 outb(0x0005, base_count>>8);
7167 // port 0b: DMA-1 Mode Register
7168 mode_register = 0x4a; // single mode, increment, autoinit disable,
7169 // transfer type=read, channel 2
7170 outb(0x000b, mode_register);
7172 // port 81: DMA-1 Page Register, channel 2
7173 outb(0x0081, page);
7175 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
7176 outb(0x000a, 0x02);
7178 //--------------------------------------
7179 // set up floppy controller for transfer
7180 //--------------------------------------
7181 floppy_prepare_controller(drive);
7183 // send write-normal-data command (9 bytes) to controller
7184 outb(0x03f5, 0xc5); // c5: write normal data
7185 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7186 outb(0x03f5, track);
7187 outb(0x03f5, head);
7188 outb(0x03f5, sector);
7189 outb(0x03f5, 2); // 512 byte sector size
7190 outb(0x03f5, sector + num_sectors - 1); // last sector to write on track
7191 outb(0x03f5, 0); // Gap length
7192 outb(0x03f5, 0xff); // Gap length
7194 // turn on interrupts
7195 ASM_START
7197 ASM_END
7199 // wait on 40:3e bit 7 to become 1
7200 do {
7201 val8 = read_byte(0x0040, 0x0040);
7202 if (val8 == 0) {
7203 floppy_reset_controller();
7204 SET_AH(0x80); // drive not ready (timeout)
7205 set_diskette_ret_status(0x80);
7206 SET_AL(0); // no sectors written
7207 SET_CF(); // error occurred
7208 return;
7210 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7211 } while ( val8 == 0 );
7213 val8 = 0; // separate asm from while() loop
7214 // turn off interrupts
7215 ASM_START
7217 ASM_END
7219 // set 40:3e bit 7 to 0
7220 val8 = read_byte(0x0040, 0x003e);
7221 val8 &= 0x7f;
7222 write_byte(0x0040, 0x003e, val8);
7224 // check port 3f4 for accessibility to status bytes
7225 val8 = inb(0x3f4);
7226 if ( (val8 & 0xc0) != 0xc0 )
7227 BX_PANIC("int13_diskette: ctrl not ready\n");
7229 // read 7 return status bytes from controller
7230 // using loop index broken, have to unroll...
7231 return_status[0] = inb(0x3f5);
7232 return_status[1] = inb(0x3f5);
7233 return_status[2] = inb(0x3f5);
7234 return_status[3] = inb(0x3f5);
7235 return_status[4] = inb(0x3f5);
7236 return_status[5] = inb(0x3f5);
7237 return_status[6] = inb(0x3f5);
7238 // record in BIOS Data Area
7239 write_byte(0x0040, 0x0042, return_status[0]);
7240 write_byte(0x0040, 0x0043, return_status[1]);
7241 write_byte(0x0040, 0x0044, return_status[2]);
7242 write_byte(0x0040, 0x0045, return_status[3]);
7243 write_byte(0x0040, 0x0046, return_status[4]);
7244 write_byte(0x0040, 0x0047, return_status[5]);
7245 write_byte(0x0040, 0x0048, return_status[6]);
7247 if ( (return_status[0] & 0xc0) != 0 ) {
7248 if ( (return_status[1] & 0x02) != 0 ) {
7249 // diskette not writable.
7250 // AH=status code=0x03 (tried to write on write-protected disk)
7251 // AL=number of sectors written=0
7252 AX = 0x0300;
7253 SET_CF();
7254 return;
7255 } else {
7256 BX_PANIC("int13_diskette_function: read error\n");
7260 // ??? should track be new val from return_status[3] ?
7261 set_diskette_current_cyl(drive, track);
7262 // AL = number of sectors read (same value as passed)
7263 SET_AH(0x00); // success
7264 CLEAR_CF(); // success
7265 return;
7266 } else { // if (ah == 0x04)
7267 // Verify Diskette Sectors
7269 // ??? should track be new val from return_status[3] ?
7270 set_diskette_current_cyl(drive, track);
7271 // AL = number of sectors verified (same value as passed)
7272 CLEAR_CF(); // success
7273 SET_AH(0x00); // success
7274 return;
7276 break;
7278 case 0x05: // format diskette track
7279 BX_DEBUG_INT13_FL("floppy f05\n");
7281 num_sectors = GET_AL();
7282 track = GET_CH();
7283 head = GET_DH();
7284 drive = GET_ELDL();
7286 if ((drive > 1) || (head > 1) || (track > 79) ||
7287 (num_sectors == 0) || (num_sectors > 18)) {
7288 SET_AH(1);
7289 set_diskette_ret_status(1);
7290 SET_CF(); // error occurred
7293 // see if drive exists
7294 if (floppy_drive_exists(drive) == 0) {
7295 SET_AH(0x80); // drive not responding
7296 set_diskette_ret_status(0x80);
7297 SET_CF(); // error occurred
7298 return;
7301 // see if media in drive, and type is known
7302 if (floppy_media_known(drive) == 0) {
7303 if (floppy_media_sense(drive) == 0) {
7304 SET_AH(0x0C); // Media type not found
7305 set_diskette_ret_status(0x0C);
7306 SET_AL(0); // no sectors read
7307 SET_CF(); // error occurred
7308 return;
7312 // set up DMA controller for transfer
7313 page = (ES >> 12); // upper 4 bits
7314 base_es = (ES << 4); // lower 16bits contributed by ES
7315 base_address = base_es + BX; // lower 16 bits of address
7316 // contributed by ES:BX
7317 if ( base_address < base_es ) {
7318 // in case of carry, adjust page by 1
7319 page++;
7321 base_count = (num_sectors * 4) - 1;
7323 // check for 64K boundary overrun
7324 last_addr = base_address + base_count;
7325 if (last_addr < base_address) {
7326 SET_AH(0x09);
7327 set_diskette_ret_status(0x09);
7328 SET_AL(0); // no sectors read
7329 SET_CF(); // error occurred
7330 return;
7333 outb(0x000a, 0x06);
7334 outb(0x000c, 0x00); // clear flip-flop
7335 outb(0x0004, base_address);
7336 outb(0x0004, base_address>>8);
7337 outb(0x000c, 0x00); // clear flip-flop
7338 outb(0x0005, base_count);
7339 outb(0x0005, base_count>>8);
7340 mode_register = 0x4a; // single mode, increment, autoinit disable,
7341 // transfer type=read, channel 2
7342 outb(0x000b, mode_register);
7343 // port 81: DMA-1 Page Register, channel 2
7344 outb(0x0081, page);
7345 outb(0x000a, 0x02);
7347 // set up floppy controller for transfer
7348 floppy_prepare_controller(drive);
7350 // send format-track command (6 bytes) to controller
7351 outb(0x03f5, 0x4d); // 4d: format track
7352 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7353 outb(0x03f5, 2); // 512 byte sector size
7354 outb(0x03f5, num_sectors); // number of sectors per track
7355 outb(0x03f5, 0); // Gap length
7356 outb(0x03f5, 0xf6); // Fill byte
7357 // turn on interrupts
7358 ASM_START
7360 ASM_END
7362 // wait on 40:3e bit 7 to become 1
7363 do {
7364 val8 = read_byte(0x0040, 0x0040);
7365 if (val8 == 0) {
7366 floppy_reset_controller();
7367 SET_AH(0x80); // drive not ready (timeout)
7368 set_diskette_ret_status(0x80);
7369 SET_CF(); // error occurred
7370 return;
7372 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7373 } while ( val8 == 0 );
7375 val8 = 0; // separate asm from while() loop
7376 // turn off interrupts
7377 ASM_START
7379 ASM_END
7380 // set 40:3e bit 7 to 0
7381 val8 = read_byte(0x0040, 0x003e);
7382 val8 &= 0x7f;
7383 write_byte(0x0040, 0x003e, val8);
7384 // check port 3f4 for accessibility to status bytes
7385 val8 = inb(0x3f4);
7386 if ( (val8 & 0xc0) != 0xc0 )
7387 BX_PANIC("int13_diskette: ctrl not ready\n");
7389 // read 7 return status bytes from controller
7390 // using loop index broken, have to unroll...
7391 return_status[0] = inb(0x3f5);
7392 return_status[1] = inb(0x3f5);
7393 return_status[2] = inb(0x3f5);
7394 return_status[3] = inb(0x3f5);
7395 return_status[4] = inb(0x3f5);
7396 return_status[5] = inb(0x3f5);
7397 return_status[6] = inb(0x3f5);
7398 // record in BIOS Data Area
7399 write_byte(0x0040, 0x0042, return_status[0]);
7400 write_byte(0x0040, 0x0043, return_status[1]);
7401 write_byte(0x0040, 0x0044, return_status[2]);
7402 write_byte(0x0040, 0x0045, return_status[3]);
7403 write_byte(0x0040, 0x0046, return_status[4]);
7404 write_byte(0x0040, 0x0047, return_status[5]);
7405 write_byte(0x0040, 0x0048, return_status[6]);
7407 if ( (return_status[0] & 0xc0) != 0 ) {
7408 if ( (return_status[1] & 0x02) != 0 ) {
7409 // diskette not writable.
7410 // AH=status code=0x03 (tried to write on write-protected disk)
7411 // AL=number of sectors written=0
7412 AX = 0x0300;
7413 SET_CF();
7414 return;
7415 } else {
7416 BX_PANIC("int13_diskette_function: write error\n");
7420 SET_AH(0);
7421 set_diskette_ret_status(0);
7422 set_diskette_current_cyl(drive, 0);
7423 CLEAR_CF(); // successful
7424 return;
7427 case 0x08: // read diskette drive parameters
7428 BX_DEBUG_INT13_FL("floppy f08\n");
7429 drive = GET_ELDL();
7431 if (drive > 1) {
7432 AX = 0;
7433 BX = 0;
7434 CX = 0;
7435 DX = 0;
7436 ES = 0;
7437 DI = 0;
7438 SET_DL(num_floppies);
7439 SET_CF();
7440 return;
7443 drive_type = inb_cmos(0x10);
7444 num_floppies = 0;
7445 if (drive_type & 0xf0)
7446 num_floppies++;
7447 if (drive_type & 0x0f)
7448 num_floppies++;
7450 if (drive == 0)
7451 drive_type >>= 4;
7452 else
7453 drive_type &= 0x0f;
7455 SET_BH(0);
7456 SET_BL(drive_type);
7457 SET_AH(0);
7458 SET_AL(0);
7459 SET_DL(num_floppies);
7461 switch (drive_type) {
7462 case 0: // none
7463 CX = 0;
7464 SET_DH(0); // max head #
7465 break;
7467 case 1: // 360KB, 5.25"
7468 CX = 0x2709; // 40 tracks, 9 sectors
7469 SET_DH(1); // max head #
7470 break;
7472 case 2: // 1.2MB, 5.25"
7473 CX = 0x4f0f; // 80 tracks, 15 sectors
7474 SET_DH(1); // max head #
7475 break;
7477 case 3: // 720KB, 3.5"
7478 CX = 0x4f09; // 80 tracks, 9 sectors
7479 SET_DH(1); // max head #
7480 break;
7482 case 4: // 1.44MB, 3.5"
7483 CX = 0x4f12; // 80 tracks, 18 sectors
7484 SET_DH(1); // max head #
7485 break;
7487 case 5: // 2.88MB, 3.5"
7488 CX = 0x4f24; // 80 tracks, 36 sectors
7489 SET_DH(1); // max head #
7490 break;
7492 case 6: // 160k, 5.25"
7493 CX = 0x2708; // 40 tracks, 8 sectors
7494 SET_DH(0); // max head #
7495 break;
7497 case 7: // 180k, 5.25"
7498 CX = 0x2709; // 40 tracks, 9 sectors
7499 SET_DH(0); // max head #
7500 break;
7502 case 8: // 320k, 5.25"
7503 CX = 0x2708; // 40 tracks, 8 sectors
7504 SET_DH(1); // max head #
7505 break;
7507 default: // ?
7508 BX_PANIC("floppy: int13: bad floppy type\n");
7511 /* set es & di to point to 11 byte diskette param table in ROM */
7512 ASM_START
7513 push bp
7514 mov bp, sp
7515 mov ax, #diskette_param_table2
7516 mov _int13_diskette_function.DI+2[bp], ax
7517 mov _int13_diskette_function.ES+2[bp], cs
7518 pop bp
7519 ASM_END
7520 CLEAR_CF(); // success
7521 /* disk status not changed upon success */
7522 return;
7525 case 0x15: // read diskette drive type
7526 BX_DEBUG_INT13_FL("floppy f15\n");
7527 drive = GET_ELDL();
7528 if (drive > 1) {
7529 SET_AH(0); // only 2 drives supported
7530 // set_diskette_ret_status here ???
7531 SET_CF();
7532 return;
7534 drive_type = inb_cmos(0x10);
7536 if (drive == 0)
7537 drive_type >>= 4;
7538 else
7539 drive_type &= 0x0f;
7540 CLEAR_CF(); // successful, not present
7541 if (drive_type==0) {
7542 SET_AH(0); // drive not present
7544 else {
7545 SET_AH(1); // drive present, does not support change line
7548 return;
7550 case 0x16: // get diskette change line status
7551 BX_DEBUG_INT13_FL("floppy f16\n");
7552 drive = GET_ELDL();
7553 if (drive > 1) {
7554 SET_AH(0x01); // invalid drive
7555 set_diskette_ret_status(0x01);
7556 SET_CF();
7557 return;
7560 SET_AH(0x06); // change line not supported
7561 set_diskette_ret_status(0x06);
7562 SET_CF();
7563 return;
7565 case 0x17: // set diskette type for format(old)
7566 BX_DEBUG_INT13_FL("floppy f17\n");
7567 /* not used for 1.44M floppies */
7568 SET_AH(0x01); // not supported
7569 set_diskette_ret_status(1); /* not supported */
7570 SET_CF();
7571 return;
7573 case 0x18: // set diskette type for format(new)
7574 BX_DEBUG_INT13_FL("floppy f18\n");
7575 SET_AH(0x01); // do later
7576 set_diskette_ret_status(1);
7577 SET_CF();
7578 return;
7580 default:
7581 BX_INFO("int13_diskette: unsupported AH=%02x\n", GET_AH());
7583 // if ( (ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e) ) {
7584 SET_AH(0x01); // ???
7585 set_diskette_ret_status(1);
7586 SET_CF();
7587 return;
7588 // }
7591 #else // #if BX_SUPPORT_FLOPPY
7592 void
7593 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
7594 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
7596 Bit8u val8;
7598 switch ( GET_AH() ) {
7600 case 0x01: // Read Diskette Status
7601 CLEAR_CF();
7602 val8 = read_byte(0x0000, 0x0441);
7603 SET_AH(val8);
7604 if (val8) {
7605 SET_CF();
7607 return;
7609 default:
7610 SET_CF();
7611 write_byte(0x0000, 0x0441, 0x01);
7612 SET_AH(0x01);
7615 #endif // #if BX_SUPPORT_FLOPPY
7617 void
7618 set_diskette_ret_status(value)
7619 Bit8u value;
7621 write_byte(0x0040, 0x0041, value);
7624 void
7625 set_diskette_current_cyl(drive, cyl)
7626 Bit8u drive;
7627 Bit8u cyl;
7629 if (drive > 1)
7630 BX_PANIC("set_diskette_current_cyl(): drive > 1\n");
7631 write_byte(0x0040, 0x0094+drive, cyl);
7634 void
7635 determine_floppy_media(drive)
7636 Bit16u drive;
7638 #if 0
7639 Bit8u val8, DOR, ctrl_info;
7641 ctrl_info = read_byte(0x0040, 0x008F);
7642 if (drive==1)
7643 ctrl_info >>= 4;
7644 else
7645 ctrl_info &= 0x0f;
7647 #if 0
7648 if (drive == 0) {
7649 DOR = 0x1c; // DOR: drive0 motor on, DMA&int enabled, normal op, drive select 0
7651 else {
7652 DOR = 0x2d; // DOR: drive1 motor on, DMA&int enabled, normal op, drive select 1
7654 #endif
7656 if ( (ctrl_info & 0x04) != 0x04 ) {
7657 // Drive not determined means no drive exists, done.
7658 return;
7661 #if 0
7662 // check Main Status Register for readiness
7663 val8 = inb(0x03f4) & 0x80; // Main Status Register
7664 if (val8 != 0x80)
7665 BX_PANIC("d_f_m: MRQ bit not set\n");
7667 // change line
7669 // existing BDA values
7671 // turn on drive motor
7672 outb(0x03f2, DOR); // Digital Output Register
7674 #endif
7675 BX_PANIC("d_f_m: OK so far\n");
7676 #endif
7679 void
7680 int17_function(regs, ds, iret_addr)
7681 pusha_regs_t regs; // regs pushed from PUSHA instruction
7682 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7683 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7685 Bit16u addr,timeout;
7686 Bit8u val8;
7688 ASM_START
7690 ASM_END
7692 addr = read_word(0x0040, (regs.u.r16.dx << 1) + 8);
7693 if ((regs.u.r8.ah < 3) && (regs.u.r16.dx < 3) && (addr > 0)) {
7694 timeout = read_byte(0x0040, 0x0078 + regs.u.r16.dx) << 8;
7695 if (regs.u.r8.ah == 0) {
7696 outb(addr, regs.u.r8.al);
7697 val8 = inb(addr+2);
7698 outb(addr+2, val8 | 0x01); // send strobe
7699 ASM_START
7701 ASM_END
7702 outb(addr+2, val8 & ~0x01);
7703 while (((inb(addr+1) & 0x40) == 0x40) && (timeout)) {
7704 timeout--;
7707 if (regs.u.r8.ah == 1) {
7708 val8 = inb(addr+2);
7709 outb(addr+2, val8 & ~0x04); // send init
7710 ASM_START
7712 ASM_END
7713 outb(addr+2, val8 | 0x04);
7715 val8 = inb(addr+1);
7716 regs.u.r8.ah = (val8 ^ 0x48);
7717 if (!timeout) regs.u.r8.ah |= 0x01;
7718 ClearCF(iret_addr.flags);
7719 } else {
7720 SetCF(iret_addr.flags); // Unsupported
7724 void
7725 int19_function(seq_nr)
7726 Bit16u seq_nr;
7728 Bit16u ebda_seg=read_word(0x0040,0x000E);
7729 Bit16u bootdev;
7730 Bit8u bootdrv;
7731 Bit8u bootchk;
7732 Bit16u bootseg;
7733 Bit16u bootip;
7734 Bit16u status;
7736 ipl_entry_t e;
7738 // if BX_ELTORITO_BOOT is not defined, old behavior
7739 // check bit 5 in CMOS reg 0x2d. load either 0x00 or 0x80 into DL
7740 // in preparation for the intial INT 13h (0=floppy A:, 0x80=C:)
7741 // 0: system boot sequence, first drive C: then A:
7742 // 1: system boot sequence, first drive A: then C:
7743 // else BX_ELTORITO_BOOT is defined
7744 // CMOS regs 0x3D and 0x38 contain the boot sequence:
7745 // CMOS reg 0x3D & 0x0f : 1st boot device
7746 // CMOS reg 0x3D & 0xf0 : 2nd boot device
7747 // CMOS reg 0x38 & 0xf0 : 3rd boot device
7748 // boot device codes:
7749 // 0x00 : not defined
7750 // 0x01 : first floppy
7751 // 0x02 : first harddrive
7752 // 0x03 : first cdrom
7753 // 0x04 - 0x0f : PnP expansion ROMs (e.g. Etherboot)
7754 // else : boot failure
7756 // Get the boot sequence
7757 #if BX_ELTORITO_BOOT
7758 bootdev = inb_cmos(0x3d);
7759 bootdev |= ((inb_cmos(0x38) & 0xf0) << 4);
7760 bootdev >>= 4 * seq_nr;
7761 bootdev &= 0xf;
7762 if (bootdev == 0) BX_PANIC("No bootable device.\n");
7764 /* Translate from CMOS runes to an IPL table offset by subtracting 1 */
7765 bootdev -= 1;
7766 #else
7767 if (seq_nr ==2) BX_PANIC("No more boot devices.");
7768 if (!!(inb_cmos(0x2d) & 0x20) ^ (seq_nr == 1))
7769 /* Boot from floppy if the bit is set or it's the second boot */
7770 bootdev = 0x00;
7771 else
7772 bootdev = 0x01;
7773 #endif
7775 /* Read the boot device from the IPL table */
7776 if (get_boot_vector(bootdev, &e) == 0) {
7777 BX_INFO("Invalid boot device (0x%x)\n", bootdev);
7778 return;
7781 /* Do the loading, and set up vector as a far pointer to the boot
7782 * address, and bootdrv as the boot drive */
7783 print_boot_device(e.type);
7785 switch(e.type) {
7786 case 0x01: /* FDD */
7787 case 0x02: /* HDD */
7789 bootdrv = (e.type == 0x02) ? 0x80 : 0x00;
7790 bootseg = 0x07c0;
7791 status = 0;
7793 ASM_START
7794 push bp
7795 mov bp, sp
7796 push ax
7797 push bx
7798 push cx
7799 push dx
7801 mov dl, _int19_function.bootdrv + 2[bp]
7802 mov ax, _int19_function.bootseg + 2[bp]
7803 mov es, ax ;; segment
7804 xor bx, bx ;; offset
7805 mov ah, #0x02 ;; function 2, read diskette sector
7806 mov al, #0x01 ;; read 1 sector
7807 mov ch, #0x00 ;; track 0
7808 mov cl, #0x01 ;; sector 1
7809 mov dh, #0x00 ;; head 0
7810 int #0x13 ;; read sector
7811 jnc int19_load_done
7812 mov ax, #0x0001
7813 mov _int19_function.status + 2[bp], ax
7815 int19_load_done:
7816 pop dx
7817 pop cx
7818 pop bx
7819 pop ax
7820 pop bp
7821 ASM_END
7823 if (status != 0) {
7824 print_boot_failure(e.type, 1);
7825 return;
7828 /* Always check the signature on a HDD boot sector; on FDD, only do
7829 * the check if the CMOS doesn't tell us to skip it */
7830 if ((e.type != 0x01) || !((inb_cmos(0x38) & 0x01))) {
7831 if (read_word(bootseg,0x1fe) != 0xaa55) {
7832 print_boot_failure(e.type, 0);
7833 return;
7837 /* Canonicalize bootseg:bootip */
7838 bootip = (bootseg & 0x0fff) << 4;
7839 bootseg &= 0xf000;
7840 break;
7842 #if BX_ELTORITO_BOOT
7843 case 0x03: /* CD-ROM */
7844 status = cdrom_boot();
7846 // If failure
7847 if ( (status & 0x00ff) !=0 ) {
7848 print_cdromboot_failure(status);
7849 print_boot_failure(e.type, 1);
7850 return;
7853 bootdrv = (Bit8u)(status>>8);
7854 bootseg = read_word(ebda_seg,&EbdaData->cdemu.load_segment);
7855 /* Canonicalize bootseg:bootip */
7856 bootip = (bootseg & 0x0fff) << 4;
7857 bootseg &= 0xf000;
7858 break;
7859 #endif
7861 case 0x80: /* Expansion ROM with a Bootstrap Entry Vector (a far pointer) */
7862 bootseg = e.vector >> 16;
7863 bootip = e.vector & 0xffff;
7864 break;
7866 default: return;
7869 /* Debugging info */
7870 BX_INFO("Booting from %x:%x\n", bootseg, bootip);
7872 /* Jump to the boot vector */
7873 ASM_START
7874 mov bp, sp
7875 ;; Build an iret stack frame that will take us to the boot vector.
7876 ;; iret pops ip, then cs, then flags, so push them in the opposite order.
7877 pushf
7878 mov ax, _int19_function.bootseg + 0[bp]
7879 push ax
7880 mov ax, _int19_function.bootip + 0[bp]
7881 push ax
7882 ;; Set the magic number in ax and the boot drive in dl.
7883 mov ax, #0xaa55
7884 mov dl, _int19_function.bootdrv + 0[bp]
7885 ;; Zero some of the other registers.
7886 xor bx, bx
7887 mov ds, bx
7888 mov es, bx
7889 mov bp, bx
7890 ;; Go!
7891 iret
7892 ASM_END
7895 void
7896 int1a_function(regs, ds, iret_addr)
7897 pusha_regs_t regs; // regs pushed from PUSHA instruction
7898 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7899 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7901 Bit8u val8;
7903 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);
7905 ASM_START
7907 ASM_END
7909 switch (regs.u.r8.ah) {
7910 case 0: // get current clock count
7911 ASM_START
7913 ASM_END
7914 regs.u.r16.cx = BiosData->ticks_high;
7915 regs.u.r16.dx = BiosData->ticks_low;
7916 regs.u.r8.al = BiosData->midnight_flag;
7917 BiosData->midnight_flag = 0; // reset flag
7918 ASM_START
7920 ASM_END
7921 // AH already 0
7922 ClearCF(iret_addr.flags); // OK
7923 break;
7925 case 1: // Set Current Clock Count
7926 ASM_START
7928 ASM_END
7929 BiosData->ticks_high = regs.u.r16.cx;
7930 BiosData->ticks_low = regs.u.r16.dx;
7931 BiosData->midnight_flag = 0; // reset flag
7932 ASM_START
7934 ASM_END
7935 regs.u.r8.ah = 0;
7936 ClearCF(iret_addr.flags); // OK
7937 break;
7940 case 2: // Read CMOS Time
7941 if (rtc_updating()) {
7942 SetCF(iret_addr.flags);
7943 break;
7946 regs.u.r8.dh = inb_cmos(0x00); // Seconds
7947 regs.u.r8.cl = inb_cmos(0x02); // Minutes
7948 regs.u.r8.ch = inb_cmos(0x04); // Hours
7949 regs.u.r8.dl = inb_cmos(0x0b) & 0x01; // Stat Reg B
7950 regs.u.r8.ah = 0;
7951 regs.u.r8.al = regs.u.r8.ch;
7952 ClearCF(iret_addr.flags); // OK
7953 break;
7955 case 3: // Set CMOS Time
7956 // Using a debugger, I notice the following masking/setting
7957 // of bits in Status Register B, by setting Reg B to
7958 // a few values and getting its value after INT 1A was called.
7960 // try#1 try#2 try#3
7961 // before 1111 1101 0111 1101 0000 0000
7962 // after 0110 0010 0110 0010 0000 0010
7964 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7965 // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
7966 if (rtc_updating()) {
7967 init_rtc();
7968 // fall through as if an update were not in progress
7970 outb_cmos(0x00, regs.u.r8.dh); // Seconds
7971 outb_cmos(0x02, regs.u.r8.cl); // Minutes
7972 outb_cmos(0x04, regs.u.r8.ch); // Hours
7973 // Set Daylight Savings time enabled bit to requested value
7974 val8 = (inb_cmos(0x0b) & 0x60) | 0x02 | (regs.u.r8.dl & 0x01);
7975 // (reg B already selected)
7976 outb_cmos(0x0b, val8);
7977 regs.u.r8.ah = 0;
7978 regs.u.r8.al = val8; // val last written to Reg B
7979 ClearCF(iret_addr.flags); // OK
7980 break;
7982 case 4: // Read CMOS Date
7983 regs.u.r8.ah = 0;
7984 if (rtc_updating()) {
7985 SetCF(iret_addr.flags);
7986 break;
7988 regs.u.r8.cl = inb_cmos(0x09); // Year
7989 regs.u.r8.dh = inb_cmos(0x08); // Month
7990 regs.u.r8.dl = inb_cmos(0x07); // Day of Month
7991 regs.u.r8.ch = inb_cmos(0x32); // Century
7992 regs.u.r8.al = regs.u.r8.ch;
7993 ClearCF(iret_addr.flags); // OK
7994 break;
7996 case 5: // Set CMOS Date
7997 // Using a debugger, I notice the following masking/setting
7998 // of bits in Status Register B, by setting Reg B to
7999 // a few values and getting its value after INT 1A was called.
8001 // try#1 try#2 try#3 try#4
8002 // before 1111 1101 0111 1101 0000 0010 0000 0000
8003 // after 0110 1101 0111 1101 0000 0010 0000 0000
8005 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8006 // My assumption: RegB = (RegB & 01111111b)
8007 if (rtc_updating()) {
8008 init_rtc();
8009 SetCF(iret_addr.flags);
8010 break;
8012 outb_cmos(0x09, regs.u.r8.cl); // Year
8013 outb_cmos(0x08, regs.u.r8.dh); // Month
8014 outb_cmos(0x07, regs.u.r8.dl); // Day of Month
8015 outb_cmos(0x32, regs.u.r8.ch); // Century
8016 val8 = inb_cmos(0x0b) & 0x7f; // clear halt-clock bit
8017 outb_cmos(0x0b, val8);
8018 regs.u.r8.ah = 0;
8019 regs.u.r8.al = val8; // AL = val last written to Reg B
8020 ClearCF(iret_addr.flags); // OK
8021 break;
8023 case 6: // Set Alarm Time in CMOS
8024 // Using a debugger, I notice the following masking/setting
8025 // of bits in Status Register B, by setting Reg B to
8026 // a few values and getting its value after INT 1A was called.
8028 // try#1 try#2 try#3
8029 // before 1101 1111 0101 1111 0000 0000
8030 // after 0110 1111 0111 1111 0010 0000
8032 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8033 // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
8034 val8 = inb_cmos(0x0b); // Get Status Reg B
8035 regs.u.r16.ax = 0;
8036 if (val8 & 0x20) {
8037 // Alarm interrupt enabled already
8038 SetCF(iret_addr.flags); // Error: alarm in use
8039 break;
8041 if (rtc_updating()) {
8042 init_rtc();
8043 // fall through as if an update were not in progress
8045 outb_cmos(0x01, regs.u.r8.dh); // Seconds alarm
8046 outb_cmos(0x03, regs.u.r8.cl); // Minutes alarm
8047 outb_cmos(0x05, regs.u.r8.ch); // Hours alarm
8048 outb(0xa1, inb(0xa1) & 0xfe); // enable IRQ 8
8049 // enable Status Reg B alarm bit, clear halt clock bit
8050 outb_cmos(0x0b, (val8 & 0x7f) | 0x20);
8051 ClearCF(iret_addr.flags); // OK
8052 break;
8054 case 7: // Turn off Alarm
8055 // Using a debugger, I notice the following masking/setting
8056 // of bits in Status Register B, by setting Reg B to
8057 // a few values and getting its value after INT 1A was called.
8059 // try#1 try#2 try#3 try#4
8060 // before 1111 1101 0111 1101 0010 0000 0010 0010
8061 // after 0100 0101 0101 0101 0000 0000 0000 0010
8063 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8064 // My assumption: RegB = (RegB & 01010111b)
8065 val8 = inb_cmos(0x0b); // Get Status Reg B
8066 // clear clock-halt bit, disable alarm bit
8067 outb_cmos(0x0b, val8 & 0x57); // disable alarm bit
8068 regs.u.r8.ah = 0;
8069 regs.u.r8.al = val8; // val last written to Reg B
8070 ClearCF(iret_addr.flags); // OK
8071 break;
8072 #if BX_PCIBIOS
8073 case 0xb1:
8074 // real mode PCI BIOS functions now handled in assembler code
8075 // this C code handles the error code for information only
8076 if (regs.u.r8.bl == 0xff) {
8077 BX_INFO("PCI BIOS: PCI not present\n");
8078 } else if (regs.u.r8.bl == 0x81) {
8079 BX_INFO("unsupported PCI BIOS function 0x%02x\n", regs.u.r8.al);
8080 } else if (regs.u.r8.bl == 0x83) {
8081 BX_INFO("bad PCI vendor ID %04x\n", regs.u.r16.dx);
8082 } else if (regs.u.r8.bl == 0x86) {
8083 if (regs.u.r8.al == 0x02) {
8084 BX_INFO("PCI device %04x:%04x not found at index %d\n", regs.u.r16.dx, regs.u.r16.cx, regs.u.r16.si);
8085 } else {
8086 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);
8089 regs.u.r8.ah = regs.u.r8.bl;
8090 SetCF(iret_addr.flags);
8091 break;
8092 #endif
8094 default:
8095 SetCF(iret_addr.flags); // Unsupported
8099 void
8100 int70_function(regs, ds, iret_addr)
8101 pusha_regs_t regs; // regs pushed from PUSHA instruction
8102 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
8103 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
8105 // INT 70h: IRQ 8 - CMOS RTC interrupt from periodic or alarm modes
8106 Bit8u registerB = 0, registerC = 0;
8108 // Check which modes are enabled and have occurred.
8109 registerB = inb_cmos( 0xB );
8110 registerC = inb_cmos( 0xC );
8112 if( ( registerB & 0x60 ) != 0 ) {
8113 if( ( registerC & 0x20 ) != 0 ) {
8114 // Handle Alarm Interrupt.
8115 ASM_START
8117 int #0x4a
8119 ASM_END
8121 if( ( registerC & 0x40 ) != 0 ) {
8122 // Handle Periodic Interrupt.
8124 if( read_byte( 0x40, 0xA0 ) != 0 ) {
8125 // Wait Interval (Int 15, AH=83) active.
8126 Bit32u time, toggle;
8128 time = read_dword( 0x40, 0x9C ); // Time left in microseconds.
8129 if( time < 0x3D1 ) {
8130 // Done waiting.
8131 Bit16u segment, offset;
8133 segment = read_word( 0x40, 0x98 );
8134 offset = read_word( 0x40, 0x9A );
8135 write_byte( 0x40, 0xA0, 0 ); // Turn of status byte.
8136 outb_cmos( 0xB, registerB & 0x37 ); // Clear the Periodic Interrupt.
8137 write_byte(segment, offset, read_byte(segment, offset) | 0x80 ); // Write to specified flag byte.
8138 } else {
8139 // Continue waiting.
8140 time -= 0x3D1;
8141 write_dword( 0x40, 0x9C, time );
8147 ASM_START
8148 call eoi_both_pics
8149 ASM_END
8153 ASM_START
8154 ;------------------------------------------
8155 ;- INT74h : PS/2 mouse hardware interrupt -
8156 ;------------------------------------------
8157 int74_handler:
8159 pusha
8160 push ds ;; save DS
8161 push #0x00 ;; placeholder for status
8162 push #0x00 ;; placeholder for X
8163 push #0x00 ;; placeholder for Y
8164 push #0x00 ;; placeholder for Z
8165 push #0x00 ;; placeholder for make_far_call boolean
8166 call _int74_function
8167 pop cx ;; remove make_far_call from stack
8168 jcxz int74_done
8170 ;; make far call to EBDA:0022
8171 push #0x00
8172 pop ds
8173 push 0x040E ;; push 0000:040E (opcodes 0xff, 0x36, 0x0E, 0x04)
8174 pop ds
8175 //CALL_EP(0x0022) ;; call far routine (call_Ep DS:0022 :opcodes 0xff, 0x1e, 0x22, 0x00)
8176 call far ptr[0x22]
8177 int74_done:
8179 call eoi_both_pics
8180 add sp, #8 ;; pop status, x, y, z
8182 pop ds ;; restore DS
8183 popa
8184 iret
8187 ;; This will perform an IRET, but will retain value of current CF
8188 ;; by altering flags on stack. Better than RETF #02.
8189 iret_modify_cf:
8190 jc carry_set
8191 push bp
8192 mov bp, sp
8193 and BYTE [bp + 0x06], #0xfe
8194 pop bp
8195 iret
8196 carry_set:
8197 push bp
8198 mov bp, sp
8199 or BYTE [bp + 0x06], #0x01
8200 pop bp
8201 iret
8204 ;----------------------
8205 ;- INT13h (relocated) -
8206 ;----------------------
8208 ; int13_relocated is a little bit messed up since I played with it
8209 ; I have to rewrite it:
8210 ; - call a function that detect which function to call
8211 ; - make all called C function get the same parameters list
8213 int13_relocated:
8215 #if BX_ELTORITO_BOOT
8216 ;; check for an eltorito function
8217 cmp ah,#0x4a
8218 jb int13_not_eltorito
8219 cmp ah,#0x4d
8220 ja int13_not_eltorito
8222 pusha
8223 push es
8224 push ds
8225 push ss
8226 pop ds
8228 push #int13_out
8229 jmp _int13_eltorito ;; ELDX not used
8231 int13_not_eltorito:
8232 push ax
8233 push bx
8234 push cx
8235 push dx
8237 ;; check if emulation active
8238 call _cdemu_isactive
8239 cmp al,#0x00
8240 je int13_cdemu_inactive
8242 ;; check if access to the emulated drive
8243 call _cdemu_emulated_drive
8244 pop dx
8245 push dx
8246 cmp al,dl ;; int13 on emulated drive
8247 jne int13_nocdemu
8249 pop dx
8250 pop cx
8251 pop bx
8252 pop ax
8254 pusha
8255 push es
8256 push ds
8257 push ss
8258 pop ds
8260 push #int13_out
8261 jmp _int13_cdemu ;; ELDX not used
8263 int13_nocdemu:
8264 and dl,#0xE0 ;; mask to get device class, including cdroms
8265 cmp al,dl ;; al is 0x00 or 0x80
8266 jne int13_cdemu_inactive ;; inactive for device class
8268 pop dx
8269 pop cx
8270 pop bx
8271 pop ax
8273 push ax
8274 push cx
8275 push dx
8276 push bx
8278 dec dl ;; real drive is dl - 1
8279 jmp int13_legacy
8281 int13_cdemu_inactive:
8282 pop dx
8283 pop cx
8284 pop bx
8285 pop ax
8287 #endif // BX_ELTORITO_BOOT
8289 int13_noeltorito:
8291 push ax
8292 push cx
8293 push dx
8294 push bx
8296 int13_legacy:
8298 push dx ;; push eltorito value of dx instead of sp
8300 push bp
8301 push si
8302 push di
8304 push es
8305 push ds
8306 push ss
8307 pop ds
8309 ;; now the 16-bit registers can be restored with:
8310 ;; pop ds; pop es; popa; iret
8311 ;; arguments passed to functions should be
8312 ;; DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS
8314 test dl, #0x80
8315 jnz int13_notfloppy
8317 push #int13_out
8318 jmp _int13_diskette_function
8320 int13_notfloppy:
8322 #if BX_USE_ATADRV
8324 cmp dl, #0xE0
8325 jb int13_notcdrom
8327 // ebx is modified: BSD 5.2.1 boot loader problem
8328 // someone should figure out which 32 bit register that actually are used
8330 shr ebx, #16
8331 push bx
8333 call _int13_cdrom
8335 pop bx
8336 shl ebx, #16
8338 jmp int13_out
8340 int13_notcdrom:
8342 #endif
8344 int13_disk:
8345 ;; int13_harddisk modifies high word of EAX
8346 shr eax, #16
8347 push ax
8348 call _int13_harddisk
8349 pop ax
8350 shl eax, #16
8352 int13_out:
8353 pop ds
8354 pop es
8355 popa
8356 iret
8358 ;----------
8359 ;- INT18h -
8360 ;----------
8361 int18_handler: ;; Boot Failure recovery: try the next device.
8363 ;; Reset SP and SS
8364 mov ax, #0xfffe
8365 mov sp, ax
8366 xor ax, ax
8367 mov ss, ax
8369 ;; Get the boot sequence number out of the IPL memory
8370 mov bx, #IPL_SEG
8371 mov ds, bx ;; Set segment
8372 mov bx, IPL_SEQUENCE_OFFSET ;; BX is now the sequence number
8373 inc bx ;; ++
8374 mov IPL_SEQUENCE_OFFSET, bx ;; Write it back
8375 mov ds, ax ;; and reset the segment to zero.
8377 ;; Carry on in the INT 19h handler, using the new sequence number
8378 push bx
8380 jmp int19_next_boot
8382 ;----------
8383 ;- INT19h -
8384 ;----------
8385 int19_relocated: ;; Boot function, relocated
8387 ;; int19 was beginning to be really complex, so now it
8388 ;; just calls a C function that does the work
8390 push bp
8391 mov bp, sp
8393 ;; Reset SS and SP
8394 mov ax, #0xfffe
8395 mov sp, ax
8396 xor ax, ax
8397 mov ss, ax
8399 ;; Start from the first boot device (0, in AX)
8400 mov bx, #IPL_SEG
8401 mov ds, bx ;; Set segment to write to the IPL memory
8402 mov IPL_SEQUENCE_OFFSET, ax ;; Save the sequence number
8403 mov ds, ax ;; and reset the segment.
8405 push ax
8407 int19_next_boot:
8409 ;; Call the C code for the next boot device
8410 call _int19_function
8412 ;; Boot failed: invoke the boot recovery function
8413 int #0x18
8415 ;----------
8416 ;- INT1Ch -
8417 ;----------
8418 int1c_handler: ;; User Timer Tick
8419 iret
8422 ;----------------------
8423 ;- POST: Floppy Drive -
8424 ;----------------------
8425 floppy_drive_post:
8426 xor ax, ax
8427 mov ds, ax
8429 mov al, #0x00
8430 mov 0x043e, al ;; drive 0 & 1 uncalibrated, no interrupt has occurred
8432 mov 0x043f, al ;; diskette motor status: read op, drive0, motors off
8434 mov 0x0440, al ;; diskette motor timeout counter: not active
8435 mov 0x0441, al ;; diskette controller status return code
8437 mov 0x0442, al ;; disk & diskette controller status register 0
8438 mov 0x0443, al ;; diskette controller status register 1
8439 mov 0x0444, al ;; diskette controller status register 2
8440 mov 0x0445, al ;; diskette controller cylinder number
8441 mov 0x0446, al ;; diskette controller head number
8442 mov 0x0447, al ;; diskette controller sector number
8443 mov 0x0448, al ;; diskette controller bytes written
8445 mov 0x048b, al ;; diskette configuration data
8447 ;; -----------------------------------------------------------------
8448 ;; (048F) diskette controller information
8450 mov al, #0x10 ;; get CMOS diskette drive type
8451 out 0x70, AL
8452 in AL, 0x71
8453 mov ah, al ;; save byte to AH
8455 look_drive0:
8456 shr al, #4 ;; look at top 4 bits for drive 0
8457 jz f0_missing ;; jump if no drive0
8458 mov bl, #0x07 ;; drive0 determined, multi-rate, has changed line
8459 jmp look_drive1
8460 f0_missing:
8461 mov bl, #0x00 ;; no drive0
8463 look_drive1:
8464 mov al, ah ;; restore from AH
8465 and al, #0x0f ;; look at bottom 4 bits for drive 1
8466 jz f1_missing ;; jump if no drive1
8467 or bl, #0x70 ;; drive1 determined, multi-rate, has changed line
8468 f1_missing:
8469 ;; leave high bits in BL zerod
8470 mov 0x048f, bl ;; put new val in BDA (diskette controller information)
8471 ;; -----------------------------------------------------------------
8473 mov al, #0x00
8474 mov 0x0490, al ;; diskette 0 media state
8475 mov 0x0491, al ;; diskette 1 media state
8477 ;; diskette 0,1 operational starting state
8478 ;; drive type has not been determined,
8479 ;; has no changed detection line
8480 mov 0x0492, al
8481 mov 0x0493, al
8483 mov 0x0494, al ;; diskette 0 current cylinder
8484 mov 0x0495, al ;; diskette 1 current cylinder
8486 mov al, #0x02
8487 out #0x0a, al ;; clear DMA-1 channel 2 mask bit
8489 SET_INT_VECTOR(0x1E, #0xF000, #diskette_param_table2)
8490 SET_INT_VECTOR(0x40, #0xF000, #int13_diskette)
8491 SET_INT_VECTOR(0x0E, #0xF000, #int0e_handler) ;; IRQ 6
8496 ;--------------------
8497 ;- POST: HARD DRIVE -
8498 ;--------------------
8499 ; relocated here because the primary POST area isnt big enough.
8500 hard_drive_post:
8501 // IRQ 14 = INT 76h
8502 // INT 76h calls INT 15h function ax=9100
8504 mov al, #0x0a ; 0000 1010 = reserved, disable IRQ 14
8505 mov dx, #0x03f6
8506 out dx, al
8508 xor ax, ax
8509 mov ds, ax
8510 mov 0x0474, al /* hard disk status of last operation */
8511 mov 0x0477, al /* hard disk port offset (XT only ???) */
8512 mov 0x048c, al /* hard disk status register */
8513 mov 0x048d, al /* hard disk error register */
8514 mov 0x048e, al /* hard disk task complete flag */
8515 mov al, #0x01
8516 mov 0x0475, al /* hard disk number attached */
8517 mov al, #0xc0
8518 mov 0x0476, al /* hard disk control byte */
8519 SET_INT_VECTOR(0x13, #0xF000, #int13_handler)
8520 SET_INT_VECTOR(0x76, #0xF000, #int76_handler)
8521 ;; INT 41h: hard disk 0 configuration pointer
8522 ;; INT 46h: hard disk 1 configuration pointer
8523 SET_INT_VECTOR(0x41, #EBDA_SEG, #0x003D)
8524 SET_INT_VECTOR(0x46, #EBDA_SEG, #0x004D)
8526 ;; move disk geometry data from CMOS to EBDA disk parameter table(s)
8527 mov al, #0x12
8528 out #0x70, al
8529 in al, #0x71
8530 and al, #0xf0
8531 cmp al, #0xf0
8532 je post_d0_extended
8533 jmp check_for_hd1
8534 post_d0_extended:
8535 mov al, #0x19
8536 out #0x70, al
8537 in al, #0x71
8538 cmp al, #47 ;; decimal 47 - user definable
8539 je post_d0_type47
8540 HALT(__LINE__)
8541 post_d0_type47:
8542 ;; CMOS purpose param table offset
8543 ;; 1b cylinders low 0
8544 ;; 1c cylinders high 1
8545 ;; 1d heads 2
8546 ;; 1e write pre-comp low 5
8547 ;; 1f write pre-comp high 6
8548 ;; 20 retries/bad map/heads>8 8
8549 ;; 21 landing zone low C
8550 ;; 22 landing zone high D
8551 ;; 23 sectors/track E
8553 mov ax, #EBDA_SEG
8554 mov ds, ax
8556 ;;; Filling EBDA table for hard disk 0.
8557 mov al, #0x1f
8558 out #0x70, al
8559 in al, #0x71
8560 mov ah, al
8561 mov al, #0x1e
8562 out #0x70, al
8563 in al, #0x71
8564 mov (0x003d + 0x05), ax ;; write precomp word
8566 mov al, #0x20
8567 out #0x70, al
8568 in al, #0x71
8569 mov (0x003d + 0x08), al ;; drive control byte
8571 mov al, #0x22
8572 out #0x70, al
8573 in al, #0x71
8574 mov ah, al
8575 mov al, #0x21
8576 out #0x70, al
8577 in al, #0x71
8578 mov (0x003d + 0x0C), ax ;; landing zone word
8580 mov al, #0x1c ;; get cylinders word in AX
8581 out #0x70, al
8582 in al, #0x71 ;; high byte
8583 mov ah, al
8584 mov al, #0x1b
8585 out #0x70, al
8586 in al, #0x71 ;; low byte
8587 mov bx, ax ;; BX = cylinders
8589 mov al, #0x1d
8590 out #0x70, al
8591 in al, #0x71
8592 mov cl, al ;; CL = heads
8594 mov al, #0x23
8595 out #0x70, al
8596 in al, #0x71
8597 mov dl, al ;; DL = sectors
8599 cmp bx, #1024
8600 jnbe hd0_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8602 hd0_post_physical_chs:
8603 ;; no logical CHS mapping used, just physical CHS
8604 ;; use Standard Fixed Disk Parameter Table (FDPT)
8605 mov (0x003d + 0x00), bx ;; number of physical cylinders
8606 mov (0x003d + 0x02), cl ;; number of physical heads
8607 mov (0x003d + 0x0E), dl ;; number of physical sectors
8608 jmp check_for_hd1
8610 hd0_post_logical_chs:
8611 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8612 mov (0x003d + 0x09), bx ;; number of physical cylinders
8613 mov (0x003d + 0x0b), cl ;; number of physical heads
8614 mov (0x003d + 0x04), dl ;; number of physical sectors
8615 mov (0x003d + 0x0e), dl ;; number of logical sectors (same)
8616 mov al, #0xa0
8617 mov (0x003d + 0x03), al ;; A0h signature, indicates translated table
8619 cmp bx, #2048
8620 jnbe hd0_post_above_2048
8621 ;; 1024 < c <= 2048 cylinders
8622 shr bx, #0x01
8623 shl cl, #0x01
8624 jmp hd0_post_store_logical
8626 hd0_post_above_2048:
8627 cmp bx, #4096
8628 jnbe hd0_post_above_4096
8629 ;; 2048 < c <= 4096 cylinders
8630 shr bx, #0x02
8631 shl cl, #0x02
8632 jmp hd0_post_store_logical
8634 hd0_post_above_4096:
8635 cmp bx, #8192
8636 jnbe hd0_post_above_8192
8637 ;; 4096 < c <= 8192 cylinders
8638 shr bx, #0x03
8639 shl cl, #0x03
8640 jmp hd0_post_store_logical
8642 hd0_post_above_8192:
8643 ;; 8192 < c <= 16384 cylinders
8644 shr bx, #0x04
8645 shl cl, #0x04
8647 hd0_post_store_logical:
8648 mov (0x003d + 0x00), bx ;; number of physical cylinders
8649 mov (0x003d + 0x02), cl ;; number of physical heads
8650 ;; checksum
8651 mov cl, #0x0f ;; repeat count
8652 mov si, #0x003d ;; offset to disk0 FDPT
8653 mov al, #0x00 ;; sum
8654 hd0_post_checksum_loop:
8655 add al, [si]
8656 inc si
8657 dec cl
8658 jnz hd0_post_checksum_loop
8659 not al ;; now take 2s complement
8660 inc al
8661 mov [si], al
8662 ;;; Done filling EBDA table for hard disk 0.
8665 check_for_hd1:
8666 ;; is there really a second hard disk? if not, return now
8667 mov al, #0x12
8668 out #0x70, al
8669 in al, #0x71
8670 and al, #0x0f
8671 jnz post_d1_exists
8673 post_d1_exists:
8674 ;; check that the hd type is really 0x0f.
8675 cmp al, #0x0f
8676 jz post_d1_extended
8677 HALT(__LINE__)
8678 post_d1_extended:
8679 ;; check that the extended type is 47 - user definable
8680 mov al, #0x1a
8681 out #0x70, al
8682 in al, #0x71
8683 cmp al, #47 ;; decimal 47 - user definable
8684 je post_d1_type47
8685 HALT(__LINE__)
8686 post_d1_type47:
8687 ;; Table for disk1.
8688 ;; CMOS purpose param table offset
8689 ;; 0x24 cylinders low 0
8690 ;; 0x25 cylinders high 1
8691 ;; 0x26 heads 2
8692 ;; 0x27 write pre-comp low 5
8693 ;; 0x28 write pre-comp high 6
8694 ;; 0x29 heads>8 8
8695 ;; 0x2a landing zone low C
8696 ;; 0x2b landing zone high D
8697 ;; 0x2c sectors/track E
8698 ;;; Fill EBDA table for hard disk 1.
8699 mov ax, #EBDA_SEG
8700 mov ds, ax
8701 mov al, #0x28
8702 out #0x70, al
8703 in al, #0x71
8704 mov ah, al
8705 mov al, #0x27
8706 out #0x70, al
8707 in al, #0x71
8708 mov (0x004d + 0x05), ax ;; write precomp word
8710 mov al, #0x29
8711 out #0x70, al
8712 in al, #0x71
8713 mov (0x004d + 0x08), al ;; drive control byte
8715 mov al, #0x2b
8716 out #0x70, al
8717 in al, #0x71
8718 mov ah, al
8719 mov al, #0x2a
8720 out #0x70, al
8721 in al, #0x71
8722 mov (0x004d + 0x0C), ax ;; landing zone word
8724 mov al, #0x25 ;; get cylinders word in AX
8725 out #0x70, al
8726 in al, #0x71 ;; high byte
8727 mov ah, al
8728 mov al, #0x24
8729 out #0x70, al
8730 in al, #0x71 ;; low byte
8731 mov bx, ax ;; BX = cylinders
8733 mov al, #0x26
8734 out #0x70, al
8735 in al, #0x71
8736 mov cl, al ;; CL = heads
8738 mov al, #0x2c
8739 out #0x70, al
8740 in al, #0x71
8741 mov dl, al ;; DL = sectors
8743 cmp bx, #1024
8744 jnbe hd1_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8746 hd1_post_physical_chs:
8747 ;; no logical CHS mapping used, just physical CHS
8748 ;; use Standard Fixed Disk Parameter Table (FDPT)
8749 mov (0x004d + 0x00), bx ;; number of physical cylinders
8750 mov (0x004d + 0x02), cl ;; number of physical heads
8751 mov (0x004d + 0x0E), dl ;; number of physical sectors
8754 hd1_post_logical_chs:
8755 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8756 mov (0x004d + 0x09), bx ;; number of physical cylinders
8757 mov (0x004d + 0x0b), cl ;; number of physical heads
8758 mov (0x004d + 0x04), dl ;; number of physical sectors
8759 mov (0x004d + 0x0e), dl ;; number of logical sectors (same)
8760 mov al, #0xa0
8761 mov (0x004d + 0x03), al ;; A0h signature, indicates translated table
8763 cmp bx, #2048
8764 jnbe hd1_post_above_2048
8765 ;; 1024 < c <= 2048 cylinders
8766 shr bx, #0x01
8767 shl cl, #0x01
8768 jmp hd1_post_store_logical
8770 hd1_post_above_2048:
8771 cmp bx, #4096
8772 jnbe hd1_post_above_4096
8773 ;; 2048 < c <= 4096 cylinders
8774 shr bx, #0x02
8775 shl cl, #0x02
8776 jmp hd1_post_store_logical
8778 hd1_post_above_4096:
8779 cmp bx, #8192
8780 jnbe hd1_post_above_8192
8781 ;; 4096 < c <= 8192 cylinders
8782 shr bx, #0x03
8783 shl cl, #0x03
8784 jmp hd1_post_store_logical
8786 hd1_post_above_8192:
8787 ;; 8192 < c <= 16384 cylinders
8788 shr bx, #0x04
8789 shl cl, #0x04
8791 hd1_post_store_logical:
8792 mov (0x004d + 0x00), bx ;; number of physical cylinders
8793 mov (0x004d + 0x02), cl ;; number of physical heads
8794 ;; checksum
8795 mov cl, #0x0f ;; repeat count
8796 mov si, #0x004d ;; offset to disk0 FDPT
8797 mov al, #0x00 ;; sum
8798 hd1_post_checksum_loop:
8799 add al, [si]
8800 inc si
8801 dec cl
8802 jnz hd1_post_checksum_loop
8803 not al ;; now take 2s complement
8804 inc al
8805 mov [si], al
8806 ;;; Done filling EBDA table for hard disk 1.
8810 ;--------------------
8811 ;- POST: EBDA segment
8812 ;--------------------
8813 ; relocated here because the primary POST area isnt big enough.
8814 ebda_post:
8815 #if BX_USE_EBDA
8816 mov ax, #EBDA_SEG
8817 mov ds, ax
8818 mov byte ptr [0x0], #EBDA_SIZE
8819 #endif
8820 xor ax, ax ; mov EBDA seg into 40E
8821 mov ds, ax
8822 mov word ptr [0x40E], #EBDA_SEG
8823 ret;;
8825 ;--------------------
8826 ;- POST: EOI + jmp via [0x40:67)
8827 ;--------------------
8828 ; relocated here because the primary POST area isnt big enough.
8829 eoi_jmp_post:
8830 call eoi_both_pics
8832 xor ax, ax
8833 mov ds, ax
8835 jmp far ptr [0x467]
8838 ;--------------------
8839 eoi_both_pics:
8840 mov al, #0x20
8841 out #0xA0, al ;; slave PIC EOI
8842 eoi_master_pic:
8843 mov al, #0x20
8844 out #0x20, al ;; master PIC EOI
8847 ;--------------------
8848 BcdToBin:
8849 ;; in: AL in BCD format
8850 ;; out: AL in binary format, AH will always be 0
8851 ;; trashes BX
8852 mov bl, al
8853 and bl, #0x0f ;; bl has low digit
8854 shr al, #4 ;; al has high digit
8855 mov bh, #10
8856 mul al, bh ;; multiply high digit by 10 (result in AX)
8857 add al, bl ;; then add low digit
8860 ;--------------------
8861 timer_tick_post:
8862 ;; Setup the Timer Ticks Count (0x46C:dword) and
8863 ;; Timer Ticks Roller Flag (0x470:byte)
8864 ;; The Timer Ticks Count needs to be set according to
8865 ;; the current CMOS time, as if ticks have been occurring
8866 ;; at 18.2hz since midnight up to this point. Calculating
8867 ;; this is a little complicated. Here are the factors I gather
8868 ;; regarding this. 14,318,180 hz was the original clock speed,
8869 ;; chosen so it could be divided by either 3 to drive the 5Mhz CPU
8870 ;; at the time, or 4 to drive the CGA video adapter. The div3
8871 ;; source was divided again by 4 to feed a 1.193Mhz signal to
8872 ;; the timer. With a maximum 16bit timer count, this is again
8873 ;; divided down by 65536 to 18.2hz.
8875 ;; 14,318,180 Hz clock
8876 ;; /3 = 4,772,726 Hz fed to orginal 5Mhz CPU
8877 ;; /4 = 1,193,181 Hz fed to timer
8878 ;; /65536 (maximum timer count) = 18.20650736 ticks/second
8879 ;; 1 second = 18.20650736 ticks
8880 ;; 1 minute = 1092.390442 ticks
8881 ;; 1 hour = 65543.42651 ticks
8883 ;; Given the values in the CMOS clock, one could calculate
8884 ;; the number of ticks by the following:
8885 ;; ticks = (BcdToBin(seconds) * 18.206507) +
8886 ;; (BcdToBin(minutes) * 1092.3904)
8887 ;; (BcdToBin(hours) * 65543.427)
8888 ;; To get a little more accuracy, since Im using integer
8889 ;; arithmatic, I use:
8890 ;; ticks = (BcdToBin(seconds) * 18206507) / 1000000 +
8891 ;; (BcdToBin(minutes) * 10923904) / 10000 +
8892 ;; (BcdToBin(hours) * 65543427) / 1000
8894 ;; assuming DS=0000
8896 ;; get CMOS seconds
8897 xor eax, eax ;; clear EAX
8898 mov al, #0x00
8899 out #0x70, al
8900 in al, #0x71 ;; AL has CMOS seconds in BCD
8901 call BcdToBin ;; EAX now has seconds in binary
8902 mov edx, #18206507
8903 mul eax, edx
8904 mov ebx, #1000000
8905 xor edx, edx
8906 div eax, ebx
8907 mov ecx, eax ;; ECX will accumulate total ticks
8909 ;; get CMOS minutes
8910 xor eax, eax ;; clear EAX
8911 mov al, #0x02
8912 out #0x70, al
8913 in al, #0x71 ;; AL has CMOS minutes in BCD
8914 call BcdToBin ;; EAX now has minutes in binary
8915 mov edx, #10923904
8916 mul eax, edx
8917 mov ebx, #10000
8918 xor edx, edx
8919 div eax, ebx
8920 add ecx, eax ;; add to total ticks
8922 ;; get CMOS hours
8923 xor eax, eax ;; clear EAX
8924 mov al, #0x04
8925 out #0x70, al
8926 in al, #0x71 ;; AL has CMOS hours in BCD
8927 call BcdToBin ;; EAX now has hours in binary
8928 mov edx, #65543427
8929 mul eax, edx
8930 mov ebx, #1000
8931 xor edx, edx
8932 div eax, ebx
8933 add ecx, eax ;; add to total ticks
8935 mov 0x46C, ecx ;; Timer Ticks Count
8936 xor al, al
8937 mov 0x470, al ;; Timer Ticks Rollover Flag
8940 ;--------------------
8941 int76_handler:
8942 ;; record completion in BIOS task complete flag
8943 push ax
8944 push ds
8945 mov ax, #0x0040
8946 mov ds, ax
8947 mov 0x008E, #0xff
8948 call eoi_both_pics
8949 pop ds
8950 pop ax
8951 iret
8954 ;--------------------
8955 #if BX_APM
8957 use32 386
8958 #define APM_PROT32
8959 #include "apmbios.S"
8961 use16 386
8962 #define APM_PROT16
8963 #include "apmbios.S"
8965 #define APM_REAL
8966 #include "apmbios.S"
8968 #endif
8970 ;--------------------
8971 #if BX_PCIBIOS
8972 use32 386
8973 .align 16
8974 bios32_structure:
8975 db 0x5f, 0x33, 0x32, 0x5f ;; "_32_" signature
8976 dw bios32_entry_point, 0xf ;; 32 bit physical address
8977 db 0 ;; revision level
8978 ;; length in paragraphs and checksum stored in a word to prevent errors
8979 dw (~(((bios32_entry_point >> 8) + (bios32_entry_point & 0xff) + 0x32) \
8980 & 0xff) << 8) + 0x01
8981 db 0,0,0,0,0 ;; reserved
8983 .align 16
8984 bios32_entry_point:
8985 pushfd
8986 cmp eax, #0x49435024 ;; "$PCI"
8987 jne unknown_service
8988 mov eax, #0x80000000
8989 mov dx, #0x0cf8
8990 out dx, eax
8991 mov dx, #0x0cfc
8992 in eax, dx
8993 #ifdef PCI_FIXED_HOST_BRIDGE
8994 cmp eax, #PCI_FIXED_HOST_BRIDGE
8995 jne unknown_service
8996 #else
8997 ;; say ok if a device is present
8998 cmp eax, #0xffffffff
8999 je unknown_service
9000 #endif
9001 mov ebx, #0x000f0000
9002 mov ecx, #0
9003 mov edx, #pcibios_protected
9004 xor al, al
9005 jmp bios32_end
9006 unknown_service:
9007 mov al, #0x80
9008 bios32_end:
9009 #ifdef BX_QEMU
9010 and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
9011 #endif
9012 popfd
9013 retf
9015 .align 16
9016 pcibios_protected:
9017 pushfd
9019 push esi
9020 push edi
9021 cmp al, #0x01 ;; installation check
9022 jne pci_pro_f02
9023 mov bx, #0x0210
9024 mov cx, #0
9025 mov edx, #0x20494350 ;; "PCI "
9026 mov al, #0x01
9027 jmp pci_pro_ok
9028 pci_pro_f02: ;; find pci device
9029 cmp al, #0x02
9030 jne pci_pro_f03
9031 shl ecx, #16
9032 mov cx, dx
9033 xor bx, bx
9034 mov di, #0x00
9035 pci_pro_devloop:
9036 call pci_pro_select_reg
9037 mov dx, #0x0cfc
9038 in eax, dx
9039 cmp eax, ecx
9040 jne pci_pro_nextdev
9041 cmp si, #0
9042 je pci_pro_ok
9043 dec si
9044 pci_pro_nextdev:
9045 inc bx
9046 cmp bx, #0x0100
9047 jne pci_pro_devloop
9048 mov ah, #0x86
9049 jmp pci_pro_fail
9050 pci_pro_f03: ;; find class code
9051 cmp al, #0x03
9052 jne pci_pro_f08
9053 xor bx, bx
9054 mov di, #0x08
9055 pci_pro_devloop2:
9056 call pci_pro_select_reg
9057 mov dx, #0x0cfc
9058 in eax, dx
9059 shr eax, #8
9060 cmp eax, ecx
9061 jne pci_pro_nextdev2
9062 cmp si, #0
9063 je pci_pro_ok
9064 dec si
9065 pci_pro_nextdev2:
9066 inc bx
9067 cmp bx, #0x0100
9068 jne pci_pro_devloop2
9069 mov ah, #0x86
9070 jmp pci_pro_fail
9071 pci_pro_f08: ;; read configuration byte
9072 cmp al, #0x08
9073 jne pci_pro_f09
9074 call pci_pro_select_reg
9075 push edx
9076 mov dx, di
9077 and dx, #0x03
9078 add dx, #0x0cfc
9079 in al, dx
9080 pop edx
9081 mov cl, al
9082 jmp pci_pro_ok
9083 pci_pro_f09: ;; read configuration word
9084 cmp al, #0x09
9085 jne pci_pro_f0a
9086 call pci_pro_select_reg
9087 push edx
9088 mov dx, di
9089 and dx, #0x02
9090 add dx, #0x0cfc
9091 in ax, dx
9092 pop edx
9093 mov cx, ax
9094 jmp pci_pro_ok
9095 pci_pro_f0a: ;; read configuration dword
9096 cmp al, #0x0a
9097 jne pci_pro_f0b
9098 call pci_pro_select_reg
9099 push edx
9100 mov dx, #0x0cfc
9101 in eax, dx
9102 pop edx
9103 mov ecx, eax
9104 jmp pci_pro_ok
9105 pci_pro_f0b: ;; write configuration byte
9106 cmp al, #0x0b
9107 jne pci_pro_f0c
9108 call pci_pro_select_reg
9109 push edx
9110 mov dx, di
9111 and dx, #0x03
9112 add dx, #0x0cfc
9113 mov al, cl
9114 out dx, al
9115 pop edx
9116 jmp pci_pro_ok
9117 pci_pro_f0c: ;; write configuration word
9118 cmp al, #0x0c
9119 jne pci_pro_f0d
9120 call pci_pro_select_reg
9121 push edx
9122 mov dx, di
9123 and dx, #0x02
9124 add dx, #0x0cfc
9125 mov ax, cx
9126 out dx, ax
9127 pop edx
9128 jmp pci_pro_ok
9129 pci_pro_f0d: ;; write configuration dword
9130 cmp al, #0x0d
9131 jne pci_pro_unknown
9132 call pci_pro_select_reg
9133 push edx
9134 mov dx, #0x0cfc
9135 mov eax, ecx
9136 out dx, eax
9137 pop edx
9138 jmp pci_pro_ok
9139 pci_pro_unknown:
9140 mov ah, #0x81
9141 pci_pro_fail:
9142 pop edi
9143 pop esi
9144 #ifdef BX_QEMU
9145 and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
9146 #endif
9147 popfd
9149 retf
9150 pci_pro_ok:
9151 xor ah, ah
9152 pop edi
9153 pop esi
9154 #ifdef BX_QEMU
9155 and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
9156 #endif
9157 popfd
9159 retf
9161 pci_pro_select_reg:
9162 push edx
9163 mov eax, #0x800000
9164 mov ax, bx
9165 shl eax, #8
9166 and di, #0xff
9167 or ax, di
9168 and al, #0xfc
9169 mov dx, #0x0cf8
9170 out dx, eax
9171 pop edx
9174 use16 386
9176 pcibios_real:
9177 push eax
9178 push dx
9179 mov eax, #0x80000000
9180 mov dx, #0x0cf8
9181 out dx, eax
9182 mov dx, #0x0cfc
9183 in eax, dx
9184 #ifdef PCI_FIXED_HOST_BRIDGE
9185 cmp eax, #PCI_FIXED_HOST_BRIDGE
9186 je pci_present
9187 #else
9188 ;; say ok if a device is present
9189 cmp eax, #0xffffffff
9190 jne pci_present
9191 #endif
9192 pop dx
9193 pop eax
9194 mov ah, #0xff
9197 pci_present:
9198 pop dx
9199 pop eax
9200 cmp al, #0x01 ;; installation check
9201 jne pci_real_f02
9202 mov ax, #0x0001
9203 mov bx, #0x0210
9204 mov cx, #0
9205 mov edx, #0x20494350 ;; "PCI "
9206 mov edi, #0xf0000
9207 mov di, #pcibios_protected
9210 pci_real_f02: ;; find pci device
9211 push esi
9212 push edi
9213 cmp al, #0x02
9214 jne pci_real_f03
9215 shl ecx, #16
9216 mov cx, dx
9217 xor bx, bx
9218 mov di, #0x00
9219 pci_real_devloop:
9220 call pci_real_select_reg
9221 mov dx, #0x0cfc
9222 in eax, dx
9223 cmp eax, ecx
9224 jne pci_real_nextdev
9225 cmp si, #0
9226 je pci_real_ok
9227 dec si
9228 pci_real_nextdev:
9229 inc bx
9230 cmp bx, #0x0100
9231 jne pci_real_devloop
9232 mov dx, cx
9233 shr ecx, #16
9234 mov ax, #0x8602
9235 jmp pci_real_fail
9236 pci_real_f03: ;; find class code
9237 cmp al, #0x03
9238 jne pci_real_f08
9239 xor bx, bx
9240 mov di, #0x08
9241 pci_real_devloop2:
9242 call pci_real_select_reg
9243 mov dx, #0x0cfc
9244 in eax, dx
9245 shr eax, #8
9246 cmp eax, ecx
9247 jne pci_real_nextdev2
9248 cmp si, #0
9249 je pci_real_ok
9250 dec si
9251 pci_real_nextdev2:
9252 inc bx
9253 cmp bx, #0x0100
9254 jne pci_real_devloop2
9255 mov dx, cx
9256 shr ecx, #16
9257 mov ax, #0x8603
9258 jmp pci_real_fail
9259 pci_real_f08: ;; read configuration byte
9260 cmp al, #0x08
9261 jne pci_real_f09
9262 call pci_real_select_reg
9263 push dx
9264 mov dx, di
9265 and dx, #0x03
9266 add dx, #0x0cfc
9267 in al, dx
9268 pop dx
9269 mov cl, al
9270 jmp pci_real_ok
9271 pci_real_f09: ;; read configuration word
9272 cmp al, #0x09
9273 jne pci_real_f0a
9274 call pci_real_select_reg
9275 push dx
9276 mov dx, di
9277 and dx, #0x02
9278 add dx, #0x0cfc
9279 in ax, dx
9280 pop dx
9281 mov cx, ax
9282 jmp pci_real_ok
9283 pci_real_f0a: ;; read configuration dword
9284 cmp al, #0x0a
9285 jne pci_real_f0b
9286 call pci_real_select_reg
9287 push dx
9288 mov dx, #0x0cfc
9289 in eax, dx
9290 pop dx
9291 mov ecx, eax
9292 jmp pci_real_ok
9293 pci_real_f0b: ;; write configuration byte
9294 cmp al, #0x0b
9295 jne pci_real_f0c
9296 call pci_real_select_reg
9297 push dx
9298 mov dx, di
9299 and dx, #0x03
9300 add dx, #0x0cfc
9301 mov al, cl
9302 out dx, al
9303 pop dx
9304 jmp pci_real_ok
9305 pci_real_f0c: ;; write configuration word
9306 cmp al, #0x0c
9307 jne pci_real_f0d
9308 call pci_real_select_reg
9309 push dx
9310 mov dx, di
9311 and dx, #0x02
9312 add dx, #0x0cfc
9313 mov ax, cx
9314 out dx, ax
9315 pop dx
9316 jmp pci_real_ok
9317 pci_real_f0d: ;; write configuration dword
9318 cmp al, #0x0d
9319 jne pci_real_f0e
9320 call pci_real_select_reg
9321 push dx
9322 mov dx, #0x0cfc
9323 mov eax, ecx
9324 out dx, eax
9325 pop dx
9326 jmp pci_real_ok
9327 pci_real_f0e: ;; get irq routing options
9328 cmp al, #0x0e
9329 jne pci_real_unknown
9330 SEG ES
9331 cmp word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9332 jb pci_real_too_small
9333 SEG ES
9334 mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9335 pushf
9336 push ds
9337 push es
9338 push cx
9339 push si
9340 push di
9342 mov si, #pci_routing_table_structure_start
9343 push cs
9344 pop ds
9345 SEG ES
9346 mov cx, [di+2]
9347 SEG ES
9348 mov es, [di+4]
9349 mov di, cx
9350 mov cx, #pci_routing_table_structure_end - pci_routing_table_structure_start
9352 movsb
9353 pop di
9354 pop si
9355 pop cx
9356 pop es
9357 pop ds
9358 popf
9359 mov bx, #(1 << 9) | (1 << 11) ;; irq 9 and 11 are used
9360 jmp pci_real_ok
9361 pci_real_too_small:
9362 SEG ES
9363 mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9364 mov ah, #0x89
9365 jmp pci_real_fail
9367 pci_real_unknown:
9368 mov ah, #0x81
9369 pci_real_fail:
9370 pop edi
9371 pop esi
9374 pci_real_ok:
9375 xor ah, ah
9376 pop edi
9377 pop esi
9381 pci_real_select_reg:
9382 push dx
9383 mov eax, #0x800000
9384 mov ax, bx
9385 shl eax, #8
9386 and di, #0xff
9387 or ax, di
9388 and al, #0xfc
9389 mov dx, #0x0cf8
9390 out dx, eax
9391 pop dx
9394 .align 16
9395 pci_routing_table_structure:
9396 db 0x24, 0x50, 0x49, 0x52 ;; "$PIR" signature
9397 db 0, 1 ;; version
9398 dw 32 + (6 * 16) ;; table size
9399 db 0 ;; PCI interrupt router bus
9400 db 0x08 ;; PCI interrupt router DevFunc
9401 dw 0x0000 ;; PCI exclusive IRQs
9402 dw 0x8086 ;; compatible PCI interrupt router vendor ID
9403 dw 0x7000 ;; compatible PCI interrupt router device ID
9404 dw 0,0 ;; Miniport data
9405 db 0,0,0,0,0,0,0,0,0,0,0 ;; reserved
9406 db 0x07 ;; checksum
9407 pci_routing_table_structure_start:
9408 ;; first slot entry PCI-to-ISA (embedded)
9409 db 0 ;; pci bus number
9410 db 0x08 ;; pci device number (bit 7-3)
9411 db 0x60 ;; link value INTA#: pointer into PCI2ISA config space
9412 dw 0xdef8 ;; IRQ bitmap INTA#
9413 db 0x61 ;; link value INTB#
9414 dw 0xdef8 ;; IRQ bitmap INTB#
9415 db 0x62 ;; link value INTC#
9416 dw 0xdef8 ;; IRQ bitmap INTC#
9417 db 0x63 ;; link value INTD#
9418 dw 0xdef8 ;; IRQ bitmap INTD#
9419 db 0 ;; physical slot (0 = embedded)
9420 db 0 ;; reserved
9421 ;; second slot entry: 1st PCI slot
9422 db 0 ;; pci bus number
9423 db 0x10 ;; pci device number (bit 7-3)
9424 db 0x61 ;; link value INTA#
9425 dw 0xdef8 ;; IRQ bitmap INTA#
9426 db 0x62 ;; link value INTB#
9427 dw 0xdef8 ;; IRQ bitmap INTB#
9428 db 0x63 ;; link value INTC#
9429 dw 0xdef8 ;; IRQ bitmap INTC#
9430 db 0x60 ;; link value INTD#
9431 dw 0xdef8 ;; IRQ bitmap INTD#
9432 db 1 ;; physical slot (0 = embedded)
9433 db 0 ;; reserved
9434 ;; third slot entry: 2nd PCI slot
9435 db 0 ;; pci bus number
9436 db 0x18 ;; pci device number (bit 7-3)
9437 db 0x62 ;; link value INTA#
9438 dw 0xdef8 ;; IRQ bitmap INTA#
9439 db 0x63 ;; link value INTB#
9440 dw 0xdef8 ;; IRQ bitmap INTB#
9441 db 0x60 ;; link value INTC#
9442 dw 0xdef8 ;; IRQ bitmap INTC#
9443 db 0x61 ;; link value INTD#
9444 dw 0xdef8 ;; IRQ bitmap INTD#
9445 db 2 ;; physical slot (0 = embedded)
9446 db 0 ;; reserved
9447 ;; 4th slot entry: 3rd PCI slot
9448 db 0 ;; pci bus number
9449 db 0x20 ;; pci device number (bit 7-3)
9450 db 0x63 ;; link value INTA#
9451 dw 0xdef8 ;; IRQ bitmap INTA#
9452 db 0x60 ;; link value INTB#
9453 dw 0xdef8 ;; IRQ bitmap INTB#
9454 db 0x61 ;; link value INTC#
9455 dw 0xdef8 ;; IRQ bitmap INTC#
9456 db 0x62 ;; link value INTD#
9457 dw 0xdef8 ;; IRQ bitmap INTD#
9458 db 3 ;; physical slot (0 = embedded)
9459 db 0 ;; reserved
9460 ;; 5th slot entry: 4rd PCI slot
9461 db 0 ;; pci bus number
9462 db 0x28 ;; pci device number (bit 7-3)
9463 db 0x60 ;; link value INTA#
9464 dw 0xdef8 ;; IRQ bitmap INTA#
9465 db 0x61 ;; link value INTB#
9466 dw 0xdef8 ;; IRQ bitmap INTB#
9467 db 0x62 ;; link value INTC#
9468 dw 0xdef8 ;; IRQ bitmap INTC#
9469 db 0x63 ;; link value INTD#
9470 dw 0xdef8 ;; IRQ bitmap INTD#
9471 db 4 ;; physical slot (0 = embedded)
9472 db 0 ;; reserved
9473 ;; 6th slot entry: 5rd PCI slot
9474 db 0 ;; pci bus number
9475 db 0x30 ;; pci device number (bit 7-3)
9476 db 0x61 ;; link value INTA#
9477 dw 0xdef8 ;; IRQ bitmap INTA#
9478 db 0x62 ;; link value INTB#
9479 dw 0xdef8 ;; IRQ bitmap INTB#
9480 db 0x63 ;; link value INTC#
9481 dw 0xdef8 ;; IRQ bitmap INTC#
9482 db 0x60 ;; link value INTD#
9483 dw 0xdef8 ;; IRQ bitmap INTD#
9484 db 5 ;; physical slot (0 = embedded)
9485 db 0 ;; reserved
9486 pci_routing_table_structure_end:
9488 #if !BX_ROMBIOS32
9489 pci_irq_list:
9490 db 11, 10, 9, 5;
9492 pcibios_init_sel_reg:
9493 push eax
9494 mov eax, #0x800000
9495 mov ax, bx
9496 shl eax, #8
9497 and dl, #0xfc
9498 or al, dl
9499 mov dx, #0x0cf8
9500 out dx, eax
9501 pop eax
9504 pcibios_init_iomem_bases:
9505 push bp
9506 mov bp, sp
9507 mov eax, #0xe0000000 ;; base for memory init
9508 push eax
9509 mov ax, #0xc000 ;; base for i/o init
9510 push ax
9511 mov ax, #0x0010 ;; start at base address #0
9512 push ax
9513 mov bx, #0x0008
9514 pci_init_io_loop1:
9515 mov dl, #0x00
9516 call pcibios_init_sel_reg
9517 mov dx, #0x0cfc
9518 in ax, dx
9519 cmp ax, #0xffff
9520 jz next_pci_dev
9521 mov dl, #0x04 ;; disable i/o and memory space access
9522 call pcibios_init_sel_reg
9523 mov dx, #0x0cfc
9524 in al, dx
9525 and al, #0xfc
9526 out dx, al
9527 pci_init_io_loop2:
9528 mov dl, [bp-8]
9529 call pcibios_init_sel_reg
9530 mov dx, #0x0cfc
9531 in eax, dx
9532 test al, #0x01
9533 jnz init_io_base
9534 mov ecx, eax
9535 mov eax, #0xffffffff
9536 out dx, eax
9537 in eax, dx
9538 cmp eax, ecx
9539 je next_pci_base
9540 xor eax, #0xffffffff
9541 mov ecx, eax
9542 mov eax, [bp-4]
9543 out dx, eax
9544 add eax, ecx ;; calculate next free mem base
9545 add eax, #0x01000000
9546 and eax, #0xff000000
9547 mov [bp-4], eax
9548 jmp next_pci_base
9549 init_io_base:
9550 mov cx, ax
9551 mov ax, #0xffff
9552 out dx, ax
9553 in ax, dx
9554 cmp ax, cx
9555 je next_pci_base
9556 xor ax, #0xfffe
9557 mov cx, ax
9558 mov ax, [bp-6]
9559 out dx, ax
9560 add ax, cx ;; calculate next free i/o base
9561 add ax, #0x0100
9562 and ax, #0xff00
9563 mov [bp-6], ax
9564 next_pci_base:
9565 mov al, [bp-8]
9566 add al, #0x04
9567 cmp al, #0x28
9568 je enable_iomem_space
9569 mov byte ptr[bp-8], al
9570 jmp pci_init_io_loop2
9571 enable_iomem_space:
9572 mov dl, #0x04 ;; enable i/o and memory space access if available
9573 call pcibios_init_sel_reg
9574 mov dx, #0x0cfc
9575 in al, dx
9576 or al, #0x07
9577 out dx, al
9578 next_pci_dev:
9579 mov byte ptr[bp-8], #0x10
9580 inc bx
9581 cmp bx, #0x0100
9582 jne pci_init_io_loop1
9583 mov sp, bp
9584 pop bp
9587 pcibios_init_set_elcr:
9588 push ax
9589 push cx
9590 mov dx, #0x04d0
9591 test al, #0x08
9592 jz is_master_pic
9593 inc dx
9594 and al, #0x07
9595 is_master_pic:
9596 mov cl, al
9597 mov bl, #0x01
9598 shl bl, cl
9599 in al, dx
9600 or al, bl
9601 out dx, al
9602 pop cx
9603 pop ax
9606 pcibios_init_irqs:
9607 push ds
9608 push bp
9609 mov ax, #0xf000
9610 mov ds, ax
9611 mov dx, #0x04d0 ;; reset ELCR1 + ELCR2
9612 mov al, #0x00
9613 out dx, al
9614 inc dx
9615 out dx, al
9616 mov si, #pci_routing_table_structure
9617 mov bh, [si+8]
9618 mov bl, [si+9]
9619 mov dl, #0x00
9620 call pcibios_init_sel_reg
9621 mov dx, #0x0cfc
9622 in eax, dx
9623 cmp eax, [si+12] ;; check irq router
9624 jne pci_init_end
9625 mov dl, [si+34]
9626 call pcibios_init_sel_reg
9627 push bx ;; save irq router bus + devfunc
9628 mov dx, #0x0cfc
9629 mov ax, #0x8080
9630 out dx, ax ;; reset PIRQ route control
9631 add dx, 2
9632 out dx, ax
9633 mov ax, [si+6]
9634 sub ax, #0x20
9635 shr ax, #4
9636 mov cx, ax
9637 add si, #0x20 ;; set pointer to 1st entry
9638 mov bp, sp
9639 mov ax, #pci_irq_list
9640 push ax
9641 xor ax, ax
9642 push ax
9643 pci_init_irq_loop1:
9644 mov bh, [si]
9645 mov bl, [si+1]
9646 pci_init_irq_loop2:
9647 mov dl, #0x00
9648 call pcibios_init_sel_reg
9649 mov dx, #0x0cfc
9650 in ax, dx
9651 cmp ax, #0xffff
9652 jnz pci_test_int_pin
9653 test bl, #0x07
9654 jz next_pir_entry
9655 jmp next_pci_func
9656 pci_test_int_pin:
9657 mov dl, #0x3c
9658 call pcibios_init_sel_reg
9659 mov dx, #0x0cfd
9660 in al, dx
9661 and al, #0x07
9662 jz next_pci_func
9663 dec al ;; determine pirq reg
9664 mov dl, #0x03
9665 mul al, dl
9666 add al, #0x02
9667 xor ah, ah
9668 mov bx, ax
9669 mov al, [si+bx]
9670 mov dl, al
9671 mov bx, [bp]
9672 call pcibios_init_sel_reg
9673 mov dx, #0x0cfc
9674 and al, #0x03
9675 add dl, al
9676 in al, dx
9677 cmp al, #0x80
9678 jb pirq_found
9679 mov bx, [bp-2] ;; pci irq list pointer
9680 mov al, [bx]
9681 out dx, al
9682 inc bx
9683 mov [bp-2], bx
9684 call pcibios_init_set_elcr
9685 pirq_found:
9686 mov bh, [si]
9687 mov bl, [si+1]
9688 add bl, [bp-3] ;; pci function number
9689 mov dl, #0x3c
9690 call pcibios_init_sel_reg
9691 mov dx, #0x0cfc
9692 out dx, al
9693 next_pci_func:
9694 inc byte ptr[bp-3]
9695 inc bl
9696 test bl, #0x07
9697 jnz pci_init_irq_loop2
9698 next_pir_entry:
9699 add si, #0x10
9700 mov byte ptr[bp-3], #0x00
9701 loop pci_init_irq_loop1
9702 mov sp, bp
9703 pop bx
9704 pci_init_end:
9705 pop bp
9706 pop ds
9708 #endif // BX_ROMBIOS32
9709 #endif // BX_PCIBIOS
9711 #if BX_ROMBIOS32
9712 rombios32_init:
9713 ;; save a20 and enable it
9714 in al, 0x92
9715 push ax
9716 or al, #0x02
9717 out 0x92, al
9719 ;; save SS:SP to the BDA
9720 xor ax, ax
9721 mov ds, ax
9722 mov 0x0469, ss
9723 mov 0x0467, sp
9725 SEG CS
9726 lidt [pmode_IDT_info]
9727 SEG CS
9728 lgdt [rombios32_gdt_48]
9729 ;; set PE bit in CR0
9730 mov eax, cr0
9731 or al, #0x01
9732 mov cr0, eax
9733 ;; start protected mode code: ljmpl 0x10:rombios32_init1
9734 db 0x66, 0xea
9735 dw rombios32_05
9736 dw 0x000f ;; high 16 bit address
9737 dw 0x0010
9739 use32 386
9740 rombios32_05:
9741 ;; init data segments
9742 mov eax, #0x18
9743 mov ds, ax
9744 mov es, ax
9745 mov ss, ax
9746 xor eax, eax
9747 mov fs, ax
9748 mov gs, ax
9751 ;; copy rombios32 code to ram (ram offset = 1MB)
9752 mov esi, #0xfffe0000
9753 mov edi, #0x00040000
9754 mov ecx, #0x10000 / 4
9756 movsd
9758 ;; init the stack pointer
9759 mov esp, #0x00080000
9761 ;; call rombios32 code
9762 mov eax, #0x00040000
9763 call eax
9765 ;; reset the memory (some boot loaders such as syslinux suppose
9766 ;; that the memory is set to zero)
9767 mov edi, #0x00040000
9768 mov ecx, #0x40000 / 4
9769 xor eax, eax
9771 stosd
9773 ;; return to 16 bit protected mode first
9774 db 0xea
9775 dd rombios32_10
9776 dw 0x20
9778 use16 386
9779 rombios32_10:
9780 ;; restore data segment limits to 0xffff
9781 mov ax, #0x28
9782 mov ds, ax
9783 mov es, ax
9784 mov ss, ax
9785 mov fs, ax
9786 mov gs, ax
9788 ;; reset PE bit in CR0
9789 mov eax, cr0
9790 and al, #0xFE
9791 mov cr0, eax
9793 ;; far jump to flush CPU queue after transition to real mode
9794 JMP_AP(0xf000, rombios32_real_mode)
9796 rombios32_real_mode:
9797 ;; restore IDT to normal real-mode defaults
9798 SEG CS
9799 lidt [rmode_IDT_info]
9801 xor ax, ax
9802 mov ds, ax
9803 mov es, ax
9804 mov fs, ax
9805 mov gs, ax
9807 ;; restore SS:SP from the BDA
9808 mov ss, 0x0469
9809 xor esp, esp
9810 mov sp, 0x0467
9811 ;; restore a20
9812 pop ax
9813 out 0x92, al
9816 rombios32_gdt_48:
9817 dw 0x30
9818 dw rombios32_gdt
9819 dw 0x000f
9821 rombios32_gdt:
9822 dw 0, 0, 0, 0
9823 dw 0, 0, 0, 0
9824 dw 0xffff, 0, 0x9b00, 0x00cf ; 32 bit flat code segment (0x10)
9825 dw 0xffff, 0, 0x9300, 0x00cf ; 32 bit flat data segment (0x18)
9826 dw 0xffff, 0, 0x9b0f, 0x0000 ; 16 bit code segment base=0xf0000 limit=0xffff
9827 dw 0xffff, 0, 0x9300, 0x0000 ; 16 bit data segment base=0x0 limit=0xffff
9828 #endif
9831 ; parallel port detection: base address in DX, index in BX, timeout in CL
9832 detect_parport:
9833 push dx
9834 add dx, #2
9835 in al, dx
9836 and al, #0xdf ; clear input mode
9837 out dx, al
9838 pop dx
9839 mov al, #0xaa
9840 out dx, al
9841 in al, dx
9842 cmp al, #0xaa
9843 jne no_parport
9844 push bx
9845 shl bx, #1
9846 mov [bx+0x408], dx ; Parallel I/O address
9847 pop bx
9848 mov [bx+0x478], cl ; Parallel printer timeout
9849 inc bx
9850 no_parport:
9853 ; serial port detection: base address in DX, index in BX, timeout in CL
9854 detect_serial:
9855 push dx
9856 inc dx
9857 mov al, #0x02
9858 out dx, al
9859 in al, dx
9860 cmp al, #0x02
9861 jne no_serial
9862 inc dx
9863 in al, dx
9864 cmp al, #0x02
9865 jne no_serial
9866 dec dx
9867 xor al, al
9868 out dx, al
9869 pop dx
9870 push bx
9871 shl bx, #1
9872 mov [bx+0x400], dx ; Serial I/O address
9873 pop bx
9874 mov [bx+0x47c], cl ; Serial timeout
9875 inc bx
9877 no_serial:
9878 pop dx
9881 rom_checksum:
9882 push ax
9883 push bx
9884 push cx
9885 xor ax, ax
9886 xor bx, bx
9887 xor cx, cx
9888 mov ch, [2]
9889 shl cx, #1
9890 checksum_loop:
9891 add al, [bx]
9892 inc bx
9893 loop checksum_loop
9894 and al, #0xff
9895 pop cx
9896 pop bx
9897 pop ax
9901 ;; We need a copy of this string, but we are not actually a PnP BIOS,
9902 ;; so make sure it is *not* aligned, so OSes will not see it if they scan.
9903 .align 16
9904 db 0
9905 pnp_string:
9906 .ascii "$PnP"
9909 rom_scan:
9910 ;; Scan for existence of valid expansion ROMS.
9911 ;; Video ROM: from 0xC0000..0xC7FFF in 2k increments
9912 ;; General ROM: from 0xC8000..0xDFFFF in 2k increments
9913 ;; System ROM: only 0xE0000
9915 ;; Header:
9916 ;; Offset Value
9917 ;; 0 0x55
9918 ;; 1 0xAA
9919 ;; 2 ROM length in 512-byte blocks
9920 ;; 3 ROM initialization entry point (FAR CALL)
9922 mov cx, #0xc000
9923 rom_scan_loop:
9924 mov ds, cx
9925 mov ax, #0x0004 ;; start with increment of 4 (512-byte) blocks = 2k
9926 cmp [0], #0xAA55 ;; look for signature
9927 jne rom_scan_increment
9928 call rom_checksum
9929 jnz rom_scan_increment
9930 mov al, [2] ;; change increment to ROM length in 512-byte blocks
9932 ;; We want our increment in 512-byte quantities, rounded to
9933 ;; the nearest 2k quantity, since we only scan at 2k intervals.
9934 test al, #0x03
9935 jz block_count_rounded
9936 and al, #0xfc ;; needs rounding up
9937 add al, #0x04
9938 block_count_rounded:
9940 xor bx, bx ;; Restore DS back to 0000:
9941 mov ds, bx
9942 push ax ;; Save AX
9943 push di ;; Save DI
9944 ;; Push addr of ROM entry point
9945 push cx ;; Push seg
9946 push #0x0003 ;; Push offset
9948 ;; Point ES:DI at "$PnP", which tells the ROM that we are a PnP BIOS.
9949 ;; That should stop it grabbing INT 19h; we will use its BEV instead.
9950 mov ax, #0xf000
9951 mov es, ax
9952 lea di, pnp_string
9954 mov bp, sp ;; Call ROM init routine using seg:off on stack
9955 db 0xff ;; call_far ss:[bp+0]
9956 db 0x5e
9957 db 0
9958 cli ;; In case expansion ROM BIOS turns IF on
9959 add sp, #2 ;; Pop offset value
9960 pop cx ;; Pop seg value (restore CX)
9962 ;; Look at the ROM's PnP Expansion header. Properly, we're supposed
9963 ;; to init all the ROMs and then go back and build an IPL table of
9964 ;; all the bootable devices, but we can get away with one pass.
9965 mov ds, cx ;; ROM base
9966 mov bx, 0x001a ;; 0x1A is the offset into ROM header that contains...
9967 mov ax, [bx] ;; the offset of PnP expansion header, where...
9968 cmp ax, #0x5024 ;; we look for signature "$PnP"
9969 jne no_bev
9970 mov ax, 2[bx]
9971 cmp ax, #0x506e
9972 jne no_bev
9973 mov ax, 0x1a[bx] ;; 0x1A is also the offset into the expansion header of...
9974 cmp ax, #0x0000 ;; the Bootstrap Entry Vector, or zero if there is none.
9975 je no_bev
9977 ;; Found a device that thinks it can boot the system. Record its BEV and product name string.
9978 mov di, 0x10[bx] ;; Pointer to the product name string or zero if none
9979 mov bx, #IPL_SEG ;; Go to the segment where the IPL table lives
9980 mov ds, bx
9981 mov bx, IPL_COUNT_OFFSET ;; Read the number of entries so far
9982 cmp bx, #IPL_TABLE_ENTRIES
9983 je no_bev ;; Get out if the table is full
9984 shl bx, #0x4 ;; Turn count into offset (entries are 16 bytes)
9985 mov 0[bx], #0x80 ;; This entry is a BEV device
9986 mov 6[bx], cx ;; Build a far pointer from the segment...
9987 mov 4[bx], ax ;; and the offset
9988 cmp di, #0x0000
9989 je no_prod_str
9990 mov 0xA[bx], cx ;; Build a far pointer from the segment...
9991 mov 8[bx], di ;; and the offset
9992 no_prod_str:
9993 shr bx, #0x4 ;; Turn the offset back into a count
9994 inc bx ;; We have one more entry now
9995 mov IPL_COUNT_OFFSET, bx ;; Remember that.
9997 no_bev:
9998 pop di ;; Restore DI
9999 pop ax ;; Restore AX
10000 rom_scan_increment:
10001 shl ax, #5 ;; convert 512-bytes blocks to 16-byte increments
10002 ;; because the segment selector is shifted left 4 bits.
10003 add cx, ax
10004 cmp cx, #0xe000
10005 jbe rom_scan_loop
10007 xor ax, ax ;; Restore DS back to 0000:
10008 mov ds, ax
10011 ;; for 'C' strings and other data, insert them here with
10012 ;; a the following hack:
10013 ;; DATA_SEG_DEFS_HERE
10016 ;; the following area can be used to write dynamically generated tables
10017 .align 16
10018 bios_table_area_start:
10019 dd 0xaafb4442
10020 dd bios_table_area_end - bios_table_area_start - 8;
10022 ;--------
10023 ;- POST -
10024 ;--------
10025 .org 0xe05b ; POST Entry Point
10026 bios_table_area_end:
10027 post:
10029 xor ax, ax
10031 ;; first reset the DMA controllers
10032 out 0x0d,al
10033 out 0xda,al
10035 ;; then initialize the DMA controllers
10036 mov al, #0xC0
10037 out 0xD6, al ; cascade mode of channel 4 enabled
10038 mov al, #0x00
10039 out 0xD4, al ; unmask channel 4
10041 ;; Examine CMOS shutdown status.
10042 mov AL, #0x0f
10043 out 0x70, AL
10044 in AL, 0x71
10046 ;; backup status
10047 mov bl, al
10049 ;; Reset CMOS shutdown status.
10050 mov AL, #0x0f
10051 out 0x70, AL ; select CMOS register Fh
10052 mov AL, #0x00
10053 out 0x71, AL ; set shutdown action to normal
10055 ;; Examine CMOS shutdown status.
10056 mov al, bl
10058 ;; 0x00, 0x09, 0x0D+ = normal startup
10059 cmp AL, #0x00
10060 jz normal_post
10061 cmp AL, #0x0d
10062 jae normal_post
10063 cmp AL, #0x09
10064 je normal_post
10066 ;; 0x05 = eoi + jmp via [0x40:0x67] jump
10067 cmp al, #0x05
10068 je eoi_jmp_post
10070 ;; Examine CMOS shutdown status.
10071 ;; 0x01,0x02,0x03,0x04,0x06,0x07,0x08, 0x0a, 0x0b, 0x0c = Unimplemented shutdown status.
10072 push bx
10073 call _shutdown_status_panic
10075 #if 0
10076 HALT(__LINE__)
10078 ;#if 0
10079 ; 0xb0, 0x20, /* mov al, #0x20 */
10080 ; 0xe6, 0x20, /* out 0x20, al ;send EOI to PIC */
10081 ;#endif
10083 pop es
10084 pop ds
10085 popa
10086 iret
10087 #endif
10089 normal_post:
10090 ; case 0: normal startup
10093 mov ax, #0xfffe
10094 mov sp, ax
10095 xor ax, ax
10096 mov ds, ax
10097 mov ss, ax
10099 ;; zero out BIOS data area (40:00..40:ff)
10100 mov es, ax
10101 mov cx, #0x0080 ;; 128 words
10102 mov di, #0x0400
10105 stosw
10107 call _log_bios_start
10109 ;; set all interrupts to default handler
10110 xor bx, bx ;; offset index
10111 mov cx, #0x0100 ;; counter (256 interrupts)
10112 mov ax, #dummy_iret_handler
10113 mov dx, #0xF000
10115 post_default_ints:
10116 mov [bx], ax
10117 add bx, 2
10118 mov [bx], dx
10119 add bx, 2
10120 loop post_default_ints
10122 ;; set vector 0x79 to zero
10123 ;; this is used by 'gardian angel' protection system
10124 SET_INT_VECTOR(0x79, #0, #0)
10126 ;; base memory in K 40:13 (word)
10127 mov ax, #BASE_MEM_IN_K
10128 mov 0x0413, ax
10131 ;; Manufacturing Test 40:12
10132 ;; zerod out above
10134 ;; Warm Boot Flag 0040:0072
10135 ;; value of 1234h = skip memory checks
10136 ;; zerod out above
10139 ;; Printer Services vector
10140 SET_INT_VECTOR(0x17, #0xF000, #int17_handler)
10142 ;; Bootstrap failure vector
10143 SET_INT_VECTOR(0x18, #0xF000, #int18_handler)
10145 ;; Bootstrap Loader vector
10146 SET_INT_VECTOR(0x19, #0xF000, #int19_handler)
10148 ;; User Timer Tick vector
10149 SET_INT_VECTOR(0x1c, #0xF000, #int1c_handler)
10151 ;; Memory Size Check vector
10152 SET_INT_VECTOR(0x12, #0xF000, #int12_handler)
10154 ;; Equipment Configuration Check vector
10155 SET_INT_VECTOR(0x11, #0xF000, #int11_handler)
10157 ;; System Services
10158 SET_INT_VECTOR(0x15, #0xF000, #int15_handler)
10160 ;; EBDA setup
10161 call ebda_post
10163 ;; PIT setup
10164 SET_INT_VECTOR(0x08, #0xF000, #int08_handler)
10165 ;; int 1C already points at dummy_iret_handler (above)
10166 mov al, #0x34 ; timer0: binary count, 16bit count, mode 2
10167 out 0x43, al
10168 mov al, #0x00 ; maximum count of 0000H = 18.2Hz
10169 out 0x40, al
10170 out 0x40, al
10172 ;; Keyboard
10173 SET_INT_VECTOR(0x09, #0xF000, #int09_handler)
10174 SET_INT_VECTOR(0x16, #0xF000, #int16_handler)
10176 xor ax, ax
10177 mov ds, ax
10178 mov 0x0417, al /* keyboard shift flags, set 1 */
10179 mov 0x0418, al /* keyboard shift flags, set 2 */
10180 mov 0x0419, al /* keyboard alt-numpad work area */
10181 mov 0x0471, al /* keyboard ctrl-break flag */
10182 mov 0x0497, al /* keyboard status flags 4 */
10183 mov al, #0x10
10184 mov 0x0496, al /* keyboard status flags 3 */
10187 /* keyboard head of buffer pointer */
10188 mov bx, #0x001E
10189 mov 0x041A, bx
10191 /* keyboard end of buffer pointer */
10192 mov 0x041C, bx
10194 /* keyboard pointer to start of buffer */
10195 mov bx, #0x001E
10196 mov 0x0480, bx
10198 /* keyboard pointer to end of buffer */
10199 mov bx, #0x003E
10200 mov 0x0482, bx
10202 /* init the keyboard */
10203 call _keyboard_init
10205 ;; mov CMOS Equipment Byte to BDA Equipment Word
10206 mov ax, 0x0410
10207 mov al, #0x14
10208 out 0x70, al
10209 in al, 0x71
10210 mov 0x0410, ax
10213 ;; Parallel setup
10214 SET_INT_VECTOR(0x0F, #0xF000, #dummy_iret_handler)
10215 xor ax, ax
10216 mov ds, ax
10217 xor bx, bx
10218 mov cl, #0x14 ; timeout value
10219 mov dx, #0x378 ; Parallel I/O address, port 1
10220 call detect_parport
10221 mov dx, #0x278 ; Parallel I/O address, port 2
10222 call detect_parport
10223 shl bx, #0x0e
10224 mov ax, 0x410 ; Equipment word bits 14..15 determing # parallel ports
10225 and ax, #0x3fff
10226 or ax, bx ; set number of parallel ports
10227 mov 0x410, ax
10229 ;; Serial setup
10230 SET_INT_VECTOR(0x0C, #0xF000, #dummy_iret_handler)
10231 SET_INT_VECTOR(0x14, #0xF000, #int14_handler)
10232 xor bx, bx
10233 mov cl, #0x0a ; timeout value
10234 mov dx, #0x03f8 ; Serial I/O address, port 1
10235 call detect_serial
10236 mov dx, #0x02f8 ; Serial I/O address, port 2
10237 call detect_serial
10238 mov dx, #0x03e8 ; Serial I/O address, port 3
10239 call detect_serial
10240 mov dx, #0x02e8 ; Serial I/O address, port 4
10241 call detect_serial
10242 shl bx, #0x09
10243 mov ax, 0x410 ; Equipment word bits 9..11 determing # serial ports
10244 and ax, #0xf1ff
10245 or ax, bx ; set number of serial port
10246 mov 0x410, ax
10248 ;; CMOS RTC
10249 SET_INT_VECTOR(0x1A, #0xF000, #int1a_handler)
10250 SET_INT_VECTOR(0x4A, #0xF000, #dummy_iret_handler)
10251 SET_INT_VECTOR(0x70, #0xF000, #int70_handler)
10252 ;; BIOS DATA AREA 0x4CE ???
10253 call timer_tick_post
10255 ;; PS/2 mouse setup
10256 SET_INT_VECTOR(0x74, #0xF000, #int74_handler)
10258 ;; IRQ13 (FPU exception) setup
10259 SET_INT_VECTOR(0x75, #0xF000, #int75_handler)
10261 ;; Video setup
10262 SET_INT_VECTOR(0x10, #0xF000, #int10_handler)
10264 ;; PIC
10265 mov al, #0x11 ; send initialisation commands
10266 out 0x20, al
10267 out 0xa0, al
10268 mov al, #0x08
10269 out 0x21, al
10270 mov al, #0x70
10271 out 0xa1, al
10272 mov al, #0x04
10273 out 0x21, al
10274 mov al, #0x02
10275 out 0xa1, al
10276 mov al, #0x01
10277 out 0x21, al
10278 out 0xa1, al
10279 mov al, #0xb8
10280 out 0x21, AL ;master pic: unmask IRQ 0, 1, 2, 6
10281 #if BX_USE_PS2_MOUSE
10282 mov al, #0x8f
10283 #else
10284 mov al, #0x9f
10285 #endif
10286 out 0xa1, AL ;slave pic: unmask IRQ 12, 13, 14
10288 #if BX_ROMBIOS32
10289 call rombios32_init
10290 #else
10291 #if BX_PCIBIOS
10292 call pcibios_init_iomem_bases
10293 call pcibios_init_irqs
10294 #endif //BX_PCIBIOS
10295 #endif
10297 call _init_boot_vectors
10299 call rom_scan
10301 call _print_bios_banner
10304 ;; Floppy setup
10306 call floppy_drive_post
10308 #if BX_USE_ATADRV
10311 ;; Hard Drive setup
10313 call hard_drive_post
10316 ;; ATA/ATAPI driver setup
10318 call _ata_init
10319 call _ata_detect
10321 #else // BX_USE_ATADRV
10324 ;; Hard Drive setup
10326 call hard_drive_post
10328 #endif // BX_USE_ATADRV
10330 #if BX_ELTORITO_BOOT
10332 ;; eltorito floppy/harddisk emulation from cd
10334 call _cdemu_init
10336 #endif // BX_ELTORITO_BOOT
10338 sti ;; enable interrupts
10339 int #0x19
10342 .org 0xe2c3 ; NMI Handler Entry Point
10343 nmi:
10344 ;; FIXME the NMI handler should not panic
10345 ;; but iret when called from int75 (fpu exception)
10346 call _nmi_handler_msg
10347 iret
10349 int75_handler:
10350 out 0xf0, al // clear irq13
10351 call eoi_both_pics // clear interrupt
10352 int 2 // legacy nmi call
10353 iret
10355 ;-------------------------------------------
10356 ;- INT 13h Fixed Disk Services Entry Point -
10357 ;-------------------------------------------
10358 .org 0xe3fe ; INT 13h Fixed Disk Services Entry Point
10359 int13_handler:
10360 //JMPL(int13_relocated)
10361 jmp int13_relocated
10363 .org 0xe401 ; Fixed Disk Parameter Table
10365 ;----------
10366 ;- INT19h -
10367 ;----------
10368 .org 0xe6f2 ; INT 19h Boot Load Service Entry Point
10369 int19_handler:
10371 jmp int19_relocated
10372 ;-------------------------------------------
10373 ;- System BIOS Configuration Data Table
10374 ;-------------------------------------------
10375 .org BIOS_CONFIG_TABLE
10376 db 0x08 ; Table size (bytes) -Lo
10377 db 0x00 ; Table size (bytes) -Hi
10378 db SYS_MODEL_ID
10379 db SYS_SUBMODEL_ID
10380 db BIOS_REVISION
10381 ; Feature byte 1
10382 ; b7: 1=DMA channel 3 used by hard disk
10383 ; b6: 1=2 interrupt controllers present
10384 ; b5: 1=RTC present
10385 ; b4: 1=BIOS calls int 15h/4Fh every key
10386 ; b3: 1=wait for extern event supported (Int 15h/41h)
10387 ; b2: 1=extended BIOS data area used
10388 ; b1: 0=AT or ESDI bus, 1=MicroChannel
10389 ; b0: 1=Dual bus (MicroChannel + ISA)
10390 db (0 << 7) | \
10391 (1 << 6) | \
10392 (1 << 5) | \
10393 (BX_CALL_INT15_4F << 4) | \
10394 (0 << 3) | \
10395 (BX_USE_EBDA << 2) | \
10396 (0 << 1) | \
10397 (0 << 0)
10398 ; Feature byte 2
10399 ; b7: 1=32-bit DMA supported
10400 ; b6: 1=int16h, function 9 supported
10401 ; b5: 1=int15h/C6h (get POS data) supported
10402 ; b4: 1=int15h/C7h (get mem map info) supported
10403 ; b3: 1=int15h/C8h (en/dis CPU) supported
10404 ; b2: 1=non-8042 kb controller
10405 ; b1: 1=data streaming supported
10406 ; b0: reserved
10407 db (0 << 7) | \
10408 (1 << 6) | \
10409 (0 << 5) | \
10410 (0 << 4) | \
10411 (0 << 3) | \
10412 (0 << 2) | \
10413 (0 << 1) | \
10414 (0 << 0)
10415 ; Feature byte 3
10416 ; b7: not used
10417 ; b6: reserved
10418 ; b5: reserved
10419 ; b4: POST supports ROM-to-RAM enable/disable
10420 ; b3: SCSI on system board
10421 ; b2: info panel installed
10422 ; b1: Initial Machine Load (IML) system - BIOS on disk
10423 ; b0: SCSI supported in IML
10424 db 0x00
10425 ; Feature byte 4
10426 ; b7: IBM private
10427 ; b6: EEPROM present
10428 ; b5-3: ABIOS presence (011 = not supported)
10429 ; b2: private
10430 ; b1: memory split above 16Mb supported
10431 ; b0: POSTEXT directly supported by POST
10432 db 0x00
10433 ; Feature byte 5 (IBM)
10434 ; b1: enhanced mouse
10435 ; b0: flash EPROM
10436 db 0x00
10440 .org 0xe729 ; Baud Rate Generator Table
10442 ;----------
10443 ;- INT14h -
10444 ;----------
10445 .org 0xe739 ; INT 14h Serial Communications Service Entry Point
10446 int14_handler:
10447 push ds
10448 pusha
10449 xor ax, ax
10450 mov ds, ax
10451 call _int14_function
10452 popa
10453 pop ds
10454 iret
10457 ;----------------------------------------
10458 ;- INT 16h Keyboard Service Entry Point -
10459 ;----------------------------------------
10460 .org 0xe82e
10461 int16_handler:
10464 push ds
10465 pushf
10466 pusha
10468 cmp ah, #0x00
10469 je int16_F00
10470 cmp ah, #0x10
10471 je int16_F00
10473 mov bx, #0xf000
10474 mov ds, bx
10475 call _int16_function
10476 popa
10477 popf
10478 pop ds
10479 jz int16_zero_set
10481 int16_zero_clear:
10482 push bp
10483 mov bp, sp
10484 //SEG SS
10485 and BYTE [bp + 0x06], #0xbf
10486 pop bp
10487 iret
10489 int16_zero_set:
10490 push bp
10491 mov bp, sp
10492 //SEG SS
10493 or BYTE [bp + 0x06], #0x40
10494 pop bp
10495 iret
10497 int16_F00:
10498 mov bx, #0x0040
10499 mov ds, bx
10501 int16_wait_for_key:
10503 mov bx, 0x001a
10504 cmp bx, 0x001c
10505 jne int16_key_found
10508 #if 0
10509 /* no key yet, call int 15h, function AX=9002 */
10510 0x50, /* push AX */
10511 0xb8, 0x02, 0x90, /* mov AX, #0x9002 */
10512 0xcd, 0x15, /* int 15h */
10513 0x58, /* pop AX */
10514 0xeb, 0xea, /* jmp WAIT_FOR_KEY */
10515 #endif
10516 jmp int16_wait_for_key
10518 int16_key_found:
10519 mov bx, #0xf000
10520 mov ds, bx
10521 call _int16_function
10522 popa
10523 popf
10524 pop ds
10525 #if 0
10526 /* notify int16 complete w/ int 15h, function AX=9102 */
10527 0x50, /* push AX */
10528 0xb8, 0x02, 0x91, /* mov AX, #0x9102 */
10529 0xcd, 0x15, /* int 15h */
10530 0x58, /* pop AX */
10531 #endif
10532 iret
10536 ;-------------------------------------------------
10537 ;- INT09h : Keyboard Hardware Service Entry Point -
10538 ;-------------------------------------------------
10539 .org 0xe987
10540 int09_handler:
10542 push ax
10544 mov al, #0xAD ;;disable keyboard
10545 out #0x64, al
10547 mov al, #0x0B
10548 out #0x20, al
10549 in al, #0x20
10550 and al, #0x02
10551 jz int09_finish
10553 in al, #0x60 ;;read key from keyboard controller
10555 push ds
10556 pusha
10557 #ifdef BX_CALL_INT15_4F
10558 mov ah, #0x4f ;; allow for keyboard intercept
10560 int #0x15
10561 jnc int09_done
10562 #endif
10564 ;; check for extended key
10565 cmp al, #0xe0
10566 jne int09_check_pause
10567 xor ax, ax
10568 mov ds, ax
10569 mov al, BYTE [0x496] ;; mf2_state |= 0x02
10570 or al, #0x02
10571 mov BYTE [0x496], al
10572 jmp int09_done
10574 int09_check_pause: ;; check for pause key
10575 cmp al, #0xe1
10576 jne int09_process_key
10577 xor ax, ax
10578 mov ds, ax
10579 mov al, BYTE [0x496] ;; mf2_state |= 0x01
10580 or al, #0x01
10581 mov BYTE [0x496], al
10582 jmp int09_done
10584 int09_process_key:
10585 mov bx, #0xf000
10586 mov ds, bx
10587 call _int09_function
10589 int09_done:
10590 popa
10591 pop ds
10593 call eoi_master_pic
10595 int09_finish:
10596 mov al, #0xAE ;;enable keyboard
10597 out #0x64, al
10598 pop ax
10599 iret
10602 ;----------------------------------------
10603 ;- INT 13h Diskette Service Entry Point -
10604 ;----------------------------------------
10605 .org 0xec59
10606 int13_diskette:
10607 jmp int13_noeltorito
10609 ;---------------------------------------------
10610 ;- INT 0Eh Diskette Hardware ISR Entry Point -
10611 ;---------------------------------------------
10612 .org 0xef57 ; INT 0Eh Diskette Hardware ISR Entry Point
10613 int0e_handler:
10614 push ax
10615 push dx
10616 mov dx, #0x03f4
10617 in al, dx
10618 and al, #0xc0
10619 cmp al, #0xc0
10620 je int0e_normal
10621 mov dx, #0x03f5
10622 mov al, #0x08 ; sense interrupt status
10623 out dx, al
10624 int0e_loop1:
10625 mov dx, #0x03f4
10626 in al, dx
10627 and al, #0xc0
10628 cmp al, #0xc0
10629 jne int0e_loop1
10630 int0e_loop2:
10631 mov dx, #0x03f5
10632 in al, dx
10633 mov dx, #0x03f4
10634 in al, dx
10635 and al, #0xc0
10636 cmp al, #0xc0
10637 je int0e_loop2
10638 int0e_normal:
10639 push ds
10640 xor ax, ax ;; segment 0000
10641 mov ds, ax
10642 call eoi_master_pic
10643 mov al, 0x043e
10644 or al, #0x80 ;; diskette interrupt has occurred
10645 mov 0x043e, al
10646 pop ds
10647 pop dx
10648 pop ax
10649 iret
10652 .org 0xefc7 ; Diskette Controller Parameter Table
10653 diskette_param_table:
10654 ;; Since no provisions are made for multiple drive types, most
10655 ;; values in this table are ignored. I set parameters for 1.44M
10656 ;; floppy here
10657 db 0xAF
10658 db 0x02 ;; head load time 0000001, DMA used
10659 db 0x25
10660 db 0x02
10661 db 18
10662 db 0x1B
10663 db 0xFF
10664 db 0x6C
10665 db 0xF6
10666 db 0x0F
10667 db 0x08
10670 ;----------------------------------------
10671 ;- INT17h : Printer Service Entry Point -
10672 ;----------------------------------------
10673 .org 0xefd2
10674 int17_handler:
10675 push ds
10676 pusha
10677 xor ax, ax
10678 mov ds, ax
10679 call _int17_function
10680 popa
10681 pop ds
10682 iret
10684 diskette_param_table2:
10685 ;; New diskette parameter table adding 3 parameters from IBM
10686 ;; Since no provisions are made for multiple drive types, most
10687 ;; values in this table are ignored. I set parameters for 1.44M
10688 ;; floppy here
10689 db 0xAF
10690 db 0x02 ;; head load time 0000001, DMA used
10691 db 0x25
10692 db 0x02
10693 db 18
10694 db 0x1B
10695 db 0xFF
10696 db 0x6C
10697 db 0xF6
10698 db 0x0F
10699 db 0x08
10700 db 79 ;; maximum track
10701 db 0 ;; data transfer rate
10702 db 4 ;; drive type in cmos
10704 .org 0xf045 ; INT 10 Functions 0-Fh Entry Point
10705 HALT(__LINE__)
10706 iret
10708 ;----------
10709 ;- INT10h -
10710 ;----------
10711 .org 0xf065 ; INT 10h Video Support Service Entry Point
10712 int10_handler:
10713 ;; dont do anything, since the VGA BIOS handles int10h requests
10714 iret
10716 .org 0xf0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
10718 ;----------
10719 ;- INT12h -
10720 ;----------
10721 .org 0xf841 ; INT 12h Memory Size Service Entry Point
10722 ; ??? different for Pentium (machine check)?
10723 int12_handler:
10724 push ds
10725 mov ax, #0x0040
10726 mov ds, ax
10727 mov ax, 0x0013
10728 pop ds
10729 iret
10731 ;----------
10732 ;- INT11h -
10733 ;----------
10734 .org 0xf84d ; INT 11h Equipment List Service Entry Point
10735 int11_handler:
10736 push ds
10737 mov ax, #0x0040
10738 mov ds, ax
10739 mov ax, 0x0010
10740 pop ds
10741 iret
10743 ;----------
10744 ;- INT15h -
10745 ;----------
10746 .org 0xf859 ; INT 15h System Services Entry Point
10747 int15_handler:
10748 pushf
10749 #if BX_APM
10750 cmp ah, #0x53
10751 je apm_call
10752 #endif
10753 push ds
10754 push es
10755 cmp ah, #0x86
10756 je int15_handler32
10757 cmp ah, #0xE8
10758 je int15_handler32
10759 pusha
10760 #if BX_USE_PS2_MOUSE
10761 cmp ah, #0xC2
10762 je int15_handler_mouse
10763 #endif
10764 call _int15_function
10765 int15_handler_mouse_ret:
10766 popa
10767 int15_handler32_ret:
10768 pop es
10769 pop ds
10770 popf
10771 jmp iret_modify_cf
10772 #if BX_APM
10773 apm_call:
10774 jmp _apmreal_entry
10775 #endif
10777 #if BX_USE_PS2_MOUSE
10778 int15_handler_mouse:
10779 call _int15_function_mouse
10780 jmp int15_handler_mouse_ret
10781 #endif
10783 int15_handler32:
10784 pushad
10785 call _int15_function32
10786 popad
10787 jmp int15_handler32_ret
10789 ;; Protected mode IDT descriptor
10791 ;; I just make the limit 0, so the machine will shutdown
10792 ;; if an exception occurs during protected mode memory
10793 ;; transfers.
10795 ;; Set base to f0000 to correspond to beginning of BIOS,
10796 ;; in case I actually define an IDT later
10797 ;; Set limit to 0
10799 pmode_IDT_info:
10800 dw 0x0000 ;; limit 15:00
10801 dw 0x0000 ;; base 15:00
10802 db 0x0f ;; base 23:16
10804 ;; Real mode IDT descriptor
10806 ;; Set to typical real-mode values.
10807 ;; base = 000000
10808 ;; limit = 03ff
10810 rmode_IDT_info:
10811 dw 0x03ff ;; limit 15:00
10812 dw 0x0000 ;; base 15:00
10813 db 0x00 ;; base 23:16
10816 ;----------
10817 ;- INT1Ah -
10818 ;----------
10819 .org 0xfe6e ; INT 1Ah Time-of-day Service Entry Point
10820 int1a_handler:
10821 #if BX_PCIBIOS
10822 cmp ah, #0xb1
10823 jne int1a_normal
10824 call pcibios_real
10825 jc pcibios_error
10826 retf 2
10827 pcibios_error:
10828 mov bl, ah
10829 mov ah, #0xb1
10830 push ds
10831 pusha
10832 mov ax, ss ; set readable descriptor to ds, for calling pcibios
10833 mov ds, ax ; on 16bit protected mode.
10834 jmp int1a_callfunction
10835 int1a_normal:
10836 #endif
10837 push ds
10838 pusha
10839 xor ax, ax
10840 mov ds, ax
10841 int1a_callfunction:
10842 call _int1a_function
10843 popa
10844 pop ds
10845 iret
10848 ;; int70h: IRQ8 - CMOS RTC
10850 int70_handler:
10851 push ds
10852 pushad
10853 xor ax, ax
10854 mov ds, ax
10855 call _int70_function
10856 popad
10857 pop ds
10858 iret
10860 ;---------
10861 ;- INT08 -
10862 ;---------
10863 .org 0xfea5 ; INT 08h System Timer ISR Entry Point
10864 int08_handler:
10866 push eax
10867 push ds
10868 xor ax, ax
10869 mov ds, ax
10871 ;; time to turn off drive(s)?
10872 mov al,0x0440
10873 or al,al
10874 jz int08_floppy_off
10875 dec al
10876 mov 0x0440,al
10877 jnz int08_floppy_off
10878 ;; turn motor(s) off
10879 push dx
10880 mov dx,#0x03f2
10881 in al,dx
10882 and al,#0xcf
10883 out dx,al
10884 pop dx
10885 int08_floppy_off:
10887 mov eax, 0x046c ;; get ticks dword
10888 inc eax
10890 ;; compare eax to one days worth of timer ticks at 18.2 hz
10891 cmp eax, #0x001800B0
10892 jb int08_store_ticks
10893 ;; there has been a midnight rollover at this point
10894 xor eax, eax ;; zero out counter
10895 inc BYTE 0x0470 ;; increment rollover flag
10897 int08_store_ticks:
10898 mov 0x046c, eax ;; store new ticks dword
10899 ;; chain to user timer tick INT #0x1c
10900 //pushf
10901 //;; call_ep [ds:loc]
10902 //CALL_EP( 0x1c << 2 )
10903 int #0x1c
10905 call eoi_master_pic
10906 pop ds
10907 pop eax
10908 iret
10910 .org 0xfef3 ; Initial Interrupt Vector Offsets Loaded by POST
10913 .org 0xff00
10914 .ascii BIOS_COPYRIGHT_STRING
10916 ;------------------------------------------------
10917 ;- IRET Instruction for Dummy Interrupt Handler -
10918 ;------------------------------------------------
10919 .org 0xff53 ; IRET Instruction for Dummy Interrupt Handler
10920 dummy_iret_handler:
10921 iret
10923 .org 0xff54 ; INT 05h Print Screen Service Entry Point
10924 HALT(__LINE__)
10925 iret
10927 .org 0xfff0 ; Power-up Entry Point
10928 jmp 0xf000:post
10930 .org 0xfff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
10931 .ascii BIOS_BUILD_DATE
10933 .org 0xfffe ; System Model ID
10934 db SYS_MODEL_ID
10935 db 0x00 ; filler
10937 .org 0xfa6e ;; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
10938 ASM_END
10940 * This font comes from the fntcol16.zip package (c) by Joseph Gil
10941 * found at ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip
10942 * This font is public domain
10944 static Bit8u vgafont8[128*8]=
10946 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
10947 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e,
10948 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e,
10949 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00,
10950 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00,
10951 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c,
10952 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c,
10953 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00,
10954 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff,
10955 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
10956 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff,
10957 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78,
10958 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18,
10959 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0,
10960 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0,
10961 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99,
10962 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00,
10963 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00,
10964 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18,
10965 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
10966 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00,
10967 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78,
10968 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00,
10969 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff,
10970 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00,
10971 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
10972 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00,
10973 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
10974 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00,
10975 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00,
10976 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00,
10977 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00,
10978 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
10979 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00,
10980 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
10981 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00,
10982 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00,
10983 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00,
10984 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00,
10985 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
10986 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00,
10987 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
10988 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
10989 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00,
10990 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60,
10991 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
10992 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00,
10993 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00,
10994 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00,
10995 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00,
10996 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00,
10997 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00,
10998 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00,
10999 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00,
11000 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00,
11001 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00,
11002 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00,
11003 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00,
11004 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00,
11005 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60,
11006 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00,
11007 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00,
11008 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00,
11009 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00,
11010 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00,
11011 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00,
11012 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00,
11013 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00,
11014 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00,
11015 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00,
11016 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00,
11017 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00,
11018 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00,
11019 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
11020 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00,
11021 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00,
11022 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00,
11023 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00,
11024 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00,
11025 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00,
11026 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
11027 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00,
11028 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00,
11029 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00,
11030 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
11031 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00,
11032 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
11033 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00,
11034 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00,
11035 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00,
11036 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00,
11037 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
11038 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00,
11039 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
11040 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
11041 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
11042 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
11043 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00,
11044 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00,
11045 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00,
11046 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00,
11047 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
11048 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00,
11049 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
11050 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00,
11051 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
11052 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78,
11053 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00,
11054 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
11055 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00,
11056 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
11057 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
11058 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0,
11059 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e,
11060 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00,
11061 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00,
11062 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00,
11063 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
11064 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
11065 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00,
11066 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00,
11067 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
11068 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00,
11069 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00,
11070 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
11071 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00,
11072 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
11073 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00,
11076 ASM_START
11077 .org 0xcc00
11078 // bcc-generated data will be placed here
11079 ASM_END