- disabled rombios32 call until the critical bugs are fixed
[gplbios.git] / rombios.c
blobbdc93d92941b20e62a1a4b59cc9801adc1f85122
1 /////////////////////////////////////////////////////////////////////////
2 // $Id$
3 /////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (C) 2002 MandrakeSoft S.A.
6 //
7 // MandrakeSoft S.A.
8 // 43, rue d'Aboukir
9 // 75002 Paris - France
10 // http://www.linux-mandrake.com/
11 // http://www.mandrakesoft.com/
13 // This library is free software; you can redistribute it and/or
14 // modify it under the terms of the GNU Lesser General Public
15 // License as published by the Free Software Foundation; either
16 // version 2 of the License, or (at your option) any later version.
18 // This library is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 // Lesser General Public License for more details.
23 // You should have received a copy of the GNU Lesser General Public
24 // License along with this library; if not, write to the Free Software
25 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27 // ROM BIOS for use with Bochs/Plex86/QEMU emulation environment
30 // ROM BIOS compatability entry points:
31 // ===================================
32 // $e05b ; POST Entry Point
33 // $e2c3 ; NMI Handler Entry Point
34 // $e3fe ; INT 13h Fixed Disk Services Entry Point
35 // $e401 ; Fixed Disk Parameter Table
36 // $e6f2 ; INT 19h Boot Load Service Entry Point
37 // $e6f5 ; Configuration Data Table
38 // $e729 ; Baud Rate Generator Table
39 // $e739 ; INT 14h Serial Communications Service Entry Point
40 // $e82e ; INT 16h Keyboard Service Entry Point
41 // $e987 ; INT 09h Keyboard Service Entry Point
42 // $ec59 ; INT 13h Diskette Service Entry Point
43 // $ef57 ; INT 0Eh Diskette Hardware ISR Entry Point
44 // $efc7 ; Diskette Controller Parameter Table
45 // $efd2 ; INT 17h Printer Service Entry Point
46 // $f045 ; INT 10 Functions 0-Fh Entry Point
47 // $f065 ; INT 10h Video Support Service Entry Point
48 // $f0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
49 // $f841 ; INT 12h Memory Size Service Entry Point
50 // $f84d ; INT 11h Equipment List Service Entry Point
51 // $f859 ; INT 15h System Services Entry Point
52 // $fa6e ; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
53 // $fe6e ; INT 1Ah Time-of-day Service Entry Point
54 // $fea5 ; INT 08h System Timer ISR Entry Point
55 // $fef3 ; Initial Interrupt Vector Offsets Loaded by POST
56 // $ff53 ; IRET Instruction for Dummy Interrupt Handler
57 // $ff54 ; INT 05h Print Screen Service Entry Point
58 // $fff0 ; Power-up Entry Point
59 // $fff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
60 // $fffe ; System Model ID
62 // NOTES for ATA/ATAPI driver (cbbochs@free.fr)
63 // Features
64 // - supports up to 4 ATA interfaces
65 // - device/geometry detection
66 // - 16bits/32bits device access
67 // - pchs/lba access
68 // - datain/dataout/packet command support
70 // NOTES for El-Torito Boot (cbbochs@free.fr)
71 // - CD-ROM booting is only available if ATA/ATAPI Driver is available
72 // - Current code is only able to boot mono-session cds
73 // - Current code can not boot and emulate a hard-disk
74 // the bios will panic otherwise
75 // - Current code also use memory in EBDA segement.
76 // - I used cmos byte 0x3D to store extended information on boot-device
77 // - Code has to be modified modified to handle multiple cdrom drives
78 // - Here are the cdrom boot failure codes:
79 // 1 : no atapi device found
80 // 2 : no atapi cdrom found
81 // 3 : can not read cd - BRVD
82 // 4 : cd is not eltorito (BRVD)
83 // 5 : cd is not eltorito (ISO TAG)
84 // 6 : cd is not eltorito (ELTORITO TAG)
85 // 7 : can not read cd - boot catalog
86 // 8 : boot catalog : bad header
87 // 9 : boot catalog : bad platform
88 // 10 : boot catalog : bad signature
89 // 11 : boot catalog : bootable flag not set
90 // 12 : can not read cd - boot image
92 // ATA driver
93 // - EBDA segment.
94 // I used memory starting at 0x121 in the segment
95 // - the translation policy is defined in cmos regs 0x39 & 0x3a
97 // TODO :
99 // int74
100 // - needs to be reworked. Uses direct [bp] offsets. (?)
102 // int13:
103 // - f04 (verify sectors) isn't complete (?)
104 // - f02/03/04 should set current cyl,etc in BDA (?)
105 // - rewrite int13_relocated & clean up int13 entry code
107 // NOTES:
108 // - NMI access (bit7 of addr written to 70h)
110 // ATA driver
111 // - should handle the "don't detect" bit (cmos regs 0x3b & 0x3c)
112 // - could send the multiple-sector read/write commands
114 // El-Torito
115 // - Emulate a Hard-disk (currently only diskette can be emulated) see "FIXME ElTorito Harddisk"
116 // - Implement remaining int13_cdemu functions (as defined by El-Torito specs)
117 // - cdrom drive is hardcoded to ide 0 device 1 in several places. see "FIXME ElTorito Hardcoded"
118 // - int13 Fix DL when emulating a cd. In that case DL is decremented before calling real int13.
119 // This is ok. But DL should be reincremented afterwards.
120 // - Fix all "FIXME ElTorito Various"
121 // - should be able to boot any cdrom instead of the first one
123 // BCC Bug: find a generic way to handle the bug of #asm after an "if" (fixed in 0.16.7)
125 #define DEBUG_ROMBIOS 0
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
146 #define BX_ROMBIOS32 0
148 #define BX_USE_ATADRV 1
149 #define BX_ELTORITO_BOOT 1
151 #define BX_MAX_ATA_INTERFACES 4
152 #define BX_MAX_ATA_DEVICES (BX_MAX_ATA_INTERFACES*2)
154 #define BX_VIRTUAL_PORTS 1 /* normal output to Bochs ports */
155 #define BX_DEBUG_SERIAL 0 /* output to COM1 */
157 /* model byte 0xFC = AT */
158 #define SYS_MODEL_ID 0xFC
159 #define SYS_SUBMODEL_ID 0x00
160 #define BIOS_REVISION 1
161 #define BIOS_CONFIG_TABLE 0xe6f5
163 /* define it to include QEMU specific code */
164 //#define BX_QEMU
166 #ifndef BIOS_BUILD_DATE
167 # define BIOS_BUILD_DATE "06/23/99"
168 #endif
170 // 1K of base memory used for Extended Bios Data Area (EBDA)
171 // EBDA is used for PS/2 mouse support, and IDE BIOS, etc.
172 #define EBDA_SEG 0x9FC0
173 #define EBDA_SIZE 1 // In KiB
174 #define BASE_MEM_IN_K (640 - EBDA_SIZE)
176 // Define the application NAME
177 #if defined(BX_QEMU)
178 # define BX_APPNAME "QEMU"
179 #elif defined(PLEX86)
180 # define BX_APPNAME "Plex86"
181 #else
182 # define BX_APPNAME "Bochs"
183 #endif
185 // Sanity Checks
186 #if BX_USE_ATADRV && BX_CPU<3
187 # error The ATA/ATAPI Driver can only to be used with a 386+ cpu
188 #endif
189 #if BX_USE_ATADRV && !BX_USE_EBDA
190 # error ATA/ATAPI Driver can only be used if EBDA is available
191 #endif
192 #if BX_ELTORITO_BOOT && !BX_USE_ATADRV
193 # error El-Torito Boot can only be use if ATA/ATAPI Driver is available
194 #endif
195 #if BX_PCIBIOS && BX_CPU<3
196 # error PCI BIOS can only be used with 386+ cpu
197 #endif
198 #if BX_APM && BX_CPU<3
199 # error APM BIOS can only be used with 386+ cpu
200 #endif
202 #define PANIC_PORT 0x400
203 #define PANIC_PORT2 0x401
204 #define INFO_PORT 0x402
205 #define DEBUG_PORT 0x403
207 // define this if you want to make PCIBIOS working on a specific bridge only
208 // undef enables PCIBIOS when at least one PCI device is found
209 // i440FX is emulated by Bochs and QEMU
210 #define PCI_FIXED_HOST_BRIDGE 0x12378086 ;; i440FX PCI bridge
212 // #20 is dec 20
213 // #$20 is hex 20 = 32
214 // #0x20 is hex 20 = 32
215 // LDA #$20
216 // JSR $E820
217 // LDD .i,S
218 // JSR $C682
219 // mov al, #$20
221 // all hex literals should be prefixed with '0x'
222 // grep "#[0-9a-fA-F][0-9a-fA-F]" rombios.c
223 // no mov SEG-REG, #value, must mov register into seg-reg
224 // grep -i "mov[ ]*.s" rombios.c
226 // This is for compiling with gcc2 and gcc3
227 #define ASM_START #asm
228 #define ASM_END #endasm
230 ASM_START
231 .rom
233 .org 0x0000
235 #if BX_CPU >= 3
236 use16 386
237 #else
238 use16 286
239 #endif
241 MACRO HALT
242 ;; the HALT macro is called with the line number of the HALT call.
243 ;; The line number is then sent to the PANIC_PORT, causing Bochs/Plex
244 ;; to print a BX_PANIC message. This will normally halt the simulation
245 ;; with a message such as "BIOS panic at rombios.c, line 4091".
246 ;; However, users can choose to make panics non-fatal and continue.
247 #if BX_VIRTUAL_PORTS
248 mov dx,#PANIC_PORT
249 mov ax,#?1
250 out dx,ax
251 #else
252 mov dx,#0x80
253 mov ax,#?1
254 out dx,al
255 #endif
256 MEND
258 MACRO JMP_AP
259 db 0xea
260 dw ?2
261 dw ?1
262 MEND
264 MACRO SET_INT_VECTOR
265 mov ax, ?3
266 mov ?1*4, ax
267 mov ax, ?2
268 mov ?1*4+2, ax
269 MEND
271 ASM_END
273 typedef unsigned char Bit8u;
274 typedef unsigned short Bit16u;
275 typedef unsigned short bx_bool;
276 typedef unsigned long Bit32u;
278 #if BX_USE_ATADRV
280 void memsetb(seg,offset,value,count);
281 void memcpyb(dseg,doffset,sseg,soffset,count);
282 void memcpyd(dseg,doffset,sseg,soffset,count);
284 // memset of count bytes
285 void
286 memsetb(seg,offset,value,count)
287 Bit16u seg;
288 Bit16u offset;
289 Bit16u value;
290 Bit16u count;
292 ASM_START
293 push bp
294 mov bp, sp
296 push ax
297 push cx
298 push es
299 push di
301 mov cx, 10[bp] ; count
302 cmp cx, #0x00
303 je memsetb_end
304 mov ax, 4[bp] ; segment
305 mov es, ax
306 mov ax, 6[bp] ; offset
307 mov di, ax
308 mov al, 8[bp] ; value
311 stosb
313 memsetb_end:
314 pop di
315 pop es
316 pop cx
317 pop ax
319 pop bp
320 ASM_END
323 #if 0
324 // memcpy of count bytes
325 void
326 memcpyb(dseg,doffset,sseg,soffset,count)
327 Bit16u dseg;
328 Bit16u doffset;
329 Bit16u sseg;
330 Bit16u soffset;
331 Bit16u count;
333 ASM_START
334 push bp
335 mov bp, sp
337 push ax
338 push cx
339 push es
340 push di
341 push ds
342 push si
344 mov cx, 12[bp] ; count
345 cmp cx, #0x0000
346 je memcpyb_end
347 mov ax, 4[bp] ; dsegment
348 mov es, ax
349 mov ax, 6[bp] ; doffset
350 mov di, ax
351 mov ax, 8[bp] ; ssegment
352 mov ds, ax
353 mov ax, 10[bp] ; soffset
354 mov si, ax
357 movsb
359 memcpyb_end:
360 pop si
361 pop ds
362 pop di
363 pop es
364 pop cx
365 pop ax
367 pop bp
368 ASM_END
371 // memcpy of count dword
372 void
373 memcpyd(dseg,doffset,sseg,soffset,count)
374 Bit16u dseg;
375 Bit16u doffset;
376 Bit16u sseg;
377 Bit16u soffset;
378 Bit16u count;
380 ASM_START
381 push bp
382 mov bp, sp
384 push ax
385 push cx
386 push es
387 push di
388 push ds
389 push si
391 mov cx, 12[bp] ; count
392 cmp cx, #0x0000
393 je memcpyd_end
394 mov ax, 4[bp] ; dsegment
395 mov es, ax
396 mov ax, 6[bp] ; doffset
397 mov di, ax
398 mov ax, 8[bp] ; ssegment
399 mov ds, ax
400 mov ax, 10[bp] ; soffset
401 mov si, ax
404 movsd
406 memcpyd_end:
407 pop si
408 pop ds
409 pop di
410 pop es
411 pop cx
412 pop ax
414 pop bp
415 ASM_END
417 #endif
418 #endif //BX_USE_ATADRV
420 // read_dword and write_dword functions
421 static Bit32u read_dword();
422 static void write_dword();
424 Bit32u
425 read_dword(seg, offset)
426 Bit16u seg;
427 Bit16u offset;
429 ASM_START
430 push bp
431 mov bp, sp
433 push bx
434 push ds
435 mov ax, 4[bp] ; segment
436 mov ds, ax
437 mov bx, 6[bp] ; offset
438 mov ax, [bx]
439 inc bx
440 inc bx
441 mov dx, [bx]
442 ;; ax = return value (word)
443 ;; dx = return value (word)
444 pop ds
445 pop bx
447 pop bp
448 ASM_END
451 void
452 write_dword(seg, offset, data)
453 Bit16u seg;
454 Bit16u offset;
455 Bit32u data;
457 ASM_START
458 push bp
459 mov bp, sp
461 push ax
462 push bx
463 push ds
464 mov ax, 4[bp] ; segment
465 mov ds, ax
466 mov bx, 6[bp] ; offset
467 mov ax, 8[bp] ; data word
468 mov [bx], ax ; write data word
469 inc bx
470 inc bx
471 mov ax, 10[bp] ; data word
472 mov [bx], ax ; write data word
473 pop ds
474 pop bx
475 pop ax
477 pop bp
478 ASM_END
481 // Bit32u (unsigned long) and long helper functions
482 ASM_START
484 ;; and function
485 landl:
486 landul:
487 SEG SS
488 and ax,[di]
489 SEG SS
490 and bx,2[di]
493 ;; add function
494 laddl:
495 laddul:
496 SEG SS
497 add ax,[di]
498 SEG SS
499 adc bx,2[di]
502 ;; cmp function
503 lcmpl:
504 lcmpul:
505 and eax, #0x0000FFFF
506 shl ebx, #16
507 add eax, ebx
508 shr ebx, #16
509 SEG SS
510 cmp eax, dword ptr [di]
513 ;; sub function
514 lsubl:
515 lsubul:
516 SEG SS
517 sub ax,[di]
518 SEG SS
519 sbb bx,2[di]
522 ;; mul function
523 lmull:
524 lmulul:
525 and eax, #0x0000FFFF
526 shl ebx, #16
527 add eax, ebx
528 SEG SS
529 mul eax, dword ptr [di]
530 mov ebx, eax
531 shr ebx, #16
534 ;; dec function
535 ldecl:
536 ldecul:
537 SEG SS
538 dec dword ptr [bx]
541 ;; or function
542 lorl:
543 lorul:
544 SEG SS
545 or ax,[di]
546 SEG SS
547 or bx,2[di]
550 ;; inc function
551 lincl:
552 lincul:
553 SEG SS
554 inc dword ptr [bx]
557 ;; tst function
558 ltstl:
559 ltstul:
560 and eax, #0x0000FFFF
561 shl ebx, #16
562 add eax, ebx
563 shr ebx, #16
564 test eax, eax
567 ;; sr function
568 lsrul:
569 mov cx,di
570 jcxz lsr_exit
571 and eax, #0x0000FFFF
572 shl ebx, #16
573 add eax, ebx
574 lsr_loop:
575 shr eax, #1
576 loop lsr_loop
577 mov ebx, eax
578 shr ebx, #16
579 lsr_exit:
582 ;; sl function
583 lsll:
584 lslul:
585 mov cx,di
586 jcxz lsl_exit
587 and eax, #0x0000FFFF
588 shl ebx, #16
589 add eax, ebx
590 lsl_loop:
591 shl eax, #1
592 loop lsl_loop
593 mov ebx, eax
594 shr ebx, #16
595 lsl_exit:
598 idiv_:
600 idiv bx
603 idiv_u:
604 xor dx,dx
605 div bx
608 ldivul:
609 and eax, #0x0000FFFF
610 shl ebx, #16
611 add eax, ebx
612 xor edx, edx
613 SEG SS
614 mov bx, 2[di]
615 shl ebx, #16
616 SEG SS
617 mov bx, [di]
618 div ebx
619 mov ebx, eax
620 shr ebx, #16
623 ASM_END
625 // for access to RAM area which is used by interrupt vectors
626 // and BIOS Data Area
628 typedef struct {
629 unsigned char filler1[0x400];
630 unsigned char filler2[0x6c];
631 Bit16u ticks_low;
632 Bit16u ticks_high;
633 Bit8u midnight_flag;
634 } bios_data_t;
636 #define BiosData ((bios_data_t *) 0)
638 #if BX_USE_ATADRV
639 typedef struct {
640 Bit16u heads; // # heads
641 Bit16u cylinders; // # cylinders
642 Bit16u spt; // # sectors / track
643 } chs_t;
645 // DPTE definition
646 typedef struct {
647 Bit16u iobase1;
648 Bit16u iobase2;
649 Bit8u prefix;
650 Bit8u unused;
651 Bit8u irq;
652 Bit8u blkcount;
653 Bit8u dma;
654 Bit8u pio;
655 Bit16u options;
656 Bit16u reserved;
657 Bit8u revision;
658 Bit8u checksum;
659 } dpte_t;
661 typedef struct {
662 Bit8u iface; // ISA or PCI
663 Bit16u iobase1; // IO Base 1
664 Bit16u iobase2; // IO Base 2
665 Bit8u irq; // IRQ
666 } ata_channel_t;
668 typedef struct {
669 Bit8u type; // Detected type of ata (ata/atapi/none/unknown)
670 Bit8u device; // Detected type of attached devices (hd/cd/none)
671 Bit8u removable; // Removable device flag
672 Bit8u lock; // Locks for removable devices
673 // Bit8u lba_capable; // LBA capable flag - always yes for bochs devices
674 Bit8u mode; // transfert mode : PIO 16/32 bits - IRQ - ISADMA - PCIDMA
675 Bit16u blksize; // block size
677 Bit8u translation; // type of translation
678 chs_t lchs; // Logical CHS
679 chs_t pchs; // Physical CHS
681 Bit32u sectors; // Total sectors count
682 } ata_device_t;
684 typedef struct {
685 // ATA channels info
686 ata_channel_t channels[BX_MAX_ATA_INTERFACES];
688 // ATA devices info
689 ata_device_t devices[BX_MAX_ATA_DEVICES];
691 // map between (bios hd id - 0x80) and ata channels
692 Bit8u hdcount, hdidmap[BX_MAX_ATA_DEVICES];
694 // map between (bios cd id - 0xE0) and ata channels
695 Bit8u cdcount, cdidmap[BX_MAX_ATA_DEVICES];
697 // Buffer for DPTE table
698 dpte_t dpte;
700 // Count of transferred sectors and bytes
701 Bit16u trsfsectors;
702 Bit32u trsfbytes;
704 } ata_t;
706 #if BX_ELTORITO_BOOT
707 // ElTorito Device Emulation data
708 typedef struct {
709 Bit8u active;
710 Bit8u media;
711 Bit8u emulated_drive;
712 Bit8u controller_index;
713 Bit16u device_spec;
714 Bit32u ilba;
715 Bit16u buffer_segment;
716 Bit16u load_segment;
717 Bit16u sector_count;
719 // Virtual device
720 chs_t vdevice;
721 } cdemu_t;
722 #endif // BX_ELTORITO_BOOT
724 // for access to EBDA area
725 // The EBDA structure should conform to
726 // http://www.frontiernet.net/~fys/rombios.htm document
727 // I made the ata and cdemu structs begin at 0x121 in the EBDA seg
728 typedef struct {
729 unsigned char filler1[0x3D];
731 // FDPT - Can be splitted in data members if needed
732 unsigned char fdpt0[0x10];
733 unsigned char fdpt1[0x10];
735 unsigned char filler2[0xC4];
737 // ATA Driver data
738 ata_t ata;
740 #if BX_ELTORITO_BOOT
741 // El Torito Emulation data
742 cdemu_t cdemu;
743 #endif // BX_ELTORITO_BOOT
745 } ebda_data_t;
747 #define EbdaData ((ebda_data_t *) 0)
749 // for access to the int13ext structure
750 typedef struct {
751 Bit8u size;
752 Bit8u reserved;
753 Bit16u count;
754 Bit16u offset;
755 Bit16u segment;
756 Bit32u lba1;
757 Bit32u lba2;
758 } int13ext_t;
760 #define Int13Ext ((int13ext_t *) 0)
762 // Disk Physical Table definition
763 typedef struct {
764 Bit16u size;
765 Bit16u infos;
766 Bit32u cylinders;
767 Bit32u heads;
768 Bit32u spt;
769 Bit32u sector_count1;
770 Bit32u sector_count2;
771 Bit16u blksize;
772 Bit16u dpte_segment;
773 Bit16u dpte_offset;
774 Bit16u key;
775 Bit8u dpi_length;
776 Bit8u reserved1;
777 Bit16u reserved2;
778 Bit8u host_bus[4];
779 Bit8u iface_type[8];
780 Bit8u iface_path[8];
781 Bit8u device_path[8];
782 Bit8u reserved3;
783 Bit8u checksum;
784 } dpt_t;
786 #define Int13DPT ((dpt_t *) 0)
788 #endif // BX_USE_ATADRV
790 typedef struct {
791 union {
792 struct {
793 Bit16u di, si, bp, sp;
794 Bit16u bx, dx, cx, ax;
795 } r16;
796 struct {
797 Bit16u filler[4];
798 Bit8u bl, bh, dl, dh, cl, ch, al, ah;
799 } r8;
800 } u;
801 } pusha_regs_t;
803 typedef struct {
804 union {
805 struct {
806 Bit32u edi, esi, ebp, esp;
807 Bit32u ebx, edx, ecx, eax;
808 } r32;
809 struct {
810 Bit16u di, filler1, si, filler2, bp, filler3, sp, filler4;
811 Bit16u bx, filler5, dx, filler6, cx, filler7, ax, filler8;
812 } r16;
813 struct {
814 Bit32u filler[4];
815 Bit8u bl, bh;
816 Bit16u filler1;
817 Bit8u dl, dh;
818 Bit16u filler2;
819 Bit8u cl, ch;
820 Bit16u filler3;
821 Bit8u al, ah;
822 Bit16u filler4;
823 } r8;
824 } u;
825 } pushad_regs_t;
827 typedef struct {
828 union {
829 struct {
830 Bit16u flags;
831 } r16;
832 struct {
833 Bit8u flagsl;
834 Bit8u flagsh;
835 } r8;
836 } u;
837 } flags_t;
839 #define SetCF(x) x.u.r8.flagsl |= 0x01
840 #define SetZF(x) x.u.r8.flagsl |= 0x40
841 #define ClearCF(x) x.u.r8.flagsl &= 0xfe
842 #define ClearZF(x) x.u.r8.flagsl &= 0xbf
843 #define GetCF(x) (x.u.r8.flagsl & 0x01)
845 typedef struct {
846 Bit16u ip;
847 Bit16u cs;
848 flags_t flags;
849 } iret_addr_t;
853 static Bit8u inb();
854 static Bit8u inb_cmos();
855 static void outb();
856 static void outb_cmos();
857 static Bit16u inw();
858 static void outw();
859 static void init_rtc();
860 static bx_bool rtc_updating();
862 static Bit8u read_byte();
863 static Bit16u read_word();
864 static void write_byte();
865 static void write_word();
866 static void bios_printf();
868 static Bit8u inhibit_mouse_int_and_events();
869 static void enable_mouse_int_and_events();
870 static Bit8u send_to_mouse_ctrl();
871 static Bit8u get_mouse_data();
872 static void set_kbd_command_byte();
874 static void int09_function();
875 static void int13_harddisk();
876 static void int13_cdrom();
877 static void int13_cdemu();
878 static void int13_eltorito();
879 static void int13_diskette_function();
880 static void int14_function();
881 static void int15_function();
882 static void int16_function();
883 static void int17_function();
884 static Bit32u int19_function();
885 static void int1a_function();
886 static void int70_function();
887 static void int74_function();
888 static Bit16u get_CS();
889 static Bit16u get_SS();
890 static unsigned int enqueue_key();
891 static unsigned int dequeue_key();
892 static void get_hd_geometry();
893 static void set_diskette_ret_status();
894 static void set_diskette_current_cyl();
895 static void determine_floppy_media();
896 static bx_bool floppy_drive_exists();
897 static bx_bool floppy_drive_recal();
898 static bx_bool floppy_media_known();
899 static bx_bool floppy_media_sense();
900 static bx_bool set_enable_a20();
901 static void debugger_on();
902 static void debugger_off();
903 static void keyboard_init();
904 static void keyboard_panic();
905 static void shutdown_status_panic();
906 static void nmi_handler_msg();
908 static void print_bios_banner();
909 static void print_boot_device();
910 static void print_boot_failure();
911 static void print_cdromboot_failure();
913 # if BX_USE_ATADRV
915 // ATA / ATAPI driver
916 void ata_init();
917 void ata_detect();
918 void ata_reset();
920 Bit16u ata_cmd_non_data();
921 Bit16u ata_cmd_data_in();
922 Bit16u ata_cmd_data_out();
923 Bit16u ata_cmd_packet();
925 Bit16u atapi_get_sense();
926 Bit16u atapi_is_ready();
927 Bit16u atapi_is_cdrom();
929 #endif // BX_USE_ATADRV
931 #if BX_ELTORITO_BOOT
933 void cdemu_init();
934 Bit8u cdemu_isactive();
935 Bit8u cdemu_emulated_drive();
937 Bit16u cdrom_boot();
939 #endif // BX_ELTORITO_BOOT
941 static char bios_cvs_version_string[] = "$Revision$ $Date$";
943 #define BIOS_COPYRIGHT_STRING "(c) 2002 MandrakeSoft S.A. Written by Kevin Lawton & the Bochs team."
945 #define BIOS_PRINTF_HALT 1
946 #define BIOS_PRINTF_SCREEN 2
947 #define BIOS_PRINTF_INFO 4
948 #define BIOS_PRINTF_DEBUG 8
949 #define BIOS_PRINTF_ALL (BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO)
950 #define BIOS_PRINTF_DEBHALT (BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO | BIOS_PRINTF_HALT)
952 #define printf(format, p...) bios_printf(BIOS_PRINTF_SCREEN, format, ##p)
954 // Defines the output macros.
955 // BX_DEBUG goes to INFO port until we can easily choose debug info on a
956 // per-device basis. Debug info are sent only in debug mode
957 #if DEBUG_ROMBIOS
958 # define BX_DEBUG(format, p...) bios_printf(BIOS_PRINTF_INFO, format, ##p)
959 #else
960 # define BX_DEBUG(format, p...)
961 #endif
962 #define BX_INFO(format, p...) bios_printf(BIOS_PRINTF_INFO, format, ##p)
963 #define BX_PANIC(format, p...) bios_printf(BIOS_PRINTF_DEBHALT, format, ##p)
965 #if DEBUG_ATA
966 # define BX_DEBUG_ATA(a...) BX_DEBUG(a)
967 #else
968 # define BX_DEBUG_ATA(a...)
969 #endif
970 #if DEBUG_INT13_HD
971 # define BX_DEBUG_INT13_HD(a...) BX_DEBUG(a)
972 #else
973 # define BX_DEBUG_INT13_HD(a...)
974 #endif
975 #if DEBUG_INT13_CD
976 # define BX_DEBUG_INT13_CD(a...) BX_DEBUG(a)
977 #else
978 # define BX_DEBUG_INT13_CD(a...)
979 #endif
980 #if DEBUG_INT13_ET
981 # define BX_DEBUG_INT13_ET(a...) BX_DEBUG(a)
982 #else
983 # define BX_DEBUG_INT13_ET(a...)
984 #endif
985 #if DEBUG_INT13_FL
986 # define BX_DEBUG_INT13_FL(a...) BX_DEBUG(a)
987 #else
988 # define BX_DEBUG_INT13_FL(a...)
989 #endif
990 #if DEBUG_INT15
991 # define BX_DEBUG_INT15(a...) BX_DEBUG(a)
992 #else
993 # define BX_DEBUG_INT15(a...)
994 #endif
995 #if DEBUG_INT16
996 # define BX_DEBUG_INT16(a...) BX_DEBUG(a)
997 #else
998 # define BX_DEBUG_INT16(a...)
999 #endif
1000 #if DEBUG_INT1A
1001 # define BX_DEBUG_INT1A(a...) BX_DEBUG(a)
1002 #else
1003 # define BX_DEBUG_INT1A(a...)
1004 #endif
1005 #if DEBUG_INT74
1006 # define BX_DEBUG_INT74(a...) BX_DEBUG(a)
1007 #else
1008 # define BX_DEBUG_INT74(a...)
1009 #endif
1011 #define SET_AL(val8) AX = ((AX & 0xff00) | (val8))
1012 #define SET_BL(val8) BX = ((BX & 0xff00) | (val8))
1013 #define SET_CL(val8) CX = ((CX & 0xff00) | (val8))
1014 #define SET_DL(val8) DX = ((DX & 0xff00) | (val8))
1015 #define SET_AH(val8) AX = ((AX & 0x00ff) | ((val8) << 8))
1016 #define SET_BH(val8) BX = ((BX & 0x00ff) | ((val8) << 8))
1017 #define SET_CH(val8) CX = ((CX & 0x00ff) | ((val8) << 8))
1018 #define SET_DH(val8) DX = ((DX & 0x00ff) | ((val8) << 8))
1020 #define GET_AL() ( AX & 0x00ff )
1021 #define GET_BL() ( BX & 0x00ff )
1022 #define GET_CL() ( CX & 0x00ff )
1023 #define GET_DL() ( DX & 0x00ff )
1024 #define GET_AH() ( AX >> 8 )
1025 #define GET_BH() ( BX >> 8 )
1026 #define GET_CH() ( CX >> 8 )
1027 #define GET_DH() ( DX >> 8 )
1029 #define GET_ELDL() ( ELDX & 0x00ff )
1030 #define GET_ELDH() ( ELDX >> 8 )
1032 #define SET_CF() FLAGS |= 0x0001
1033 #define CLEAR_CF() FLAGS &= 0xfffe
1034 #define GET_CF() (FLAGS & 0x0001)
1036 #define SET_ZF() FLAGS |= 0x0040
1037 #define CLEAR_ZF() FLAGS &= 0xffbf
1038 #define GET_ZF() (FLAGS & 0x0040)
1040 #define UNSUPPORTED_FUNCTION 0x86
1042 #define none 0
1043 #define MAX_SCAN_CODE 0x58
1045 static struct {
1046 Bit16u normal;
1047 Bit16u shift;
1048 Bit16u control;
1049 Bit16u alt;
1050 Bit8u lock_flags;
1051 } scan_to_scanascii[MAX_SCAN_CODE + 1] = {
1052 { none, none, none, none, none },
1053 { 0x011b, 0x011b, 0x011b, 0x0100, none }, /* escape */
1054 { 0x0231, 0x0221, none, 0x7800, none }, /* 1! */
1055 { 0x0332, 0x0340, 0x0300, 0x7900, none }, /* 2@ */
1056 { 0x0433, 0x0423, none, 0x7a00, none }, /* 3# */
1057 { 0x0534, 0x0524, none, 0x7b00, none }, /* 4$ */
1058 { 0x0635, 0x0625, none, 0x7c00, none }, /* 5% */
1059 { 0x0736, 0x075e, 0x071e, 0x7d00, none }, /* 6^ */
1060 { 0x0837, 0x0826, none, 0x7e00, none }, /* 7& */
1061 { 0x0938, 0x092a, none, 0x7f00, none }, /* 8* */
1062 { 0x0a39, 0x0a28, none, 0x8000, none }, /* 9( */
1063 { 0x0b30, 0x0b29, none, 0x8100, none }, /* 0) */
1064 { 0x0c2d, 0x0c5f, 0x0c1f, 0x8200, none }, /* -_ */
1065 { 0x0d3d, 0x0d2b, none, 0x8300, none }, /* =+ */
1066 { 0x0e08, 0x0e08, 0x0e7f, none, none }, /* backspace */
1067 { 0x0f09, 0x0f00, none, none, none }, /* tab */
1068 { 0x1071, 0x1051, 0x1011, 0x1000, 0x40 }, /* Q */
1069 { 0x1177, 0x1157, 0x1117, 0x1100, 0x40 }, /* W */
1070 { 0x1265, 0x1245, 0x1205, 0x1200, 0x40 }, /* E */
1071 { 0x1372, 0x1352, 0x1312, 0x1300, 0x40 }, /* R */
1072 { 0x1474, 0x1454, 0x1414, 0x1400, 0x40 }, /* T */
1073 { 0x1579, 0x1559, 0x1519, 0x1500, 0x40 }, /* Y */
1074 { 0x1675, 0x1655, 0x1615, 0x1600, 0x40 }, /* U */
1075 { 0x1769, 0x1749, 0x1709, 0x1700, 0x40 }, /* I */
1076 { 0x186f, 0x184f, 0x180f, 0x1800, 0x40 }, /* O */
1077 { 0x1970, 0x1950, 0x1910, 0x1900, 0x40 }, /* P */
1078 { 0x1a5b, 0x1a7b, 0x1a1b, none, none }, /* [{ */
1079 { 0x1b5d, 0x1b7d, 0x1b1d, none, none }, /* ]} */
1080 { 0x1c0d, 0x1c0d, 0x1c0a, none, none }, /* Enter */
1081 { none, none, none, none, none }, /* L Ctrl */
1082 { 0x1e61, 0x1e41, 0x1e01, 0x1e00, 0x40 }, /* A */
1083 { 0x1f73, 0x1f53, 0x1f13, 0x1f00, 0x40 }, /* S */
1084 { 0x2064, 0x2044, 0x2004, 0x2000, 0x40 }, /* D */
1085 { 0x2166, 0x2146, 0x2106, 0x2100, 0x40 }, /* F */
1086 { 0x2267, 0x2247, 0x2207, 0x2200, 0x40 }, /* G */
1087 { 0x2368, 0x2348, 0x2308, 0x2300, 0x40 }, /* H */
1088 { 0x246a, 0x244a, 0x240a, 0x2400, 0x40 }, /* J */
1089 { 0x256b, 0x254b, 0x250b, 0x2500, 0x40 }, /* K */
1090 { 0x266c, 0x264c, 0x260c, 0x2600, 0x40 }, /* L */
1091 { 0x273b, 0x273a, none, none, none }, /* ;: */
1092 { 0x2827, 0x2822, none, none, none }, /* '" */
1093 { 0x2960, 0x297e, none, none, none }, /* `~ */
1094 { none, none, none, none, none }, /* L shift */
1095 { 0x2b5c, 0x2b7c, 0x2b1c, none, none }, /* |\ */
1096 { 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00, 0x40 }, /* Z */
1097 { 0x2d78, 0x2d58, 0x2d18, 0x2d00, 0x40 }, /* X */
1098 { 0x2e63, 0x2e43, 0x2e03, 0x2e00, 0x40 }, /* C */
1099 { 0x2f76, 0x2f56, 0x2f16, 0x2f00, 0x40 }, /* V */
1100 { 0x3062, 0x3042, 0x3002, 0x3000, 0x40 }, /* B */
1101 { 0x316e, 0x314e, 0x310e, 0x3100, 0x40 }, /* N */
1102 { 0x326d, 0x324d, 0x320d, 0x3200, 0x40 }, /* M */
1103 { 0x332c, 0x333c, none, none, none }, /* ,< */
1104 { 0x342e, 0x343e, none, none, none }, /* .> */
1105 { 0x352f, 0x353f, none, none, none }, /* /? */
1106 { none, none, none, none, none }, /* R Shift */
1107 { 0x372a, 0x372a, none, none, none }, /* * */
1108 { none, none, none, none, none }, /* L Alt */
1109 { 0x3920, 0x3920, 0x3920, 0x3920, none }, /* space */
1110 { none, none, none, none, none }, /* caps lock */
1111 { 0x3b00, 0x5400, 0x5e00, 0x6800, none }, /* F1 */
1112 { 0x3c00, 0x5500, 0x5f00, 0x6900, none }, /* F2 */
1113 { 0x3d00, 0x5600, 0x6000, 0x6a00, none }, /* F3 */
1114 { 0x3e00, 0x5700, 0x6100, 0x6b00, none }, /* F4 */
1115 { 0x3f00, 0x5800, 0x6200, 0x6c00, none }, /* F5 */
1116 { 0x4000, 0x5900, 0x6300, 0x6d00, none }, /* F6 */
1117 { 0x4100, 0x5a00, 0x6400, 0x6e00, none }, /* F7 */
1118 { 0x4200, 0x5b00, 0x6500, 0x6f00, none }, /* F8 */
1119 { 0x4300, 0x5c00, 0x6600, 0x7000, none }, /* F9 */
1120 { 0x4400, 0x5d00, 0x6700, 0x7100, none }, /* F10 */
1121 { none, none, none, none, none }, /* Num Lock */
1122 { none, none, none, none, none }, /* Scroll Lock */
1123 { 0x4700, 0x4737, 0x7700, none, 0x20 }, /* 7 Home */
1124 { 0x4800, 0x4838, none, none, 0x20 }, /* 8 UP */
1125 { 0x4900, 0x4939, 0x8400, none, 0x20 }, /* 9 PgUp */
1126 { 0x4a2d, 0x4a2d, none, none, none }, /* - */
1127 { 0x4b00, 0x4b34, 0x7300, none, 0x20 }, /* 4 Left */
1128 { 0x4c00, 0x4c35, none, none, 0x20 }, /* 5 */
1129 { 0x4d00, 0x4d36, 0x7400, none, 0x20 }, /* 6 Right */
1130 { 0x4e2b, 0x4e2b, none, none, none }, /* + */
1131 { 0x4f00, 0x4f31, 0x7500, none, 0x20 }, /* 1 End */
1132 { 0x5000, 0x5032, none, none, 0x20 }, /* 2 Down */
1133 { 0x5100, 0x5133, 0x7600, none, 0x20 }, /* 3 PgDn */
1134 { 0x5200, 0x5230, none, none, 0x20 }, /* 0 Ins */
1135 { 0x5300, 0x532e, none, none, 0x20 }, /* Del */
1136 { none, none, none, none, none },
1137 { none, none, none, none, none },
1138 { 0x565c, 0x567c, none, none, none }, /* \| */
1139 { 0x5700, 0x5700, none, none, none }, /* F11 */
1140 { 0x5800, 0x5800, none, none, none } /* F12 */
1143 Bit8u
1144 inb(port)
1145 Bit16u port;
1147 ASM_START
1148 push bp
1149 mov bp, sp
1151 push dx
1152 mov dx, 4[bp]
1153 in al, dx
1154 pop dx
1156 pop bp
1157 ASM_END
1160 #if BX_USE_ATADRV
1161 Bit16u
1162 inw(port)
1163 Bit16u port;
1165 ASM_START
1166 push bp
1167 mov bp, sp
1169 push dx
1170 mov dx, 4[bp]
1171 in ax, dx
1172 pop dx
1174 pop bp
1175 ASM_END
1177 #endif
1179 void
1180 outb(port, val)
1181 Bit16u port;
1182 Bit8u val;
1184 ASM_START
1185 push bp
1186 mov bp, sp
1188 push ax
1189 push dx
1190 mov dx, 4[bp]
1191 mov al, 6[bp]
1192 out dx, al
1193 pop dx
1194 pop ax
1196 pop bp
1197 ASM_END
1200 #if BX_USE_ATADRV
1201 void
1202 outw(port, val)
1203 Bit16u port;
1204 Bit16u val;
1206 ASM_START
1207 push bp
1208 mov bp, sp
1210 push ax
1211 push dx
1212 mov dx, 4[bp]
1213 mov ax, 6[bp]
1214 out dx, ax
1215 pop dx
1216 pop ax
1218 pop bp
1219 ASM_END
1221 #endif
1223 void
1224 outb_cmos(cmos_reg, val)
1225 Bit8u cmos_reg;
1226 Bit8u val;
1228 ASM_START
1229 push bp
1230 mov bp, sp
1232 mov al, 4[bp] ;; cmos_reg
1233 out 0x70, al
1234 mov al, 6[bp] ;; val
1235 out 0x71, al
1237 pop bp
1238 ASM_END
1241 Bit8u
1242 inb_cmos(cmos_reg)
1243 Bit8u cmos_reg;
1245 ASM_START
1246 push bp
1247 mov bp, sp
1249 mov al, 4[bp] ;; cmos_reg
1250 out 0x70, al
1251 in al, 0x71
1253 pop bp
1254 ASM_END
1257 void
1258 init_rtc()
1260 outb_cmos(0x0a, 0x26);
1261 outb_cmos(0x0b, 0x02);
1262 inb_cmos(0x0c);
1263 inb_cmos(0x0d);
1266 bx_bool
1267 rtc_updating()
1269 // This function checks to see if the update-in-progress bit
1270 // is set in CMOS Status Register A. If not, it returns 0.
1271 // If it is set, it tries to wait until there is a transition
1272 // to 0, and will return 0 if such a transition occurs. A 1
1273 // is returned only after timing out. The maximum period
1274 // that this bit should be set is constrained to 244useconds.
1275 // The count I use below guarantees coverage or more than
1276 // this time, with any reasonable IPS setting.
1278 Bit16u count;
1280 count = 25000;
1281 while (--count != 0) {
1282 if ( (inb_cmos(0x0a) & 0x80) == 0 )
1283 return(0);
1285 return(1); // update-in-progress never transitioned to 0
1289 Bit8u
1290 read_byte(seg, offset)
1291 Bit16u seg;
1292 Bit16u offset;
1294 ASM_START
1295 push bp
1296 mov bp, sp
1298 push bx
1299 push ds
1300 mov ax, 4[bp] ; segment
1301 mov ds, ax
1302 mov bx, 6[bp] ; offset
1303 mov al, [bx]
1304 ;; al = return value (byte)
1305 pop ds
1306 pop bx
1308 pop bp
1309 ASM_END
1312 Bit16u
1313 read_word(seg, offset)
1314 Bit16u seg;
1315 Bit16u offset;
1317 ASM_START
1318 push bp
1319 mov bp, sp
1321 push bx
1322 push ds
1323 mov ax, 4[bp] ; segment
1324 mov ds, ax
1325 mov bx, 6[bp] ; offset
1326 mov ax, [bx]
1327 ;; ax = return value (word)
1328 pop ds
1329 pop bx
1331 pop bp
1332 ASM_END
1335 void
1336 write_byte(seg, offset, data)
1337 Bit16u seg;
1338 Bit16u offset;
1339 Bit8u data;
1341 ASM_START
1342 push bp
1343 mov bp, sp
1345 push ax
1346 push bx
1347 push ds
1348 mov ax, 4[bp] ; segment
1349 mov ds, ax
1350 mov bx, 6[bp] ; offset
1351 mov al, 8[bp] ; data byte
1352 mov [bx], al ; write data byte
1353 pop ds
1354 pop bx
1355 pop ax
1357 pop bp
1358 ASM_END
1361 void
1362 write_word(seg, offset, data)
1363 Bit16u seg;
1364 Bit16u offset;
1365 Bit16u data;
1367 ASM_START
1368 push bp
1369 mov bp, sp
1371 push ax
1372 push bx
1373 push ds
1374 mov ax, 4[bp] ; segment
1375 mov ds, ax
1376 mov bx, 6[bp] ; offset
1377 mov ax, 8[bp] ; data word
1378 mov [bx], ax ; write data word
1379 pop ds
1380 pop bx
1381 pop ax
1383 pop bp
1384 ASM_END
1387 Bit16u
1388 get_CS()
1390 ASM_START
1391 mov ax, cs
1392 ASM_END
1395 Bit16u
1396 get_SS()
1398 ASM_START
1399 mov ax, ss
1400 ASM_END
1403 #if BX_DEBUG_SERIAL
1404 /* serial debug port*/
1405 #define BX_DEBUG_PORT 0x03f8
1407 /* data */
1408 #define UART_RBR 0x00
1409 #define UART_THR 0x00
1411 /* control */
1412 #define UART_IER 0x01
1413 #define UART_IIR 0x02
1414 #define UART_FCR 0x02
1415 #define UART_LCR 0x03
1416 #define UART_MCR 0x04
1417 #define UART_DLL 0x00
1418 #define UART_DLM 0x01
1420 /* status */
1421 #define UART_LSR 0x05
1422 #define UART_MSR 0x06
1423 #define UART_SCR 0x07
1425 int uart_can_tx_byte(base_port)
1426 Bit16u base_port;
1428 return inb(base_port + UART_LSR) & 0x20;
1431 void uart_wait_to_tx_byte(base_port)
1432 Bit16u base_port;
1434 while (!uart_can_tx_byte(base_port));
1437 void uart_wait_until_sent(base_port)
1438 Bit16u base_port;
1440 while (!(inb(base_port + UART_LSR) & 0x40));
1443 void uart_tx_byte(base_port, data)
1444 Bit16u base_port;
1445 Bit8u data;
1447 uart_wait_to_tx_byte(base_port);
1448 outb(base_port + UART_THR, data);
1449 uart_wait_until_sent(base_port);
1451 #endif
1453 void
1454 wrch(c)
1455 Bit8u c;
1457 ASM_START
1458 push bp
1459 mov bp, sp
1461 push bx
1462 mov ah, #0x0e
1463 mov al, 4[bp]
1464 xor bx,bx
1465 int #0x10
1466 pop bx
1468 pop bp
1469 ASM_END
1472 void
1473 send(action, c)
1474 Bit16u action;
1475 Bit8u c;
1477 #if BX_DEBUG_SERIAL
1478 if (c == '\n') uart_tx_byte(BX_DEBUG_PORT, '\r');
1479 uart_tx_byte(BX_DEBUG_PORT, c);
1480 #endif
1481 #if BX_VIRTUAL_PORTS
1482 if (action & BIOS_PRINTF_DEBUG) outb(DEBUG_PORT, c);
1483 if (action & BIOS_PRINTF_INFO) outb(INFO_PORT, c);
1484 #endif
1485 if (action & BIOS_PRINTF_SCREEN) {
1486 if (c == '\n') wrch('\r');
1487 wrch(c);
1491 void
1492 put_int(action, val, width, neg)
1493 Bit16u action;
1494 short val, width;
1495 bx_bool neg;
1497 short nval = val / 10;
1498 if (nval)
1499 put_int(action, nval, width - 1, neg);
1500 else {
1501 while (--width > 0) send(action, ' ');
1502 if (neg) send(action, '-');
1504 send(action, val - (nval * 10) + '0');
1507 void
1508 put_uint(action, val, width, neg)
1509 Bit16u action;
1510 unsigned short val;
1511 short width;
1512 bx_bool neg;
1514 unsigned short nval = val / 10;
1515 if (nval)
1516 put_uint(action, nval, width - 1, neg);
1517 else {
1518 while (--width > 0) send(action, ' ');
1519 if (neg) send(action, '-');
1521 send(action, val - (nval * 10) + '0');
1524 void
1525 put_luint(action, val, width, neg)
1526 Bit16u action;
1527 unsigned long val;
1528 short width;
1529 bx_bool neg;
1531 unsigned long nval = val / 10;
1532 if (nval)
1533 put_luint(action, nval, width - 1, neg);
1534 else {
1535 while (--width > 0) send(action, ' ');
1536 if (neg) send(action, '-');
1538 send(action, val - (nval * 10) + '0');
1541 //--------------------------------------------------------------------------
1542 // bios_printf()
1543 // A compact variable argument printf function which prints its output via
1544 // an I/O port so that it can be logged by Bochs/Plex.
1545 // Currently, only %x is supported (or %02x, %04x, etc).
1547 // Supports %[format_width][format]
1548 // where format can be d,x,c,s
1549 //--------------------------------------------------------------------------
1550 void
1551 bios_printf(action, s)
1552 Bit16u action;
1553 Bit8u *s;
1555 Bit8u c, format_char;
1556 bx_bool in_format;
1557 short i;
1558 Bit16u *arg_ptr;
1559 Bit16u arg_seg, arg, nibble, hibyte, shift_count, format_width;
1561 arg_ptr = &s;
1562 arg_seg = get_SS();
1564 in_format = 0;
1565 format_width = 0;
1567 if ((action & BIOS_PRINTF_DEBHALT) == BIOS_PRINTF_DEBHALT) {
1568 #if BX_VIRTUAL_PORTS
1569 outb(PANIC_PORT2, 0x00);
1570 #endif
1571 bios_printf (BIOS_PRINTF_SCREEN, "FATAL: ");
1574 while (c = read_byte(get_CS(), s)) {
1575 if ( c == '%' ) {
1576 in_format = 1;
1577 format_width = 0;
1579 else if (in_format) {
1580 if ( (c>='0') && (c<='9') ) {
1581 format_width = (format_width * 10) + (c - '0');
1583 else {
1584 arg_ptr++; // increment to next arg
1585 arg = read_word(arg_seg, arg_ptr);
1586 if (c == 'x') {
1587 if (format_width == 0)
1588 format_width = 4;
1589 for (i=format_width-1; i>=0; i--) {
1590 nibble = (arg >> (4 * i)) & 0x000f;
1591 send (action, (nibble<=9)? (nibble+'0') : (nibble-10+'A'));
1594 else if (c == 'u') {
1595 put_uint(action, arg, format_width, 0);
1597 else if (c == 'l') {
1598 s++;
1599 arg_ptr++; /* increment to next arg */
1600 hibyte = read_word(arg_seg, arg_ptr);
1601 put_luint(action, ((Bit32u) hibyte << 16) | arg, format_width, 0);
1603 else if (c == 'd') {
1604 if (arg & 0x8000)
1605 put_int(action, -arg, format_width - 1, 1);
1606 else
1607 put_int(action, arg, format_width, 0);
1609 else if (c == 's') {
1610 bios_printf(action & (~BIOS_PRINTF_HALT), arg);
1612 else if (c == 'c') {
1613 send(action, arg);
1615 else
1616 BX_PANIC("bios_printf: unknown format\n");
1617 in_format = 0;
1620 else {
1621 send(action, c);
1623 s ++;
1626 if (action & BIOS_PRINTF_HALT) {
1627 // freeze in a busy loop.
1628 ASM_START
1630 halt2_loop:
1632 jmp halt2_loop
1633 ASM_END
1637 //--------------------------------------------------------------------------
1638 // keyboard_init
1639 //--------------------------------------------------------------------------
1640 // this file is based on LinuxBIOS implementation of keyboard.c
1641 // could convert to #asm to gain space
1642 void
1643 keyboard_init()
1645 Bit16u max;
1647 /* ------------------- Flush buffers ------------------------*/
1648 /* Wait until buffer is empty */
1649 max=0xffff;
1650 while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
1652 /* flush incoming keys */
1653 max=0x2000;
1654 while (--max > 0) {
1655 outb(0x80, 0x00);
1656 if (inb(0x64) & 0x01) {
1657 inb(0x60);
1658 max = 0x2000;
1662 // Due to timer issues, and if the IPS setting is > 15000000,
1663 // the incoming keys might not be flushed here. That will
1664 // cause a panic a few lines below. See sourceforge bug report :
1665 // [ 642031 ] FATAL: Keyboard RESET error:993
1667 /* ------------------- controller side ----------------------*/
1668 /* send cmd = 0xAA, self test 8042 */
1669 outb(0x64, 0xaa);
1671 /* Wait until buffer is empty */
1672 max=0xffff;
1673 while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
1674 if (max==0x0) keyboard_panic(00);
1676 /* Wait for data */
1677 max=0xffff;
1678 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x01);
1679 if (max==0x0) keyboard_panic(01);
1681 /* read self-test result, 0x55 should be returned from 0x60 */
1682 if ((inb(0x60) != 0x55)){
1683 keyboard_panic(991);
1686 /* send cmd = 0xAB, keyboard interface test */
1687 outb(0x64,0xab);
1689 /* Wait until buffer is empty */
1690 max=0xffff;
1691 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x10);
1692 if (max==0x0) keyboard_panic(10);
1694 /* Wait for data */
1695 max=0xffff;
1696 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x11);
1697 if (max==0x0) keyboard_panic(11);
1699 /* read keyboard interface test result, */
1700 /* 0x00 should be returned form 0x60 */
1701 if ((inb(0x60) != 0x00)) {
1702 keyboard_panic(992);
1705 /* Enable Keyboard clock */
1706 outb(0x64,0xae);
1707 outb(0x64,0xa8);
1709 /* ------------------- keyboard side ------------------------*/
1710 /* reset kerboard and self test (keyboard side) */
1711 outb(0x60, 0xff);
1713 /* Wait until buffer is empty */
1714 max=0xffff;
1715 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x20);
1716 if (max==0x0) keyboard_panic(20);
1718 /* Wait for data */
1719 max=0xffff;
1720 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x21);
1721 if (max==0x0) keyboard_panic(21);
1723 /* keyboard should return ACK */
1724 if ((inb(0x60) != 0xfa)) {
1725 keyboard_panic(993);
1728 /* Wait for data */
1729 max=0xffff;
1730 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x31);
1731 if (max==0x0) keyboard_panic(31);
1733 if ((inb(0x60) != 0xaa)) {
1734 keyboard_panic(994);
1737 /* Disable keyboard */
1738 outb(0x60, 0xf5);
1740 /* Wait until buffer is empty */
1741 max=0xffff;
1742 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x40);
1743 if (max==0x0) keyboard_panic(40);
1745 /* Wait for data */
1746 max=0xffff;
1747 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x41);
1748 if (max==0x0) keyboard_panic(41);
1750 /* keyboard should return ACK */
1751 if ((inb(0x60) != 0xfa)) {
1752 keyboard_panic(995);
1755 /* Write Keyboard Mode */
1756 outb(0x64, 0x60);
1758 /* Wait until buffer is empty */
1759 max=0xffff;
1760 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x50);
1761 if (max==0x0) keyboard_panic(50);
1763 /* send cmd: scan code convert, disable mouse, enable IRQ 1 */
1764 outb(0x60, 0x61);
1766 /* Wait until buffer is empty */
1767 max=0xffff;
1768 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x60);
1769 if (max==0x0) keyboard_panic(60);
1771 /* Enable keyboard */
1772 outb(0x60, 0xf4);
1774 /* Wait until buffer is empty */
1775 max=0xffff;
1776 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x70);
1777 if (max==0x0) keyboard_panic(70);
1779 /* Wait for data */
1780 max=0xffff;
1781 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x71);
1782 if (max==0x0) keyboard_panic(70);
1784 /* keyboard should return ACK */
1785 if ((inb(0x60) != 0xfa)) {
1786 keyboard_panic(996);
1789 outb(0x80, 0x77);
1792 //--------------------------------------------------------------------------
1793 // keyboard_panic
1794 //--------------------------------------------------------------------------
1795 void
1796 keyboard_panic(status)
1797 Bit16u status;
1799 // If you're getting a 993 keyboard panic here,
1800 // please see the comment in keyboard_init
1802 BX_PANIC("Keyboard error:%u\n",status);
1805 //--------------------------------------------------------------------------
1806 // shutdown_status_panic
1807 // called when the shutdown statsu is not implemented, displays the status
1808 //--------------------------------------------------------------------------
1809 void
1810 shutdown_status_panic(status)
1811 Bit16u status;
1813 BX_PANIC("Unimplemented shutdown status: %02x\n",(Bit8u)status);
1816 //--------------------------------------------------------------------------
1817 // print_bios_banner
1818 // displays a the bios version
1819 //--------------------------------------------------------------------------
1820 void
1821 print_bios_banner()
1823 printf(BX_APPNAME" BIOS - build: %s\n%s\nOptions: ",
1824 BIOS_BUILD_DATE, bios_cvs_version_string);
1825 printf(
1826 #if BX_APM
1827 "apmbios "
1828 #endif
1829 #if BX_PCIBIOS
1830 "pcibios "
1831 #endif
1832 #if BX_ELTORITO_BOOT
1833 "eltorito "
1834 #endif
1835 #if BX_ROMBIOS32
1836 "rombios32 "
1837 #endif
1838 "\n\n");
1841 //--------------------------------------------------------------------------
1842 // print_boot_device
1843 // displays the boot device
1844 //--------------------------------------------------------------------------
1846 static char drivetypes[][10]={"Floppy","Hard Disk","CD-Rom"};
1848 void
1849 print_boot_device(cdboot, drive)
1850 Bit8u cdboot; Bit16u drive;
1852 Bit8u i;
1854 // cdboot contains 0 if floppy/harddisk, 1 otherwise
1855 // drive contains real/emulated boot drive
1857 if(cdboot)i=2; // CD-Rom
1858 else if((drive&0x0080)==0x00)i=0; // Floppy
1859 else if((drive&0x0080)==0x80)i=1; // Hard drive
1860 else return;
1862 printf("Booting from %s...\n",drivetypes[i]);
1865 //--------------------------------------------------------------------------
1866 // print_boot_failure
1867 // displays the reason why boot failed
1868 //--------------------------------------------------------------------------
1869 void
1870 print_boot_failure(cdboot, drive, reason, lastdrive)
1871 Bit8u cdboot; Bit8u drive; Bit8u lastdrive;
1873 Bit16u drivenum = drive&0x7f;
1875 // cdboot: 1 if boot from cd, 0 otherwise
1876 // drive : drive number
1877 // reason: 0 signature check failed, 1 read error
1878 // lastdrive: 1 boot drive is the last one in boot sequence
1880 if (cdboot)
1881 bios_printf(BIOS_PRINTF_INFO | BIOS_PRINTF_SCREEN, "Boot from %s failed\n",drivetypes[2]);
1882 else if (drive & 0x80)
1883 bios_printf(BIOS_PRINTF_INFO | BIOS_PRINTF_SCREEN, "Boot from %s %d failed\n", drivetypes[1],drivenum);
1884 else
1885 bios_printf(BIOS_PRINTF_INFO | BIOS_PRINTF_SCREEN, "Boot from %s %d failed\n", drivetypes[0],drivenum);
1887 if (lastdrive==1) {
1888 if (reason==0)
1889 BX_PANIC("Not a bootable disk\n");
1890 else
1891 BX_PANIC("Could not read the boot disk\n");
1895 //--------------------------------------------------------------------------
1896 // print_cdromboot_failure
1897 // displays the reason why boot failed
1898 //--------------------------------------------------------------------------
1899 void
1900 print_cdromboot_failure( code )
1901 Bit16u code;
1903 bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, "CDROM boot failure code : %04x\n",code);
1905 return;
1908 void
1909 nmi_handler_msg()
1911 BX_PANIC("NMI Handler called\n");
1914 void
1915 int18_panic_msg()
1917 BX_PANIC("INT18: BOOT FAILURE\n");
1920 void
1921 log_bios_start()
1923 #if BX_DEBUG_SERIAL
1924 outb(BX_DEBUG_PORT+UART_LCR, 0x03); /* setup for serial logging: 8N1 */
1925 #endif
1926 BX_INFO("%s\n", bios_cvs_version_string);
1929 bx_bool
1930 set_enable_a20(val)
1931 bx_bool val;
1933 Bit8u oldval;
1935 // Use PS2 System Control port A to set A20 enable
1937 // get current setting first
1938 oldval = inb(0x92);
1940 // change A20 status
1941 if (val)
1942 outb(0x92, oldval | 0x02);
1943 else
1944 outb(0x92, oldval & 0xfd);
1946 return((oldval & 0x02) != 0);
1949 void
1950 debugger_on()
1952 outb(0xfedc, 0x01);
1955 void
1956 debugger_off()
1958 outb(0xfedc, 0x00);
1961 #if BX_USE_ATADRV
1963 // ---------------------------------------------------------------------------
1964 // Start of ATA/ATAPI Driver
1965 // ---------------------------------------------------------------------------
1967 // Global defines -- ATA register and register bits.
1968 // command block & control block regs
1969 #define ATA_CB_DATA 0 // data reg in/out pio_base_addr1+0
1970 #define ATA_CB_ERR 1 // error in pio_base_addr1+1
1971 #define ATA_CB_FR 1 // feature reg out pio_base_addr1+1
1972 #define ATA_CB_SC 2 // sector count in/out pio_base_addr1+2
1973 #define ATA_CB_SN 3 // sector number in/out pio_base_addr1+3
1974 #define ATA_CB_CL 4 // cylinder low in/out pio_base_addr1+4
1975 #define ATA_CB_CH 5 // cylinder high in/out pio_base_addr1+5
1976 #define ATA_CB_DH 6 // device head in/out pio_base_addr1+6
1977 #define ATA_CB_STAT 7 // primary status in pio_base_addr1+7
1978 #define ATA_CB_CMD 7 // command out pio_base_addr1+7
1979 #define ATA_CB_ASTAT 6 // alternate status in pio_base_addr2+6
1980 #define ATA_CB_DC 6 // device control out pio_base_addr2+6
1981 #define ATA_CB_DA 7 // device address in pio_base_addr2+7
1983 #define ATA_CB_ER_ICRC 0x80 // ATA Ultra DMA bad CRC
1984 #define ATA_CB_ER_BBK 0x80 // ATA bad block
1985 #define ATA_CB_ER_UNC 0x40 // ATA uncorrected error
1986 #define ATA_CB_ER_MC 0x20 // ATA media change
1987 #define ATA_CB_ER_IDNF 0x10 // ATA id not found
1988 #define ATA_CB_ER_MCR 0x08 // ATA media change request
1989 #define ATA_CB_ER_ABRT 0x04 // ATA command aborted
1990 #define ATA_CB_ER_NTK0 0x02 // ATA track 0 not found
1991 #define ATA_CB_ER_NDAM 0x01 // ATA address mark not found
1993 #define ATA_CB_ER_P_SNSKEY 0xf0 // ATAPI sense key (mask)
1994 #define ATA_CB_ER_P_MCR 0x08 // ATAPI Media Change Request
1995 #define ATA_CB_ER_P_ABRT 0x04 // ATAPI command abort
1996 #define ATA_CB_ER_P_EOM 0x02 // ATAPI End of Media
1997 #define ATA_CB_ER_P_ILI 0x01 // ATAPI Illegal Length Indication
1999 // ATAPI Interrupt Reason bits in the Sector Count reg (CB_SC)
2000 #define ATA_CB_SC_P_TAG 0xf8 // ATAPI tag (mask)
2001 #define ATA_CB_SC_P_REL 0x04 // ATAPI release
2002 #define ATA_CB_SC_P_IO 0x02 // ATAPI I/O
2003 #define ATA_CB_SC_P_CD 0x01 // ATAPI C/D
2005 // bits 7-4 of the device/head (CB_DH) reg
2006 #define ATA_CB_DH_DEV0 0xa0 // select device 0
2007 #define ATA_CB_DH_DEV1 0xb0 // select device 1
2009 // status reg (CB_STAT and CB_ASTAT) bits
2010 #define ATA_CB_STAT_BSY 0x80 // busy
2011 #define ATA_CB_STAT_RDY 0x40 // ready
2012 #define ATA_CB_STAT_DF 0x20 // device fault
2013 #define ATA_CB_STAT_WFT 0x20 // write fault (old name)
2014 #define ATA_CB_STAT_SKC 0x10 // seek complete
2015 #define ATA_CB_STAT_SERV 0x10 // service
2016 #define ATA_CB_STAT_DRQ 0x08 // data request
2017 #define ATA_CB_STAT_CORR 0x04 // corrected
2018 #define ATA_CB_STAT_IDX 0x02 // index
2019 #define ATA_CB_STAT_ERR 0x01 // error (ATA)
2020 #define ATA_CB_STAT_CHK 0x01 // check (ATAPI)
2022 // device control reg (CB_DC) bits
2023 #define ATA_CB_DC_HD15 0x08 // bit should always be set to one
2024 #define ATA_CB_DC_SRST 0x04 // soft reset
2025 #define ATA_CB_DC_NIEN 0x02 // disable interrupts
2027 // Most mandtory and optional ATA commands (from ATA-3),
2028 #define ATA_CMD_CFA_ERASE_SECTORS 0xC0
2029 #define ATA_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03
2030 #define ATA_CMD_CFA_TRANSLATE_SECTOR 0x87
2031 #define ATA_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD
2032 #define ATA_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38
2033 #define ATA_CMD_CHECK_POWER_MODE1 0xE5
2034 #define ATA_CMD_CHECK_POWER_MODE2 0x98
2035 #define ATA_CMD_DEVICE_RESET 0x08
2036 #define ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90
2037 #define ATA_CMD_FLUSH_CACHE 0xE7
2038 #define ATA_CMD_FORMAT_TRACK 0x50
2039 #define ATA_CMD_IDENTIFY_DEVICE 0xEC
2040 #define ATA_CMD_IDENTIFY_DEVICE_PACKET 0xA1
2041 #define ATA_CMD_IDENTIFY_PACKET_DEVICE 0xA1
2042 #define ATA_CMD_IDLE1 0xE3
2043 #define ATA_CMD_IDLE2 0x97
2044 #define ATA_CMD_IDLE_IMMEDIATE1 0xE1
2045 #define ATA_CMD_IDLE_IMMEDIATE2 0x95
2046 #define ATA_CMD_INITIALIZE_DRIVE_PARAMETERS 0x91
2047 #define ATA_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91
2048 #define ATA_CMD_NOP 0x00
2049 #define ATA_CMD_PACKET 0xA0
2050 #define ATA_CMD_READ_BUFFER 0xE4
2051 #define ATA_CMD_READ_DMA 0xC8
2052 #define ATA_CMD_READ_DMA_QUEUED 0xC7
2053 #define ATA_CMD_READ_MULTIPLE 0xC4
2054 #define ATA_CMD_READ_SECTORS 0x20
2055 #define ATA_CMD_READ_VERIFY_SECTORS 0x40
2056 #define ATA_CMD_RECALIBRATE 0x10
2057 #define ATA_CMD_SEEK 0x70
2058 #define ATA_CMD_SET_FEATURES 0xEF
2059 #define ATA_CMD_SET_MULTIPLE_MODE 0xC6
2060 #define ATA_CMD_SLEEP1 0xE6
2061 #define ATA_CMD_SLEEP2 0x99
2062 #define ATA_CMD_STANDBY1 0xE2
2063 #define ATA_CMD_STANDBY2 0x96
2064 #define ATA_CMD_STANDBY_IMMEDIATE1 0xE0
2065 #define ATA_CMD_STANDBY_IMMEDIATE2 0x94
2066 #define ATA_CMD_WRITE_BUFFER 0xE8
2067 #define ATA_CMD_WRITE_DMA 0xCA
2068 #define ATA_CMD_WRITE_DMA_QUEUED 0xCC
2069 #define ATA_CMD_WRITE_MULTIPLE 0xC5
2070 #define ATA_CMD_WRITE_SECTORS 0x30
2071 #define ATA_CMD_WRITE_VERIFY 0x3C
2073 #define ATA_IFACE_NONE 0x00
2074 #define ATA_IFACE_ISA 0x00
2075 #define ATA_IFACE_PCI 0x01
2077 #define ATA_TYPE_NONE 0x00
2078 #define ATA_TYPE_UNKNOWN 0x01
2079 #define ATA_TYPE_ATA 0x02
2080 #define ATA_TYPE_ATAPI 0x03
2082 #define ATA_DEVICE_NONE 0x00
2083 #define ATA_DEVICE_HD 0xFF
2084 #define ATA_DEVICE_CDROM 0x05
2086 #define ATA_MODE_NONE 0x00
2087 #define ATA_MODE_PIO16 0x00
2088 #define ATA_MODE_PIO32 0x01
2089 #define ATA_MODE_ISADMA 0x02
2090 #define ATA_MODE_PCIDMA 0x03
2091 #define ATA_MODE_USEIRQ 0x10
2093 #define ATA_TRANSLATION_NONE 0
2094 #define ATA_TRANSLATION_LBA 1
2095 #define ATA_TRANSLATION_LARGE 2
2096 #define ATA_TRANSLATION_RECHS 3
2098 #define ATA_DATA_NO 0x00
2099 #define ATA_DATA_IN 0x01
2100 #define ATA_DATA_OUT 0x02
2102 // ---------------------------------------------------------------------------
2103 // ATA/ATAPI driver : initialization
2104 // ---------------------------------------------------------------------------
2105 void ata_init( )
2107 Bit16u ebda_seg=read_word(0x0040,0x000E);
2108 Bit8u channel, device;
2110 // Channels info init.
2111 for (channel=0; channel<BX_MAX_ATA_INTERFACES; channel++) {
2112 write_byte(ebda_seg,&EbdaData->ata.channels[channel].iface,ATA_IFACE_NONE);
2113 write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1,0x0);
2114 write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2,0x0);
2115 write_byte(ebda_seg,&EbdaData->ata.channels[channel].irq,0);
2118 // Devices info init.
2119 for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2120 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
2121 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_NONE);
2122 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable,0);
2123 write_byte(ebda_seg,&EbdaData->ata.devices[device].lock,0);
2124 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode,ATA_MODE_NONE);
2125 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize,0);
2126 write_byte(ebda_seg,&EbdaData->ata.devices[device].translation,ATA_TRANSLATION_NONE);
2127 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads,0);
2128 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders,0);
2129 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt,0);
2130 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads,0);
2131 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders,0);
2132 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt,0);
2134 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors,0L);
2137 // hdidmap and cdidmap init.
2138 for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2139 write_byte(ebda_seg,&EbdaData->ata.hdidmap[device],BX_MAX_ATA_DEVICES);
2140 write_byte(ebda_seg,&EbdaData->ata.cdidmap[device],BX_MAX_ATA_DEVICES);
2143 write_byte(ebda_seg,&EbdaData->ata.hdcount,0);
2144 write_byte(ebda_seg,&EbdaData->ata.cdcount,0);
2147 // ---------------------------------------------------------------------------
2148 // ATA/ATAPI driver : device detection
2149 // ---------------------------------------------------------------------------
2151 void ata_detect( )
2153 Bit16u ebda_seg=read_word(0x0040,0x000E);
2154 Bit8u hdcount, cdcount, device, type;
2155 Bit8u buffer[0x0200];
2157 #if BX_MAX_ATA_INTERFACES > 0
2158 write_byte(ebda_seg,&EbdaData->ata.channels[0].iface,ATA_IFACE_ISA);
2159 write_word(ebda_seg,&EbdaData->ata.channels[0].iobase1,0x1f0);
2160 write_word(ebda_seg,&EbdaData->ata.channels[0].iobase2,0x3f0);
2161 write_byte(ebda_seg,&EbdaData->ata.channels[0].irq,14);
2162 #endif
2163 #if BX_MAX_ATA_INTERFACES > 1
2164 write_byte(ebda_seg,&EbdaData->ata.channels[1].iface,ATA_IFACE_ISA);
2165 write_word(ebda_seg,&EbdaData->ata.channels[1].iobase1,0x170);
2166 write_word(ebda_seg,&EbdaData->ata.channels[1].iobase2,0x370);
2167 write_byte(ebda_seg,&EbdaData->ata.channels[1].irq,15);
2168 #endif
2169 #if BX_MAX_ATA_INTERFACES > 2
2170 write_byte(ebda_seg,&EbdaData->ata.channels[2].iface,ATA_IFACE_ISA);
2171 write_word(ebda_seg,&EbdaData->ata.channels[2].iobase1,0x1e8);
2172 write_word(ebda_seg,&EbdaData->ata.channels[2].iobase2,0x3e0);
2173 write_byte(ebda_seg,&EbdaData->ata.channels[2].irq,12);
2174 #endif
2175 #if BX_MAX_ATA_INTERFACES > 3
2176 write_byte(ebda_seg,&EbdaData->ata.channels[3].iface,ATA_IFACE_ISA);
2177 write_word(ebda_seg,&EbdaData->ata.channels[3].iobase1,0x168);
2178 write_word(ebda_seg,&EbdaData->ata.channels[3].iobase2,0x360);
2179 write_byte(ebda_seg,&EbdaData->ata.channels[3].irq,11);
2180 #endif
2181 #if BX_MAX_ATA_INTERFACES > 4
2182 #error Please fill the ATA interface informations
2183 #endif
2185 // Device detection
2186 hdcount=cdcount=0;
2188 for(device=0; device<BX_MAX_ATA_DEVICES; device++) {
2189 Bit16u iobase1, iobase2;
2190 Bit8u channel, slave, shift;
2191 Bit8u sc, sn, cl, ch, st;
2193 channel = device / 2;
2194 slave = device % 2;
2196 iobase1 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1);
2197 iobase2 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2);
2199 // Disable interrupts
2200 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2202 // Look for device
2203 outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2204 outb(iobase1+ATA_CB_SC, 0x55);
2205 outb(iobase1+ATA_CB_SN, 0xaa);
2206 outb(iobase1+ATA_CB_SC, 0xaa);
2207 outb(iobase1+ATA_CB_SN, 0x55);
2208 outb(iobase1+ATA_CB_SC, 0x55);
2209 outb(iobase1+ATA_CB_SN, 0xaa);
2211 // If we found something
2212 sc = inb(iobase1+ATA_CB_SC);
2213 sn = inb(iobase1+ATA_CB_SN);
2215 if ( (sc == 0x55) && (sn == 0xaa) ) {
2216 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_UNKNOWN);
2218 // reset the channel
2219 ata_reset(device);
2221 // check for ATA or ATAPI
2222 outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2223 sc = inb(iobase1+ATA_CB_SC);
2224 sn = inb(iobase1+ATA_CB_SN);
2225 if ((sc==0x01) && (sn==0x01)) {
2226 cl = inb(iobase1+ATA_CB_CL);
2227 ch = inb(iobase1+ATA_CB_CH);
2228 st = inb(iobase1+ATA_CB_STAT);
2230 if ((cl==0x14) && (ch==0xeb)) {
2231 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATAPI);
2232 } else if ((cl==0x00) && (ch==0x00) && (st!=0x00)) {
2233 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATA);
2234 } else if ((cl==0xff) && (ch==0xff)) {
2235 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
2240 type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type);
2242 // Now we send a IDENTIFY command to ATA device
2243 if(type == ATA_TYPE_ATA) {
2244 Bit32u sectors;
2245 Bit16u cylinders, heads, spt, blksize;
2246 Bit8u translation, removable, mode;
2248 //Temporary values to do the transfer
2249 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2250 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2252 if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE, 1, 0, 0, 0, 0L, get_SS(),buffer) !=0 )
2253 BX_PANIC("ata-detect: Failed to detect ATA device\n");
2255 removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2256 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2257 blksize = read_word(get_SS(),buffer+10);
2259 cylinders = read_word(get_SS(),buffer+(1*2)); // word 1
2260 heads = read_word(get_SS(),buffer+(3*2)); // word 3
2261 spt = read_word(get_SS(),buffer+(6*2)); // word 6
2263 sectors = read_dword(get_SS(),buffer+(60*2)); // word 60 and word 61
2265 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2266 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2267 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2268 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2269 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads, heads);
2270 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders, cylinders);
2271 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt, spt);
2272 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors, sectors);
2273 BX_INFO("ata%d-%d: PCHS=%u/%d/%d translation=", channel, slave,cylinders, heads, spt);
2275 translation = inb_cmos(0x39 + channel/2);
2276 for (shift=device%4; shift>0; shift--) translation >>= 2;
2277 translation &= 0x03;
2279 write_byte(ebda_seg,&EbdaData->ata.devices[device].translation, translation);
2281 switch (translation) {
2282 case ATA_TRANSLATION_NONE:
2283 BX_INFO("none");
2284 break;
2285 case ATA_TRANSLATION_LBA:
2286 BX_INFO("lba");
2287 break;
2288 case ATA_TRANSLATION_LARGE:
2289 BX_INFO("large");
2290 break;
2291 case ATA_TRANSLATION_RECHS:
2292 BX_INFO("r-echs");
2293 break;
2295 switch (translation) {
2296 case ATA_TRANSLATION_NONE:
2297 break;
2298 case ATA_TRANSLATION_LBA:
2299 spt = 63;
2300 sectors /= 63;
2301 heads = sectors / 1024;
2302 if (heads>128) heads = 255;
2303 else if (heads>64) heads = 128;
2304 else if (heads>32) heads = 64;
2305 else if (heads>16) heads = 32;
2306 else heads=16;
2307 cylinders = sectors / heads;
2308 break;
2309 case ATA_TRANSLATION_RECHS:
2310 // Take care not to overflow
2311 if (heads==16) {
2312 if(cylinders>61439) cylinders=61439;
2313 heads=15;
2314 cylinders = (Bit16u)((Bit32u)(cylinders)*16/15);
2316 // then go through the large bitshift process
2317 case ATA_TRANSLATION_LARGE:
2318 while(cylinders > 1024) {
2319 cylinders >>= 1;
2320 heads <<= 1;
2322 // If we max out the head count
2323 if (heads > 127) break;
2325 break;
2327 // clip to 1024 cylinders in lchs
2328 if (cylinders > 1024) cylinders=1024;
2329 BX_INFO(" LCHS=%d/%d/%d\n", cylinders, heads, spt);
2331 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads, heads);
2332 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders, cylinders);
2333 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt, spt);
2335 // fill hdidmap
2336 write_byte(ebda_seg,&EbdaData->ata.hdidmap[hdcount], device);
2337 hdcount++;
2340 // Now we send a IDENTIFY command to ATAPI device
2341 if(type == ATA_TYPE_ATAPI) {
2343 Bit8u type, removable, mode;
2344 Bit16u blksize;
2346 //Temporary values to do the transfer
2347 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_CDROM);
2348 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2350 if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE_PACKET, 1, 0, 0, 0, 0L, get_SS(),buffer) != 0)
2351 BX_PANIC("ata-detect: Failed to detect ATAPI device\n");
2353 type = read_byte(get_SS(),buffer+1) & 0x1f;
2354 removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2355 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2356 blksize = 2048;
2358 write_byte(ebda_seg,&EbdaData->ata.devices[device].device, type);
2359 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2360 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2361 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2363 // fill cdidmap
2364 write_byte(ebda_seg,&EbdaData->ata.cdidmap[cdcount], device);
2365 cdcount++;
2369 Bit32u sizeinmb;
2370 Bit16u ataversion;
2371 Bit8u c, i, version, model[41];
2373 switch (type) {
2374 case ATA_TYPE_ATA:
2375 sizeinmb = read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors);
2376 sizeinmb >>= 11;
2377 case ATA_TYPE_ATAPI:
2378 // Read ATA/ATAPI version
2379 ataversion=((Bit16u)(read_byte(get_SS(),buffer+161))<<8)|read_byte(get_SS(),buffer+160);
2380 for(version=15;version>0;version--) {
2381 if((ataversion&(1<<version))!=0)
2382 break;
2385 // Read model name
2386 for(i=0;i<20;i++){
2387 write_byte(get_SS(),model+(i*2),read_byte(get_SS(),buffer+(i*2)+54+1));
2388 write_byte(get_SS(),model+(i*2)+1,read_byte(get_SS(),buffer+(i*2)+54));
2391 // Reformat
2392 write_byte(get_SS(),model+40,0x00);
2393 for(i=39;i>0;i--){
2394 if(read_byte(get_SS(),model+i)==0x20)
2395 write_byte(get_SS(),model+i,0x00);
2396 else break;
2398 break;
2401 switch (type) {
2402 case ATA_TYPE_ATA:
2403 printf("ata%d %s: ",channel,slave?" slave":"master");
2404 i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
2405 printf(" ATA-%d Hard-Disk (%lu MBytes)\n", version, sizeinmb);
2406 break;
2407 case ATA_TYPE_ATAPI:
2408 printf("ata%d %s: ",channel,slave?" slave":"master");
2409 i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
2410 if(read_byte(ebda_seg,&EbdaData->ata.devices[device].device)==ATA_DEVICE_CDROM)
2411 printf(" ATAPI-%d CD-Rom/DVD-Rom\n",version);
2412 else
2413 printf(" ATAPI-%d Device\n",version);
2414 break;
2415 case ATA_TYPE_UNKNOWN:
2416 printf("ata%d %s: Unknown device\n",channel,slave?" slave":"master");
2417 break;
2422 // Store the devices counts
2423 write_byte(ebda_seg,&EbdaData->ata.hdcount, hdcount);
2424 write_byte(ebda_seg,&EbdaData->ata.cdcount, cdcount);
2425 write_byte(0x40,0x75, hdcount);
2427 printf("\n");
2429 // FIXME : should use bios=cmos|auto|disable bits
2430 // FIXME : should know about translation bits
2431 // FIXME : move hard_drive_post here
2435 // ---------------------------------------------------------------------------
2436 // ATA/ATAPI driver : software reset
2437 // ---------------------------------------------------------------------------
2438 // ATA-3
2439 // 8.2.1 Software reset - Device 0
2441 void ata_reset(device)
2442 Bit16u device;
2444 Bit16u ebda_seg=read_word(0x0040,0x000E);
2445 Bit16u iobase1, iobase2;
2446 Bit8u channel, slave, sn, sc;
2447 Bit16u max;
2449 channel = device / 2;
2450 slave = device % 2;
2452 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2453 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2455 // Reset
2457 // 8.2.1 (a) -- set SRST in DC
2458 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST);
2460 // 8.2.1 (b) -- wait for BSY
2461 max=0xff;
2462 while(--max>0) {
2463 Bit8u status = inb(iobase1+ATA_CB_STAT);
2464 if ((status & ATA_CB_STAT_BSY) != 0) break;
2467 // 8.2.1 (f) -- clear SRST
2468 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2470 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_NONE) {
2472 // 8.2.1 (g) -- check for sc==sn==0x01
2473 // select device
2474 outb(iobase1+ATA_CB_DH, slave?ATA_CB_DH_DEV1:ATA_CB_DH_DEV0);
2475 sc = inb(iobase1+ATA_CB_SC);
2476 sn = inb(iobase1+ATA_CB_SN);
2478 if ( (sc==0x01) && (sn==0x01) ) {
2480 // 8.2.1 (h) -- wait for not BSY
2481 max=0xff;
2482 while(--max>0) {
2483 Bit8u status = inb(iobase1+ATA_CB_STAT);
2484 if ((status & ATA_CB_STAT_BSY) == 0) break;
2489 // 8.2.1 (i) -- wait for DRDY
2490 max=0xfff;
2491 while(--max>0) {
2492 Bit8u status = inb(iobase1+ATA_CB_STAT);
2493 if ((status & ATA_CB_STAT_RDY) != 0) break;
2496 // Enable interrupts
2497 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2500 // ---------------------------------------------------------------------------
2501 // ATA/ATAPI driver : execute a non data command
2502 // ---------------------------------------------------------------------------
2504 Bit16u ata_cmd_non_data()
2505 {return 0;}
2507 // ---------------------------------------------------------------------------
2508 // ATA/ATAPI driver : execute a data-in command
2509 // ---------------------------------------------------------------------------
2510 // returns
2511 // 0 : no error
2512 // 1 : BUSY bit set
2513 // 2 : read error
2514 // 3 : expected DRQ=1
2515 // 4 : no sectors left to read/verify
2516 // 5 : more sectors to read/verify
2517 // 6 : no sectors left to write
2518 // 7 : more sectors to write
2519 Bit16u ata_cmd_data_in(device, command, count, cylinder, head, sector, lba, segment, offset)
2520 Bit16u device, command, count, cylinder, head, sector, segment, offset;
2521 Bit32u lba;
2523 Bit16u ebda_seg=read_word(0x0040,0x000E);
2524 Bit16u iobase1, iobase2, blksize;
2525 Bit8u channel, slave;
2526 Bit8u status, current, mode;
2528 channel = device / 2;
2529 slave = device % 2;
2531 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2532 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2533 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2534 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
2535 if (mode == ATA_MODE_PIO32) blksize>>=2;
2536 else blksize>>=1;
2538 // sector will be 0 only on lba access. Convert to lba-chs
2539 if (sector == 0) {
2540 sector = (Bit16u) (lba & 0x000000ffL);
2541 lba >>= 8;
2542 cylinder = (Bit16u) (lba & 0x0000ffffL);
2543 lba >>= 16;
2544 head = ((Bit16u) (lba & 0x0000000fL)) | 0x40;
2547 // Reset count of transferred data
2548 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2549 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2550 current = 0;
2552 status = inb(iobase1 + ATA_CB_STAT);
2553 if (status & ATA_CB_STAT_BSY) return 1;
2555 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2556 outb(iobase1 + ATA_CB_FR, 0x00);
2557 outb(iobase1 + ATA_CB_SC, count);
2558 outb(iobase1 + ATA_CB_SN, sector);
2559 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
2560 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
2561 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
2562 outb(iobase1 + ATA_CB_CMD, command);
2564 while (1) {
2565 status = inb(iobase1 + ATA_CB_STAT);
2566 if ( !(status & ATA_CB_STAT_BSY) ) break;
2569 if (status & ATA_CB_STAT_ERR) {
2570 BX_DEBUG_ATA("ata_cmd_data_in : read error\n");
2571 return 2;
2572 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2573 BX_DEBUG_ATA("ata_cmd_data_in : DRQ not set (status %02x)\n", (unsigned) status);
2574 return 3;
2577 // FIXME : move seg/off translation here
2579 ASM_START
2580 sti ;; enable higher priority interrupts
2581 ASM_END
2583 while (1) {
2585 ASM_START
2586 push bp
2587 mov bp, sp
2588 mov di, _ata_cmd_data_in.offset + 2[bp]
2589 mov ax, _ata_cmd_data_in.segment + 2[bp]
2590 mov cx, _ata_cmd_data_in.blksize + 2[bp]
2592 ;; adjust if there will be an overrun. 2K max sector size
2593 cmp di, #0xf800 ;;
2594 jbe ata_in_no_adjust
2596 ata_in_adjust:
2597 sub di, #0x0800 ;; sub 2 kbytes from offset
2598 add ax, #0x0080 ;; add 2 Kbytes to segment
2600 ata_in_no_adjust:
2601 mov es, ax ;; segment in es
2603 mov dx, _ata_cmd_data_in.iobase1 + 2[bp] ;; ATA data read port
2605 mov ah, _ata_cmd_data_in.mode + 2[bp]
2606 cmp ah, #ATA_MODE_PIO32
2607 je ata_in_32
2609 ata_in_16:
2611 insw ;; CX words transfered from port(DX) to ES:[DI]
2612 jmp ata_in_done
2614 ata_in_32:
2616 insd ;; CX dwords transfered from port(DX) to ES:[DI]
2618 ata_in_done:
2619 mov _ata_cmd_data_in.offset + 2[bp], di
2620 mov _ata_cmd_data_in.segment + 2[bp], es
2621 pop bp
2622 ASM_END
2624 current++;
2625 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
2626 count--;
2627 status = inb(iobase1 + ATA_CB_STAT);
2628 if (count == 0) {
2629 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2630 != ATA_CB_STAT_RDY ) {
2631 BX_DEBUG_ATA("ata_cmd_data_in : no sectors left (status %02x)\n", (unsigned) status);
2632 return 4;
2634 break;
2636 else {
2637 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2638 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2639 BX_DEBUG_ATA("ata_cmd_data_in : more sectors left (status %02x)\n", (unsigned) status);
2640 return 5;
2642 continue;
2645 // Enable interrupts
2646 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2647 return 0;
2650 // ---------------------------------------------------------------------------
2651 // ATA/ATAPI driver : execute a data-out command
2652 // ---------------------------------------------------------------------------
2653 // returns
2654 // 0 : no error
2655 // 1 : BUSY bit set
2656 // 2 : read error
2657 // 3 : expected DRQ=1
2658 // 4 : no sectors left to read/verify
2659 // 5 : more sectors to read/verify
2660 // 6 : no sectors left to write
2661 // 7 : more sectors to write
2662 Bit16u ata_cmd_data_out(device, command, count, cylinder, head, sector, lba, segment, offset)
2663 Bit16u device, command, count, cylinder, head, sector, segment, offset;
2664 Bit32u lba;
2666 Bit16u ebda_seg=read_word(0x0040,0x000E);
2667 Bit16u iobase1, iobase2, blksize;
2668 Bit8u channel, slave;
2669 Bit8u status, current, mode;
2671 channel = device / 2;
2672 slave = device % 2;
2674 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2675 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2676 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2677 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
2678 if (mode == ATA_MODE_PIO32) blksize>>=2;
2679 else blksize>>=1;
2681 // sector will be 0 only on lba access. Convert to lba-chs
2682 if (sector == 0) {
2683 sector = (Bit16u) (lba & 0x000000ffL);
2684 lba >>= 8;
2685 cylinder = (Bit16u) (lba & 0x0000ffffL);
2686 lba >>= 16;
2687 head = ((Bit16u) (lba & 0x0000000fL)) | 0x40;
2690 // Reset count of transferred data
2691 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2692 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2693 current = 0;
2695 status = inb(iobase1 + ATA_CB_STAT);
2696 if (status & ATA_CB_STAT_BSY) return 1;
2698 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2699 outb(iobase1 + ATA_CB_FR, 0x00);
2700 outb(iobase1 + ATA_CB_SC, count);
2701 outb(iobase1 + ATA_CB_SN, sector);
2702 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
2703 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
2704 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
2705 outb(iobase1 + ATA_CB_CMD, command);
2707 while (1) {
2708 status = inb(iobase1 + ATA_CB_STAT);
2709 if ( !(status & ATA_CB_STAT_BSY) ) break;
2712 if (status & ATA_CB_STAT_ERR) {
2713 BX_DEBUG_ATA("ata_cmd_data_out : read error\n");
2714 return 2;
2715 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2716 BX_DEBUG_ATA("ata_cmd_data_out : DRQ not set (status %02x)\n", (unsigned) status);
2717 return 3;
2720 // FIXME : move seg/off translation here
2722 ASM_START
2723 sti ;; enable higher priority interrupts
2724 ASM_END
2726 while (1) {
2728 ASM_START
2729 push bp
2730 mov bp, sp
2731 mov si, _ata_cmd_data_out.offset + 2[bp]
2732 mov ax, _ata_cmd_data_out.segment + 2[bp]
2733 mov cx, _ata_cmd_data_out.blksize + 2[bp]
2735 ;; adjust if there will be an overrun. 2K max sector size
2736 cmp si, #0xf800 ;;
2737 jbe ata_out_no_adjust
2739 ata_out_adjust:
2740 sub si, #0x0800 ;; sub 2 kbytes from offset
2741 add ax, #0x0080 ;; add 2 Kbytes to segment
2743 ata_out_no_adjust:
2744 mov es, ax ;; segment in es
2746 mov dx, _ata_cmd_data_out.iobase1 + 2[bp] ;; ATA data write port
2748 mov ah, _ata_cmd_data_out.mode + 2[bp]
2749 cmp ah, #ATA_MODE_PIO32
2750 je ata_out_32
2752 ata_out_16:
2753 seg ES
2755 outsw ;; CX words transfered from port(DX) to ES:[SI]
2756 jmp ata_out_done
2758 ata_out_32:
2759 seg ES
2761 outsd ;; CX dwords transfered from port(DX) to ES:[SI]
2763 ata_out_done:
2764 mov _ata_cmd_data_out.offset + 2[bp], si
2765 mov _ata_cmd_data_out.segment + 2[bp], es
2766 pop bp
2767 ASM_END
2769 current++;
2770 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
2771 count--;
2772 status = inb(iobase1 + ATA_CB_STAT);
2773 if (count == 0) {
2774 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2775 != ATA_CB_STAT_RDY ) {
2776 BX_DEBUG_ATA("ata_cmd_data_out : no sectors left (status %02x)\n", (unsigned) status);
2777 return 6;
2779 break;
2781 else {
2782 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2783 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2784 BX_DEBUG_ATA("ata_cmd_data_out : more sectors left (status %02x)\n", (unsigned) status);
2785 return 7;
2787 continue;
2790 // Enable interrupts
2791 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2792 return 0;
2795 // ---------------------------------------------------------------------------
2796 // ATA/ATAPI driver : execute a packet command
2797 // ---------------------------------------------------------------------------
2798 // returns
2799 // 0 : no error
2800 // 1 : error in parameters
2801 // 2 : BUSY bit set
2802 // 3 : error
2803 // 4 : not ready
2804 Bit16u ata_cmd_packet(device, cmdlen, cmdseg, cmdoff, header, length, inout, bufseg, bufoff)
2805 Bit8u cmdlen,inout;
2806 Bit16u device,cmdseg, cmdoff, bufseg, bufoff;
2807 Bit16u header;
2808 Bit32u length;
2810 Bit16u ebda_seg=read_word(0x0040,0x000E);
2811 Bit16u iobase1, iobase2;
2812 Bit16u lcount, lbefore, lafter, count;
2813 Bit8u channel, slave;
2814 Bit8u status, mode, lmode;
2815 Bit32u total, transfer;
2817 channel = device / 2;
2818 slave = device % 2;
2820 // Data out is not supported yet
2821 if (inout == ATA_DATA_OUT) {
2822 BX_INFO("ata_cmd_packet: DATA_OUT not supported yet\n");
2823 return 1;
2826 // The header length must be even
2827 if (header & 1) {
2828 BX_DEBUG_ATA("ata_cmd_packet : header must be even (%04x)\n",header);
2829 return 1;
2832 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2833 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2834 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2835 transfer= 0L;
2837 if (cmdlen < 12) cmdlen=12;
2838 if (cmdlen > 12) cmdlen=16;
2839 cmdlen>>=1;
2841 // Reset count of transferred data
2842 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2843 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2845 status = inb(iobase1 + ATA_CB_STAT);
2846 if (status & ATA_CB_STAT_BSY) return 2;
2848 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2849 // outb(iobase1 + ATA_CB_FR, 0x00);
2850 // outb(iobase1 + ATA_CB_SC, 0x00);
2851 // outb(iobase1 + ATA_CB_SN, 0x00);
2852 outb(iobase1 + ATA_CB_CL, 0xfff0 & 0x00ff);
2853 outb(iobase1 + ATA_CB_CH, 0xfff0 >> 8);
2854 outb(iobase1 + ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2855 outb(iobase1 + ATA_CB_CMD, ATA_CMD_PACKET);
2857 // Device should ok to receive command
2858 while (1) {
2859 status = inb(iobase1 + ATA_CB_STAT);
2860 if ( !(status & ATA_CB_STAT_BSY) ) break;
2863 if (status & ATA_CB_STAT_ERR) {
2864 BX_DEBUG_ATA("ata_cmd_packet : error, status is %02x\n",status);
2865 return 3;
2866 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2867 BX_DEBUG_ATA("ata_cmd_packet : DRQ not set (status %02x)\n", (unsigned) status);
2868 return 4;
2871 // Normalize address
2872 cmdseg += (cmdoff / 16);
2873 cmdoff %= 16;
2875 // Send command to device
2876 ASM_START
2877 sti ;; enable higher priority interrupts
2879 push bp
2880 mov bp, sp
2882 mov si, _ata_cmd_packet.cmdoff + 2[bp]
2883 mov ax, _ata_cmd_packet.cmdseg + 2[bp]
2884 mov cx, _ata_cmd_packet.cmdlen + 2[bp]
2885 mov es, ax ;; segment in es
2887 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data write port
2889 seg ES
2891 outsw ;; CX words transfered from port(DX) to ES:[SI]
2893 pop bp
2894 ASM_END
2896 if (inout == ATA_DATA_NO) {
2897 status = inb(iobase1 + ATA_CB_STAT);
2899 else {
2900 while (1) {
2902 status = inb(iobase1 + ATA_CB_STAT);
2904 // Check if command completed
2905 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ) ) ==0 ) break;
2907 if (status & ATA_CB_STAT_ERR) {
2908 BX_DEBUG_ATA("ata_cmd_packet : error (status %02x)\n",status);
2909 return 3;
2912 // Device must be ready to send data
2913 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2914 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2915 BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", status);
2916 return 4;
2919 // Normalize address
2920 bufseg += (bufoff / 16);
2921 bufoff %= 16;
2923 // Get the byte count
2924 lcount = ((Bit16u)(inb(iobase1 + ATA_CB_CH))<<8)+inb(iobase1 + ATA_CB_CL);
2926 // adjust to read what we want
2927 if(header>lcount) {
2928 lbefore=lcount;
2929 header-=lcount;
2930 lcount=0;
2932 else {
2933 lbefore=header;
2934 header=0;
2935 lcount-=lbefore;
2938 if(lcount>length) {
2939 lafter=lcount-length;
2940 lcount=length;
2941 length=0;
2943 else {
2944 lafter=0;
2945 length-=lcount;
2948 // Save byte count
2949 count = lcount;
2951 BX_DEBUG_ATA("Trying to read %04x bytes (%04x %04x %04x) ",lbefore+lcount+lafter,lbefore,lcount,lafter);
2952 BX_DEBUG_ATA("to 0x%04x:0x%04x\n",bufseg,bufoff);
2954 // If counts not dividable by 4, use 16bits mode
2955 lmode = mode;
2956 if (lbefore & 0x03) lmode=ATA_MODE_PIO16;
2957 if (lcount & 0x03) lmode=ATA_MODE_PIO16;
2958 if (lafter & 0x03) lmode=ATA_MODE_PIO16;
2960 // adds an extra byte if count are odd. before is always even
2961 if (lcount & 0x01) {
2962 lcount+=1;
2963 if ((lafter > 0) && (lafter & 0x01)) {
2964 lafter-=1;
2968 if (lmode == ATA_MODE_PIO32) {
2969 lcount>>=2; lbefore>>=2; lafter>>=2;
2971 else {
2972 lcount>>=1; lbefore>>=1; lafter>>=1;
2975 ; // FIXME bcc bug
2977 ASM_START
2978 push bp
2979 mov bp, sp
2981 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data read port
2983 mov cx, _ata_cmd_packet.lbefore + 2[bp]
2984 jcxz ata_packet_no_before
2986 mov ah, _ata_cmd_packet.lmode + 2[bp]
2987 cmp ah, #ATA_MODE_PIO32
2988 je ata_packet_in_before_32
2990 ata_packet_in_before_16:
2991 in ax, dx
2992 loop ata_packet_in_before_16
2993 jmp ata_packet_no_before
2995 ata_packet_in_before_32:
2996 push eax
2997 ata_packet_in_before_32_loop:
2998 in eax, dx
2999 loop ata_packet_in_before_32_loop
3000 pop eax
3002 ata_packet_no_before:
3003 mov cx, _ata_cmd_packet.lcount + 2[bp]
3004 jcxz ata_packet_after
3006 mov di, _ata_cmd_packet.bufoff + 2[bp]
3007 mov ax, _ata_cmd_packet.bufseg + 2[bp]
3008 mov es, ax
3010 mov ah, _ata_cmd_packet.lmode + 2[bp]
3011 cmp ah, #ATA_MODE_PIO32
3012 je ata_packet_in_32
3014 ata_packet_in_16:
3016 insw ;; CX words transfered tp port(DX) to ES:[DI]
3017 jmp ata_packet_after
3019 ata_packet_in_32:
3021 insd ;; CX dwords transfered to port(DX) to ES:[DI]
3023 ata_packet_after:
3024 mov cx, _ata_cmd_packet.lafter + 2[bp]
3025 jcxz ata_packet_done
3027 mov ah, _ata_cmd_packet.lmode + 2[bp]
3028 cmp ah, #ATA_MODE_PIO32
3029 je ata_packet_in_after_32
3031 ata_packet_in_after_16:
3032 in ax, dx
3033 loop ata_packet_in_after_16
3034 jmp ata_packet_done
3036 ata_packet_in_after_32:
3037 push eax
3038 ata_packet_in_after_32_loop:
3039 in eax, dx
3040 loop ata_packet_in_after_32_loop
3041 pop eax
3043 ata_packet_done:
3044 pop bp
3045 ASM_END
3047 // Compute new buffer address
3048 bufoff += count;
3050 // Save transferred bytes count
3051 transfer += count;
3052 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,transfer);
3056 // Final check, device must be ready
3057 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
3058 != ATA_CB_STAT_RDY ) {
3059 BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", (unsigned) status);
3060 return 4;
3063 // Enable interrupts
3064 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
3065 return 0;
3068 // ---------------------------------------------------------------------------
3069 // End of ATA/ATAPI Driver
3070 // ---------------------------------------------------------------------------
3072 // ---------------------------------------------------------------------------
3073 // Start of ATA/ATAPI generic functions
3074 // ---------------------------------------------------------------------------
3076 Bit16u
3077 atapi_get_sense(device)
3078 Bit16u device;
3080 Bit8u atacmd[12];
3081 Bit8u buffer[16];
3082 Bit8u i;
3084 memsetb(get_SS(),atacmd,0,12);
3086 // Request SENSE
3087 atacmd[0]=0x03;
3088 atacmd[4]=0x20;
3089 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 16L, ATA_DATA_IN, get_SS(), buffer) != 0)
3090 return 0x0002;
3092 if ((buffer[0] & 0x7e) == 0x70) {
3093 return (((Bit16u)buffer[2]&0x0f)*0x100)+buffer[12];
3096 return 0;
3099 Bit16u
3100 atapi_is_ready(device)
3101 Bit16u device;
3103 Bit8u atacmd[12];
3104 Bit8u buffer[];
3106 memsetb(get_SS(),atacmd,0,12);
3108 // Test Unit Ready
3109 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0)
3110 return 0x000f;
3112 if (atapi_get_sense(device) !=0 ) {
3113 memsetb(get_SS(),atacmd,0,12);
3115 // try to send Test Unit Ready again
3116 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0)
3117 return 0x000f;
3119 return atapi_get_sense(device);
3121 return 0;
3124 Bit16u
3125 atapi_is_cdrom(device)
3126 Bit8u device;
3128 Bit16u ebda_seg=read_word(0x0040,0x000E);
3130 if (device >= BX_MAX_ATA_DEVICES)
3131 return 0;
3133 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI)
3134 return 0;
3136 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].device) != ATA_DEVICE_CDROM)
3137 return 0;
3139 return 1;
3142 // ---------------------------------------------------------------------------
3143 // End of ATA/ATAPI generic functions
3144 // ---------------------------------------------------------------------------
3146 #endif // BX_USE_ATADRV
3148 #if BX_ELTORITO_BOOT
3150 // ---------------------------------------------------------------------------
3151 // Start of El-Torito boot functions
3152 // ---------------------------------------------------------------------------
3154 void
3155 cdemu_init()
3157 Bit16u ebda_seg=read_word(0x0040,0x000E);
3159 // the only important data is this one for now
3160 write_byte(ebda_seg,&EbdaData->cdemu.active,0x00);
3163 Bit8u
3164 cdemu_isactive()
3166 Bit16u ebda_seg=read_word(0x0040,0x000E);
3168 return(read_byte(ebda_seg,&EbdaData->cdemu.active));
3171 Bit8u
3172 cdemu_emulated_drive()
3174 Bit16u ebda_seg=read_word(0x0040,0x000E);
3176 return(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
3179 static char isotag[6]="CD001";
3180 static char eltorito[24]="EL TORITO SPECIFICATION";
3182 // Returns ah: emulated drive, al: error code
3184 Bit16u
3185 cdrom_boot()
3187 Bit16u ebda_seg=read_word(0x0040,0x000E);
3188 Bit8u atacmd[12], buffer[2048];
3189 Bit32u lba;
3190 Bit16u boot_segment, nbsectors, i, error;
3191 Bit8u device;
3193 // Find out the first cdrom
3194 for (device=0; device<BX_MAX_ATA_DEVICES;device++) {
3195 if (atapi_is_cdrom(device)) break;
3198 // if not found
3199 if(device >= BX_MAX_ATA_DEVICES) return 2;
3201 // Read the Boot Record Volume Descriptor
3202 memsetb(get_SS(),atacmd,0,12);
3203 atacmd[0]=0x28; // READ command
3204 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3205 atacmd[8]=(0x01 & 0x00ff); // Sectors
3206 atacmd[2]=(0x11 & 0xff000000) >> 24; // LBA
3207 atacmd[3]=(0x11 & 0x00ff0000) >> 16;
3208 atacmd[4]=(0x11 & 0x0000ff00) >> 8;
3209 atacmd[5]=(0x11 & 0x000000ff);
3210 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3211 return 3;
3213 // Validity checks
3214 if(buffer[0]!=0)return 4;
3215 for(i=0;i<5;i++){
3216 if(buffer[1+i]!=read_byte(0xf000,&isotag[i]))return 5;
3218 for(i=0;i<23;i++)
3219 if(buffer[7+i]!=read_byte(0xf000,&eltorito[i]))return 6;
3221 // ok, now we calculate the Boot catalog address
3222 lba=buffer[0x4A]*0x1000000+buffer[0x49]*0x10000+buffer[0x48]*0x100+buffer[0x47];
3224 // And we read the Boot Catalog
3225 memsetb(get_SS(),atacmd,0,12);
3226 atacmd[0]=0x28; // READ command
3227 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3228 atacmd[8]=(0x01 & 0x00ff); // Sectors
3229 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3230 atacmd[3]=(lba & 0x00ff0000) >> 16;
3231 atacmd[4]=(lba & 0x0000ff00) >> 8;
3232 atacmd[5]=(lba & 0x000000ff);
3233 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3234 return 7;
3236 // Validation entry
3237 if(buffer[0x00]!=0x01)return 8; // Header
3238 if(buffer[0x01]!=0x00)return 9; // Platform
3239 if(buffer[0x1E]!=0x55)return 10; // key 1
3240 if(buffer[0x1F]!=0xAA)return 10; // key 2
3242 // Initial/Default Entry
3243 if(buffer[0x20]!=0x88)return 11; // Bootable
3245 write_byte(ebda_seg,&EbdaData->cdemu.media,buffer[0x21]);
3246 if(buffer[0x21]==0){
3247 // FIXME ElTorito Hardcoded. cdrom is hardcoded as device 0xE0.
3248 // Win2000 cd boot needs to know it booted from cd
3249 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0xE0);
3251 else if(buffer[0x21]<4)
3252 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x00);
3253 else
3254 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x80);
3256 write_byte(ebda_seg,&EbdaData->cdemu.controller_index,device/2);
3257 write_byte(ebda_seg,&EbdaData->cdemu.device_spec,device%2);
3259 boot_segment=buffer[0x23]*0x100+buffer[0x22];
3260 if(boot_segment==0x0000)boot_segment=0x07C0;
3262 write_word(ebda_seg,&EbdaData->cdemu.load_segment,boot_segment);
3263 write_word(ebda_seg,&EbdaData->cdemu.buffer_segment,0x0000);
3265 nbsectors=buffer[0x27]*0x100+buffer[0x26];
3266 write_word(ebda_seg,&EbdaData->cdemu.sector_count,nbsectors);
3268 lba=buffer[0x2B]*0x1000000+buffer[0x2A]*0x10000+buffer[0x29]*0x100+buffer[0x28];
3269 write_dword(ebda_seg,&EbdaData->cdemu.ilba,lba);
3271 // And we read the image in memory
3272 memsetb(get_SS(),atacmd,0,12);
3273 atacmd[0]=0x28; // READ command
3274 atacmd[7]=((1+(nbsectors-1)/4) & 0xff00) >> 8; // Sectors
3275 atacmd[8]=((1+(nbsectors-1)/4) & 0x00ff); // Sectors
3276 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3277 atacmd[3]=(lba & 0x00ff0000) >> 16;
3278 atacmd[4]=(lba & 0x0000ff00) >> 8;
3279 atacmd[5]=(lba & 0x000000ff);
3280 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, nbsectors*512L, ATA_DATA_IN, boot_segment,0)) != 0)
3281 return 12;
3283 // Remember the media type
3284 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
3285 case 0x01: // 1.2M floppy
3286 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,15);
3287 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3288 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3289 break;
3290 case 0x02: // 1.44M floppy
3291 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,18);
3292 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3293 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3294 break;
3295 case 0x03: // 2.88M floppy
3296 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,36);
3297 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3298 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3299 break;
3300 case 0x04: // Harddrive
3301 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,read_byte(boot_segment,446+6)&0x3f);
3302 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,
3303 (read_byte(boot_segment,446+6)<<2) + read_byte(boot_segment,446+7) + 1);
3304 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,read_byte(boot_segment,446+5) + 1);
3305 break;
3308 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0) {
3309 // Increase bios installed hardware number of devices
3310 if(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)==0x00)
3311 write_byte(0x40,0x10,read_byte(0x40,0x10)|0x41);
3312 else
3313 write_byte(ebda_seg, &EbdaData->ata.hdcount, read_byte(ebda_seg, &EbdaData->ata.hdcount) + 1);
3317 // everything is ok, so from now on, the emulation is active
3318 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0)
3319 write_byte(ebda_seg,&EbdaData->cdemu.active,0x01);
3321 // return the boot drive + no error
3322 return (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)*0x100)+0;
3325 // ---------------------------------------------------------------------------
3326 // End of El-Torito boot functions
3327 // ---------------------------------------------------------------------------
3328 #endif // BX_ELTORITO_BOOT
3330 void
3331 int14_function(regs, ds, iret_addr)
3332 pusha_regs_t regs; // regs pushed from PUSHA instruction
3333 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
3334 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
3336 Bit16u addr,timer,val16;
3337 Bit8u timeout;
3339 ASM_START
3341 ASM_END
3343 addr = read_word(0x0040, (regs.u.r16.dx << 1));
3344 timeout = read_byte(0x0040, 0x007C + regs.u.r16.dx);
3345 if ((regs.u.r16.dx < 4) && (addr > 0)) {
3346 switch (regs.u.r8.ah) {
3347 case 0:
3348 outb(addr+3, inb(addr+3) | 0x80);
3349 if (regs.u.r8.al & 0xE0 == 0) {
3350 outb(addr, 0x17);
3351 outb(addr+1, 0x04);
3352 } else {
3353 val16 = 0x600 >> ((regs.u.r8.al & 0xE0) >> 5);
3354 outb(addr, val16 & 0xFF);
3355 outb(addr+1, val16 >> 8);
3357 outb(addr+3, regs.u.r8.al & 0x1F);
3358 regs.u.r8.ah = inb(addr+5);
3359 regs.u.r8.al = inb(addr+6);
3360 ClearCF(iret_addr.flags);
3361 break;
3362 case 1:
3363 timer = read_word(0x0040, 0x006C);
3364 while (((inb(addr+5) & 0x60) != 0x60) && (timeout)) {
3365 val16 = read_word(0x0040, 0x006C);
3366 if (val16 != timer) {
3367 timer = val16;
3368 timeout--;
3371 if (timeout) outb(addr, regs.u.r8.al);
3372 regs.u.r8.ah = inb(addr+5);
3373 if (!timeout) regs.u.r8.ah |= 0x80;
3374 ClearCF(iret_addr.flags);
3375 break;
3376 case 2:
3377 timer = read_word(0x0040, 0x006C);
3378 while (((inb(addr+5) & 0x01) == 0) && (timeout)) {
3379 val16 = read_word(0x0040, 0x006C);
3380 if (val16 != timer) {
3381 timer = val16;
3382 timeout--;
3385 if (timeout) {
3386 regs.u.r8.ah = 0;
3387 regs.u.r8.al = inb(addr);
3388 } else {
3389 regs.u.r8.ah = inb(addr+5);
3391 ClearCF(iret_addr.flags);
3392 break;
3393 case 3:
3394 regs.u.r8.ah = inb(addr+5);
3395 regs.u.r8.al = inb(addr+6);
3396 ClearCF(iret_addr.flags);
3397 break;
3398 default:
3399 SetCF(iret_addr.flags); // Unsupported
3401 } else {
3402 SetCF(iret_addr.flags); // Unsupported
3406 void
3407 int15_function(regs, ES, DS, FLAGS)
3408 pusha_regs_t regs; // REGS pushed via pusha
3409 Bit16u ES, DS, FLAGS;
3411 Bit16u ebda_seg=read_word(0x0040,0x000E);
3412 bx_bool prev_a20_enable;
3413 Bit16u base15_00;
3414 Bit8u base23_16;
3415 Bit16u ss;
3416 Bit16u CX,DX;
3418 Bit16u bRegister;
3419 Bit8u irqDisable;
3421 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3423 switch (regs.u.r8.ah) {
3424 case 0x24: /* A20 Control */
3425 switch (regs.u.r8.al) {
3426 case 0x00:
3427 set_enable_a20(0);
3428 CLEAR_CF();
3429 regs.u.r8.ah = 0;
3430 break;
3431 case 0x01:
3432 set_enable_a20(1);
3433 CLEAR_CF();
3434 regs.u.r8.ah = 0;
3435 break;
3436 case 0x02:
3437 regs.u.r8.al = (inb(0x92) >> 1) & 0x01;
3438 CLEAR_CF();
3439 regs.u.r8.ah = 0;
3440 break;
3441 case 0x03:
3442 CLEAR_CF();
3443 regs.u.r8.ah = 0;
3444 regs.u.r16.bx = 3;
3445 break;
3446 default:
3447 BX_INFO("int15: Func 24h, subfunc %02xh, A20 gate control not supported\n", (unsigned) regs.u.r8.al);
3448 SET_CF();
3449 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3451 break;
3453 case 0x41:
3454 SET_CF();
3455 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3456 break;
3458 case 0x4f:
3459 /* keyboard intercept */
3460 #if BX_CPU < 2
3461 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3462 #else
3463 // nop
3464 #endif
3465 SET_CF();
3466 break;
3468 case 0x52: // removable media eject
3469 CLEAR_CF();
3470 regs.u.r8.ah = 0; // "ok ejection may proceed"
3471 break;
3473 case 0x83: {
3474 if( regs.u.r8.al == 0 ) {
3475 // Set Interval requested.
3476 if( ( read_byte( 0x40, 0xA0 ) & 1 ) == 0 ) {
3477 // Interval not already set.
3478 write_byte( 0x40, 0xA0, 1 ); // Set status byte.
3479 write_word( 0x40, 0x98, ES ); // Byte location, segment
3480 write_word( 0x40, 0x9A, regs.u.r16.bx ); // Byte location, offset
3481 write_word( 0x40, 0x9C, regs.u.r16.dx ); // Low word, delay
3482 write_word( 0x40, 0x9E, regs.u.r16.cx ); // High word, delay.
3483 CLEAR_CF( );
3484 irqDisable = inb( 0xA1 );
3485 outb( 0xA1, irqDisable & 0xFE );
3486 bRegister = inb_cmos( 0xB ); // Unmask IRQ8 so INT70 will get through.
3487 outb_cmos( 0xB, bRegister | 0x40 ); // Turn on the Periodic Interrupt timer
3488 } else {
3489 // Interval already set.
3490 BX_DEBUG_INT15("int15: Func 83h, failed, already waiting.\n" );
3491 SET_CF();
3492 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3494 } else if( regs.u.r8.al == 1 ) {
3495 // Clear Interval requested
3496 write_byte( 0x40, 0xA0, 0 ); // Clear status byte
3497 CLEAR_CF( );
3498 bRegister = inb_cmos( 0xB );
3499 outb_cmos( 0xB, bRegister & ~0x40 ); // Turn off the Periodic Interrupt timer
3500 } else {
3501 BX_DEBUG_INT15("int15: Func 83h, failed.\n" );
3502 SET_CF();
3503 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3504 regs.u.r8.al--;
3507 break;
3510 case 0x87:
3511 #if BX_CPU < 3
3512 # error "Int15 function 87h not supported on < 80386"
3513 #endif
3514 // +++ should probably have descriptor checks
3515 // +++ should have exception handlers
3517 // turn off interrupts
3518 ASM_START
3520 ASM_END
3522 prev_a20_enable = set_enable_a20(1); // enable A20 line
3524 // 128K max of transfer on 386+ ???
3525 // source == destination ???
3527 // ES:SI points to descriptor table
3528 // offset use initially comments
3529 // ==============================================
3530 // 00..07 Unused zeros Null descriptor
3531 // 08..0f GDT zeros filled in by BIOS
3532 // 10..17 source ssssssss source of data
3533 // 18..1f dest dddddddd destination of data
3534 // 20..27 CS zeros filled in by BIOS
3535 // 28..2f SS zeros filled in by BIOS
3537 //es:si
3538 //eeee0
3539 //0ssss
3540 //-----
3542 // check for access rights of source & dest here
3544 // Initialize GDT descriptor
3545 base15_00 = (ES << 4) + regs.u.r16.si;
3546 base23_16 = ES >> 12;
3547 if (base15_00 < (ES<<4))
3548 base23_16++;
3549 write_word(ES, regs.u.r16.si+0x08+0, 47); // limit 15:00 = 6 * 8bytes/descriptor
3550 write_word(ES, regs.u.r16.si+0x08+2, base15_00);// base 15:00
3551 write_byte(ES, regs.u.r16.si+0x08+4, base23_16);// base 23:16
3552 write_byte(ES, regs.u.r16.si+0x08+5, 0x93); // access
3553 write_word(ES, regs.u.r16.si+0x08+6, 0x0000); // base 31:24/reserved/limit 19:16
3555 // Initialize CS descriptor
3556 write_word(ES, regs.u.r16.si+0x20+0, 0xffff);// limit 15:00 = normal 64K limit
3557 write_word(ES, regs.u.r16.si+0x20+2, 0x0000);// base 15:00
3558 write_byte(ES, regs.u.r16.si+0x20+4, 0x000f);// base 23:16
3559 write_byte(ES, regs.u.r16.si+0x20+5, 0x9b); // access
3560 write_word(ES, regs.u.r16.si+0x20+6, 0x0000);// base 31:24/reserved/limit 19:16
3562 // Initialize SS descriptor
3563 ss = get_SS();
3564 base15_00 = ss << 4;
3565 base23_16 = ss >> 12;
3566 write_word(ES, regs.u.r16.si+0x28+0, 0xffff); // limit 15:00 = normal 64K limit
3567 write_word(ES, regs.u.r16.si+0x28+2, base15_00);// base 15:00
3568 write_byte(ES, regs.u.r16.si+0x28+4, base23_16);// base 23:16
3569 write_byte(ES, regs.u.r16.si+0x28+5, 0x93); // access
3570 write_word(ES, regs.u.r16.si+0x28+6, 0x0000); // base 31:24/reserved/limit 19:16
3572 CX = regs.u.r16.cx;
3573 ASM_START
3574 // Compile generates locals offset info relative to SP.
3575 // Get CX (word count) from stack.
3576 mov bx, sp
3577 SEG SS
3578 mov cx, _int15_function.CX [bx]
3580 // since we need to set SS:SP, save them to the BDA
3581 // for future restore
3582 push eax
3583 xor eax, eax
3584 mov ds, ax
3585 mov 0x0469, ss
3586 mov 0x0467, sp
3588 SEG ES
3589 lgdt [si + 0x08]
3590 SEG CS
3591 lidt [pmode_IDT_info]
3592 ;; perhaps do something with IDT here
3594 ;; set PE bit in CR0
3595 mov eax, cr0
3596 or al, #0x01
3597 mov cr0, eax
3598 ;; far jump to flush CPU queue after transition to protected mode
3599 JMP_AP(0x0020, protected_mode)
3601 protected_mode:
3602 ;; GDT points to valid descriptor table, now load SS, DS, ES
3603 mov ax, #0x28 ;; 101 000 = 5th descriptor in table, TI=GDT, RPL=00
3604 mov ss, ax
3605 mov ax, #0x10 ;; 010 000 = 2nd descriptor in table, TI=GDT, RPL=00
3606 mov ds, ax
3607 mov ax, #0x18 ;; 011 000 = 3rd descriptor in table, TI=GDT, RPL=00
3608 mov es, ax
3609 xor si, si
3610 xor di, di
3613 movsw ;; move CX words from DS:SI to ES:DI
3615 ;; make sure DS and ES limits are 64KB
3616 mov ax, #0x28
3617 mov ds, ax
3618 mov es, ax
3620 ;; reset PG bit in CR0 ???
3621 mov eax, cr0
3622 and al, #0xFE
3623 mov cr0, eax
3625 ;; far jump to flush CPU queue after transition to real mode
3626 JMP_AP(0xf000, real_mode)
3628 real_mode:
3629 ;; restore IDT to normal real-mode defaults
3630 SEG CS
3631 lidt [rmode_IDT_info]
3633 // restore SS:SP from the BDA
3634 xor ax, ax
3635 mov ds, ax
3636 mov ss, 0x0469
3637 mov sp, 0x0467
3638 pop eax
3639 ASM_END
3641 set_enable_a20(prev_a20_enable);
3643 // turn back on interrupts
3644 ASM_START
3646 ASM_END
3648 regs.u.r8.ah = 0;
3649 CLEAR_CF();
3650 break;
3653 case 0x88:
3654 // Get the amount of extended memory (above 1M)
3655 #if BX_CPU < 2
3656 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3657 SET_CF();
3658 #else
3659 regs.u.r8.al = inb_cmos(0x30);
3660 regs.u.r8.ah = inb_cmos(0x31);
3662 // According to Ralf Brown's interrupt the limit should be 15M,
3663 // but real machines mostly return max. 63M.
3664 if(regs.u.r16.ax > 0xffc0)
3665 regs.u.r16.ax = 0xffc0;
3667 CLEAR_CF();
3668 #endif
3669 break;
3671 case 0x90:
3672 /* Device busy interrupt. Called by Int 16h when no key available */
3673 break;
3675 case 0x91:
3676 /* Interrupt complete. Called by Int 16h when key becomes available */
3677 break;
3679 case 0xbf:
3680 BX_INFO("*** int 15h function AH=bf not yet supported!\n");
3681 SET_CF();
3682 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3683 break;
3685 case 0xC0:
3686 #if 0
3687 SET_CF();
3688 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3689 break;
3690 #endif
3691 CLEAR_CF();
3692 regs.u.r8.ah = 0;
3693 regs.u.r16.bx = BIOS_CONFIG_TABLE;
3694 ES = 0xF000;
3695 break;
3697 case 0xc1:
3698 ES = ebda_seg;
3699 CLEAR_CF();
3700 break;
3702 case 0xd8:
3703 bios_printf(BIOS_PRINTF_DEBUG, "EISA BIOS not present\n");
3704 SET_CF();
3705 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3706 break;
3708 default:
3709 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
3710 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
3711 SET_CF();
3712 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3713 break;
3717 #if BX_USE_PS2_MOUSE
3718 void
3719 int15_function_mouse(regs, ES, DS, FLAGS)
3720 pusha_regs_t regs; // REGS pushed via pusha
3721 Bit16u ES, DS, FLAGS;
3723 Bit16u ebda_seg=read_word(0x0040,0x000E);
3724 Bit8u mouse_flags_1, mouse_flags_2;
3725 Bit16u mouse_driver_seg;
3726 Bit16u mouse_driver_offset;
3727 Bit8u comm_byte, prev_command_byte;
3728 Bit8u ret, mouse_data1, mouse_data2, mouse_data3;
3730 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3732 switch (regs.u.r8.ah) {
3733 case 0xC2:
3734 // Return Codes status in AH
3735 // =========================
3736 // 00: success
3737 // 01: invalid subfunction (AL > 7)
3738 // 02: invalid input value (out of allowable range)
3739 // 03: interface error
3740 // 04: resend command received from mouse controller,
3741 // device driver should attempt command again
3742 // 05: cannot enable mouse, since no far call has been installed
3743 // 80/86: mouse service not implemented
3745 switch (regs.u.r8.al) {
3746 case 0: // Disable/Enable Mouse
3747 BX_DEBUG_INT15("case 0:\n");
3748 switch (regs.u.r8.bh) {
3749 case 0: // Disable Mouse
3750 BX_DEBUG_INT15("case 0: disable mouse\n");
3751 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3752 ret = send_to_mouse_ctrl(0xF5); // disable mouse command
3753 if (ret == 0) {
3754 ret = get_mouse_data(&mouse_data1);
3755 if ( (ret == 0) || (mouse_data1 == 0xFA) ) {
3756 CLEAR_CF();
3757 regs.u.r8.ah = 0;
3758 return;
3762 // error
3763 SET_CF();
3764 regs.u.r8.ah = ret;
3765 return;
3766 break;
3768 case 1: // Enable Mouse
3769 BX_DEBUG_INT15("case 1: enable mouse\n");
3770 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
3771 if ( (mouse_flags_2 & 0x80) == 0 ) {
3772 BX_DEBUG_INT15("INT 15h C2 Enable Mouse, no far call handler\n");
3773 SET_CF(); // error
3774 regs.u.r8.ah = 5; // no far call installed
3775 return;
3777 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3778 ret = send_to_mouse_ctrl(0xF4); // enable mouse command
3779 if (ret == 0) {
3780 ret = get_mouse_data(&mouse_data1);
3781 if ( (ret == 0) && (mouse_data1 == 0xFA) ) {
3782 enable_mouse_int_and_events(); // turn IRQ12 and packet generation on
3783 CLEAR_CF();
3784 regs.u.r8.ah = 0;
3785 return;
3788 SET_CF();
3789 regs.u.r8.ah = ret;
3790 return;
3792 default: // invalid subfunction
3793 BX_DEBUG_INT15("INT 15h C2 AL=0, BH=%02x\n", (unsigned) regs.u.r8.bh);
3794 SET_CF(); // error
3795 regs.u.r8.ah = 1; // invalid subfunction
3796 return;
3798 break;
3800 case 1: // Reset Mouse
3801 case 5: // Initialize Mouse
3802 BX_DEBUG_INT15("case 1 or 5:\n");
3803 if (regs.u.r8.al == 5) {
3804 if (regs.u.r8.bh != 3) {
3805 SET_CF();
3806 regs.u.r8.ah = 0x02; // invalid input
3807 return;
3809 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
3810 mouse_flags_2 = (mouse_flags_2 & 0x00) | regs.u.r8.bh;
3811 mouse_flags_1 = 0x00;
3812 write_byte(ebda_seg, 0x0026, mouse_flags_1);
3813 write_byte(ebda_seg, 0x0027, mouse_flags_2);
3816 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3817 ret = send_to_mouse_ctrl(0xFF); // reset mouse command
3818 if (ret == 0) {
3819 ret = get_mouse_data(&mouse_data3);
3820 // if no mouse attached, it will return RESEND
3821 if (mouse_data3 == 0xfe) {
3822 SET_CF();
3823 return;
3825 if (mouse_data3 != 0xfa)
3826 BX_PANIC("Mouse reset returned %02x (should be ack)\n", (unsigned)mouse_data3);
3827 if ( ret == 0 ) {
3828 ret = get_mouse_data(&mouse_data1);
3829 if ( ret == 0 ) {
3830 ret = get_mouse_data(&mouse_data2);
3831 if ( ret == 0 ) {
3832 // turn IRQ12 and packet generation on
3833 enable_mouse_int_and_events();
3834 CLEAR_CF();
3835 regs.u.r8.ah = 0;
3836 regs.u.r8.bl = mouse_data1;
3837 regs.u.r8.bh = mouse_data2;
3838 return;
3844 // error
3845 SET_CF();
3846 regs.u.r8.ah = ret;
3847 return;
3849 case 2: // Set Sample Rate
3850 BX_DEBUG_INT15("case 2:\n");
3851 switch (regs.u.r8.bh) {
3852 case 0: mouse_data1 = 10; break; // 10 reports/sec
3853 case 1: mouse_data1 = 20; break; // 20 reports/sec
3854 case 2: mouse_data1 = 40; break; // 40 reports/sec
3855 case 3: mouse_data1 = 60; break; // 60 reports/sec
3856 case 4: mouse_data1 = 80; break; // 80 reports/sec
3857 case 5: mouse_data1 = 100; break; // 100 reports/sec (default)
3858 case 6: mouse_data1 = 200; break; // 200 reports/sec
3859 default: mouse_data1 = 0;
3861 if (mouse_data1 > 0) {
3862 ret = send_to_mouse_ctrl(0xF3); // set sample rate command
3863 if (ret == 0) {
3864 ret = get_mouse_data(&mouse_data2);
3865 ret = send_to_mouse_ctrl(mouse_data1);
3866 ret = get_mouse_data(&mouse_data2);
3867 CLEAR_CF();
3868 regs.u.r8.ah = 0;
3869 } else {
3870 // error
3871 SET_CF();
3872 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3874 } else {
3875 // error
3876 SET_CF();
3877 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3879 break;
3881 case 3: // Set Resolution
3882 BX_DEBUG_INT15("case 3:\n");
3883 // BX:
3884 // 0 = 25 dpi, 1 count per millimeter
3885 // 1 = 50 dpi, 2 counts per millimeter
3886 // 2 = 100 dpi, 4 counts per millimeter
3887 // 3 = 200 dpi, 8 counts per millimeter
3888 CLEAR_CF();
3889 regs.u.r8.ah = 0;
3890 break;
3892 case 4: // Get Device ID
3893 BX_DEBUG_INT15("case 4:\n");
3894 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3895 ret = send_to_mouse_ctrl(0xF2); // get mouse ID command
3896 if (ret == 0) {
3897 ret = get_mouse_data(&mouse_data1);
3898 ret = get_mouse_data(&mouse_data2);
3899 CLEAR_CF();
3900 regs.u.r8.ah = 0;
3901 regs.u.r8.bh = mouse_data2;
3902 } else {
3903 // error
3904 SET_CF();
3905 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3907 break;
3909 case 6: // Return Status & Set Scaling Factor...
3910 BX_DEBUG_INT15("case 6:\n");
3911 switch (regs.u.r8.bh) {
3912 case 0: // Return Status
3913 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3914 ret = send_to_mouse_ctrl(0xE9); // get mouse info command
3915 if (ret == 0) {
3916 ret = get_mouse_data(&mouse_data1);
3917 if (mouse_data1 != 0xfa)
3918 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
3919 if (ret == 0) {
3920 ret = get_mouse_data(&mouse_data1);
3921 if ( ret == 0 ) {
3922 ret = get_mouse_data(&mouse_data2);
3923 if ( ret == 0 ) {
3924 ret = get_mouse_data(&mouse_data3);
3925 if ( ret == 0 ) {
3926 CLEAR_CF();
3927 regs.u.r8.ah = 0;
3928 regs.u.r8.bl = mouse_data1;
3929 regs.u.r8.cl = mouse_data2;
3930 regs.u.r8.dl = mouse_data3;
3931 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
3932 return;
3939 // error
3940 SET_CF();
3941 regs.u.r8.ah = ret;
3942 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
3943 return;
3945 case 1: // Set Scaling Factor to 1:1
3946 case 2: // Set Scaling Factor to 2:1
3947 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3948 if (regs.u.r8.bh == 1) {
3949 ret = send_to_mouse_ctrl(0xE6);
3950 } else {
3951 ret = send_to_mouse_ctrl(0xE7);
3953 if (ret == 0) {
3954 get_mouse_data(&mouse_data1);
3955 ret = (mouse_data1 != 0xFA);
3957 if (ret == 0) {
3958 CLEAR_CF();
3959 regs.u.r8.ah = 0;
3960 } else {
3961 // error
3962 SET_CF();
3963 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3965 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
3966 break;
3968 default:
3969 BX_PANIC("INT 15h C2 AL=6, BH=%02x\n", (unsigned) regs.u.r8.bh);
3971 break;
3973 case 7: // Set Mouse Handler Address
3974 BX_DEBUG_INT15("case 7:\n");
3975 mouse_driver_seg = ES;
3976 mouse_driver_offset = regs.u.r16.bx;
3977 write_word(ebda_seg, 0x0022, mouse_driver_offset);
3978 write_word(ebda_seg, 0x0024, mouse_driver_seg);
3979 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
3980 if (mouse_driver_offset == 0 && mouse_driver_seg == 0) {
3981 /* remove handler */
3982 if ( (mouse_flags_2 & 0x80) != 0 ) {
3983 mouse_flags_2 &= ~0x80;
3984 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3987 else {
3988 /* install handler */
3989 mouse_flags_2 |= 0x80;
3991 write_byte(ebda_seg, 0x0027, mouse_flags_2);
3992 CLEAR_CF();
3993 regs.u.r8.ah = 0;
3994 break;
3996 default:
3997 BX_DEBUG_INT15("case default:\n");
3998 regs.u.r8.ah = 1; // invalid function
3999 SET_CF();
4001 break;
4003 default:
4004 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4005 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4006 SET_CF();
4007 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4008 break;
4011 #endif
4014 void set_e820_range(ES, DI, start, end, type)
4015 Bit16u ES;
4016 Bit16u DI;
4017 Bit32u start;
4018 Bit32u end;
4019 Bit16u type;
4021 write_word(ES, DI, start);
4022 write_word(ES, DI+2, start >> 16);
4023 write_word(ES, DI+4, 0x00);
4024 write_word(ES, DI+6, 0x00);
4026 end -= start;
4027 write_word(ES, DI+8, end);
4028 write_word(ES, DI+10, end >> 16);
4029 write_word(ES, DI+12, 0x0000);
4030 write_word(ES, DI+14, 0x0000);
4032 write_word(ES, DI+16, type);
4033 write_word(ES, DI+18, 0x0);
4036 void
4037 int15_function32(regs, ES, DS, FLAGS)
4038 pushad_regs_t regs; // REGS pushed via pushad
4039 Bit16u ES, DS, FLAGS;
4041 Bit32u extended_memory_size=0; // 64bits long
4042 Bit16u CX,DX;
4044 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
4046 switch (regs.u.r8.ah) {
4047 case 0x86:
4048 // Wait for CX:DX microseconds. currently using the
4049 // refresh request port 0x61 bit4, toggling every 15usec
4051 CX = regs.u.r16.cx;
4052 DX = regs.u.r16.dx;
4054 ASM_START
4057 ;; Get the count in eax
4058 mov bx, sp
4059 SEG SS
4060 mov ax, _int15_function.CX [bx]
4061 shl eax, #16
4062 SEG SS
4063 mov ax, _int15_function.DX [bx]
4065 ;; convert to numbers of 15usec ticks
4066 mov ebx, #15
4067 xor edx, edx
4068 div eax, ebx
4069 mov ecx, eax
4071 ;; wait for ecx number of refresh requests
4072 in al, #0x61
4073 and al,#0x10
4074 mov ah, al
4076 or ecx, ecx
4077 je int1586_tick_end
4078 int1586_tick:
4079 in al, #0x61
4080 and al,#0x10
4081 cmp al, ah
4082 je int1586_tick
4083 mov ah, al
4084 dec ecx
4085 jnz int1586_tick
4086 int1586_tick_end:
4087 ASM_END
4089 break;
4091 case 0xe8:
4092 switch(regs.u.r8.al)
4094 case 0x20: // coded by osmaker aka K.J.
4095 if(regs.u.r32.edx == 0x534D4150)
4097 extended_memory_size = inb_cmos(0x35);
4098 extended_memory_size <<= 8;
4099 extended_memory_size |= inb_cmos(0x34);
4100 extended_memory_size *= 64;
4101 // greater than EFF00000???
4102 if(extended_memory_size > 0x3bc000) {
4103 extended_memory_size = 0x3bc000; // everything after this is reserved memory until we get to 0x100000000
4105 extended_memory_size *= 1024;
4106 extended_memory_size += (16L * 1024 * 1024);
4108 if(extended_memory_size <= (16L * 1024 * 1024)) {
4109 extended_memory_size = inb_cmos(0x31);
4110 extended_memory_size <<= 8;
4111 extended_memory_size |= inb_cmos(0x30);
4112 extended_memory_size *= 1024;
4115 switch(regs.u.r16.bx)
4117 case 0:
4118 set_e820_range(ES, regs.u.r16.di,
4119 0x0000000L, 0x0009fc00L, 1);
4120 regs.u.r32.ebx = 1;
4121 regs.u.r32.eax = 0x534D4150;
4122 regs.u.r32.ecx = 0x14;
4123 CLEAR_CF();
4124 return;
4125 break;
4126 case 1:
4127 set_e820_range(ES, regs.u.r16.di,
4128 0x0009fc00L, 0x000a0000L, 2);
4129 regs.u.r32.ebx = 2;
4130 regs.u.r32.eax = 0x534D4150;
4131 regs.u.r32.ecx = 0x14;
4132 CLEAR_CF();
4133 return;
4134 break;
4135 case 2:
4136 set_e820_range(ES, regs.u.r16.di,
4137 0x000e8000L, 0x00100000L, 2);
4138 regs.u.r32.ebx = 3;
4139 regs.u.r32.eax = 0x534D4150;
4140 regs.u.r32.ecx = 0x14;
4141 CLEAR_CF();
4142 return;
4143 break;
4144 case 3:
4145 set_e820_range(ES, regs.u.r16.di,
4146 0x00100000L,
4147 extended_memory_size - 0x10000L, 1);
4148 regs.u.r32.ebx = 4;
4149 regs.u.r32.eax = 0x534D4150;
4150 regs.u.r32.ecx = 0x14;
4151 CLEAR_CF();
4152 return;
4153 break;
4154 case 4:
4155 set_e820_range(ES, regs.u.r16.di,
4156 extended_memory_size - 0x10000L,
4157 extended_memory_size, 3); // ACPI RAM
4158 regs.u.r32.ebx = 5;
4159 regs.u.r32.eax = 0x534D4150;
4160 regs.u.r32.ecx = 0x14;
4161 CLEAR_CF();
4162 return;
4163 break;
4164 case 5:
4165 /* 256KB BIOS area at the end of 4 GB */
4166 set_e820_range(ES, regs.u.r16.di,
4167 0xfffc0000L, 0x00000000L, 2);
4168 regs.u.r32.ebx = 0;
4169 regs.u.r32.eax = 0x534D4150;
4170 regs.u.r32.ecx = 0x14;
4171 CLEAR_CF();
4172 return;
4173 default: /* AX=E820, DX=534D4150, BX unrecognized */
4174 goto int15_unimplemented;
4175 break;
4177 } else {
4178 // if DX != 0x534D4150)
4179 goto int15_unimplemented;
4181 break;
4183 case 0x01:
4184 // do we have any reason to fail here ?
4185 CLEAR_CF();
4187 // my real system sets ax and bx to 0
4188 // this is confirmed by Ralph Brown list
4189 // but syslinux v1.48 is known to behave
4190 // strangely if ax is set to 0
4191 // regs.u.r16.ax = 0;
4192 // regs.u.r16.bx = 0;
4194 // Get the amount of extended memory (above 1M)
4195 regs.u.r8.cl = inb_cmos(0x30);
4196 regs.u.r8.ch = inb_cmos(0x31);
4198 // limit to 15M
4199 if(regs.u.r16.cx > 0x3c00)
4201 regs.u.r16.cx = 0x3c00;
4204 // Get the amount of extended memory above 16M in 64k blocs
4205 regs.u.r8.dl = inb_cmos(0x34);
4206 regs.u.r8.dh = inb_cmos(0x35);
4208 // Set configured memory equal to extended memory
4209 regs.u.r16.ax = regs.u.r16.cx;
4210 regs.u.r16.bx = regs.u.r16.dx;
4211 break;
4212 default: /* AH=0xE8?? but not implemented */
4213 goto int15_unimplemented;
4215 break;
4216 int15_unimplemented:
4217 // fall into the default
4218 default:
4219 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4220 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4221 SET_CF();
4222 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4223 break;
4227 void
4228 int16_function(DI, SI, BP, SP, BX, DX, CX, AX, FLAGS)
4229 Bit16u DI, SI, BP, SP, BX, DX, CX, AX, FLAGS;
4231 Bit8u scan_code, ascii_code, shift_flags, led_flags, count;
4232 Bit16u kbd_code, max;
4234 BX_DEBUG_INT16("int16: AX=%04x BX=%04x CX=%04x DX=%04x \n", AX, BX, CX, DX);
4236 shift_flags = read_byte(0x0040, 0x17);
4237 led_flags = read_byte(0x0040, 0x97);
4238 if ((((shift_flags >> 4) & 0x07) ^ (led_flags & 0x07)) != 0) {
4239 ASM_START
4241 ASM_END
4242 outb(0x60, 0xed);
4243 while ((inb(0x64) & 0x01) == 0) outb(0x80, 0x21);
4244 if ((inb(0x60) == 0xfa)) {
4245 led_flags &= 0xf8;
4246 led_flags |= ((shift_flags >> 4) & 0x07);
4247 outb(0x60, led_flags & 0x07);
4248 while ((inb(0x64) & 0x01) == 0) outb(0x80, 0x21);
4249 inb(0x60);
4250 write_byte(0x0040, 0x97, led_flags);
4252 ASM_START
4254 ASM_END
4257 switch (GET_AH()) {
4258 case 0x00: /* read keyboard input */
4260 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4261 BX_PANIC("KBD: int16h: out of keyboard input\n");
4263 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4264 else if (ascii_code == 0xE0) ascii_code = 0;
4265 AX = (scan_code << 8) | ascii_code;
4266 break;
4268 case 0x01: /* check keyboard status */
4269 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4270 SET_ZF();
4271 return;
4273 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4274 else if (ascii_code == 0xE0) ascii_code = 0;
4275 AX = (scan_code << 8) | ascii_code;
4276 CLEAR_ZF();
4277 break;
4279 case 0x02: /* get shift flag status */
4280 shift_flags = read_byte(0x0040, 0x17);
4281 SET_AL(shift_flags);
4282 break;
4284 case 0x05: /* store key-stroke into buffer */
4285 if ( !enqueue_key(GET_CH(), GET_CL()) ) {
4286 SET_AL(1);
4288 else {
4289 SET_AL(0);
4291 break;
4293 case 0x09: /* GET KEYBOARD FUNCTIONALITY */
4294 // bit Bochs Description
4295 // 7 0 reserved
4296 // 6 0 INT 16/AH=20h-22h supported (122-key keyboard support)
4297 // 5 1 INT 16/AH=10h-12h supported (enhanced keyboard support)
4298 // 4 1 INT 16/AH=0Ah supported
4299 // 3 0 INT 16/AX=0306h supported
4300 // 2 0 INT 16/AX=0305h supported
4301 // 1 0 INT 16/AX=0304h supported
4302 // 0 0 INT 16/AX=0300h supported
4304 SET_AL(0x30);
4305 break;
4307 case 0x0A: /* GET KEYBOARD ID */
4308 count = 2;
4309 kbd_code = 0x0;
4310 outb(0x60, 0xf2);
4311 /* Wait for data */
4312 max=0xffff;
4313 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4314 if (max>0x0) {
4315 if ((inb(0x60) == 0xfa)) {
4316 do {
4317 max=0xffff;
4318 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4319 if (max>0x0) {
4320 kbd_code >>= 8;
4321 kbd_code |= (inb(0x60) << 8);
4323 } while (--count>0);
4326 BX=kbd_code;
4327 break;
4329 case 0x10: /* read MF-II keyboard input */
4331 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4332 BX_PANIC("KBD: int16h: out of keyboard input\n");
4334 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4335 AX = (scan_code << 8) | ascii_code;
4336 break;
4338 case 0x11: /* check MF-II keyboard status */
4339 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4340 SET_ZF();
4341 return;
4343 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4344 AX = (scan_code << 8) | ascii_code;
4345 CLEAR_ZF();
4346 break;
4348 case 0x12: /* get extended keyboard status */
4349 shift_flags = read_byte(0x0040, 0x17);
4350 SET_AL(shift_flags);
4351 shift_flags = read_byte(0x0040, 0x18) & 0x73;
4352 shift_flags |= read_byte(0x0040, 0x96) & 0x0c;
4353 SET_AH(shift_flags);
4354 BX_DEBUG_INT16("int16: func 12 sending %04x\n",AX);
4355 break;
4357 case 0x92: /* keyboard capability check called by DOS 5.0+ keyb */
4358 SET_AH(0x80); // function int16 ah=0x10-0x12 supported
4359 break;
4361 case 0xA2: /* 122 keys capability check called by DOS 5.0+ keyb */
4362 // don't change AH : function int16 ah=0x20-0x22 NOT supported
4363 break;
4365 case 0x6F:
4366 if (GET_AL() == 0x08)
4367 SET_AH(0x02); // unsupported, aka normal keyboard
4369 default:
4370 BX_INFO("KBD: unsupported int 16h function %02x\n", GET_AH());
4374 unsigned int
4375 dequeue_key(scan_code, ascii_code, incr)
4376 Bit8u *scan_code;
4377 Bit8u *ascii_code;
4378 unsigned int incr;
4380 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail;
4381 Bit16u ss;
4382 Bit8u acode, scode;
4384 #if BX_CPU < 2
4385 buffer_start = 0x001E;
4386 buffer_end = 0x003E;
4387 #else
4388 buffer_start = read_word(0x0040, 0x0080);
4389 buffer_end = read_word(0x0040, 0x0082);
4390 #endif
4392 buffer_head = read_word(0x0040, 0x001a);
4393 buffer_tail = read_word(0x0040, 0x001c);
4395 if (buffer_head != buffer_tail) {
4396 ss = get_SS();
4397 acode = read_byte(0x0040, buffer_head);
4398 scode = read_byte(0x0040, buffer_head+1);
4399 write_byte(ss, ascii_code, acode);
4400 write_byte(ss, scan_code, scode);
4402 if (incr) {
4403 buffer_head += 2;
4404 if (buffer_head >= buffer_end)
4405 buffer_head = buffer_start;
4406 write_word(0x0040, 0x001a, buffer_head);
4408 return(1);
4410 else {
4411 return(0);
4415 static char panic_msg_keyb_buffer_full[] = "%s: keyboard input buffer full\n";
4417 Bit8u
4418 inhibit_mouse_int_and_events()
4420 Bit8u command_byte, prev_command_byte;
4422 // Turn off IRQ generation and aux data line
4423 if ( inb(0x64) & 0x02 )
4424 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4425 outb(0x64, 0x20); // get command byte
4426 while ( (inb(0x64) & 0x01) != 0x01 );
4427 prev_command_byte = inb(0x60);
4428 command_byte = prev_command_byte;
4429 //while ( (inb(0x64) & 0x02) );
4430 if ( inb(0x64) & 0x02 )
4431 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4432 command_byte &= 0xfd; // turn off IRQ 12 generation
4433 command_byte |= 0x20; // disable mouse serial clock line
4434 outb(0x64, 0x60); // write command byte
4435 outb(0x60, command_byte);
4436 return(prev_command_byte);
4439 void
4440 enable_mouse_int_and_events()
4442 Bit8u command_byte;
4444 // Turn on IRQ generation and aux data line
4445 if ( inb(0x64) & 0x02 )
4446 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4447 outb(0x64, 0x20); // get command byte
4448 while ( (inb(0x64) & 0x01) != 0x01 );
4449 command_byte = inb(0x60);
4450 //while ( (inb(0x64) & 0x02) );
4451 if ( inb(0x64) & 0x02 )
4452 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4453 command_byte |= 0x02; // turn on IRQ 12 generation
4454 command_byte &= 0xdf; // enable mouse serial clock line
4455 outb(0x64, 0x60); // write command byte
4456 outb(0x60, command_byte);
4459 Bit8u
4460 send_to_mouse_ctrl(sendbyte)
4461 Bit8u sendbyte;
4463 Bit8u response;
4465 // wait for chance to write to ctrl
4466 if ( inb(0x64) & 0x02 )
4467 BX_PANIC(panic_msg_keyb_buffer_full,"sendmouse");
4468 outb(0x64, 0xD4);
4469 outb(0x60, sendbyte);
4470 return(0);
4474 Bit8u
4475 get_mouse_data(data)
4476 Bit8u *data;
4478 Bit8u response;
4479 Bit16u ss;
4481 while ( (inb(0x64) & 0x21) != 0x21 ) {
4484 response = inb(0x60);
4486 ss = get_SS();
4487 write_byte(ss, data, response);
4488 return(0);
4491 void
4492 set_kbd_command_byte(command_byte)
4493 Bit8u command_byte;
4495 if ( inb(0x64) & 0x02 )
4496 BX_PANIC(panic_msg_keyb_buffer_full,"setkbdcomm");
4497 outb(0x64, 0xD4);
4499 outb(0x64, 0x60); // write command byte
4500 outb(0x60, command_byte);
4503 void
4504 int09_function(DI, SI, BP, SP, BX, DX, CX, AX)
4505 Bit16u DI, SI, BP, SP, BX, DX, CX, AX;
4507 Bit8u scancode, asciicode, shift_flags;
4508 Bit8u mf2_flags, mf2_state;
4511 // DS has been set to F000 before call
4515 scancode = GET_AL();
4517 if (scancode == 0) {
4518 BX_INFO("KBD: int09 handler: AL=0\n");
4519 return;
4523 shift_flags = read_byte(0x0040, 0x17);
4524 mf2_flags = read_byte(0x0040, 0x18);
4525 mf2_state = read_byte(0x0040, 0x96);
4526 asciicode = 0;
4528 switch (scancode) {
4529 case 0x3a: /* Caps Lock press */
4530 shift_flags ^= 0x40;
4531 write_byte(0x0040, 0x17, shift_flags);
4532 mf2_flags |= 0x40;
4533 write_byte(0x0040, 0x18, mf2_flags);
4534 break;
4535 case 0xba: /* Caps Lock release */
4536 mf2_flags &= ~0x40;
4537 write_byte(0x0040, 0x18, mf2_flags);
4538 break;
4540 case 0x2a: /* L Shift press */
4541 shift_flags |= 0x02;
4542 write_byte(0x0040, 0x17, shift_flags);
4543 break;
4544 case 0xaa: /* L Shift release */
4545 shift_flags &= ~0x02;
4546 write_byte(0x0040, 0x17, shift_flags);
4547 break;
4549 case 0x36: /* R Shift press */
4550 shift_flags |= 0x01;
4551 write_byte(0x0040, 0x17, shift_flags);
4552 break;
4553 case 0xb6: /* R Shift release */
4554 shift_flags &= ~0x01;
4555 write_byte(0x0040, 0x17, shift_flags);
4556 break;
4558 case 0x1d: /* Ctrl press */
4559 if ((mf2_state & 0x01) == 0) {
4560 shift_flags |= 0x04;
4561 write_byte(0x0040, 0x17, shift_flags);
4562 if (mf2_state & 0x02) {
4563 mf2_state |= 0x04;
4564 write_byte(0x0040, 0x96, mf2_state);
4565 } else {
4566 mf2_flags |= 0x01;
4567 write_byte(0x0040, 0x18, mf2_flags);
4570 break;
4571 case 0x9d: /* Ctrl release */
4572 if ((mf2_state & 0x01) == 0) {
4573 shift_flags &= ~0x04;
4574 write_byte(0x0040, 0x17, shift_flags);
4575 if (mf2_state & 0x02) {
4576 mf2_state &= ~0x04;
4577 write_byte(0x0040, 0x96, mf2_state);
4578 } else {
4579 mf2_flags &= ~0x01;
4580 write_byte(0x0040, 0x18, mf2_flags);
4583 break;
4585 case 0x38: /* Alt press */
4586 shift_flags |= 0x08;
4587 write_byte(0x0040, 0x17, shift_flags);
4588 if (mf2_state & 0x02) {
4589 mf2_state |= 0x08;
4590 write_byte(0x0040, 0x96, mf2_state);
4591 } else {
4592 mf2_flags |= 0x02;
4593 write_byte(0x0040, 0x18, mf2_flags);
4595 break;
4596 case 0xb8: /* Alt release */
4597 shift_flags &= ~0x08;
4598 write_byte(0x0040, 0x17, shift_flags);
4599 if (mf2_state & 0x02) {
4600 mf2_state &= ~0x08;
4601 write_byte(0x0040, 0x96, mf2_state);
4602 } else {
4603 mf2_flags &= ~0x02;
4604 write_byte(0x0040, 0x18, mf2_flags);
4606 break;
4608 case 0x45: /* Num Lock press */
4609 if ((mf2_state & 0x03) == 0) {
4610 mf2_flags |= 0x20;
4611 write_byte(0x0040, 0x18, mf2_flags);
4612 shift_flags ^= 0x20;
4613 write_byte(0x0040, 0x17, shift_flags);
4615 break;
4616 case 0xc5: /* Num Lock release */
4617 if ((mf2_state & 0x03) == 0) {
4618 mf2_flags &= ~0x20;
4619 write_byte(0x0040, 0x18, mf2_flags);
4621 break;
4623 case 0x46: /* Scroll Lock press */
4624 mf2_flags |= 0x10;
4625 write_byte(0x0040, 0x18, mf2_flags);
4626 shift_flags ^= 0x10;
4627 write_byte(0x0040, 0x17, shift_flags);
4628 break;
4630 case 0xc6: /* Scroll Lock release */
4631 mf2_flags &= ~0x10;
4632 write_byte(0x0040, 0x18, mf2_flags);
4633 break;
4635 default:
4636 if (scancode & 0x80) {
4637 break; /* toss key releases ... */
4639 if (scancode > MAX_SCAN_CODE) {
4640 BX_INFO("KBD: int09h_handler(): unknown scancode read: 0x%02x!\n", scancode);
4641 return;
4643 if (shift_flags & 0x08) { /* ALT */
4644 asciicode = scan_to_scanascii[scancode].alt;
4645 scancode = scan_to_scanascii[scancode].alt >> 8;
4646 } else if (shift_flags & 0x04) { /* CONTROL */
4647 asciicode = scan_to_scanascii[scancode].control;
4648 scancode = scan_to_scanascii[scancode].control >> 8;
4649 } else if (((mf2_state & 0x02) > 0) && ((scancode >= 0x47) && (scancode <= 0x53))) {
4650 /* extended keys handling */
4651 asciicode = 0xe0;
4652 scancode = scan_to_scanascii[scancode].normal >> 8;
4653 } else if (shift_flags & 0x03) { /* LSHIFT + RSHIFT */
4654 /* check if lock state should be ignored
4655 * because a SHIFT key are pressed */
4657 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
4658 asciicode = scan_to_scanascii[scancode].normal;
4659 scancode = scan_to_scanascii[scancode].normal >> 8;
4660 } else {
4661 asciicode = scan_to_scanascii[scancode].shift;
4662 scancode = scan_to_scanascii[scancode].shift >> 8;
4664 } else {
4665 /* check if lock is on */
4666 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
4667 asciicode = scan_to_scanascii[scancode].shift;
4668 scancode = scan_to_scanascii[scancode].shift >> 8;
4669 } else {
4670 asciicode = scan_to_scanascii[scancode].normal;
4671 scancode = scan_to_scanascii[scancode].normal >> 8;
4674 if (scancode==0 && asciicode==0) {
4675 BX_INFO("KBD: int09h_handler(): scancode & asciicode are zero?\n");
4677 enqueue_key(scancode, asciicode);
4678 break;
4680 if ((scancode & 0x7f) != 0x1d) {
4681 mf2_state &= ~0x01;
4683 mf2_state &= ~0x02;
4684 write_byte(0x0040, 0x96, mf2_state);
4687 unsigned int
4688 enqueue_key(scan_code, ascii_code)
4689 Bit8u scan_code, ascii_code;
4691 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail, temp_tail;
4693 #if BX_CPU < 2
4694 buffer_start = 0x001E;
4695 buffer_end = 0x003E;
4696 #else
4697 buffer_start = read_word(0x0040, 0x0080);
4698 buffer_end = read_word(0x0040, 0x0082);
4699 #endif
4701 buffer_head = read_word(0x0040, 0x001A);
4702 buffer_tail = read_word(0x0040, 0x001C);
4704 temp_tail = buffer_tail;
4705 buffer_tail += 2;
4706 if (buffer_tail >= buffer_end)
4707 buffer_tail = buffer_start;
4709 if (buffer_tail == buffer_head) {
4710 return(0);
4713 write_byte(0x0040, temp_tail, ascii_code);
4714 write_byte(0x0040, temp_tail+1, scan_code);
4715 write_word(0x0040, 0x001C, buffer_tail);
4716 return(1);
4720 void
4721 int74_function(make_farcall, Z, Y, X, status)
4722 Bit16u make_farcall, Z, Y, X, status;
4724 Bit16u ebda_seg=read_word(0x0040,0x000E);
4725 Bit8u in_byte, index, package_count;
4726 Bit8u mouse_flags_1, mouse_flags_2;
4728 BX_DEBUG_INT74("entering int74_function\n");
4729 make_farcall = 0;
4731 in_byte = inb(0x64);
4732 if ( (in_byte & 0x21) != 0x21 ) {
4733 return;
4735 in_byte = inb(0x60);
4736 BX_DEBUG_INT74("int74: read byte %02x\n", in_byte);
4738 mouse_flags_1 = read_byte(ebda_seg, 0x0026);
4739 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4741 if ( (mouse_flags_2 & 0x80) != 0x80 ) {
4742 return;
4745 package_count = mouse_flags_2 & 0x07;
4746 index = mouse_flags_1 & 0x07;
4747 write_byte(ebda_seg, 0x28 + index, in_byte);
4749 if ( (index+1) >= package_count ) {
4750 BX_DEBUG_INT74("int74_function: make_farcall=1\n");
4751 status = read_byte(ebda_seg, 0x0028 + 0);
4752 X = read_byte(ebda_seg, 0x0028 + 1);
4753 Y = read_byte(ebda_seg, 0x0028 + 2);
4754 Z = 0;
4755 mouse_flags_1 = 0;
4756 // check if far call handler installed
4757 if (mouse_flags_2 & 0x80)
4758 make_farcall = 1;
4760 else {
4761 mouse_flags_1++;
4763 write_byte(ebda_seg, 0x0026, mouse_flags_1);
4766 #define SET_DISK_RET_STATUS(status) write_byte(0x0040, 0x0074, status)
4768 #if BX_USE_ATADRV
4770 void
4771 int13_harddisk(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
4772 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
4774 Bit32u lba;
4775 Bit16u ebda_seg=read_word(0x0040,0x000E);
4776 Bit16u cylinder, head, sector;
4777 Bit16u segment, offset;
4778 Bit16u npc, nph, npspt, nlc, nlh, nlspt;
4779 Bit16u size, count;
4780 Bit8u device, status;
4782 BX_DEBUG_INT13_HD("int13_harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
4784 write_byte(0x0040, 0x008e, 0); // clear completion flag
4786 // basic check : device has to be defined
4787 if ( (GET_ELDL() < 0x80) || (GET_ELDL() >= 0x80 + BX_MAX_ATA_DEVICES) ) {
4788 BX_INFO("int13_harddisk: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
4789 goto int13_fail;
4792 // Get the ata channel
4793 device=read_byte(ebda_seg,&EbdaData->ata.hdidmap[GET_ELDL()-0x80]);
4795 // basic check : device has to be valid
4796 if (device >= BX_MAX_ATA_DEVICES) {
4797 BX_INFO("int13_harddisk: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
4798 goto int13_fail;
4801 switch (GET_AH()) {
4803 case 0x00: /* disk controller reset */
4804 ata_reset (device);
4805 goto int13_success;
4806 break;
4808 case 0x01: /* read disk status */
4809 status = read_byte(0x0040, 0x0074);
4810 SET_AH(status);
4811 SET_DISK_RET_STATUS(0);
4812 /* set CF if error status read */
4813 if (status) goto int13_fail_nostatus;
4814 else goto int13_success_noah;
4815 break;
4817 case 0x02: // read disk sectors
4818 case 0x03: // write disk sectors
4819 case 0x04: // verify disk sectors
4821 count = GET_AL();
4822 cylinder = GET_CH();
4823 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
4824 sector = (GET_CL() & 0x3f);
4825 head = GET_DH();
4827 segment = ES;
4828 offset = BX;
4830 if ( (count > 128) || (count == 0) ) {
4831 BX_INFO("int13_harddisk: function %02x, count out of range!\n",GET_AH());
4832 goto int13_fail;
4835 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
4836 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
4837 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
4839 // sanity check on cyl heads, sec
4840 if( (cylinder >= nlc) || (head >= nlh) || (sector > nlspt )) {
4841 BX_INFO("int13_harddisk: function %02x, parameters out of range %04x/%04x/%04x!\n", GET_AH(), cylinder, head, sector);
4842 goto int13_fail;
4845 // FIXME verify
4846 if ( GET_AH() == 0x04 ) goto int13_success;
4848 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
4849 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
4851 // if needed, translate lchs to lba, and execute command
4852 if ( (nph != nlh) || (npspt != nlspt)) {
4853 lba = ((((Bit32u)cylinder * (Bit32u)nlh) + (Bit32u)head) * (Bit32u)nlspt) + (Bit32u)sector - 1;
4854 sector = 0; // this forces the command to be lba
4857 if ( GET_AH() == 0x02 )
4858 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, cylinder, head, sector, lba, segment, offset);
4859 else
4860 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, cylinder, head, sector, lba, segment, offset);
4862 // Set nb of sector transferred
4863 SET_AL(read_word(ebda_seg, &EbdaData->ata.trsfsectors));
4865 if (status != 0) {
4866 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
4867 SET_AH(0x0c);
4868 goto int13_fail_noah;
4871 goto int13_success;
4872 break;
4874 case 0x05: /* format disk track */
4875 BX_INFO("format disk track called\n");
4876 goto int13_success;
4877 return;
4878 break;
4880 case 0x08: /* read disk drive parameters */
4882 // Get logical geometry from table
4883 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
4884 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
4885 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
4886 count = read_byte(ebda_seg, &EbdaData->ata.hdcount);
4888 nlc = nlc - 2; /* 0 based , last sector not used */
4889 SET_AL(0);
4890 SET_CH(nlc & 0xff);
4891 SET_CL(((nlc >> 2) & 0xc0) | (nlspt & 0x3f));
4892 SET_DH(nlh - 1);
4893 SET_DL(count); /* FIXME returns 0, 1, or n hard drives */
4895 // FIXME should set ES & DI
4897 goto int13_success;
4898 break;
4900 case 0x10: /* check drive ready */
4901 // should look at 40:8E also???
4903 // Read the status from controller
4904 status = inb(read_word(ebda_seg, &EbdaData->ata.channels[device/2].iobase1) + ATA_CB_STAT);
4905 if ( (status & ( ATA_CB_STAT_BSY | ATA_CB_STAT_RDY )) == ATA_CB_STAT_RDY ) {
4906 goto int13_success;
4908 else {
4909 SET_AH(0xAA);
4910 goto int13_fail_noah;
4912 break;
4914 case 0x15: /* read disk drive size */
4916 // Get physical geometry from table
4917 npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
4918 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
4919 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
4921 // Compute sector count seen by int13
4922 lba = (Bit32u)(npc - 1) * (Bit32u)nph * (Bit32u)npspt;
4923 CX = lba >> 16;
4924 DX = lba & 0xffff;
4926 SET_AH(3); // hard disk accessible
4927 goto int13_success_noah;
4928 break;
4930 case 0x41: // IBM/MS installation check
4931 BX=0xaa55; // install check
4932 SET_AH(0x30); // EDD 3.0
4933 CX=0x0007; // ext disk access and edd, removable supported
4934 goto int13_success_noah;
4935 break;
4937 case 0x42: // IBM/MS extended read
4938 case 0x43: // IBM/MS extended write
4939 case 0x44: // IBM/MS verify
4940 case 0x47: // IBM/MS extended seek
4942 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
4943 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
4944 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
4946 // Can't use 64 bits lba
4947 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
4948 if (lba != 0L) {
4949 BX_PANIC("int13_harddisk: function %02x. Can't use 64bits lba\n",GET_AH());
4950 goto int13_fail;
4953 // Get 32 bits lba and check
4954 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
4955 if (lba >= read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors) ) {
4956 BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH());
4957 goto int13_fail;
4960 // If verify or seek
4961 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
4962 goto int13_success;
4964 // Execute the command
4965 if ( GET_AH() == 0x42 )
4966 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, 0, 0, 0, lba, segment, offset);
4967 else
4968 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, 0, 0, 0, lba, segment, offset);
4970 count=read_word(ebda_seg, &EbdaData->ata.trsfsectors);
4971 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
4973 if (status != 0) {
4974 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
4975 SET_AH(0x0c);
4976 goto int13_fail_noah;
4979 goto int13_success;
4980 break;
4982 case 0x45: // IBM/MS lock/unlock drive
4983 case 0x49: // IBM/MS extended media change
4984 goto int13_success; // Always success for HD
4985 break;
4987 case 0x46: // IBM/MS eject media
4988 SET_AH(0xb2); // Volume Not Removable
4989 goto int13_fail_noah; // Always fail for HD
4990 break;
4992 case 0x48: // IBM/MS get drive parameters
4993 size=read_word(DS,SI+(Bit16u)&Int13DPT->size);
4995 // Buffer is too small
4996 if(size < 0x1a)
4997 goto int13_fail;
4999 // EDD 1.x
5000 if(size >= 0x1a) {
5001 Bit16u blksize;
5003 npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
5004 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
5005 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
5006 lba = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors);
5007 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5009 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5010 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x02); // geometry is valid
5011 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, (Bit32u)npc);
5012 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, (Bit32u)nph);
5013 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, (Bit32u)npspt);
5014 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, lba); // FIXME should be Bit64
5015 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0L);
5016 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5019 // EDD 2.x
5020 if(size >= 0x1e) {
5021 Bit8u channel, dev, irq, mode, checksum, i, translation;
5022 Bit16u iobase1, iobase2, options;
5024 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5026 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5027 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5029 // Fill in dpte
5030 channel = device / 2;
5031 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5032 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5033 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5034 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5035 translation = read_byte(ebda_seg, &EbdaData->ata.devices[device].translation);
5037 options = (translation==ATA_TRANSLATION_NONE?0:1<<3); // chs translation
5038 options |= (1<<4); // lba translation
5039 options |= (mode==ATA_MODE_PIO32?1:0<<7);
5040 options |= (translation==ATA_TRANSLATION_LBA?1:0<<9);
5041 options |= (translation==ATA_TRANSLATION_RECHS?3:0<<9);
5043 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5044 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2);
5045 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5046 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5047 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5048 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5049 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5050 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5051 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5052 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5053 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5055 checksum=0;
5056 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, (&EbdaData->ata.dpte) + i);
5057 checksum = ~checksum;
5058 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5061 // EDD 3.x
5062 if(size >= 0x42) {
5063 Bit8u channel, iface, checksum, i;
5064 Bit16u iobase1;
5066 channel = device / 2;
5067 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5068 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5070 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5071 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5072 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5073 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5074 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5076 if (iface==ATA_IFACE_ISA) {
5077 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5078 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5079 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5080 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5082 else {
5083 // FIXME PCI
5085 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5086 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5087 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5088 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5090 if (iface==ATA_IFACE_ISA) {
5091 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5092 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5093 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5095 else {
5096 // FIXME PCI
5098 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5099 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5100 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5101 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5103 checksum=0;
5104 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5105 checksum = ~checksum;
5106 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5109 goto int13_success;
5110 break;
5112 case 0x4e: // // IBM/MS set hardware configuration
5113 // DMA, prefetch, PIO maximum not supported
5114 switch (GET_AL()) {
5115 case 0x01:
5116 case 0x03:
5117 case 0x04:
5118 case 0x06:
5119 goto int13_success;
5120 break;
5121 default :
5122 goto int13_fail;
5124 break;
5126 case 0x09: /* initialize drive parameters */
5127 case 0x0c: /* seek to specified cylinder */
5128 case 0x0d: /* alternate disk reset */
5129 case 0x11: /* recalibrate */
5130 case 0x14: /* controller internal diagnostic */
5131 BX_INFO("int13h_harddisk function %02xh unimplemented, returns success\n", GET_AH());
5132 goto int13_success;
5133 break;
5135 case 0x0a: /* read disk sectors with ECC */
5136 case 0x0b: /* write disk sectors with ECC */
5137 case 0x18: // set media type for format
5138 case 0x50: // IBM/MS send packet command
5139 default:
5140 BX_INFO("int13_harddisk function %02xh unsupported, returns fail\n", GET_AH());
5141 goto int13_fail;
5142 break;
5145 int13_fail:
5146 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5147 int13_fail_noah:
5148 SET_DISK_RET_STATUS(GET_AH());
5149 int13_fail_nostatus:
5150 SET_CF(); // error occurred
5151 return;
5153 int13_success:
5154 SET_AH(0x00); // no error
5155 int13_success_noah:
5156 SET_DISK_RET_STATUS(0x00);
5157 CLEAR_CF(); // no error
5158 return;
5161 // ---------------------------------------------------------------------------
5162 // Start of int13 for cdrom
5163 // ---------------------------------------------------------------------------
5165 void
5166 int13_cdrom(EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
5167 Bit16u EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
5169 Bit16u ebda_seg=read_word(0x0040,0x000E);
5170 Bit8u device, status, locks;
5171 Bit8u atacmd[12];
5172 Bit32u lba;
5173 Bit16u count, segment, offset, i, size;
5175 BX_DEBUG_INT13_CD("int13_cdrom: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5177 SET_DISK_RET_STATUS(0x00);
5179 /* basic check : device should be 0xE0+ */
5180 if( (GET_ELDL() < 0xE0) || (GET_ELDL() >= 0xE0+BX_MAX_ATA_DEVICES) ) {
5181 BX_INFO("int13_cdrom: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
5182 goto int13_fail;
5185 // Get the ata channel
5186 device=read_byte(ebda_seg,&EbdaData->ata.cdidmap[GET_ELDL()-0xE0]);
5188 /* basic check : device has to be valid */
5189 if (device >= BX_MAX_ATA_DEVICES) {
5190 BX_INFO("int13_cdrom: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
5191 goto int13_fail;
5194 switch (GET_AH()) {
5196 // all those functions return SUCCESS
5197 case 0x00: /* disk controller reset */
5198 case 0x09: /* initialize drive parameters */
5199 case 0x0c: /* seek to specified cylinder */
5200 case 0x0d: /* alternate disk reset */
5201 case 0x10: /* check drive ready */
5202 case 0x11: /* recalibrate */
5203 case 0x14: /* controller internal diagnostic */
5204 case 0x16: /* detect disk change */
5205 goto int13_success;
5206 break;
5208 // all those functions return disk write-protected
5209 case 0x03: /* write disk sectors */
5210 case 0x05: /* format disk track */
5211 case 0x43: // IBM/MS extended write
5212 SET_AH(0x03);
5213 goto int13_fail_noah;
5214 break;
5216 case 0x01: /* read disk status */
5217 status = read_byte(0x0040, 0x0074);
5218 SET_AH(status);
5219 SET_DISK_RET_STATUS(0);
5221 /* set CF if error status read */
5222 if (status) goto int13_fail_nostatus;
5223 else goto int13_success_noah;
5224 break;
5226 case 0x15: /* read disk drive size */
5227 SET_AH(0x02);
5228 goto int13_fail_noah;
5229 break;
5231 case 0x41: // IBM/MS installation check
5232 BX=0xaa55; // install check
5233 SET_AH(0x30); // EDD 2.1
5234 CX=0x0007; // ext disk access, removable and edd
5235 goto int13_success_noah;
5236 break;
5238 case 0x42: // IBM/MS extended read
5239 case 0x44: // IBM/MS verify sectors
5240 case 0x47: // IBM/MS extended seek
5242 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
5243 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
5244 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
5246 // Can't use 64 bits lba
5247 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
5248 if (lba != 0L) {
5249 BX_PANIC("int13_cdrom: function %02x. Can't use 64bits lba\n",GET_AH());
5250 goto int13_fail;
5253 // Get 32 bits lba
5254 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5256 // If verify or seek
5257 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
5258 goto int13_success;
5260 memsetb(get_SS(),atacmd,0,12);
5261 atacmd[0]=0x28; // READ command
5262 atacmd[7]=(count & 0xff00) >> 8; // Sectors
5263 atacmd[8]=(count & 0x00ff); // Sectors
5264 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
5265 atacmd[3]=(lba & 0x00ff0000) >> 16;
5266 atacmd[4]=(lba & 0x0000ff00) >> 8;
5267 atacmd[5]=(lba & 0x000000ff);
5268 status = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, count*2048L, ATA_DATA_IN, segment,offset);
5270 count = (Bit16u)(read_dword(ebda_seg, &EbdaData->ata.trsfbytes) >> 11);
5271 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5273 if (status != 0) {
5274 BX_INFO("int13_cdrom: function %02x, status %02x !\n",GET_AH(),status);
5275 SET_AH(0x0c);
5276 goto int13_fail_noah;
5279 goto int13_success;
5280 break;
5282 case 0x45: // IBM/MS lock/unlock drive
5283 if (GET_AL() > 2) goto int13_fail;
5285 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5287 switch (GET_AL()) {
5288 case 0 : // lock
5289 if (locks == 0xff) {
5290 SET_AH(0xb4);
5291 SET_AL(1);
5292 goto int13_fail_noah;
5294 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, ++locks);
5295 SET_AL(1);
5296 break;
5297 case 1 : // unlock
5298 if (locks == 0x00) {
5299 SET_AH(0xb0);
5300 SET_AL(0);
5301 goto int13_fail_noah;
5303 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, --locks);
5304 SET_AL(locks==0?0:1);
5305 break;
5306 case 2 : // status
5307 SET_AL(locks==0?0:1);
5308 break;
5310 goto int13_success;
5311 break;
5313 case 0x46: // IBM/MS eject media
5314 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5316 if (locks != 0) {
5317 SET_AH(0xb1); // media locked
5318 goto int13_fail_noah;
5320 // FIXME should handle 0x31 no media in device
5321 // FIXME should handle 0xb5 valid request failed
5323 // Call removable media eject
5324 ASM_START
5325 push bp
5326 mov bp, sp
5328 mov ah, #0x52
5329 int 15
5330 mov _int13_cdrom.status + 2[bp], ah
5331 jnc int13_cdrom_rme_end
5332 mov _int13_cdrom.status, #1
5333 int13_cdrom_rme_end:
5334 pop bp
5335 ASM_END
5337 if (status != 0) {
5338 SET_AH(0xb1); // media locked
5339 goto int13_fail_noah;
5342 goto int13_success;
5343 break;
5345 case 0x48: // IBM/MS get drive parameters
5346 size = read_word(DS,SI+(Bit16u)&Int13Ext->size);
5348 // Buffer is too small
5349 if(size < 0x1a)
5350 goto int13_fail;
5352 // EDD 1.x
5353 if(size >= 0x1a) {
5354 Bit16u cylinders, heads, spt, blksize;
5356 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5358 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5359 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x74); // removable, media change, lockable, max values
5360 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0xffffffff);
5361 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, 0xffffffff);
5362 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, 0xffffffff);
5363 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, 0xffffffff); // FIXME should be Bit64
5364 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0xffffffff);
5365 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5368 // EDD 2.x
5369 if(size >= 0x1e) {
5370 Bit8u channel, dev, irq, mode, checksum, i;
5371 Bit16u iobase1, iobase2, options;
5373 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5375 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5376 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5378 // Fill in dpte
5379 channel = device / 2;
5380 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5381 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5382 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5383 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5385 // FIXME atapi device
5386 options = (1<<4); // lba translation
5387 options |= (1<<5); // removable device
5388 options |= (1<<6); // atapi device
5389 options |= (mode==ATA_MODE_PIO32?1:0<<7);
5391 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5392 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2);
5393 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5394 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5395 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5396 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5397 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5398 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5399 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5400 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5401 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5403 checksum=0;
5404 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, (&EbdaData->ata.dpte) + i);
5405 checksum = ~checksum;
5406 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5409 // EDD 3.x
5410 if(size >= 0x42) {
5411 Bit8u channel, iface, checksum, i;
5412 Bit16u iobase1;
5414 channel = device / 2;
5415 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5416 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5418 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5419 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5420 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5421 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5422 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5424 if (iface==ATA_IFACE_ISA) {
5425 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5426 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5427 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5428 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5430 else {
5431 // FIXME PCI
5433 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5434 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5435 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5436 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5438 if (iface==ATA_IFACE_ISA) {
5439 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5440 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5441 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5443 else {
5444 // FIXME PCI
5446 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5447 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5448 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5449 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5451 checksum=0;
5452 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5453 checksum = ~checksum;
5454 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5457 goto int13_success;
5458 break;
5460 case 0x49: // IBM/MS extended media change
5461 // always send changed ??
5462 SET_AH(06);
5463 goto int13_fail_nostatus;
5464 break;
5466 case 0x4e: // // IBM/MS set hardware configuration
5467 // DMA, prefetch, PIO maximum not supported
5468 switch (GET_AL()) {
5469 case 0x01:
5470 case 0x03:
5471 case 0x04:
5472 case 0x06:
5473 goto int13_success;
5474 break;
5475 default :
5476 goto int13_fail;
5478 break;
5480 // all those functions return unimplemented
5481 case 0x02: /* read sectors */
5482 case 0x04: /* verify sectors */
5483 case 0x08: /* read disk drive parameters */
5484 case 0x0a: /* read disk sectors with ECC */
5485 case 0x0b: /* write disk sectors with ECC */
5486 case 0x18: /* set media type for format */
5487 case 0x50: // ? - send packet command
5488 default:
5489 BX_INFO("int13_cdrom: unsupported AH=%02x\n", GET_AH());
5490 goto int13_fail;
5491 break;
5494 int13_fail:
5495 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5496 int13_fail_noah:
5497 SET_DISK_RET_STATUS(GET_AH());
5498 int13_fail_nostatus:
5499 SET_CF(); // error occurred
5500 return;
5502 int13_success:
5503 SET_AH(0x00); // no error
5504 int13_success_noah:
5505 SET_DISK_RET_STATUS(0x00);
5506 CLEAR_CF(); // no error
5507 return;
5510 // ---------------------------------------------------------------------------
5511 // End of int13 for cdrom
5512 // ---------------------------------------------------------------------------
5514 #if BX_ELTORITO_BOOT
5515 // ---------------------------------------------------------------------------
5516 // Start of int13 for eltorito functions
5517 // ---------------------------------------------------------------------------
5519 void
5520 int13_eltorito(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
5521 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
5523 Bit16u ebda_seg=read_word(0x0040,0x000E);
5525 BX_DEBUG_INT13_ET("int13_eltorito: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5526 // BX_DEBUG_INT13_ET("int13_eltorito: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
5528 switch (GET_AH()) {
5530 // FIXME ElTorito Various. Should be implemented
5531 case 0x4a: // ElTorito - Initiate disk emu
5532 case 0x4c: // ElTorito - Initiate disk emu and boot
5533 case 0x4d: // ElTorito - Return Boot catalog
5534 BX_PANIC("Int13 eltorito call with AX=%04x. Please report\n",AX);
5535 goto int13_fail;
5536 break;
5538 case 0x4b: // ElTorito - Terminate disk emu
5539 // FIXME ElTorito Hardcoded
5540 write_byte(DS,SI+0x00,0x13);
5541 write_byte(DS,SI+0x01,read_byte(ebda_seg,&EbdaData->cdemu.media));
5542 write_byte(DS,SI+0x02,read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
5543 write_byte(DS,SI+0x03,read_byte(ebda_seg,&EbdaData->cdemu.controller_index));
5544 write_dword(DS,SI+0x04,read_dword(ebda_seg,&EbdaData->cdemu.ilba));
5545 write_word(DS,SI+0x08,read_word(ebda_seg,&EbdaData->cdemu.device_spec));
5546 write_word(DS,SI+0x0a,read_word(ebda_seg,&EbdaData->cdemu.buffer_segment));
5547 write_word(DS,SI+0x0c,read_word(ebda_seg,&EbdaData->cdemu.load_segment));
5548 write_word(DS,SI+0x0e,read_word(ebda_seg,&EbdaData->cdemu.sector_count));
5549 write_byte(DS,SI+0x10,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.cylinders));
5550 write_byte(DS,SI+0x11,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.spt));
5551 write_byte(DS,SI+0x12,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.heads));
5553 // If we have to terminate emulation
5554 if(GET_AL() == 0x00) {
5555 // FIXME ElTorito Various. Should be handled accordingly to spec
5556 write_byte(ebda_seg,&EbdaData->cdemu.active, 0x00); // bye bye
5559 goto int13_success;
5560 break;
5562 default:
5563 BX_INFO("int13_eltorito: unsupported AH=%02x\n", GET_AH());
5564 goto int13_fail;
5565 break;
5568 int13_fail:
5569 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5570 SET_DISK_RET_STATUS(GET_AH());
5571 SET_CF(); // error occurred
5572 return;
5574 int13_success:
5575 SET_AH(0x00); // no error
5576 SET_DISK_RET_STATUS(0x00);
5577 CLEAR_CF(); // no error
5578 return;
5581 // ---------------------------------------------------------------------------
5582 // End of int13 for eltorito functions
5583 // ---------------------------------------------------------------------------
5585 // ---------------------------------------------------------------------------
5586 // Start of int13 when emulating a device from the cd
5587 // ---------------------------------------------------------------------------
5589 void
5590 int13_cdemu(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
5591 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
5593 Bit16u ebda_seg=read_word(0x0040,0x000E);
5594 Bit8u device, status;
5595 Bit16u vheads, vspt, vcylinders;
5596 Bit16u head, sector, cylinder, nbsectors;
5597 Bit32u vlba, ilba, slba, elba;
5598 Bit16u before, segment, offset;
5599 Bit8u atacmd[12];
5601 BX_DEBUG_INT13_ET("int13_cdemu: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5603 /* at this point, we are emulating a floppy/harddisk */
5605 // Recompute the device number
5606 device = read_byte(ebda_seg,&EbdaData->cdemu.controller_index) * 2;
5607 device += read_byte(ebda_seg,&EbdaData->cdemu.device_spec);
5609 SET_DISK_RET_STATUS(0x00);
5611 /* basic checks : emulation should be active, dl should equal the emulated drive */
5612 if( (read_byte(ebda_seg,&EbdaData->cdemu.active) ==0 )
5613 || (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive ) != GET_DL())) {
5614 BX_INFO("int13_cdemu: function %02x, emulation not active for DL= %02x\n", GET_AH(), GET_DL());
5615 goto int13_fail;
5618 switch (GET_AH()) {
5620 // all those functions return SUCCESS
5621 case 0x00: /* disk controller reset */
5622 case 0x09: /* initialize drive parameters */
5623 case 0x0c: /* seek to specified cylinder */
5624 case 0x0d: /* alternate disk reset */ // FIXME ElTorito Various. should really reset ?
5625 case 0x10: /* check drive ready */ // FIXME ElTorito Various. should check if ready ?
5626 case 0x11: /* recalibrate */
5627 case 0x14: /* controller internal diagnostic */
5628 case 0x16: /* detect disk change */
5629 goto int13_success;
5630 break;
5632 // all those functions return disk write-protected
5633 case 0x03: /* write disk sectors */
5634 case 0x05: /* format disk track */
5635 SET_AH(0x03);
5636 goto int13_fail_noah;
5637 break;
5639 case 0x01: /* read disk status */
5640 status=read_byte(0x0040, 0x0074);
5641 SET_AH(status);
5642 SET_DISK_RET_STATUS(0);
5644 /* set CF if error status read */
5645 if (status) goto int13_fail_nostatus;
5646 else goto int13_success_noah;
5647 break;
5649 case 0x02: // read disk sectors
5650 case 0x04: // verify disk sectors
5651 vspt = read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
5652 vcylinders = read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders);
5653 vheads = read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads);
5655 ilba = read_dword(ebda_seg,&EbdaData->cdemu.ilba);
5657 sector = GET_CL() & 0x003f;
5658 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
5659 head = GET_DH();
5660 nbsectors = GET_AL();
5661 segment = ES;
5662 offset = BX;
5664 // no sector to read ?
5665 if(nbsectors==0) goto int13_success;
5667 // sanity checks sco openserver needs this!
5668 if ((sector > vspt)
5669 || (cylinder >= vcylinders)
5670 || (head >= vheads)) {
5671 goto int13_fail;
5674 // After controls, verify do nothing
5675 if (GET_AH() == 0x04) goto int13_success;
5677 segment = ES+(BX / 16);
5678 offset = BX % 16;
5680 // calculate the virtual lba inside the image
5681 vlba=((((Bit32u)cylinder*(Bit32u)vheads)+(Bit32u)head)*(Bit32u)vspt)+((Bit32u)(sector-1));
5683 // In advance so we don't loose the count
5684 SET_AL(nbsectors);
5686 // start lba on cd
5687 slba = (Bit32u)vlba/4;
5688 before= (Bit16u)vlba%4;
5690 // end lba on cd
5691 elba = (Bit32u)(vlba+nbsectors-1)/4;
5693 memsetb(get_SS(),atacmd,0,12);
5694 atacmd[0]=0x28; // READ command
5695 atacmd[7]=((Bit16u)(elba-slba+1) & 0xff00) >> 8; // Sectors
5696 atacmd[8]=((Bit16u)(elba-slba+1) & 0x00ff); // Sectors
5697 atacmd[2]=(ilba+slba & 0xff000000) >> 24; // LBA
5698 atacmd[3]=(ilba+slba & 0x00ff0000) >> 16;
5699 atacmd[4]=(ilba+slba & 0x0000ff00) >> 8;
5700 atacmd[5]=(ilba+slba & 0x000000ff);
5701 if((status = ata_cmd_packet(device, 12, get_SS(), atacmd, before*512, nbsectors*512L, ATA_DATA_IN, segment,offset)) != 0) {
5702 BX_INFO("int13_cdemu: function %02x, error %02x !\n",GET_AH(),status);
5703 SET_AH(0x02);
5704 SET_AL(0);
5705 goto int13_fail_noah;
5708 goto int13_success;
5709 break;
5711 case 0x08: /* read disk drive parameters */
5712 vspt=read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
5713 vcylinders=read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders) - 1;
5714 vheads=read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads) - 1;
5716 SET_AL( 0x00 );
5717 SET_BL( 0x00 );
5718 SET_CH( vcylinders & 0xff );
5719 SET_CL((( vcylinders >> 2) & 0xc0) | ( vspt & 0x3f ));
5720 SET_DH( vheads );
5721 SET_DL( 0x02 ); // FIXME ElTorito Various. should send the real count of drives 1 or 2
5722 // FIXME ElTorito Harddisk. should send the HD count
5724 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
5725 case 0x01: SET_BL( 0x02 ); break;
5726 case 0x02: SET_BL( 0x04 ); break;
5727 case 0x03: SET_BL( 0x06 ); break;
5730 ASM_START
5731 push bp
5732 mov bp, sp
5733 mov ax, #diskette_param_table2
5734 mov _int13_cdemu.DI+2[bp], ax
5735 mov _int13_cdemu.ES+2[bp], cs
5736 pop bp
5737 ASM_END
5738 goto int13_success;
5739 break;
5741 case 0x15: /* read disk drive size */
5742 // FIXME ElTorito Harddisk. What geometry to send ?
5743 SET_AH(0x03);
5744 goto int13_success_noah;
5745 break;
5747 // all those functions return unimplemented
5748 case 0x0a: /* read disk sectors with ECC */
5749 case 0x0b: /* write disk sectors with ECC */
5750 case 0x18: /* set media type for format */
5751 case 0x41: // IBM/MS installation check
5752 // FIXME ElTorito Harddisk. Darwin would like to use EDD
5753 case 0x42: // IBM/MS extended read
5754 case 0x43: // IBM/MS extended write
5755 case 0x44: // IBM/MS verify sectors
5756 case 0x45: // IBM/MS lock/unlock drive
5757 case 0x46: // IBM/MS eject media
5758 case 0x47: // IBM/MS extended seek
5759 case 0x48: // IBM/MS get drive parameters
5760 case 0x49: // IBM/MS extended media change
5761 case 0x4e: // ? - set hardware configuration
5762 case 0x50: // ? - send packet command
5763 default:
5764 BX_INFO("int13_cdemu function AH=%02x unsupported, returns fail\n", GET_AH());
5765 goto int13_fail;
5766 break;
5769 int13_fail:
5770 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5771 int13_fail_noah:
5772 SET_DISK_RET_STATUS(GET_AH());
5773 int13_fail_nostatus:
5774 SET_CF(); // error occurred
5775 return;
5777 int13_success:
5778 SET_AH(0x00); // no error
5779 int13_success_noah:
5780 SET_DISK_RET_STATUS(0x00);
5781 CLEAR_CF(); // no error
5782 return;
5785 // ---------------------------------------------------------------------------
5786 // End of int13 when emulating a device from the cd
5787 // ---------------------------------------------------------------------------
5789 #endif // BX_ELTORITO_BOOT
5791 #else //BX_USE_ATADRV
5793 void
5794 outLBA(cylinder,hd_heads,head,hd_sectors,sector,dl)
5795 Bit16u cylinder;
5796 Bit16u hd_heads;
5797 Bit16u head;
5798 Bit16u hd_sectors;
5799 Bit16u sector;
5800 Bit16u dl;
5802 ASM_START
5803 push bp
5804 mov bp, sp
5805 push eax
5806 push ebx
5807 push edx
5808 xor eax,eax
5809 mov ax,4[bp] // cylinder
5810 xor ebx,ebx
5811 mov bl,6[bp] // hd_heads
5812 imul ebx
5814 mov bl,8[bp] // head
5815 add eax,ebx
5816 mov bl,10[bp] // hd_sectors
5817 imul ebx
5818 mov bl,12[bp] // sector
5819 add eax,ebx
5821 dec eax
5822 mov dx,#0x1f3
5823 out dx,al
5824 mov dx,#0x1f4
5825 mov al,ah
5826 out dx,al
5827 shr eax,#16
5828 mov dx,#0x1f5
5829 out dx,al
5830 and ah,#0xf
5831 mov bl,14[bp] // dl
5832 and bl,#1
5833 shl bl,#4
5834 or ah,bl
5835 or ah,#0xe0
5836 mov al,ah
5837 mov dx,#0x01f6
5838 out dx,al
5839 pop edx
5840 pop ebx
5841 pop eax
5842 pop bp
5843 ASM_END
5846 void
5847 int13_harddisk(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
5848 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
5850 Bit8u drive, num_sectors, sector, head, status, mod;
5851 Bit8u drive_map;
5852 Bit8u n_drives;
5853 Bit16u cyl_mod, ax;
5854 Bit16u max_cylinder, cylinder, total_sectors;
5855 Bit16u hd_cylinders;
5856 Bit8u hd_heads, hd_sectors;
5857 Bit16u val16;
5858 Bit8u sector_count;
5859 unsigned int i;
5860 Bit16u tempbx;
5861 Bit16u dpsize;
5863 Bit16u count, segment, offset;
5864 Bit32u lba;
5865 Bit16u error;
5867 BX_DEBUG_INT13_HD("int13 harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5869 write_byte(0x0040, 0x008e, 0); // clear completion flag
5871 /* at this point, DL is >= 0x80 to be passed from the floppy int13h
5872 handler code */
5873 /* check how many disks first (cmos reg 0x12), return an error if
5874 drive not present */
5875 drive_map = inb_cmos(0x12);
5876 drive_map = (((drive_map & 0xf0)==0) ? 0 : 1) |
5877 (((drive_map & 0x0f)==0) ? 0 : 2);
5878 n_drives = (drive_map==0) ? 0 :
5879 ((drive_map==3) ? 2 : 1);
5881 if (!(drive_map & (1<<(GET_ELDL()&0x7f)))) { /* allow 0, 1, or 2 disks */
5882 SET_AH(0x01);
5883 SET_DISK_RET_STATUS(0x01);
5884 SET_CF(); /* error occurred */
5885 return;
5888 switch (GET_AH()) {
5890 case 0x00: /* disk controller reset */
5891 BX_DEBUG_INT13_HD("int13_f00\n");
5893 SET_AH(0);
5894 SET_DISK_RET_STATUS(0);
5895 set_diskette_ret_status(0);
5896 set_diskette_current_cyl(0, 0); /* current cylinder, diskette 1 */
5897 set_diskette_current_cyl(1, 0); /* current cylinder, diskette 2 */
5898 CLEAR_CF(); /* successful */
5899 return;
5900 break;
5902 case 0x01: /* read disk status */
5903 BX_DEBUG_INT13_HD("int13_f01\n");
5904 status = read_byte(0x0040, 0x0074);
5905 SET_AH(status);
5906 SET_DISK_RET_STATUS(0);
5907 /* set CF if error status read */
5908 if (status) SET_CF();
5909 else CLEAR_CF();
5910 return;
5911 break;
5913 case 0x04: // verify disk sectors
5914 case 0x02: // read disk sectors
5915 drive = GET_ELDL();
5916 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
5918 num_sectors = GET_AL();
5919 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
5920 sector = (GET_CL() & 0x3f);
5921 head = GET_DH();
5924 if (hd_cylinders > 1024) {
5925 if (hd_cylinders <= 2048) {
5926 cylinder <<= 1;
5928 else if (hd_cylinders <= 4096) {
5929 cylinder <<= 2;
5931 else if (hd_cylinders <= 8192) {
5932 cylinder <<= 3;
5934 else { // hd_cylinders <= 16384
5935 cylinder <<= 4;
5938 ax = head / hd_heads;
5939 cyl_mod = ax & 0xff;
5940 head = ax >> 8;
5941 cylinder |= cyl_mod;
5944 if ( (cylinder >= hd_cylinders) ||
5945 (sector > hd_sectors) ||
5946 (head >= hd_heads) ) {
5947 SET_AH(1);
5948 SET_DISK_RET_STATUS(1);
5949 SET_CF(); /* error occurred */
5950 return;
5953 if ( (num_sectors > 128) || (num_sectors == 0) )
5954 BX_PANIC("int13_harddisk(): num_sectors out of range!\n");
5956 if (head > 15)
5957 BX_PANIC("hard drive BIOS:(read/verify) head > 15\n");
5959 if ( GET_AH() == 0x04 ) {
5960 SET_AH(0);
5961 SET_DISK_RET_STATUS(0);
5962 CLEAR_CF();
5963 return;
5966 status = inb(0x1f7);
5967 if (status & 0x80) {
5968 BX_PANIC("hard drive BIOS:(read/verify) BUSY bit set\n");
5970 outb(0x01f2, num_sectors);
5971 /* activate LBA? (tomv) */
5972 if (hd_heads > 16) {
5973 BX_DEBUG_INT13_HD("CHS: %x %x %x\n", cylinder, head, sector);
5974 outLBA(cylinder,hd_heads,head,hd_sectors,sector,drive);
5976 else {
5977 outb(0x01f3, sector);
5978 outb(0x01f4, cylinder & 0x00ff);
5979 outb(0x01f5, cylinder >> 8);
5980 outb(0x01f6, 0xa0 | ((drive & 0x01)<<4) | (head & 0x0f));
5982 outb(0x01f7, 0x20);
5984 while (1) {
5985 status = inb(0x1f7);
5986 if ( !(status & 0x80) ) break;
5989 if (status & 0x01) {
5990 BX_PANIC("hard drive BIOS:(read/verify) read error\n");
5991 } else if ( !(status & 0x08) ) {
5992 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
5993 BX_PANIC("hard drive BIOS:(read/verify) expected DRQ=1\n");
5996 sector_count = 0;
5997 tempbx = BX;
5999 ASM_START
6000 sti ;; enable higher priority interrupts
6001 ASM_END
6003 while (1) {
6004 ASM_START
6005 ;; store temp bx in real DI register
6006 push bp
6007 mov bp, sp
6008 mov di, _int13_harddisk.tempbx + 2 [bp]
6009 pop bp
6011 ;; adjust if there will be an overrun
6012 cmp di, #0xfe00
6013 jbe i13_f02_no_adjust
6014 i13_f02_adjust:
6015 sub di, #0x0200 ; sub 512 bytes from offset
6016 mov ax, es
6017 add ax, #0x0020 ; add 512 to segment
6018 mov es, ax
6020 i13_f02_no_adjust:
6021 mov cx, #0x0100 ;; counter (256 words = 512b)
6022 mov dx, #0x01f0 ;; AT data read port
6025 insw ;; CX words transfered from port(DX) to ES:[DI]
6027 i13_f02_done:
6028 ;; store real DI register back to temp bx
6029 push bp
6030 mov bp, sp
6031 mov _int13_harddisk.tempbx + 2 [bp], di
6032 pop bp
6033 ASM_END
6035 sector_count++;
6036 num_sectors--;
6037 if (num_sectors == 0) {
6038 status = inb(0x1f7);
6039 if ( (status & 0xc9) != 0x40 )
6040 BX_PANIC("no sectors left to read/verify, status is %02x\n", (unsigned) status);
6041 break;
6043 else {
6044 status = inb(0x1f7);
6045 if ( (status & 0xc9) != 0x48 )
6046 BX_PANIC("more sectors left to read/verify, status is %02x\n", (unsigned) status);
6047 continue;
6051 SET_AH(0);
6052 SET_DISK_RET_STATUS(0);
6053 SET_AL(sector_count);
6054 CLEAR_CF(); /* successful */
6055 return;
6056 break;
6059 case 0x03: /* write disk sectors */
6060 BX_DEBUG_INT13_HD("int13_f03\n");
6061 drive = GET_ELDL ();
6062 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6064 num_sectors = GET_AL();
6065 cylinder = GET_CH();
6066 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
6067 sector = (GET_CL() & 0x3f);
6068 head = GET_DH();
6070 if (hd_cylinders > 1024) {
6071 if (hd_cylinders <= 2048) {
6072 cylinder <<= 1;
6074 else if (hd_cylinders <= 4096) {
6075 cylinder <<= 2;
6077 else if (hd_cylinders <= 8192) {
6078 cylinder <<= 3;
6080 else { // hd_cylinders <= 16384
6081 cylinder <<= 4;
6084 ax = head / hd_heads;
6085 cyl_mod = ax & 0xff;
6086 head = ax >> 8;
6087 cylinder |= cyl_mod;
6090 if ( (cylinder >= hd_cylinders) ||
6091 (sector > hd_sectors) ||
6092 (head >= hd_heads) ) {
6093 SET_AH( 1);
6094 SET_DISK_RET_STATUS(1);
6095 SET_CF(); /* error occurred */
6096 return;
6099 if ( (num_sectors > 128) || (num_sectors == 0) )
6100 BX_PANIC("int13_harddisk(): num_sectors out of range!\n");
6102 if (head > 15)
6103 BX_PANIC("hard drive BIOS:(read) head > 15\n");
6105 status = inb(0x1f7);
6106 if (status & 0x80) {
6107 BX_PANIC("hard drive BIOS:(read) BUSY bit set\n");
6109 // should check for Drive Ready Bit also in status reg
6110 outb(0x01f2, num_sectors);
6112 /* activate LBA? (tomv) */
6113 if (hd_heads > 16) {
6114 BX_DEBUG_INT13_HD("CHS (write): %x %x %x\n", cylinder, head, sector);
6115 outLBA(cylinder,hd_heads,head,hd_sectors,sector,GET_ELDL());
6117 else {
6118 outb(0x01f3, sector);
6119 outb(0x01f4, cylinder & 0x00ff);
6120 outb(0x01f5, cylinder >> 8);
6121 outb(0x01f6, 0xa0 | ((GET_ELDL() & 0x01)<<4) | (head & 0x0f));
6123 outb(0x01f7, 0x30);
6125 // wait for busy bit to turn off after seeking
6126 while (1) {
6127 status = inb(0x1f7);
6128 if ( !(status & 0x80) ) break;
6131 if ( !(status & 0x08) ) {
6132 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
6133 BX_PANIC("hard drive BIOS:(write) data-request bit not set\n");
6136 sector_count = 0;
6137 tempbx = BX;
6139 ASM_START
6140 sti ;; enable higher priority interrupts
6141 ASM_END
6143 while (1) {
6144 ASM_START
6145 ;; store temp bx in real SI register
6146 push bp
6147 mov bp, sp
6148 mov si, _int13_harddisk.tempbx + 2 [bp]
6149 pop bp
6151 ;; adjust if there will be an overrun
6152 cmp si, #0xfe00
6153 jbe i13_f03_no_adjust
6154 i13_f03_adjust:
6155 sub si, #0x0200 ; sub 512 bytes from offset
6156 mov ax, es
6157 add ax, #0x0020 ; add 512 to segment
6158 mov es, ax
6160 i13_f03_no_adjust:
6161 mov cx, #0x0100 ;; counter (256 words = 512b)
6162 mov dx, #0x01f0 ;; AT data read port
6164 seg ES
6166 outsw ;; CX words tranfered from ES:[SI] to port(DX)
6168 ;; store real SI register back to temp bx
6169 push bp
6170 mov bp, sp
6171 mov _int13_harddisk.tempbx + 2 [bp], si
6172 pop bp
6173 ASM_END
6175 sector_count++;
6176 num_sectors--;
6177 if (num_sectors == 0) {
6178 status = inb(0x1f7);
6179 if ( (status & 0xe9) != 0x40 )
6180 BX_PANIC("no sectors left to write, status is %02x\n", (unsigned) status);
6181 break;
6183 else {
6184 status = inb(0x1f7);
6185 if ( (status & 0xc9) != 0x48 )
6186 BX_PANIC("more sectors left to write, status is %02x\n", (unsigned) status);
6187 continue;
6191 SET_AH(0);
6192 SET_DISK_RET_STATUS(0);
6193 SET_AL(sector_count);
6194 CLEAR_CF(); /* successful */
6195 return;
6196 break;
6198 case 0x05: /* format disk track */
6199 BX_DEBUG_INT13_HD("int13_f05\n");
6200 BX_PANIC("format disk track called\n");
6201 /* nop */
6202 SET_AH(0);
6203 SET_DISK_RET_STATUS(0);
6204 CLEAR_CF(); /* successful */
6205 return;
6206 break;
6208 case 0x08: /* read disk drive parameters */
6209 BX_DEBUG_INT13_HD("int13_f08\n");
6211 drive = GET_ELDL ();
6212 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6214 // translate CHS
6216 if (hd_cylinders <= 1024) {
6217 // hd_cylinders >>= 0;
6218 // hd_heads <<= 0;
6220 else if (hd_cylinders <= 2048) {
6221 hd_cylinders >>= 1;
6222 hd_heads <<= 1;
6224 else if (hd_cylinders <= 4096) {
6225 hd_cylinders >>= 2;
6226 hd_heads <<= 2;
6228 else if (hd_cylinders <= 8192) {
6229 hd_cylinders >>= 3;
6230 hd_heads <<= 3;
6232 else { // hd_cylinders <= 16384
6233 hd_cylinders >>= 4;
6234 hd_heads <<= 4;
6237 max_cylinder = hd_cylinders - 2; /* 0 based */
6238 SET_AL(0);
6239 SET_CH(max_cylinder & 0xff);
6240 SET_CL(((max_cylinder >> 2) & 0xc0) | (hd_sectors & 0x3f));
6241 SET_DH(hd_heads - 1);
6242 SET_DL(n_drives); /* returns 0, 1, or 2 hard drives */
6243 SET_AH(0);
6244 SET_DISK_RET_STATUS(0);
6245 CLEAR_CF(); /* successful */
6247 return;
6248 break;
6250 case 0x09: /* initialize drive parameters */
6251 BX_DEBUG_INT13_HD("int13_f09\n");
6252 SET_AH(0);
6253 SET_DISK_RET_STATUS(0);
6254 CLEAR_CF(); /* successful */
6255 return;
6256 break;
6258 case 0x0a: /* read disk sectors with ECC */
6259 BX_DEBUG_INT13_HD("int13_f0a\n");
6260 case 0x0b: /* write disk sectors with ECC */
6261 BX_DEBUG_INT13_HD("int13_f0b\n");
6262 BX_PANIC("int13h Functions 0Ah & 0Bh not implemented!\n");
6263 return;
6264 break;
6266 case 0x0c: /* seek to specified cylinder */
6267 BX_DEBUG_INT13_HD("int13_f0c\n");
6268 BX_INFO("int13h function 0ch (seek) not implemented!\n");
6269 SET_AH(0);
6270 SET_DISK_RET_STATUS(0);
6271 CLEAR_CF(); /* successful */
6272 return;
6273 break;
6275 case 0x0d: /* alternate disk reset */
6276 BX_DEBUG_INT13_HD("int13_f0d\n");
6277 SET_AH(0);
6278 SET_DISK_RET_STATUS(0);
6279 CLEAR_CF(); /* successful */
6280 return;
6281 break;
6283 case 0x10: /* check drive ready */
6284 BX_DEBUG_INT13_HD("int13_f10\n");
6285 //SET_AH(0);
6286 //SET_DISK_RET_STATUS(0);
6287 //CLEAR_CF(); /* successful */
6288 //return;
6289 //break;
6291 // should look at 40:8E also???
6292 status = inb(0x01f7);
6293 if ( (status & 0xc0) == 0x40 ) {
6294 SET_AH(0);
6295 SET_DISK_RET_STATUS(0);
6296 CLEAR_CF(); // drive ready
6297 return;
6299 else {
6300 SET_AH(0xAA);
6301 SET_DISK_RET_STATUS(0xAA);
6302 SET_CF(); // not ready
6303 return;
6305 break;
6307 case 0x11: /* recalibrate */
6308 BX_DEBUG_INT13_HD("int13_f11\n");
6309 SET_AH(0);
6310 SET_DISK_RET_STATUS(0);
6311 CLEAR_CF(); /* successful */
6312 return;
6313 break;
6315 case 0x14: /* controller internal diagnostic */
6316 BX_DEBUG_INT13_HD("int13_f14\n");
6317 SET_AH(0);
6318 SET_DISK_RET_STATUS(0);
6319 CLEAR_CF(); /* successful */
6320 SET_AL(0);
6321 return;
6322 break;
6324 case 0x15: /* read disk drive size */
6325 drive = GET_ELDL();
6326 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6327 ASM_START
6328 push bp
6329 mov bp, sp
6330 mov al, _int13_harddisk.hd_heads + 2 [bp]
6331 mov ah, _int13_harddisk.hd_sectors + 2 [bp]
6332 mul al, ah ;; ax = heads * sectors
6333 mov bx, _int13_harddisk.hd_cylinders + 2 [bp]
6334 dec bx ;; use (cylinders - 1) ???
6335 mul ax, bx ;; dx:ax = (cylinders -1) * (heads * sectors)
6336 ;; now we need to move the 32bit result dx:ax to what the
6337 ;; BIOS wants which is cx:dx.
6338 ;; and then into CX:DX on the stack
6339 mov _int13_harddisk.CX + 2 [bp], dx
6340 mov _int13_harddisk.DX + 2 [bp], ax
6341 pop bp
6342 ASM_END
6343 SET_AH(3); // hard disk accessible
6344 SET_DISK_RET_STATUS(0); // ??? should this be 0
6345 CLEAR_CF(); // successful
6346 return;
6347 break;
6349 case 0x18: // set media type for format
6350 case 0x41: // IBM/MS
6351 case 0x42: // IBM/MS
6352 case 0x43: // IBM/MS
6353 case 0x44: // IBM/MS
6354 case 0x45: // IBM/MS lock/unlock drive
6355 case 0x46: // IBM/MS eject media
6356 case 0x47: // IBM/MS extended seek
6357 case 0x49: // IBM/MS extended media change
6358 case 0x50: // IBM/MS send packet command
6359 default:
6360 BX_INFO("int13_harddisk: unsupported AH=%02x\n", GET_AH());
6362 SET_AH(1); // code=invalid function in AH or invalid parameter
6363 SET_DISK_RET_STATUS(1);
6364 SET_CF(); /* unsuccessful */
6365 return;
6366 break;
6370 static char panic_msg_reg12h[] = "HD%d cmos reg 12h not type F\n";
6371 static char panic_msg_reg19h[] = "HD%d cmos reg %02xh not user definable type 47\n";
6373 void
6374 get_hd_geometry(drive, hd_cylinders, hd_heads, hd_sectors)
6375 Bit8u drive;
6376 Bit16u *hd_cylinders;
6377 Bit8u *hd_heads;
6378 Bit8u *hd_sectors;
6380 Bit8u hd_type;
6381 Bit16u ss;
6382 Bit16u cylinders;
6383 Bit8u iobase;
6385 ss = get_SS();
6386 if (drive == 0x80) {
6387 hd_type = inb_cmos(0x12) & 0xf0;
6388 if (hd_type != 0xf0)
6389 BX_INFO(panic_msg_reg12h,0);
6390 hd_type = inb_cmos(0x19); // HD0: extended type
6391 if (hd_type != 47)
6392 BX_INFO(panic_msg_reg19h,0,0x19);
6393 iobase = 0x1b;
6394 } else {
6395 hd_type = inb_cmos(0x12) & 0x0f;
6396 if (hd_type != 0x0f)
6397 BX_INFO(panic_msg_reg12h,1);
6398 hd_type = inb_cmos(0x1a); // HD0: extended type
6399 if (hd_type != 47)
6400 BX_INFO(panic_msg_reg19h,0,0x1a);
6401 iobase = 0x24;
6404 // cylinders
6405 cylinders = inb_cmos(iobase) | (inb_cmos(iobase+1) << 8);
6406 write_word(ss, hd_cylinders, cylinders);
6408 // heads
6409 write_byte(ss, hd_heads, inb_cmos(iobase+2));
6411 // sectors per track
6412 write_byte(ss, hd_sectors, inb_cmos(iobase+8));
6415 #endif //else BX_USE_ATADRV
6418 //////////////////////
6419 // FLOPPY functions //
6420 //////////////////////
6422 void floppy_reset_controller()
6424 Bit8u val8;
6426 // Reset controller
6427 val8 = inb(0x03f2);
6428 outb(0x03f2, val8 & ~0x04);
6429 outb(0x03f2, val8 | 0x04);
6431 // Wait for controller to come out of reset
6432 do {
6433 val8 = inb(0x3f4);
6434 } while ( (val8 & 0xc0) != 0x80 );
6437 void floppy_prepare_controller(drive)
6438 Bit16u drive;
6440 Bit8u val8, dor, prev_reset;
6442 // set 40:3e bit 7 to 0
6443 val8 = read_byte(0x0040, 0x003e);
6444 val8 &= 0x7f;
6445 write_byte(0x0040, 0x003e, val8);
6447 // turn on motor of selected drive, DMA & int enabled, normal operation
6448 prev_reset = inb(0x03f2) & 0x04;
6449 if (drive)
6450 dor = 0x20;
6451 else
6452 dor = 0x10;
6453 dor |= 0x0c;
6454 dor |= drive;
6455 outb(0x03f2, dor);
6457 // reset the disk motor timeout value of INT 08
6458 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
6460 // wait for drive readiness
6461 do {
6462 val8 = inb(0x3f4);
6463 } while ( (val8 & 0xc0) != 0x80 );
6465 if (prev_reset == 0) {
6466 // turn on interrupts
6467 ASM_START
6469 ASM_END
6470 // wait on 40:3e bit 7 to become 1
6471 do {
6472 val8 = read_byte(0x0040, 0x003e);
6473 } while ( (val8 & 0x80) == 0 );
6474 val8 &= 0x7f;
6475 ASM_START
6477 ASM_END
6478 write_byte(0x0040, 0x003e, val8);
6482 bx_bool
6483 floppy_media_known(drive)
6484 Bit16u drive;
6486 Bit8u val8;
6487 Bit16u media_state_offset;
6489 val8 = read_byte(0x0040, 0x003e); // diskette recal status
6490 if (drive)
6491 val8 >>= 1;
6492 val8 &= 0x01;
6493 if (val8 == 0)
6494 return(0);
6496 media_state_offset = 0x0090;
6497 if (drive)
6498 media_state_offset += 1;
6500 val8 = read_byte(0x0040, media_state_offset);
6501 val8 = (val8 >> 4) & 0x01;
6502 if (val8 == 0)
6503 return(0);
6505 // check pass, return KNOWN
6506 return(1);
6509 bx_bool
6510 floppy_media_sense(drive)
6511 Bit16u drive;
6513 bx_bool retval;
6514 Bit16u media_state_offset;
6515 Bit8u drive_type, config_data, media_state;
6517 if (floppy_drive_recal(drive) == 0) {
6518 return(0);
6521 // for now cheat and get drive type from CMOS,
6522 // assume media is same as drive type
6524 // ** config_data **
6525 // Bitfields for diskette media control:
6526 // Bit(s) Description (Table M0028)
6527 // 7-6 last data rate set by controller
6528 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6529 // 5-4 last diskette drive step rate selected
6530 // 00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
6531 // 3-2 {data rate at start of operation}
6532 // 1-0 reserved
6534 // ** media_state **
6535 // Bitfields for diskette drive media state:
6536 // Bit(s) Description (Table M0030)
6537 // 7-6 data rate
6538 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6539 // 5 double stepping required (e.g. 360kB in 1.2MB)
6540 // 4 media type established
6541 // 3 drive capable of supporting 4MB media
6542 // 2-0 on exit from BIOS, contains
6543 // 000 trying 360kB in 360kB
6544 // 001 trying 360kB in 1.2MB
6545 // 010 trying 1.2MB in 1.2MB
6546 // 011 360kB in 360kB established
6547 // 100 360kB in 1.2MB established
6548 // 101 1.2MB in 1.2MB established
6549 // 110 reserved
6550 // 111 all other formats/drives
6552 drive_type = inb_cmos(0x10);
6553 if (drive == 0)
6554 drive_type >>= 4;
6555 else
6556 drive_type &= 0x0f;
6557 if ( drive_type == 1 ) {
6558 // 360K 5.25" drive
6559 config_data = 0x00; // 0000 0000
6560 media_state = 0x25; // 0010 0101
6561 retval = 1;
6563 else if ( drive_type == 2 ) {
6564 // 1.2 MB 5.25" drive
6565 config_data = 0x00; // 0000 0000
6566 media_state = 0x25; // 0010 0101 // need double stepping??? (bit 5)
6567 retval = 1;
6569 else if ( drive_type == 3 ) {
6570 // 720K 3.5" drive
6571 config_data = 0x00; // 0000 0000 ???
6572 media_state = 0x17; // 0001 0111
6573 retval = 1;
6575 else if ( drive_type == 4 ) {
6576 // 1.44 MB 3.5" drive
6577 config_data = 0x00; // 0000 0000
6578 media_state = 0x17; // 0001 0111
6579 retval = 1;
6581 else if ( drive_type == 5 ) {
6582 // 2.88 MB 3.5" drive
6583 config_data = 0xCC; // 1100 1100
6584 media_state = 0xD7; // 1101 0111
6585 retval = 1;
6588 // Extended floppy size uses special cmos setting
6589 else if ( drive_type == 6 ) {
6590 // 160k 5.25" drive
6591 config_data = 0x00; // 0000 0000
6592 media_state = 0x27; // 0010 0111
6593 retval = 1;
6595 else if ( drive_type == 7 ) {
6596 // 180k 5.25" drive
6597 config_data = 0x00; // 0000 0000
6598 media_state = 0x27; // 0010 0111
6599 retval = 1;
6601 else if ( drive_type == 8 ) {
6602 // 320k 5.25" drive
6603 config_data = 0x00; // 0000 0000
6604 media_state = 0x27; // 0010 0111
6605 retval = 1;
6608 else {
6609 // not recognized
6610 config_data = 0x00; // 0000 0000
6611 media_state = 0x00; // 0000 0000
6612 retval = 0;
6615 if (drive == 0)
6616 media_state_offset = 0x90;
6617 else
6618 media_state_offset = 0x91;
6619 write_byte(0x0040, 0x008B, config_data);
6620 write_byte(0x0040, media_state_offset, media_state);
6622 return(retval);
6625 bx_bool
6626 floppy_drive_recal(drive)
6627 Bit16u drive;
6629 Bit8u val8;
6630 Bit16u curr_cyl_offset;
6632 floppy_prepare_controller(drive);
6634 // send Recalibrate command (2 bytes) to controller
6635 outb(0x03f5, 0x07); // 07: Recalibrate
6636 outb(0x03f5, drive); // 0=drive0, 1=drive1
6638 // turn on interrupts
6639 ASM_START
6641 ASM_END
6643 // wait on 40:3e bit 7 to become 1
6644 do {
6645 val8 = (read_byte(0x0040, 0x003e) & 0x80);
6646 } while ( val8 == 0 );
6648 val8 = 0; // separate asm from while() loop
6649 // turn off interrupts
6650 ASM_START
6652 ASM_END
6654 // set 40:3e bit 7 to 0, and calibrated bit
6655 val8 = read_byte(0x0040, 0x003e);
6656 val8 &= 0x7f;
6657 if (drive) {
6658 val8 |= 0x02; // Drive 1 calibrated
6659 curr_cyl_offset = 0x0095;
6660 } else {
6661 val8 |= 0x01; // Drive 0 calibrated
6662 curr_cyl_offset = 0x0094;
6664 write_byte(0x0040, 0x003e, val8);
6665 write_byte(0x0040, curr_cyl_offset, 0); // current cylinder is 0
6667 return(1);
6672 bx_bool
6673 floppy_drive_exists(drive)
6674 Bit16u drive;
6676 Bit8u drive_type;
6678 // check CMOS to see if drive exists
6679 drive_type = inb_cmos(0x10);
6680 if (drive == 0)
6681 drive_type >>= 4;
6682 else
6683 drive_type &= 0x0f;
6684 if ( drive_type == 0 )
6685 return(0);
6686 else
6687 return(1);
6690 #if BX_SUPPORT_FLOPPY
6691 void
6692 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
6693 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
6695 Bit8u drive, num_sectors, track, sector, head, status;
6696 Bit16u base_address, base_count, base_es;
6697 Bit8u page, mode_register, val8, dor;
6698 Bit8u return_status[7];
6699 Bit8u drive_type, num_floppies, ah;
6700 Bit16u es, last_addr;
6702 BX_DEBUG_INT13_FL("int13_diskette: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
6704 ah = GET_AH();
6706 switch ( ah ) {
6707 case 0x00: // diskette controller reset
6708 BX_DEBUG_INT13_FL("floppy f00\n");
6709 drive = GET_ELDL();
6710 if (drive > 1) {
6711 SET_AH(1); // invalid param
6712 set_diskette_ret_status(1);
6713 SET_CF();
6714 return;
6716 drive_type = inb_cmos(0x10);
6718 if (drive == 0)
6719 drive_type >>= 4;
6720 else
6721 drive_type &= 0x0f;
6722 if (drive_type == 0) {
6723 SET_AH(0x80); // drive not responding
6724 set_diskette_ret_status(0x80);
6725 SET_CF();
6726 return;
6728 SET_AH(0);
6729 set_diskette_ret_status(0);
6730 CLEAR_CF(); // successful
6731 set_diskette_current_cyl(drive, 0); // current cylinder
6732 return;
6734 case 0x01: // Read Diskette Status
6735 CLEAR_CF();
6736 val8 = read_byte(0x0000, 0x0441);
6737 SET_AH(val8);
6738 if (val8) {
6739 SET_CF();
6741 return;
6743 case 0x02: // Read Diskette Sectors
6744 case 0x03: // Write Diskette Sectors
6745 case 0x04: // Verify Diskette Sectors
6746 num_sectors = GET_AL();
6747 track = GET_CH();
6748 sector = GET_CL();
6749 head = GET_DH();
6750 drive = GET_ELDL();
6752 if ( (drive > 1) || (head > 1) ||
6753 (num_sectors == 0) || (num_sectors > 72) ) {
6754 BX_INFO("floppy: drive>1 || head>1 ...\n");
6755 SET_AH(1);
6756 set_diskette_ret_status(1);
6757 SET_AL(0); // no sectors read
6758 SET_CF(); // error occurred
6759 return;
6762 // see if drive exists
6763 if (floppy_drive_exists(drive) == 0) {
6764 SET_AH(0x80); // not responding
6765 set_diskette_ret_status(0x80);
6766 SET_AL(0); // no sectors read
6767 SET_CF(); // error occurred
6768 return;
6771 // see if media in drive, and type is known
6772 if (floppy_media_known(drive) == 0) {
6773 if (floppy_media_sense(drive) == 0) {
6774 SET_AH(0x0C); // Media type not found
6775 set_diskette_ret_status(0x0C);
6776 SET_AL(0); // no sectors read
6777 SET_CF(); // error occurred
6778 return;
6782 if (ah == 0x02) {
6783 // Read Diskette Sectors
6785 //-----------------------------------
6786 // set up DMA controller for transfer
6787 //-----------------------------------
6789 // es:bx = pointer to where to place information from diskette
6790 // port 04: DMA-1 base and current address, channel 2
6791 // port 05: DMA-1 base and current count, channel 2
6792 page = (ES >> 12); // upper 4 bits
6793 base_es = (ES << 4); // lower 16bits contributed by ES
6794 base_address = base_es + BX; // lower 16 bits of address
6795 // contributed by ES:BX
6796 if ( base_address < base_es ) {
6797 // in case of carry, adjust page by 1
6798 page++;
6800 base_count = (num_sectors * 512) - 1;
6802 // check for 64K boundary overrun
6803 last_addr = base_address + base_count;
6804 if (last_addr < base_address) {
6805 SET_AH(0x09);
6806 set_diskette_ret_status(0x09);
6807 SET_AL(0); // no sectors read
6808 SET_CF(); // error occurred
6809 return;
6812 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
6813 outb(0x000a, 0x06);
6815 BX_DEBUG_INT13_FL("clear flip-flop\n");
6816 outb(0x000c, 0x00); // clear flip-flop
6817 outb(0x0004, base_address);
6818 outb(0x0004, base_address>>8);
6819 BX_DEBUG_INT13_FL("clear flip-flop\n");
6820 outb(0x000c, 0x00); // clear flip-flop
6821 outb(0x0005, base_count);
6822 outb(0x0005, base_count>>8);
6824 // port 0b: DMA-1 Mode Register
6825 mode_register = 0x46; // single mode, increment, autoinit disable,
6826 // transfer type=write, channel 2
6827 BX_DEBUG_INT13_FL("setting mode register\n");
6828 outb(0x000b, mode_register);
6830 BX_DEBUG_INT13_FL("setting page register\n");
6831 // port 81: DMA-1 Page Register, channel 2
6832 outb(0x0081, page);
6834 BX_DEBUG_INT13_FL("unmask chan 2\n");
6835 outb(0x000a, 0x02); // unmask channel 2
6837 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
6838 outb(0x000a, 0x02);
6840 //--------------------------------------
6841 // set up floppy controller for transfer
6842 //--------------------------------------
6843 floppy_prepare_controller(drive);
6845 // send read-normal-data command (9 bytes) to controller
6846 outb(0x03f5, 0xe6); // e6: read normal data
6847 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
6848 outb(0x03f5, track);
6849 outb(0x03f5, head);
6850 outb(0x03f5, sector);
6851 outb(0x03f5, 2); // 512 byte sector size
6852 outb(0x03f5, sector + num_sectors - 1); // last sector to read on track
6853 outb(0x03f5, 0); // Gap length
6854 outb(0x03f5, 0xff); // Gap length
6856 // turn on interrupts
6857 ASM_START
6859 ASM_END
6861 // wait on 40:3e bit 7 to become 1
6862 do {
6863 val8 = read_byte(0x0040, 0x0040);
6864 if (val8 == 0) {
6865 floppy_reset_controller();
6866 SET_AH(0x80); // drive not ready (timeout)
6867 set_diskette_ret_status(0x80);
6868 SET_AL(0); // no sectors read
6869 SET_CF(); // error occurred
6870 return;
6872 val8 = (read_byte(0x0040, 0x003e) & 0x80);
6873 } while ( val8 == 0 );
6875 val8 = 0; // separate asm from while() loop
6876 // turn off interrupts
6877 ASM_START
6879 ASM_END
6881 // set 40:3e bit 7 to 0
6882 val8 = read_byte(0x0040, 0x003e);
6883 val8 &= 0x7f;
6884 write_byte(0x0040, 0x003e, val8);
6886 // check port 3f4 for accessibility to status bytes
6887 val8 = inb(0x3f4);
6888 if ( (val8 & 0xc0) != 0xc0 )
6889 BX_PANIC("int13_diskette: ctrl not ready\n");
6891 // read 7 return status bytes from controller
6892 // using loop index broken, have to unroll...
6893 return_status[0] = inb(0x3f5);
6894 return_status[1] = inb(0x3f5);
6895 return_status[2] = inb(0x3f5);
6896 return_status[3] = inb(0x3f5);
6897 return_status[4] = inb(0x3f5);
6898 return_status[5] = inb(0x3f5);
6899 return_status[6] = inb(0x3f5);
6900 // record in BIOS Data Area
6901 write_byte(0x0040, 0x0042, return_status[0]);
6902 write_byte(0x0040, 0x0043, return_status[1]);
6903 write_byte(0x0040, 0x0044, return_status[2]);
6904 write_byte(0x0040, 0x0045, return_status[3]);
6905 write_byte(0x0040, 0x0046, return_status[4]);
6906 write_byte(0x0040, 0x0047, return_status[5]);
6907 write_byte(0x0040, 0x0048, return_status[6]);
6909 if ( (return_status[0] & 0xc0) != 0 ) {
6910 SET_AH(0x20);
6911 set_diskette_ret_status(0x20);
6912 SET_AL(0); // no sectors read
6913 SET_CF(); // error occurred
6914 return;
6917 // ??? should track be new val from return_status[3] ?
6918 set_diskette_current_cyl(drive, track);
6919 // AL = number of sectors read (same value as passed)
6920 SET_AH(0x00); // success
6921 CLEAR_CF(); // success
6922 return;
6923 } else if (ah == 0x03) {
6924 // Write Diskette Sectors
6926 //-----------------------------------
6927 // set up DMA controller for transfer
6928 //-----------------------------------
6930 // es:bx = pointer to where to place information from diskette
6931 // port 04: DMA-1 base and current address, channel 2
6932 // port 05: DMA-1 base and current count, channel 2
6933 page = (ES >> 12); // upper 4 bits
6934 base_es = (ES << 4); // lower 16bits contributed by ES
6935 base_address = base_es + BX; // lower 16 bits of address
6936 // contributed by ES:BX
6937 if ( base_address < base_es ) {
6938 // in case of carry, adjust page by 1
6939 page++;
6941 base_count = (num_sectors * 512) - 1;
6943 // check for 64K boundary overrun
6944 last_addr = base_address + base_count;
6945 if (last_addr < base_address) {
6946 SET_AH(0x09);
6947 set_diskette_ret_status(0x09);
6948 SET_AL(0); // no sectors read
6949 SET_CF(); // error occurred
6950 return;
6953 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
6954 outb(0x000a, 0x06);
6956 outb(0x000c, 0x00); // clear flip-flop
6957 outb(0x0004, base_address);
6958 outb(0x0004, base_address>>8);
6959 outb(0x000c, 0x00); // clear flip-flop
6960 outb(0x0005, base_count);
6961 outb(0x0005, base_count>>8);
6963 // port 0b: DMA-1 Mode Register
6964 mode_register = 0x4a; // single mode, increment, autoinit disable,
6965 // transfer type=read, channel 2
6966 outb(0x000b, mode_register);
6968 // port 81: DMA-1 Page Register, channel 2
6969 outb(0x0081, page);
6971 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
6972 outb(0x000a, 0x02);
6974 //--------------------------------------
6975 // set up floppy controller for transfer
6976 //--------------------------------------
6977 floppy_prepare_controller(drive);
6979 // send write-normal-data command (9 bytes) to controller
6980 outb(0x03f5, 0xc5); // c5: write normal data
6981 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
6982 outb(0x03f5, track);
6983 outb(0x03f5, head);
6984 outb(0x03f5, sector);
6985 outb(0x03f5, 2); // 512 byte sector size
6986 outb(0x03f5, sector + num_sectors - 1); // last sector to write on track
6987 outb(0x03f5, 0); // Gap length
6988 outb(0x03f5, 0xff); // Gap length
6990 // turn on interrupts
6991 ASM_START
6993 ASM_END
6995 // wait on 40:3e bit 7 to become 1
6996 do {
6997 val8 = read_byte(0x0040, 0x0040);
6998 if (val8 == 0) {
6999 floppy_reset_controller();
7000 SET_AH(0x80); // drive not ready (timeout)
7001 set_diskette_ret_status(0x80);
7002 SET_AL(0); // no sectors written
7003 SET_CF(); // error occurred
7004 return;
7006 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7007 } while ( val8 == 0 );
7009 val8 = 0; // separate asm from while() loop
7010 // turn off interrupts
7011 ASM_START
7013 ASM_END
7015 // set 40:3e bit 7 to 0
7016 val8 = read_byte(0x0040, 0x003e);
7017 val8 &= 0x7f;
7018 write_byte(0x0040, 0x003e, val8);
7020 // check port 3f4 for accessibility to status bytes
7021 val8 = inb(0x3f4);
7022 if ( (val8 & 0xc0) != 0xc0 )
7023 BX_PANIC("int13_diskette: ctrl not ready\n");
7025 // read 7 return status bytes from controller
7026 // using loop index broken, have to unroll...
7027 return_status[0] = inb(0x3f5);
7028 return_status[1] = inb(0x3f5);
7029 return_status[2] = inb(0x3f5);
7030 return_status[3] = inb(0x3f5);
7031 return_status[4] = inb(0x3f5);
7032 return_status[5] = inb(0x3f5);
7033 return_status[6] = inb(0x3f5);
7034 // record in BIOS Data Area
7035 write_byte(0x0040, 0x0042, return_status[0]);
7036 write_byte(0x0040, 0x0043, return_status[1]);
7037 write_byte(0x0040, 0x0044, return_status[2]);
7038 write_byte(0x0040, 0x0045, return_status[3]);
7039 write_byte(0x0040, 0x0046, return_status[4]);
7040 write_byte(0x0040, 0x0047, return_status[5]);
7041 write_byte(0x0040, 0x0048, return_status[6]);
7043 if ( (return_status[0] & 0xc0) != 0 ) {
7044 if ( (return_status[1] & 0x02) != 0 ) {
7045 // diskette not writable.
7046 // AH=status code=0x03 (tried to write on write-protected disk)
7047 // AL=number of sectors written=0
7048 AX = 0x0300;
7049 SET_CF();
7050 return;
7051 } else {
7052 BX_PANIC("int13_diskette_function: read error\n");
7056 // ??? should track be new val from return_status[3] ?
7057 set_diskette_current_cyl(drive, track);
7058 // AL = number of sectors read (same value as passed)
7059 SET_AH(0x00); // success
7060 CLEAR_CF(); // success
7061 return;
7062 } else { // if (ah == 0x04)
7063 // Verify Diskette Sectors
7065 // ??? should track be new val from return_status[3] ?
7066 set_diskette_current_cyl(drive, track);
7067 // AL = number of sectors verified (same value as passed)
7068 CLEAR_CF(); // success
7069 SET_AH(0x00); // success
7070 return;
7072 break;
7074 case 0x05: // format diskette track
7075 BX_DEBUG_INT13_FL("floppy f05\n");
7077 num_sectors = GET_AL();
7078 track = GET_CH();
7079 head = GET_DH();
7080 drive = GET_ELDL();
7082 if ((drive > 1) || (head > 1) || (track > 79) ||
7083 (num_sectors == 0) || (num_sectors > 18)) {
7084 SET_AH(1);
7085 set_diskette_ret_status(1);
7086 SET_CF(); // error occurred
7089 // see if drive exists
7090 if (floppy_drive_exists(drive) == 0) {
7091 SET_AH(0x80); // drive not responding
7092 set_diskette_ret_status(0x80);
7093 SET_CF(); // error occurred
7094 return;
7097 // see if media in drive, and type is known
7098 if (floppy_media_known(drive) == 0) {
7099 if (floppy_media_sense(drive) == 0) {
7100 SET_AH(0x0C); // Media type not found
7101 set_diskette_ret_status(0x0C);
7102 SET_AL(0); // no sectors read
7103 SET_CF(); // error occurred
7104 return;
7108 // set up DMA controller for transfer
7109 page = (ES >> 12); // upper 4 bits
7110 base_es = (ES << 4); // lower 16bits contributed by ES
7111 base_address = base_es + BX; // lower 16 bits of address
7112 // contributed by ES:BX
7113 if ( base_address < base_es ) {
7114 // in case of carry, adjust page by 1
7115 page++;
7117 base_count = (num_sectors * 4) - 1;
7119 // check for 64K boundary overrun
7120 last_addr = base_address + base_count;
7121 if (last_addr < base_address) {
7122 SET_AH(0x09);
7123 set_diskette_ret_status(0x09);
7124 SET_AL(0); // no sectors read
7125 SET_CF(); // error occurred
7126 return;
7129 outb(0x000a, 0x06);
7130 outb(0x000c, 0x00); // clear flip-flop
7131 outb(0x0004, base_address);
7132 outb(0x0004, base_address>>8);
7133 outb(0x000c, 0x00); // clear flip-flop
7134 outb(0x0005, base_count);
7135 outb(0x0005, base_count>>8);
7136 mode_register = 0x4a; // single mode, increment, autoinit disable,
7137 // transfer type=read, channel 2
7138 outb(0x000b, mode_register);
7139 // port 81: DMA-1 Page Register, channel 2
7140 outb(0x0081, page);
7141 outb(0x000a, 0x02);
7143 // set up floppy controller for transfer
7144 floppy_prepare_controller(drive);
7146 // send format-track command (6 bytes) to controller
7147 outb(0x03f5, 0x4d); // 4d: format track
7148 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7149 outb(0x03f5, 2); // 512 byte sector size
7150 outb(0x03f5, num_sectors); // number of sectors per track
7151 outb(0x03f5, 0); // Gap length
7152 outb(0x03f5, 0xf6); // Fill byte
7153 // turn on interrupts
7154 ASM_START
7156 ASM_END
7158 // wait on 40:3e bit 7 to become 1
7159 do {
7160 val8 = read_byte(0x0040, 0x0040);
7161 if (val8 == 0) {
7162 floppy_reset_controller();
7163 SET_AH(0x80); // drive not ready (timeout)
7164 set_diskette_ret_status(0x80);
7165 SET_CF(); // error occurred
7166 return;
7168 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7169 } while ( val8 == 0 );
7171 val8 = 0; // separate asm from while() loop
7172 // turn off interrupts
7173 ASM_START
7175 ASM_END
7176 // set 40:3e bit 7 to 0
7177 val8 = read_byte(0x0040, 0x003e);
7178 val8 &= 0x7f;
7179 write_byte(0x0040, 0x003e, val8);
7180 // check port 3f4 for accessibility to status bytes
7181 val8 = inb(0x3f4);
7182 if ( (val8 & 0xc0) != 0xc0 )
7183 BX_PANIC("int13_diskette: ctrl not ready\n");
7185 // read 7 return status bytes from controller
7186 // using loop index broken, have to unroll...
7187 return_status[0] = inb(0x3f5);
7188 return_status[1] = inb(0x3f5);
7189 return_status[2] = inb(0x3f5);
7190 return_status[3] = inb(0x3f5);
7191 return_status[4] = inb(0x3f5);
7192 return_status[5] = inb(0x3f5);
7193 return_status[6] = inb(0x3f5);
7194 // record in BIOS Data Area
7195 write_byte(0x0040, 0x0042, return_status[0]);
7196 write_byte(0x0040, 0x0043, return_status[1]);
7197 write_byte(0x0040, 0x0044, return_status[2]);
7198 write_byte(0x0040, 0x0045, return_status[3]);
7199 write_byte(0x0040, 0x0046, return_status[4]);
7200 write_byte(0x0040, 0x0047, return_status[5]);
7201 write_byte(0x0040, 0x0048, return_status[6]);
7203 if ( (return_status[0] & 0xc0) != 0 ) {
7204 if ( (return_status[1] & 0x02) != 0 ) {
7205 // diskette not writable.
7206 // AH=status code=0x03 (tried to write on write-protected disk)
7207 // AL=number of sectors written=0
7208 AX = 0x0300;
7209 SET_CF();
7210 return;
7211 } else {
7212 BX_PANIC("int13_diskette_function: write error\n");
7216 SET_AH(0);
7217 set_diskette_ret_status(0);
7218 set_diskette_current_cyl(drive, 0);
7219 CLEAR_CF(); // successful
7220 return;
7223 case 0x08: // read diskette drive parameters
7224 BX_DEBUG_INT13_FL("floppy f08\n");
7225 drive = GET_ELDL();
7227 if (drive > 1) {
7228 AX = 0;
7229 BX = 0;
7230 CX = 0;
7231 DX = 0;
7232 ES = 0;
7233 DI = 0;
7234 SET_DL(num_floppies);
7235 SET_CF();
7236 return;
7239 drive_type = inb_cmos(0x10);
7240 num_floppies = 0;
7241 if (drive_type & 0xf0)
7242 num_floppies++;
7243 if (drive_type & 0x0f)
7244 num_floppies++;
7246 if (drive == 0)
7247 drive_type >>= 4;
7248 else
7249 drive_type &= 0x0f;
7251 SET_BH(0);
7252 SET_BL(drive_type);
7253 SET_AH(0);
7254 SET_AL(0);
7255 SET_DL(num_floppies);
7257 switch (drive_type) {
7258 case 0: // none
7259 CX = 0;
7260 SET_DH(0); // max head #
7261 break;
7263 case 1: // 360KB, 5.25"
7264 CX = 0x2709; // 40 tracks, 9 sectors
7265 SET_DH(1); // max head #
7266 break;
7268 case 2: // 1.2MB, 5.25"
7269 CX = 0x4f0f; // 80 tracks, 15 sectors
7270 SET_DH(1); // max head #
7271 break;
7273 case 3: // 720KB, 3.5"
7274 CX = 0x4f09; // 80 tracks, 9 sectors
7275 SET_DH(1); // max head #
7276 break;
7278 case 4: // 1.44MB, 3.5"
7279 CX = 0x4f12; // 80 tracks, 18 sectors
7280 SET_DH(1); // max head #
7281 break;
7283 case 5: // 2.88MB, 3.5"
7284 CX = 0x4f24; // 80 tracks, 36 sectors
7285 SET_DH(1); // max head #
7286 break;
7288 case 6: // 160k, 5.25"
7289 CX = 0x2708; // 40 tracks, 8 sectors
7290 SET_DH(0); // max head #
7291 break;
7293 case 7: // 180k, 5.25"
7294 CX = 0x2709; // 40 tracks, 9 sectors
7295 SET_DH(0); // max head #
7296 break;
7298 case 8: // 320k, 5.25"
7299 CX = 0x2708; // 40 tracks, 8 sectors
7300 SET_DH(1); // max head #
7301 break;
7303 default: // ?
7304 BX_PANIC("floppy: int13: bad floppy type\n");
7307 /* set es & di to point to 11 byte diskette param table in ROM */
7308 ASM_START
7309 push bp
7310 mov bp, sp
7311 mov ax, #diskette_param_table2
7312 mov _int13_diskette_function.DI+2[bp], ax
7313 mov _int13_diskette_function.ES+2[bp], cs
7314 pop bp
7315 ASM_END
7316 CLEAR_CF(); // success
7317 /* disk status not changed upon success */
7318 return;
7321 case 0x15: // read diskette drive type
7322 BX_DEBUG_INT13_FL("floppy f15\n");
7323 drive = GET_ELDL();
7324 if (drive > 1) {
7325 SET_AH(0); // only 2 drives supported
7326 // set_diskette_ret_status here ???
7327 SET_CF();
7328 return;
7330 drive_type = inb_cmos(0x10);
7332 if (drive == 0)
7333 drive_type >>= 4;
7334 else
7335 drive_type &= 0x0f;
7336 CLEAR_CF(); // successful, not present
7337 if (drive_type==0) {
7338 SET_AH(0); // drive not present
7340 else {
7341 SET_AH(1); // drive present, does not support change line
7344 return;
7346 case 0x16: // get diskette change line status
7347 BX_DEBUG_INT13_FL("floppy f16\n");
7348 drive = GET_ELDL();
7349 if (drive > 1) {
7350 SET_AH(0x01); // invalid drive
7351 set_diskette_ret_status(0x01);
7352 SET_CF();
7353 return;
7356 SET_AH(0x06); // change line not supported
7357 set_diskette_ret_status(0x06);
7358 SET_CF();
7359 return;
7361 case 0x17: // set diskette type for format(old)
7362 BX_DEBUG_INT13_FL("floppy f17\n");
7363 /* not used for 1.44M floppies */
7364 SET_AH(0x01); // not supported
7365 set_diskette_ret_status(1); /* not supported */
7366 SET_CF();
7367 return;
7369 case 0x18: // set diskette type for format(new)
7370 BX_DEBUG_INT13_FL("floppy f18\n");
7371 SET_AH(0x01); // do later
7372 set_diskette_ret_status(1);
7373 SET_CF();
7374 return;
7376 default:
7377 BX_INFO("int13_diskette: unsupported AH=%02x\n", GET_AH());
7379 // if ( (ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e) ) {
7380 SET_AH(0x01); // ???
7381 set_diskette_ret_status(1);
7382 SET_CF();
7383 return;
7384 // }
7387 #else // #if BX_SUPPORT_FLOPPY
7388 void
7389 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
7390 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
7392 Bit8u val8;
7394 switch ( GET_AH() ) {
7396 case 0x01: // Read Diskette Status
7397 CLEAR_CF();
7398 val8 = read_byte(0x0000, 0x0441);
7399 SET_AH(val8);
7400 if (val8) {
7401 SET_CF();
7403 return;
7405 default:
7406 SET_CF();
7407 write_byte(0x0000, 0x0441, 0x01);
7408 SET_AH(0x01);
7411 #endif // #if BX_SUPPORT_FLOPPY
7413 void
7414 set_diskette_ret_status(value)
7415 Bit8u value;
7417 write_byte(0x0040, 0x0041, value);
7420 void
7421 set_diskette_current_cyl(drive, cyl)
7422 Bit8u drive;
7423 Bit8u cyl;
7425 if (drive > 1)
7426 BX_PANIC("set_diskette_current_cyl(): drive > 1\n");
7427 write_byte(0x0040, 0x0094+drive, cyl);
7430 void
7431 determine_floppy_media(drive)
7432 Bit16u drive;
7434 #if 0
7435 Bit8u val8, DOR, ctrl_info;
7437 ctrl_info = read_byte(0x0040, 0x008F);
7438 if (drive==1)
7439 ctrl_info >>= 4;
7440 else
7441 ctrl_info &= 0x0f;
7443 #if 0
7444 if (drive == 0) {
7445 DOR = 0x1c; // DOR: drive0 motor on, DMA&int enabled, normal op, drive select 0
7447 else {
7448 DOR = 0x2d; // DOR: drive1 motor on, DMA&int enabled, normal op, drive select 1
7450 #endif
7452 if ( (ctrl_info & 0x04) != 0x04 ) {
7453 // Drive not determined means no drive exists, done.
7454 return;
7457 #if 0
7458 // check Main Status Register for readiness
7459 val8 = inb(0x03f4) & 0x80; // Main Status Register
7460 if (val8 != 0x80)
7461 BX_PANIC("d_f_m: MRQ bit not set\n");
7463 // change line
7465 // existing BDA values
7467 // turn on drive motor
7468 outb(0x03f2, DOR); // Digital Output Register
7470 #endif
7471 BX_PANIC("d_f_m: OK so far\n");
7472 #endif
7475 void
7476 int17_function(regs, ds, iret_addr)
7477 pusha_regs_t regs; // regs pushed from PUSHA instruction
7478 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7479 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7481 Bit16u addr,timeout;
7482 Bit8u val8;
7484 ASM_START
7486 ASM_END
7488 addr = read_word(0x0040, (regs.u.r16.dx << 1) + 8);
7489 if ((regs.u.r8.ah < 3) && (regs.u.r16.dx < 3) && (addr > 0)) {
7490 timeout = read_byte(0x0040, 0x0078 + regs.u.r16.dx) << 8;
7491 if (regs.u.r8.ah == 0) {
7492 outb(addr, regs.u.r8.al);
7493 val8 = inb(addr+2);
7494 outb(addr+2, val8 | 0x01); // send strobe
7495 ASM_START
7497 ASM_END
7498 outb(addr+2, val8 & ~0x01);
7499 while (((inb(addr+1) & 0x40) == 0x40) && (timeout)) {
7500 timeout--;
7503 if (regs.u.r8.ah == 1) {
7504 val8 = inb(addr+2);
7505 outb(addr+2, val8 & ~0x04); // send init
7506 ASM_START
7508 ASM_END
7509 outb(addr+2, val8 | 0x04);
7511 val8 = inb(addr+1);
7512 regs.u.r8.ah = (val8 ^ 0x48);
7513 if (!timeout) regs.u.r8.ah |= 0x01;
7514 ClearCF(iret_addr.flags);
7515 } else {
7516 SetCF(iret_addr.flags); // Unsupported
7520 // returns bootsegment in ax, drive in bl
7521 Bit32u
7522 int19_function(bseqnr)
7523 Bit8u bseqnr;
7525 Bit16u ebda_seg=read_word(0x0040,0x000E);
7526 Bit16u bootseq;
7527 Bit8u bootdrv;
7528 Bit8u bootcd;
7529 Bit8u bootchk;
7530 Bit16u bootseg;
7531 Bit16u status;
7532 Bit8u lastdrive=0;
7534 // if BX_ELTORITO_BOOT is not defined, old behavior
7535 // check bit 5 in CMOS reg 0x2d. load either 0x00 or 0x80 into DL
7536 // in preparation for the intial INT 13h (0=floppy A:, 0x80=C:)
7537 // 0: system boot sequence, first drive C: then A:
7538 // 1: system boot sequence, first drive A: then C:
7539 // else BX_ELTORITO_BOOT is defined
7540 // CMOS regs 0x3D and 0x38 contain the boot sequence:
7541 // CMOS reg 0x3D & 0x0f : 1st boot device
7542 // CMOS reg 0x3D & 0xf0 : 2nd boot device
7543 // CMOS reg 0x38 & 0xf0 : 3rd boot device
7544 // boot device codes:
7545 // 0x00 : not defined
7546 // 0x01 : first floppy
7547 // 0x02 : first harddrive
7548 // 0x03 : first cdrom
7549 // else : boot failure
7551 // Get the boot sequence
7552 #if BX_ELTORITO_BOOT
7553 bootseq=inb_cmos(0x3d);
7554 bootseq|=((inb_cmos(0x38) & 0xf0) << 4);
7556 if (bseqnr==2) bootseq >>= 4;
7557 if (bseqnr==3) bootseq >>= 8;
7558 if (bootseq<0x10) lastdrive = 1;
7559 bootdrv=0x00; bootcd=0;
7560 switch(bootseq & 0x0f) {
7561 case 0x01: bootdrv=0x00; bootcd=0; break;
7562 case 0x02: bootdrv=0x80; bootcd=0; break;
7563 case 0x03: bootdrv=0x00; bootcd=1; break;
7564 default: return 0x00000000;
7566 #else
7567 bootseq=inb_cmos(0x2d);
7569 if (bseqnr==2) {
7570 bootseq ^= 0x20;
7571 lastdrive = 1;
7573 bootdrv=0x00; bootcd=0;
7574 if((bootseq&0x20)==0) bootdrv=0x80;
7575 #endif // BX_ELTORITO_BOOT
7577 #if BX_ELTORITO_BOOT
7578 // We have to boot from cd
7579 if (bootcd != 0) {
7580 status = cdrom_boot();
7582 // If failure
7583 if ( (status & 0x00ff) !=0 ) {
7584 print_cdromboot_failure(status);
7585 print_boot_failure(bootcd, bootdrv, 1, lastdrive);
7586 return 0x00000000;
7589 bootseg = read_word(ebda_seg,&EbdaData->cdemu.load_segment);
7590 bootdrv = (Bit8u)(status>>8);
7593 #endif // BX_ELTORITO_BOOT
7595 // We have to boot from harddisk or floppy
7596 if (bootcd == 0) {
7597 bootseg=0x07c0;
7599 ASM_START
7600 push bp
7601 mov bp, sp
7603 mov ax, #0x0000
7604 mov _int19_function.status + 2[bp], ax
7605 mov dl, _int19_function.bootdrv + 2[bp]
7606 mov ax, _int19_function.bootseg + 2[bp]
7607 mov es, ax ;; segment
7608 mov bx, #0x0000 ;; offset
7609 mov ah, #0x02 ;; function 2, read diskette sector
7610 mov al, #0x01 ;; read 1 sector
7611 mov ch, #0x00 ;; track 0
7612 mov cl, #0x01 ;; sector 1
7613 mov dh, #0x00 ;; head 0
7614 int #0x13 ;; read sector
7615 jnc int19_load_done
7616 mov ax, #0x0001
7617 mov _int19_function.status + 2[bp], ax
7619 int19_load_done:
7620 pop bp
7621 ASM_END
7623 if (status != 0) {
7624 print_boot_failure(bootcd, bootdrv, 1, lastdrive);
7625 return 0x00000000;
7629 // check signature if instructed by cmos reg 0x38, only for floppy
7630 // bootchk = 1 : signature check disabled
7631 // bootchk = 0 : signature check enabled
7632 if (bootdrv != 0) bootchk = 0;
7633 else bootchk = inb_cmos(0x38) & 0x01;
7635 #if BX_ELTORITO_BOOT
7636 // if boot from cd, no signature check
7637 if (bootcd != 0)
7638 bootchk = 1;
7639 #endif // BX_ELTORITO_BOOT
7641 if (bootchk == 0) {
7642 if (read_word(bootseg,0x1fe) != 0xaa55) {
7643 print_boot_failure(bootcd, bootdrv, 0, lastdrive);
7644 return 0x00000000;
7648 #if BX_ELTORITO_BOOT
7649 // Print out the boot string
7650 print_boot_device(bootcd, bootdrv);
7651 #else // BX_ELTORITO_BOOT
7652 print_boot_device(0, bootdrv);
7653 #endif // BX_ELTORITO_BOOT
7655 // return the boot segment
7656 return (((Bit32u)bootdrv) << 16) + bootseg;
7659 void
7660 int1a_function(regs, ds, iret_addr)
7661 pusha_regs_t regs; // regs pushed from PUSHA instruction
7662 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7663 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7665 Bit8u val8;
7667 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);
7669 ASM_START
7671 ASM_END
7673 switch (regs.u.r8.ah) {
7674 case 0: // get current clock count
7675 ASM_START
7677 ASM_END
7678 regs.u.r16.cx = BiosData->ticks_high;
7679 regs.u.r16.dx = BiosData->ticks_low;
7680 regs.u.r8.al = BiosData->midnight_flag;
7681 BiosData->midnight_flag = 0; // reset flag
7682 ASM_START
7684 ASM_END
7685 // AH already 0
7686 ClearCF(iret_addr.flags); // OK
7687 break;
7689 case 1: // Set Current Clock Count
7690 ASM_START
7692 ASM_END
7693 BiosData->ticks_high = regs.u.r16.cx;
7694 BiosData->ticks_low = regs.u.r16.dx;
7695 BiosData->midnight_flag = 0; // reset flag
7696 ASM_START
7698 ASM_END
7699 regs.u.r8.ah = 0;
7700 ClearCF(iret_addr.flags); // OK
7701 break;
7704 case 2: // Read CMOS Time
7705 if (rtc_updating()) {
7706 SetCF(iret_addr.flags);
7707 break;
7710 regs.u.r8.dh = inb_cmos(0x00); // Seconds
7711 regs.u.r8.cl = inb_cmos(0x02); // Minutes
7712 regs.u.r8.ch = inb_cmos(0x04); // Hours
7713 regs.u.r8.dl = inb_cmos(0x0b) & 0x01; // Stat Reg B
7714 regs.u.r8.ah = 0;
7715 regs.u.r8.al = regs.u.r8.ch;
7716 ClearCF(iret_addr.flags); // OK
7717 break;
7719 case 3: // Set CMOS Time
7720 // Using a debugger, I notice the following masking/setting
7721 // of bits in Status Register B, by setting Reg B to
7722 // a few values and getting its value after INT 1A was called.
7724 // try#1 try#2 try#3
7725 // before 1111 1101 0111 1101 0000 0000
7726 // after 0110 0010 0110 0010 0000 0010
7728 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7729 // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
7730 if (rtc_updating()) {
7731 init_rtc();
7732 // fall through as if an update were not in progress
7734 outb_cmos(0x00, regs.u.r8.dh); // Seconds
7735 outb_cmos(0x02, regs.u.r8.cl); // Minutes
7736 outb_cmos(0x04, regs.u.r8.ch); // Hours
7737 // Set Daylight Savings time enabled bit to requested value
7738 val8 = (inb_cmos(0x0b) & 0x60) | 0x02 | (regs.u.r8.dl & 0x01);
7739 // (reg B already selected)
7740 outb_cmos(0x0b, val8);
7741 regs.u.r8.ah = 0;
7742 regs.u.r8.al = val8; // val last written to Reg B
7743 ClearCF(iret_addr.flags); // OK
7744 break;
7746 case 4: // Read CMOS Date
7747 regs.u.r8.ah = 0;
7748 if (rtc_updating()) {
7749 SetCF(iret_addr.flags);
7750 break;
7752 regs.u.r8.cl = inb_cmos(0x09); // Year
7753 regs.u.r8.dh = inb_cmos(0x08); // Month
7754 regs.u.r8.dl = inb_cmos(0x07); // Day of Month
7755 regs.u.r8.ch = inb_cmos(0x32); // Century
7756 regs.u.r8.al = regs.u.r8.ch;
7757 ClearCF(iret_addr.flags); // OK
7758 break;
7760 case 5: // Set CMOS Date
7761 // Using a debugger, I notice the following masking/setting
7762 // of bits in Status Register B, by setting Reg B to
7763 // a few values and getting its value after INT 1A was called.
7765 // try#1 try#2 try#3 try#4
7766 // before 1111 1101 0111 1101 0000 0010 0000 0000
7767 // after 0110 1101 0111 1101 0000 0010 0000 0000
7769 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7770 // My assumption: RegB = (RegB & 01111111b)
7771 if (rtc_updating()) {
7772 init_rtc();
7773 SetCF(iret_addr.flags);
7774 break;
7776 outb_cmos(0x09, regs.u.r8.cl); // Year
7777 outb_cmos(0x08, regs.u.r8.dh); // Month
7778 outb_cmos(0x07, regs.u.r8.dl); // Day of Month
7779 outb_cmos(0x32, regs.u.r8.ch); // Century
7780 val8 = inb_cmos(0x0b) & 0x7f; // clear halt-clock bit
7781 outb_cmos(0x0b, val8);
7782 regs.u.r8.ah = 0;
7783 regs.u.r8.al = val8; // AL = val last written to Reg B
7784 ClearCF(iret_addr.flags); // OK
7785 break;
7787 case 6: // Set Alarm Time in CMOS
7788 // Using a debugger, I notice the following masking/setting
7789 // of bits in Status Register B, by setting Reg B to
7790 // a few values and getting its value after INT 1A was called.
7792 // try#1 try#2 try#3
7793 // before 1101 1111 0101 1111 0000 0000
7794 // after 0110 1111 0111 1111 0010 0000
7796 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7797 // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
7798 val8 = inb_cmos(0x0b); // Get Status Reg B
7799 regs.u.r16.ax = 0;
7800 if (val8 & 0x20) {
7801 // Alarm interrupt enabled already
7802 SetCF(iret_addr.flags); // Error: alarm in use
7803 break;
7805 if (rtc_updating()) {
7806 init_rtc();
7807 // fall through as if an update were not in progress
7809 outb_cmos(0x01, regs.u.r8.dh); // Seconds alarm
7810 outb_cmos(0x03, regs.u.r8.cl); // Minutes alarm
7811 outb_cmos(0x05, regs.u.r8.ch); // Hours alarm
7812 outb(0xa1, inb(0xa1) & 0xfe); // enable IRQ 8
7813 // enable Status Reg B alarm bit, clear halt clock bit
7814 outb_cmos(0x0b, (val8 & 0x7f) | 0x20);
7815 ClearCF(iret_addr.flags); // OK
7816 break;
7818 case 7: // Turn off Alarm
7819 // Using a debugger, I notice the following masking/setting
7820 // of bits in Status Register B, by setting Reg B to
7821 // a few values and getting its value after INT 1A was called.
7823 // try#1 try#2 try#3 try#4
7824 // before 1111 1101 0111 1101 0010 0000 0010 0010
7825 // after 0100 0101 0101 0101 0000 0000 0000 0010
7827 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7828 // My assumption: RegB = (RegB & 01010111b)
7829 val8 = inb_cmos(0x0b); // Get Status Reg B
7830 // clear clock-halt bit, disable alarm bit
7831 outb_cmos(0x0b, val8 & 0x57); // disable alarm bit
7832 regs.u.r8.ah = 0;
7833 regs.u.r8.al = val8; // val last written to Reg B
7834 ClearCF(iret_addr.flags); // OK
7835 break;
7836 #if BX_PCIBIOS
7837 case 0xb1:
7838 // real mode PCI BIOS functions now handled in assembler code
7839 // this C code handles the error code for information only
7840 if (regs.u.r8.bl == 0xff) {
7841 BX_INFO("PCI BIOS: PCI not present\n");
7842 } else if (regs.u.r8.bl == 0x81) {
7843 BX_INFO("unsupported PCI BIOS function 0x%02x\n", regs.u.r8.al);
7844 } else if (regs.u.r8.bl == 0x83) {
7845 BX_INFO("bad PCI vendor ID %04x\n", regs.u.r16.dx);
7846 } else if (regs.u.r8.bl == 0x86) {
7847 BX_INFO("PCI device %04x:%04x not found at index %d\n", regs.u.r16.dx, regs.u.r16.cx, regs.u.r16.si);
7849 regs.u.r8.ah = regs.u.r8.bl;
7850 SetCF(iret_addr.flags);
7851 break;
7852 #endif
7854 default:
7855 SetCF(iret_addr.flags); // Unsupported
7859 void
7860 int70_function(regs, ds, iret_addr)
7861 pusha_regs_t regs; // regs pushed from PUSHA instruction
7862 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7863 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7865 // INT 70h: IRQ 8 - CMOS RTC interrupt from periodic or alarm modes
7866 Bit8u registerB = 0, registerC = 0;
7868 // Check which modes are enabled and have occurred.
7869 registerB = inb_cmos( 0xB );
7870 registerC = inb_cmos( 0xC );
7872 if( ( registerB & 0x60 ) != 0 ) {
7873 if( ( registerC & 0x20 ) != 0 ) {
7874 // Handle Alarm Interrupt.
7875 ASM_START
7877 int #0x4a
7879 ASM_END
7881 if( ( registerC & 0x40 ) != 0 ) {
7882 // Handle Periodic Interrupt.
7884 if( read_byte( 0x40, 0xA0 ) != 0 ) {
7885 // Wait Interval (Int 15, AH=83) active.
7886 Bit32u time, toggle;
7888 time = read_dword( 0x40, 0x9C ); // Time left in microseconds.
7889 if( time < 0x3D1 ) {
7890 // Done waiting.
7891 Bit16u segment, offset;
7893 segment = read_word( 0x40, 0x98 );
7894 offset = read_word( 0x40, 0x9A );
7895 write_byte( 0x40, 0xA0, 0 ); // Turn of status byte.
7896 outb_cmos( 0xB, registerB & 0x37 ); // Clear the Periodic Interrupt.
7897 write_byte(segment, offset, read_byte(segment, offset) | 0x80 ); // Write to specified flag byte.
7898 } else {
7899 // Continue waiting.
7900 time -= 0x3D1;
7901 write_dword( 0x40, 0x9C, time );
7907 ASM_START
7908 call eoi_both_pics
7909 ASM_END
7913 ASM_START
7914 ;------------------------------------------
7915 ;- INT74h : PS/2 mouse hardware interrupt -
7916 ;------------------------------------------
7917 int74_handler:
7919 pusha
7920 push ds ;; save DS
7921 push #0x00 ;; placeholder for status
7922 push #0x00 ;; placeholder for X
7923 push #0x00 ;; placeholder for Y
7924 push #0x00 ;; placeholder for Z
7925 push #0x00 ;; placeholder for make_far_call boolean
7926 call _int74_function
7927 pop cx ;; remove make_far_call from stack
7928 jcxz int74_done
7930 ;; make far call to EBDA:0022
7931 push #0x00
7932 pop ds
7933 push 0x040E ;; push 0000:040E (opcodes 0xff, 0x36, 0x0E, 0x04)
7934 pop ds
7935 //CALL_EP(0x0022) ;; call far routine (call_Ep DS:0022 :opcodes 0xff, 0x1e, 0x22, 0x00)
7936 call far ptr[0x22]
7937 int74_done:
7939 call eoi_both_pics
7940 add sp, #8 ;; pop status, x, y, z
7942 pop ds ;; restore DS
7943 popa
7944 iret
7947 ;; This will perform an IRET, but will retain value of current CF
7948 ;; by altering flags on stack. Better than RETF #02.
7949 iret_modify_cf:
7950 jc carry_set
7951 push bp
7952 mov bp, sp
7953 and BYTE [bp + 0x06], #0xfe
7954 pop bp
7955 iret
7956 carry_set:
7957 push bp
7958 mov bp, sp
7959 or BYTE [bp + 0x06], #0x01
7960 pop bp
7961 iret
7964 ;----------------------
7965 ;- INT13h (relocated) -
7966 ;----------------------
7968 ; int13_relocated is a little bit messed up since I played with it
7969 ; I have to rewrite it:
7970 ; - call a function that detect which function to call
7971 ; - make all called C function get the same parameters list
7973 int13_relocated:
7975 #if BX_ELTORITO_BOOT
7976 ;; check for an eltorito function
7977 cmp ah,#0x4a
7978 jb int13_not_eltorito
7979 cmp ah,#0x4d
7980 ja int13_not_eltorito
7982 pusha
7983 push es
7984 push ds
7985 push ss
7986 pop ds
7988 push #int13_out
7989 jmp _int13_eltorito ;; ELDX not used
7991 int13_not_eltorito:
7992 push ax
7993 push bx
7994 push cx
7995 push dx
7997 ;; check if emulation active
7998 call _cdemu_isactive
7999 cmp al,#0x00
8000 je int13_cdemu_inactive
8002 ;; check if access to the emulated drive
8003 call _cdemu_emulated_drive
8004 pop dx
8005 push dx
8006 cmp al,dl ;; int13 on emulated drive
8007 jne int13_nocdemu
8009 pop dx
8010 pop cx
8011 pop bx
8012 pop ax
8014 pusha
8015 push es
8016 push ds
8017 push ss
8018 pop ds
8020 push #int13_out
8021 jmp _int13_cdemu ;; ELDX not used
8023 int13_nocdemu:
8024 and dl,#0xE0 ;; mask to get device class, including cdroms
8025 cmp al,dl ;; al is 0x00 or 0x80
8026 jne int13_cdemu_inactive ;; inactive for device class
8028 pop dx
8029 pop cx
8030 pop bx
8031 pop ax
8033 push ax
8034 push cx
8035 push dx
8036 push bx
8038 dec dl ;; real drive is dl - 1
8039 jmp int13_legacy
8041 int13_cdemu_inactive:
8042 pop dx
8043 pop cx
8044 pop bx
8045 pop ax
8047 #endif // BX_ELTORITO_BOOT
8049 int13_noeltorito:
8051 push ax
8052 push cx
8053 push dx
8054 push bx
8056 int13_legacy:
8058 push dx ;; push eltorito value of dx instead of sp
8060 push bp
8061 push si
8062 push di
8064 push es
8065 push ds
8066 push ss
8067 pop ds
8069 ;; now the 16-bit registers can be restored with:
8070 ;; pop ds; pop es; popa; iret
8071 ;; arguments passed to functions should be
8072 ;; DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS
8074 test dl, #0x80
8075 jnz int13_notfloppy
8077 push #int13_out
8078 jmp _int13_diskette_function
8080 int13_notfloppy:
8082 #if BX_USE_ATADRV
8084 cmp dl, #0xE0
8085 jb int13_notcdrom
8087 // ebx is modified: BSD 5.2.1 boot loader problem
8088 // someone should figure out which 32 bit register that actually are used
8090 shr ebx, #16
8091 push bx
8093 call _int13_cdrom
8095 pop bx
8096 shl ebx, #16
8098 jmp int13_out
8100 int13_notcdrom:
8102 #endif
8104 int13_disk:
8105 call _int13_harddisk
8107 int13_out:
8108 pop ds
8109 pop es
8110 popa
8111 iret
8114 ;----------
8115 ;- INT18h -
8116 ;----------
8117 int18_handler: ;; Boot Failure routing
8118 call _int18_panic_msg
8120 iret
8122 ;----------
8123 ;- INT19h -
8124 ;----------
8125 int19_relocated: ;; Boot function, relocated
8127 ;; int19 was beginning to be really complex, so now it
8128 ;; just calls an C function, that does the work
8129 ;; it returns in BL the boot drive, and in AX the boot segment
8130 ;; the boot segment will be 0x0000 if something has failed
8132 push bp
8133 mov bp, sp
8135 ;; drop ds
8136 xor ax, ax
8137 mov ds, ax
8139 ;; 1st boot device
8140 mov ax, #0x0001
8141 push ax
8142 call _int19_function
8143 inc sp
8144 inc sp
8145 ;; bl contains the boot drive
8146 ;; ax contains the boot segment or 0 if failure
8148 test ax, ax ;; if ax is 0 try next boot device
8149 jnz boot_setup
8151 ;; 2nd boot device
8152 mov ax, #0x0002
8153 push ax
8154 call _int19_function
8155 inc sp
8156 inc sp
8157 test ax, ax ;; if ax is 0 try next boot device
8158 jnz boot_setup
8160 ;; 3rd boot device
8161 mov ax, #0x0003
8162 push ax
8163 call _int19_function
8164 inc sp
8165 inc sp
8166 test ax, ax ;; if ax is 0 call int18
8167 jz int18_handler
8169 boot_setup:
8170 mov dl, bl ;; set drive so guest os find it
8171 shl eax, #0x04 ;; convert seg to ip
8172 mov 2[bp], ax ;; set ip
8174 shr eax, #0x04 ;; get cs back
8175 and ax, #0xF000 ;; remove what went in ip
8176 mov 4[bp], ax ;; set cs
8177 xor ax, ax
8178 mov es, ax ;; set es to zero fixes [ 549815 ]
8179 mov [bp], ax ;; set bp to zero
8180 mov ax, #0xaa55 ;; set ok flag
8182 pop bp
8183 iret ;; Beam me up Scotty
8185 ;----------
8186 ;- INT1Ch -
8187 ;----------
8188 int1c_handler: ;; User Timer Tick
8189 iret
8192 ;----------------------
8193 ;- POST: Floppy Drive -
8194 ;----------------------
8195 floppy_drive_post:
8196 mov ax, #0x0000
8197 mov ds, ax
8199 mov al, #0x00
8200 mov 0x043e, al ;; drive 0 & 1 uncalibrated, no interrupt has occurred
8202 mov 0x043f, al ;; diskette motor status: read op, drive0, motors off
8204 mov 0x0440, al ;; diskette motor timeout counter: not active
8205 mov 0x0441, al ;; diskette controller status return code
8207 mov 0x0442, al ;; disk & diskette controller status register 0
8208 mov 0x0443, al ;; diskette controller status register 1
8209 mov 0x0444, al ;; diskette controller status register 2
8210 mov 0x0445, al ;; diskette controller cylinder number
8211 mov 0x0446, al ;; diskette controller head number
8212 mov 0x0447, al ;; diskette controller sector number
8213 mov 0x0448, al ;; diskette controller bytes written
8215 mov 0x048b, al ;; diskette configuration data
8217 ;; -----------------------------------------------------------------
8218 ;; (048F) diskette controller information
8220 mov al, #0x10 ;; get CMOS diskette drive type
8221 out 0x70, AL
8222 in AL, 0x71
8223 mov ah, al ;; save byte to AH
8225 look_drive0:
8226 shr al, #4 ;; look at top 4 bits for drive 0
8227 jz f0_missing ;; jump if no drive0
8228 mov bl, #0x07 ;; drive0 determined, multi-rate, has changed line
8229 jmp look_drive1
8230 f0_missing:
8231 mov bl, #0x00 ;; no drive0
8233 look_drive1:
8234 mov al, ah ;; restore from AH
8235 and al, #0x0f ;; look at bottom 4 bits for drive 1
8236 jz f1_missing ;; jump if no drive1
8237 or bl, #0x70 ;; drive1 determined, multi-rate, has changed line
8238 f1_missing:
8239 ;; leave high bits in BL zerod
8240 mov 0x048f, bl ;; put new val in BDA (diskette controller information)
8241 ;; -----------------------------------------------------------------
8243 mov al, #0x00
8244 mov 0x0490, al ;; diskette 0 media state
8245 mov 0x0491, al ;; diskette 1 media state
8247 ;; diskette 0,1 operational starting state
8248 ;; drive type has not been determined,
8249 ;; has no changed detection line
8250 mov 0x0492, al
8251 mov 0x0493, al
8253 mov 0x0494, al ;; diskette 0 current cylinder
8254 mov 0x0495, al ;; diskette 1 current cylinder
8256 mov al, #0x02
8257 out #0x0a, al ;; clear DMA-1 channel 2 mask bit
8259 SET_INT_VECTOR(0x1E, #0xF000, #diskette_param_table2)
8260 SET_INT_VECTOR(0x40, #0xF000, #int13_diskette)
8261 SET_INT_VECTOR(0x0E, #0xF000, #int0e_handler) ;; IRQ 6
8266 ;--------------------
8267 ;- POST: HARD DRIVE -
8268 ;--------------------
8269 ; relocated here because the primary POST area isnt big enough.
8270 hard_drive_post:
8271 // IRQ 14 = INT 76h
8272 // INT 76h calls INT 15h function ax=9100
8274 mov al, #0x0a ; 0000 1010 = reserved, disable IRQ 14
8275 mov dx, #0x03f6
8276 out dx, al
8278 mov ax, #0x0000
8279 mov ds, ax
8280 mov 0x0474, al /* hard disk status of last operation */
8281 mov 0x0477, al /* hard disk port offset (XT only ???) */
8282 mov 0x048c, al /* hard disk status register */
8283 mov 0x048d, al /* hard disk error register */
8284 mov 0x048e, al /* hard disk task complete flag */
8285 mov al, #0x01
8286 mov 0x0475, al /* hard disk number attached */
8287 mov al, #0xc0
8288 mov 0x0476, al /* hard disk control byte */
8289 SET_INT_VECTOR(0x13, #0xF000, #int13_handler)
8290 SET_INT_VECTOR(0x76, #0xF000, #int76_handler)
8291 ;; INT 41h: hard disk 0 configuration pointer
8292 ;; INT 46h: hard disk 1 configuration pointer
8293 SET_INT_VECTOR(0x41, #EBDA_SEG, #0x003D)
8294 SET_INT_VECTOR(0x46, #EBDA_SEG, #0x004D)
8296 ;; move disk geometry data from CMOS to EBDA disk parameter table(s)
8297 mov al, #0x12
8298 out #0x70, al
8299 in al, #0x71
8300 and al, #0xf0
8301 cmp al, #0xf0
8302 je post_d0_extended
8303 jmp check_for_hd1
8304 post_d0_extended:
8305 mov al, #0x19
8306 out #0x70, al
8307 in al, #0x71
8308 cmp al, #47 ;; decimal 47 - user definable
8309 je post_d0_type47
8310 HALT(__LINE__)
8311 post_d0_type47:
8312 ;; CMOS purpose param table offset
8313 ;; 1b cylinders low 0
8314 ;; 1c cylinders high 1
8315 ;; 1d heads 2
8316 ;; 1e write pre-comp low 5
8317 ;; 1f write pre-comp high 6
8318 ;; 20 retries/bad map/heads>8 8
8319 ;; 21 landing zone low C
8320 ;; 22 landing zone high D
8321 ;; 23 sectors/track E
8323 mov ax, #EBDA_SEG
8324 mov ds, ax
8326 ;;; Filling EBDA table for hard disk 0.
8327 mov al, #0x1f
8328 out #0x70, al
8329 in al, #0x71
8330 mov ah, al
8331 mov al, #0x1e
8332 out #0x70, al
8333 in al, #0x71
8334 mov (0x003d + 0x05), ax ;; write precomp word
8336 mov al, #0x20
8337 out #0x70, al
8338 in al, #0x71
8339 mov (0x003d + 0x08), al ;; drive control byte
8341 mov al, #0x22
8342 out #0x70, al
8343 in al, #0x71
8344 mov ah, al
8345 mov al, #0x21
8346 out #0x70, al
8347 in al, #0x71
8348 mov (0x003d + 0x0C), ax ;; landing zone word
8350 mov al, #0x1c ;; get cylinders word in AX
8351 out #0x70, al
8352 in al, #0x71 ;; high byte
8353 mov ah, al
8354 mov al, #0x1b
8355 out #0x70, al
8356 in al, #0x71 ;; low byte
8357 mov bx, ax ;; BX = cylinders
8359 mov al, #0x1d
8360 out #0x70, al
8361 in al, #0x71
8362 mov cl, al ;; CL = heads
8364 mov al, #0x23
8365 out #0x70, al
8366 in al, #0x71
8367 mov dl, al ;; DL = sectors
8369 cmp bx, #1024
8370 jnbe hd0_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8372 hd0_post_physical_chs:
8373 ;; no logical CHS mapping used, just physical CHS
8374 ;; use Standard Fixed Disk Parameter Table (FDPT)
8375 mov (0x003d + 0x00), bx ;; number of physical cylinders
8376 mov (0x003d + 0x02), cl ;; number of physical heads
8377 mov (0x003d + 0x0E), dl ;; number of physical sectors
8378 jmp check_for_hd1
8380 hd0_post_logical_chs:
8381 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8382 mov (0x003d + 0x09), bx ;; number of physical cylinders
8383 mov (0x003d + 0x0b), cl ;; number of physical heads
8384 mov (0x003d + 0x04), dl ;; number of physical sectors
8385 mov (0x003d + 0x0e), dl ;; number of logical sectors (same)
8386 mov al, #0xa0
8387 mov (0x003d + 0x03), al ;; A0h signature, indicates translated table
8389 cmp bx, #2048
8390 jnbe hd0_post_above_2048
8391 ;; 1024 < c <= 2048 cylinders
8392 shr bx, #0x01
8393 shl cl, #0x01
8394 jmp hd0_post_store_logical
8396 hd0_post_above_2048:
8397 cmp bx, #4096
8398 jnbe hd0_post_above_4096
8399 ;; 2048 < c <= 4096 cylinders
8400 shr bx, #0x02
8401 shl cl, #0x02
8402 jmp hd0_post_store_logical
8404 hd0_post_above_4096:
8405 cmp bx, #8192
8406 jnbe hd0_post_above_8192
8407 ;; 4096 < c <= 8192 cylinders
8408 shr bx, #0x03
8409 shl cl, #0x03
8410 jmp hd0_post_store_logical
8412 hd0_post_above_8192:
8413 ;; 8192 < c <= 16384 cylinders
8414 shr bx, #0x04
8415 shl cl, #0x04
8417 hd0_post_store_logical:
8418 mov (0x003d + 0x00), bx ;; number of physical cylinders
8419 mov (0x003d + 0x02), cl ;; number of physical heads
8420 ;; checksum
8421 mov cl, #0x0f ;; repeat count
8422 mov si, #0x003d ;; offset to disk0 FDPT
8423 mov al, #0x00 ;; sum
8424 hd0_post_checksum_loop:
8425 add al, [si]
8426 inc si
8427 dec cl
8428 jnz hd0_post_checksum_loop
8429 not al ;; now take 2s complement
8430 inc al
8431 mov [si], al
8432 ;;; Done filling EBDA table for hard disk 0.
8435 check_for_hd1:
8436 ;; is there really a second hard disk? if not, return now
8437 mov al, #0x12
8438 out #0x70, al
8439 in al, #0x71
8440 and al, #0x0f
8441 jnz post_d1_exists
8443 post_d1_exists:
8444 ;; check that the hd type is really 0x0f.
8445 cmp al, #0x0f
8446 jz post_d1_extended
8447 HALT(__LINE__)
8448 post_d1_extended:
8449 ;; check that the extended type is 47 - user definable
8450 mov al, #0x1a
8451 out #0x70, al
8452 in al, #0x71
8453 cmp al, #47 ;; decimal 47 - user definable
8454 je post_d1_type47
8455 HALT(__LINE__)
8456 post_d1_type47:
8457 ;; Table for disk1.
8458 ;; CMOS purpose param table offset
8459 ;; 0x24 cylinders low 0
8460 ;; 0x25 cylinders high 1
8461 ;; 0x26 heads 2
8462 ;; 0x27 write pre-comp low 5
8463 ;; 0x28 write pre-comp high 6
8464 ;; 0x29 heads>8 8
8465 ;; 0x2a landing zone low C
8466 ;; 0x2b landing zone high D
8467 ;; 0x2c sectors/track E
8468 ;;; Fill EBDA table for hard disk 1.
8469 mov ax, #EBDA_SEG
8470 mov ds, ax
8471 mov al, #0x28
8472 out #0x70, al
8473 in al, #0x71
8474 mov ah, al
8475 mov al, #0x27
8476 out #0x70, al
8477 in al, #0x71
8478 mov (0x004d + 0x05), ax ;; write precomp word
8480 mov al, #0x29
8481 out #0x70, al
8482 in al, #0x71
8483 mov (0x004d + 0x08), al ;; drive control byte
8485 mov al, #0x2b
8486 out #0x70, al
8487 in al, #0x71
8488 mov ah, al
8489 mov al, #0x2a
8490 out #0x70, al
8491 in al, #0x71
8492 mov (0x004d + 0x0C), ax ;; landing zone word
8494 mov al, #0x25 ;; get cylinders word in AX
8495 out #0x70, al
8496 in al, #0x71 ;; high byte
8497 mov ah, al
8498 mov al, #0x24
8499 out #0x70, al
8500 in al, #0x71 ;; low byte
8501 mov bx, ax ;; BX = cylinders
8503 mov al, #0x26
8504 out #0x70, al
8505 in al, #0x71
8506 mov cl, al ;; CL = heads
8508 mov al, #0x2c
8509 out #0x70, al
8510 in al, #0x71
8511 mov dl, al ;; DL = sectors
8513 cmp bx, #1024
8514 jnbe hd1_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8516 hd1_post_physical_chs:
8517 ;; no logical CHS mapping used, just physical CHS
8518 ;; use Standard Fixed Disk Parameter Table (FDPT)
8519 mov (0x004d + 0x00), bx ;; number of physical cylinders
8520 mov (0x004d + 0x02), cl ;; number of physical heads
8521 mov (0x004d + 0x0E), dl ;; number of physical sectors
8524 hd1_post_logical_chs:
8525 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8526 mov (0x004d + 0x09), bx ;; number of physical cylinders
8527 mov (0x004d + 0x0b), cl ;; number of physical heads
8528 mov (0x004d + 0x04), dl ;; number of physical sectors
8529 mov (0x004d + 0x0e), dl ;; number of logical sectors (same)
8530 mov al, #0xa0
8531 mov (0x004d + 0x03), al ;; A0h signature, indicates translated table
8533 cmp bx, #2048
8534 jnbe hd1_post_above_2048
8535 ;; 1024 < c <= 2048 cylinders
8536 shr bx, #0x01
8537 shl cl, #0x01
8538 jmp hd1_post_store_logical
8540 hd1_post_above_2048:
8541 cmp bx, #4096
8542 jnbe hd1_post_above_4096
8543 ;; 2048 < c <= 4096 cylinders
8544 shr bx, #0x02
8545 shl cl, #0x02
8546 jmp hd1_post_store_logical
8548 hd1_post_above_4096:
8549 cmp bx, #8192
8550 jnbe hd1_post_above_8192
8551 ;; 4096 < c <= 8192 cylinders
8552 shr bx, #0x03
8553 shl cl, #0x03
8554 jmp hd1_post_store_logical
8556 hd1_post_above_8192:
8557 ;; 8192 < c <= 16384 cylinders
8558 shr bx, #0x04
8559 shl cl, #0x04
8561 hd1_post_store_logical:
8562 mov (0x004d + 0x00), bx ;; number of physical cylinders
8563 mov (0x004d + 0x02), cl ;; number of physical heads
8564 ;; checksum
8565 mov cl, #0x0f ;; repeat count
8566 mov si, #0x004d ;; offset to disk0 FDPT
8567 mov al, #0x00 ;; sum
8568 hd1_post_checksum_loop:
8569 add al, [si]
8570 inc si
8571 dec cl
8572 jnz hd1_post_checksum_loop
8573 not al ;; now take 2s complement
8574 inc al
8575 mov [si], al
8576 ;;; Done filling EBDA table for hard disk 1.
8580 ;--------------------
8581 ;- POST: EBDA segment
8582 ;--------------------
8583 ; relocated here because the primary POST area isnt big enough.
8584 ebda_post:
8585 #if BX_USE_EBDA
8586 mov ax, #EBDA_SEG
8587 mov ds, ax
8588 mov byte ptr [0x0], #EBDA_SIZE
8589 #endif
8590 xor ax, ax ; mov EBDA seg into 40E
8591 mov ds, ax
8592 mov word ptr [0x40E], #EBDA_SEG
8593 ret;;
8595 ;--------------------
8596 ;- POST: EOI + jmp via [0x40:67)
8597 ;--------------------
8598 ; relocated here because the primary POST area isnt big enough.
8599 eoi_jmp_post:
8600 call eoi_both_pics
8602 xor ax, ax
8603 mov ds, ax
8605 jmp far ptr [0x467]
8608 ;--------------------
8609 eoi_both_pics:
8610 mov al, #0x20
8611 out #0xA0, al ;; slave PIC EOI
8612 eoi_master_pic:
8613 mov al, #0x20
8614 out #0x20, al ;; master PIC EOI
8617 ;--------------------
8618 BcdToBin:
8619 ;; in: AL in BCD format
8620 ;; out: AL in binary format, AH will always be 0
8621 ;; trashes BX
8622 mov bl, al
8623 and bl, #0x0f ;; bl has low digit
8624 shr al, #4 ;; al has high digit
8625 mov bh, #10
8626 mul al, bh ;; multiply high digit by 10 (result in AX)
8627 add al, bl ;; then add low digit
8630 ;--------------------
8631 timer_tick_post:
8632 ;; Setup the Timer Ticks Count (0x46C:dword) and
8633 ;; Timer Ticks Roller Flag (0x470:byte)
8634 ;; The Timer Ticks Count needs to be set according to
8635 ;; the current CMOS time, as if ticks have been occurring
8636 ;; at 18.2hz since midnight up to this point. Calculating
8637 ;; this is a little complicated. Here are the factors I gather
8638 ;; regarding this. 14,318,180 hz was the original clock speed,
8639 ;; chosen so it could be divided by either 3 to drive the 5Mhz CPU
8640 ;; at the time, or 4 to drive the CGA video adapter. The div3
8641 ;; source was divided again by 4 to feed a 1.193Mhz signal to
8642 ;; the timer. With a maximum 16bit timer count, this is again
8643 ;; divided down by 65536 to 18.2hz.
8645 ;; 14,318,180 Hz clock
8646 ;; /3 = 4,772,726 Hz fed to orginal 5Mhz CPU
8647 ;; /4 = 1,193,181 Hz fed to timer
8648 ;; /65536 (maximum timer count) = 18.20650736 ticks/second
8649 ;; 1 second = 18.20650736 ticks
8650 ;; 1 minute = 1092.390442 ticks
8651 ;; 1 hour = 65543.42651 ticks
8653 ;; Given the values in the CMOS clock, one could calculate
8654 ;; the number of ticks by the following:
8655 ;; ticks = (BcdToBin(seconds) * 18.206507) +
8656 ;; (BcdToBin(minutes) * 1092.3904)
8657 ;; (BcdToBin(hours) * 65543.427)
8658 ;; To get a little more accuracy, since Im using integer
8659 ;; arithmatic, I use:
8660 ;; ticks = (BcdToBin(seconds) * 18206507) / 1000000 +
8661 ;; (BcdToBin(minutes) * 10923904) / 10000 +
8662 ;; (BcdToBin(hours) * 65543427) / 1000
8664 ;; assuming DS=0000
8666 ;; get CMOS seconds
8667 xor eax, eax ;; clear EAX
8668 mov al, #0x00
8669 out #0x70, al
8670 in al, #0x71 ;; AL has CMOS seconds in BCD
8671 call BcdToBin ;; EAX now has seconds in binary
8672 mov edx, #18206507
8673 mul eax, edx
8674 mov ebx, #1000000
8675 xor edx, edx
8676 div eax, ebx
8677 mov ecx, eax ;; ECX will accumulate total ticks
8679 ;; get CMOS minutes
8680 xor eax, eax ;; clear EAX
8681 mov al, #0x02
8682 out #0x70, al
8683 in al, #0x71 ;; AL has CMOS minutes in BCD
8684 call BcdToBin ;; EAX now has minutes in binary
8685 mov edx, #10923904
8686 mul eax, edx
8687 mov ebx, #10000
8688 xor edx, edx
8689 div eax, ebx
8690 add ecx, eax ;; add to total ticks
8692 ;; get CMOS hours
8693 xor eax, eax ;; clear EAX
8694 mov al, #0x04
8695 out #0x70, al
8696 in al, #0x71 ;; AL has CMOS hours in BCD
8697 call BcdToBin ;; EAX now has hours in binary
8698 mov edx, #65543427
8699 mul eax, edx
8700 mov ebx, #1000
8701 xor edx, edx
8702 div eax, ebx
8703 add ecx, eax ;; add to total ticks
8705 mov 0x46C, ecx ;; Timer Ticks Count
8706 xor al, al
8707 mov 0x470, al ;; Timer Ticks Rollover Flag
8710 ;--------------------
8711 int76_handler:
8712 ;; record completion in BIOS task complete flag
8713 push ax
8714 push ds
8715 mov ax, #0x0040
8716 mov ds, ax
8717 mov 0x008E, #0xff
8718 call eoi_both_pics
8719 pop ds
8720 pop ax
8721 iret
8724 ;--------------------
8725 #if BX_APM
8727 use32 386
8728 #define APM_PROT32
8729 #include "apmbios.S"
8731 use16 386
8732 #define APM_PROT16
8733 #include "apmbios.S"
8735 #define APM_REAL
8736 #include "apmbios.S"
8738 #endif
8740 ;--------------------
8741 #if BX_PCIBIOS
8742 use32 386
8743 .align 16
8744 bios32_structure:
8745 db 0x5f, 0x33, 0x32, 0x5f ;; "_32_" signature
8746 dw bios32_entry_point, 0xf ;; 32 bit physical address
8747 db 0 ;; revision level
8748 ;; length in paragraphs and checksum stored in a word to prevent errors
8749 dw (~(((bios32_entry_point >> 8) + (bios32_entry_point & 0xff) + 0x32) \
8750 & 0xff) << 8) + 0x01
8751 db 0,0,0,0,0 ;; reserved
8753 .align 16
8754 bios32_entry_point:
8755 pushf
8756 cmp eax, #0x49435024 ;; "$PCI"
8757 jne unknown_service
8758 mov eax, #0x80000000
8759 mov dx, #0x0cf8
8760 out dx, eax
8761 mov dx, #0x0cfc
8762 in eax, dx
8763 #ifdef PCI_FIXED_HOST_BRIDGE
8764 cmp eax, #PCI_FIXED_HOST_BRIDGE
8765 jne unknown_service
8766 #else
8767 ;; say ok if a device is present
8768 cmp eax, #0xffffffff
8769 je unknown_service
8770 #endif
8771 mov ebx, #0x000f0000
8772 mov ecx, #0
8773 mov edx, #pcibios_protected
8774 xor al, al
8775 jmp bios32_end
8776 unknown_service:
8777 mov al, #0x80
8778 bios32_end:
8779 #ifdef BX_QEMU
8780 and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
8781 #endif
8782 popf
8783 retf
8785 .align 16
8786 pcibios_protected:
8787 pushf
8789 push esi
8790 push edi
8791 cmp al, #0x01 ;; installation check
8792 jne pci_pro_f02
8793 mov bx, #0x0210
8794 mov cx, #0
8795 mov edx, #0x20494350 ;; "PCI "
8796 mov al, #0x01
8797 jmp pci_pro_ok
8798 pci_pro_f02: ;; find pci device
8799 cmp al, #0x02
8800 jne pci_pro_f08
8801 shl ecx, #16
8802 mov cx, dx
8803 mov bx, #0x0000
8804 mov di, #0x00
8805 pci_pro_devloop:
8806 call pci_pro_select_reg
8807 mov dx, #0x0cfc
8808 in eax, dx
8809 cmp eax, ecx
8810 jne pci_pro_nextdev
8811 cmp si, #0
8812 je pci_pro_ok
8813 dec si
8814 pci_pro_nextdev:
8815 inc bx
8816 cmp bx, #0x0100
8817 jne pci_pro_devloop
8818 mov ah, #0x86
8819 jmp pci_pro_fail
8820 pci_pro_f08: ;; read configuration byte
8821 cmp al, #0x08
8822 jne pci_pro_f09
8823 call pci_pro_select_reg
8824 push edx
8825 mov dx, di
8826 and dx, #0x03
8827 add dx, #0x0cfc
8828 in al, dx
8829 pop edx
8830 mov cl, al
8831 jmp pci_pro_ok
8832 pci_pro_f09: ;; read configuration word
8833 cmp al, #0x09
8834 jne pci_pro_f0a
8835 call pci_pro_select_reg
8836 push edx
8837 mov dx, di
8838 and dx, #0x02
8839 add dx, #0x0cfc
8840 in ax, dx
8841 pop edx
8842 mov cx, ax
8843 jmp pci_pro_ok
8844 pci_pro_f0a: ;; read configuration dword
8845 cmp al, #0x0a
8846 jne pci_pro_f0b
8847 call pci_pro_select_reg
8848 push edx
8849 mov dx, #0x0cfc
8850 in eax, dx
8851 pop edx
8852 mov ecx, eax
8853 jmp pci_pro_ok
8854 pci_pro_f0b: ;; write configuration byte
8855 cmp al, #0x0b
8856 jne pci_pro_f0c
8857 call pci_pro_select_reg
8858 push edx
8859 mov dx, di
8860 and dx, #0x03
8861 add dx, #0x0cfc
8862 mov al, cl
8863 out dx, al
8864 pop edx
8865 jmp pci_pro_ok
8866 pci_pro_f0c: ;; write configuration word
8867 cmp al, #0x0c
8868 jne pci_pro_f0d
8869 call pci_pro_select_reg
8870 push edx
8871 mov dx, di
8872 and dx, #0x02
8873 add dx, #0x0cfc
8874 mov ax, cx
8875 out dx, ax
8876 pop edx
8877 jmp pci_pro_ok
8878 pci_pro_f0d: ;; write configuration dword
8879 cmp al, #0x0d
8880 jne pci_pro_unknown
8881 call pci_pro_select_reg
8882 push edx
8883 mov dx, #0x0cfc
8884 mov eax, ecx
8885 out dx, eax
8886 pop edx
8887 jmp pci_pro_ok
8888 pci_pro_unknown:
8889 mov ah, #0x81
8890 pci_pro_fail:
8891 pop edi
8892 pop esi
8893 #ifdef BX_QEMU
8894 and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
8895 #endif
8896 popf
8898 retf
8899 pci_pro_ok:
8900 xor ah, ah
8901 pop edi
8902 pop esi
8903 #ifdef BX_QEMU
8904 and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
8905 #endif
8906 popf
8908 retf
8910 pci_pro_select_reg:
8911 push edx
8912 mov eax, #0x800000
8913 mov ax, bx
8914 shl eax, #8
8915 and di, #0xff
8916 or ax, di
8917 and al, #0xfc
8918 mov dx, #0x0cf8
8919 out dx, eax
8920 pop edx
8923 use16 386
8925 pcibios_real:
8926 push eax
8927 push dx
8928 mov eax, #0x80000000
8929 mov dx, #0x0cf8
8930 out dx, eax
8931 mov dx, #0x0cfc
8932 in eax, dx
8933 #ifdef PCI_FIXED_HOST_BRIDGE
8934 cmp eax, #PCI_FIXED_HOST_BRIDGE
8935 je pci_present
8936 #else
8937 ;; say ok if a device is present
8938 cmp eax, #0xffffffff
8939 jne pci_present
8940 #endif
8941 pop dx
8942 pop eax
8943 mov ah, #0xff
8946 pci_present:
8947 pop dx
8948 pop eax
8949 cmp al, #0x01 ;; installation check
8950 jne pci_real_f02
8951 mov ax, #0x0001
8952 mov bx, #0x0210
8953 mov cx, #0
8954 mov edx, #0x20494350 ;; "PCI "
8955 mov edi, #0xf0000
8956 mov di, #pcibios_protected
8959 pci_real_f02: ;; find pci device
8960 push esi
8961 push edi
8962 cmp al, #0x02
8963 jne pci_real_f08
8964 shl ecx, #16
8965 mov cx, dx
8966 mov bx, #0x0000
8967 mov di, #0x00
8968 pci_real_devloop:
8969 call pci_real_select_reg
8970 mov dx, #0x0cfc
8971 in eax, dx
8972 cmp eax, ecx
8973 jne pci_real_nextdev
8974 cmp si, #0
8975 je pci_real_ok
8976 dec si
8977 pci_real_nextdev:
8978 inc bx
8979 cmp bx, #0x0100
8980 jne pci_real_devloop
8981 mov dx, cx
8982 shr ecx, #16
8983 mov ah, #0x86
8984 jmp pci_real_fail
8985 pci_real_f08: ;; read configuration byte
8986 cmp al, #0x08
8987 jne pci_real_f09
8988 call pci_real_select_reg
8989 push dx
8990 mov dx, di
8991 and dx, #0x03
8992 add dx, #0x0cfc
8993 in al, dx
8994 pop dx
8995 mov cl, al
8996 jmp pci_real_ok
8997 pci_real_f09: ;; read configuration word
8998 cmp al, #0x09
8999 jne pci_real_f0a
9000 call pci_real_select_reg
9001 push dx
9002 mov dx, di
9003 and dx, #0x02
9004 add dx, #0x0cfc
9005 in ax, dx
9006 pop dx
9007 mov cx, ax
9008 jmp pci_real_ok
9009 pci_real_f0a: ;; read configuration dword
9010 cmp al, #0x0a
9011 jne pci_real_f0b
9012 call pci_real_select_reg
9013 push dx
9014 mov dx, #0x0cfc
9015 in eax, dx
9016 pop dx
9017 mov ecx, eax
9018 jmp pci_real_ok
9019 pci_real_f0b: ;; write configuration byte
9020 cmp al, #0x0b
9021 jne pci_real_f0c
9022 call pci_real_select_reg
9023 push dx
9024 mov dx, di
9025 and dx, #0x03
9026 add dx, #0x0cfc
9027 mov al, cl
9028 out dx, al
9029 pop dx
9030 jmp pci_real_ok
9031 pci_real_f0c: ;; write configuration word
9032 cmp al, #0x0c
9033 jne pci_real_f0d
9034 call pci_real_select_reg
9035 push dx
9036 mov dx, di
9037 and dx, #0x02
9038 add dx, #0x0cfc
9039 mov ax, cx
9040 out dx, ax
9041 pop dx
9042 jmp pci_real_ok
9043 pci_real_f0d: ;; write configuration dword
9044 cmp al, #0x0d
9045 jne pci_real_f0e
9046 call pci_real_select_reg
9047 push dx
9048 mov dx, #0x0cfc
9049 mov eax, ecx
9050 out dx, eax
9051 pop dx
9052 jmp pci_real_ok
9053 pci_real_f0e: ;; get irq routing options
9054 cmp al, #0x0e
9055 jne pci_real_unknown
9056 SEG ES
9057 cmp word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9058 jb pci_real_too_small
9059 SEG ES
9060 mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9061 pushf
9062 push ds
9063 push es
9064 push cx
9065 push si
9066 push di
9068 mov si, #pci_routing_table_structure_start
9069 push cs
9070 pop ds
9071 SEG ES
9072 mov cx, [di+2]
9073 SEG ES
9074 mov es, [di+4]
9075 mov di, cx
9076 mov cx, #pci_routing_table_structure_end - pci_routing_table_structure_start
9077 rep
9078 movsb
9079 pop di
9080 pop si
9081 pop cx
9082 pop es
9083 pop ds
9084 popf
9085 mov bx, #(1 << 9) | (1 << 11) ;; irq 9 and 11 are used
9086 jmp pci_real_ok
9087 pci_real_too_small:
9088 SEG ES
9089 mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
9090 mov ah, #0x89
9091 jmp pci_real_fail
9093 pci_real_unknown:
9094 mov ah, #0x81
9095 pci_real_fail:
9096 pop edi
9097 pop esi
9100 pci_real_ok:
9101 xor ah, ah
9102 pop edi
9103 pop esi
9107 pci_real_select_reg:
9108 push dx
9109 mov eax, #0x800000
9110 mov ax, bx
9111 shl eax, #8
9112 and di, #0xff
9113 or ax, di
9114 and al, #0xfc
9115 mov dx, #0x0cf8
9116 out dx, eax
9117 pop dx
9120 .align 16
9121 pci_routing_table_structure:
9122 db 0x24, 0x50, 0x49, 0x52 ;; "$PIR" signature
9123 db 0, 1 ;; version
9124 dw 32 + (6 * 16) ;; table size
9125 db 0 ;; PCI interrupt router bus
9126 db 0x08 ;; PCI interrupt router DevFunc
9127 dw 0x0000 ;; PCI exclusive IRQs
9128 dw 0x8086 ;; compatible PCI interrupt router vendor ID
9129 dw 0x7000 ;; compatible PCI interrupt router device ID
9130 dw 0,0 ;; Miniport data
9131 db 0,0,0,0,0,0,0,0,0,0,0 ;; reserved
9132 db 0x07 ;; checksum
9133 pci_routing_table_structure_start:
9134 ;; first slot entry PCI-to-ISA (embedded)
9135 db 0 ;; pci bus number
9136 db 0x08 ;; pci device number (bit 7-3)
9137 db 0x60 ;; link value INTA#: pointer into PCI2ISA config space
9138 dw 0xdef8 ;; IRQ bitmap INTA#
9139 db 0x61 ;; link value INTB#
9140 dw 0xdef8 ;; IRQ bitmap INTB#
9141 db 0x62 ;; link value INTC#
9142 dw 0xdef8 ;; IRQ bitmap INTC#
9143 db 0x63 ;; link value INTD#
9144 dw 0xdef8 ;; IRQ bitmap INTD#
9145 db 0 ;; physical slot (0 = embedded)
9146 db 0 ;; reserved
9147 ;; second slot entry: 1st PCI slot
9148 db 0 ;; pci bus number
9149 db 0x10 ;; pci device number (bit 7-3)
9150 db 0x61 ;; link value INTA#
9151 dw 0xdef8 ;; IRQ bitmap INTA#
9152 db 0x62 ;; link value INTB#
9153 dw 0xdef8 ;; IRQ bitmap INTB#
9154 db 0x63 ;; link value INTC#
9155 dw 0xdef8 ;; IRQ bitmap INTC#
9156 db 0x60 ;; link value INTD#
9157 dw 0xdef8 ;; IRQ bitmap INTD#
9158 db 1 ;; physical slot (0 = embedded)
9159 db 0 ;; reserved
9160 ;; third slot entry: 2nd PCI slot
9161 db 0 ;; pci bus number
9162 db 0x18 ;; pci device number (bit 7-3)
9163 db 0x62 ;; link value INTA#
9164 dw 0xdef8 ;; IRQ bitmap INTA#
9165 db 0x63 ;; link value INTB#
9166 dw 0xdef8 ;; IRQ bitmap INTB#
9167 db 0x60 ;; link value INTC#
9168 dw 0xdef8 ;; IRQ bitmap INTC#
9169 db 0x61 ;; link value INTD#
9170 dw 0xdef8 ;; IRQ bitmap INTD#
9171 db 2 ;; physical slot (0 = embedded)
9172 db 0 ;; reserved
9173 ;; 4th slot entry: 3rd PCI slot
9174 db 0 ;; pci bus number
9175 db 0x20 ;; pci device number (bit 7-3)
9176 db 0x63 ;; link value INTA#
9177 dw 0xdef8 ;; IRQ bitmap INTA#
9178 db 0x60 ;; link value INTB#
9179 dw 0xdef8 ;; IRQ bitmap INTB#
9180 db 0x61 ;; link value INTC#
9181 dw 0xdef8 ;; IRQ bitmap INTC#
9182 db 0x62 ;; link value INTD#
9183 dw 0xdef8 ;; IRQ bitmap INTD#
9184 db 3 ;; physical slot (0 = embedded)
9185 db 0 ;; reserved
9186 ;; 5th slot entry: 4rd PCI slot
9187 db 0 ;; pci bus number
9188 db 0x28 ;; pci device number (bit 7-3)
9189 db 0x60 ;; link value INTA#
9190 dw 0xdef8 ;; IRQ bitmap INTA#
9191 db 0x61 ;; link value INTB#
9192 dw 0xdef8 ;; IRQ bitmap INTB#
9193 db 0x62 ;; link value INTC#
9194 dw 0xdef8 ;; IRQ bitmap INTC#
9195 db 0x63 ;; link value INTD#
9196 dw 0xdef8 ;; IRQ bitmap INTD#
9197 db 4 ;; physical slot (0 = embedded)
9198 db 0 ;; reserved
9199 ;; 6th slot entry: 5rd PCI slot
9200 db 0 ;; pci bus number
9201 db 0x30 ;; pci device number (bit 7-3)
9202 db 0x61 ;; link value INTA#
9203 dw 0xdef8 ;; IRQ bitmap INTA#
9204 db 0x62 ;; link value INTB#
9205 dw 0xdef8 ;; IRQ bitmap INTB#
9206 db 0x63 ;; link value INTC#
9207 dw 0xdef8 ;; IRQ bitmap INTC#
9208 db 0x60 ;; link value INTD#
9209 dw 0xdef8 ;; IRQ bitmap INTD#
9210 db 5 ;; physical slot (0 = embedded)
9211 db 0 ;; reserved
9212 pci_routing_table_structure_end:
9214 #if !BX_ROMBIOS32
9215 pci_irq_list:
9216 db 11, 10, 9, 5;
9218 pcibios_init_sel_reg:
9219 push eax
9220 mov eax, #0x800000
9221 mov ax, bx
9222 shl eax, #8
9223 and dl, #0xfc
9224 or al, dl
9225 mov dx, #0x0cf8
9226 out dx, eax
9227 pop eax
9230 pcibios_init_iomem_bases:
9231 push bp
9232 mov bp, sp
9233 mov eax, #0xe0000000 ;; base for memory init
9234 push eax
9235 mov ax, #0xc000 ;; base for i/o init
9236 push ax
9237 mov ax, #0x0010 ;; start at base address #0
9238 push ax
9239 mov bx, #0x0008
9240 pci_init_io_loop1:
9241 mov dl, #0x00
9242 call pcibios_init_sel_reg
9243 mov dx, #0x0cfc
9244 in ax, dx
9245 cmp ax, #0xffff
9246 jz next_pci_dev
9247 mov dl, #0x04 ;; disable i/o and memory space access
9248 call pcibios_init_sel_reg
9249 mov dx, #0x0cfc
9250 in al, dx
9251 and al, #0xfc
9252 out dx, al
9253 pci_init_io_loop2:
9254 mov dl, [bp-8]
9255 call pcibios_init_sel_reg
9256 mov dx, #0x0cfc
9257 in eax, dx
9258 test al, #0x01
9259 jnz init_io_base
9260 mov ecx, eax
9261 mov eax, #0xffffffff
9262 out dx, eax
9263 in eax, dx
9264 cmp eax, ecx
9265 je next_pci_base
9266 xor eax, #0xffffffff
9267 mov ecx, eax
9268 mov eax, [bp-4]
9269 out dx, eax
9270 add eax, ecx ;; calculate next free mem base
9271 add eax, #0x01000000
9272 and eax, #0xff000000
9273 mov [bp-4], eax
9274 jmp next_pci_base
9275 init_io_base:
9276 mov cx, ax
9277 mov ax, #0xffff
9278 out dx, ax
9279 in ax, dx
9280 cmp ax, cx
9281 je next_pci_base
9282 xor ax, #0xfffe
9283 mov cx, ax
9284 mov ax, [bp-6]
9285 out dx, ax
9286 add ax, cx ;; calculate next free i/o base
9287 add ax, #0x0100
9288 and ax, #0xff00
9289 mov [bp-6], ax
9290 next_pci_base:
9291 mov al, [bp-8]
9292 add al, #0x04
9293 cmp al, #0x28
9294 je enable_iomem_space
9295 mov byte ptr[bp-8], al
9296 jmp pci_init_io_loop2
9297 enable_iomem_space:
9298 mov dl, #0x04 ;; enable i/o and memory space access if available
9299 call pcibios_init_sel_reg
9300 mov dx, #0x0cfc
9301 in al, dx
9302 or al, #0x07
9303 out dx, al
9304 next_pci_dev:
9305 mov byte ptr[bp-8], #0x10
9306 inc bx
9307 cmp bx, #0x0100
9308 jne pci_init_io_loop1
9309 mov sp, bp
9310 pop bp
9313 pcibios_init_set_elcr:
9314 push ax
9315 push cx
9316 mov dx, #0x04d0
9317 test al, #0x08
9318 jz is_master_pic
9319 inc dx
9320 and al, #0x07
9321 is_master_pic:
9322 mov cl, al
9323 mov bl, #0x01
9324 shl bl, cl
9325 in al, dx
9326 or al, bl
9327 out dx, al
9328 pop cx
9329 pop ax
9332 pcibios_init_irqs:
9333 push ds
9334 push bp
9335 mov ax, #0xf000
9336 mov ds, ax
9337 mov dx, #0x04d0 ;; reset ELCR1 + ELCR2
9338 mov al, #0x00
9339 out dx, al
9340 inc dx
9341 out dx, al
9342 mov si, #pci_routing_table_structure
9343 mov bh, [si+8]
9344 mov bl, [si+9]
9345 mov dl, #0x00
9346 call pcibios_init_sel_reg
9347 mov dx, #0x0cfc
9348 in eax, dx
9349 cmp eax, [si+12] ;; check irq router
9350 jne pci_init_end
9351 mov dl, [si+34]
9352 call pcibios_init_sel_reg
9353 push bx ;; save irq router bus + devfunc
9354 mov dx, #0x0cfc
9355 mov ax, #0x8080
9356 out dx, ax ;; reset PIRQ route control
9357 inc dx
9358 inc dx
9359 out dx, ax
9360 mov ax, [si+6]
9361 sub ax, #0x20
9362 shr ax, #4
9363 mov cx, ax
9364 add si, #0x20 ;; set pointer to 1st entry
9365 mov bp, sp
9366 mov ax, #pci_irq_list
9367 push ax
9368 xor ax, ax
9369 push ax
9370 pci_init_irq_loop1:
9371 mov bh, [si]
9372 mov bl, [si+1]
9373 pci_init_irq_loop2:
9374 mov dl, #0x00
9375 call pcibios_init_sel_reg
9376 mov dx, #0x0cfc
9377 in ax, dx
9378 cmp ax, #0xffff
9379 jnz pci_test_int_pin
9380 test bl, #0x07
9381 jz next_pir_entry
9382 jmp next_pci_func
9383 pci_test_int_pin:
9384 mov dl, #0x3c
9385 call pcibios_init_sel_reg
9386 mov dx, #0x0cfd
9387 in al, dx
9388 and al, #0x07
9389 jz next_pci_func
9390 dec al ;; determine pirq reg
9391 mov dl, #0x03
9392 mul al, dl
9393 add al, #0x02
9394 xor ah, ah
9395 mov bx, ax
9396 mov al, [si+bx]
9397 mov dl, al
9398 mov bx, [bp]
9399 call pcibios_init_sel_reg
9400 mov dx, #0x0cfc
9401 and al, #0x03
9402 add dl, al
9403 in al, dx
9404 cmp al, #0x80
9405 jb pirq_found
9406 mov bx, [bp-2] ;; pci irq list pointer
9407 mov al, [bx]
9408 out dx, al
9409 inc bx
9410 mov [bp-2], bx
9411 call pcibios_init_set_elcr
9412 pirq_found:
9413 mov bh, [si]
9414 mov bl, [si+1]
9415 add bl, [bp-3] ;; pci function number
9416 mov dl, #0x3c
9417 call pcibios_init_sel_reg
9418 mov dx, #0x0cfc
9419 out dx, al
9420 next_pci_func:
9421 inc byte ptr[bp-3]
9422 inc bl
9423 test bl, #0x07
9424 jnz pci_init_irq_loop2
9425 next_pir_entry:
9426 add si, #0x10
9427 mov byte ptr[bp-3], #0x00
9428 loop pci_init_irq_loop1
9429 mov sp, bp
9430 pop bx
9431 pci_init_end:
9432 pop bp
9433 pop ds
9435 #endif // BX_ROMBIOS32
9436 #endif // BX_PCIBIOS
9438 #if BX_ROMBIOS32
9439 rombios32_init:
9440 ;; save a20 and enable it
9441 in al, 0x92
9442 push ax
9443 or al, #0x02
9444 out 0x92, al
9446 ;; save SS:SP to the BDA
9447 xor ax, ax
9448 mov ds, ax
9449 mov 0x0469, ss
9450 mov 0x0467, sp
9452 SEG CS
9453 lidt [pmode_IDT_info]
9454 SEG CS
9455 lgdt [rombios32_gdt_48]
9456 ;; set PE bit in CR0
9457 mov eax, cr0
9458 or al, #0x01
9459 mov cr0, eax
9460 ;; start protected mode code: ljmpl 0x10:rombios32_init1
9461 db 0x66, 0xea
9462 dw rombios32_05
9463 dw 0x000f ;; high 16 bit address
9464 dw 0x0010
9466 use32 386
9467 rombios32_05:
9468 ;; init data segments
9469 mov eax, #0x18
9470 mov ds, ax
9471 mov es, ax
9472 mov ss, ax
9473 xor eax, eax
9474 mov fs, ax
9475 mov gs, ax
9478 ;; copy rombios32 code to ram (ram offset = 1MB)
9479 mov esi, #0xfffe0000
9480 mov edi, #0x00040000
9481 mov ecx, #0x10000 / 4
9482 rep
9483 movsd
9485 ;; init the stack pointer
9486 mov esp, #0x00080000
9488 ;; call rombios32 code
9489 mov eax, #0x00040000
9490 call eax
9492 ;; return to 16 bit protected mode first
9493 db 0xea
9494 dd rombios32_10
9495 dw 0x20
9497 use16 386
9498 rombios32_10:
9499 ;; restore data segment limits to 0xffff
9500 mov ax, #0x28
9501 mov ds, ax
9502 mov es, ax
9503 mov ss, ax
9504 mov fs, ax
9505 mov gs, ax
9507 ;; reset PE bit in CR0
9508 mov eax, cr0
9509 and al, #0xFE
9510 mov cr0, eax
9512 ;; far jump to flush CPU queue after transition to real mode
9513 JMP_AP(0xf000, rombios32_real_mode)
9515 rombios32_real_mode:
9516 ;; restore IDT to normal real-mode defaults
9517 SEG CS
9518 lidt [rmode_IDT_info]
9520 xor ax, ax
9521 mov ds, ax
9522 mov es, ax
9523 mov fs, ax
9524 mov gs, ax
9526 ;; restore SS:SP from the BDA
9527 mov ss, 0x0469
9528 mov sp, 0x0467
9529 ;; restore a20
9530 pop ax
9531 out 0x92, al
9534 rombios32_gdt_48:
9535 dw 0x30
9536 dw rombios32_gdt
9537 dw 0x000f
9539 rombios32_gdt:
9540 dw 0, 0, 0, 0
9541 dw 0, 0, 0, 0
9542 dw 0xffff, 0, 0x9b00, 0x00cf ; 32 bit flat code segment (0x10)
9543 dw 0xffff, 0, 0x9300, 0x00cf ; 32 bit flat data segment (0x18)
9544 dw 0xffff, 0, 0x9b0f, 0x0000 ; 16 bit code segment base=0xf0000 limit=0xffff
9545 dw 0xffff, 0, 0x9300, 0x0000 ; 16 bit data segment base=0x0 limit=0xffff
9546 #endif
9549 ; parallel port detection: base address in DX, index in BX, timeout in CL
9550 detect_parport:
9551 push dx
9552 add dx, #2
9553 in al, dx
9554 and al, #0xdf ; clear input mode
9555 out dx, al
9556 pop dx
9557 mov al, #0xaa
9558 out dx, al
9559 in al, dx
9560 cmp al, #0xaa
9561 jne no_parport
9562 push bx
9563 shl bx, #1
9564 mov [bx+0x408], dx ; Parallel I/O address
9565 pop bx
9566 mov [bx+0x478], cl ; Parallel printer timeout
9567 inc bx
9568 no_parport:
9571 ; serial port detection: base address in DX, index in BX, timeout in CL
9572 detect_serial:
9573 push dx
9574 inc dx
9575 mov al, #0x02
9576 out dx, al
9577 in al, dx
9578 cmp al, #0x02
9579 jne no_serial
9580 inc dx
9581 in al, dx
9582 cmp al, #0x02
9583 jne no_serial
9584 dec dx
9585 xor al, al
9586 out dx, al
9587 pop dx
9588 push bx
9589 shl bx, #1
9590 mov [bx+0x400], dx ; Serial I/O address
9591 pop bx
9592 mov [bx+0x47c], cl ; Serial timeout
9593 inc bx
9595 no_serial:
9596 pop dx
9599 rom_checksum:
9600 push ax
9601 push bx
9602 push cx
9603 xor ax, ax
9604 xor bx, bx
9605 xor cx, cx
9606 mov ch, [2]
9607 shl cx, #1
9608 checksum_loop:
9609 add al, [bx]
9610 inc bx
9611 loop checksum_loop
9612 and al, #0xff
9613 pop cx
9614 pop bx
9615 pop ax
9618 rom_scan:
9619 ;; Scan for existence of valid expansion ROMS.
9620 ;; Video ROM: from 0xC0000..0xC7FFF in 2k increments
9621 ;; General ROM: from 0xC8000..0xDFFFF in 2k increments
9622 ;; System ROM: only 0xE0000
9624 ;; Header:
9625 ;; Offset Value
9626 ;; 0 0x55
9627 ;; 1 0xAA
9628 ;; 2 ROM length in 512-byte blocks
9629 ;; 3 ROM initialization entry point (FAR CALL)
9631 mov cx, #0xc000
9632 rom_scan_loop:
9633 mov ds, cx
9634 mov ax, #0x0004 ;; start with increment of 4 (512-byte) blocks = 2k
9635 cmp [0], #0xAA55 ;; look for signature
9636 jne rom_scan_increment
9637 call rom_checksum
9638 jnz rom_scan_increment
9639 mov al, [2] ;; change increment to ROM length in 512-byte blocks
9641 ;; We want our increment in 512-byte quantities, rounded to
9642 ;; the nearest 2k quantity, since we only scan at 2k intervals.
9643 test al, #0x03
9644 jz block_count_rounded
9645 and al, #0xfc ;; needs rounding up
9646 add al, #0x04
9647 block_count_rounded:
9649 xor bx, bx ;; Restore DS back to 0000:
9650 mov ds, bx
9651 push ax ;; Save AX
9652 ;; Push addr of ROM entry point
9653 push cx ;; Push seg
9654 push #0x0003 ;; Push offset
9655 mov bp, sp ;; Call ROM init routine using seg:off on stack
9656 db 0xff ;; call_far ss:[bp+0]
9657 db 0x5e
9658 db 0
9659 cli ;; In case expansion ROM BIOS turns IF on
9660 add sp, #2 ;; Pop offset value
9661 pop cx ;; Pop seg value (restore CX)
9662 pop ax ;; Restore AX
9663 rom_scan_increment:
9664 shl ax, #5 ;; convert 512-bytes blocks to 16-byte increments
9665 ;; because the segment selector is shifted left 4 bits.
9666 add cx, ax
9667 cmp cx, #0xe000
9668 jbe rom_scan_loop
9670 xor ax, ax ;; Restore DS back to 0000:
9671 mov ds, ax
9674 ;; for 'C' strings and other data, insert them here with
9675 ;; a the following hack:
9676 ;; DATA_SEG_DEFS_HERE
9679 ;; the following area can be used to write dynamically generated tables
9680 .align 16
9681 bios_table_area_start:
9682 dd 0xaafb4442
9683 dd bios_table_area_end - bios_table_area_start - 8;
9685 ;--------
9686 ;- POST -
9687 ;--------
9688 .org 0xe05b ; POST Entry Point
9689 bios_table_area_end:
9690 post:
9692 xor ax, ax
9694 ;; first reset the DMA controllers
9695 out 0x0d,al
9696 out 0xda,al
9698 ;; then initialize the DMA controllers
9699 mov al, #0xC0
9700 out 0xD6, al ; cascade mode of channel 4 enabled
9701 mov al, #0x00
9702 out 0xD4, al ; unmask channel 4
9704 ;; Examine CMOS shutdown status.
9705 mov AL, #0x0f
9706 out 0x70, AL
9707 in AL, 0x71
9709 ;; backup status
9710 mov bl, al
9712 ;; Reset CMOS shutdown status.
9713 mov AL, #0x0f
9714 out 0x70, AL ; select CMOS register Fh
9715 mov AL, #0x00
9716 out 0x71, AL ; set shutdown action to normal
9718 ;; Examine CMOS shutdown status.
9719 mov al, bl
9721 ;; 0x00, 0x09, 0x0D+ = normal startup
9722 cmp AL, #0x00
9723 jz normal_post
9724 cmp AL, #0x0d
9725 jae normal_post
9726 cmp AL, #0x09
9727 je normal_post
9729 ;; 0x05 = eoi + jmp via [0x40:0x67] jump
9730 cmp al, #0x05
9731 je eoi_jmp_post
9733 ;; Examine CMOS shutdown status.
9734 ;; 0x01,0x02,0x03,0x04,0x06,0x07,0x08, 0x0a, 0x0b, 0x0c = Unimplemented shutdown status.
9735 push bx
9736 call _shutdown_status_panic
9738 #if 0
9739 HALT(__LINE__)
9741 ;#if 0
9742 ; 0xb0, 0x20, /* mov al, #0x20 */
9743 ; 0xe6, 0x20, /* out 0x20, al ;send EOI to PIC */
9744 ;#endif
9746 pop es
9747 pop ds
9748 popa
9749 iret
9750 #endif
9752 normal_post:
9753 ; case 0: normal startup
9756 mov ax, #0xfffe
9757 mov sp, ax
9758 mov ax, #0x0000
9759 mov ds, ax
9760 mov ss, ax
9762 ;; zero out BIOS data area (40:00..40:ff)
9763 mov es, ax
9764 mov cx, #0x0080 ;; 128 words
9765 mov di, #0x0400
9768 stosw
9770 call _log_bios_start
9772 ;; set all interrupts to default handler
9773 mov bx, #0x0000 ;; offset index
9774 mov cx, #0x0100 ;; counter (256 interrupts)
9775 mov ax, #dummy_iret_handler
9776 mov dx, #0xF000
9778 post_default_ints:
9779 mov [bx], ax
9780 inc bx
9781 inc bx
9782 mov [bx], dx
9783 inc bx
9784 inc bx
9785 loop post_default_ints
9787 ;; set vector 0x79 to zero
9788 ;; this is used by 'gardian angel' protection system
9789 SET_INT_VECTOR(0x79, #0, #0)
9791 ;; base memory in K 40:13 (word)
9792 mov ax, #BASE_MEM_IN_K
9793 mov 0x0413, ax
9796 ;; Manufacturing Test 40:12
9797 ;; zerod out above
9799 ;; Warm Boot Flag 0040:0072
9800 ;; value of 1234h = skip memory checks
9801 ;; zerod out above
9804 ;; Printer Services vector
9805 SET_INT_VECTOR(0x17, #0xF000, #int17_handler)
9807 ;; Bootstrap failure vector
9808 SET_INT_VECTOR(0x18, #0xF000, #int18_handler)
9810 ;; Bootstrap Loader vector
9811 SET_INT_VECTOR(0x19, #0xF000, #int19_handler)
9813 ;; User Timer Tick vector
9814 SET_INT_VECTOR(0x1c, #0xF000, #int1c_handler)
9816 ;; Memory Size Check vector
9817 SET_INT_VECTOR(0x12, #0xF000, #int12_handler)
9819 ;; Equipment Configuration Check vector
9820 SET_INT_VECTOR(0x11, #0xF000, #int11_handler)
9822 ;; System Services
9823 SET_INT_VECTOR(0x15, #0xF000, #int15_handler)
9825 ;; EBDA setup
9826 call ebda_post
9828 ;; PIT setup
9829 SET_INT_VECTOR(0x08, #0xF000, #int08_handler)
9830 ;; int 1C already points at dummy_iret_handler (above)
9831 mov al, #0x34 ; timer0: binary count, 16bit count, mode 2
9832 out 0x43, al
9833 mov al, #0x00 ; maximum count of 0000H = 18.2Hz
9834 out 0x40, al
9835 out 0x40, al
9837 ;; Keyboard
9838 SET_INT_VECTOR(0x09, #0xF000, #int09_handler)
9839 SET_INT_VECTOR(0x16, #0xF000, #int16_handler)
9841 xor ax, ax
9842 mov ds, ax
9843 mov 0x0417, al /* keyboard shift flags, set 1 */
9844 mov 0x0418, al /* keyboard shift flags, set 2 */
9845 mov 0x0419, al /* keyboard alt-numpad work area */
9846 mov 0x0471, al /* keyboard ctrl-break flag */
9847 mov 0x0497, al /* keyboard status flags 4 */
9848 mov al, #0x10
9849 mov 0x0496, al /* keyboard status flags 3 */
9852 /* keyboard head of buffer pointer */
9853 mov bx, #0x001E
9854 mov 0x041A, bx
9856 /* keyboard end of buffer pointer */
9857 mov 0x041C, bx
9859 /* keyboard pointer to start of buffer */
9860 mov bx, #0x001E
9861 mov 0x0480, bx
9863 /* keyboard pointer to end of buffer */
9864 mov bx, #0x003E
9865 mov 0x0482, bx
9867 /* init the keyboard */
9868 call _keyboard_init
9870 ;; mov CMOS Equipment Byte to BDA Equipment Word
9871 mov ax, 0x0410
9872 mov al, #0x14
9873 out 0x70, al
9874 in al, 0x71
9875 mov 0x0410, ax
9878 ;; Parallel setup
9879 SET_INT_VECTOR(0x0F, #0xF000, #dummy_iret_handler)
9880 xor ax, ax
9881 mov ds, ax
9882 xor bx, bx
9883 mov cl, #0x14 ; timeout value
9884 mov dx, #0x378 ; Parallel I/O address, port 1
9885 call detect_parport
9886 mov dx, #0x278 ; Parallel I/O address, port 2
9887 call detect_parport
9888 shl bx, #0x0e
9889 mov ax, 0x410 ; Equipment word bits 14..15 determing # parallel ports
9890 and ax, #0x3fff
9891 or ax, bx ; set number of parallel ports
9892 mov 0x410, ax
9894 ;; Serial setup
9895 SET_INT_VECTOR(0x0C, #0xF000, #dummy_iret_handler)
9896 SET_INT_VECTOR(0x14, #0xF000, #int14_handler)
9897 xor bx, bx
9898 mov cl, #0x0a ; timeout value
9899 mov dx, #0x03f8 ; Serial I/O address, port 1
9900 call detect_serial
9901 mov dx, #0x02f8 ; Serial I/O address, port 2
9902 call detect_serial
9903 mov dx, #0x03e8 ; Serial I/O address, port 3
9904 call detect_serial
9905 mov dx, #0x02e8 ; Serial I/O address, port 4
9906 call detect_serial
9907 shl bx, #0x09
9908 mov ax, 0x410 ; Equipment word bits 9..11 determing # serial ports
9909 and ax, #0xf1ff
9910 or ax, bx ; set number of serial port
9911 mov 0x410, ax
9913 ;; CMOS RTC
9914 SET_INT_VECTOR(0x1A, #0xF000, #int1a_handler)
9915 SET_INT_VECTOR(0x4A, #0xF000, #dummy_iret_handler)
9916 SET_INT_VECTOR(0x70, #0xF000, #int70_handler)
9917 ;; BIOS DATA AREA 0x4CE ???
9918 call timer_tick_post
9920 ;; PS/2 mouse setup
9921 SET_INT_VECTOR(0x74, #0xF000, #int74_handler)
9923 ;; IRQ13 (FPU exception) setup
9924 SET_INT_VECTOR(0x75, #0xF000, #int75_handler)
9926 ;; Video setup
9927 SET_INT_VECTOR(0x10, #0xF000, #int10_handler)
9929 ;; PIC
9930 mov al, #0x11 ; send initialisation commands
9931 out 0x20, al
9932 out 0xa0, al
9933 mov al, #0x08
9934 out 0x21, al
9935 mov al, #0x70
9936 out 0xa1, al
9937 mov al, #0x04
9938 out 0x21, al
9939 mov al, #0x02
9940 out 0xa1, al
9941 mov al, #0x01
9942 out 0x21, al
9943 out 0xa1, al
9944 mov al, #0xb8
9945 out 0x21, AL ;master pic: unmask IRQ 0, 1, 2, 6
9946 #if BX_USE_PS2_MOUSE
9947 mov al, #0x8f
9948 #else
9949 mov al, #0x9f
9950 #endif
9951 out 0xa1, AL ;slave pic: unmask IRQ 12, 13, 14
9953 #if BX_ROMBIOS32
9954 call rombios32_init
9955 #else
9956 call pcibios_init_iomem_bases
9957 call pcibios_init_irqs
9958 #endif
9959 call rom_scan
9961 call _print_bios_banner
9964 ;; Floppy setup
9966 call floppy_drive_post
9968 #if BX_USE_ATADRV
9971 ;; Hard Drive setup
9973 call hard_drive_post
9976 ;; ATA/ATAPI driver setup
9978 call _ata_init
9979 call _ata_detect
9981 #else // BX_USE_ATADRV
9984 ;; Hard Drive setup
9986 call hard_drive_post
9988 #endif // BX_USE_ATADRV
9990 #if BX_ELTORITO_BOOT
9992 ;; eltorito floppy/harddisk emulation from cd
9994 call _cdemu_init
9996 #endif // BX_ELTORITO_BOOT
9998 sti ;; enable interrupts
9999 int #0x19
10002 .org 0xe2c3 ; NMI Handler Entry Point
10003 nmi:
10004 ;; FIXME the NMI handler should not panic
10005 ;; but iret when called from int75 (fpu exception)
10006 call _nmi_handler_msg
10007 iret
10009 int75_handler:
10010 out 0xf0, al // clear irq13
10011 call eoi_both_pics // clear interrupt
10012 int 2 // legacy nmi call
10013 iret
10015 ;-------------------------------------------
10016 ;- INT 13h Fixed Disk Services Entry Point -
10017 ;-------------------------------------------
10018 .org 0xe3fe ; INT 13h Fixed Disk Services Entry Point
10019 int13_handler:
10020 //JMPL(int13_relocated)
10021 jmp int13_relocated
10023 .org 0xe401 ; Fixed Disk Parameter Table
10025 ;----------
10026 ;- INT19h -
10027 ;----------
10028 .org 0xe6f2 ; INT 19h Boot Load Service Entry Point
10029 int19_handler:
10031 jmp int19_relocated
10032 ;-------------------------------------------
10033 ;- System BIOS Configuration Data Table
10034 ;-------------------------------------------
10035 .org BIOS_CONFIG_TABLE
10036 db 0x08 ; Table size (bytes) -Lo
10037 db 0x00 ; Table size (bytes) -Hi
10038 db SYS_MODEL_ID
10039 db SYS_SUBMODEL_ID
10040 db BIOS_REVISION
10041 ; Feature byte 1
10042 ; b7: 1=DMA channel 3 used by hard disk
10043 ; b6: 1=2 interrupt controllers present
10044 ; b5: 1=RTC present
10045 ; b4: 1=BIOS calls int 15h/4Fh every key
10046 ; b3: 1=wait for extern event supported (Int 15h/41h)
10047 ; b2: 1=extended BIOS data area used
10048 ; b1: 0=AT or ESDI bus, 1=MicroChannel
10049 ; b0: 1=Dual bus (MicroChannel + ISA)
10050 db (0 << 7) | \
10051 (1 << 6) | \
10052 (1 << 5) | \
10053 (BX_CALL_INT15_4F << 4) | \
10054 (0 << 3) | \
10055 (BX_USE_EBDA << 2) | \
10056 (0 << 1) | \
10057 (0 << 0)
10058 ; Feature byte 2
10059 ; b7: 1=32-bit DMA supported
10060 ; b6: 1=int16h, function 9 supported
10061 ; b5: 1=int15h/C6h (get POS data) supported
10062 ; b4: 1=int15h/C7h (get mem map info) supported
10063 ; b3: 1=int15h/C8h (en/dis CPU) supported
10064 ; b2: 1=non-8042 kb controller
10065 ; b1: 1=data streaming supported
10066 ; b0: reserved
10067 db (0 << 7) | \
10068 (1 << 6) | \
10069 (0 << 5) | \
10070 (0 << 4) | \
10071 (0 << 3) | \
10072 (0 << 2) | \
10073 (0 << 1) | \
10074 (0 << 0)
10075 ; Feature byte 3
10076 ; b7: not used
10077 ; b6: reserved
10078 ; b5: reserved
10079 ; b4: POST supports ROM-to-RAM enable/disable
10080 ; b3: SCSI on system board
10081 ; b2: info panel installed
10082 ; b1: Initial Machine Load (IML) system - BIOS on disk
10083 ; b0: SCSI supported in IML
10084 db 0x00
10085 ; Feature byte 4
10086 ; b7: IBM private
10087 ; b6: EEPROM present
10088 ; b5-3: ABIOS presence (011 = not supported)
10089 ; b2: private
10090 ; b1: memory split above 16Mb supported
10091 ; b0: POSTEXT directly supported by POST
10092 db 0x00
10093 ; Feature byte 5 (IBM)
10094 ; b1: enhanced mouse
10095 ; b0: flash EPROM
10096 db 0x00
10100 .org 0xe729 ; Baud Rate Generator Table
10102 ;----------
10103 ;- INT14h -
10104 ;----------
10105 .org 0xe739 ; INT 14h Serial Communications Service Entry Point
10106 int14_handler:
10107 push ds
10108 pusha
10109 mov ax, #0x0000
10110 mov ds, ax
10111 call _int14_function
10112 popa
10113 pop ds
10114 iret
10117 ;----------------------------------------
10118 ;- INT 16h Keyboard Service Entry Point -
10119 ;----------------------------------------
10120 .org 0xe82e
10121 int16_handler:
10124 push ds
10125 pushf
10126 pusha
10128 cmp ah, #0x00
10129 je int16_F00
10130 cmp ah, #0x10
10131 je int16_F00
10133 mov bx, #0xf000
10134 mov ds, bx
10135 call _int16_function
10136 popa
10137 popf
10138 pop ds
10139 jz int16_zero_set
10141 int16_zero_clear:
10142 push bp
10143 mov bp, sp
10144 //SEG SS
10145 and BYTE [bp + 0x06], #0xbf
10146 pop bp
10147 iret
10149 int16_zero_set:
10150 push bp
10151 mov bp, sp
10152 //SEG SS
10153 or BYTE [bp + 0x06], #0x40
10154 pop bp
10155 iret
10157 int16_F00:
10158 mov bx, #0x0040
10159 mov ds, bx
10161 int16_wait_for_key:
10163 mov bx, 0x001a
10164 cmp bx, 0x001c
10165 jne int16_key_found
10168 #if 0
10169 /* no key yet, call int 15h, function AX=9002 */
10170 0x50, /* push AX */
10171 0xb8, 0x02, 0x90, /* mov AX, #0x9002 */
10172 0xcd, 0x15, /* int 15h */
10173 0x58, /* pop AX */
10174 0xeb, 0xea, /* jmp WAIT_FOR_KEY */
10175 #endif
10176 jmp int16_wait_for_key
10178 int16_key_found:
10179 mov bx, #0xf000
10180 mov ds, bx
10181 call _int16_function
10182 popa
10183 popf
10184 pop ds
10185 #if 0
10186 /* notify int16 complete w/ int 15h, function AX=9102 */
10187 0x50, /* push AX */
10188 0xb8, 0x02, 0x91, /* mov AX, #0x9102 */
10189 0xcd, 0x15, /* int 15h */
10190 0x58, /* pop AX */
10191 #endif
10192 iret
10196 ;-------------------------------------------------
10197 ;- INT09h : Keyboard Hardware Service Entry Point -
10198 ;-------------------------------------------------
10199 .org 0xe987
10200 int09_handler:
10202 push ax
10204 mov al, #0xAD ;;disable keyboard
10205 out #0x64, al
10207 mov al, #0x0B
10208 out #0x20, al
10209 in al, #0x20
10210 and al, #0x02
10211 jz int09_finish
10213 in al, #0x60 ;;read key from keyboard controller
10215 push ds
10216 pusha
10217 #ifdef BX_CALL_INT15_4F
10218 mov ah, #0x4f ;; allow for keyboard intercept
10220 int #0x15
10221 jnc int09_done
10222 #endif
10224 ;; check for extended key
10225 cmp al, #0xe0
10226 jne int09_check_pause
10227 xor ax, ax
10228 mov ds, ax
10229 mov al, BYTE [0x496] ;; mf2_state |= 0x02
10230 or al, #0x02
10231 mov BYTE [0x496], al
10232 jmp int09_done
10234 int09_check_pause: ;; check for pause key
10235 cmp al, #0xe1
10236 jne int09_process_key
10237 xor ax, ax
10238 mov ds, ax
10239 mov al, BYTE [0x496] ;; mf2_state |= 0x01
10240 or al, #0x01
10241 mov BYTE [0x496], al
10242 jmp int09_done
10244 int09_process_key:
10245 mov bx, #0xf000
10246 mov ds, bx
10247 call _int09_function
10249 int09_done:
10250 popa
10251 pop ds
10253 call eoi_master_pic
10255 int09_finish:
10256 mov al, #0xAE ;;enable keyboard
10257 out #0x64, al
10258 pop ax
10259 iret
10262 ;----------------------------------------
10263 ;- INT 13h Diskette Service Entry Point -
10264 ;----------------------------------------
10265 .org 0xec59
10266 int13_diskette:
10267 jmp int13_noeltorito
10269 ;---------------------------------------------
10270 ;- INT 0Eh Diskette Hardware ISR Entry Point -
10271 ;---------------------------------------------
10272 .org 0xef57 ; INT 0Eh Diskette Hardware ISR Entry Point
10273 int0e_handler:
10274 push ax
10275 push dx
10276 mov dx, #0x03f4
10277 in al, dx
10278 and al, #0xc0
10279 cmp al, #0xc0
10280 je int0e_normal
10281 mov dx, #0x03f5
10282 mov al, #0x08 ; sense interrupt status
10283 out dx, al
10284 int0e_loop1:
10285 mov dx, #0x03f4
10286 in al, dx
10287 and al, #0xc0
10288 cmp al, #0xc0
10289 jne int0e_loop1
10290 int0e_loop2:
10291 mov dx, #0x03f5
10292 in al, dx
10293 mov dx, #0x03f4
10294 in al, dx
10295 and al, #0xc0
10296 cmp al, #0xc0
10297 je int0e_loop2
10298 int0e_normal:
10299 push ds
10300 mov ax, #0x0000 ;; segment 0000
10301 mov ds, ax
10302 call eoi_master_pic
10303 mov al, 0x043e
10304 or al, #0x80 ;; diskette interrupt has occurred
10305 mov 0x043e, al
10306 pop ds
10307 pop dx
10308 pop ax
10309 iret
10312 .org 0xefc7 ; Diskette Controller Parameter Table
10313 diskette_param_table:
10314 ;; Since no provisions are made for multiple drive types, most
10315 ;; values in this table are ignored. I set parameters for 1.44M
10316 ;; floppy here
10317 db 0xAF
10318 db 0x02 ;; head load time 0000001, DMA used
10319 db 0x25
10320 db 0x02
10321 db 18
10322 db 0x1B
10323 db 0xFF
10324 db 0x6C
10325 db 0xF6
10326 db 0x0F
10327 db 0x08
10330 ;----------------------------------------
10331 ;- INT17h : Printer Service Entry Point -
10332 ;----------------------------------------
10333 .org 0xefd2
10334 int17_handler:
10335 push ds
10336 pusha
10337 mov ax, #0x0000
10338 mov ds, ax
10339 call _int17_function
10340 popa
10341 pop ds
10342 iret
10344 diskette_param_table2:
10345 ;; New diskette parameter table adding 3 parameters from IBM
10346 ;; Since no provisions are made for multiple drive types, most
10347 ;; values in this table are ignored. I set parameters for 1.44M
10348 ;; floppy here
10349 db 0xAF
10350 db 0x02 ;; head load time 0000001, DMA used
10351 db 0x25
10352 db 0x02
10353 db 18
10354 db 0x1B
10355 db 0xFF
10356 db 0x6C
10357 db 0xF6
10358 db 0x0F
10359 db 0x08
10360 db 79 ;; maximum track
10361 db 0 ;; data transfer rate
10362 db 4 ;; drive type in cmos
10364 .org 0xf045 ; INT 10 Functions 0-Fh Entry Point
10365 HALT(__LINE__)
10366 iret
10368 ;----------
10369 ;- INT10h -
10370 ;----------
10371 .org 0xf065 ; INT 10h Video Support Service Entry Point
10372 int10_handler:
10373 ;; dont do anything, since the VGA BIOS handles int10h requests
10374 iret
10376 .org 0xf0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
10378 ;----------
10379 ;- INT12h -
10380 ;----------
10381 .org 0xf841 ; INT 12h Memory Size Service Entry Point
10382 ; ??? different for Pentium (machine check)?
10383 int12_handler:
10384 push ds
10385 mov ax, #0x0040
10386 mov ds, ax
10387 mov ax, 0x0013
10388 pop ds
10389 iret
10391 ;----------
10392 ;- INT11h -
10393 ;----------
10394 .org 0xf84d ; INT 11h Equipment List Service Entry Point
10395 int11_handler:
10396 push ds
10397 mov ax, #0x0040
10398 mov ds, ax
10399 mov ax, 0x0010
10400 pop ds
10401 iret
10403 ;----------
10404 ;- INT15h -
10405 ;----------
10406 .org 0xf859 ; INT 15h System Services Entry Point
10407 int15_handler:
10408 pushf
10409 #if BX_APM
10410 cmp ah, #0x53
10411 je apm_call
10412 #endif
10413 push ds
10414 push es
10415 cmp ah, #0x86
10416 je int15_handler32
10417 cmp ah, #0xE8
10418 je int15_handler32
10419 pusha
10420 #if BX_USE_PS2_MOUSE
10421 cmp ah, #0xC2
10422 je int15_handler_mouse
10423 #endif
10424 call _int15_function
10425 int15_handler_mouse_ret:
10426 popa
10427 int15_handler32_ret:
10428 pop es
10429 pop ds
10430 popf
10431 jmp iret_modify_cf
10432 #if BX_APM
10433 apm_call:
10434 jmp _apmreal_entry
10435 #endif
10437 #if BX_USE_PS2_MOUSE
10438 int15_handler_mouse:
10439 call _int15_function_mouse
10440 jmp int15_handler_mouse_ret
10441 #endif
10443 int15_handler32:
10444 pushad
10445 call _int15_function32
10446 popad
10447 jmp int15_handler32_ret
10449 ;; Protected mode IDT descriptor
10451 ;; I just make the limit 0, so the machine will shutdown
10452 ;; if an exception occurs during protected mode memory
10453 ;; transfers.
10455 ;; Set base to f0000 to correspond to beginning of BIOS,
10456 ;; in case I actually define an IDT later
10457 ;; Set limit to 0
10459 pmode_IDT_info:
10460 dw 0x0000 ;; limit 15:00
10461 dw 0x0000 ;; base 15:00
10462 db 0x0f ;; base 23:16
10464 ;; Real mode IDT descriptor
10466 ;; Set to typical real-mode values.
10467 ;; base = 000000
10468 ;; limit = 03ff
10470 rmode_IDT_info:
10471 dw 0x03ff ;; limit 15:00
10472 dw 0x0000 ;; base 15:00
10473 db 0x00 ;; base 23:16
10476 ;----------
10477 ;- INT1Ah -
10478 ;----------
10479 .org 0xfe6e ; INT 1Ah Time-of-day Service Entry Point
10480 int1a_handler:
10481 #if BX_PCIBIOS
10482 cmp ah, #0xb1
10483 jne int1a_normal
10484 call pcibios_real
10485 jc pcibios_error
10486 retf 2
10487 pcibios_error:
10488 mov bl, ah
10489 mov ah, #0xb1
10490 push ds
10491 pusha
10492 mov ax, ss ; set readable descriptor to ds, for calling pcibios
10493 mov ds, ax ; on 16bit protected mode.
10494 jmp int1a_callfunction
10495 int1a_normal:
10496 #endif
10497 push ds
10498 pusha
10499 xor ax, ax
10500 mov ds, ax
10501 int1a_callfunction:
10502 call _int1a_function
10503 popa
10504 pop ds
10505 iret
10508 ;; int70h: IRQ8 - CMOS RTC
10510 int70_handler:
10511 push ds
10512 pushad
10513 xor ax, ax
10514 mov ds, ax
10515 call _int70_function
10516 popad
10517 pop ds
10518 iret
10520 ;---------
10521 ;- INT08 -
10522 ;---------
10523 .org 0xfea5 ; INT 08h System Timer ISR Entry Point
10524 int08_handler:
10526 push eax
10527 push ds
10528 xor ax, ax
10529 mov ds, ax
10531 ;; time to turn off drive(s)?
10532 mov al,0x0440
10533 or al,al
10534 jz int08_floppy_off
10535 dec al
10536 mov 0x0440,al
10537 jnz int08_floppy_off
10538 ;; turn motor(s) off
10539 push dx
10540 mov dx,#0x03f2
10541 in al,dx
10542 and al,#0xcf
10543 out dx,al
10544 pop dx
10545 int08_floppy_off:
10547 mov eax, 0x046c ;; get ticks dword
10548 inc eax
10550 ;; compare eax to one days worth of timer ticks at 18.2 hz
10551 cmp eax, #0x001800B0
10552 jb int08_store_ticks
10553 ;; there has been a midnight rollover at this point
10554 xor eax, eax ;; zero out counter
10555 inc BYTE 0x0470 ;; increment rollover flag
10557 int08_store_ticks:
10558 mov 0x046c, eax ;; store new ticks dword
10559 ;; chain to user timer tick INT #0x1c
10560 //pushf
10561 //;; call_ep [ds:loc]
10562 //CALL_EP( 0x1c << 2 )
10563 int #0x1c
10565 call eoi_master_pic
10566 pop ds
10567 pop eax
10568 iret
10570 .org 0xfef3 ; Initial Interrupt Vector Offsets Loaded by POST
10573 .org 0xff00
10574 .ascii BIOS_COPYRIGHT_STRING
10576 ;------------------------------------------------
10577 ;- IRET Instruction for Dummy Interrupt Handler -
10578 ;------------------------------------------------
10579 .org 0xff53 ; IRET Instruction for Dummy Interrupt Handler
10580 dummy_iret_handler:
10581 iret
10583 .org 0xff54 ; INT 05h Print Screen Service Entry Point
10584 HALT(__LINE__)
10585 iret
10587 .org 0xfff0 ; Power-up Entry Point
10588 jmp 0xf000:post
10590 .org 0xfff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
10591 .ascii BIOS_BUILD_DATE
10593 .org 0xfffe ; System Model ID
10594 db SYS_MODEL_ID
10595 db 0x00 ; filler
10597 .org 0xfa6e ;; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
10598 ASM_END
10600 * This font comes from the fntcol16.zip package (c) by Joseph Gil
10601 * found at ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip
10602 * This font is public domain
10604 static Bit8u vgafont8[128*8]=
10606 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
10607 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e,
10608 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e,
10609 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00,
10610 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00,
10611 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c,
10612 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c,
10613 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00,
10614 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff,
10615 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
10616 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff,
10617 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78,
10618 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18,
10619 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0,
10620 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0,
10621 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99,
10622 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00,
10623 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00,
10624 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18,
10625 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
10626 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00,
10627 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78,
10628 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00,
10629 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff,
10630 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00,
10631 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
10632 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00,
10633 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
10634 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00,
10635 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00,
10636 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00,
10637 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00,
10638 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
10639 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00,
10640 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
10641 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00,
10642 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00,
10643 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00,
10644 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00,
10645 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
10646 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00,
10647 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
10648 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
10649 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00,
10650 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60,
10651 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
10652 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00,
10653 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00,
10654 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00,
10655 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00,
10656 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00,
10657 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00,
10658 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00,
10659 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00,
10660 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00,
10661 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00,
10662 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00,
10663 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00,
10664 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00,
10665 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60,
10666 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00,
10667 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00,
10668 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00,
10669 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00,
10670 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00,
10671 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00,
10672 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00,
10673 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00,
10674 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00,
10675 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00,
10676 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00,
10677 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00,
10678 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00,
10679 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
10680 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00,
10681 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00,
10682 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00,
10683 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00,
10684 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00,
10685 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00,
10686 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
10687 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00,
10688 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00,
10689 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00,
10690 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
10691 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00,
10692 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
10693 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00,
10694 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00,
10695 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00,
10696 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00,
10697 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
10698 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00,
10699 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
10700 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
10701 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
10702 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
10703 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00,
10704 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00,
10705 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00,
10706 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00,
10707 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
10708 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00,
10709 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
10710 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00,
10711 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
10712 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78,
10713 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00,
10714 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
10715 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00,
10716 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
10717 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
10718 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0,
10719 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e,
10720 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00,
10721 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00,
10722 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00,
10723 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
10724 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
10725 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00,
10726 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00,
10727 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
10728 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00,
10729 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00,
10730 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
10731 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00,
10732 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
10733 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00,
10736 ASM_START
10737 .org 0xcc00
10738 // bcc-generated data will be placed here
10739 ASM_END