- PCI IRQs must always be level sensitive (setup ELCR1/ELCR2 in pcibios init)
[gplbios.git] / rombios.c
blob57760ccee442cd3c09da37b231555efffd594f82
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 // ROM BIOS for use with Bochs/Plex x86 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 // Boot
124 // - the bios should try to boot from different devices.
125 // Use cmos regs 0x3d & high nibble of 0x38 to store up to 3 boot devices from 16
127 // BCC Bug: find a generic way to handle the bug of #asm after an "if" (fixed in 0.16.7)
129 #define DEBUG_ROMBIOS 0
131 #define DEBUG_ATA 0
132 #define DEBUG_INT13_HD 0
133 #define DEBUG_INT13_CD 0
134 #define DEBUG_INT13_ET 0
135 #define DEBUG_INT13_FL 0
136 #define DEBUG_INT15 0
137 #define DEBUG_INT16 0
138 #define DEBUG_INT1A 0
139 #define DEBUG_INT74 0
140 #define DEBUG_APM 0
142 #define BX_CPU 3
143 #define BX_USE_PS2_MOUSE 1
144 #define BX_CALL_INT15_4F 1
145 #define BX_USE_EBDA 1
146 #define BX_SUPPORT_FLOPPY 1
147 #define BX_FLOPPY_ON_CNT 37 // 2 seconds
148 #define BX_PCIBIOS 1
149 #define BX_APM 1
151 #define BX_USE_ATADRV 1
152 #define BX_ELTORITO_BOOT 1
154 #define BX_MAX_ATA_INTERFACES 4
155 #define BX_MAX_ATA_DEVICES (BX_MAX_ATA_INTERFACES*2)
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 // 1K of base memory used for Extended Bios Data Area (EBDA)
164 // EBDA is used for PS/2 mouse support, and IDE BIOS, etc.
165 #define EBDA_SEG 0x9FC0
166 #define EBDA_SIZE 1 // In KiB
167 #define BASE_MEM_IN_K (640 - EBDA_SIZE)
169 // Define the application NAME
170 #ifdef PLEX86
171 # define BX_APPNAME "Plex86"
172 #else
173 # define BX_APPNAME "Bochs"
174 #endif
176 // Sanity Checks
177 #if BX_USE_ATADRV && BX_CPU<3
178 # error The ATA/ATAPI Driver can only to be used with a 386+ cpu
179 #endif
180 #if BX_USE_ATADRV && !BX_USE_EBDA
181 # error ATA/ATAPI Driver can only be used if EBDA is available
182 #endif
183 #if BX_ELTORITO_BOOT && !BX_USE_ATADRV
184 # error El-Torito Boot can only be use if ATA/ATAPI Driver is available
185 #endif
186 #if BX_PCIBIOS && BX_CPU<3
187 # error PCI BIOS can only be used with 386+ cpu
188 #endif
191 #define PANIC_PORT 0x400
192 #define PANIC_PORT2 0x401
193 #define INFO_PORT 0x402
194 #define DEBUG_PORT 0x403
196 // #20 is dec 20
197 // #$20 is hex 20 = 32
198 // LDA #$20
199 // JSR $E820
200 // LDD .i,S
201 // JSR $C682
202 // mov al, #$20
204 // all hex literals should be prefixed with '0x'
205 // grep "#[0-9a-fA-F][0-9a-fA-F]" rombios.c
206 // no mov SEG-REG, #value, must mov register into seg-reg
207 // grep -i "mov[ ]*.s" rombios.c
209 // This is for compiling with gcc2 and gcc3
210 #define ASM_START #asm
211 #define ASM_END #endasm
213 ASM_START
214 .rom
216 .org 0x0000
218 #if BX_CPU >= 3
219 use16 386
220 #else
221 use16 286
222 #endif
224 MACRO HALT
225 ;; the HALT macro is called with the line number of the HALT call.
226 ;; The line number is then sent to the PANIC_PORT, causing Bochs/Plex
227 ;; to print a BX_PANIC message. This will normally halt the simulation
228 ;; with a message such as "BIOS panic at rombios.c, line 4091".
229 ;; However, users can choose to make panics non-fatal and continue.
230 mov dx,#PANIC_PORT
231 mov ax,#?1
232 out dx,ax
233 MEND
235 MACRO JMP_AP
236 db 0xea
237 dw ?2
238 dw ?1
239 MEND
241 MACRO SET_INT_VECTOR
242 mov ax, ?3
243 mov ?1*4, ax
244 mov ax, ?2
245 mov ?1*4+2, ax
246 MEND
248 ASM_END
250 typedef unsigned char Bit8u;
251 typedef unsigned short Bit16u;
252 typedef unsigned short bx_bool;
253 typedef unsigned long Bit32u;
255 #if BX_USE_ATADRV
257 void memsetb(seg,offset,value,count);
258 void memcpyb(dseg,doffset,sseg,soffset,count);
259 void memcpyd(dseg,doffset,sseg,soffset,count);
261 // memset of count bytes
262 void
263 memsetb(seg,offset,value,count)
264 Bit16u seg;
265 Bit16u offset;
266 Bit16u value;
267 Bit16u count;
269 ASM_START
270 push bp
271 mov bp, sp
273 push ax
274 push cx
275 push es
276 push di
278 mov cx, 10[bp] ; count
279 cmp cx, #0x00
280 je memsetb_end
281 mov ax, 4[bp] ; segment
282 mov es, ax
283 mov ax, 6[bp] ; offset
284 mov di, ax
285 mov al, 8[bp] ; value
288 stosb
290 memsetb_end:
291 pop di
292 pop es
293 pop cx
294 pop ax
296 pop bp
297 ASM_END
300 #if 0
301 // memcpy of count bytes
302 void
303 memcpyb(dseg,doffset,sseg,soffset,count)
304 Bit16u dseg;
305 Bit16u doffset;
306 Bit16u sseg;
307 Bit16u soffset;
308 Bit16u count;
310 ASM_START
311 push bp
312 mov bp, sp
314 push ax
315 push cx
316 push es
317 push di
318 push ds
319 push si
321 mov cx, 12[bp] ; count
322 cmp cx, #0x0000
323 je memcpyb_end
324 mov ax, 4[bp] ; dsegment
325 mov es, ax
326 mov ax, 6[bp] ; doffset
327 mov di, ax
328 mov ax, 8[bp] ; ssegment
329 mov ds, ax
330 mov ax, 10[bp] ; soffset
331 mov si, ax
334 movsb
336 memcpyb_end:
337 pop si
338 pop ds
339 pop di
340 pop es
341 pop cx
342 pop ax
344 pop bp
345 ASM_END
348 // memcpy of count dword
349 void
350 memcpyd(dseg,doffset,sseg,soffset,count)
351 Bit16u dseg;
352 Bit16u doffset;
353 Bit16u sseg;
354 Bit16u soffset;
355 Bit16u count;
357 ASM_START
358 push bp
359 mov bp, sp
361 push ax
362 push cx
363 push es
364 push di
365 push ds
366 push si
368 mov cx, 12[bp] ; count
369 cmp cx, #0x0000
370 je memcpyd_end
371 mov ax, 4[bp] ; dsegment
372 mov es, ax
373 mov ax, 6[bp] ; doffset
374 mov di, ax
375 mov ax, 8[bp] ; ssegment
376 mov ds, ax
377 mov ax, 10[bp] ; soffset
378 mov si, ax
381 movsd
383 memcpyd_end:
384 pop si
385 pop ds
386 pop di
387 pop es
388 pop cx
389 pop ax
391 pop bp
392 ASM_END
394 #endif
395 #endif //BX_USE_ATADRV
397 // read_dword and write_dword functions
398 static Bit32u read_dword();
399 static void write_dword();
401 Bit32u
402 read_dword(seg, offset)
403 Bit16u seg;
404 Bit16u offset;
406 ASM_START
407 push bp
408 mov bp, sp
410 push bx
411 push ds
412 mov ax, 4[bp] ; segment
413 mov ds, ax
414 mov bx, 6[bp] ; offset
415 mov ax, [bx]
416 inc bx
417 inc bx
418 mov dx, [bx]
419 ;; ax = return value (word)
420 ;; dx = return value (word)
421 pop ds
422 pop bx
424 pop bp
425 ASM_END
428 void
429 write_dword(seg, offset, data)
430 Bit16u seg;
431 Bit16u offset;
432 Bit32u data;
434 ASM_START
435 push bp
436 mov bp, sp
438 push ax
439 push bx
440 push ds
441 mov ax, 4[bp] ; segment
442 mov ds, ax
443 mov bx, 6[bp] ; offset
444 mov ax, 8[bp] ; data word
445 mov [bx], ax ; write data word
446 inc bx
447 inc bx
448 mov ax, 10[bp] ; data word
449 mov [bx], ax ; write data word
450 pop ds
451 pop bx
452 pop ax
454 pop bp
455 ASM_END
458 // Bit32u (unsigned long) and long helper functions
459 ASM_START
461 ;; and function
462 landl:
463 landul:
464 SEG SS
465 and ax,[di]
466 SEG SS
467 and bx,2[di]
470 ;; add function
471 laddl:
472 laddul:
473 SEG SS
474 add ax,[di]
475 SEG SS
476 adc bx,2[di]
479 ;; cmp function
480 lcmpl:
481 lcmpul:
482 and eax, #0x0000FFFF
483 shl ebx, #16
484 add eax, ebx
485 shr ebx, #16
486 SEG SS
487 cmp eax, dword ptr [di]
490 ;; sub function
491 lsubl:
492 lsubul:
493 SEG SS
494 sub ax,[di]
495 SEG SS
496 sbb bx,2[di]
499 ;; mul function
500 lmull:
501 lmulul:
502 and eax, #0x0000FFFF
503 shl ebx, #16
504 add eax, ebx
505 SEG SS
506 mul eax, dword ptr [di]
507 mov ebx, eax
508 shr ebx, #16
511 ;; dec function
512 ldecl:
513 ldecul:
514 SEG SS
515 dec dword ptr [bx]
518 ;; or function
519 lorl:
520 lorul:
521 SEG SS
522 or ax,[di]
523 SEG SS
524 or bx,2[di]
527 ;; inc function
528 lincl:
529 lincul:
530 SEG SS
531 inc dword ptr [bx]
534 ;; tst function
535 ltstl:
536 ltstul:
537 and eax, #0x0000FFFF
538 shl ebx, #16
539 add eax, ebx
540 shr ebx, #16
541 test eax, eax
544 ;; sr function
545 lsrul:
546 mov cx,di
547 jcxz lsr_exit
548 and eax, #0x0000FFFF
549 shl ebx, #16
550 add eax, ebx
551 lsr_loop:
552 shr eax, #1
553 loop lsr_loop
554 mov ebx, eax
555 shr ebx, #16
556 lsr_exit:
559 ;; sl function
560 lsll:
561 lslul:
562 mov cx,di
563 jcxz lsl_exit
564 and eax, #0x0000FFFF
565 shl ebx, #16
566 add eax, ebx
567 lsl_loop:
568 shl eax, #1
569 loop lsl_loop
570 mov ebx, eax
571 shr ebx, #16
572 lsl_exit:
575 idiv_:
577 idiv bx
580 idiv_u:
581 xor dx,dx
582 div bx
585 ldivul:
586 and eax, #0x0000FFFF
587 shl ebx, #16
588 add eax, ebx
589 xor edx, edx
590 SEG SS
591 mov bx, 2[di]
592 shl ebx, #16
593 SEG SS
594 mov bx, [di]
595 div ebx
596 mov ebx, eax
597 shr ebx, #16
600 ASM_END
602 // for access to RAM area which is used by interrupt vectors
603 // and BIOS Data Area
605 typedef struct {
606 unsigned char filler1[0x400];
607 unsigned char filler2[0x6c];
608 Bit16u ticks_low;
609 Bit16u ticks_high;
610 Bit8u midnight_flag;
611 } bios_data_t;
613 #define BiosData ((bios_data_t *) 0)
615 #if BX_USE_ATADRV
616 typedef struct {
617 Bit16u heads; // # heads
618 Bit16u cylinders; // # cylinders
619 Bit16u spt; // # sectors / track
620 } chs_t;
622 // DPTE definition
623 typedef struct {
624 Bit16u iobase1;
625 Bit16u iobase2;
626 Bit8u prefix;
627 Bit8u unused;
628 Bit8u irq;
629 Bit8u blkcount;
630 Bit8u dma;
631 Bit8u pio;
632 Bit16u options;
633 Bit16u reserved;
634 Bit8u revision;
635 Bit8u checksum;
636 } dpte_t;
638 typedef struct {
639 Bit8u iface; // ISA or PCI
640 Bit16u iobase1; // IO Base 1
641 Bit16u iobase2; // IO Base 2
642 Bit8u irq; // IRQ
643 } ata_channel_t;
645 typedef struct {
646 Bit8u type; // Detected type of ata (ata/atapi/none/unknown)
647 Bit8u device; // Detected type of attached devices (hd/cd/none)
648 Bit8u removable; // Removable device flag
649 Bit8u lock; // Locks for removable devices
650 // Bit8u lba_capable; // LBA capable flag - always yes for bochs devices
651 Bit8u mode; // transfert mode : PIO 16/32 bits - IRQ - ISADMA - PCIDMA
652 Bit16u blksize; // block size
654 Bit8u translation; // type of translation
655 chs_t lchs; // Logical CHS
656 chs_t pchs; // Physical CHS
658 Bit32u sectors; // Total sectors count
659 } ata_device_t;
661 typedef struct {
662 // ATA channels info
663 ata_channel_t channels[BX_MAX_ATA_INTERFACES];
665 // ATA devices info
666 ata_device_t devices[BX_MAX_ATA_DEVICES];
668 // map between (bios hd id - 0x80) and ata channels
669 Bit8u hdcount, hdidmap[BX_MAX_ATA_DEVICES];
671 // map between (bios cd id - 0xE0) and ata channels
672 Bit8u cdcount, cdidmap[BX_MAX_ATA_DEVICES];
674 // Buffer for DPTE table
675 dpte_t dpte;
677 // Count of transferred sectors and bytes
678 Bit16u trsfsectors;
679 Bit32u trsfbytes;
681 } ata_t;
683 #if BX_ELTORITO_BOOT
684 // ElTorito Device Emulation data
685 typedef struct {
686 Bit8u active;
687 Bit8u media;
688 Bit8u emulated_drive;
689 Bit8u controller_index;
690 Bit16u device_spec;
691 Bit32u ilba;
692 Bit16u buffer_segment;
693 Bit16u load_segment;
694 Bit16u sector_count;
696 // Virtual device
697 chs_t vdevice;
698 } cdemu_t;
699 #endif // BX_ELTORITO_BOOT
701 // for access to EBDA area
702 // The EBDA structure should conform to
703 // http://www.cybertrails.com/~fys/rombios.htm document
704 // I made the ata and cdemu structs begin at 0x121 in the EBDA seg
705 typedef struct {
706 unsigned char filler1[0x3D];
708 // FDPT - Can be splitted in data members if needed
709 unsigned char fdpt0[0x10];
710 unsigned char fdpt1[0x10];
712 unsigned char filler2[0xC4];
714 // ATA Driver data
715 ata_t ata;
717 #if BX_ELTORITO_BOOT
718 // El Torito Emulation data
719 cdemu_t cdemu;
720 #endif // BX_ELTORITO_BOOT
722 } ebda_data_t;
724 #define EbdaData ((ebda_data_t *) 0)
726 // for access to the int13ext structure
727 typedef struct {
728 Bit8u size;
729 Bit8u reserved;
730 Bit16u count;
731 Bit16u offset;
732 Bit16u segment;
733 Bit32u lba1;
734 Bit32u lba2;
735 } int13ext_t;
737 #define Int13Ext ((int13ext_t *) 0)
739 // Disk Physical Table definition
740 typedef struct {
741 Bit16u size;
742 Bit16u infos;
743 Bit32u cylinders;
744 Bit32u heads;
745 Bit32u spt;
746 Bit32u sector_count1;
747 Bit32u sector_count2;
748 Bit16u blksize;
749 Bit16u dpte_segment;
750 Bit16u dpte_offset;
751 Bit16u key;
752 Bit8u dpi_length;
753 Bit8u reserved1;
754 Bit16u reserved2;
755 Bit8u host_bus[4];
756 Bit8u iface_type[8];
757 Bit8u iface_path[8];
758 Bit8u device_path[8];
759 Bit8u reserved3;
760 Bit8u checksum;
761 } dpt_t;
763 #define Int13DPT ((dpt_t *) 0)
765 #endif // BX_USE_ATADRV
767 typedef struct {
768 union {
769 struct {
770 Bit16u di, si, bp, sp;
771 Bit16u bx, dx, cx, ax;
772 } r16;
773 struct {
774 Bit16u filler[4];
775 Bit8u bl, bh, dl, dh, cl, ch, al, ah;
776 } r8;
777 } u;
778 } pusha_regs_t;
780 typedef struct {
781 union {
782 struct {
783 Bit32u edi, esi, ebp, esp;
784 Bit32u ebx, edx, ecx, eax;
785 } r32;
786 struct {
787 Bit16u di, filler1, si, filler2, bp, filler3, sp, filler4;
788 Bit16u bx, filler5, dx, filler6, cx, filler7, ax, filler8;
789 } r16;
790 struct {
791 Bit32u filler[4];
792 Bit8u bl, bh;
793 Bit16u filler1;
794 Bit8u dl, dh;
795 Bit16u filler2;
796 Bit8u cl, ch;
797 Bit16u filler3;
798 Bit8u al, ah;
799 Bit16u filler4;
800 } r8;
801 } u;
802 } pushad_regs_t;
804 typedef struct {
805 union {
806 struct {
807 Bit16u flags;
808 } r16;
809 struct {
810 Bit8u flagsl;
811 Bit8u flagsh;
812 } r8;
813 } u;
814 } flags_t;
816 #define SetCF(x) x.u.r8.flagsl |= 0x01
817 #define SetZF(x) x.u.r8.flagsl |= 0x40
818 #define ClearCF(x) x.u.r8.flagsl &= 0xfe
819 #define ClearZF(x) x.u.r8.flagsl &= 0xbf
820 #define GetCF(x) (x.u.r8.flagsl & 0x01)
822 typedef struct {
823 Bit16u ip;
824 Bit16u cs;
825 flags_t flags;
826 } iret_addr_t;
830 static Bit8u inb();
831 static Bit8u inb_cmos();
832 static void outb();
833 static void outb_cmos();
834 static Bit16u inw();
835 static void outw();
836 static void init_rtc();
837 static bx_bool rtc_updating();
839 static Bit8u read_byte();
840 static Bit16u read_word();
841 static void write_byte();
842 static void write_word();
843 static void bios_printf();
845 static Bit8u inhibit_mouse_int_and_events();
846 static void enable_mouse_int_and_events();
847 static Bit8u send_to_mouse_ctrl();
848 static Bit8u get_mouse_data();
849 static void set_kbd_command_byte();
851 static void int09_function();
852 static void int13_harddisk();
853 static void int13_cdrom();
854 static void int13_cdemu();
855 static void int13_eltorito();
856 static void int13_diskette_function();
857 static void int14_function();
858 static void int15_function();
859 static void int16_function();
860 static void int17_function();
861 static Bit32u int19_function();
862 static void int1a_function();
863 static void int70_function();
864 static void int74_function();
865 //static Bit16u get_DS();
866 //static void set_DS();
867 static Bit16u get_SS();
868 static unsigned int enqueue_key();
869 static unsigned int dequeue_key();
870 static void get_hd_geometry();
871 static void set_diskette_ret_status();
872 static void set_diskette_current_cyl();
873 static void determine_floppy_media();
874 static bx_bool floppy_drive_exists();
875 static bx_bool floppy_drive_recal();
876 static bx_bool floppy_media_known();
877 static bx_bool floppy_media_sense();
878 static bx_bool set_enable_a20();
879 static void debugger_on();
880 static void debugger_off();
881 static void keyboard_init();
882 static void keyboard_panic();
883 static void shutdown_status_panic();
884 static void nmi_handler_msg();
886 static void print_bios_banner();
887 static void print_boot_device();
888 static void print_boot_failure();
889 static void print_cdromboot_failure();
891 # if BX_USE_ATADRV
893 // ATA / ATAPI driver
894 void ata_init();
895 void ata_detect();
896 void ata_reset();
898 Bit16u ata_cmd_non_data();
899 Bit16u ata_cmd_data_in();
900 Bit16u ata_cmd_data_out();
901 Bit16u ata_cmd_packet();
903 Bit16u atapi_get_sense();
904 Bit16u atapi_is_ready();
905 Bit16u atapi_is_cdrom();
907 #endif // BX_USE_ATADRV
909 #if BX_ELTORITO_BOOT
911 void cdemu_init();
912 Bit8u cdemu_isactive();
913 Bit8u cdemu_emulated_drive();
915 Bit16u cdrom_boot();
917 #endif // BX_ELTORITO_BOOT
919 static char bios_cvs_version_string[] = "$Revision$";
920 static char bios_date_string[] = "$Date$";
922 static char CVSID[] = "$Id$";
924 /* Offset to skip the CVS $Id: prefix */
925 #define bios_version_string (CVSID + 4)
927 #define BIOS_PRINTF_HALT 1
928 #define BIOS_PRINTF_SCREEN 2
929 #define BIOS_PRINTF_INFO 4
930 #define BIOS_PRINTF_DEBUG 8
931 #define BIOS_PRINTF_ALL (BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO)
932 #define BIOS_PRINTF_DEBHALT (BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO | BIOS_PRINTF_HALT)
934 #define printf(format, p...) bios_printf(BIOS_PRINTF_SCREEN, format, ##p)
936 // Defines the output macros.
937 // BX_DEBUG goes to INFO port until we can easily choose debug info on a
938 // per-device basis. Debug info are sent only in debug mode
939 #if DEBUG_ROMBIOS
940 # define BX_DEBUG(format, p...) bios_printf(BIOS_PRINTF_INFO, format, ##p)
941 #else
942 # define BX_DEBUG(format, p...)
943 #endif
944 #define BX_INFO(format, p...) bios_printf(BIOS_PRINTF_INFO, format, ##p)
945 #define BX_PANIC(format, p...) bios_printf(BIOS_PRINTF_DEBHALT, format, ##p)
947 #if DEBUG_ATA
948 # define BX_DEBUG_ATA(a...) BX_DEBUG(##a)
949 #else
950 # define BX_DEBUG_ATA(a...)
951 #endif
952 #if DEBUG_INT13_HD
953 # define BX_DEBUG_INT13_HD(a...) BX_DEBUG(##a)
954 #else
955 # define BX_DEBUG_INT13_HD(a...)
956 #endif
957 #if DEBUG_INT13_CD
958 # define BX_DEBUG_INT13_CD(a...) BX_DEBUG(##a)
959 #else
960 # define BX_DEBUG_INT13_CD(a...)
961 #endif
962 #if DEBUG_INT13_ET
963 # define BX_DEBUG_INT13_ET(a...) BX_DEBUG(##a)
964 #else
965 # define BX_DEBUG_INT13_ET(a...)
966 #endif
967 #if DEBUG_INT13_FL
968 # define BX_DEBUG_INT13_FL(a...) BX_DEBUG(##a)
969 #else
970 # define BX_DEBUG_INT13_FL(a...)
971 #endif
972 #if DEBUG_INT15
973 # define BX_DEBUG_INT15(a...) BX_DEBUG(##a)
974 #else
975 # define BX_DEBUG_INT15(a...)
976 #endif
977 #if DEBUG_INT16
978 # define BX_DEBUG_INT16(a...) BX_DEBUG(##a)
979 #else
980 # define BX_DEBUG_INT16(a...)
981 #endif
982 #if DEBUG_INT1A
983 # define BX_DEBUG_INT1A(a...) BX_DEBUG(##a)
984 #else
985 # define BX_DEBUG_INT1A(a...)
986 #endif
987 #if DEBUG_INT74
988 # define BX_DEBUG_INT74(a...) BX_DEBUG(##a)
989 #else
990 # define BX_DEBUG_INT74(a...)
991 #endif
993 #define SET_AL(val8) AX = ((AX & 0xff00) | (val8))
994 #define SET_BL(val8) BX = ((BX & 0xff00) | (val8))
995 #define SET_CL(val8) CX = ((CX & 0xff00) | (val8))
996 #define SET_DL(val8) DX = ((DX & 0xff00) | (val8))
997 #define SET_AH(val8) AX = ((AX & 0x00ff) | ((val8) << 8))
998 #define SET_BH(val8) BX = ((BX & 0x00ff) | ((val8) << 8))
999 #define SET_CH(val8) CX = ((CX & 0x00ff) | ((val8) << 8))
1000 #define SET_DH(val8) DX = ((DX & 0x00ff) | ((val8) << 8))
1002 #define GET_AL() ( AX & 0x00ff )
1003 #define GET_BL() ( BX & 0x00ff )
1004 #define GET_CL() ( CX & 0x00ff )
1005 #define GET_DL() ( DX & 0x00ff )
1006 #define GET_AH() ( AX >> 8 )
1007 #define GET_BH() ( BX >> 8 )
1008 #define GET_CH() ( CX >> 8 )
1009 #define GET_DH() ( DX >> 8 )
1012 #define SET_CF() FLAGS |= 0x0001
1013 #define CLEAR_CF() FLAGS &= 0xfffe
1014 #define GET_CF() (FLAGS & 0x0001)
1016 #define SET_ZF() FLAGS |= 0x0040
1017 #define CLEAR_ZF() FLAGS &= 0xffbf
1018 #define GET_ZF() (FLAGS & 0x0040)
1020 #define UNSUPPORTED_FUNCTION 0x86
1022 #define none 0
1023 #define MAX_SCAN_CODE 0x53
1025 static struct {
1026 Bit16u normal;
1027 Bit16u shift;
1028 Bit16u control;
1029 Bit16u alt;
1030 Bit8u lock_flags;
1031 } scan_to_scanascii[MAX_SCAN_CODE + 1] = {
1032 { none, none, none, none, none },
1033 { 0x011b, 0x011b, 0x011b, 0x0100, none }, /* escape */
1034 { 0x0231, 0x0221, none, 0x7800, none }, /* 1! */
1035 { 0x0332, 0x0340, 0x0300, 0x7900, none }, /* 2@ */
1036 { 0x0433, 0x0423, none, 0x7a00, none }, /* 3# */
1037 { 0x0534, 0x0524, none, 0x7b00, none }, /* 4$ */
1038 { 0x0635, 0x0625, none, 0x7c00, none }, /* 5% */
1039 { 0x0736, 0x075e, 0x071e, 0x7d00, none }, /* 6^ */
1040 { 0x0837, 0x0826, none, 0x7e00, none }, /* 7& */
1041 { 0x0938, 0x092a, none, 0x7f00, none }, /* 8* */
1042 { 0x0a39, 0x0a28, none, 0x8000, none }, /* 9( */
1043 { 0x0b30, 0x0b29, none, 0x8100, none }, /* 0) */
1044 { 0x0c2d, 0x0c5f, 0x0c1f, 0x8200, none }, /* -_ */
1045 { 0x0d3d, 0x0d2b, none, 0x8300, none }, /* =+ */
1046 { 0x0e08, 0x0e08, 0x0e7f, none, none }, /* backspace */
1047 { 0x0f09, 0x0f00, none, none, none }, /* tab */
1048 { 0x1071, 0x1051, 0x1011, 0x1000, 0x40 }, /* Q */
1049 { 0x1177, 0x1157, 0x1117, 0x1100, 0x40 }, /* W */
1050 { 0x1265, 0x1245, 0x1205, 0x1200, 0x40 }, /* E */
1051 { 0x1372, 0x1352, 0x1312, 0x1300, 0x40 }, /* R */
1052 { 0x1474, 0x1454, 0x1414, 0x1400, 0x40 }, /* T */
1053 { 0x1579, 0x1559, 0x1519, 0x1500, 0x40 }, /* Y */
1054 { 0x1675, 0x1655, 0x1615, 0x1600, 0x40 }, /* U */
1055 { 0x1769, 0x1749, 0x1709, 0x1700, 0x40 }, /* I */
1056 { 0x186f, 0x184f, 0x180f, 0x1800, 0x40 }, /* O */
1057 { 0x1970, 0x1950, 0x1910, 0x1900, 0x40 }, /* P */
1058 { 0x1a5b, 0x1a7b, 0x1a1b, none, none }, /* [{ */
1059 { 0x1b5d, 0x1b7d, 0x1b1d, none, none }, /* ]} */
1060 { 0x1c0d, 0x1c0d, 0x1c0a, none, none }, /* Enter */
1061 { none, none, none, none, none }, /* L Ctrl */
1062 { 0x1e61, 0x1e41, 0x1e01, 0x1e00, 0x40 }, /* A */
1063 { 0x1f73, 0x1f53, 0x1f13, 0x1f00, 0x40 }, /* S */
1064 { 0x2064, 0x2044, 0x2004, 0x2000, 0x40 }, /* D */
1065 { 0x2166, 0x2146, 0x2106, 0x2100, 0x40 }, /* F */
1066 { 0x2267, 0x2247, 0x2207, 0x2200, 0x40 }, /* G */
1067 { 0x2368, 0x2348, 0x2308, 0x2300, 0x40 }, /* H */
1068 { 0x246a, 0x244a, 0x240a, 0x2400, 0x40 }, /* J */
1069 { 0x256b, 0x254b, 0x250b, 0x2500, 0x40 }, /* K */
1070 { 0x266c, 0x264c, 0x260c, 0x2600, 0x40 }, /* L */
1071 { 0x273b, 0x273a, none, none, none }, /* ;: */
1072 { 0x2827, 0x2822, none, none, none }, /* '" */
1073 { 0x2960, 0x297e, none, none, none }, /* `~ */
1074 { none, none, none, none, none }, /* L shift */
1075 { 0x2b5c, 0x2b7c, 0x2b1c, none, none }, /* |\ */
1076 { 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00, 0x40 }, /* Z */
1077 { 0x2d78, 0x2d58, 0x2d18, 0x2d00, 0x40 }, /* X */
1078 { 0x2e63, 0x2e43, 0x2e03, 0x2e00, 0x40 }, /* C */
1079 { 0x2f76, 0x2f56, 0x2f16, 0x2f00, 0x40 }, /* V */
1080 { 0x3062, 0x3042, 0x3002, 0x3000, 0x40 }, /* B */
1081 { 0x316e, 0x314e, 0x310e, 0x3100, 0x40 }, /* N */
1082 { 0x326d, 0x324d, 0x320d, 0x3200, 0x40 }, /* M */
1083 { 0x332c, 0x333c, none, none, none }, /* ,< */
1084 { 0x342e, 0x343e, none, none, none }, /* .> */
1085 { 0x352f, 0x353f, none, none, none }, /* /? */
1086 { none, none, none, none, none }, /* R Shift */
1087 { 0x372a, 0x372a, none, none, none }, /* * */
1088 { none, none, none, none, none }, /* L Alt */
1089 { 0x3920, 0x3920, 0x3920, 0x3920, none }, /* space */
1090 { none, none, none, none, none }, /* caps lock */
1091 { 0x3b00, 0x5400, 0x5e00, 0x6800, none }, /* F1 */
1092 { 0x3c00, 0x5500, 0x5f00, 0x6900, none }, /* F2 */
1093 { 0x3d00, 0x5600, 0x6000, 0x6a00, none }, /* F3 */
1094 { 0x3e00, 0x5700, 0x6100, 0x6b00, none }, /* F4 */
1095 { 0x3f00, 0x5800, 0x6200, 0x6c00, none }, /* F5 */
1096 { 0x4000, 0x5900, 0x6300, 0x6d00, none }, /* F6 */
1097 { 0x4100, 0x5a00, 0x6400, 0x6e00, none }, /* F7 */
1098 { 0x4200, 0x5b00, 0x6500, 0x6f00, none }, /* F8 */
1099 { 0x4300, 0x5c00, 0x6600, 0x7000, none }, /* F9 */
1100 { 0x4400, 0x5d00, 0x6700, 0x7100, none }, /* F10 */
1101 { none, none, none, none, none }, /* Num Lock */
1102 { none, none, none, none, none }, /* Scroll Lock */
1103 { 0x4700, 0x4737, 0x7700, none, 0x20 }, /* 7 Home */
1104 { 0x4800, 0x4838, none, none, 0x20 }, /* 8 UP */
1105 { 0x4900, 0x4939, 0x8400, none, 0x20 }, /* 9 PgUp */
1106 { 0x4a2d, 0x4a2d, none, none, none }, /* - */
1107 { 0x4b00, 0x4b34, 0x7300, none, 0x20 }, /* 4 Left */
1108 { 0x4c00, 0x4c35, none, none, 0x20 }, /* 5 */
1109 { 0x4d00, 0x4d36, 0x7400, none, 0x20 }, /* 6 Right */
1110 { 0x4e2b, 0x4e2b, none, none, none }, /* + */
1111 { 0x4f00, 0x4f31, 0x7500, none, 0x20 }, /* 1 End */
1112 { 0x5000, 0x5032, none, none, 0x20 }, /* 2 Down */
1113 { 0x5100, 0x5133, 0x7600, none, 0x20 }, /* 3 PgDn */
1114 { 0x5200, 0x5230, none, none, 0x20 }, /* 0 Ins */
1115 { 0x5300, 0x532e, none, none, 0x20 } /* Del */
1118 Bit8u
1119 inb(port)
1120 Bit16u port;
1122 ASM_START
1123 push bp
1124 mov bp, sp
1126 push dx
1127 mov dx, 4[bp]
1128 in al, dx
1129 pop dx
1131 pop bp
1132 ASM_END
1135 #if BX_USE_ATADRV
1136 Bit16u
1137 inw(port)
1138 Bit16u port;
1140 ASM_START
1141 push bp
1142 mov bp, sp
1144 push dx
1145 mov dx, 4[bp]
1146 in ax, dx
1147 pop dx
1149 pop bp
1150 ASM_END
1152 #endif
1154 void
1155 outb(port, val)
1156 Bit16u port;
1157 Bit8u val;
1159 ASM_START
1160 push bp
1161 mov bp, sp
1163 push ax
1164 push dx
1165 mov dx, 4[bp]
1166 mov al, 6[bp]
1167 out dx, al
1168 pop dx
1169 pop ax
1171 pop bp
1172 ASM_END
1175 #if BX_USE_ATADRV
1176 void
1177 outw(port, val)
1178 Bit16u port;
1179 Bit16u val;
1181 ASM_START
1182 push bp
1183 mov bp, sp
1185 push ax
1186 push dx
1187 mov dx, 4[bp]
1188 mov ax, 6[bp]
1189 out dx, ax
1190 pop dx
1191 pop ax
1193 pop bp
1194 ASM_END
1196 #endif
1198 void
1199 outb_cmos(cmos_reg, val)
1200 Bit8u cmos_reg;
1201 Bit8u val;
1203 ASM_START
1204 push bp
1205 mov bp, sp
1207 mov al, 4[bp] ;; cmos_reg
1208 out 0x70, al
1209 mov al, 6[bp] ;; val
1210 out 0x71, al
1212 pop bp
1213 ASM_END
1216 Bit8u
1217 inb_cmos(cmos_reg)
1218 Bit8u cmos_reg;
1220 ASM_START
1221 push bp
1222 mov bp, sp
1224 mov al, 4[bp] ;; cmos_reg
1225 out 0x70, al
1226 in al, 0x71
1228 pop bp
1229 ASM_END
1232 void
1233 init_rtc()
1235 outb_cmos(0x0a, 0x26);
1236 outb_cmos(0x0b, 0x02);
1237 inb_cmos(0x0c);
1238 inb_cmos(0x0d);
1241 bx_bool
1242 rtc_updating()
1244 // This function checks to see if the update-in-progress bit
1245 // is set in CMOS Status Register A. If not, it returns 0.
1246 // If it is set, it tries to wait until there is a transition
1247 // to 0, and will return 0 if such a transition occurs. A 1
1248 // is returned only after timing out. The maximum period
1249 // that this bit should be set is constrained to 244useconds.
1250 // The count I use below guarantees coverage or more than
1251 // this time, with any reasonable IPS setting.
1253 Bit16u count;
1255 count = 25000;
1256 while (--count != 0) {
1257 if ( (inb_cmos(0x0a) & 0x80) == 0 )
1258 return(0);
1260 return(1); // update-in-progress never transitioned to 0
1264 Bit8u
1265 read_byte(seg, offset)
1266 Bit16u seg;
1267 Bit16u offset;
1269 ASM_START
1270 push bp
1271 mov bp, sp
1273 push bx
1274 push ds
1275 mov ax, 4[bp] ; segment
1276 mov ds, ax
1277 mov bx, 6[bp] ; offset
1278 mov al, [bx]
1279 ;; al = return value (byte)
1280 pop ds
1281 pop bx
1283 pop bp
1284 ASM_END
1287 Bit16u
1288 read_word(seg, offset)
1289 Bit16u seg;
1290 Bit16u offset;
1292 ASM_START
1293 push bp
1294 mov bp, sp
1296 push bx
1297 push ds
1298 mov ax, 4[bp] ; segment
1299 mov ds, ax
1300 mov bx, 6[bp] ; offset
1301 mov ax, [bx]
1302 ;; ax = return value (word)
1303 pop ds
1304 pop bx
1306 pop bp
1307 ASM_END
1310 void
1311 write_byte(seg, offset, data)
1312 Bit16u seg;
1313 Bit16u offset;
1314 Bit8u data;
1316 ASM_START
1317 push bp
1318 mov bp, sp
1320 push ax
1321 push bx
1322 push ds
1323 mov ax, 4[bp] ; segment
1324 mov ds, ax
1325 mov bx, 6[bp] ; offset
1326 mov al, 8[bp] ; data byte
1327 mov [bx], al ; write data byte
1328 pop ds
1329 pop bx
1330 pop ax
1332 pop bp
1333 ASM_END
1336 void
1337 write_word(seg, offset, data)
1338 Bit16u seg;
1339 Bit16u offset;
1340 Bit16u data;
1342 ASM_START
1343 push bp
1344 mov bp, sp
1346 push ax
1347 push bx
1348 push ds
1349 mov ax, 4[bp] ; segment
1350 mov ds, ax
1351 mov bx, 6[bp] ; offset
1352 mov ax, 8[bp] ; data word
1353 mov [bx], ax ; write data word
1354 pop ds
1355 pop bx
1356 pop ax
1358 pop bp
1359 ASM_END
1362 // Bit16u
1363 //get_DS()
1365 //ASM_START
1366 // mov ax, ds
1367 //ASM_END
1370 // void
1371 //set_DS(ds_selector)
1372 // Bit16u ds_selector;
1374 //ASM_START
1375 // push bp
1376 // mov bp, sp
1378 // push ax
1379 // mov ax, 4[bp] ; ds_selector
1380 // mov ds, ax
1381 // pop ax
1383 // pop bp
1384 //ASM_END
1387 Bit16u
1388 get_SS()
1390 ASM_START
1391 mov ax, ss
1392 ASM_END
1395 void
1396 wrch(c)
1397 Bit8u c;
1399 ASM_START
1400 push bp
1401 mov bp, sp
1403 push bx
1404 mov ah, #$0e
1405 mov al, 4[bp]
1406 xor bx,bx
1407 int #$10
1408 pop bx
1410 pop bp
1411 ASM_END
1414 void
1415 send(action, c)
1416 Bit16u action;
1417 Bit8u c;
1419 if (action & BIOS_PRINTF_DEBUG) outb(DEBUG_PORT, c);
1420 if (action & BIOS_PRINTF_INFO) outb(INFO_PORT, c);
1421 if (action & BIOS_PRINTF_SCREEN) {
1422 if (c == '\n') wrch('\r');
1423 wrch(c);
1427 void
1428 put_int(action, val, width, neg)
1429 Bit16u action;
1430 short val, width;
1431 bx_bool neg;
1433 short nval = val / 10;
1434 if (nval)
1435 put_int(action, nval, width - 1, neg);
1436 else {
1437 while (--width > 0) send(action, ' ');
1438 if (neg) send(action, '-');
1440 send(action, val - (nval * 10) + '0');
1443 void
1444 put_uint(action, val, width, neg)
1445 Bit16u action;
1446 unsigned short val;
1447 short width;
1448 bx_bool neg;
1450 unsigned short nval = val / 10;
1451 if (nval)
1452 put_uint(action, nval, width - 1, neg);
1453 else {
1454 while (--width > 0) send(action, ' ');
1455 if (neg) send(action, '-');
1457 send(action, val - (nval * 10) + '0');
1460 //--------------------------------------------------------------------------
1461 // bios_printf()
1462 // A compact variable argument printf function which prints its output via
1463 // an I/O port so that it can be logged by Bochs/Plex.
1464 // Currently, only %x is supported (or %02x, %04x, etc).
1466 // Supports %[format_width][format]
1467 // where format can be d,x,c,s
1468 //--------------------------------------------------------------------------
1469 void
1470 bios_printf(action, s)
1471 Bit16u action;
1472 Bit8u *s;
1474 Bit8u c, format_char;
1475 bx_bool in_format;
1476 short i;
1477 Bit16u *arg_ptr;
1478 Bit16u arg_seg, arg, nibble, shift_count, format_width;
1480 arg_ptr = &s;
1481 arg_seg = get_SS();
1483 in_format = 0;
1484 format_width = 0;
1486 if ((action & BIOS_PRINTF_DEBHALT) == BIOS_PRINTF_DEBHALT) {
1487 outb(PANIC_PORT2, 0x00);
1488 bios_printf (BIOS_PRINTF_SCREEN, "FATAL: ");
1491 while (c = read_byte(0xf000, s)) {
1492 if ( c == '%' ) {
1493 in_format = 1;
1494 format_width = 0;
1496 else if (in_format) {
1497 if ( (c>='0') && (c<='9') ) {
1498 format_width = (format_width * 10) + (c - '0');
1500 else {
1501 arg_ptr++; // increment to next arg
1502 arg = read_word(arg_seg, arg_ptr);
1503 if (c == 'x') {
1504 if (format_width == 0)
1505 format_width = 4;
1506 for (i=format_width-1; i>=0; i--) {
1507 nibble = (arg >> (4 * i)) & 0x000f;
1508 send (action, (nibble<=9)? (nibble+'0') : (nibble-10+'A'));
1511 else if (c == 'u') {
1512 put_uint(action, arg, format_width, 0);
1514 else if (c == 'd') {
1515 if (arg & 0x8000)
1516 put_int(action, -arg, format_width - 1, 1);
1517 else
1518 put_int(action, arg, format_width, 0);
1520 else if (c == 's') {
1521 bios_printf(action & (~BIOS_PRINTF_HALT), arg);
1523 else if (c == 'c') {
1524 send(action, arg);
1526 else
1527 BX_PANIC("bios_printf: unknown format\n");
1528 in_format = 0;
1531 else {
1532 send(action, c);
1534 s ++;
1537 if (action & BIOS_PRINTF_HALT) {
1538 // freeze in a busy loop.
1539 ASM_START
1541 halt2_loop:
1543 jmp halt2_loop
1544 ASM_END
1548 //--------------------------------------------------------------------------
1549 // keyboard_init
1550 //--------------------------------------------------------------------------
1551 // this file is based on LinuxBIOS implementation of keyboard.c
1552 // could convert to #asm to gain space
1553 void
1554 keyboard_init()
1556 Bit16u max;
1558 /* ------------------- Flush buffers ------------------------*/
1559 /* Wait until buffer is empty */
1560 max=0xffff;
1561 while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
1563 /* flush incoming keys */
1564 max=0x2000;
1565 while (--max > 0) {
1566 outb(0x80, 0x00);
1567 if (inb(0x64) & 0x01) {
1568 inb(0x60);
1569 max = 0x2000;
1573 // Due to timer issues, and if the IPS setting is > 15000000,
1574 // the incoming keys might not be flushed here. That will
1575 // cause a panic a few lines below. See sourceforge bug report :
1576 // [ 642031 ] FATAL: Keyboard RESET error:993
1578 /* ------------------- controller side ----------------------*/
1579 /* send cmd = 0xAA, self test 8042 */
1580 outb(0x64, 0xaa);
1582 /* Wait until buffer is empty */
1583 max=0xffff;
1584 while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
1585 if (max==0x0) keyboard_panic(00);
1587 /* Wait for data */
1588 max=0xffff;
1589 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x01);
1590 if (max==0x0) keyboard_panic(01);
1592 /* read self-test result, 0x55 should be returned from 0x60 */
1593 if ((inb(0x60) != 0x55)){
1594 keyboard_panic(991);
1597 /* send cmd = 0xAB, keyboard interface test */
1598 outb(0x64,0xab);
1600 /* Wait until buffer is empty */
1601 max=0xffff;
1602 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x10);
1603 if (max==0x0) keyboard_panic(10);
1605 /* Wait for data */
1606 max=0xffff;
1607 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x11);
1608 if (max==0x0) keyboard_panic(11);
1610 /* read keyboard interface test result, */
1611 /* 0x00 should be returned form 0x60 */
1612 if ((inb(0x60) != 0x00)) {
1613 keyboard_panic(992);
1616 /* Enable Keyboard clock */
1617 outb(0x64,0xae);
1618 outb(0x64,0xa8);
1620 /* ------------------- keyboard side ------------------------*/
1621 /* reset kerboard and self test (keyboard side) */
1622 outb(0x60, 0xff);
1624 /* Wait until buffer is empty */
1625 max=0xffff;
1626 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x20);
1627 if (max==0x0) keyboard_panic(20);
1629 /* Wait for data */
1630 max=0xffff;
1631 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x21);
1632 if (max==0x0) keyboard_panic(21);
1634 /* keyboard should return ACK */
1635 if ((inb(0x60) != 0xfa)) {
1636 keyboard_panic(993);
1639 /* Wait for data */
1640 max=0xffff;
1641 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x31);
1642 if (max==0x0) keyboard_panic(31);
1644 if ((inb(0x60) != 0xaa)) {
1645 keyboard_panic(994);
1648 /* Disable keyboard */
1649 outb(0x60, 0xf5);
1651 /* Wait until buffer is empty */
1652 max=0xffff;
1653 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x40);
1654 if (max==0x0) keyboard_panic(40);
1656 /* Wait for data */
1657 max=0xffff;
1658 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x41);
1659 if (max==0x0) keyboard_panic(41);
1661 /* keyboard should return ACK */
1662 if ((inb(0x60) != 0xfa)) {
1663 keyboard_panic(995);
1666 /* Write Keyboard Mode */
1667 outb(0x64, 0x60);
1669 /* Wait until buffer is empty */
1670 max=0xffff;
1671 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x50);
1672 if (max==0x0) keyboard_panic(50);
1674 /* send cmd: scan code convert, disable mouse, enable IRQ 1 */
1675 outb(0x60, 0x61);
1677 /* Wait until buffer is empty */
1678 max=0xffff;
1679 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x60);
1680 if (max==0x0) keyboard_panic(60);
1682 /* Enable keyboard */
1683 outb(0x60, 0xf4);
1685 /* Wait until buffer is empty */
1686 max=0xffff;
1687 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x70);
1688 if (max==0x0) keyboard_panic(70);
1690 /* Wait for data */
1691 max=0xffff;
1692 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x71);
1693 if (max==0x0) keyboard_panic(70);
1695 /* keyboard should return ACK */
1696 if ((inb(0x60) != 0xfa)) {
1697 keyboard_panic(996);
1700 outb(0x80, 0x77);
1703 //--------------------------------------------------------------------------
1704 // keyboard_panic
1705 //--------------------------------------------------------------------------
1706 void
1707 keyboard_panic(status)
1708 Bit16u status;
1710 // If you're getting a 993 keyboard panic here,
1711 // please see the comment in keyboard_init
1713 BX_PANIC("Keyboard error:%u\n",status);
1716 //--------------------------------------------------------------------------
1717 // shutdown_status_panic
1718 // called when the shutdown statsu is not implemented, displays the status
1719 //--------------------------------------------------------------------------
1720 void
1721 shutdown_status_panic(status)
1722 Bit16u status;
1724 BX_PANIC("Unimplemented shutdown status: %02x\n",(Bit8u)status);
1727 //--------------------------------------------------------------------------
1728 // print_bios_banner
1729 // displays a the bios version
1730 //--------------------------------------------------------------------------
1731 void
1732 print_bios_banner()
1734 printf(BX_APPNAME" BIOS, %d cpu%s, ", BX_SMP_PROCESSORS, BX_SMP_PROCESSORS>1?"s":"");
1735 printf("%s %s\n", bios_cvs_version_string, bios_date_string);
1736 printf("\n");
1739 //--------------------------------------------------------------------------
1740 // print_boot_device
1741 // displays the boot device
1742 //--------------------------------------------------------------------------
1744 static char drivetypes[][10]={"Floppy","Hard Disk","CD-Rom"};
1746 void
1747 print_boot_device(cdboot, drive)
1748 Bit8u cdboot; Bit16u drive;
1750 Bit8u i;
1752 // cdboot contains 0 if floppy/harddisk, 1 otherwise
1753 // drive contains real/emulated boot drive
1755 if(cdboot)i=2; // CD-Rom
1756 else if((drive&0x0080)==0x00)i=0; // Floppy
1757 else if((drive&0x0080)==0x80)i=1; // Hard drive
1758 else return;
1760 printf("Booting from %s...\n",drivetypes[i]);
1763 //--------------------------------------------------------------------------
1764 // print_boot_failure
1765 // displays the reason why boot failed
1766 //--------------------------------------------------------------------------
1767 void
1768 print_boot_failure(cdboot, drive, reason)
1769 Bit8u cdboot; Bit8u drive;
1771 Bit16u drivenum = drive&0x7f;
1773 // cdboot: 1 if boot from cd, 0 otherwise
1774 // drive : drive number
1775 // reason: 0 signature check failed, 1 read error
1777 if (cdboot)
1778 bios_printf(BIOS_PRINTF_INFO | BIOS_PRINTF_SCREEN, "Boot from %s failed\n",drivetypes[2]);
1779 else if (drive & 0x80)
1780 bios_printf(BIOS_PRINTF_INFO | BIOS_PRINTF_SCREEN, "Boot from %s %d failed\n", drivetypes[1],drivenum);
1781 else
1782 bios_printf(BIOS_PRINTF_INFO | BIOS_PRINTF_SCREEN, "Boot from %s %d failed\n", drivetypes[0],drivenum);
1784 if (reason==0)
1785 BX_PANIC("Not a bootable disk\n");
1786 else
1787 BX_PANIC("Could not read the boot disk\n");
1790 //--------------------------------------------------------------------------
1791 // print_cdromboot_failure
1792 // displays the reason why boot failed
1793 //--------------------------------------------------------------------------
1794 void
1795 print_cdromboot_failure( code )
1796 Bit16u code;
1798 bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, "CDROM boot failure code : %04x\n",code);
1800 return;
1803 void
1804 nmi_handler_msg()
1806 BX_PANIC("NMI Handler called\n");
1809 void
1810 int18_panic_msg()
1812 BX_PANIC("INT18: BOOT FAILURE\n");
1815 void
1816 log_bios_start()
1818 BX_INFO("%s\n", bios_version_string);
1821 bx_bool
1822 set_enable_a20(val)
1823 bx_bool val;
1825 Bit8u oldval;
1827 // Use PS2 System Control port A to set A20 enable
1829 // get current setting first
1830 oldval = inb(0x92);
1832 // change A20 status
1833 if (val)
1834 outb(0x92, oldval | 0x02);
1835 else
1836 outb(0x92, oldval & 0xfd);
1838 return((oldval & 0x02) != 0);
1841 void
1842 debugger_on()
1844 outb(0xfedc, 0x01);
1847 void
1848 debugger_off()
1850 outb(0xfedc, 0x00);
1853 #if BX_USE_ATADRV
1855 // ---------------------------------------------------------------------------
1856 // Start of ATA/ATAPI Driver
1857 // ---------------------------------------------------------------------------
1859 // Global defines -- ATA register and register bits.
1860 // command block & control block regs
1861 #define ATA_CB_DATA 0 // data reg in/out pio_base_addr1+0
1862 #define ATA_CB_ERR 1 // error in pio_base_addr1+1
1863 #define ATA_CB_FR 1 // feature reg out pio_base_addr1+1
1864 #define ATA_CB_SC 2 // sector count in/out pio_base_addr1+2
1865 #define ATA_CB_SN 3 // sector number in/out pio_base_addr1+3
1866 #define ATA_CB_CL 4 // cylinder low in/out pio_base_addr1+4
1867 #define ATA_CB_CH 5 // cylinder high in/out pio_base_addr1+5
1868 #define ATA_CB_DH 6 // device head in/out pio_base_addr1+6
1869 #define ATA_CB_STAT 7 // primary status in pio_base_addr1+7
1870 #define ATA_CB_CMD 7 // command out pio_base_addr1+7
1871 #define ATA_CB_ASTAT 6 // alternate status in pio_base_addr2+6
1872 #define ATA_CB_DC 6 // device control out pio_base_addr2+6
1873 #define ATA_CB_DA 7 // device address in pio_base_addr2+7
1875 #define ATA_CB_ER_ICRC 0x80 // ATA Ultra DMA bad CRC
1876 #define ATA_CB_ER_BBK 0x80 // ATA bad block
1877 #define ATA_CB_ER_UNC 0x40 // ATA uncorrected error
1878 #define ATA_CB_ER_MC 0x20 // ATA media change
1879 #define ATA_CB_ER_IDNF 0x10 // ATA id not found
1880 #define ATA_CB_ER_MCR 0x08 // ATA media change request
1881 #define ATA_CB_ER_ABRT 0x04 // ATA command aborted
1882 #define ATA_CB_ER_NTK0 0x02 // ATA track 0 not found
1883 #define ATA_CB_ER_NDAM 0x01 // ATA address mark not found
1885 #define ATA_CB_ER_P_SNSKEY 0xf0 // ATAPI sense key (mask)
1886 #define ATA_CB_ER_P_MCR 0x08 // ATAPI Media Change Request
1887 #define ATA_CB_ER_P_ABRT 0x04 // ATAPI command abort
1888 #define ATA_CB_ER_P_EOM 0x02 // ATAPI End of Media
1889 #define ATA_CB_ER_P_ILI 0x01 // ATAPI Illegal Length Indication
1891 // ATAPI Interrupt Reason bits in the Sector Count reg (CB_SC)
1892 #define ATA_CB_SC_P_TAG 0xf8 // ATAPI tag (mask)
1893 #define ATA_CB_SC_P_REL 0x04 // ATAPI release
1894 #define ATA_CB_SC_P_IO 0x02 // ATAPI I/O
1895 #define ATA_CB_SC_P_CD 0x01 // ATAPI C/D
1897 // bits 7-4 of the device/head (CB_DH) reg
1898 #define ATA_CB_DH_DEV0 0xa0 // select device 0
1899 #define ATA_CB_DH_DEV1 0xb0 // select device 1
1901 // status reg (CB_STAT and CB_ASTAT) bits
1902 #define ATA_CB_STAT_BSY 0x80 // busy
1903 #define ATA_CB_STAT_RDY 0x40 // ready
1904 #define ATA_CB_STAT_DF 0x20 // device fault
1905 #define ATA_CB_STAT_WFT 0x20 // write fault (old name)
1906 #define ATA_CB_STAT_SKC 0x10 // seek complete
1907 #define ATA_CB_STAT_SERV 0x10 // service
1908 #define ATA_CB_STAT_DRQ 0x08 // data request
1909 #define ATA_CB_STAT_CORR 0x04 // corrected
1910 #define ATA_CB_STAT_IDX 0x02 // index
1911 #define ATA_CB_STAT_ERR 0x01 // error (ATA)
1912 #define ATA_CB_STAT_CHK 0x01 // check (ATAPI)
1914 // device control reg (CB_DC) bits
1915 #define ATA_CB_DC_HD15 0x08 // bit should always be set to one
1916 #define ATA_CB_DC_SRST 0x04 // soft reset
1917 #define ATA_CB_DC_NIEN 0x02 // disable interrupts
1919 // Most mandtory and optional ATA commands (from ATA-3),
1920 #define ATA_CMD_CFA_ERASE_SECTORS 0xC0
1921 #define ATA_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03
1922 #define ATA_CMD_CFA_TRANSLATE_SECTOR 0x87
1923 #define ATA_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD
1924 #define ATA_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38
1925 #define ATA_CMD_CHECK_POWER_MODE1 0xE5
1926 #define ATA_CMD_CHECK_POWER_MODE2 0x98
1927 #define ATA_CMD_DEVICE_RESET 0x08
1928 #define ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90
1929 #define ATA_CMD_FLUSH_CACHE 0xE7
1930 #define ATA_CMD_FORMAT_TRACK 0x50
1931 #define ATA_CMD_IDENTIFY_DEVICE 0xEC
1932 #define ATA_CMD_IDENTIFY_DEVICE_PACKET 0xA1
1933 #define ATA_CMD_IDENTIFY_PACKET_DEVICE 0xA1
1934 #define ATA_CMD_IDLE1 0xE3
1935 #define ATA_CMD_IDLE2 0x97
1936 #define ATA_CMD_IDLE_IMMEDIATE1 0xE1
1937 #define ATA_CMD_IDLE_IMMEDIATE2 0x95
1938 #define ATA_CMD_INITIALIZE_DRIVE_PARAMETERS 0x91
1939 #define ATA_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91
1940 #define ATA_CMD_NOP 0x00
1941 #define ATA_CMD_PACKET 0xA0
1942 #define ATA_CMD_READ_BUFFER 0xE4
1943 #define ATA_CMD_READ_DMA 0xC8
1944 #define ATA_CMD_READ_DMA_QUEUED 0xC7
1945 #define ATA_CMD_READ_MULTIPLE 0xC4
1946 #define ATA_CMD_READ_SECTORS 0x20
1947 #define ATA_CMD_READ_VERIFY_SECTORS 0x40
1948 #define ATA_CMD_RECALIBRATE 0x10
1949 #define ATA_CMD_SEEK 0x70
1950 #define ATA_CMD_SET_FEATURES 0xEF
1951 #define ATA_CMD_SET_MULTIPLE_MODE 0xC6
1952 #define ATA_CMD_SLEEP1 0xE6
1953 #define ATA_CMD_SLEEP2 0x99
1954 #define ATA_CMD_STANDBY1 0xE2
1955 #define ATA_CMD_STANDBY2 0x96
1956 #define ATA_CMD_STANDBY_IMMEDIATE1 0xE0
1957 #define ATA_CMD_STANDBY_IMMEDIATE2 0x94
1958 #define ATA_CMD_WRITE_BUFFER 0xE8
1959 #define ATA_CMD_WRITE_DMA 0xCA
1960 #define ATA_CMD_WRITE_DMA_QUEUED 0xCC
1961 #define ATA_CMD_WRITE_MULTIPLE 0xC5
1962 #define ATA_CMD_WRITE_SECTORS 0x30
1963 #define ATA_CMD_WRITE_VERIFY 0x3C
1965 #define ATA_IFACE_NONE 0x00
1966 #define ATA_IFACE_ISA 0x00
1967 #define ATA_IFACE_PCI 0x01
1969 #define ATA_TYPE_NONE 0x00
1970 #define ATA_TYPE_UNKNOWN 0x01
1971 #define ATA_TYPE_ATA 0x02
1972 #define ATA_TYPE_ATAPI 0x03
1974 #define ATA_DEVICE_NONE 0x00
1975 #define ATA_DEVICE_HD 0xFF
1976 #define ATA_DEVICE_CDROM 0x05
1978 #define ATA_MODE_NONE 0x00
1979 #define ATA_MODE_PIO16 0x00
1980 #define ATA_MODE_PIO32 0x01
1981 #define ATA_MODE_ISADMA 0x02
1982 #define ATA_MODE_PCIDMA 0x03
1983 #define ATA_MODE_USEIRQ 0x10
1985 #define ATA_TRANSLATION_NONE 0
1986 #define ATA_TRANSLATION_LBA 1
1987 #define ATA_TRANSLATION_LARGE 2
1988 #define ATA_TRANSLATION_RECHS 3
1990 #define ATA_DATA_NO 0x00
1991 #define ATA_DATA_IN 0x01
1992 #define ATA_DATA_OUT 0x02
1994 // ---------------------------------------------------------------------------
1995 // ATA/ATAPI driver : initialization
1996 // ---------------------------------------------------------------------------
1997 void ata_init( )
1999 Bit16u ebda_seg=read_word(0x0040,0x000E);
2000 Bit8u channel, device;
2002 // Channels info init.
2003 for (channel=0; channel<BX_MAX_ATA_INTERFACES; channel++) {
2004 write_byte(ebda_seg,&EbdaData->ata.channels[channel].iface,ATA_IFACE_NONE);
2005 write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1,0x0);
2006 write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2,0x0);
2007 write_byte(ebda_seg,&EbdaData->ata.channels[channel].irq,0);
2010 // Devices info init.
2011 for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2012 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
2013 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_NONE);
2014 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable,0);
2015 write_byte(ebda_seg,&EbdaData->ata.devices[device].lock,0);
2016 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode,ATA_MODE_NONE);
2017 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize,0);
2018 write_byte(ebda_seg,&EbdaData->ata.devices[device].translation,ATA_TRANSLATION_NONE);
2019 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads,0);
2020 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders,0);
2021 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt,0);
2022 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads,0);
2023 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders,0);
2024 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt,0);
2026 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors,0L);
2029 // hdidmap and cdidmap init.
2030 for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2031 write_byte(ebda_seg,&EbdaData->ata.hdidmap[device],BX_MAX_ATA_DEVICES);
2032 write_byte(ebda_seg,&EbdaData->ata.cdidmap[device],BX_MAX_ATA_DEVICES);
2035 write_byte(ebda_seg,&EbdaData->ata.hdcount,0);
2036 write_byte(ebda_seg,&EbdaData->ata.cdcount,0);
2039 // ---------------------------------------------------------------------------
2040 // ATA/ATAPI driver : device detection
2041 // ---------------------------------------------------------------------------
2043 void ata_detect( )
2045 Bit16u ebda_seg=read_word(0x0040,0x000E);
2046 Bit8u hdcount, cdcount, device, type;
2047 Bit8u buffer[0x0200];
2049 #if BX_MAX_ATA_INTERFACES > 0
2050 write_byte(ebda_seg,&EbdaData->ata.channels[0].iface,ATA_IFACE_ISA);
2051 write_word(ebda_seg,&EbdaData->ata.channels[0].iobase1,0x1f0);
2052 write_word(ebda_seg,&EbdaData->ata.channels[0].iobase2,0x3f0);
2053 write_byte(ebda_seg,&EbdaData->ata.channels[0].irq,14);
2054 #endif
2055 #if BX_MAX_ATA_INTERFACES > 1
2056 write_byte(ebda_seg,&EbdaData->ata.channels[1].iface,ATA_IFACE_ISA);
2057 write_word(ebda_seg,&EbdaData->ata.channels[1].iobase1,0x170);
2058 write_word(ebda_seg,&EbdaData->ata.channels[1].iobase2,0x370);
2059 write_byte(ebda_seg,&EbdaData->ata.channels[1].irq,15);
2060 #endif
2061 #if BX_MAX_ATA_INTERFACES > 2
2062 write_byte(ebda_seg,&EbdaData->ata.channels[2].iface,ATA_IFACE_ISA);
2063 write_word(ebda_seg,&EbdaData->ata.channels[2].iobase1,0x1e8);
2064 write_word(ebda_seg,&EbdaData->ata.channels[2].iobase2,0x3e0);
2065 write_byte(ebda_seg,&EbdaData->ata.channels[2].irq,12);
2066 #endif
2067 #if BX_MAX_ATA_INTERFACES > 3
2068 write_byte(ebda_seg,&EbdaData->ata.channels[3].iface,ATA_IFACE_ISA);
2069 write_word(ebda_seg,&EbdaData->ata.channels[3].iobase1,0x168);
2070 write_word(ebda_seg,&EbdaData->ata.channels[3].iobase2,0x360);
2071 write_byte(ebda_seg,&EbdaData->ata.channels[3].irq,11);
2072 #endif
2073 #if BX_MAX_ATA_INTERFACES > 4
2074 #error Please fill the ATA interface informations
2075 #endif
2077 // Device detection
2078 hdcount=cdcount=0;
2080 for(device=0; device<BX_MAX_ATA_DEVICES; device++) {
2081 Bit16u iobase1, iobase2;
2082 Bit8u channel, slave, shift;
2083 Bit8u sc, sn, cl, ch, st;
2085 channel = device / 2;
2086 slave = device % 2;
2088 iobase1 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1);
2089 iobase2 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2);
2091 // Disable interrupts
2092 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2094 // Look for device
2095 outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2096 outb(iobase1+ATA_CB_SC, 0x55);
2097 outb(iobase1+ATA_CB_SN, 0xaa);
2098 outb(iobase1+ATA_CB_SC, 0xaa);
2099 outb(iobase1+ATA_CB_SN, 0x55);
2100 outb(iobase1+ATA_CB_SC, 0x55);
2101 outb(iobase1+ATA_CB_SN, 0xaa);
2103 // If we found something
2104 sc = inb(iobase1+ATA_CB_SC);
2105 sn = inb(iobase1+ATA_CB_SN);
2107 if ( (sc == 0x55) && (sn == 0xaa) ) {
2108 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_UNKNOWN);
2110 // reset the channel
2111 ata_reset (device);
2113 // check for ATA or ATAPI
2114 outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2115 sc = inb(iobase1+ATA_CB_SC);
2116 sn = inb(iobase1+ATA_CB_SN);
2117 if ( (sc==0x01) && (sn==0x01) ) {
2118 cl = inb(iobase1+ATA_CB_CL);
2119 ch = inb(iobase1+ATA_CB_CH);
2120 st = inb(iobase1+ATA_CB_STAT);
2122 if ( (cl==0x14) && (ch==0xeb) ) {
2123 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATAPI);
2125 else if ( (cl==0x00) && (ch==0x00) && (st!=0x00) ) {
2126 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATA);
2131 type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type);
2133 // Now we send a IDENTIFY command to ATA device
2134 if(type == ATA_TYPE_ATA) {
2135 Bit32u sectors;
2136 Bit16u cylinders, heads, spt, blksize;
2137 Bit8u translation, removable, mode;
2139 //Temporary values to do the transfer
2140 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2141 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2143 if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE, 1, 0, 0, 0, 0L, get_SS(),buffer) !=0 )
2144 BX_PANIC("ata-detect: Failed to detect ATA device\n");
2146 removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2147 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2148 blksize = read_word(get_SS(),buffer+10);
2150 cylinders = read_word(get_SS(),buffer+(1*2)); // word 1
2151 heads = read_word(get_SS(),buffer+(3*2)); // word 3
2152 spt = read_word(get_SS(),buffer+(6*2)); // word 6
2154 sectors = read_dword(get_SS(),buffer+(60*2)); // word 60 and word 61
2156 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2157 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2158 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2159 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2160 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads, heads);
2161 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders, cylinders);
2162 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt, spt);
2163 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors, sectors);
2164 BX_INFO("ata%d-%d: PCHS=%u/%d/%d translation=", channel, slave,cylinders, heads, spt);
2166 translation = inb_cmos(0x39 + channel/2);
2167 for (shift=device%4; shift>0; shift--) translation >>= 2;
2168 translation &= 0x03;
2170 write_byte(ebda_seg,&EbdaData->ata.devices[device].translation, translation);
2172 switch (translation) {
2173 case ATA_TRANSLATION_NONE:
2174 BX_INFO("none");
2175 break;
2176 case ATA_TRANSLATION_LBA:
2177 BX_INFO("lba");
2178 break;
2179 case ATA_TRANSLATION_LARGE:
2180 BX_INFO("large");
2181 break;
2182 case ATA_TRANSLATION_RECHS:
2183 BX_INFO("r-echs");
2184 break;
2186 switch (translation) {
2187 case ATA_TRANSLATION_NONE:
2188 break;
2189 case ATA_TRANSLATION_LBA:
2190 spt = 63;
2191 sectors /= 63;
2192 heads = sectors / 1024;
2193 if (heads>128) heads = 255;
2194 else if (heads>64) heads = 128;
2195 else if (heads>32) heads = 64;
2196 else if (heads>16) heads = 32;
2197 else heads=16;
2198 cylinders = sectors / heads;
2199 break;
2200 case ATA_TRANSLATION_RECHS:
2201 // Take care not to overflow
2202 if (heads==16) {
2203 if(cylinders>61439) cylinders=61439;
2204 heads=15;
2205 cylinders = (Bit16u)((Bit32u)(cylinders)*16/15);
2207 // then go through the large bitshift process
2208 case ATA_TRANSLATION_LARGE:
2209 while(cylinders > 1024) {
2210 cylinders >>= 1;
2211 heads <<= 1;
2213 // If we max out the head count
2214 if (heads > 127) break;
2216 break;
2218 // clip to 1024 cylinders in lchs
2219 if (cylinders > 1024) cylinders=1024;
2220 BX_INFO(" LCHS=%d/%d/%d\n", cylinders, heads, spt);
2222 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads, heads);
2223 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders, cylinders);
2224 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt, spt);
2226 // fill hdidmap
2227 write_byte(ebda_seg,&EbdaData->ata.hdidmap[hdcount], device);
2228 hdcount++;
2231 // Now we send a IDENTIFY command to ATAPI device
2232 if(type == ATA_TYPE_ATAPI) {
2234 Bit8u type, removable, mode;
2235 Bit16u blksize;
2237 //Temporary values to do the transfer
2238 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_CDROM);
2239 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2241 if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE_PACKET, 1, 0, 0, 0, 0L, get_SS(),buffer) != 0)
2242 BX_PANIC("ata-detect: Failed to detect ATAPI device\n");
2244 type = read_byte(get_SS(),buffer+1) & 0x1f;
2245 removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2246 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2247 blksize = 2048;
2249 write_byte(ebda_seg,&EbdaData->ata.devices[device].device, type);
2250 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2251 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2252 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2254 // fill cdidmap
2255 write_byte(ebda_seg,&EbdaData->ata.cdidmap[cdcount], device);
2256 cdcount++;
2260 Bit32u sizeinmb;
2261 Bit16u ataversion;
2262 Bit8u c, i, version, model[41];
2264 switch (type) {
2265 case ATA_TYPE_ATA:
2266 sizeinmb = read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors);
2267 sizeinmb >>= 11;
2268 case ATA_TYPE_ATAPI:
2269 // Read ATA/ATAPI version
2270 ataversion=((Bit16u)(read_byte(get_SS(),buffer+161))<<8)|read_byte(get_SS(),buffer+160);
2271 for(version=15;version>0;version--) {
2272 if((ataversion&(1<<version))!=0)
2273 break;
2276 // Read model name
2277 for(i=0;i<20;i++){
2278 write_byte(get_SS(),model+(i*2),read_byte(get_SS(),buffer+(i*2)+54+1));
2279 write_byte(get_SS(),model+(i*2)+1,read_byte(get_SS(),buffer+(i*2)+54));
2282 // Reformat
2283 write_byte(get_SS(),model+40,0x00);
2284 for(i=39;i>0;i--){
2285 if(read_byte(get_SS(),model+i)==0x20)
2286 write_byte(get_SS(),model+i,0x00);
2287 else break;
2289 break;
2292 switch (type) {
2293 case ATA_TYPE_ATA:
2294 printf("ata%d %s: ",channel,slave?" slave":"master");
2295 i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
2296 printf(" ATA-%d Hard-Disk (%d MBytes)\n",version,(Bit16u)sizeinmb);
2297 break;
2298 case ATA_TYPE_ATAPI:
2299 printf("ata%d %s: ",channel,slave?" slave":"master");
2300 i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
2301 if(read_byte(ebda_seg,&EbdaData->ata.devices[device].device)==ATA_DEVICE_CDROM)
2302 printf(" ATAPI-%d CD-Rom/DVD-Rom\n",version);
2303 else
2304 printf(" ATAPI-%d Device\n",version);
2305 break;
2306 case ATA_TYPE_UNKNOWN:
2307 printf("ata%d %s: Unknown device\n",channel,slave?" slave":"master");
2308 break;
2313 // Store the devices counts
2314 write_byte(ebda_seg,&EbdaData->ata.hdcount, hdcount);
2315 write_byte(ebda_seg,&EbdaData->ata.cdcount, cdcount);
2316 write_byte(0x40,0x75, hdcount);
2318 printf("\n");
2320 // FIXME : should use bios=cmos|auto|disable bits
2321 // FIXME : should know about translation bits
2322 // FIXME : move hard_drive_post here
2326 // ---------------------------------------------------------------------------
2327 // ATA/ATAPI driver : software reset
2328 // ---------------------------------------------------------------------------
2329 // ATA-3
2330 // 8.2.1 Software reset - Device 0
2332 void ata_reset(device)
2333 Bit16u device;
2335 Bit16u ebda_seg=read_word(0x0040,0x000E);
2336 Bit16u iobase1, iobase2;
2337 Bit8u channel, slave, sn, sc;
2338 Bit16u max;
2340 channel = device / 2;
2341 slave = device % 2;
2343 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2344 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2346 // Reset
2348 // 8.2.1 (a) -- set SRST in DC
2349 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST);
2351 // 8.2.1 (b) -- wait for BSY
2352 max=0xff;
2353 while(--max>0) {
2354 Bit8u status = inb(iobase1+ATA_CB_STAT);
2355 if ((status & ATA_CB_STAT_BSY) != 0) break;
2358 // 8.2.1 (f) -- clear SRST
2359 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2361 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_NONE) {
2363 // 8.2.1 (g) -- check for sc==sn==0x01
2364 // select device
2365 outb(iobase1+ATA_CB_DH, slave?ATA_CB_DH_DEV1:ATA_CB_DH_DEV0);
2366 sc = inb(iobase1+ATA_CB_SC);
2367 sn = inb(iobase1+ATA_CB_SN);
2369 if ( (sc==0x01) && (sn==0x01) ) {
2371 // 8.2.1 (h) -- wait for not BSY
2372 max=0xff;
2373 while(--max>0) {
2374 Bit8u status = inb(iobase1+ATA_CB_STAT);
2375 if ((status & ATA_CB_STAT_BSY) == 0) break;
2380 // 8.2.1 (i) -- wait for DRDY
2381 max=0xfff;
2382 while(--max>0) {
2383 Bit8u status = inb(iobase1+ATA_CB_STAT);
2384 if ((status & ATA_CB_STAT_RDY) != 0) break;
2387 // Enable interrupts
2388 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2391 // ---------------------------------------------------------------------------
2392 // ATA/ATAPI driver : execute a non data command
2393 // ---------------------------------------------------------------------------
2395 Bit16u ata_cmd_non_data()
2396 {return 0;}
2398 // ---------------------------------------------------------------------------
2399 // ATA/ATAPI driver : execute a data-in command
2400 // ---------------------------------------------------------------------------
2401 // returns
2402 // 0 : no error
2403 // 1 : BUSY bit set
2404 // 2 : read error
2405 // 3 : expected DRQ=1
2406 // 4 : no sectors left to read/verify
2407 // 5 : more sectors to read/verify
2408 // 6 : no sectors left to write
2409 // 7 : more sectors to write
2410 Bit16u ata_cmd_data_in(device, command, count, cylinder, head, sector, lba, segment, offset)
2411 Bit16u device, command, count, cylinder, head, sector, segment, offset;
2412 Bit32u lba;
2414 Bit16u ebda_seg=read_word(0x0040,0x000E);
2415 Bit16u iobase1, iobase2, blksize;
2416 Bit8u channel, slave;
2417 Bit8u status, current, mode;
2419 channel = device / 2;
2420 slave = device % 2;
2422 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2423 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2424 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2425 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
2426 if (mode == ATA_MODE_PIO32) blksize>>=2;
2427 else blksize>>=1;
2429 // sector will be 0 only on lba access. Convert to lba-chs
2430 if (sector == 0) {
2431 sector = (Bit16u) (lba & 0x000000ffL);
2432 lba >>= 8;
2433 cylinder = (Bit16u) (lba & 0x0000ffffL);
2434 lba >>= 16;
2435 head = ((Bit16u) (lba & 0x0000000fL)) | 0x40;
2438 // Reset count of transferred data
2439 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2440 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2441 current = 0;
2443 status = inb(iobase1 + ATA_CB_STAT);
2444 if (status & ATA_CB_STAT_BSY) return 1;
2446 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2447 outb(iobase1 + ATA_CB_FR, 0x00);
2448 outb(iobase1 + ATA_CB_SC, count);
2449 outb(iobase1 + ATA_CB_SN, sector);
2450 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
2451 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
2452 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
2453 outb(iobase1 + ATA_CB_CMD, command);
2455 while (1) {
2456 status = inb(iobase1 + ATA_CB_STAT);
2457 if ( !(status & ATA_CB_STAT_BSY) ) break;
2460 if (status & ATA_CB_STAT_ERR) {
2461 BX_DEBUG_ATA("ata_cmd_data_in : read error\n");
2462 return 2;
2463 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2464 BX_DEBUG_ATA("ata_cmd_data_in : DRQ not set (status %02x)\n", (unsigned) status);
2465 return 3;
2468 // FIXME : move seg/off translation here
2470 ASM_START
2471 sti ;; enable higher priority interrupts
2472 ASM_END
2474 while (1) {
2476 ASM_START
2477 push bp
2478 mov bp, sp
2479 mov di, _ata_cmd_data_in.offset + 2[bp]
2480 mov ax, _ata_cmd_data_in.segment + 2[bp]
2481 mov cx, _ata_cmd_data_in.blksize + 2[bp]
2483 ;; adjust if there will be an overrun. 2K max sector size
2484 cmp di, #0xf800 ;;
2485 jbe ata_in_no_adjust
2487 ata_in_adjust:
2488 sub di, #0x0800 ;; sub 2 kbytes from offset
2489 add ax, #0x0080 ;; add 2 Kbytes to segment
2491 ata_in_no_adjust:
2492 mov es, ax ;; segment in es
2494 mov dx, _ata_cmd_data_in.iobase1 + 2[bp] ;; ATA data read port
2496 mov ah, _ata_cmd_data_in.mode + 2[bp]
2497 cmp ah, #ATA_MODE_PIO32
2498 je ata_in_32
2500 ata_in_16:
2502 insw ;; CX words transfered from port(DX) to ES:[DI]
2503 jmp ata_in_done
2505 ata_in_32:
2507 insd ;; CX dwords transfered from port(DX) to ES:[DI]
2509 ata_in_done:
2510 mov _ata_cmd_data_in.offset + 2[bp], di
2511 mov _ata_cmd_data_in.segment + 2[bp], es
2512 pop bp
2513 ASM_END
2515 current++;
2516 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
2517 count--;
2518 status = inb(iobase1 + ATA_CB_STAT);
2519 if (count == 0) {
2520 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2521 != ATA_CB_STAT_RDY ) {
2522 BX_DEBUG_ATA("ata_cmd_data_in : no sectors left (status %02x)\n", (unsigned) status);
2523 return 4;
2525 break;
2527 else {
2528 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2529 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2530 BX_DEBUG_ATA("ata_cmd_data_in : more sectors left (status %02x)\n", (unsigned) status);
2531 return 5;
2533 continue;
2536 // Enable interrupts
2537 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2538 return 0;
2541 // ---------------------------------------------------------------------------
2542 // ATA/ATAPI driver : execute a data-out command
2543 // ---------------------------------------------------------------------------
2544 // returns
2545 // 0 : no error
2546 // 1 : BUSY bit set
2547 // 2 : read error
2548 // 3 : expected DRQ=1
2549 // 4 : no sectors left to read/verify
2550 // 5 : more sectors to read/verify
2551 // 6 : no sectors left to write
2552 // 7 : more sectors to write
2553 Bit16u ata_cmd_data_out(device, command, count, cylinder, head, sector, lba, segment, offset)
2554 Bit16u device, command, count, cylinder, head, sector, segment, offset;
2555 Bit32u lba;
2557 Bit16u ebda_seg=read_word(0x0040,0x000E);
2558 Bit16u iobase1, iobase2, blksize;
2559 Bit8u channel, slave;
2560 Bit8u status, current, mode;
2562 channel = device / 2;
2563 slave = device % 2;
2565 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2566 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2567 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2568 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
2569 if (mode == ATA_MODE_PIO32) blksize>>=2;
2570 else blksize>>=1;
2572 // sector will be 0 only on lba access. Convert to lba-chs
2573 if (sector == 0) {
2574 sector = (Bit16u) (lba & 0x000000ffL);
2575 lba >>= 8;
2576 cylinder = (Bit16u) (lba & 0x0000ffffL);
2577 lba >>= 16;
2578 head = ((Bit16u) (lba & 0x0000000fL)) | 0x40;
2581 // Reset count of transferred data
2582 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2583 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2584 current = 0;
2586 status = inb(iobase1 + ATA_CB_STAT);
2587 if (status & ATA_CB_STAT_BSY) return 1;
2589 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2590 outb(iobase1 + ATA_CB_FR, 0x00);
2591 outb(iobase1 + ATA_CB_SC, count);
2592 outb(iobase1 + ATA_CB_SN, sector);
2593 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
2594 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
2595 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
2596 outb(iobase1 + ATA_CB_CMD, command);
2598 while (1) {
2599 status = inb(iobase1 + ATA_CB_STAT);
2600 if ( !(status & ATA_CB_STAT_BSY) ) break;
2603 if (status & ATA_CB_STAT_ERR) {
2604 BX_DEBUG_ATA("ata_cmd_data_out : read error\n");
2605 return 2;
2606 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2607 BX_DEBUG_ATA("ata_cmd_data_out : DRQ not set (status %02x)\n", (unsigned) status);
2608 return 3;
2611 // FIXME : move seg/off translation here
2613 ASM_START
2614 sti ;; enable higher priority interrupts
2615 ASM_END
2617 while (1) {
2619 ASM_START
2620 push bp
2621 mov bp, sp
2622 mov si, _ata_cmd_data_out.offset + 2[bp]
2623 mov ax, _ata_cmd_data_out.segment + 2[bp]
2624 mov cx, _ata_cmd_data_out.blksize + 2[bp]
2626 ;; adjust if there will be an overrun. 2K max sector size
2627 cmp si, #0xf800 ;;
2628 jbe ata_out_no_adjust
2630 ata_out_adjust:
2631 sub si, #0x0800 ;; sub 2 kbytes from offset
2632 add ax, #0x0080 ;; add 2 Kbytes to segment
2634 ata_out_no_adjust:
2635 mov es, ax ;; segment in es
2637 mov dx, _ata_cmd_data_out.iobase1 + 2[bp] ;; ATA data write port
2639 mov ah, _ata_cmd_data_out.mode + 2[bp]
2640 cmp ah, #ATA_MODE_PIO32
2641 je ata_out_32
2643 ata_out_16:
2644 seg ES
2646 outsw ;; CX words transfered from port(DX) to ES:[SI]
2647 jmp ata_out_done
2649 ata_out_32:
2650 seg ES
2652 outsd ;; CX dwords transfered from port(DX) to ES:[SI]
2654 ata_out_done:
2655 mov _ata_cmd_data_out.offset + 2[bp], si
2656 mov _ata_cmd_data_out.segment + 2[bp], es
2657 pop bp
2658 ASM_END
2660 current++;
2661 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
2662 count--;
2663 status = inb(iobase1 + ATA_CB_STAT);
2664 if (count == 0) {
2665 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2666 != ATA_CB_STAT_RDY ) {
2667 BX_DEBUG_ATA("ata_cmd_data_out : no sectors left (status %02x)\n", (unsigned) status);
2668 return 6;
2670 break;
2672 else {
2673 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2674 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2675 BX_DEBUG_ATA("ata_cmd_data_out : more sectors left (status %02x)\n", (unsigned) status);
2676 return 7;
2678 continue;
2681 // Enable interrupts
2682 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2683 return 0;
2686 // ---------------------------------------------------------------------------
2687 // ATA/ATAPI driver : execute a packet command
2688 // ---------------------------------------------------------------------------
2689 // returns
2690 // 0 : no error
2691 // 1 : error in parameters
2692 // 2 : BUSY bit set
2693 // 3 : error
2694 // 4 : not ready
2695 Bit16u ata_cmd_packet(device, cmdlen, cmdseg, cmdoff, header, length, inout, bufseg, bufoff)
2696 Bit8u cmdlen,inout;
2697 Bit16u device,cmdseg, cmdoff, bufseg, bufoff;
2698 Bit16u header;
2699 Bit32u length;
2701 Bit16u ebda_seg=read_word(0x0040,0x000E);
2702 Bit16u iobase1, iobase2;
2703 Bit16u lcount, lbefore, lafter, count;
2704 Bit8u channel, slave;
2705 Bit8u status, mode, lmode;
2706 Bit32u total, transfer;
2708 channel = device / 2;
2709 slave = device % 2;
2711 // Data out is not supported yet
2712 if (inout == ATA_DATA_OUT) {
2713 BX_INFO("ata_cmd_packet: DATA_OUT not supported yet");
2714 return 1;
2717 // The header length must be even
2718 if (header & 1) {
2719 BX_DEBUG_ATA("ata_cmd_packet : header must be even (%04x)\n",header);
2720 return 1;
2723 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2724 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2725 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2726 transfer= 0L;
2728 if (cmdlen < 12) cmdlen=12;
2729 if (cmdlen > 12) cmdlen=16;
2730 cmdlen>>=1;
2732 // Reset count of transferred data
2733 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2734 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2736 status = inb(iobase1 + ATA_CB_STAT);
2737 if (status & ATA_CB_STAT_BSY) return 2;
2739 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2740 // outb(iobase1 + ATA_CB_FR, 0x00);
2741 // outb(iobase1 + ATA_CB_SC, 0x00);
2742 // outb(iobase1 + ATA_CB_SN, 0x00);
2743 outb(iobase1 + ATA_CB_CL, 0xfff0 & 0x00ff);
2744 outb(iobase1 + ATA_CB_CH, 0xfff0 >> 8);
2745 outb(iobase1 + ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2746 outb(iobase1 + ATA_CB_CMD, ATA_CMD_PACKET);
2748 // Device should ok to receive command
2749 while (1) {
2750 status = inb(iobase1 + ATA_CB_STAT);
2751 if ( !(status & ATA_CB_STAT_BSY) ) break;
2754 if (status & ATA_CB_STAT_ERR) {
2755 BX_DEBUG_ATA("ata_cmd_packet : error, status is %02x\n",status);
2756 return 3;
2757 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2758 BX_DEBUG_ATA("ata_cmd_packet : DRQ not set (status %02x)\n", (unsigned) status);
2759 return 4;
2762 // Normalize address
2763 cmdseg += (cmdoff / 16);
2764 cmdoff %= 16;
2766 // Send command to device
2767 ASM_START
2768 sti ;; enable higher priority interrupts
2770 push bp
2771 mov bp, sp
2773 mov si, _ata_cmd_packet.cmdoff + 2[bp]
2774 mov ax, _ata_cmd_packet.cmdseg + 2[bp]
2775 mov cx, _ata_cmd_packet.cmdlen + 2[bp]
2776 mov es, ax ;; segment in es
2778 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data write port
2780 seg ES
2782 outsw ;; CX words transfered from port(DX) to ES:[SI]
2784 pop bp
2785 ASM_END
2787 if (inout == ATA_DATA_NO) {
2788 status = inb(iobase1 + ATA_CB_STAT);
2790 else {
2791 while (1) {
2793 status = inb(iobase1 + ATA_CB_STAT);
2795 // Check if command completed
2796 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ) ) ==0 ) break;
2798 if (status & ATA_CB_STAT_ERR) {
2799 BX_DEBUG_ATA("ata_cmd_packet : error (status %02x)\n",status);
2800 return 3;
2803 // Device must be ready to send data
2804 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2805 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2806 BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", status);
2807 return 4;
2810 // Normalize address
2811 bufseg += (bufoff / 16);
2812 bufoff %= 16;
2814 // Get the byte count
2815 lcount = ((Bit16u)(inb(iobase1 + ATA_CB_CH))<<8)+inb(iobase1 + ATA_CB_CL);
2817 // adjust to read what we want
2818 if(header>lcount) {
2819 lbefore=lcount;
2820 header-=lcount;
2821 lcount=0;
2823 else {
2824 lbefore=header;
2825 header=0;
2826 lcount-=lbefore;
2829 if(lcount>length) {
2830 lafter=lcount-length;
2831 lcount=length;
2832 length=0;
2834 else {
2835 lafter=0;
2836 length-=lcount;
2839 // Save byte count
2840 count = lcount;
2842 BX_DEBUG_ATA("Trying to read %04x bytes (%04x %04x %04x) ",lbefore+lcount+lafter,lbefore,lcount,lafter);
2843 BX_DEBUG_ATA("to 0x%04x:0x%04x\n",bufseg,bufoff);
2845 // If counts not dividable by 4, use 16bits mode
2846 lmode = mode;
2847 if (lbefore & 0x03) lmode=ATA_MODE_PIO16;
2848 if (lcount & 0x03) lmode=ATA_MODE_PIO16;
2849 if (lafter & 0x03) lmode=ATA_MODE_PIO16;
2851 // adds an extra byte if count are odd. before is always even
2852 if (lcount & 0x01) {
2853 lcount+=1;
2854 if ((lafter > 0) && (lafter & 0x01)) {
2855 lafter-=1;
2859 if (lmode == ATA_MODE_PIO32) {
2860 lcount>>=2; lbefore>>=2; lafter>>=2;
2862 else {
2863 lcount>>=1; lbefore>>=1; lafter>>=1;
2866 ; // FIXME bcc bug
2868 ASM_START
2869 push bp
2870 mov bp, sp
2872 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data read port
2874 mov cx, _ata_cmd_packet.lbefore + 2[bp]
2875 jcxz ata_packet_no_before
2877 mov ah, _ata_cmd_packet.lmode + 2[bp]
2878 cmp ah, #ATA_MODE_PIO32
2879 je ata_packet_in_before_32
2881 ata_packet_in_before_16:
2882 in ax, dx
2883 loop ata_packet_in_before_16
2884 jmp ata_packet_no_before
2886 ata_packet_in_before_32:
2887 in eax, dx
2888 loop ata_packet_in_before_32
2890 ata_packet_no_before:
2891 mov cx, _ata_cmd_packet.lcount + 2[bp]
2892 jcxz ata_packet_after
2894 mov di, _ata_cmd_packet.bufoff + 2[bp]
2895 mov ax, _ata_cmd_packet.bufseg + 2[bp]
2896 mov es, ax
2898 mov ah, _ata_cmd_packet.lmode + 2[bp]
2899 cmp ah, #ATA_MODE_PIO32
2900 je ata_packet_in_32
2902 ata_packet_in_16:
2904 insw ;; CX words transfered tp port(DX) to ES:[DI]
2905 jmp ata_packet_after
2907 ata_packet_in_32:
2909 insd ;; CX dwords transfered to port(DX) to ES:[DI]
2911 ata_packet_after:
2912 mov cx, _ata_cmd_packet.lafter + 2[bp]
2913 jcxz ata_packet_done
2915 mov ah, _ata_cmd_packet.lmode + 2[bp]
2916 cmp ah, #ATA_MODE_PIO32
2917 je ata_packet_in_after_32
2919 ata_packet_in_after_16:
2920 in ax, dx
2921 loop ata_packet_in_after_16
2922 jmp ata_packet_done
2924 ata_packet_in_after_32:
2925 in eax, dx
2926 loop ata_packet_in_after_32
2928 ata_packet_done:
2929 pop bp
2930 ASM_END
2932 // Compute new buffer address
2933 bufoff += count;
2935 // Save transferred bytes count
2936 transfer += count;
2937 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,transfer);
2941 // Final check, device must be ready
2942 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2943 != ATA_CB_STAT_RDY ) {
2944 BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", (unsigned) status);
2945 return 4;
2948 // Enable interrupts
2949 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2950 return 0;
2953 // ---------------------------------------------------------------------------
2954 // End of ATA/ATAPI Driver
2955 // ---------------------------------------------------------------------------
2957 // ---------------------------------------------------------------------------
2958 // Start of ATA/ATAPI generic functions
2959 // ---------------------------------------------------------------------------
2961 Bit16u
2962 atapi_get_sense(device)
2963 Bit16u device;
2965 Bit8u atacmd[12];
2966 Bit8u buffer[16];
2967 Bit8u i;
2969 memsetb(get_SS(),atacmd,0,12);
2971 // Request SENSE
2972 atacmd[0]=0x03;
2973 atacmd[4]=0x20;
2974 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 16L, ATA_DATA_IN, get_SS(), buffer) != 0)
2975 return 0x0002;
2977 if ((buffer[0] & 0x7e) == 0x70) {
2978 return (((Bit16u)buffer[2]&0x0f)*0x100)+buffer[12];
2981 return 0;
2984 Bit16u
2985 atapi_is_ready(device)
2986 Bit16u device;
2988 Bit8u atacmd[12];
2989 Bit8u buffer[];
2991 memsetb(get_SS(),atacmd,0,12);
2993 // Test Unit Ready
2994 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0)
2995 return 0x000f;
2997 if (atapi_get_sense(device) !=0 ) {
2998 memsetb(get_SS(),atacmd,0,12);
3000 // try to send Test Unit Ready again
3001 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0)
3002 return 0x000f;
3004 return atapi_get_sense(device);
3006 return 0;
3009 Bit16u
3010 atapi_is_cdrom(device)
3011 Bit8u device;
3013 Bit16u ebda_seg=read_word(0x0040,0x000E);
3015 if (device >= BX_MAX_ATA_DEVICES)
3016 return 0;
3018 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI)
3019 return 0;
3021 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].device) != ATA_DEVICE_CDROM)
3022 return 0;
3024 return 1;
3027 // ---------------------------------------------------------------------------
3028 // End of ATA/ATAPI generic functions
3029 // ---------------------------------------------------------------------------
3031 #endif // BX_USE_ATADRV
3033 #if BX_ELTORITO_BOOT
3035 // ---------------------------------------------------------------------------
3036 // Start of El-Torito boot functions
3037 // ---------------------------------------------------------------------------
3039 void
3040 cdemu_init()
3042 Bit16u ebda_seg=read_word(0x0040,0x000E);
3044 // the only important data is this one for now
3045 write_byte(ebda_seg,&EbdaData->cdemu.active,0x00);
3048 Bit8u
3049 cdemu_isactive()
3051 Bit16u ebda_seg=read_word(0x0040,0x000E);
3053 return(read_byte(ebda_seg,&EbdaData->cdemu.active));
3056 Bit8u
3057 cdemu_emulated_drive()
3059 Bit16u ebda_seg=read_word(0x0040,0x000E);
3061 return(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
3064 static char isotag[6]="CD001";
3065 static char eltorito[24]="EL TORITO SPECIFICATION";
3067 // Returns ah: emulated drive, al: error code
3069 Bit16u
3070 cdrom_boot()
3072 Bit16u ebda_seg=read_word(0x0040,0x000E);
3073 Bit8u atacmd[12], buffer[2048];
3074 Bit32u lba;
3075 Bit16u boot_segment, nbsectors, i, error;
3076 Bit8u device;
3078 // Find out the first cdrom
3079 for (device=0; device<BX_MAX_ATA_DEVICES;device++) {
3080 if (atapi_is_cdrom(device)) break;
3083 // if not found
3084 if(device >= BX_MAX_ATA_DEVICES) return 2;
3086 // Read the Boot Record Volume Descriptor
3087 memsetb(get_SS(),atacmd,0,12);
3088 atacmd[0]=0x28; // READ command
3089 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3090 atacmd[8]=(0x01 & 0x00ff); // Sectors
3091 atacmd[2]=(0x11 & 0xff000000) >> 24; // LBA
3092 atacmd[3]=(0x11 & 0x00ff0000) >> 16;
3093 atacmd[4]=(0x11 & 0x0000ff00) >> 8;
3094 atacmd[5]=(0x11 & 0x000000ff);
3095 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3096 return 3;
3098 // Validity checks
3099 if(buffer[0]!=0)return 4;
3100 for(i=0;i<5;i++){
3101 if(buffer[1+i]!=read_byte(0xf000,&isotag[i]))return 5;
3103 for(i=0;i<23;i++)
3104 if(buffer[7+i]!=read_byte(0xf000,&eltorito[i]))return 6;
3106 // ok, now we calculate the Boot catalog address
3107 lba=buffer[0x4A]*0x1000000+buffer[0x49]*0x10000+buffer[0x48]*0x100+buffer[0x47];
3109 // And we read the Boot Catalog
3110 memsetb(get_SS(),atacmd,0,12);
3111 atacmd[0]=0x28; // READ command
3112 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3113 atacmd[8]=(0x01 & 0x00ff); // Sectors
3114 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3115 atacmd[3]=(lba & 0x00ff0000) >> 16;
3116 atacmd[4]=(lba & 0x0000ff00) >> 8;
3117 atacmd[5]=(lba & 0x000000ff);
3118 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3119 return 7;
3121 // Validation entry
3122 if(buffer[0x00]!=0x01)return 8; // Header
3123 if(buffer[0x01]!=0x00)return 9; // Platform
3124 if(buffer[0x1E]!=0x55)return 10; // key 1
3125 if(buffer[0x1F]!=0xAA)return 10; // key 2
3127 // Initial/Default Entry
3128 if(buffer[0x20]!=0x88)return 11; // Bootable
3130 write_byte(ebda_seg,&EbdaData->cdemu.media,buffer[0x21]);
3131 if(buffer[0x21]==0){
3132 // FIXME ElTorito Hardcoded. cdrom is hardcoded as device 0xE0.
3133 // Win2000 cd boot needs to know it booted from cd
3134 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0xE0);
3136 else if(buffer[0x21]<4)
3137 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x00);
3138 else
3139 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x80);
3141 write_byte(ebda_seg,&EbdaData->cdemu.controller_index,device/2);
3142 write_byte(ebda_seg,&EbdaData->cdemu.device_spec,device%2);
3144 boot_segment=buffer[0x23]*0x100+buffer[0x22];
3145 if(boot_segment==0x0000)boot_segment=0x07C0;
3147 write_word(ebda_seg,&EbdaData->cdemu.load_segment,boot_segment);
3148 write_word(ebda_seg,&EbdaData->cdemu.buffer_segment,0x0000);
3150 nbsectors=buffer[0x27]*0x100+buffer[0x26];
3151 write_word(ebda_seg,&EbdaData->cdemu.sector_count,nbsectors);
3153 lba=buffer[0x2B]*0x1000000+buffer[0x2A]*0x10000+buffer[0x29]*0x100+buffer[0x28];
3154 write_dword(ebda_seg,&EbdaData->cdemu.ilba,lba);
3156 // And we read the image in memory
3157 memsetb(get_SS(),atacmd,0,12);
3158 atacmd[0]=0x28; // READ command
3159 atacmd[7]=((1+(nbsectors-1)/4) & 0xff00) >> 8; // Sectors
3160 atacmd[8]=((1+(nbsectors-1)/4) & 0x00ff); // Sectors
3161 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3162 atacmd[3]=(lba & 0x00ff0000) >> 16;
3163 atacmd[4]=(lba & 0x0000ff00) >> 8;
3164 atacmd[5]=(lba & 0x000000ff);
3165 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, nbsectors*512L, ATA_DATA_IN, boot_segment,0)) != 0)
3166 return 12;
3168 // Remember the media type
3169 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
3170 case 0x01: // 1.2M floppy
3171 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,15);
3172 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3173 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3174 break;
3175 case 0x02: // 1.44M floppy
3176 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,18);
3177 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3178 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3179 break;
3180 case 0x03: // 2.88M floppy
3181 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,36);
3182 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3183 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3184 break;
3185 case 0x04: // Harddrive
3186 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,read_byte(boot_segment,446+6)&0x3f);
3187 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,
3188 (read_byte(boot_segment,446+6)<<2) + read_byte(boot_segment,446+7) + 1);
3189 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,read_byte(boot_segment,446+5) + 1);
3190 break;
3193 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0) {
3194 // Increase bios installed hardware number of devices
3195 if(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)==0x00)
3196 write_byte(0x40,0x10,read_byte(0x40,0x10)|0x41);
3197 else
3198 write_byte(ebda_seg, &EbdaData->ata.hdcount, read_byte(ebda_seg, &EbdaData->ata.hdcount) + 1);
3202 // everything is ok, so from now on, the emulation is active
3203 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0)
3204 write_byte(ebda_seg,&EbdaData->cdemu.active,0x01);
3206 // return the boot drive + no error
3207 return (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)*0x100)+0;
3210 // ---------------------------------------------------------------------------
3211 // End of El-Torito boot functions
3212 // ---------------------------------------------------------------------------
3213 #endif // BX_ELTORITO_BOOT
3215 void
3216 int14_function(regs, ds, iret_addr)
3217 pusha_regs_t regs; // regs pushed from PUSHA instruction
3218 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
3219 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
3221 Bit16u addr,timer,val16;
3222 Bit8u timeout;
3224 ASM_START
3226 ASM_END
3228 addr = read_word(0x0040, (regs.u.r16.dx << 1));
3229 timeout = read_byte(0x0040, 0x007C + regs.u.r16.dx);
3230 if ((regs.u.r16.dx < 4) && (addr > 0)) {
3231 switch (regs.u.r8.ah) {
3232 case 0:
3233 outb(addr+3, inb(addr+3) | 0x80);
3234 if (regs.u.r8.al & 0xE0 == 0) {
3235 outb(addr, 0x17);
3236 outb(addr+1, 0x04);
3237 } else {
3238 val16 = 0x600 >> ((regs.u.r8.al & 0xE0) >> 5);
3239 outb(addr, val16 & 0xFF);
3240 outb(addr+1, val16 >> 8);
3242 outb(addr+3, regs.u.r8.al & 0x1F);
3243 regs.u.r8.ah = inb(addr+5);
3244 regs.u.r8.al = inb(addr+6);
3245 ClearCF(iret_addr.flags);
3246 break;
3247 case 1:
3248 timer = read_word(0x0040, 0x006C);
3249 while (((inb(addr+5) & 0x60) != 0x60) && (timeout)) {
3250 val16 = read_word(0x0040, 0x006C);
3251 if (val16 != timer) {
3252 timer = val16;
3253 timeout--;
3256 if (timeout) outb(addr, regs.u.r8.al);
3257 regs.u.r8.ah = inb(addr+5);
3258 if (!timeout) regs.u.r8.ah |= 0x80;
3259 ClearCF(iret_addr.flags);
3260 break;
3261 case 2:
3262 timer = read_word(0x0040, 0x006C);
3263 while (((inb(addr+5) & 0x01) == 0) && (timeout)) {
3264 val16 = read_word(0x0040, 0x006C);
3265 if (val16 != timer) {
3266 timer = val16;
3267 timeout--;
3270 if (timeout) {
3271 regs.u.r8.ah = 0;
3272 regs.u.r8.al = inb(addr);
3273 } else {
3274 regs.u.r8.ah = inb(addr+5);
3276 ClearCF(iret_addr.flags);
3277 break;
3278 case 3:
3279 regs.u.r8.ah = inb(addr+5);
3280 regs.u.r8.al = inb(addr+6);
3281 ClearCF(iret_addr.flags);
3282 break;
3283 default:
3284 SetCF(iret_addr.flags); // Unsupported
3286 } else {
3287 SetCF(iret_addr.flags); // Unsupported
3291 void
3292 int15_function(regs, ES, DS, FLAGS)
3293 pusha_regs_t regs; // REGS pushed via pusha
3294 Bit16u ES, DS, FLAGS;
3296 Bit16u ebda_seg=read_word(0x0040,0x000E);
3297 bx_bool prev_a20_enable;
3298 Bit16u base15_00;
3299 Bit8u base23_16;
3300 Bit16u ss;
3301 Bit16u CX,DX;
3303 Bit16u bRegister;
3304 Bit8u irqDisable;
3306 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3308 switch (regs.u.r8.ah) {
3309 case 0x24: /* A20 Control */
3310 switch (regs.u.r8.al) {
3311 case 0x00:
3312 set_enable_a20(0);
3313 CLEAR_CF();
3314 regs.u.r8.ah = 0;
3315 break;
3316 case 0x01:
3317 set_enable_a20(1);
3318 CLEAR_CF();
3319 regs.u.r8.ah = 0;
3320 break;
3321 case 0x02:
3322 regs.u.r8.al = (inb(0x92) >> 1) & 0x01;
3323 CLEAR_CF();
3324 regs.u.r8.ah = 0;
3325 break;
3326 case 0x03:
3327 CLEAR_CF();
3328 regs.u.r8.ah = 0;
3329 regs.u.r16.bx = 3;
3330 break;
3331 default:
3332 BX_INFO("int15: Func 24h, subfunc %02xh, A20 gate control not supported\n", (unsigned) regs.u.r8.al);
3333 SET_CF();
3334 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3336 break;
3338 case 0x41:
3339 SET_CF();
3340 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3341 break;
3343 case 0x4f:
3344 /* keyboard intercept */
3345 #if BX_CPU < 2
3346 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3347 #else
3348 // nop
3349 #endif
3350 SET_CF();
3351 break;
3353 case 0x52: // removable media eject
3354 CLEAR_CF();
3355 regs.u.r8.ah = 0; // "ok ejection may proceed"
3356 break;
3358 case 0x83: {
3359 if( regs.u.r8.al == 0 ) {
3360 // Set Interval requested.
3361 if( ( read_byte( 0x40, 0xA0 ) & 1 ) == 0 ) {
3362 // Interval not already set.
3363 write_byte( 0x40, 0xA0, 1 ); // Set status byte.
3364 write_word( 0x40, 0x98, ES ); // Byte location, segment
3365 write_word( 0x40, 0x9A, regs.u.r16.bx ); // Byte location, offset
3366 write_word( 0x40, 0x9C, regs.u.r16.dx ); // Low word, delay
3367 write_word( 0x40, 0x9E, regs.u.r16.cx ); // High word, delay.
3368 CLEAR_CF( );
3369 irqDisable = inb( 0xA1 );
3370 outb( 0xA1, irqDisable & 0xFE );
3371 bRegister = inb_cmos( 0xB ); // Unmask IRQ8 so INT70 will get through.
3372 outb_cmos( 0xB, bRegister | 0x40 ); // Turn on the Periodic Interrupt timer
3373 } else {
3374 // Interval already set.
3375 BX_DEBUG_INT15("int15: Func 83h, failed, already waiting.\n" );
3376 SET_CF();
3377 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3379 } else if( regs.u.r8.al == 1 ) {
3380 // Clear Interval requested
3381 write_byte( 0x40, 0xA0, 0 ); // Clear status byte
3382 CLEAR_CF( );
3383 bRegister = inb_cmos( 0xB );
3384 outb_cmos( 0xB, bRegister & ~0x40 ); // Turn off the Periodic Interrupt timer
3385 } else {
3386 BX_DEBUG_INT15("int15: Func 83h, failed.\n" );
3387 SET_CF();
3388 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3389 regs.u.r8.al--;
3392 break;
3395 case 0x87:
3396 #if BX_CPU < 3
3397 # error "Int15 function 87h not supported on < 80386"
3398 #endif
3399 // +++ should probably have descriptor checks
3400 // +++ should have exception handlers
3402 // turn off interrupts
3403 ASM_START
3405 ASM_END
3407 prev_a20_enable = set_enable_a20(1); // enable A20 line
3409 // 128K max of transfer on 386+ ???
3410 // source == destination ???
3412 // ES:SI points to descriptor table
3413 // offset use initially comments
3414 // ==============================================
3415 // 00..07 Unused zeros Null descriptor
3416 // 08..0f GDT zeros filled in by BIOS
3417 // 10..17 source ssssssss source of data
3418 // 18..1f dest dddddddd destination of data
3419 // 20..27 CS zeros filled in by BIOS
3420 // 28..2f SS zeros filled in by BIOS
3422 //es:si
3423 //eeee0
3424 //0ssss
3425 //-----
3427 // check for access rights of source & dest here
3429 // Initialize GDT descriptor
3430 base15_00 = (ES << 4) + regs.u.r16.si;
3431 base23_16 = ES >> 12;
3432 if (base15_00 < (ES<<4))
3433 base23_16++;
3434 write_word(ES, regs.u.r16.si+0x08+0, 47); // limit 15:00 = 6 * 8bytes/descriptor
3435 write_word(ES, regs.u.r16.si+0x08+2, base15_00);// base 15:00
3436 write_byte(ES, regs.u.r16.si+0x08+4, base23_16);// base 23:16
3437 write_byte(ES, regs.u.r16.si+0x08+5, 0x93); // access
3438 write_word(ES, regs.u.r16.si+0x08+6, 0x0000); // base 31:24/reserved/limit 19:16
3440 // Initialize CS descriptor
3441 write_word(ES, regs.u.r16.si+0x20+0, 0xffff);// limit 15:00 = normal 64K limit
3442 write_word(ES, regs.u.r16.si+0x20+2, 0x0000);// base 15:00
3443 write_byte(ES, regs.u.r16.si+0x20+4, 0x000f);// base 23:16
3444 write_byte(ES, regs.u.r16.si+0x20+5, 0x9b); // access
3445 write_word(ES, regs.u.r16.si+0x20+6, 0x0000);// base 31:24/reserved/limit 19:16
3447 // Initialize SS descriptor
3448 ss = get_SS();
3449 base15_00 = ss << 4;
3450 base23_16 = ss >> 12;
3451 write_word(ES, regs.u.r16.si+0x28+0, 0xffff); // limit 15:00 = normal 64K limit
3452 write_word(ES, regs.u.r16.si+0x28+2, base15_00);// base 15:00
3453 write_byte(ES, regs.u.r16.si+0x28+4, base23_16);// base 23:16
3454 write_byte(ES, regs.u.r16.si+0x28+5, 0x93); // access
3455 write_word(ES, regs.u.r16.si+0x28+6, 0x0000); // base 31:24/reserved/limit 19:16
3457 CX = regs.u.r16.cx;
3458 ASM_START
3459 // Compile generates locals offset info relative to SP.
3460 // Get CX (word count) from stack.
3461 mov bx, sp
3462 SEG SS
3463 mov cx, _int15_function.CX [bx]
3465 // since we need to set SS:SP, save them to the BDA
3466 // for future restore
3467 push eax
3468 xor eax, eax
3469 mov ds, ax
3470 mov 0x0469, ss
3471 mov 0x0467, sp
3473 SEG ES
3474 lgdt [si + 0x08]
3475 SEG CS
3476 lidt [pmode_IDT_info]
3477 ;; perhaps do something with IDT here
3479 ;; set PE bit in CR0
3480 mov eax, cr0
3481 or al, #0x01
3482 mov cr0, eax
3483 ;; far jump to flush CPU queue after transition to protected mode
3484 JMP_AP(0x0020, protected_mode)
3486 protected_mode:
3487 ;; GDT points to valid descriptor table, now load SS, DS, ES
3488 mov ax, #0x28 ;; 101 000 = 5th descriptor in table, TI=GDT, RPL=00
3489 mov ss, ax
3490 mov ax, #0x10 ;; 010 000 = 2nd descriptor in table, TI=GDT, RPL=00
3491 mov ds, ax
3492 mov ax, #0x18 ;; 011 000 = 3rd descriptor in table, TI=GDT, RPL=00
3493 mov es, ax
3494 xor si, si
3495 xor di, di
3498 movsw ;; move CX words from DS:SI to ES:DI
3500 ;; make sure DS and ES limits are 64KB
3501 mov ax, #0x28
3502 mov ds, ax
3503 mov es, ax
3505 ;; reset PG bit in CR0 ???
3506 mov eax, cr0
3507 and al, #0xFE
3508 mov cr0, eax
3510 ;; far jump to flush CPU queue after transition to real mode
3511 JMP_AP(0xf000, real_mode)
3513 real_mode:
3514 ;; restore IDT to normal real-mode defaults
3515 SEG CS
3516 lidt [rmode_IDT_info]
3518 // restore SS:SP from the BDA
3519 xor ax, ax
3520 mov ds, ax
3521 mov ss, 0x0469
3522 mov sp, 0x0467
3523 pop eax
3524 ASM_END
3526 set_enable_a20(prev_a20_enable);
3528 // turn back on interrupts
3529 ASM_START
3531 ASM_END
3533 regs.u.r8.ah = 0;
3534 CLEAR_CF();
3535 break;
3538 case 0x88: /* extended memory size */
3539 #if BX_CPU < 2
3540 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3541 SET_CF();
3542 #else
3543 /* ??? change this back later... */
3544 /* number of 1K blocks of extended memory, subtract off 1st 1Meg */
3545 // AX = bx_mem.get_memory_in_k() - 1024;
3546 regs.u.r8.al = inb_cmos(0x30);
3547 regs.u.r8.ah = inb_cmos(0x31);
3548 CLEAR_CF();
3549 #endif
3550 break;
3552 case 0x90:
3553 /* Device busy interrupt. Called by Int 16h when no key available */
3554 break;
3556 case 0x91:
3557 /* Interrupt complete. Called by Int 16h when key becomes available */
3558 break;
3560 case 0xbf:
3561 BX_INFO("*** int 15h function AH=bf not yet supported!\n");
3562 SET_CF();
3563 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3564 break;
3566 case 0xC0:
3567 #if 0
3568 SET_CF();
3569 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3570 break;
3571 #endif
3572 CLEAR_CF();
3573 regs.u.r8.ah = 0;
3574 regs.u.r16.bx = BIOS_CONFIG_TABLE;
3575 ES = 0xF000;
3576 break;
3578 case 0xc1:
3579 #if BX_USE_PS2_MOUSE
3580 ES = read_word(0x0040, 0x000E);
3581 CLEAR_CF();
3582 #else
3583 SET_CF();
3584 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3585 #endif
3586 break;
3588 case 0xd8:
3589 bios_printf(BIOS_PRINTF_DEBUG, "EISA BIOS not present\n");
3590 SET_CF();
3591 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3592 break;
3594 default:
3595 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
3596 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
3597 SET_CF();
3598 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3599 break;
3603 #if BX_USE_PS2_MOUSE
3604 void
3605 int15_function_mouse(regs, ES, DS, FLAGS)
3606 pusha_regs_t regs; // REGS pushed via pusha
3607 Bit16u ES, DS, FLAGS;
3609 Bit16u ebda_seg=read_word(0x0040,0x000E);
3610 Bit8u mouse_flags_1, mouse_flags_2;
3611 Bit16u mouse_driver_seg;
3612 Bit16u mouse_driver_offset;
3613 Bit8u response, prev_command_byte;
3614 bx_bool prev_a20_enable;
3615 Bit8u ret, mouse_data1, mouse_data2, mouse_data3;
3616 Bit8u comm_byte, mf2_state;
3618 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3620 switch (regs.u.r8.ah) {
3621 case 0xC2:
3622 // Return Codes status in AH
3623 // =========================
3624 // 00: success
3625 // 01: invalid subfunction (AL > 7)
3626 // 02: invalid input value (out of allowable range)
3627 // 03: interface error
3628 // 04: resend command received from mouse controller,
3629 // device driver should attempt command again
3630 // 05: cannot enable mouse, since no far call has been installed
3631 // 80/86: mouse service not implemented
3633 switch (regs.u.r8.al) {
3634 case 0: // Disable/Enable Mouse
3635 BX_DEBUG_INT15("case 0:\n");
3636 switch (regs.u.r8.bh) {
3637 case 0: // Disable Mouse
3638 BX_DEBUG_INT15("case 0: disable mouse\n");
3639 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3640 ret = send_to_mouse_ctrl(0xF5); // disable mouse command
3641 if (ret == 0) {
3642 ret = get_mouse_data(&mouse_data1);
3643 if ( (ret == 0) || (mouse_data1 == 0xFA) ) {
3644 CLEAR_CF();
3645 regs.u.r8.ah = 0;
3646 return;
3650 // error
3651 SET_CF();
3652 regs.u.r8.ah = ret;
3653 return;
3654 break;
3656 case 1: // Enable Mouse
3657 BX_DEBUG_INT15("case 1: enable mouse\n");
3658 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
3659 if ( (mouse_flags_2 & 0x80) == 0 ) {
3660 BX_DEBUG_INT15("INT 15h C2 Enable Mouse, no far call handler\n");
3661 SET_CF(); // error
3662 regs.u.r8.ah = 5; // no far call installed
3663 return;
3665 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3666 ret = send_to_mouse_ctrl(0xF4); // enable mouse command
3667 if (ret == 0) {
3668 ret = get_mouse_data(&mouse_data1);
3669 if ( (ret == 0) && (mouse_data1 == 0xFA) ) {
3670 enable_mouse_int_and_events(); // turn IRQ12 and packet generation on
3671 CLEAR_CF();
3672 regs.u.r8.ah = 0;
3673 return;
3676 SET_CF();
3677 regs.u.r8.ah = ret;
3678 return;
3680 default: // invalid subfunction
3681 BX_DEBUG_INT15("INT 15h C2 AL=0, BH=%02x\n", (unsigned) regs.u.r8.bh);
3682 SET_CF(); // error
3683 regs.u.r8.ah = 1; // invalid subfunction
3684 return;
3686 break;
3688 case 1: // Reset Mouse
3689 case 5: // Initialize Mouse
3690 BX_DEBUG_INT15("case 1 or 5:\n");
3691 if (regs.u.r8.al == 5) {
3692 if (regs.u.r8.bh != 3)
3693 BX_PANIC("INT 15h C2 AL=5, BH=%02x\n", (unsigned) regs.u.r8.bh);
3694 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
3695 mouse_flags_2 = (mouse_flags_2 & 0x00) | regs.u.r8.bh;
3696 mouse_flags_1 = 0x00;
3697 write_byte(ebda_seg, 0x0026, mouse_flags_1);
3698 write_byte(ebda_seg, 0x0027, mouse_flags_2);
3701 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3702 ret = send_to_mouse_ctrl(0xFF); // disable mouse command
3703 if (ret == 0) {
3704 ret = get_mouse_data(&mouse_data3);
3705 if (mouse_data3 != 0xfa)
3706 BX_PANIC("Mouse reset returned %02x (should be ack)\n", (unsigned)mouse_data3);
3707 if ( ret == 0 ) {
3708 ret = get_mouse_data(&mouse_data1);
3709 if ( ret == 0 ) {
3710 ret = get_mouse_data(&mouse_data2);
3711 if ( ret == 0 ) {
3712 // turn IRQ12 and packet generation on
3713 enable_mouse_int_and_events();
3714 CLEAR_CF();
3715 regs.u.r8.ah = 0;
3716 regs.u.r8.bl = mouse_data1;
3717 regs.u.r8.bh = mouse_data2;
3718 return;
3724 // error
3725 SET_CF();
3726 regs.u.r8.ah = ret;
3727 return;
3729 case 2: // Set Sample Rate
3730 BX_DEBUG_INT15("case 2:\n");
3731 switch (regs.u.r8.bh) {
3732 case 0: // 10 reports/sec
3733 case 1: // 20 reports/sec
3734 case 2: // 40 reports/sec
3735 case 3: // 60 reports/sec
3736 case 4: // 80 reports/sec
3737 case 5: // 100 reports/sec (default)
3738 case 6: // 200 reports/sec
3739 CLEAR_CF();
3740 regs.u.r8.ah = 0;
3741 break;
3742 default:
3743 BX_PANIC("INT 15h C2 AL=2, BH=%02x\n", (unsigned) regs.u.r8.bh);
3745 break;
3747 case 3: // Set Resolution
3748 BX_DEBUG_INT15("case 3:\n");
3749 // BX:
3750 // 0 = 25 dpi, 1 count per millimeter
3751 // 1 = 50 dpi, 2 counts per millimeter
3752 // 2 = 100 dpi, 4 counts per millimeter
3753 // 3 = 200 dpi, 8 counts per millimeter
3754 CLEAR_CF();
3755 regs.u.r8.ah = 0;
3756 break;
3758 case 4: // Get Device ID
3759 BX_DEBUG_INT15("case 4:\n");
3760 CLEAR_CF();
3761 regs.u.r8.ah = 0;
3762 regs.u.r8.bh = 0;
3763 break;
3765 case 6: // Return Status & Set Scaling Factor...
3766 BX_DEBUG_INT15("case 6:\n");
3767 switch (regs.u.r8.bh) {
3768 case 0: // Return Status
3769 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3770 ret = send_to_mouse_ctrl(0xE9); // get mouse info command
3771 if (ret == 0) {
3772 ret = get_mouse_data(&mouse_data1);
3773 if (mouse_data1 != 0xfa)
3774 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
3775 if (ret == 0) {
3776 ret = get_mouse_data(&mouse_data1);
3777 if ( ret == 0 ) {
3778 ret = get_mouse_data(&mouse_data2);
3779 if ( ret == 0 ) {
3780 ret = get_mouse_data(&mouse_data3);
3781 if ( ret == 0 ) {
3782 CLEAR_CF();
3783 regs.u.r8.ah = 0;
3784 regs.u.r8.bl = mouse_data1;
3785 regs.u.r8.cl = mouse_data2;
3786 regs.u.r8.dl = mouse_data3;
3787 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
3788 return;
3795 // error
3796 SET_CF();
3797 regs.u.r8.ah = ret;
3798 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
3799 return;
3801 case 1: // Set Scaling Factor to 1:1
3802 CLEAR_CF();
3803 regs.u.r8.ah = 0;
3804 break;
3806 default:
3807 BX_PANIC("INT 15h C2 AL=6, BH=%02x\n", (unsigned) regs.u.r8.bh);
3809 break;
3811 case 7: // Set Mouse Handler Address
3812 BX_DEBUG_INT15("case 7:\n");
3813 mouse_driver_seg = ES;
3814 mouse_driver_offset = regs.u.r16.bx;
3815 write_word(ebda_seg, 0x0022, mouse_driver_offset);
3816 write_word(ebda_seg, 0x0024, mouse_driver_seg);
3817 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
3818 if (mouse_driver_offset == 0 && mouse_driver_seg == 0) {
3819 /* remove handler */
3820 if ( (mouse_flags_2 & 0x80) != 0 ) {
3821 mouse_flags_2 &= ~0x80;
3822 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3825 else {
3826 /* install handler */
3827 mouse_flags_2 |= 0x80;
3829 write_byte(ebda_seg, 0x0027, mouse_flags_2);
3830 CLEAR_CF();
3831 regs.u.r8.ah = 0;
3832 break;
3834 default:
3835 BX_DEBUG_INT15("case default:\n");
3836 regs.u.r8.ah = 1; // invalid function
3837 SET_CF();
3839 break;
3841 default:
3842 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
3843 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
3844 SET_CF();
3845 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3846 break;
3849 #endif
3851 void
3852 int15_function32(regs, ES, DS, FLAGS)
3853 pushad_regs_t regs; // REGS pushed via pushad
3854 Bit16u ES, DS, FLAGS;
3856 Bit32u extended_memory_size=0; // 64bits long
3857 Bit16u CX,DX;
3859 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3861 switch (regs.u.r8.ah) {
3862 case 0x86:
3863 // Wait for CX:DX microseconds. currently using the
3864 // refresh request port 0x61 bit4, toggling every 15usec
3866 CX = regs.u.r16.cx;
3867 DX = regs.u.r16.dx;
3869 ASM_START
3872 ;; Get the count in eax
3873 mov bx, sp
3874 SEG SS
3875 mov ax, _int15_function.CX [bx]
3876 shl eax, #16
3877 SEG SS
3878 mov ax, _int15_function.DX [bx]
3880 ;; convert to numbers of 15usec ticks
3881 mov ebx, #15
3882 xor edx, edx
3883 div eax, ebx
3884 mov ecx, eax
3886 ;; wait for ecx number of refresh requests
3887 in al, #0x61
3888 and al,#0x10
3889 mov ah, al
3891 or ecx, ecx
3892 je int1586_tick_end
3893 int1586_tick:
3894 in al, #0x61
3895 and al,#0x10
3896 cmp al, ah
3897 je int1586_tick
3898 mov ah, al
3899 dec ecx
3900 jnz int1586_tick
3901 int1586_tick_end:
3902 ASM_END
3904 break;
3906 case 0xe8:
3907 switch(regs.u.r8.al)
3909 case 0x20: // coded by osmaker aka K.J.
3910 if(regs.u.r32.edx == 0x534D4150)
3912 switch(regs.u.r16.bx)
3914 case 0:
3915 write_word(ES, regs.u.r16.di, 0x00);
3916 write_word(ES, regs.u.r16.di+2, 0x00);
3917 write_word(ES, regs.u.r16.di+4, 0x00);
3918 write_word(ES, regs.u.r16.di+6, 0x00);
3920 write_word(ES, regs.u.r16.di+8, 0xFC00);
3921 write_word(ES, regs.u.r16.di+10, 0x0009);
3922 write_word(ES, regs.u.r16.di+12, 0x0000);
3923 write_word(ES, regs.u.r16.di+14, 0x0000);
3925 write_word(ES, regs.u.r16.di+16, 0x1);
3926 write_word(ES, regs.u.r16.di+18, 0x0);
3928 regs.u.r32.ebx = 1;
3929 regs.u.r32.eax = 0x534D4150;
3930 regs.u.r32.ecx = 0x14;
3931 CLEAR_CF();
3932 return;
3933 break;
3934 case 1:
3935 extended_memory_size = inb_cmos(0x35);
3936 extended_memory_size <<= 8;
3937 extended_memory_size |= inb_cmos(0x34);
3938 extended_memory_size *= 64;
3939 if(extended_memory_size > 0x3bc000) // greater than EFF00000???
3941 extended_memory_size = 0x3bc000; // everything after this is reserved memory until we get to 0x100000000
3943 extended_memory_size *= 1024;
3944 extended_memory_size += 15728640; // make up for the 16mb of memory that is chopped off
3946 if(extended_memory_size <= 15728640)
3948 extended_memory_size = inb_cmos(0x31);
3949 extended_memory_size <<= 8;
3950 extended_memory_size |= inb_cmos(0x30);
3951 extended_memory_size *= 1024;
3954 write_word(ES, regs.u.r16.di, 0x0000);
3955 write_word(ES, regs.u.r16.di+2, 0x0010);
3956 write_word(ES, regs.u.r16.di+4, 0x0000);
3957 write_word(ES, regs.u.r16.di+6, 0x0000);
3959 write_word(ES, regs.u.r16.di+8, extended_memory_size);
3960 extended_memory_size >>= 16;
3961 write_word(ES, regs.u.r16.di+10, extended_memory_size);
3962 extended_memory_size >>= 16;
3963 write_word(ES, regs.u.r16.di+12, extended_memory_size);
3964 extended_memory_size >>= 16;
3965 write_word(ES, regs.u.r16.di+14, extended_memory_size);
3967 write_word(ES, regs.u.r16.di+16, 0x1);
3968 write_word(ES, regs.u.r16.di+18, 0x0);
3970 regs.u.r32.ebx = 0;
3971 regs.u.r32.eax = 0x534D4150;
3972 regs.u.r32.ecx = 0x14;
3973 CLEAR_CF();
3974 return;
3975 break;
3976 default: /* AX=E820, DX=534D4150, BX unrecognized */
3977 goto int15_unimplemented;
3978 break;
3980 } else {
3981 // if DX != 0x534D4150)
3982 goto int15_unimplemented;
3984 break;
3986 case 0x01:
3987 // do we have any reason to fail here ?
3988 CLEAR_CF();
3990 // my real system sets ax and bx to 0
3991 // this is confirmed by Ralph Brown list
3992 // but syslinux v1.48 is known to behave
3993 // strangely if ax is set to 0
3994 // regs.u.r16.ax = 0;
3995 // regs.u.r16.bx = 0;
3997 // Get the amount of extended memory (above 1M)
3998 regs.u.r8.cl = inb_cmos(0x30);
3999 regs.u.r8.ch = inb_cmos(0x31);
4001 // limit to 15M
4002 if(regs.u.r16.cx > 0x3c00)
4004 regs.u.r16.cx = 0x3c00;
4007 // Get the amount of extended memory above 16M in 64k blocs
4008 regs.u.r8.dl = inb_cmos(0x34);
4009 regs.u.r8.dh = inb_cmos(0x35);
4011 // Set configured memory equal to extended memory
4012 regs.u.r16.ax = regs.u.r16.cx;
4013 regs.u.r16.bx = regs.u.r16.dx;
4014 break;
4015 default: /* AH=0xE8?? but not implemented */
4016 goto int15_unimplemented;
4018 break;
4019 int15_unimplemented:
4020 // fall into the default
4021 default:
4022 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4023 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4024 SET_CF();
4025 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4026 break;
4030 void
4031 int16_function(DI, SI, BP, SP, BX, DX, CX, AX, FLAGS)
4032 Bit16u DI, SI, BP, SP, BX, DX, CX, AX, FLAGS;
4034 Bit8u scan_code, ascii_code, shift_flags, count;
4035 Bit16u kbd_code, max;
4037 BX_DEBUG_INT16("int16: AX=%04x BX=%04x CX=%04x DX=%04x \n", AX, BX, CX, DX);
4039 switch (GET_AH()) {
4040 case 0x00: /* read keyboard input */
4042 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4043 BX_PANIC("KBD: int16h: out of keyboard input\n");
4045 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4046 else if (ascii_code == 0xE0) ascii_code = 0;
4047 AX = (scan_code << 8) | ascii_code;
4048 break;
4050 case 0x01: /* check keyboard status */
4051 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4052 SET_ZF();
4053 return;
4055 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4056 else if (ascii_code == 0xE0) ascii_code = 0;
4057 AX = (scan_code << 8) | ascii_code;
4058 CLEAR_ZF();
4059 break;
4061 case 0x02: /* get shift flag status */
4062 shift_flags = read_byte(0x0040, 0x17);
4063 SET_AL(shift_flags);
4064 break;
4066 case 0x05: /* store key-stroke into buffer */
4067 if ( !enqueue_key(GET_CH(), GET_CL()) ) {
4068 SET_AL(1);
4070 else {
4071 SET_AL(0);
4073 break;
4075 case 0x09: /* GET KEYBOARD FUNCTIONALITY */
4076 // bit Bochs Description
4077 // 7 0 reserved
4078 // 6 0 INT 16/AH=20h-22h supported (122-key keyboard support)
4079 // 5 1 INT 16/AH=10h-12h supported (enhanced keyboard support)
4080 // 4 1 INT 16/AH=0Ah supported
4081 // 3 0 INT 16/AX=0306h supported
4082 // 2 0 INT 16/AX=0305h supported
4083 // 1 0 INT 16/AX=0304h supported
4084 // 0 0 INT 16/AX=0300h supported
4086 SET_AL(0x30);
4087 break;
4089 case 0x0A: /* GET KEYBOARD ID */
4090 count = 2;
4091 kbd_code = 0x0;
4092 outb(0x60, 0xf2);
4093 /* Wait for data */
4094 max=0xffff;
4095 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4096 if (max>0x0) {
4097 if ((inb(0x60) == 0xfa)) {
4098 do {
4099 max=0xffff;
4100 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4101 if (max>0x0) {
4102 kbd_code >>= 8;
4103 kbd_code |= (inb(0x60) << 8);
4105 } while (--count>0);
4108 BX=kbd_code;
4109 break;
4111 case 0x10: /* read MF-II keyboard input */
4113 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4114 BX_PANIC("KBD: int16h: out of keyboard input\n");
4116 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4117 AX = (scan_code << 8) | ascii_code;
4118 break;
4120 case 0x11: /* check MF-II keyboard status */
4121 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4122 SET_ZF();
4123 return;
4125 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4126 AX = (scan_code << 8) | ascii_code;
4127 CLEAR_ZF();
4128 break;
4130 case 0x12: /* get extended keyboard status */
4131 shift_flags = read_byte(0x0040, 0x17);
4132 SET_AL(shift_flags);
4133 shift_flags = read_byte(0x0040, 0x18);
4134 SET_AH(shift_flags);
4135 BX_DEBUG_INT16("int16: func 12 sending %04x\n",AX);
4136 break;
4138 case 0x92: /* keyboard capability check called by DOS 5.0+ keyb */
4139 SET_AH(0x80); // function int16 ah=0x10-0x12 supported
4140 break;
4142 case 0xA2: /* 122 keys capability check called by DOS 5.0+ keyb */
4143 // don't change AH : function int16 ah=0x20-0x22 NOT supported
4144 break;
4146 default:
4147 BX_INFO("KBD: unsupported int 16h function %02x\n", GET_AH());
4151 unsigned int
4152 dequeue_key(scan_code, ascii_code, incr)
4153 Bit8u *scan_code;
4154 Bit8u *ascii_code;
4155 unsigned int incr;
4157 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail;
4158 Bit16u ss;
4159 Bit8u acode, scode;
4161 #if BX_CPU < 2
4162 buffer_start = 0x001E;
4163 buffer_end = 0x003E;
4164 #else
4165 buffer_start = read_word(0x0040, 0x0080);
4166 buffer_end = read_word(0x0040, 0x0082);
4167 #endif
4169 buffer_head = read_word(0x0040, 0x001a);
4170 buffer_tail = read_word(0x0040, 0x001c);
4172 if (buffer_head != buffer_tail) {
4173 ss = get_SS();
4174 acode = read_byte(0x0040, buffer_head);
4175 scode = read_byte(0x0040, buffer_head+1);
4176 write_byte(ss, ascii_code, acode);
4177 write_byte(ss, scan_code, scode);
4179 if (incr) {
4180 buffer_head += 2;
4181 if (buffer_head >= buffer_end)
4182 buffer_head = buffer_start;
4183 write_word(0x0040, 0x001a, buffer_head);
4185 return(1);
4187 else {
4188 return(0);
4192 static char panic_msg_keyb_buffer_full[] = "%s: keyboard input buffer full\n";
4194 Bit8u
4195 inhibit_mouse_int_and_events()
4197 Bit8u command_byte, prev_command_byte;
4199 // Turn off IRQ generation and aux data line
4200 if ( inb(0x64) & 0x02 )
4201 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4202 outb(0x64, 0x20); // get command byte
4203 while ( (inb(0x64) & 0x01) != 0x01 );
4204 prev_command_byte = inb(0x60);
4205 command_byte = prev_command_byte;
4206 //while ( (inb(0x64) & 0x02) );
4207 if ( inb(0x64) & 0x02 )
4208 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4209 command_byte &= 0xfd; // turn off IRQ 12 generation
4210 command_byte |= 0x20; // disable mouse serial clock line
4211 outb(0x64, 0x60); // write command byte
4212 outb(0x60, command_byte);
4213 return(prev_command_byte);
4216 void
4217 enable_mouse_int_and_events()
4219 Bit8u command_byte;
4221 // Turn on IRQ generation and aux data line
4222 if ( inb(0x64) & 0x02 )
4223 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4224 outb(0x64, 0x20); // get command byte
4225 while ( (inb(0x64) & 0x01) != 0x01 );
4226 command_byte = inb(0x60);
4227 //while ( (inb(0x64) & 0x02) );
4228 if ( inb(0x64) & 0x02 )
4229 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4230 command_byte |= 0x02; // turn on IRQ 12 generation
4231 command_byte &= 0xdf; // enable mouse serial clock line
4232 outb(0x64, 0x60); // write command byte
4233 outb(0x60, command_byte);
4236 Bit8u
4237 send_to_mouse_ctrl(sendbyte)
4238 Bit8u sendbyte;
4240 Bit8u response;
4242 // wait for chance to write to ctrl
4243 if ( inb(0x64) & 0x02 )
4244 BX_PANIC(panic_msg_keyb_buffer_full,"sendmouse");
4245 outb(0x64, 0xD4);
4246 outb(0x60, sendbyte);
4247 return(0);
4251 Bit8u
4252 get_mouse_data(data)
4253 Bit8u *data;
4255 Bit8u response;
4256 Bit16u ss;
4258 while ( (inb(0x64) & 0x21) != 0x21 ) {
4261 response = inb(0x60);
4263 ss = get_SS();
4264 write_byte(ss, data, response);
4265 return(0);
4268 void
4269 set_kbd_command_byte(command_byte)
4270 Bit8u command_byte;
4272 if ( inb(0x64) & 0x02 )
4273 BX_PANIC(panic_msg_keyb_buffer_full,"setkbdcomm");
4274 outb(0x64, 0xD4);
4276 outb(0x64, 0x60); // write command byte
4277 outb(0x60, command_byte);
4280 void
4281 int09_function(DI, SI, BP, SP, BX, DX, CX, AX)
4282 Bit16u DI, SI, BP, SP, BX, DX, CX, AX;
4284 Bit8u scancode, asciicode, shift_flags;
4285 Bit8u mf2_flags, mf2_state, led_flags;
4288 // DS has been set to F000 before call
4292 scancode = GET_AL();
4294 if (scancode == 0) {
4295 BX_INFO("KBD: int09 handler: AL=0\n");
4296 return;
4300 shift_flags = read_byte(0x0040, 0x17);
4301 mf2_flags = read_byte(0x0040, 0x18);
4302 mf2_state = read_byte(0x0040, 0x96);
4303 led_flags = read_byte(0x0040, 0x97);
4304 asciicode = 0;
4306 switch (scancode) {
4307 case 0x3a: /* Caps Lock press */
4308 shift_flags ^= 0x40;
4309 write_byte(0x0040, 0x17, shift_flags);
4310 mf2_flags |= 0x40;
4311 write_byte(0x0040, 0x18, mf2_flags);
4312 led_flags ^= 0x04;
4313 write_byte(0x0040, 0x97, led_flags);
4314 break;
4315 case 0xba: /* Caps Lock release */
4316 mf2_flags &= ~0x40;
4317 write_byte(0x0040, 0x18, mf2_flags);
4318 break;
4320 case 0x2a: /* L Shift press */
4321 /*shift_flags &= ~0x40;*/
4322 shift_flags |= 0x02;
4323 write_byte(0x0040, 0x17, shift_flags);
4324 led_flags &= ~0x04;
4325 write_byte(0x0040, 0x97, led_flags);
4326 break;
4327 case 0xaa: /* L Shift release */
4328 shift_flags &= ~0x02;
4329 write_byte(0x0040, 0x17, shift_flags);
4330 break;
4332 case 0x36: /* R Shift press */
4333 /*shift_flags &= ~0x40;*/
4334 shift_flags |= 0x01;
4335 write_byte(0x0040, 0x17, shift_flags);
4336 led_flags &= ~0x04;
4337 write_byte(0x0040, 0x97, led_flags);
4338 break;
4339 case 0xb6: /* R Shift release */
4340 shift_flags &= ~0x01;
4341 write_byte(0x0040, 0x17, shift_flags);
4342 break;
4344 case 0x1d: /* Ctrl press */
4345 shift_flags |= 0x04;
4346 write_byte(0x0040, 0x17, shift_flags);
4347 if (mf2_state & 0x01) {
4348 mf2_flags |= 0x04;
4349 } else {
4350 mf2_flags |= 0x01;
4352 write_byte(0x0040, 0x18, mf2_flags);
4353 break;
4354 case 0x9d: /* Ctrl release */
4355 shift_flags &= ~0x04;
4356 write_byte(0x0040, 0x17, shift_flags);
4357 if (mf2_state & 0x01) {
4358 mf2_flags &= ~0x04;
4359 } else {
4360 mf2_flags &= ~0x01;
4362 write_byte(0x0040, 0x18, mf2_flags);
4363 break;
4365 case 0x38: /* Alt press */
4366 shift_flags |= 0x08;
4367 write_byte(0x0040, 0x17, shift_flags);
4368 if (mf2_state & 0x01) {
4369 mf2_flags |= 0x08;
4370 } else {
4371 mf2_flags |= 0x02;
4373 write_byte(0x0040, 0x18, mf2_flags);
4374 break;
4375 case 0xb8: /* Alt release */
4376 shift_flags &= ~0x08;
4377 write_byte(0x0040, 0x17, shift_flags);
4378 if (mf2_state & 0x01) {
4379 mf2_flags &= ~0x08;
4380 } else {
4381 mf2_flags &= ~0x02;
4383 write_byte(0x0040, 0x18, mf2_flags);
4384 break;
4386 case 0x45: /* Num Lock press */
4387 if ((mf2_state & 0x01) == 0) {
4388 mf2_flags |= 0x20;
4389 write_byte(0x0040, 0x18, mf2_flags);
4390 shift_flags ^= 0x20;
4391 led_flags ^= 0x02;
4392 write_byte(0x0040, 0x17, shift_flags);
4393 write_byte(0x0040, 0x97, led_flags);
4395 break;
4396 case 0xc5: /* Num Lock release */
4397 if ((mf2_state & 0x01) == 0) {
4398 mf2_flags &= ~0x20;
4399 write_byte(0x0040, 0x18, mf2_flags);
4401 break;
4403 case 0x46: /* Scroll Lock press */
4404 mf2_flags |= 0x10;
4405 write_byte(0x0040, 0x18, mf2_flags);
4406 shift_flags ^= 0x10;
4407 led_flags ^= 0x01;
4408 write_byte(0x0040, 0x17, shift_flags);
4409 write_byte(0x0040, 0x97, led_flags);
4410 break;
4412 case 0xc6: /* Scroll Lock release */
4413 mf2_flags &= ~0x10;
4414 write_byte(0x0040, 0x18, mf2_flags);
4415 break;
4417 default:
4418 if (scancode & 0x80) return; /* toss key releases ... */
4419 if (scancode > MAX_SCAN_CODE) {
4420 BX_INFO("KBD: int09h_handler(): unknown scancode read!");
4421 return;
4423 if (shift_flags & 0x08) { /* ALT */
4424 asciicode = scan_to_scanascii[scancode].alt;
4425 scancode = scan_to_scanascii[scancode].alt >> 8;
4427 else if (shift_flags & 0x04) { /* CONTROL */
4428 asciicode = scan_to_scanascii[scancode].control;
4429 scancode = scan_to_scanascii[scancode].control >> 8;
4431 else if (shift_flags & 0x03) { /* LSHIFT + RSHIFT */
4432 /* check if lock state should be ignored
4433 * because a SHIFT key are pressed */
4435 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
4436 asciicode = scan_to_scanascii[scancode].normal;
4437 scancode = scan_to_scanascii[scancode].normal >> 8;
4439 else {
4440 asciicode = scan_to_scanascii[scancode].shift;
4441 scancode = scan_to_scanascii[scancode].shift >> 8;
4444 else {
4445 /* check if lock is on */
4446 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
4447 asciicode = scan_to_scanascii[scancode].shift;
4448 scancode = scan_to_scanascii[scancode].shift >> 8;
4450 else {
4451 asciicode = scan_to_scanascii[scancode].normal;
4452 scancode = scan_to_scanascii[scancode].normal >> 8;
4455 if (scancode==0 && asciicode==0) {
4456 BX_INFO("KBD: int09h_handler(): scancode & asciicode are zero?");
4458 enqueue_key(scancode, asciicode);
4459 break;
4461 mf2_state &= ~0x01;
4464 unsigned int
4465 enqueue_key(scan_code, ascii_code)
4466 Bit8u scan_code, ascii_code;
4468 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail, temp_tail;
4470 //BX_INFO("KBD: enqueue_key() called scan:%02x, ascii:%02x\n",
4471 // scan_code, ascii_code);
4473 #if BX_CPU < 2
4474 buffer_start = 0x001E;
4475 buffer_end = 0x003E;
4476 #else
4477 buffer_start = read_word(0x0040, 0x0080);
4478 buffer_end = read_word(0x0040, 0x0082);
4479 #endif
4481 buffer_head = read_word(0x0040, 0x001A);
4482 buffer_tail = read_word(0x0040, 0x001C);
4484 temp_tail = buffer_tail;
4485 buffer_tail += 2;
4486 if (buffer_tail >= buffer_end)
4487 buffer_tail = buffer_start;
4489 if (buffer_tail == buffer_head) {
4490 return(0);
4493 write_byte(0x0040, temp_tail, ascii_code);
4494 write_byte(0x0040, temp_tail+1, scan_code);
4495 write_word(0x0040, 0x001C, buffer_tail);
4496 return(1);
4500 void
4501 int74_function(make_farcall, Z, Y, X, status)
4502 Bit16u make_farcall, Z, Y, X, status;
4504 Bit16u ebda_seg=read_word(0x0040,0x000E);
4505 Bit8u in_byte, index, package_count;
4506 Bit8u mouse_flags_1, mouse_flags_2;
4508 BX_DEBUG_INT74("entering int74_function\n");
4509 make_farcall = 0;
4511 in_byte = inb(0x64);
4512 if ( (in_byte & 0x21) != 0x21 ) {
4513 return;
4515 in_byte = inb(0x60);
4516 BX_DEBUG_INT74("int74: read byte %02x\n", in_byte);
4518 mouse_flags_1 = read_byte(ebda_seg, 0x0026);
4519 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4521 if ( (mouse_flags_2 & 0x80) != 0x80 ) {
4522 // BX_PANIC("int74_function:\n");
4523 return;
4526 package_count = mouse_flags_2 & 0x07;
4527 index = mouse_flags_1 & 0x07;
4528 write_byte(ebda_seg, 0x28 + index, in_byte);
4530 if ( (index+1) >= package_count ) {
4531 BX_DEBUG_INT74("int74_function: make_farcall=1\n");
4532 status = read_byte(ebda_seg, 0x0028 + 0);
4533 X = read_byte(ebda_seg, 0x0028 + 1);
4534 Y = read_byte(ebda_seg, 0x0028 + 2);
4535 Z = 0;
4536 mouse_flags_1 = 0;
4537 // check if far call handler installed
4538 if (mouse_flags_2 & 0x80)
4539 make_farcall = 1;
4541 else {
4542 mouse_flags_1++;
4544 write_byte(ebda_seg, 0x0026, mouse_flags_1);
4547 #define SET_DISK_RET_STATUS(status) write_byte(0x0040, 0x0074, status)
4549 #if BX_USE_ATADRV
4551 void
4552 int13_harddisk(DI, SI, BP, SP, BX, DX, CX, AX, DS, ES, FLAGS)
4553 Bit16u DI, SI, BP, SP, BX, DX, CX, AX, DS, ES, FLAGS;
4555 Bit32u lba;
4556 Bit16u ebda_seg=read_word(0x0040,0x000E);
4557 Bit16u cylinder, head, sector;
4558 Bit16u segment, offset;
4559 Bit16u npc, nph, npspt, nlc, nlh, nlspt;
4560 Bit16u size, count;
4561 Bit8u device, status;
4563 BX_DEBUG_INT13_HD("int13_harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
4565 write_byte(0x0040, 0x008e, 0); // clear completion flag
4567 // basic check : device has to be defined
4568 if ( (GET_DL() < 0x80) || (GET_DL() >= 0x80 + BX_MAX_ATA_DEVICES) ) {
4569 BX_INFO("int13_harddisk: function %02x, DL out of range %02x\n", GET_AH(), GET_DL());
4570 goto int13_fail;
4573 // Get the ata channel
4574 device=read_byte(ebda_seg,&EbdaData->ata.hdidmap[GET_DL()-0x80]);
4576 // basic check : device has to be valid
4577 if (device >= BX_MAX_ATA_DEVICES) {
4578 BX_INFO("int13_harddisk: function %02x, unmapped device for DL=%02x\n", GET_AH(), GET_DL());
4579 goto int13_fail;
4582 switch (GET_AH()) {
4584 case 0x00: /* disk controller reset */
4585 ata_reset (device);
4586 goto int13_success;
4587 break;
4589 case 0x01: /* read disk status */
4590 status = read_byte(0x0040, 0x0074);
4591 SET_AH(status);
4592 SET_DISK_RET_STATUS(0);
4593 /* set CF if error status read */
4594 if (status) goto int13_fail_nostatus;
4595 else goto int13_success_noah;
4596 break;
4598 case 0x02: // read disk sectors
4599 case 0x03: // write disk sectors
4600 case 0x04: // verify disk sectors
4602 count = GET_AL();
4603 cylinder = GET_CH();
4604 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
4605 sector = (GET_CL() & 0x3f);
4606 head = GET_DH();
4608 segment = ES;
4609 offset = BX;
4611 if ( (count > 128) || (count == 0) ) {
4612 BX_INFO("int13_harddisk: function %02x, count out of range!\n",GET_AH());
4613 goto int13_fail;
4616 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
4617 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
4618 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
4620 // sanity check on cyl heads, sec
4621 if( (cylinder >= nlc) || (head >= nlh) || (sector > nlspt )) {
4622 BX_INFO("int13_harddisk: function %02x, parameters out of range %04x/%04x/%04x!\n", GET_AH(), cylinder, head, sector);
4623 goto int13_fail;
4626 // FIXME verify
4627 if ( GET_AH() == 0x04 ) goto int13_success;
4629 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
4630 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
4632 // if needed, translate lchs to lba, and execute command
4633 if ( (nph != nlh) || (npspt != nlspt)) {
4634 lba = ((((Bit32u)cylinder * (Bit32u)nlh) + (Bit32u)head) * (Bit32u)nlspt) + (Bit32u)sector - 1;
4635 sector = 0; // this forces the command to be lba
4638 if ( GET_AH() == 0x02 )
4639 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, cylinder, head, sector, lba, segment, offset);
4640 else
4641 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, cylinder, head, sector, lba, segment, offset);
4643 // Set nb of sector transferred
4644 SET_AL(read_word(ebda_seg, &EbdaData->ata.trsfsectors));
4646 if (status != 0) {
4647 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
4648 SET_AH(0x0c);
4649 goto int13_fail_noah;
4652 goto int13_success;
4653 break;
4655 case 0x05: /* format disk track */
4656 BX_INFO("format disk track called\n");
4657 goto int13_success;
4658 return;
4659 break;
4661 case 0x08: /* read disk drive parameters */
4663 // Get logical geometry from table
4664 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
4665 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
4666 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
4667 count = read_byte(ebda_seg, &EbdaData->ata.hdcount);
4669 nlc = nlc - 2; /* 0 based , last sector not used */
4670 SET_AL(0);
4671 SET_CH(nlc & 0xff);
4672 SET_CL(((nlc >> 2) & 0xc0) | (nlspt & 0x3f));
4673 SET_DH(nlh - 1);
4674 SET_DL(count); /* FIXME returns 0, 1, or n hard drives */
4676 // FIXME should set ES & DI
4678 goto int13_success;
4679 break;
4681 case 0x10: /* check drive ready */
4682 // should look at 40:8E also???
4684 // Read the status from controller
4685 status = inb(read_word(ebda_seg, &EbdaData->ata.channels[device/2].iobase1) + ATA_CB_STAT);
4686 if ( (status & ( ATA_CB_STAT_BSY | ATA_CB_STAT_RDY )) == ATA_CB_STAT_RDY ) {
4687 goto int13_success;
4689 else {
4690 SET_AH(0xAA);
4691 goto int13_fail_noah;
4693 break;
4695 case 0x15: /* read disk drive size */
4697 // Get physical geometry from table
4698 npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
4699 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
4700 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
4702 // Compute sector count seen by int13
4703 lba = (Bit32u)(npc - 1) * (Bit32u)nph * (Bit32u)npspt;
4704 CX = lba >> 16;
4705 DX = lba & 0xffff;
4707 SET_AH(3); // hard disk accessible
4708 goto int13_success_noah;
4709 break;
4711 case 0x41: // IBM/MS installation check
4712 BX=0xaa55; // install check
4713 SET_AH(0x30); // EDD 3.0
4714 CX=0x0007; // ext disk access and edd, removable supported
4715 goto int13_success_noah;
4716 break;
4718 case 0x42: // IBM/MS extended read
4719 case 0x43: // IBM/MS extended write
4720 case 0x44: // IBM/MS verify
4721 case 0x47: // IBM/MS extended seek
4723 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
4724 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
4725 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
4727 // Can't use 64 bits lba
4728 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
4729 if (lba != 0L) {
4730 BX_PANIC("int13_harddisk: function %02x. Can't use 64bits lba\n",GET_AH());
4731 goto int13_fail;
4734 // Get 32 bits lba and check
4735 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
4736 if (lba >= read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors) ) {
4737 BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH());
4738 goto int13_fail;
4741 // If verify or seek
4742 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
4743 goto int13_success;
4745 // Execute the command
4746 if ( GET_AH() == 0x42 )
4747 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, 0, 0, 0, lba, segment, offset);
4748 else
4749 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, 0, 0, 0, lba, segment, offset);
4751 count=read_word(ebda_seg, &EbdaData->ata.trsfsectors);
4752 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
4754 if (status != 0) {
4755 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
4756 SET_AH(0x0c);
4757 goto int13_fail_noah;
4760 goto int13_success;
4761 break;
4763 case 0x45: // IBM/MS lock/unlock drive
4764 case 0x49: // IBM/MS extended media change
4765 goto int13_success; // Always success for HD
4766 break;
4768 case 0x46: // IBM/MS eject media
4769 SET_AH(0xb2); // Volume Not Removable
4770 goto int13_fail_noah; // Always fail for HD
4771 break;
4773 case 0x48: // IBM/MS get drive parameters
4774 size=read_word(DS,SI+(Bit16u)&Int13DPT->size);
4776 // Buffer is too small
4777 if(size < 0x1a)
4778 goto int13_fail;
4780 // EDD 1.x
4781 if(size >= 0x1a) {
4782 Bit16u blksize;
4784 npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
4785 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
4786 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
4787 lba = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors);
4788 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
4790 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
4791 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x02); // geometry is valid
4792 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, (Bit32u)npc);
4793 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, (Bit32u)nph);
4794 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, (Bit32u)npspt);
4795 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, lba); // FIXME should be Bit64
4796 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0L);
4797 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
4800 // EDD 2.x
4801 if(size >= 0x1e) {
4802 Bit8u channel, dev, irq, mode, checksum, i, translation;
4803 Bit16u iobase1, iobase2, options;
4805 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
4807 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
4808 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
4810 // Fill in dpte
4811 channel = device / 2;
4812 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
4813 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
4814 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
4815 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
4816 translation = read_byte(ebda_seg, &EbdaData->ata.devices[device].translation);
4818 options = (translation==ATA_TRANSLATION_NONE?0:1<<3); // chs translation
4819 options |= (1<<4); // lba translation
4820 options |= (mode==ATA_MODE_PIO32?1:0<<7);
4821 options |= (translation==ATA_TRANSLATION_LBA?1:0<<9);
4822 options |= (translation==ATA_TRANSLATION_RECHS?3:0<<9);
4824 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
4825 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2);
4826 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
4827 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
4828 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
4829 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
4830 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
4831 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
4832 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
4833 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
4834 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
4836 checksum=0;
4837 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, (&EbdaData->ata.dpte) + i);
4838 checksum = ~checksum;
4839 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
4842 // EDD 3.x
4843 if(size >= 0x42) {
4844 Bit8u channel, iface, checksum, i;
4845 Bit16u iobase1;
4847 channel = device / 2;
4848 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
4849 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
4851 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
4852 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
4853 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
4854 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
4855 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
4857 if (iface==ATA_IFACE_ISA) {
4858 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
4859 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
4860 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
4861 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
4863 else {
4864 // FIXME PCI
4866 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
4867 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
4868 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
4869 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
4871 if (iface==ATA_IFACE_ISA) {
4872 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
4873 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
4874 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
4876 else {
4877 // FIXME PCI
4879 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
4880 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
4881 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
4882 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
4884 checksum=0;
4885 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
4886 checksum = ~checksum;
4887 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
4890 goto int13_success;
4891 break;
4893 case 0x4e: // // IBM/MS set hardware configuration
4894 // DMA, prefetch, PIO maximum not supported
4895 switch (GET_AL()) {
4896 case 0x01:
4897 case 0x03:
4898 case 0x04:
4899 case 0x06:
4900 goto int13_success;
4901 break;
4902 default :
4903 goto int13_fail;
4905 break;
4907 case 0x09: /* initialize drive parameters */
4908 case 0x0c: /* seek to specified cylinder */
4909 case 0x0d: /* alternate disk reset */
4910 case 0x11: /* recalibrate */
4911 case 0x14: /* controller internal diagnostic */
4912 BX_INFO("int13h_harddisk function %02xh unimplemented, returns success\n", GET_AH());
4913 goto int13_success;
4914 break;
4916 case 0x0a: /* read disk sectors with ECC */
4917 case 0x0b: /* write disk sectors with ECC */
4918 case 0x18: // set media type for format
4919 case 0x50: // IBM/MS send packet command
4920 default:
4921 BX_INFO("int13_harddisk function %02xh unsupported, returns fail\n", GET_AH());
4922 goto int13_fail;
4923 break;
4926 int13_fail:
4927 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
4928 int13_fail_noah:
4929 SET_DISK_RET_STATUS(GET_AH());
4930 int13_fail_nostatus:
4931 SET_CF(); // error occurred
4932 return;
4934 int13_success:
4935 SET_AH(0x00); // no error
4936 int13_success_noah:
4937 SET_DISK_RET_STATUS(0x00);
4938 CLEAR_CF(); // no error
4939 return;
4942 // ---------------------------------------------------------------------------
4943 // Start of int13 for cdrom
4944 // ---------------------------------------------------------------------------
4946 void
4947 int13_cdrom(DI, DIH, SI, SIH, BP, BPH, SP, SPH, BX, BXH, DX, DXH, CX, CXH, AX, AXH,
4948 DS, ES, FLAGS)
4949 Bit16u DI, DIH, SI, SIH, BP, BPH, SP, SPH, BX, BXH, DX, DXH, CX, CXH, AX, AXH,
4950 DS, ES, FLAGS;
4952 Bit16u ebda_seg=read_word(0x0040,0x000E);
4953 Bit8u device, status, locks;
4954 Bit8u atacmd[12];
4955 Bit32u lba;
4956 Bit16u count, segment, offset, i, size;
4958 BX_DEBUG_INT13_CD("int13_cdrom: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
4959 // BX_DEBUG_INT13_CD("int13_cdrom: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
4961 SET_DISK_RET_STATUS(0x00);
4963 /* basic check : device should be 0xE0+ */
4964 if( (GET_DL() < 0xE0) || (GET_DL() >= 0xE0+BX_MAX_ATA_DEVICES) ) {
4965 BX_INFO("int13_cdrom: function %02x, DL out of range %02x\n", GET_AH(), GET_DL());
4966 goto int13_fail;
4969 // Get the ata channel
4970 device=read_byte(ebda_seg,&EbdaData->ata.cdidmap[GET_DL()-0xE0]);
4972 /* basic check : device has to be valid */
4973 if (device >= BX_MAX_ATA_DEVICES) {
4974 BX_INFO("int13_cdrom: function %02x, unmapped device for DL=%02x\n", GET_AH(), GET_DL());
4975 goto int13_fail;
4978 switch (GET_AH()) {
4980 // all those functions return SUCCESS
4981 case 0x00: /* disk controller reset */
4982 case 0x09: /* initialize drive parameters */
4983 case 0x0c: /* seek to specified cylinder */
4984 case 0x0d: /* alternate disk reset */
4985 case 0x10: /* check drive ready */
4986 case 0x11: /* recalibrate */
4987 case 0x14: /* controller internal diagnostic */
4988 case 0x16: /* detect disk change */
4989 goto int13_success;
4990 break;
4992 // all those functions return disk write-protected
4993 case 0x03: /* write disk sectors */
4994 case 0x05: /* format disk track */
4995 case 0x43: // IBM/MS extended write
4996 SET_AH(0x03);
4997 goto int13_fail_noah;
4998 break;
5000 case 0x01: /* read disk status */
5001 status = read_byte(0x0040, 0x0074);
5002 SET_AH(status);
5003 SET_DISK_RET_STATUS(0);
5005 /* set CF if error status read */
5006 if (status) goto int13_fail_nostatus;
5007 else goto int13_success_noah;
5008 break;
5010 case 0x15: /* read disk drive size */
5011 SET_AH(0x02);
5012 goto int13_fail_noah;
5013 break;
5015 case 0x41: // IBM/MS installation check
5016 BX=0xaa55; // install check
5017 SET_AH(0x30); // EDD 2.1
5018 CX=0x0007; // ext disk access, removable and edd
5019 goto int13_success_noah;
5020 break;
5022 case 0x42: // IBM/MS extended read
5023 case 0x44: // IBM/MS verify sectors
5024 case 0x47: // IBM/MS extended seek
5026 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
5027 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
5028 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
5030 // Can't use 64 bits lba
5031 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
5032 if (lba != 0L) {
5033 BX_PANIC("int13_cdrom: function %02x. Can't use 64bits lba\n",GET_AH());
5034 goto int13_fail;
5037 // Get 32 bits lba
5038 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5040 // If verify or seek
5041 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
5042 goto int13_success;
5044 memsetb(get_SS(),atacmd,0,12);
5045 atacmd[0]=0x28; // READ command
5046 atacmd[7]=(count & 0xff00) >> 8; // Sectors
5047 atacmd[8]=(count & 0x00ff); // Sectors
5048 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
5049 atacmd[3]=(lba & 0x00ff0000) >> 16;
5050 atacmd[4]=(lba & 0x0000ff00) >> 8;
5051 atacmd[5]=(lba & 0x000000ff);
5052 status = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, count*2048L, ATA_DATA_IN, segment,offset);
5054 count = (Bit16u)(read_dword(ebda_seg, &EbdaData->ata.trsfbytes) >> 11);
5055 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5057 if (status != 0) {
5058 BX_INFO("int13_cdrom: function %02x, status %02x !\n",GET_AH(),status);
5059 SET_AH(0x0c);
5060 goto int13_fail_noah;
5063 goto int13_success;
5064 break;
5066 case 0x45: // IBM/MS lock/unlock drive
5067 if (GET_AL() > 2) goto int13_fail;
5069 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5071 switch (GET_AL()) {
5072 case 0 : // lock
5073 if (locks == 0xff) {
5074 SET_AH(0xb4);
5075 SET_AL(1);
5076 goto int13_fail_noah;
5078 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, ++locks);
5079 SET_AL(1);
5080 break;
5081 case 1 : // unlock
5082 if (locks == 0x00) {
5083 SET_AH(0xb0);
5084 SET_AL(0);
5085 goto int13_fail_noah;
5087 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, --locks);
5088 SET_AL(locks==0?0:1);
5089 break;
5090 case 2 : // status
5091 SET_AL(locks==0?0:1);
5092 break;
5094 goto int13_success;
5095 break;
5097 case 0x46: // IBM/MS eject media
5098 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5100 if (locks != 0) {
5101 SET_AH(0xb1); // media locked
5102 goto int13_fail_noah;
5104 // FIXME should handle 0x31 no media in device
5105 // FIXME should handle 0xb5 valid request failed
5107 // Call removable media eject
5108 ASM_START
5109 push bp
5110 mov bp, sp
5112 mov ah, #0x52
5113 int 15
5114 mov _int13_cdrom.status + 2[bp], ah
5115 jnc int13_cdrom_rme_end
5116 mov _int13_cdrom.status, #1
5117 int13_cdrom_rme_end:
5118 pop bp
5119 ASM_END
5121 if (status != 0) {
5122 SET_AH(0xb1); // media locked
5123 goto int13_fail_noah;
5126 goto int13_success;
5127 break;
5129 case 0x48: // IBM/MS get drive parameters
5130 size = read_word(DS,SI+(Bit16u)&Int13Ext->size);
5132 // Buffer is too small
5133 if(size < 0x1a)
5134 goto int13_fail;
5136 // EDD 1.x
5137 if(size >= 0x1a) {
5138 Bit16u cylinders, heads, spt, blksize;
5140 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5142 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5143 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x74); // removable, media change, lockable, max values
5144 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0xffffffff);
5145 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, 0xffffffff);
5146 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, 0xffffffff);
5147 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, 0xffffffff); // FIXME should be Bit64
5148 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0xffffffff);
5149 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5152 // EDD 2.x
5153 if(size >= 0x1e) {
5154 Bit8u channel, dev, irq, mode, checksum, i;
5155 Bit16u iobase1, iobase2, options;
5157 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5159 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5160 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5162 // Fill in dpte
5163 channel = device / 2;
5164 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5165 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5166 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5167 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5169 // FIXME atapi device
5170 options = (1<<4); // lba translation
5171 options |= (1<<5); // removable device
5172 options |= (1<<6); // atapi device
5173 options |= (mode==ATA_MODE_PIO32?1:0<<7);
5175 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5176 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2);
5177 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5178 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5179 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5180 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5181 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5182 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5183 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5184 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5185 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5187 checksum=0;
5188 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, (&EbdaData->ata.dpte) + i);
5189 checksum = ~checksum;
5190 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5193 // EDD 3.x
5194 if(size >= 0x42) {
5195 Bit8u channel, iface, checksum, i;
5196 Bit16u iobase1;
5198 channel = device / 2;
5199 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5200 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5202 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5203 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5204 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5205 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5206 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5208 if (iface==ATA_IFACE_ISA) {
5209 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5210 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5211 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5212 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5214 else {
5215 // FIXME PCI
5217 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5218 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5219 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5220 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5222 if (iface==ATA_IFACE_ISA) {
5223 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5224 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5225 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5227 else {
5228 // FIXME PCI
5230 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5231 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5232 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5233 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5235 checksum=0;
5236 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5237 checksum = ~checksum;
5238 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5241 goto int13_success;
5242 break;
5244 case 0x49: // IBM/MS extended media change
5245 // always send changed ??
5246 SET_AH(06);
5247 goto int13_fail_nostatus;
5248 break;
5250 case 0x4e: // // IBM/MS set hardware configuration
5251 // DMA, prefetch, PIO maximum not supported
5252 switch (GET_AL()) {
5253 case 0x01:
5254 case 0x03:
5255 case 0x04:
5256 case 0x06:
5257 goto int13_success;
5258 break;
5259 default :
5260 goto int13_fail;
5262 break;
5264 // all those functions return unimplemented
5265 case 0x02: /* read sectors */
5266 case 0x04: /* verify sectors */
5267 case 0x08: /* read disk drive parameters */
5268 case 0x0a: /* read disk sectors with ECC */
5269 case 0x0b: /* write disk sectors with ECC */
5270 case 0x18: /* set media type for format */
5271 case 0x50: // ? - send packet command
5272 default:
5273 BX_INFO("int13_cdrom: unsupported AH=%02x\n", GET_AH());
5274 goto int13_fail;
5275 break;
5278 int13_fail:
5279 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5280 int13_fail_noah:
5281 SET_DISK_RET_STATUS(GET_AH());
5282 int13_fail_nostatus:
5283 SET_CF(); // error occurred
5284 return;
5286 int13_success:
5287 SET_AH(0x00); // no error
5288 int13_success_noah:
5289 SET_DISK_RET_STATUS(0x00);
5290 CLEAR_CF(); // no error
5291 return;
5294 // ---------------------------------------------------------------------------
5295 // End of int13 for cdrom
5296 // ---------------------------------------------------------------------------
5298 #if BX_ELTORITO_BOOT
5299 // ---------------------------------------------------------------------------
5300 // Start of int13 for eltorito functions
5301 // ---------------------------------------------------------------------------
5303 void
5304 int13_eltorito(DI, SI, BP, SP, BX, DX, CX, AX, DS, ES, FLAGS)
5305 Bit16u DI, SI, BP, SP, BX, DX, CX, AX, DS, ES, FLAGS;
5307 Bit16u ebda_seg=read_word(0x0040,0x000E);
5309 BX_DEBUG_INT13_ET("int13_eltorito: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5310 // BX_DEBUG_INT13_ET("int13_eltorito: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
5312 switch (GET_AH()) {
5314 // FIXME ElTorito Various. Should be implemented
5315 case 0x4a: // ElTorito - Initiate disk emu
5316 case 0x4c: // ElTorito - Initiate disk emu and boot
5317 case 0x4d: // ElTorito - Return Boot catalog
5318 BX_PANIC("Int13 eltorito call with AX=%04x. Please report\n",AX);
5319 goto int13_fail;
5320 break;
5322 case 0x4b: // ElTorito - Terminate disk emu
5323 // FIXME ElTorito Hardcoded
5324 write_byte(DS,SI+0x00,0x13);
5325 write_byte(DS,SI+0x01,read_byte(ebda_seg,&EbdaData->cdemu.media));
5326 write_byte(DS,SI+0x02,read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
5327 write_byte(DS,SI+0x03,read_byte(ebda_seg,&EbdaData->cdemu.controller_index));
5328 write_dword(DS,SI+0x04,read_dword(ebda_seg,&EbdaData->cdemu.ilba));
5329 write_word(DS,SI+0x08,read_word(ebda_seg,&EbdaData->cdemu.device_spec));
5330 write_word(DS,SI+0x0a,read_word(ebda_seg,&EbdaData->cdemu.buffer_segment));
5331 write_word(DS,SI+0x0c,read_word(ebda_seg,&EbdaData->cdemu.load_segment));
5332 write_word(DS,SI+0x0e,read_word(ebda_seg,&EbdaData->cdemu.sector_count));
5333 write_byte(DS,SI+0x10,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.cylinders));
5334 write_byte(DS,SI+0x11,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.spt));
5335 write_byte(DS,SI+0x12,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.heads));
5337 // If we have to terminate emulation
5338 if(GET_AL() == 0x00) {
5339 // FIXME ElTorito Various. Should be handled accordingly to spec
5340 write_byte(ebda_seg,&EbdaData->cdemu.active, 0x00); // bye bye
5343 goto int13_success;
5344 break;
5346 default:
5347 BX_INFO("int13_eltorito: unsupported AH=%02x\n", GET_AH());
5348 goto int13_fail;
5349 break;
5352 int13_fail:
5353 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5354 SET_DISK_RET_STATUS(GET_AH());
5355 SET_CF(); // error occurred
5356 return;
5358 int13_success:
5359 SET_AH(0x00); // no error
5360 SET_DISK_RET_STATUS(0x00);
5361 CLEAR_CF(); // no error
5362 return;
5365 // ---------------------------------------------------------------------------
5366 // End of int13 for eltorito functions
5367 // ---------------------------------------------------------------------------
5369 // ---------------------------------------------------------------------------
5370 // Start of int13 when emulating a device from the cd
5371 // ---------------------------------------------------------------------------
5373 void
5374 int13_cdemu(DI, SI, BP, SP, BX, DX, CX, AX, ES, FLAGS)
5375 Bit16u DI, SI, BP, SP, BX, DX, CX, AX, ES, FLAGS;
5377 Bit16u ebda_seg=read_word(0x0040,0x000E);
5378 Bit8u device, status;
5379 Bit16u vheads, vspt, vcylinders;
5380 Bit16u head, sector, cylinder, nbsectors;
5381 Bit32u vlba, ilba, slba, elba;
5382 Bit16u before, segment, offset;
5383 Bit8u atacmd[12];
5385 BX_DEBUG_INT13_ET("int13_cdemu: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5386 //BX_DEBUG_INT13_ET("int13_cdemu: SS=%04x ES=%04x DI=%04x SI=%04x\n", get_SS(), ES, DI, SI);
5388 /* at this point, we are emulating a floppy/harddisk */
5390 // Recompute the device number
5391 device = read_byte(ebda_seg,&EbdaData->cdemu.controller_index) * 2;
5392 device += read_byte(ebda_seg,&EbdaData->cdemu.device_spec);
5394 SET_DISK_RET_STATUS(0x00);
5396 /* basic checks : emulation should be active, dl should equal the emulated drive */
5397 if( (read_byte(ebda_seg,&EbdaData->cdemu.active) ==0 )
5398 || (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive ) != GET_DL())) {
5399 BX_INFO("int13_cdemu: function %02x, emulation not active for DL= %02x\n", GET_AH(), GET_DL());
5400 goto int13_fail;
5403 switch (GET_AH()) {
5405 // all those functions return SUCCESS
5406 case 0x00: /* disk controller reset */
5407 case 0x09: /* initialize drive parameters */
5408 case 0x0c: /* seek to specified cylinder */
5409 case 0x0d: /* alternate disk reset */ // FIXME ElTorito Various. should really reset ?
5410 case 0x10: /* check drive ready */ // FIXME ElTorito Various. should check if ready ?
5411 case 0x11: /* recalibrate */
5412 case 0x14: /* controller internal diagnostic */
5413 case 0x16: /* detect disk change */
5414 goto int13_success;
5415 break;
5417 // all those functions return disk write-protected
5418 case 0x03: /* write disk sectors */
5419 case 0x05: /* format disk track */
5420 SET_AH(0x03);
5421 goto int13_fail_noah;
5422 break;
5424 case 0x01: /* read disk status */
5425 status=read_byte(0x0040, 0x0074);
5426 SET_AH(status);
5427 SET_DISK_RET_STATUS(0);
5429 /* set CF if error status read */
5430 if (status) goto int13_fail_nostatus;
5431 else goto int13_success_noah;
5432 break;
5434 case 0x02: // read disk sectors
5435 case 0x04: // verify disk sectors
5436 vspt = read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
5437 vcylinders = read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders);
5438 vheads = read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads);
5440 ilba = read_dword(ebda_seg,&EbdaData->cdemu.ilba);
5442 sector = GET_CL() & 0x003f;
5443 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
5444 head = GET_DH();
5445 nbsectors = GET_AL();
5446 segment = ES;
5447 offset = BX;
5449 // no sector to read ?
5450 if(nbsectors==0) goto int13_success;
5452 // sanity checks sco openserver needs this!
5453 if ((sector > vspt)
5454 || (cylinder >= vcylinders)
5455 || (head >= vheads)) {
5456 goto int13_fail;
5459 // After controls, verify do nothing
5460 if (GET_AH() == 0x04) goto int13_success;
5462 segment = ES+(BX / 16);
5463 offset = BX % 16;
5465 // calculate the virtual lba inside the image
5466 vlba=((((Bit32u)cylinder*(Bit32u)vheads)+(Bit32u)head)*(Bit32u)vspt)+((Bit32u)(sector-1));
5468 // In advance so we don't loose the count
5469 SET_AL(nbsectors);
5471 // start lba on cd
5472 slba = (Bit32u)vlba/4;
5473 before= (Bit16u)vlba%4;
5475 // end lba on cd
5476 elba = (Bit32u)(vlba+nbsectors-1)/4;
5478 memsetb(get_SS(),atacmd,0,12);
5479 atacmd[0]=0x28; // READ command
5480 atacmd[7]=((Bit16u)(elba-slba+1) & 0xff00) >> 8; // Sectors
5481 atacmd[8]=((Bit16u)(elba-slba+1) & 0x00ff); // Sectors
5482 atacmd[2]=(ilba+slba & 0xff000000) >> 24; // LBA
5483 atacmd[3]=(ilba+slba & 0x00ff0000) >> 16;
5484 atacmd[4]=(ilba+slba & 0x0000ff00) >> 8;
5485 atacmd[5]=(ilba+slba & 0x000000ff);
5486 if((status = ata_cmd_packet(device, 12, get_SS(), atacmd, before*512, nbsectors*512L, ATA_DATA_IN, segment,offset)) != 0) {
5487 BX_INFO("int13_cdemu: function %02x, error %02x !\n",GET_AH(),status);
5488 SET_AH(0x02);
5489 SET_AL(0);
5490 goto int13_fail_noah;
5493 goto int13_success;
5494 break;
5496 case 0x08: /* read disk drive parameters */
5497 vspt=read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
5498 vcylinders=read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders) - 1;
5499 vheads=read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads) - 1;
5501 SET_AL( 0x00 );
5502 SET_BL( 0x00 );
5503 SET_CH( vcylinders & 0xff );
5504 SET_CL((( vcylinders >> 2) & 0xc0) | ( vspt & 0x3f ));
5505 SET_DH( vheads );
5506 SET_DL( 0x02 ); // FIXME ElTorito Various. should send the real count of drives 1 or 2
5507 // FIXME ElTorito Harddisk. should send the HD count
5509 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
5510 case 0x01: SET_BL( 0x02 ); break;
5511 case 0x02: SET_BL( 0x04 ); break;
5512 case 0x03: SET_BL( 0x06 ); break;
5515 DI = 0xefc7;
5516 ES = 0xf000;
5517 goto int13_success;
5518 break;
5520 case 0x15: /* read disk drive size */
5521 // FIXME ElTorito Harddisk. What geometry to send ?
5522 SET_AH(0x03);
5523 goto int13_success_noah;
5524 break;
5526 // all those functions return unimplemented
5527 case 0x0a: /* read disk sectors with ECC */
5528 case 0x0b: /* write disk sectors with ECC */
5529 case 0x18: /* set media type for format */
5530 case 0x41: // IBM/MS installation check
5531 // FIXME ElTorito Harddisk. Darwin would like to use EDD
5532 case 0x42: // IBM/MS extended read
5533 case 0x43: // IBM/MS extended write
5534 case 0x44: // IBM/MS verify sectors
5535 case 0x45: // IBM/MS lock/unlock drive
5536 case 0x46: // IBM/MS eject media
5537 case 0x47: // IBM/MS extended seek
5538 case 0x48: // IBM/MS get drive parameters
5539 case 0x49: // IBM/MS extended media change
5540 case 0x4e: // ? - set hardware configuration
5541 case 0x50: // ? - send packet command
5542 default:
5543 BX_INFO("int13_cdemu function AH=%02x unsupported, returns fail\n", GET_AH());
5544 goto int13_fail;
5545 break;
5548 int13_fail:
5549 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5550 int13_fail_noah:
5551 SET_DISK_RET_STATUS(GET_AH());
5552 int13_fail_nostatus:
5553 SET_CF(); // error occurred
5554 return;
5556 int13_success:
5557 SET_AH(0x00); // no error
5558 int13_success_noah:
5559 SET_DISK_RET_STATUS(0x00);
5560 CLEAR_CF(); // no error
5561 return;
5564 // ---------------------------------------------------------------------------
5565 // End of int13 when emulating a device from the cd
5566 // ---------------------------------------------------------------------------
5568 #endif // BX_ELTORITO_BOOT
5570 #else //BX_USE_ATADRV
5572 void
5573 outLBA(cylinder,hd_heads,head,hd_sectors,sector,dl)
5574 Bit16u cylinder;
5575 Bit16u hd_heads;
5576 Bit16u head;
5577 Bit16u hd_sectors;
5578 Bit16u sector;
5579 Bit16u dl;
5581 ASM_START
5582 push bp
5583 mov bp, sp
5584 push eax
5585 push ebx
5586 push edx
5587 xor eax,eax
5588 mov ax,4[bp] // cylinder
5589 xor ebx,ebx
5590 mov bl,6[bp] // hd_heads
5591 imul ebx
5593 mov bl,8[bp] // head
5594 add eax,ebx
5595 mov bl,10[bp] // hd_sectors
5596 imul ebx
5597 mov bl,12[bp] // sector
5598 add eax,ebx
5600 dec eax
5601 mov dx,#0x1f3
5602 out dx,al
5603 mov dx,#0x1f4
5604 mov al,ah
5605 out dx,al
5606 shr eax,#16
5607 mov dx,#0x1f5
5608 out dx,al
5609 and ah,#0xf
5610 mov bl,14[bp] // dl
5611 and bl,#1
5612 shl bl,#4
5613 or ah,bl
5614 or ah,#0xe0
5615 mov al,ah
5616 mov dx,#0x01f6
5617 out dx,al
5618 pop edx
5619 pop ebx
5620 pop eax
5621 pop bp
5622 ASM_END
5625 void
5626 int13_harddisk(DI, SI, BP, SP, BX, DX, CX, AX, DS, ES, FLAGS)
5627 Bit16u DI, SI, BP, SP, BX, DX, CX, AX, DS, ES, FLAGS;
5629 Bit8u drive, num_sectors, sector, head, status, mod;
5630 Bit8u drive_map;
5631 Bit8u n_drives;
5632 Bit16u cyl_mod, ax;
5633 Bit16u max_cylinder, cylinder, total_sectors;
5634 Bit16u hd_cylinders;
5635 Bit8u hd_heads, hd_sectors;
5636 Bit16u val16;
5637 Bit8u sector_count;
5638 unsigned int i;
5639 Bit16u tempbx;
5640 Bit16u dpsize;
5642 Bit16u count, segment, offset;
5643 Bit32u lba;
5644 Bit16u error;
5646 BX_DEBUG_INT13_HD("int13 harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5648 write_byte(0x0040, 0x008e, 0); // clear completion flag
5650 /* at this point, DL is >= 0x80 to be passed from the floppy int13h
5651 handler code */
5652 /* check how many disks first (cmos reg 0x12), return an error if
5653 drive not present */
5654 drive_map = inb_cmos(0x12);
5655 drive_map = (((drive_map & 0xf0)==0) ? 0 : 1) |
5656 (((drive_map & 0x0f)==0) ? 0 : 2);
5657 n_drives = (drive_map==0) ? 0 :
5658 ((drive_map==3) ? 2 : 1);
5660 if (!(drive_map & (1<<(GET_DL()&0x7f)))) { /* allow 0, 1, or 2 disks */
5661 SET_AH(0x01);
5662 SET_DISK_RET_STATUS(0x01);
5663 SET_CF(); /* error occurred */
5664 return;
5667 switch (GET_AH()) {
5669 case 0x00: /* disk controller reset */
5670 BX_DEBUG_INT13_HD("int13_f00\n");
5672 SET_AH(0);
5673 SET_DISK_RET_STATUS(0);
5674 set_diskette_ret_status(0);
5675 set_diskette_current_cyl(0, 0); /* current cylinder, diskette 1 */
5676 set_diskette_current_cyl(1, 0); /* current cylinder, diskette 2 */
5677 CLEAR_CF(); /* successful */
5678 return;
5679 break;
5681 case 0x01: /* read disk status */
5682 BX_DEBUG_INT13_HD("int13_f01\n");
5683 status = read_byte(0x0040, 0x0074);
5684 SET_AH(status);
5685 SET_DISK_RET_STATUS(0);
5686 /* set CF if error status read */
5687 if (status) SET_CF();
5688 else CLEAR_CF();
5689 return;
5690 break;
5692 case 0x04: // verify disk sectors
5693 case 0x02: // read disk sectors
5694 drive = GET_DL();
5695 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
5697 num_sectors = GET_AL();
5698 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
5699 sector = (GET_CL() & 0x3f);
5700 head = GET_DH();
5703 if (hd_cylinders > 1024) {
5704 if (hd_cylinders <= 2048) {
5705 cylinder <<= 1;
5707 else if (hd_cylinders <= 4096) {
5708 cylinder <<= 2;
5710 else if (hd_cylinders <= 8192) {
5711 cylinder <<= 3;
5713 else { // hd_cylinders <= 16384
5714 cylinder <<= 4;
5717 ax = head / hd_heads;
5718 cyl_mod = ax & 0xff;
5719 head = ax >> 8;
5720 cylinder |= cyl_mod;
5723 if ( (cylinder >= hd_cylinders) ||
5724 (sector > hd_sectors) ||
5725 (head >= hd_heads) ) {
5726 SET_AH(1);
5727 SET_DISK_RET_STATUS(1);
5728 SET_CF(); /* error occurred */
5729 return;
5732 if ( (num_sectors > 128) || (num_sectors == 0) )
5733 BX_PANIC("int13_harddisk(): num_sectors out of range!\n");
5735 if (head > 15)
5736 BX_PANIC("hard drive BIOS:(read/verify) head > 15\n");
5738 if ( GET_AH() == 0x04 ) {
5739 SET_AH(0);
5740 SET_DISK_RET_STATUS(0);
5741 CLEAR_CF();
5742 return;
5745 status = inb(0x1f7);
5746 if (status & 0x80) {
5747 BX_PANIC("hard drive BIOS:(read/verify) BUSY bit set\n");
5749 outb(0x01f2, num_sectors);
5750 /* activate LBA? (tomv) */
5751 if (hd_heads > 16) {
5752 BX_DEBUG_INT13_HD("CHS: %x %x %x\n", cylinder, head, sector);
5753 outLBA(cylinder,hd_heads,head,hd_sectors,sector,drive);
5755 else {
5756 outb(0x01f3, sector);
5757 outb(0x01f4, cylinder & 0x00ff);
5758 outb(0x01f5, cylinder >> 8);
5759 outb(0x01f6, 0xa0 | ((drive & 0x01)<<4) | (head & 0x0f));
5761 outb(0x01f7, 0x20);
5763 while (1) {
5764 status = inb(0x1f7);
5765 if ( !(status & 0x80) ) break;
5768 if (status & 0x01) {
5769 BX_PANIC("hard drive BIOS:(read/verify) read error\n");
5770 } else if ( !(status & 0x08) ) {
5771 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
5772 BX_PANIC("hard drive BIOS:(read/verify) expected DRQ=1\n");
5775 sector_count = 0;
5776 tempbx = BX;
5778 ASM_START
5779 sti ;; enable higher priority interrupts
5780 ASM_END
5782 while (1) {
5783 ASM_START
5784 ;; store temp bx in real DI register
5785 push bp
5786 mov bp, sp
5787 mov di, _int13_harddisk.tempbx + 2 [bp]
5788 pop bp
5790 ;; adjust if there will be an overrun
5791 cmp di, #0xfe00
5792 jbe i13_f02_no_adjust
5793 i13_f02_adjust:
5794 sub di, #0x0200 ; sub 512 bytes from offset
5795 mov ax, es
5796 add ax, #0x0020 ; add 512 to segment
5797 mov es, ax
5799 i13_f02_no_adjust:
5800 mov cx, #0x0100 ;; counter (256 words = 512b)
5801 mov dx, #0x01f0 ;; AT data read port
5804 insw ;; CX words transfered from port(DX) to ES:[DI]
5806 i13_f02_done:
5807 ;; store real DI register back to temp bx
5808 push bp
5809 mov bp, sp
5810 mov _int13_harddisk.tempbx + 2 [bp], di
5811 pop bp
5812 ASM_END
5814 sector_count++;
5815 num_sectors--;
5816 if (num_sectors == 0) {
5817 status = inb(0x1f7);
5818 if ( (status & 0xc9) != 0x40 )
5819 BX_PANIC("no sectors left to read/verify, status is %02x\n", (unsigned) status);
5820 break;
5822 else {
5823 status = inb(0x1f7);
5824 if ( (status & 0xc9) != 0x48 )
5825 BX_PANIC("more sectors left to read/verify, status is %02x\n", (unsigned) status);
5826 continue;
5830 SET_AH(0);
5831 SET_DISK_RET_STATUS(0);
5832 SET_AL(sector_count);
5833 CLEAR_CF(); /* successful */
5834 return;
5835 break;
5838 case 0x03: /* write disk sectors */
5839 BX_DEBUG_INT13_HD("int13_f03\n");
5840 drive = GET_DL ();
5841 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
5843 num_sectors = GET_AL();
5844 cylinder = GET_CH();
5845 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
5846 sector = (GET_CL() & 0x3f);
5847 head = GET_DH();
5849 if (hd_cylinders > 1024) {
5850 if (hd_cylinders <= 2048) {
5851 cylinder <<= 1;
5853 else if (hd_cylinders <= 4096) {
5854 cylinder <<= 2;
5856 else if (hd_cylinders <= 8192) {
5857 cylinder <<= 3;
5859 else { // hd_cylinders <= 16384
5860 cylinder <<= 4;
5863 ax = head / hd_heads;
5864 cyl_mod = ax & 0xff;
5865 head = ax >> 8;
5866 cylinder |= cyl_mod;
5869 if ( (cylinder >= hd_cylinders) ||
5870 (sector > hd_sectors) ||
5871 (head >= hd_heads) ) {
5872 SET_AH( 1);
5873 SET_DISK_RET_STATUS(1);
5874 SET_CF(); /* error occurred */
5875 return;
5878 if ( (num_sectors > 128) || (num_sectors == 0) )
5879 BX_PANIC("int13_harddisk(): num_sectors out of range!\n");
5881 if (head > 15)
5882 BX_PANIC("hard drive BIOS:(read) head > 15\n");
5884 status = inb(0x1f7);
5885 if (status & 0x80) {
5886 BX_PANIC("hard drive BIOS:(read) BUSY bit set\n");
5888 // should check for Drive Ready Bit also in status reg
5889 outb(0x01f2, num_sectors);
5891 /* activate LBA? (tomv) */
5892 if (hd_heads > 16) {
5893 BX_DEBUG_INT13_HD("CHS (write): %x %x %x\n", cylinder, head, sector);
5894 outLBA(cylinder,hd_heads,head,hd_sectors,sector,GET_DL());
5896 else {
5897 outb(0x01f3, sector);
5898 outb(0x01f4, cylinder & 0x00ff);
5899 outb(0x01f5, cylinder >> 8);
5900 outb(0x01f6, 0xa0 | ((GET_DL() & 0x01)<<4) | (head & 0x0f));
5902 outb(0x01f7, 0x30);
5904 // wait for busy bit to turn off after seeking
5905 while (1) {
5906 status = inb(0x1f7);
5907 if ( !(status & 0x80) ) break;
5910 if ( !(status & 0x08) ) {
5911 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
5912 BX_PANIC("hard drive BIOS:(write) data-request bit not set\n");
5915 sector_count = 0;
5916 tempbx = BX;
5918 ASM_START
5919 sti ;; enable higher priority interrupts
5920 ASM_END
5922 while (1) {
5923 ASM_START
5924 ;; store temp bx in real SI register
5925 push bp
5926 mov bp, sp
5927 mov si, _int13_harddisk.tempbx + 2 [bp]
5928 pop bp
5930 ;; adjust if there will be an overrun
5931 cmp si, #0xfe00
5932 jbe i13_f03_no_adjust
5933 i13_f03_adjust:
5934 sub si, #0x0200 ; sub 512 bytes from offset
5935 mov ax, es
5936 add ax, #0x0020 ; add 512 to segment
5937 mov es, ax
5939 i13_f03_no_adjust:
5940 mov cx, #0x0100 ;; counter (256 words = 512b)
5941 mov dx, #0x01f0 ;; AT data read port
5943 seg ES
5945 outsw ;; CX words tranfered from ES:[SI] to port(DX)
5947 ;; store real SI register back to temp bx
5948 push bp
5949 mov bp, sp
5950 mov _int13_harddisk.tempbx + 2 [bp], si
5951 pop bp
5952 ASM_END
5954 sector_count++;
5955 num_sectors--;
5956 if (num_sectors == 0) {
5957 status = inb(0x1f7);
5958 if ( (status & 0xe9) != 0x40 )
5959 BX_PANIC("no sectors left to write, status is %02x\n", (unsigned) status);
5960 break;
5962 else {
5963 status = inb(0x1f7);
5964 if ( (status & 0xc9) != 0x48 )
5965 BX_PANIC("more sectors left to write, status is %02x\n", (unsigned) status);
5966 continue;
5970 SET_AH(0);
5971 SET_DISK_RET_STATUS(0);
5972 SET_AL(sector_count);
5973 CLEAR_CF(); /* successful */
5974 return;
5975 break;
5977 case 0x05: /* format disk track */
5978 BX_DEBUG_INT13_HD("int13_f05\n");
5979 BX_PANIC("format disk track called\n");
5980 /* nop */
5981 SET_AH(0);
5982 SET_DISK_RET_STATUS(0);
5983 CLEAR_CF(); /* successful */
5984 return;
5985 break;
5987 case 0x08: /* read disk drive parameters */
5988 BX_DEBUG_INT13_HD("int13_f08\n");
5990 drive = GET_DL ();
5991 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
5993 // translate CHS
5995 if (hd_cylinders <= 1024) {
5996 // hd_cylinders >>= 0;
5997 // hd_heads <<= 0;
5999 else if (hd_cylinders <= 2048) {
6000 hd_cylinders >>= 1;
6001 hd_heads <<= 1;
6003 else if (hd_cylinders <= 4096) {
6004 hd_cylinders >>= 2;
6005 hd_heads <<= 2;
6007 else if (hd_cylinders <= 8192) {
6008 hd_cylinders >>= 3;
6009 hd_heads <<= 3;
6011 else { // hd_cylinders <= 16384
6012 hd_cylinders >>= 4;
6013 hd_heads <<= 4;
6016 max_cylinder = hd_cylinders - 2; /* 0 based */
6017 SET_AL(0);
6018 SET_CH(max_cylinder & 0xff);
6019 SET_CL(((max_cylinder >> 2) & 0xc0) | (hd_sectors & 0x3f));
6020 SET_DH(hd_heads - 1);
6021 SET_DL(n_drives); /* returns 0, 1, or 2 hard drives */
6022 SET_AH(0);
6023 SET_DISK_RET_STATUS(0);
6024 CLEAR_CF(); /* successful */
6026 return;
6027 break;
6029 case 0x09: /* initialize drive parameters */
6030 BX_DEBUG_INT13_HD("int13_f09\n");
6031 SET_AH(0);
6032 SET_DISK_RET_STATUS(0);
6033 CLEAR_CF(); /* successful */
6034 return;
6035 break;
6037 case 0x0a: /* read disk sectors with ECC */
6038 BX_DEBUG_INT13_HD("int13_f0a\n");
6039 case 0x0b: /* write disk sectors with ECC */
6040 BX_DEBUG_INT13_HD("int13_f0b\n");
6041 BX_PANIC("int13h Functions 0Ah & 0Bh not implemented!\n");
6042 return;
6043 break;
6045 case 0x0c: /* seek to specified cylinder */
6046 BX_DEBUG_INT13_HD("int13_f0c\n");
6047 BX_INFO("int13h function 0ch (seek) not implemented!\n");
6048 SET_AH(0);
6049 SET_DISK_RET_STATUS(0);
6050 CLEAR_CF(); /* successful */
6051 return;
6052 break;
6054 case 0x0d: /* alternate disk reset */
6055 BX_DEBUG_INT13_HD("int13_f0d\n");
6056 SET_AH(0);
6057 SET_DISK_RET_STATUS(0);
6058 CLEAR_CF(); /* successful */
6059 return;
6060 break;
6062 case 0x10: /* check drive ready */
6063 BX_DEBUG_INT13_HD("int13_f10\n");
6064 //SET_AH(0);
6065 //SET_DISK_RET_STATUS(0);
6066 //CLEAR_CF(); /* successful */
6067 //return;
6068 //break;
6070 // should look at 40:8E also???
6071 status = inb(0x01f7);
6072 if ( (status & 0xc0) == 0x40 ) {
6073 SET_AH(0);
6074 SET_DISK_RET_STATUS(0);
6075 CLEAR_CF(); // drive ready
6076 return;
6078 else {
6079 SET_AH(0xAA);
6080 SET_DISK_RET_STATUS(0xAA);
6081 SET_CF(); // not ready
6082 return;
6084 break;
6086 case 0x11: /* recalibrate */
6087 BX_DEBUG_INT13_HD("int13_f11\n");
6088 SET_AH(0);
6089 SET_DISK_RET_STATUS(0);
6090 CLEAR_CF(); /* successful */
6091 return;
6092 break;
6094 case 0x14: /* controller internal diagnostic */
6095 BX_DEBUG_INT13_HD("int13_f14\n");
6096 SET_AH(0);
6097 SET_DISK_RET_STATUS(0);
6098 CLEAR_CF(); /* successful */
6099 SET_AL(0);
6100 return;
6101 break;
6103 case 0x15: /* read disk drive size */
6104 drive = GET_DL();
6105 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6106 ASM_START
6107 push bp
6108 mov bp, sp
6109 mov al, _int13_harddisk.hd_heads + 2 [bp]
6110 mov ah, _int13_harddisk.hd_sectors + 2 [bp]
6111 mul al, ah ;; ax = heads * sectors
6112 mov bx, _int13_harddisk.hd_cylinders + 2 [bp]
6113 dec bx ;; use (cylinders - 1) ???
6114 mul ax, bx ;; dx:ax = (cylinders -1) * (heads * sectors)
6115 ;; now we need to move the 32bit result dx:ax to what the
6116 ;; BIOS wants which is cx:dx.
6117 ;; and then into CX:DX on the stack
6118 mov _int13_harddisk.CX + 2 [bp], dx
6119 mov _int13_harddisk.DX + 2 [bp], ax
6120 pop bp
6121 ASM_END
6122 SET_AH(3); // hard disk accessible
6123 SET_DISK_RET_STATUS(0); // ??? should this be 0
6124 CLEAR_CF(); // successful
6125 return;
6126 break;
6128 case 0x18: // set media type for format
6129 case 0x41: // IBM/MS
6130 case 0x42: // IBM/MS
6131 case 0x43: // IBM/MS
6132 case 0x44: // IBM/MS
6133 case 0x45: // IBM/MS lock/unlock drive
6134 case 0x46: // IBM/MS eject media
6135 case 0x47: // IBM/MS extended seek
6136 case 0x49: // IBM/MS extended media change
6137 case 0x50: // IBM/MS send packet command
6138 default:
6139 BX_INFO("int13_harddisk: unsupported AH=%02x\n", GET_AH());
6141 SET_AH(1); // code=invalid function in AH or invalid parameter
6142 SET_DISK_RET_STATUS(1);
6143 SET_CF(); /* unsuccessful */
6144 return;
6145 break;
6149 static char panic_msg_reg12h[] = "HD%d cmos reg 12h not type F";
6150 static char panic_msg_reg19h[] = "HD%d cmos reg %02xh not user definable type 47";
6152 void
6153 get_hd_geometry(drive, hd_cylinders, hd_heads, hd_sectors)
6154 Bit8u drive;
6155 Bit16u *hd_cylinders;
6156 Bit8u *hd_heads;
6157 Bit8u *hd_sectors;
6159 Bit8u hd_type;
6160 Bit16u ss;
6161 Bit16u cylinders;
6162 Bit8u iobase;
6164 ss = get_SS();
6165 if (drive == 0x80) {
6166 hd_type = inb_cmos(0x12) & 0xf0;
6167 if (hd_type != 0xf0)
6168 BX_INFO(panic_msg_reg12h,0);
6169 hd_type = inb_cmos(0x19); // HD0: extended type
6170 if (hd_type != 47)
6171 BX_INFO(panic_msg_reg19h,0,0x19);
6172 iobase = 0x1b;
6173 } else {
6174 hd_type = inb_cmos(0x12) & 0x0f;
6175 if (hd_type != 0x0f)
6176 BX_INFO(panic_msg_reg12h,1);
6177 hd_type = inb_cmos(0x1a); // HD0: extended type
6178 if (hd_type != 47)
6179 BX_INFO(panic_msg_reg19h,0,0x1a);
6180 iobase = 0x24;
6183 // cylinders
6184 cylinders = inb_cmos(iobase) | (inb_cmos(iobase+1) << 8);
6185 write_word(ss, hd_cylinders, cylinders);
6187 // heads
6188 write_byte(ss, hd_heads, inb_cmos(iobase+2));
6190 // sectors per track
6191 write_byte(ss, hd_sectors, inb_cmos(iobase+8));
6194 #endif //else BX_USE_ATADRV
6197 //////////////////////
6198 // FLOPPY functions //
6199 //////////////////////
6201 bx_bool
6202 floppy_media_known(drive)
6203 Bit16u drive;
6205 Bit8u val8;
6206 Bit16u media_state_offset;
6208 val8 = read_byte(0x0040, 0x003e); // diskette recal status
6209 if (drive)
6210 val8 >>= 1;
6211 val8 &= 0x01;
6212 if (val8 == 0)
6213 return(0);
6215 media_state_offset = 0x0090;
6216 if (drive)
6217 media_state_offset += 1;
6219 val8 = read_byte(0x0040, media_state_offset);
6220 val8 = (val8 >> 4) & 0x01;
6221 if (val8 == 0)
6222 return(0);
6224 // check pass, return KNOWN
6225 return(1);
6228 bx_bool
6229 floppy_media_sense(drive)
6230 Bit16u drive;
6232 bx_bool retval;
6233 Bit16u media_state_offset;
6234 Bit8u drive_type, config_data, media_state;
6236 if (floppy_drive_recal(drive) == 0) {
6237 return(0);
6240 // for now cheat and get drive type from CMOS,
6241 // assume media is same as drive type
6243 // ** config_data **
6244 // Bitfields for diskette media control:
6245 // Bit(s) Description (Table M0028)
6246 // 7-6 last data rate set by controller
6247 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6248 // 5-4 last diskette drive step rate selected
6249 // 00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
6250 // 3-2 {data rate at start of operation}
6251 // 1-0 reserved
6253 // ** media_state **
6254 // Bitfields for diskette drive media state:
6255 // Bit(s) Description (Table M0030)
6256 // 7-6 data rate
6257 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6258 // 5 double stepping required (e.g. 360kB in 1.2MB)
6259 // 4 media type established
6260 // 3 drive capable of supporting 4MB media
6261 // 2-0 on exit from BIOS, contains
6262 // 000 trying 360kB in 360kB
6263 // 001 trying 360kB in 1.2MB
6264 // 010 trying 1.2MB in 1.2MB
6265 // 011 360kB in 360kB established
6266 // 100 360kB in 1.2MB established
6267 // 101 1.2MB in 1.2MB established
6268 // 110 reserved
6269 // 111 all other formats/drives
6271 drive_type = inb_cmos(0x10);
6272 if (drive == 0)
6273 drive_type >>= 4;
6274 else
6275 drive_type &= 0x0f;
6276 if ( drive_type == 1 ) {
6277 // 360K 5.25" drive
6278 config_data = 0x00; // 0000 0000
6279 media_state = 0x25; // 0010 0101
6280 retval = 1;
6282 else if ( drive_type == 2 ) {
6283 // 1.2 MB 5.25" drive
6284 config_data = 0x00; // 0000 0000
6285 media_state = 0x25; // 0010 0101 // need double stepping??? (bit 5)
6286 retval = 1;
6288 else if ( drive_type == 3 ) {
6289 // 720K 3.5" drive
6290 config_data = 0x00; // 0000 0000 ???
6291 media_state = 0x17; // 0001 0111
6292 retval = 1;
6294 else if ( drive_type == 4 ) {
6295 // 1.44 MB 3.5" drive
6296 config_data = 0x00; // 0000 0000
6297 media_state = 0x17; // 0001 0111
6298 retval = 1;
6300 else if ( drive_type == 5 ) {
6301 // 2.88 MB 3.5" drive
6302 config_data = 0xCC; // 1100 1100
6303 media_state = 0xD7; // 1101 0111
6304 retval = 1;
6307 // Extended floppy size uses special cmos setting
6308 else if ( drive_type == 6 ) {
6309 // 160k 5.25" drive
6310 config_data = 0x00; // 0000 0000
6311 media_state = 0x27; // 0010 0111
6312 retval = 1;
6314 else if ( drive_type == 7 ) {
6315 // 180k 5.25" drive
6316 config_data = 0x00; // 0000 0000
6317 media_state = 0x27; // 0010 0111
6318 retval = 1;
6320 else if ( drive_type == 8 ) {
6321 // 320k 5.25" drive
6322 config_data = 0x00; // 0000 0000
6323 media_state = 0x27; // 0010 0111
6324 retval = 1;
6327 else {
6328 // not recognized
6329 config_data = 0x00; // 0000 0000
6330 media_state = 0x00; // 0000 0000
6331 retval = 0;
6334 if (drive == 0)
6335 media_state_offset = 0x90;
6336 else
6337 media_state_offset = 0x91;
6338 write_byte(0x0040, 0x008B, config_data);
6339 write_byte(0x0040, media_state_offset, media_state);
6341 return(retval);
6344 bx_bool
6345 floppy_drive_recal(drive)
6346 Bit16u drive;
6348 Bit8u val8, dor;
6349 Bit16u curr_cyl_offset;
6351 // set 40:3e bit 7 to 0
6352 val8 = read_byte(0x0000, 0x043e);
6353 val8 &= 0x7f;
6354 write_byte(0x0000, 0x043e, val8);
6356 // turn on motor of selected drive, DMA & int enabled, normal operation
6357 if (drive)
6358 dor = 0x20;
6359 else
6360 dor = 0x10;
6361 dor |= 0x0c;
6362 dor |= drive;
6363 outb(0x03f2, dor);
6365 // reset the disk motor timeout value of INT 08
6366 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
6368 // check port 3f4 for drive readiness
6369 val8 = inb(0x3f4);
6370 if ( (val8 & 0xf0) != 0x80 )
6371 BX_PANIC("floppy recal:f07: ctrl not ready\n");
6373 // send Recalibrate command (2 bytes) to controller
6374 outb(0x03f5, 0x07); // 07: Recalibrate
6375 outb(0x03f5, drive); // 0=drive0, 1=drive1
6377 // turn on interrupts
6378 ASM_START
6380 ASM_END
6382 // wait on 40:3e bit 7 to become 1
6383 val8 = (read_byte(0x0000, 0x043e) & 0x80);
6384 while ( val8 == 0 ) {
6385 val8 = (read_byte(0x0000, 0x043e) & 0x80);
6388 val8 = 0; // separate asm from while() loop
6389 // turn off interrupts
6390 ASM_START
6392 ASM_END
6394 // set 40:3e bit 7 to 0, and calibrated bit
6395 val8 = read_byte(0x0000, 0x043e);
6396 val8 &= 0x7f;
6397 if (drive) {
6398 val8 |= 0x02; // Drive 1 calibrated
6399 curr_cyl_offset = 0x0095;
6401 else {
6402 val8 |= 0x01; // Drive 0 calibrated
6403 curr_cyl_offset = 0x0094;
6405 write_byte(0x0040, 0x003e, val8);
6406 write_byte(0x0040, curr_cyl_offset, 0); // current cylinder is 0
6408 return(1);
6413 bx_bool
6414 floppy_drive_exists(drive)
6415 Bit16u drive;
6417 Bit8u drive_type;
6419 // check CMOS to see if drive exists
6420 drive_type = inb_cmos(0x10);
6421 if (drive == 0)
6422 drive_type >>= 4;
6423 else
6424 drive_type &= 0x0f;
6425 if ( drive_type == 0 )
6426 return(0);
6427 else
6428 return(1);
6431 #if BX_SUPPORT_FLOPPY
6432 void
6433 int13_diskette_function(DI, SI, BP, SP, BX, DX, CX, AX, ES, FLAGS)
6434 Bit16u DI, SI, BP, SP, BX, DX, CX, AX, ES, FLAGS;
6436 Bit8u drive, num_sectors, track, sector, head, status;
6437 Bit16u base_address, base_count, base_es;
6438 Bit8u page, mode_register, val8, dor;
6439 Bit8u return_status[7];
6440 Bit8u drive_type, num_floppies, ah;
6441 Bit16u es, last_addr;
6443 BX_DEBUG_INT13_FL("int13_diskette: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
6444 // BX_DEBUG_INT13_FL("int13_diskette: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), get_DS(), ES, DI, SI);
6446 ah = GET_AH();
6448 switch ( ah ) {
6449 case 0x00: // diskette controller reset
6450 BX_DEBUG_INT13_FL("floppy f00\n");
6451 drive = GET_DL();
6452 if (drive > 1) {
6453 SET_AH(1); // invalid param
6454 set_diskette_ret_status(1);
6455 SET_CF();
6456 return;
6458 drive_type = inb_cmos(0x10);
6460 if (drive == 0)
6461 drive_type >>= 4;
6462 else
6463 drive_type &= 0x0f;
6464 if (drive_type == 0) {
6465 SET_AH(0x80); // drive not responding
6466 set_diskette_ret_status(0x80);
6467 SET_CF();
6468 return;
6470 SET_AH(0);
6471 set_diskette_ret_status(0);
6472 CLEAR_CF(); // successful
6473 set_diskette_current_cyl(drive, 0); // current cylinder
6474 return;
6476 case 0x01: // Read Diskette Status
6477 CLEAR_CF();
6478 val8 = read_byte(0x0000, 0x0441);
6479 SET_AH(val8);
6480 if (val8) {
6481 SET_CF();
6483 return;
6485 case 0x02: // Read Diskette Sectors
6486 case 0x03: // Write Diskette Sectors
6487 case 0x04: // Verify Diskette Sectors
6488 num_sectors = GET_AL();
6489 track = GET_CH();
6490 sector = GET_CL();
6491 head = GET_DH();
6492 drive = GET_DL();
6494 if ( (drive > 1) || (head > 1) ||
6495 (num_sectors == 0) || (num_sectors > 72) ) {
6496 BX_INFO("floppy: drive>1 || head>1 ...\n");
6497 SET_AH(1);
6498 set_diskette_ret_status(1);
6499 SET_AL(0); // no sectors read
6500 SET_CF(); // error occurred
6501 return;
6504 // see if drive exists
6505 if (floppy_drive_exists(drive) == 0) {
6506 SET_AH(0x80); // not responding
6507 set_diskette_ret_status(0x80);
6508 SET_AL(0); // no sectors read
6509 SET_CF(); // error occurred
6510 return;
6513 // see if media in drive, and type is known
6514 if (floppy_media_known(drive) == 0) {
6515 if (floppy_media_sense(drive) == 0) {
6516 SET_AH(0x0C); // Media type not found
6517 set_diskette_ret_status(0x0C);
6518 SET_AL(0); // no sectors read
6519 SET_CF(); // error occurred
6520 return;
6524 if (ah == 0x02) {
6525 // Read Diskette Sectors
6527 //-----------------------------------
6528 // set up DMA controller for transfer
6529 //-----------------------------------
6531 // es:bx = pointer to where to place information from diskette
6532 // port 04: DMA-1 base and current address, channel 2
6533 // port 05: DMA-1 base and current count, channel 2
6534 page = (ES >> 12); // upper 4 bits
6535 base_es = (ES << 4); // lower 16bits contributed by ES
6536 base_address = base_es + BX; // lower 16 bits of address
6537 // contributed by ES:BX
6538 if ( base_address < base_es ) {
6539 // in case of carry, adjust page by 1
6540 page++;
6542 base_count = (num_sectors * 512) - 1;
6544 // check for 64K boundary overrun
6545 last_addr = base_address + base_count;
6546 if (last_addr < base_address) {
6547 SET_AH(0x09);
6548 set_diskette_ret_status(0x09);
6549 SET_AL(0); // no sectors read
6550 SET_CF(); // error occurred
6551 return;
6554 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
6555 outb(0x000a, 0x06);
6557 BX_DEBUG_INT13_FL("clear flip-flop\n");
6558 outb(0x000c, 0x00); // clear flip-flop
6559 outb(0x0004, base_address);
6560 outb(0x0004, base_address>>8);
6561 BX_DEBUG_INT13_FL("clear flip-flop\n");
6562 outb(0x000c, 0x00); // clear flip-flop
6563 outb(0x0005, base_count);
6564 outb(0x0005, base_count>>8);
6566 // port 0b: DMA-1 Mode Register
6567 mode_register = 0x46; // single mode, increment, autoinit disable,
6568 // transfer type=write, channel 2
6569 BX_DEBUG_INT13_FL("setting mode register\n");
6570 outb(0x000b, mode_register);
6572 BX_DEBUG_INT13_FL("setting page register\n");
6573 // port 81: DMA-1 Page Register, channel 2
6574 outb(0x0081, page);
6576 BX_DEBUG_INT13_FL("unmask chan 2\n");
6577 outb(0x000a, 0x02); // unmask channel 2
6579 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
6580 outb(0x000a, 0x02);
6582 //--------------------------------------
6583 // set up floppy controller for transfer
6584 //--------------------------------------
6586 // set 40:3e bit 7 to 0
6587 val8 = read_byte(0x0000, 0x043e);
6588 val8 &= 0x7f;
6589 write_byte(0x0000, 0x043e, val8);
6591 // turn on motor of selected drive, DMA & int enabled, normal operation
6592 if (drive)
6593 dor = 0x20;
6594 else
6595 dor = 0x10;
6596 dor |= 0x0c;
6597 dor |= drive;
6598 outb(0x03f2, dor);
6600 // reset the disk motor timeout value of INT 08
6601 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
6603 // check port 3f4 for drive readiness
6604 val8 = inb(0x3f4);
6605 if ( (val8 & 0xf0) != 0x80 )
6606 BX_PANIC("int13_diskette:f02: ctrl not ready\n");
6608 // send read-normal-data command (9 bytes) to controller
6609 outb(0x03f5, 0xe6); // e6: read normal data
6610 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
6611 outb(0x03f5, track);
6612 outb(0x03f5, head);
6613 outb(0x03f5, sector);
6614 outb(0x03f5, 2); // 512 byte sector size
6615 outb(0x03f5, 0); // last sector number possible on track
6616 outb(0x03f5, 0); // Gap length
6617 outb(0x03f5, 0xff); // Gap length
6619 // turn on interrupts
6620 ASM_START
6622 ASM_END
6624 // wait on 40:3e bit 7 to become 1
6625 val8 = (read_byte(0x0000, 0x043e) & 0x80);
6626 while ( val8 == 0 ) {
6627 val8 = (read_byte(0x0000, 0x043e) & 0x80);
6630 val8 = 0; // separate asm from while() loop
6631 // turn off interrupts
6632 ASM_START
6634 ASM_END
6636 // set 40:3e bit 7 to 0
6637 val8 = read_byte(0x0000, 0x043e);
6638 val8 &= 0x7f;
6639 write_byte(0x0000, 0x043e, val8);
6641 // check port 3f4 for accessibility to status bytes
6642 val8 = inb(0x3f4);
6643 if ( (val8 & 0xc0) != 0xc0 )
6644 BX_PANIC("int13_diskette: ctrl not ready\n");
6646 // read 7 return status bytes from controller
6647 // using loop index broken, have to unroll...
6648 return_status[0] = inb(0x3f5);
6649 return_status[1] = inb(0x3f5);
6650 return_status[2] = inb(0x3f5);
6651 return_status[3] = inb(0x3f5);
6652 return_status[4] = inb(0x3f5);
6653 return_status[5] = inb(0x3f5);
6654 return_status[6] = inb(0x3f5);
6655 // record in BIOS Data Area
6656 write_byte(0x0040, 0x0042, return_status[0]);
6657 write_byte(0x0040, 0x0043, return_status[1]);
6658 write_byte(0x0040, 0x0044, return_status[2]);
6659 write_byte(0x0040, 0x0045, return_status[3]);
6660 write_byte(0x0040, 0x0046, return_status[4]);
6661 write_byte(0x0040, 0x0047, return_status[5]);
6662 write_byte(0x0040, 0x0048, return_status[6]);
6664 if ( (return_status[0] & 0xc0) != 0 ) {
6665 SET_AH(0x20);
6666 set_diskette_ret_status(0x20);
6667 SET_AL(0); // no sectors read
6668 SET_CF(); // error occurred
6669 return;
6672 // ??? should track be new val from return_status[3] ?
6673 set_diskette_current_cyl(drive, track);
6674 // AL = number of sectors read (same value as passed)
6675 SET_AH(0x00); // success
6676 CLEAR_CF(); // success
6677 return;
6679 else if (ah == 0x03) {
6680 // Write Diskette Sectors
6682 //-----------------------------------
6683 // set up DMA controller for transfer
6684 //-----------------------------------
6686 // es:bx = pointer to where to place information from diskette
6687 // port 04: DMA-1 base and current address, channel 2
6688 // port 05: DMA-1 base and current count, channel 2
6689 page = (ES >> 12); // upper 4 bits
6690 base_es = (ES << 4); // lower 16bits contributed by ES
6691 base_address = base_es + BX; // lower 16 bits of address
6692 // contributed by ES:BX
6693 if ( base_address < base_es ) {
6694 // in case of carry, adjust page by 1
6695 page++;
6697 base_count = (num_sectors * 512) - 1;
6699 // check for 64K boundary overrun
6700 last_addr = base_address + base_count;
6701 if (last_addr < base_address) {
6702 SET_AH(0x09);
6703 set_diskette_ret_status(0x09);
6704 SET_AL(0); // no sectors read
6705 SET_CF(); // error occurred
6706 return;
6709 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
6710 outb(0x000a, 0x06);
6712 outb(0x000c, 0x00); // clear flip-flop
6713 outb(0x0004, base_address);
6714 outb(0x0004, base_address>>8);
6715 outb(0x000c, 0x00); // clear flip-flop
6716 outb(0x0005, base_count);
6717 outb(0x0005, base_count>>8);
6719 // port 0b: DMA-1 Mode Register
6720 mode_register = 0x4a; // single mode, increment, autoinit disable,
6721 // transfer type=read, channel 2
6722 outb(0x000b, mode_register);
6724 // port 81: DMA-1 Page Register, channel 2
6725 outb(0x0081, page);
6727 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
6728 outb(0x000a, 0x02);
6730 //--------------------------------------
6731 // set up floppy controller for transfer
6732 //--------------------------------------
6734 // set 40:3e bit 7 to 0
6735 val8 = read_byte(0x0000, 0x043e);
6736 val8 &= 0x7f;
6737 write_byte(0x0000, 0x043e, val8);
6739 // turn on motor of selected drive, DMA & int enabled, normal operation
6740 if (drive)
6741 dor = 0x20;
6742 else
6743 dor = 0x10;
6744 dor |= 0x0c;
6745 dor |= drive;
6746 outb(0x03f2, dor);
6748 // reset the disk motor timeout value of INT 08
6749 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
6751 // check port 3f4 for drive readiness
6752 val8 = inb(0x3f4);
6753 if ( (val8 & 0xf0) != 0x80 )
6754 BX_PANIC("int13_diskette:f03: ctrl not ready\n");
6756 // send read-normal-data command (9 bytes) to controller
6757 outb(0x03f5, 0xc5); // c5: write normal data
6758 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
6759 outb(0x03f5, track);
6760 outb(0x03f5, head);
6761 outb(0x03f5, sector);
6762 outb(0x03f5, 2); // 512 byte sector size
6763 outb(0x03f5, 0); // last sector number possible on track
6764 outb(0x03f5, 0); // Gap length
6765 outb(0x03f5, 0xff); // Gap length
6767 // turn on interrupts
6768 ASM_START
6770 ASM_END
6772 // wait on 40:3e bit 7 to become 1
6773 val8 = (read_byte(0x0000, 0x043e) & 0x80);
6774 while ( val8 == 0 ) {
6775 val8 = (read_byte(0x0000, 0x043e) & 0x80);
6778 val8 = 0; // separate asm from while() loop
6779 // turn off interrupts
6780 ASM_START
6782 ASM_END
6784 // set 40:3e bit 7 to 0
6785 val8 = read_byte(0x0000, 0x043e);
6786 val8 &= 0x7f;
6787 write_byte(0x0000, 0x043e, val8);
6789 // check port 3f4 for accessibility to status bytes
6790 val8 = inb(0x3f4);
6791 if ( (val8 & 0xc0) != 0xc0 )
6792 BX_PANIC("int13_diskette: ctrl not ready\n");
6794 // read 7 return status bytes from controller
6795 // using loop index broken, have to unroll...
6796 return_status[0] = inb(0x3f5);
6797 return_status[1] = inb(0x3f5);
6798 return_status[2] = inb(0x3f5);
6799 return_status[3] = inb(0x3f5);
6800 return_status[4] = inb(0x3f5);
6801 return_status[5] = inb(0x3f5);
6802 return_status[6] = inb(0x3f5);
6803 // record in BIOS Data Area
6804 write_byte(0x0040, 0x0042, return_status[0]);
6805 write_byte(0x0040, 0x0043, return_status[1]);
6806 write_byte(0x0040, 0x0044, return_status[2]);
6807 write_byte(0x0040, 0x0045, return_status[3]);
6808 write_byte(0x0040, 0x0046, return_status[4]);
6809 write_byte(0x0040, 0x0047, return_status[5]);
6810 write_byte(0x0040, 0x0048, return_status[6]);
6812 if ( (return_status[0] & 0xc0) != 0 ) {
6813 if ( (return_status[1] & 0x02) != 0 ) {
6814 // diskette not writable.
6815 // AH=status code=0x03 (tried to write on write-protected disk)
6816 // AL=number of sectors written=0
6817 AX = 0x0300;
6818 SET_CF();
6819 return;
6820 } else {
6821 BX_PANIC("int13_diskette_function: read error\n");
6825 // ??? should track be new val from return_status[3] ?
6826 set_diskette_current_cyl(drive, track);
6827 // AL = number of sectors read (same value as passed)
6828 SET_AH(0x00); // success
6829 CLEAR_CF(); // success
6830 return;
6832 else { // if (ah == 0x04)
6833 // Verify Diskette Sectors
6835 // ??? should track be new val from return_status[3] ?
6836 set_diskette_current_cyl(drive, track);
6837 // AL = number of sectors verified (same value as passed)
6838 CLEAR_CF(); // success
6839 SET_AH(0x00); // success
6840 return;
6844 case 0x05: // format diskette track
6845 BX_DEBUG_INT13_FL("floppy f05\n");
6847 num_sectors = GET_AL();
6848 track = GET_CH();
6849 head = GET_DH();
6850 drive = GET_DL();
6852 if ((drive > 1) || (head > 1) || (track > 79) ||
6853 (num_sectors == 0) || (num_sectors > 18)) {
6854 SET_AH(1);
6855 set_diskette_ret_status(1);
6856 SET_CF(); // error occurred
6859 // see if drive exists
6860 if (floppy_drive_exists(drive) == 0) {
6861 SET_AH(0x80); // drive not responding
6862 set_diskette_ret_status(0x80);
6863 SET_CF(); // error occurred
6864 return;
6867 // see if media in drive, and type is known
6868 if (floppy_media_known(drive) == 0) {
6869 if (floppy_media_sense(drive) == 0) {
6870 SET_AH(0x0C); // Media type not found
6871 set_diskette_ret_status(0x0C);
6872 SET_AL(0); // no sectors read
6873 SET_CF(); // error occurred
6874 return;
6878 // set up DMA controller for transfer
6879 page = (ES >> 12); // upper 4 bits
6880 base_es = (ES << 4); // lower 16bits contributed by ES
6881 base_address = base_es + BX; // lower 16 bits of address
6882 // contributed by ES:BX
6883 if ( base_address < base_es ) {
6884 // in case of carry, adjust page by 1
6885 page++;
6887 base_count = (num_sectors * 4) - 1;
6889 // check for 64K boundary overrun
6890 last_addr = base_address + base_count;
6891 if (last_addr < base_address) {
6892 SET_AH(0x09);
6893 set_diskette_ret_status(0x09);
6894 SET_AL(0); // no sectors read
6895 SET_CF(); // error occurred
6896 return;
6899 outb(0x000a, 0x06);
6900 outb(0x000c, 0x00); // clear flip-flop
6901 outb(0x0004, base_address);
6902 outb(0x0004, base_address>>8);
6903 outb(0x000c, 0x00); // clear flip-flop
6904 outb(0x0005, base_count);
6905 outb(0x0005, base_count>>8);
6906 mode_register = 0x4a; // single mode, increment, autoinit disable,
6907 // transfer type=read, channel 2
6908 outb(0x000b, mode_register);
6909 // port 81: DMA-1 Page Register, channel 2
6910 outb(0x0081, page);
6911 outb(0x000a, 0x02);
6913 // set up floppy controller for transfer
6914 val8 = read_byte(0x0000, 0x043e);
6915 val8 &= 0x7f;
6916 write_byte(0x0000, 0x043e, val8);
6917 // turn on motor of selected drive, DMA & int enabled, normal operation
6918 if (drive)
6919 dor = 0x20;
6920 else
6921 dor = 0x10;
6922 dor |= 0x0c;
6923 dor |= drive;
6924 outb(0x03f2, dor);
6926 // reset the disk motor timeout value of INT 08
6927 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
6929 // check port 3f4 for drive readiness
6930 val8 = inb(0x3f4);
6931 if ( (val8 & 0xf0) != 0x80 )
6932 BX_PANIC("int13_diskette:f05: ctrl not ready\n");
6934 // send read-normal-data command (6 bytes) to controller
6935 outb(0x03f5, 0x4d); // 4d: format track
6936 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
6937 outb(0x03f5, 2); // 512 byte sector size
6938 outb(0x03f5, num_sectors); // number of sectors per track
6939 outb(0x03f5, 0); // Gap length
6940 outb(0x03f5, 0xf6); // Fill byte
6941 // turn on interrupts
6942 ASM_START
6944 ASM_END
6945 // wait on 40:3e bit 7 to become 1
6946 val8 = (read_byte(0x0000, 0x043e) & 0x80);
6947 while ( val8 == 0 ) {
6948 val8 = (read_byte(0x0000, 0x043e) & 0x80);
6950 val8 = 0; // separate asm from while() loop
6951 // turn off interrupts
6952 ASM_START
6954 ASM_END
6955 // set 40:3e bit 7 to 0
6956 val8 = read_byte(0x0000, 0x043e);
6957 val8 &= 0x7f;
6958 write_byte(0x0000, 0x043e, val8);
6959 // check port 3f4 for accessibility to status bytes
6960 val8 = inb(0x3f4);
6961 if ( (val8 & 0xc0) != 0xc0 )
6962 BX_PANIC("int13_diskette: ctrl not ready\n");
6964 // read 7 return status bytes from controller
6965 // using loop index broken, have to unroll...
6966 return_status[0] = inb(0x3f5);
6967 return_status[1] = inb(0x3f5);
6968 return_status[2] = inb(0x3f5);
6969 return_status[3] = inb(0x3f5);
6970 return_status[4] = inb(0x3f5);
6971 return_status[5] = inb(0x3f5);
6972 return_status[6] = inb(0x3f5);
6973 // record in BIOS Data Area
6974 write_byte(0x0040, 0x0042, return_status[0]);
6975 write_byte(0x0040, 0x0043, return_status[1]);
6976 write_byte(0x0040, 0x0044, return_status[2]);
6977 write_byte(0x0040, 0x0045, return_status[3]);
6978 write_byte(0x0040, 0x0046, return_status[4]);
6979 write_byte(0x0040, 0x0047, return_status[5]);
6980 write_byte(0x0040, 0x0048, return_status[6]);
6982 if ( (return_status[0] & 0xc0) != 0 ) {
6983 if ( (return_status[1] & 0x02) != 0 ) {
6984 // diskette not writable.
6985 // AH=status code=0x03 (tried to write on write-protected disk)
6986 // AL=number of sectors written=0
6987 AX = 0x0300;
6988 SET_CF();
6989 return;
6990 } else {
6991 BX_PANIC("int13_diskette_function: write error\n");
6995 SET_AH(0);
6996 set_diskette_ret_status(0);
6997 set_diskette_current_cyl(drive, 0);
6998 CLEAR_CF(); // successful
6999 return;
7002 case 0x08: // read diskette drive parameters
7003 BX_DEBUG_INT13_FL("floppy f08\n");
7004 drive = GET_DL();
7006 if (drive > 1) {
7007 AX = 0;
7008 BX = 0;
7009 CX = 0;
7010 DX = 0;
7011 ES = 0;
7012 DI = 0;
7013 SET_DL(num_floppies);
7014 SET_CF();
7015 return;
7018 drive_type = inb_cmos(0x10);
7019 num_floppies = 0;
7020 if (drive_type & 0xf0)
7021 num_floppies++;
7022 if (drive_type & 0x0f)
7023 num_floppies++;
7025 if (drive == 0)
7026 drive_type >>= 4;
7027 else
7028 drive_type &= 0x0f;
7030 SET_BH(0);
7031 SET_BL(drive_type);
7032 SET_AH(0);
7033 SET_AL(0);
7034 SET_DL(num_floppies);
7036 switch (drive_type) {
7037 case 0: // none
7038 CX = 0;
7039 SET_DH(0); // max head #
7040 break;
7042 case 1: // 360KB, 5.25"
7043 CX = 0x2709; // 40 tracks, 9 sectors
7044 SET_DH(1); // max head #
7045 break;
7047 case 2: // 1.2MB, 5.25"
7048 CX = 0x4f0f; // 80 tracks, 15 sectors
7049 SET_DH(1); // max head #
7050 break;
7052 case 3: // 720KB, 3.5"
7053 CX = 0x4f09; // 80 tracks, 9 sectors
7054 SET_DH(1); // max head #
7055 break;
7057 case 4: // 1.44MB, 3.5"
7058 CX = 0x4f12; // 80 tracks, 18 sectors
7059 SET_DH(1); // max head #
7060 break;
7062 case 5: // 2.88MB, 3.5"
7063 CX = 0x4f24; // 80 tracks, 36 sectors
7064 SET_DH(1); // max head #
7065 break;
7067 case 6: // 160k, 5.25"
7068 CX = 0x2708; // 40 tracks, 8 sectors
7069 SET_DH(0); // max head #
7070 break;
7072 case 7: // 180k, 5.25"
7073 CX = 0x2709; // 40 tracks, 9 sectors
7074 SET_DH(0); // max head #
7075 break;
7077 case 8: // 320k, 5.25"
7078 CX = 0x2708; // 40 tracks, 8 sectors
7079 SET_DH(1); // max head #
7080 break;
7082 default: // ?
7083 BX_PANIC("floppy: int13: bad floppy type\n");
7086 /* set es & di to point to 11 byte diskette param table in ROM */
7087 DI = 0xefc7;
7088 ES = 0xf000;
7089 CLEAR_CF(); // success
7090 /* disk status not changed upon success */
7091 return;
7094 case 0x15: // read diskette drive type
7095 BX_DEBUG_INT13_FL("floppy f15\n");
7096 drive = GET_DL();
7097 if (drive > 1) {
7098 SET_AH(0); // only 2 drives supported
7099 // set_diskette_ret_status here ???
7100 SET_CF();
7101 return;
7103 drive_type = inb_cmos(0x10);
7105 if (drive == 0)
7106 drive_type >>= 4;
7107 else
7108 drive_type &= 0x0f;
7109 CLEAR_CF(); // successful, not present
7110 if (drive_type==0) {
7111 SET_AH(0); // drive not present
7113 else {
7114 SET_AH(1); // drive present, does not support change line
7117 #if BX_ELTORITO_BOOT
7118 // This is mandatory. Otherwise Win98 does not boot
7119 if ((cdemu_isactive() != 00) && (cdemu_emulated_drive() == drive))
7120 DX+=0x0001;
7121 #endif
7122 return;
7124 case 0x16: // get diskette change line status
7125 BX_DEBUG_INT13_FL("floppy f16\n");
7126 drive = GET_DL();
7127 if (drive > 1) {
7128 SET_AH(0x01); // invalid drive
7129 set_diskette_ret_status(0x01);
7130 SET_CF();
7131 return;
7134 SET_AH(0x06); // change line not supported
7135 set_diskette_ret_status(0x06);
7136 SET_CF();
7137 return;
7139 case 0x17: // set diskette type for format(old)
7140 BX_DEBUG_INT13_FL("floppy f17\n");
7141 /* not used for 1.44M floppies */
7142 SET_AH(0x01); // not supported
7143 set_diskette_ret_status(1); /* not supported */
7144 SET_CF();
7145 return;
7147 case 0x18: // set diskette type for format(new)
7148 BX_DEBUG_INT13_FL("floppy f18\n");
7149 SET_AH(0x01); // do later
7150 set_diskette_ret_status(1);
7151 SET_CF();
7152 return;
7154 default:
7155 BX_INFO("int13_diskette: unsupported AH=%02x\n", GET_AH());
7157 // if ( (ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e) ) {
7158 SET_AH(0x01); // ???
7159 set_diskette_ret_status(1);
7160 SET_CF();
7161 return;
7162 // }
7165 #else // #if BX_SUPPORT_FLOPPY
7166 void
7167 int13_diskette_function(DI, SI, BP, SP, BX, DX, CX, AX, ES, FLAGS)
7168 Bit16u DI, SI, BP, SP, BX, DX, CX, AX, ES, FLAGS;
7170 Bit8u val8;
7172 switch ( GET_AH() ) {
7174 case 0x01: // Read Diskette Status
7175 CLEAR_CF();
7176 val8 = read_byte(0x0000, 0x0441);
7177 SET_AH(val8);
7178 if (val8) {
7179 SET_CF();
7181 return;
7183 default:
7184 SET_CF();
7185 write_byte(0x0000, 0x0441, 0x01);
7186 SET_AH(0x01);
7189 #endif // #if BX_SUPPORT_FLOPPY
7191 void
7192 set_diskette_ret_status(value)
7193 Bit8u value;
7195 write_byte(0x0040, 0x0041, value);
7198 void
7199 set_diskette_current_cyl(drive, cyl)
7200 Bit8u drive;
7201 Bit8u cyl;
7203 if (drive > 1)
7204 BX_PANIC("set_diskette_current_cyl(): drive > 1\n");
7205 write_byte(0x0040, 0x0094+drive, cyl);
7208 void
7209 determine_floppy_media(drive)
7210 Bit16u drive;
7212 #if 0
7213 Bit8u val8, DOR, ctrl_info;
7215 ctrl_info = read_byte(0x0040, 0x008F);
7216 if (drive==1)
7217 ctrl_info >>= 4;
7218 else
7219 ctrl_info &= 0x0f;
7221 #if 0
7222 if (drive == 0) {
7223 DOR = 0x1c; // DOR: drive0 motor on, DMA&int enabled, normal op, drive select 0
7225 else {
7226 DOR = 0x2d; // DOR: drive1 motor on, DMA&int enabled, normal op, drive select 1
7228 #endif
7230 if ( (ctrl_info & 0x04) != 0x04 ) {
7231 // Drive not determined means no drive exists, done.
7232 return;
7235 #if 0
7236 // check Main Status Register for readiness
7237 val8 = inb(0x03f4) & 0x80; // Main Status Register
7238 if (val8 != 0x80)
7239 BX_PANIC("d_f_m: MRQ bit not set\n");
7241 // change line
7243 // existing BDA values
7245 // turn on drive motor
7246 outb(0x03f2, DOR); // Digital Output Register
7248 #endif
7249 BX_PANIC("d_f_m: OK so far\n");
7250 #endif
7253 void
7254 int17_function(regs, ds, iret_addr)
7255 pusha_regs_t regs; // regs pushed from PUSHA instruction
7256 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7257 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7259 Bit16u addr,timeout;
7260 Bit8u val8;
7262 ASM_START
7264 ASM_END
7266 addr = read_word(0x0040, (regs.u.r16.dx << 1) + 8);
7267 if ((regs.u.r8.ah < 3) && (regs.u.r16.dx < 3) && (addr > 0)) {
7268 timeout = read_byte(0x0040, 0x0078 + regs.u.r16.dx) << 8;
7269 if (regs.u.r8.ah == 0) {
7270 outb(addr, regs.u.r8.al);
7271 val8 = inb(addr+2);
7272 outb(addr+2, val8 | 0x01); // send strobe
7273 ASM_START
7275 ASM_END
7276 outb(addr+2, val8 & ~0x01);
7277 while (((inb(addr+1) & 0x40) == 0x40) && (timeout)) {
7278 timeout--;
7281 if (regs.u.r8.ah == 1) {
7282 val8 = inb(addr+2);
7283 outb(addr+2, val8 & ~0x04); // send init
7284 ASM_START
7286 ASM_END
7287 outb(addr+2, val8 | 0x04);
7289 regs.u.r8.ah = inb(addr+1);
7290 val8 = (~regs.u.r8.ah & 0x48);
7291 regs.u.r8.ah &= 0xB7;
7292 regs.u.r8.ah |= val8;
7293 if (!timeout) regs.u.r8.ah |= 0x01;
7294 ClearCF(iret_addr.flags);
7295 } else {
7296 SetCF(iret_addr.flags); // Unsupported
7300 // returns bootsegment in ax, drive in bl
7301 Bit32u
7302 int19_function()
7304 Bit16u ebda_seg=read_word(0x0040,0x000E);
7305 Bit8u bootseq;
7306 Bit8u bootdrv;
7307 Bit8u bootcd;
7308 Bit8u bootchk;
7309 Bit16u bootseg;
7310 Bit16u status;
7312 // if BX_ELTORITO_BOOT is not defined, old behavior
7313 // check bit 5 in CMOS reg 0x2d. load either 0x00 or 0x80 into DL
7314 // in preparation for the intial INT 13h (0=floppy A:, 0x80=C:)
7315 // 0: system boot sequence, first drive C: then A:
7316 // 1: system boot sequence, first drive A: then C:
7317 // else BX_ELTORITO_BOOT is defined
7318 // CMOS reg 0x3D contains the boot device :
7319 // 0x01 : floppy
7320 // 0x02 : harddrive
7321 // 0x03 : cdrom
7322 // else : floppy for now.
7324 // Get the boot sequence
7325 #if BX_ELTORITO_BOOT
7326 bootseq=inb_cmos(0x3d);
7328 bootdrv=0x00; bootcd=0;
7329 switch(bootseq) {
7330 case 0x01: bootdrv=0x00; bootcd=0; break;
7331 case 0x02: bootdrv=0x80; bootcd=0; break;
7332 case 0x03: bootdrv=0x00; bootcd=1; break;
7334 #else
7335 bootseq=inb_cmos(0x2d);
7337 bootdrv=0x00; bootcd=0;
7338 if((bootseq&0x20)==0) bootdrv=0x80;
7339 #endif // BX_ELTORITO_BOOT
7341 #if BX_ELTORITO_BOOT
7342 // We have to boot from cd
7343 if (bootcd != 0) {
7344 status = cdrom_boot();
7346 // If failure
7347 if ( (status & 0x00ff) !=0 ) {
7348 print_cdromboot_failure(status);
7349 print_boot_failure(bootcd, bootdrv, 1);
7350 return 0x00000000;
7353 bootseg = read_word(ebda_seg,&EbdaData->cdemu.load_segment);
7354 bootdrv = (Bit8u)(status>>8);
7357 #endif // BX_ELTORITO_BOOT
7359 // We have to boot from harddisk or floppy
7360 if (bootcd == 0) {
7361 bootseg=0x07c0;
7363 ASM_START
7364 push bp
7365 mov bp, sp
7367 mov ax, #0x0000
7368 mov _int19_function.status + 2[bp], ax
7369 mov dl, _int19_function.bootdrv + 2[bp]
7370 mov ax, _int19_function.bootseg + 2[bp]
7371 mov es, ax ;; segment
7372 mov bx, #0x0000 ;; offset
7373 mov ah, #0x02 ;; function 2, read diskette sector
7374 mov al, #0x01 ;; read 1 sector
7375 mov ch, #0x00 ;; track 0
7376 mov cl, #0x01 ;; sector 1
7377 mov dh, #0x00 ;; head 0
7378 int #0x13 ;; read sector
7379 jnc int19_load_done
7380 mov ax, #0x0001
7381 mov _int19_function.status + 2[bp], ax
7383 int19_load_done:
7384 pop bp
7385 ASM_END
7387 if (status != 0) {
7388 print_boot_failure(bootcd, bootdrv, 1);
7389 return 0x00000000;
7393 // check signature if instructed by cmos reg 0x38, only for floppy
7394 // bootchk = 1 : signature check disabled
7395 // bootchk = 0 : signature check enabled
7396 if (bootdrv != 0) bootchk = 0;
7397 else bootchk = inb_cmos(0x38);
7399 #if BX_ELTORITO_BOOT
7400 // if boot from cd, no signature check
7401 if (bootcd != 0)
7402 bootchk = 1;
7403 #endif // BX_ELTORITO_BOOT
7405 if (bootchk == 0) {
7406 if (read_word(bootseg,0x1fe) != 0xaa55) {
7407 print_boot_failure(bootcd, bootdrv, 0);
7408 return 0x00000000;
7412 #if BX_ELTORITO_BOOT
7413 // Print out the boot string
7414 print_boot_device(bootcd, bootdrv);
7415 #else // BX_ELTORITO_BOOT
7416 print_boot_device(0, bootdrv);
7417 #endif // BX_ELTORITO_BOOT
7419 // return the boot segment
7420 return (((Bit32u)bootdrv) << 16) + bootseg;
7423 void
7424 int1a_function(regs, ds, iret_addr)
7425 pusha_regs_t regs; // regs pushed from PUSHA instruction
7426 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7427 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7429 Bit8u val8;
7431 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);
7433 ASM_START
7435 ASM_END
7437 switch (regs.u.r8.ah) {
7438 case 0: // get current clock count
7439 ASM_START
7441 ASM_END
7442 regs.u.r16.cx = BiosData->ticks_high;
7443 regs.u.r16.dx = BiosData->ticks_low;
7444 regs.u.r8.al = BiosData->midnight_flag;
7445 BiosData->midnight_flag = 0; // reset flag
7446 ASM_START
7448 ASM_END
7449 // AH already 0
7450 ClearCF(iret_addr.flags); // OK
7451 break;
7453 case 1: // Set Current Clock Count
7454 ASM_START
7456 ASM_END
7457 BiosData->ticks_high = regs.u.r16.cx;
7458 BiosData->ticks_low = regs.u.r16.dx;
7459 BiosData->midnight_flag = 0; // reset flag
7460 ASM_START
7462 ASM_END
7463 regs.u.r8.ah = 0;
7464 ClearCF(iret_addr.flags); // OK
7465 break;
7468 case 2: // Read CMOS Time
7469 if (rtc_updating()) {
7470 SetCF(iret_addr.flags);
7471 break;
7474 regs.u.r8.dh = inb_cmos(0x00); // Seconds
7475 regs.u.r8.cl = inb_cmos(0x02); // Minutes
7476 regs.u.r8.ch = inb_cmos(0x04); // Hours
7477 regs.u.r8.dl = inb_cmos(0x0b) & 0x01; // Stat Reg B
7478 regs.u.r8.ah = 0;
7479 regs.u.r8.al = regs.u.r8.ch;
7480 ClearCF(iret_addr.flags); // OK
7481 break;
7483 case 3: // Set CMOS Time
7484 // Using a debugger, I notice the following masking/setting
7485 // of bits in Status Register B, by setting Reg B to
7486 // a few values and getting its value after INT 1A was called.
7488 // try#1 try#2 try#3
7489 // before 1111 1101 0111 1101 0000 0000
7490 // after 0110 0010 0110 0010 0000 0010
7492 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7493 // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
7494 if (rtc_updating()) {
7495 init_rtc();
7496 // fall through as if an update were not in progress
7498 outb_cmos(0x00, regs.u.r8.dh); // Seconds
7499 outb_cmos(0x02, regs.u.r8.cl); // Minutes
7500 outb_cmos(0x04, regs.u.r8.ch); // Hours
7501 // Set Daylight Savings time enabled bit to requested value
7502 val8 = (inb_cmos(0x0b) & 0x60) | 0x02 | (regs.u.r8.dl & 0x01);
7503 // (reg B already selected)
7504 outb_cmos(0x0b, val8);
7505 regs.u.r8.ah = 0;
7506 regs.u.r8.al = val8; // val last written to Reg B
7507 ClearCF(iret_addr.flags); // OK
7508 break;
7510 case 4: // Read CMOS Date
7511 regs.u.r8.ah = 0;
7512 if (rtc_updating()) {
7513 SetCF(iret_addr.flags);
7514 break;
7516 regs.u.r8.cl = inb_cmos(0x09); // Year
7517 regs.u.r8.dh = inb_cmos(0x08); // Month
7518 regs.u.r8.dl = inb_cmos(0x07); // Day of Month
7519 regs.u.r8.ch = inb_cmos(0x32); // Century
7520 regs.u.r8.al = regs.u.r8.ch;
7521 ClearCF(iret_addr.flags); // OK
7522 break;
7524 case 5: // Set CMOS Date
7525 // Using a debugger, I notice the following masking/setting
7526 // of bits in Status Register B, by setting Reg B to
7527 // a few values and getting its value after INT 1A was called.
7529 // try#1 try#2 try#3 try#4
7530 // before 1111 1101 0111 1101 0000 0010 0000 0000
7531 // after 0110 1101 0111 1101 0000 0010 0000 0000
7533 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7534 // My assumption: RegB = (RegB & 01111111b)
7535 if (rtc_updating()) {
7536 init_rtc();
7537 SetCF(iret_addr.flags);
7538 break;
7540 outb_cmos(0x09, regs.u.r8.cl); // Year
7541 outb_cmos(0x08, regs.u.r8.dh); // Month
7542 outb_cmos(0x07, regs.u.r8.dl); // Day of Month
7543 outb_cmos(0x32, regs.u.r8.ch); // Century
7544 val8 = inb_cmos(0x0b) & 0x7f; // clear halt-clock bit
7545 outb_cmos(0x0b, val8);
7546 regs.u.r8.ah = 0;
7547 regs.u.r8.al = val8; // AL = val last written to Reg B
7548 ClearCF(iret_addr.flags); // OK
7549 break;
7551 case 6: // Set Alarm Time in CMOS
7552 // Using a debugger, I notice the following masking/setting
7553 // of bits in Status Register B, by setting Reg B to
7554 // a few values and getting its value after INT 1A was called.
7556 // try#1 try#2 try#3
7557 // before 1101 1111 0101 1111 0000 0000
7558 // after 0110 1111 0111 1111 0010 0000
7560 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7561 // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
7562 val8 = inb_cmos(0x0b); // Get Status Reg B
7563 regs.u.r16.ax = 0;
7564 if (val8 & 0x20) {
7565 // Alarm interrupt enabled already
7566 SetCF(iret_addr.flags); // Error: alarm in use
7567 break;
7569 if (rtc_updating()) {
7570 init_rtc();
7571 // fall through as if an update were not in progress
7573 outb_cmos(0x01, regs.u.r8.dh); // Seconds alarm
7574 outb_cmos(0x03, regs.u.r8.cl); // Minutes alarm
7575 outb_cmos(0x05, regs.u.r8.ch); // Hours alarm
7576 outb(0xa1, inb(0xa1) & 0xfe); // enable IRQ 8
7577 // enable Status Reg B alarm bit, clear halt clock bit
7578 outb_cmos(0x0b, (val8 & 0x7f) | 0x20);
7579 ClearCF(iret_addr.flags); // OK
7580 break;
7582 case 7: // Turn off Alarm
7583 // Using a debugger, I notice the following masking/setting
7584 // of bits in Status Register B, by setting Reg B to
7585 // a few values and getting its value after INT 1A was called.
7587 // try#1 try#2 try#3 try#4
7588 // before 1111 1101 0111 1101 0010 0000 0010 0010
7589 // after 0100 0101 0101 0101 0000 0000 0000 0010
7591 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7592 // My assumption: RegB = (RegB & 01010111b)
7593 val8 = inb_cmos(0x0b); // Get Status Reg B
7594 // clear clock-halt bit, disable alarm bit
7595 outb_cmos(0x0b, val8 & 0x57); // disable alarm bit
7596 regs.u.r8.ah = 0;
7597 regs.u.r8.al = val8; // val last written to Reg B
7598 ClearCF(iret_addr.flags); // OK
7599 break;
7600 #if BX_PCIBIOS
7601 case 0xb1:
7602 // real mode PCI BIOS functions now handled in assembler code
7603 // this C code handles the error code for information only
7604 if (regs.u.r8.al == 0xff) {
7605 BX_INFO("PCI BIOS not present\n");
7606 } else if (regs.u.r8.al == 0x81) {
7607 BX_INFO("unsupported PCI BIOS function 0x%02x\n", regs.u.r8.al);
7608 } else if (regs.u.r8.al == 0x83) {
7609 BX_INFO("bad PCI vendor ID %04x\n", regs.u.r16.dx);
7610 } else if (regs.u.r8.al == 0x86) {
7611 BX_INFO("PCI device %04x:%04x not found\n", regs.u.r16.dx, regs.u.r16.cx);
7613 regs.u.r8.ah = regs.u.r8.al;
7614 SetCF(iret_addr.flags);
7615 break;
7616 #endif
7618 default:
7619 SetCF(iret_addr.flags); // Unsupported
7623 void
7624 int70_function(regs, ds, iret_addr)
7625 pusha_regs_t regs; // regs pushed from PUSHA instruction
7626 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7627 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7629 // INT 70h: IRQ 8 - CMOS RTC interrupt from periodic or alarm modes
7630 Bit8u registerB = 0, registerC = 0;
7632 // Check which modes are enabled and have occurred.
7633 registerB = inb_cmos( 0xB );
7634 registerC = inb_cmos( 0xC );
7636 if( ( registerB & 0x60 ) != 0 ) {
7637 if( ( registerC & 0x20 ) != 0 ) {
7638 // Handle Alarm Interrupt.
7639 ASM_START
7641 int #0x4a
7643 ASM_END
7645 if( ( registerC & 0x40 ) != 0 ) {
7646 // Handle Periodic Interrupt.
7648 if( read_byte( 0x40, 0xA0 ) != 0 ) {
7649 // Wait Interval (Int 15, AH=83) active.
7650 Bit32u time, toggle;
7652 time = read_dword( 0x40, 0x9C ); // Time left in microseconds.
7653 if( time < 0x3D1 ) {
7654 // Done waiting.
7655 Bit16u segment, offset;
7657 offset = read_word( 0x40, 0x98 );
7658 segment = read_word( 0x40, 0x9A );
7659 write_byte( 0x40, 0xA0, 0 ); // Turn of status byte.
7660 outb_cmos( 0xB, registerB & 0x37 ); // Clear the Periodic Interrupt.
7661 write_byte( segment, offset, 0x80 ); // Write to specified flag byte.
7662 } else {
7663 // Continue waiting.
7664 time -= 0x3D1;
7665 write_dword( 0x40, 0x9C, time );
7671 ASM_START
7672 call eoi_both_pics
7673 ASM_END
7677 ASM_START
7678 ;------------------------------------------
7679 ;- INT74h : PS/2 mouse hardware interrupt -
7680 ;------------------------------------------
7681 int74_handler:
7683 pusha
7684 push ds ;; save DS
7685 push #0x00 ;; placeholder for status
7686 push #0x00 ;; placeholder for X
7687 push #0x00 ;; placeholder for Y
7688 push #0x00 ;; placeholder for Z
7689 push #0x00 ;; placeholder for make_far_call boolean
7690 call _int74_function
7691 pop cx ;; remove make_far_call from stack
7692 jcxz int74_done
7694 ;; make far call to EBDA:0022
7695 push #0x00
7696 pop ds
7697 push 0x040E ;; push 0000:040E (opcodes 0xff, 0x36, 0x0E, 0x04)
7698 pop ds
7699 //CALL_EP(0x0022) ;; call far routine (call_Ep DS:0022 :opcodes 0xff, 0x1e, 0x22, 0x00)
7700 call far ptr[0x22]
7701 int74_done:
7703 call eoi_both_pics
7704 add sp, #8 ;; pop status, x, y, z
7706 pop ds ;; restore DS
7707 popa
7708 iret
7711 ;; This will perform an IRET, but will retain value of current CF
7712 ;; by altering flags on stack. Better than RETF #02.
7713 iret_modify_cf:
7714 jc carry_set
7715 push bp
7716 mov bp, sp
7717 and BYTE [bp + 0x06], #0xfe
7718 pop bp
7719 iret
7720 carry_set:
7721 push bp
7722 mov bp, sp
7723 or BYTE [bp + 0x06], #0x01
7724 pop bp
7725 iret
7728 ;----------------------
7729 ;- INT13h (relocated) -
7730 ;----------------------
7732 ; int13_relocated is a little bit messed up since I played with it
7733 ; I have to rewrite it:
7734 ; - call a function that detect which function to call
7735 ; - make all called C function get the same parameters list
7737 int13_relocated:
7739 #if BX_ELTORITO_BOOT
7740 ;; check for an eltorito function
7741 cmp ah,#0x4a
7742 jb int13_not_eltorito
7743 cmp ah,#0x4d
7744 ja int13_not_eltorito
7745 jmp int13_eltorito
7747 int13_not_eltorito:
7748 push ax
7749 push bx
7750 push cx
7751 push dx
7753 ;; check if emulation active
7754 call _cdemu_isactive
7755 cmp al,#0x00
7756 je int13_cdemu_inactive
7758 ;; check if access to the emulated drive
7759 call _cdemu_emulated_drive
7760 pop dx
7761 push dx
7762 cmp al,dl ;; int13 on emulated drive
7763 je int13_cdemu
7765 ;; otherwise
7766 and dl,#0xE0 ;; mask to get device class, including cdroms
7767 cmp al,dl ;; al is 0x00 or 0x80
7768 jne int13_cdemu_inactive ;; inactive for device class
7770 int13_cdemu_active:
7771 pop dx
7772 pop cx
7773 pop bx
7774 pop ax
7775 dec dl ;; real drive is dl - 1
7776 jmp int13_legacy
7778 int13_cdemu_inactive:
7779 pop dx
7780 pop cx
7781 pop bx
7782 pop ax
7784 int13_legacy:
7786 #endif // BX_ELTORITO_BOOT
7788 pushf
7789 test dl, #0x80
7790 jz int13_floppy
7792 #if BX_USE_ATADRV
7794 int13_disk_or_cdrom:
7795 cmp dl, #0xE0
7796 jae int13_cdrom
7798 #endif
7800 int13_disk:
7801 ;; pushf already done
7802 push es
7803 push ds
7804 push ss
7805 pop ds
7806 pusha
7807 call _int13_harddisk
7808 popa
7809 pop ds
7810 pop es
7811 popf
7812 // JMPL(iret_modify_cf)
7813 jmp iret_modify_cf
7815 int13_floppy:
7816 popf
7817 // JMPL(int13_diskette)
7818 jmp int13_diskette
7821 #if BX_USE_ATADRV
7822 int13_cdrom:
7823 ;; pushf already done
7824 ;; popf
7825 ;; pushf
7826 push es
7827 push ds
7828 push ss
7829 pop ds
7830 // ebx is modified: BSD 5.2.1 boot loader problem, so we save all
7831 // the 32 bit registers. It should be done in all the bios or no 32
7832 // bit register should be used without saving it first.
7833 pushad
7834 call _int13_cdrom
7835 popad
7836 pop ds
7837 pop es
7838 popf
7840 // JMPL(iret_modify_cf)
7841 jmp iret_modify_cf
7842 #endif
7844 #if BX_ELTORITO_BOOT
7845 int13_cdemu:
7846 pop dx
7847 pop cx
7848 pop bx
7849 pop ax
7851 push ds
7852 push ss
7853 pop ds
7855 pushf
7856 push es
7857 pusha
7858 call _int13_cdemu
7859 popa
7860 pop es
7861 popf
7863 pop ds
7864 jmp iret_modify_cf
7866 int13_eltorito:
7868 pushf
7869 push es
7870 push ds
7871 push ss
7872 pop ds
7873 pusha
7874 call _int13_eltorito
7875 popa
7876 pop ds
7877 pop es
7878 popf
7880 jmp iret_modify_cf
7881 #endif
7884 ;----------
7885 ;- INT18h -
7886 ;----------
7887 int18_handler: ;; Boot Failure routing
7888 call _int18_panic_msg
7890 iret
7892 ;----------
7893 ;- INT19h -
7894 ;----------
7895 int19_relocated: ;; Boot function, relocated
7897 ;; int19 was beginning to be really complex, so now it
7898 ;; just calls an C function, that does the work
7899 ;; it returns in BL the boot drive, and in AX the boot segment
7900 ;; the boot segment will be 0x0000 if something has failed
7902 push bp
7903 mov bp, sp
7905 ;; drop ds
7906 xor ax, ax
7907 mov ds, ax
7909 call _int19_function
7910 ;; bl contains the boot drive
7911 ;; ax contains the boot segment or 0 if failure
7913 test ax, ax ;; if ax is 0 call int18
7914 jz int18_handler
7916 mov dl, bl ;; set drive so guest os find it
7917 shl eax, #0x04 ;; convert seg to ip
7918 mov 2[bp], ax ;; set ip
7920 shr eax, #0x04 ;; get cs back
7921 and ax, #0xF000 ;; remove what went in ip
7922 mov 4[bp], ax ;; set cs
7923 xor ax, ax
7924 mov es, ax ;; set es to zero fixes [ 549815 ]
7925 mov [bp], ax ;; set bp to zero
7926 mov ax, #0xaa55 ;; set ok flag
7928 pop bp
7929 iret ;; Beam me up Scotty
7931 ;----------
7932 ;- INT1Ch -
7933 ;----------
7934 int1c_handler: ;; User Timer Tick
7935 iret
7938 ;----------------------
7939 ;- POST: Floppy Drive -
7940 ;----------------------
7941 floppy_drive_post:
7942 mov ax, #0x0000
7943 mov ds, ax
7945 mov al, #0x00
7946 mov 0x043e, al ;; drive 0 & 1 uncalibrated, no interrupt has occurred
7948 mov 0x043f, al ;; diskette motor status: read op, drive0, motors off
7950 mov 0x0440, al ;; diskette motor timeout counter: not active
7951 mov 0x0441, al ;; diskette controller status return code
7953 mov 0x0442, al ;; disk & diskette controller status register 0
7954 mov 0x0443, al ;; diskette controller status register 1
7955 mov 0x0444, al ;; diskette controller status register 2
7956 mov 0x0445, al ;; diskette controller cylinder number
7957 mov 0x0446, al ;; diskette controller head number
7958 mov 0x0447, al ;; diskette controller sector number
7959 mov 0x0448, al ;; diskette controller bytes written
7961 mov 0x048b, al ;; diskette configuration data
7963 ;; -----------------------------------------------------------------
7964 ;; (048F) diskette controller information
7966 mov al, #0x10 ;; get CMOS diskette drive type
7967 out 0x70, AL
7968 in AL, 0x71
7969 mov ah, al ;; save byte to AH
7971 look_drive0:
7972 shr al, #4 ;; look at top 4 bits for drive 0
7973 jz f0_missing ;; jump if no drive0
7974 mov bl, #0x07 ;; drive0 determined, multi-rate, has changed line
7975 jmp look_drive1
7976 f0_missing:
7977 mov bl, #0x00 ;; no drive0
7979 look_drive1:
7980 mov al, ah ;; restore from AH
7981 and al, #0x0f ;; look at bottom 4 bits for drive 1
7982 jz f1_missing ;; jump if no drive1
7983 or bl, #0x70 ;; drive1 determined, multi-rate, has changed line
7984 f1_missing:
7985 ;; leave high bits in BL zerod
7986 mov 0x048f, bl ;; put new val in BDA (diskette controller information)
7987 ;; -----------------------------------------------------------------
7989 mov al, #0x00
7990 mov 0x0490, al ;; diskette 0 media state
7991 mov 0x0491, al ;; diskette 1 media state
7993 ;; diskette 0,1 operational starting state
7994 ;; drive type has not been determined,
7995 ;; has no changed detection line
7996 mov 0x0492, al
7997 mov 0x0493, al
7999 mov 0x0494, al ;; diskette 0 current cylinder
8000 mov 0x0495, al ;; diskette 1 current cylinder
8002 mov al, #0x02
8003 out #0x0a, al ;; clear DMA-1 channel 2 mask bit
8005 SET_INT_VECTOR(0x1E, #0xF000, #diskette_param_table)
8006 SET_INT_VECTOR(0x40, #0xF000, #int13_diskette)
8007 SET_INT_VECTOR(0x0E, #0xF000, #int0e_handler) ;; IRQ 6
8012 ;--------------------
8013 ;- POST: HARD DRIVE -
8014 ;--------------------
8015 ; relocated here because the primary POST area isnt big enough.
8016 hard_drive_post:
8017 // IRQ 14 = INT 76h
8018 // INT 76h calls INT 15h function ax=9100
8020 mov al, #0x0a ; 0000 1010 = reserved, disable IRQ 14
8021 mov dx, #0x03f6
8022 out dx, al
8024 mov ax, #0x0000
8025 mov ds, ax
8026 mov 0x0474, al /* hard disk status of last operation */
8027 mov 0x0477, al /* hard disk port offset (XT only ???) */
8028 mov 0x048c, al /* hard disk status register */
8029 mov 0x048d, al /* hard disk error register */
8030 mov 0x048e, al /* hard disk task complete flag */
8031 mov al, #0x01
8032 mov 0x0475, al /* hard disk number attached */
8033 mov al, #0xc0
8034 mov 0x0476, al /* hard disk control byte */
8035 SET_INT_VECTOR(0x13, #0xF000, #int13_handler)
8036 SET_INT_VECTOR(0x76, #0xF000, #int76_handler)
8037 ;; INT 41h: hard disk 0 configuration pointer
8038 ;; INT 46h: hard disk 1 configuration pointer
8039 SET_INT_VECTOR(0x41, #EBDA_SEG, #0x003D)
8040 SET_INT_VECTOR(0x46, #EBDA_SEG, #0x004D)
8042 ;; move disk geometry data from CMOS to EBDA disk parameter table(s)
8043 mov al, #0x12
8044 out #0x70, al
8045 in al, #0x71
8046 and al, #0xf0
8047 cmp al, #0xf0
8048 je post_d0_extended
8049 jmp check_for_hd1
8050 post_d0_extended:
8051 mov al, #0x19
8052 out #0x70, al
8053 in al, #0x71
8054 cmp al, #47 ;; decimal 47 - user definable
8055 je post_d0_type47
8056 HALT(__LINE__)
8057 post_d0_type47:
8058 ;; CMOS purpose param table offset
8059 ;; 1b cylinders low 0
8060 ;; 1c cylinders high 1
8061 ;; 1d heads 2
8062 ;; 1e write pre-comp low 5
8063 ;; 1f write pre-comp high 6
8064 ;; 20 retries/bad map/heads>8 8
8065 ;; 21 landing zone low C
8066 ;; 22 landing zone high D
8067 ;; 23 sectors/track E
8069 mov ax, #EBDA_SEG
8070 mov ds, ax
8072 ;;; Filling EBDA table for hard disk 0.
8073 mov al, #0x1f
8074 out #0x70, al
8075 in al, #0x71
8076 mov ah, al
8077 mov al, #0x1e
8078 out #0x70, al
8079 in al, #0x71
8080 mov (0x003d + 0x05), ax ;; write precomp word
8082 mov al, #0x20
8083 out #0x70, al
8084 in al, #0x71
8085 mov (0x003d + 0x08), al ;; drive control byte
8087 mov al, #0x22
8088 out #0x70, al
8089 in al, #0x71
8090 mov ah, al
8091 mov al, #0x21
8092 out #0x70, al
8093 in al, #0x71
8094 mov (0x003d + 0x0C), ax ;; landing zone word
8096 mov al, #0x1c ;; get cylinders word in AX
8097 out #0x70, al
8098 in al, #0x71 ;; high byte
8099 mov ah, al
8100 mov al, #0x1b
8101 out #0x70, al
8102 in al, #0x71 ;; low byte
8103 mov bx, ax ;; BX = cylinders
8105 mov al, #0x1d
8106 out #0x70, al
8107 in al, #0x71
8108 mov cl, al ;; CL = heads
8110 mov al, #0x23
8111 out #0x70, al
8112 in al, #0x71
8113 mov dl, al ;; DL = sectors
8115 cmp bx, #1024
8116 jnbe hd0_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8118 hd0_post_physical_chs:
8119 ;; no logical CHS mapping used, just physical CHS
8120 ;; use Standard Fixed Disk Parameter Table (FDPT)
8121 mov (0x003d + 0x00), bx ;; number of physical cylinders
8122 mov (0x003d + 0x02), cl ;; number of physical heads
8123 mov (0x003d + 0x0E), dl ;; number of physical sectors
8124 jmp check_for_hd1
8126 hd0_post_logical_chs:
8127 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8128 mov (0x003d + 0x09), bx ;; number of physical cylinders
8129 mov (0x003d + 0x0b), cl ;; number of physical heads
8130 mov (0x003d + 0x04), dl ;; number of physical sectors
8131 mov (0x003d + 0x0e), dl ;; number of logical sectors (same)
8132 mov al, #0xa0
8133 mov (0x003d + 0x03), al ;; A0h signature, indicates translated table
8135 cmp bx, #2048
8136 jnbe hd0_post_above_2048
8137 ;; 1024 < c <= 2048 cylinders
8138 shr bx, #0x01
8139 shl cl, #0x01
8140 jmp hd0_post_store_logical
8142 hd0_post_above_2048:
8143 cmp bx, #4096
8144 jnbe hd0_post_above_4096
8145 ;; 2048 < c <= 4096 cylinders
8146 shr bx, #0x02
8147 shl cl, #0x02
8148 jmp hd0_post_store_logical
8150 hd0_post_above_4096:
8151 cmp bx, #8192
8152 jnbe hd0_post_above_8192
8153 ;; 4096 < c <= 8192 cylinders
8154 shr bx, #0x03
8155 shl cl, #0x03
8156 jmp hd0_post_store_logical
8158 hd0_post_above_8192:
8159 ;; 8192 < c <= 16384 cylinders
8160 shr bx, #0x04
8161 shl cl, #0x04
8163 hd0_post_store_logical:
8164 mov (0x003d + 0x00), bx ;; number of physical cylinders
8165 mov (0x003d + 0x02), cl ;; number of physical heads
8166 ;; checksum
8167 mov cl, #0x0f ;; repeat count
8168 mov si, #0x003d ;; offset to disk0 FDPT
8169 mov al, #0x00 ;; sum
8170 hd0_post_checksum_loop:
8171 add al, [si]
8172 inc si
8173 dec cl
8174 jnz hd0_post_checksum_loop
8175 not al ;; now take 2s complement
8176 inc al
8177 mov [si], al
8178 ;;; Done filling EBDA table for hard disk 0.
8181 check_for_hd1:
8182 ;; is there really a second hard disk? if not, return now
8183 mov al, #0x12
8184 out #0x70, al
8185 in al, #0x71
8186 and al, #0x0f
8187 jnz post_d1_exists
8189 post_d1_exists:
8190 ;; check that the hd type is really 0x0f.
8191 cmp al, #0x0f
8192 jz post_d1_extended
8193 HALT(__LINE__)
8194 post_d1_extended:
8195 ;; check that the extended type is 47 - user definable
8196 mov al, #0x1a
8197 out #0x70, al
8198 in al, #0x71
8199 cmp al, #47 ;; decimal 47 - user definable
8200 je post_d1_type47
8201 HALT(__LINE__)
8202 post_d1_type47:
8203 ;; Table for disk1.
8204 ;; CMOS purpose param table offset
8205 ;; 0x24 cylinders low 0
8206 ;; 0x25 cylinders high 1
8207 ;; 0x26 heads 2
8208 ;; 0x27 write pre-comp low 5
8209 ;; 0x28 write pre-comp high 6
8210 ;; 0x29 heads>8 8
8211 ;; 0x2a landing zone low C
8212 ;; 0x2b landing zone high D
8213 ;; 0x2c sectors/track E
8214 ;;; Fill EBDA table for hard disk 1.
8215 mov ax, #EBDA_SEG
8216 mov ds, ax
8217 mov al, #0x28
8218 out #0x70, al
8219 in al, #0x71
8220 mov ah, al
8221 mov al, #0x27
8222 out #0x70, al
8223 in al, #0x71
8224 mov (0x004d + 0x05), ax ;; write precomp word
8226 mov al, #0x29
8227 out #0x70, al
8228 in al, #0x71
8229 mov (0x004d + 0x08), al ;; drive control byte
8231 mov al, #0x2b
8232 out #0x70, al
8233 in al, #0x71
8234 mov ah, al
8235 mov al, #0x2a
8236 out #0x70, al
8237 in al, #0x71
8238 mov (0x004d + 0x0C), ax ;; landing zone word
8240 mov al, #0x25 ;; get cylinders word in AX
8241 out #0x70, al
8242 in al, #0x71 ;; high byte
8243 mov ah, al
8244 mov al, #0x24
8245 out #0x70, al
8246 in al, #0x71 ;; low byte
8247 mov bx, ax ;; BX = cylinders
8249 mov al, #0x26
8250 out #0x70, al
8251 in al, #0x71
8252 mov cl, al ;; CL = heads
8254 mov al, #0x2c
8255 out #0x70, al
8256 in al, #0x71
8257 mov dl, al ;; DL = sectors
8259 cmp bx, #1024
8260 jnbe hd1_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8262 hd1_post_physical_chs:
8263 ;; no logical CHS mapping used, just physical CHS
8264 ;; use Standard Fixed Disk Parameter Table (FDPT)
8265 mov (0x004d + 0x00), bx ;; number of physical cylinders
8266 mov (0x004d + 0x02), cl ;; number of physical heads
8267 mov (0x004d + 0x0E), dl ;; number of physical sectors
8270 hd1_post_logical_chs:
8271 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8272 mov (0x004d + 0x09), bx ;; number of physical cylinders
8273 mov (0x004d + 0x0b), cl ;; number of physical heads
8274 mov (0x004d + 0x04), dl ;; number of physical sectors
8275 mov (0x004d + 0x0e), dl ;; number of logical sectors (same)
8276 mov al, #0xa0
8277 mov (0x004d + 0x03), al ;; A0h signature, indicates translated table
8279 cmp bx, #2048
8280 jnbe hd1_post_above_2048
8281 ;; 1024 < c <= 2048 cylinders
8282 shr bx, #0x01
8283 shl cl, #0x01
8284 jmp hd1_post_store_logical
8286 hd1_post_above_2048:
8287 cmp bx, #4096
8288 jnbe hd1_post_above_4096
8289 ;; 2048 < c <= 4096 cylinders
8290 shr bx, #0x02
8291 shl cl, #0x02
8292 jmp hd1_post_store_logical
8294 hd1_post_above_4096:
8295 cmp bx, #8192
8296 jnbe hd1_post_above_8192
8297 ;; 4096 < c <= 8192 cylinders
8298 shr bx, #0x03
8299 shl cl, #0x03
8300 jmp hd1_post_store_logical
8302 hd1_post_above_8192:
8303 ;; 8192 < c <= 16384 cylinders
8304 shr bx, #0x04
8305 shl cl, #0x04
8307 hd1_post_store_logical:
8308 mov (0x004d + 0x00), bx ;; number of physical cylinders
8309 mov (0x004d + 0x02), cl ;; number of physical heads
8310 ;; checksum
8311 mov cl, #0x0f ;; repeat count
8312 mov si, #0x004d ;; offset to disk0 FDPT
8313 mov al, #0x00 ;; sum
8314 hd1_post_checksum_loop:
8315 add al, [si]
8316 inc si
8317 dec cl
8318 jnz hd1_post_checksum_loop
8319 not al ;; now take 2s complement
8320 inc al
8321 mov [si], al
8322 ;;; Done filling EBDA table for hard disk 1.
8326 ;--------------------
8327 ;- POST: EBDA segment
8328 ;--------------------
8329 ; relocated here because the primary POST area isnt big enough.
8330 ebda_post:
8331 #if BX_USE_EBDA
8332 mov ax, #EBDA_SEG
8333 mov ds, ax
8334 mov byte ptr [0x0], #EBDA_SIZE
8335 #endif
8336 xor ax, ax ; mov EBDA seg into 40E
8337 mov ds, ax
8338 mov word ptr [0x40E], #EBDA_SEG
8339 ret;;
8341 ;--------------------
8342 ;- POST: EOI + jmp via [0x40:67)
8343 ;--------------------
8344 ; relocated here because the primary POST area isnt big enough.
8345 eoi_jmp_post:
8346 call eoi_both_pics
8348 xor ax, ax
8349 mov ds, ax
8351 jmp far ptr [0x467]
8354 ;--------------------
8355 eoi_both_pics:
8356 mov al, #0x20
8357 out #0xA0, al ;; slave PIC EOI
8358 eoi_master_pic:
8359 mov al, #0x20
8360 out #0x20, al ;; master PIC EOI
8363 ;--------------------
8364 BcdToBin:
8365 ;; in: AL in BCD format
8366 ;; out: AL in binary format, AH will always be 0
8367 ;; trashes BX
8368 mov bl, al
8369 and bl, #0x0f ;; bl has low digit
8370 shr al, #4 ;; al has high digit
8371 mov bh, #10
8372 mul al, bh ;; multiply high digit by 10 (result in AX)
8373 add al, bl ;; then add low digit
8376 ;--------------------
8377 timer_tick_post:
8378 ;; Setup the Timer Ticks Count (0x46C:dword) and
8379 ;; Timer Ticks Roller Flag (0x470:byte)
8380 ;; The Timer Ticks Count needs to be set according to
8381 ;; the current CMOS time, as if ticks have been occurring
8382 ;; at 18.2hz since midnight up to this point. Calculating
8383 ;; this is a little complicated. Here are the factors I gather
8384 ;; regarding this. 14,318,180 hz was the original clock speed,
8385 ;; chosen so it could be divided by either 3 to drive the 5Mhz CPU
8386 ;; at the time, or 4 to drive the CGA video adapter. The div3
8387 ;; source was divided again by 4 to feed a 1.193Mhz signal to
8388 ;; the timer. With a maximum 16bit timer count, this is again
8389 ;; divided down by 65536 to 18.2hz.
8391 ;; 14,318,180 Hz clock
8392 ;; /3 = 4,772,726 Hz fed to orginal 5Mhz CPU
8393 ;; /4 = 1,193,181 Hz fed to timer
8394 ;; /65536 (maximum timer count) = 18.20650736 ticks/second
8395 ;; 1 second = 18.20650736 ticks
8396 ;; 1 minute = 1092.390442 ticks
8397 ;; 1 hour = 65543.42651 ticks
8399 ;; Given the values in the CMOS clock, one could calculate
8400 ;; the number of ticks by the following:
8401 ;; ticks = (BcdToBin(seconds) * 18.206507) +
8402 ;; (BcdToBin(minutes) * 1092.3904)
8403 ;; (BcdToBin(hours) * 65543.427)
8404 ;; To get a little more accuracy, since Im using integer
8405 ;; arithmatic, I use:
8406 ;; ticks = (BcdToBin(seconds) * 18206507) / 1000000 +
8407 ;; (BcdToBin(minutes) * 10923904) / 10000 +
8408 ;; (BcdToBin(hours) * 65543427) / 1000
8410 ;; assuming DS=0000
8412 ;; get CMOS seconds
8413 xor eax, eax ;; clear EAX
8414 mov al, #0x00
8415 out #0x70, al
8416 in al, #0x71 ;; AL has CMOS seconds in BCD
8417 call BcdToBin ;; EAX now has seconds in binary
8418 mov edx, #18206507
8419 mul eax, edx
8420 mov ebx, #1000000
8421 xor edx, edx
8422 div eax, ebx
8423 mov ecx, eax ;; ECX will accumulate total ticks
8425 ;; get CMOS minutes
8426 xor eax, eax ;; clear EAX
8427 mov al, #0x02
8428 out #0x70, al
8429 in al, #0x71 ;; AL has CMOS minutes in BCD
8430 call BcdToBin ;; EAX now has minutes in binary
8431 mov edx, #10923904
8432 mul eax, edx
8433 mov ebx, #10000
8434 xor edx, edx
8435 div eax, ebx
8436 add ecx, eax ;; add to total ticks
8438 ;; get CMOS hours
8439 xor eax, eax ;; clear EAX
8440 mov al, #0x04
8441 out #0x70, al
8442 in al, #0x71 ;; AL has CMOS hours in BCD
8443 call BcdToBin ;; EAX now has hours in binary
8444 mov edx, #65543427
8445 mul eax, edx
8446 mov ebx, #1000
8447 xor edx, edx
8448 div eax, ebx
8449 add ecx, eax ;; add to total ticks
8451 mov 0x46C, ecx ;; Timer Ticks Count
8452 xor al, al
8453 mov 0x470, al ;; Timer Ticks Rollover Flag
8456 ;--------------------
8457 int76_handler:
8458 ;; record completion in BIOS task complete flag
8459 push ax
8460 push ds
8461 mov ax, #0x0040
8462 mov ds, ax
8463 mov 0x008E, #0xff
8464 call eoi_both_pics
8465 pop ds
8466 pop ax
8467 iret
8470 ;--------------------
8471 #if BX_APM
8472 use32 386
8473 #define APM_PROT32
8474 #include "apmbios.S"
8475 use16 386
8477 #define APM_REAL
8478 #include "apmbios.S"
8480 #endif
8482 ;--------------------
8483 #if BX_PCIBIOS
8484 use32 386
8485 .align 16
8486 bios32_structure:
8487 db 0x5f, 0x33, 0x32, 0x5f ;; "_32_" signature
8488 dw bios32_entry_point, 0xf ;; 32 bit physical address
8489 db 0 ;; revision level
8490 ;; length in paragraphs and checksum stored in a word to prevent errors
8491 dw (~(((bios32_entry_point >> 8) + (bios32_entry_point & 0xff) + 0x32) \
8492 & 0xff) << 8) + 0x01
8493 db 0,0,0,0,0 ;; reserved
8495 .align 16
8496 bios32_entry_point:
8497 pushf
8498 cmp eax, #0x49435024
8499 jne unknown_service
8500 mov eax, #0x80000000
8501 mov dx, #0x0cf8
8502 out dx, eax
8503 mov dx, #0x0cfc
8504 in eax, dx
8505 cmp eax, #0x12378086
8506 jne unknown_service
8507 mov ebx, #0x000f0000
8508 mov ecx, #0
8509 mov edx, #pcibios_protected
8510 xor al, al
8511 jmp bios32_end
8512 unknown_service:
8513 mov al, #0x80
8514 bios32_end:
8515 popf
8516 retf
8518 .align 16
8519 pcibios_protected:
8520 pushf
8522 cmp al, #0x01 ;; installation check
8523 jne pci_pro_f02
8524 mov bx, #0x0210
8525 mov cx, #0
8526 mov edx, #0x20494350
8527 mov al, #0x01
8528 jmp pci_pro_ok
8529 pci_pro_f02: ;; find pci device
8530 cmp al, #0x02
8531 jne pci_pro_f08
8532 shl ecx, #16
8533 or ecx, edx
8534 mov bx, #0x0000
8535 mov di, #0x00
8536 pci_pro_devloop:
8537 call pci_pro_select_reg
8538 mov dx, #0x0cfc
8539 in eax, dx
8540 cmp eax, ecx
8541 jne pci_pro_nextdev
8542 cmp si, #0
8543 je pci_pro_ok
8544 dec si
8545 pci_pro_nextdev:
8546 inc bx
8547 cmp bx, #0x0100
8548 jne pci_pro_devloop
8549 mov ah, #0x86
8550 jmp pci_pro_fail
8551 pci_pro_f08: ;; read configuration byte
8552 cmp al, #0x08
8553 jne pci_pro_f09
8554 call pci_pro_select_reg
8555 push edx
8556 mov dx, di
8557 and dx, #0x03
8558 add dx, #0x0cfc
8559 in al, dx
8560 pop edx
8561 mov cl, al
8562 jmp pci_pro_ok
8563 pci_pro_f09: ;; read configuration word
8564 cmp al, #0x09
8565 jne pci_pro_f0a
8566 call pci_pro_select_reg
8567 push edx
8568 mov dx, di
8569 and dx, #0x02
8570 add dx, #0x0cfc
8571 in ax, dx
8572 pop edx
8573 mov cx, ax
8574 jmp pci_pro_ok
8575 pci_pro_f0a: ;; read configuration dword
8576 cmp al, #0x0a
8577 jne pci_pro_f0b
8578 call pci_pro_select_reg
8579 push edx
8580 mov dx, #0x0cfc
8581 in eax, dx
8582 pop edx
8583 mov ecx, eax
8584 jmp pci_pro_ok
8585 pci_pro_f0b: ;; write configuration byte
8586 cmp al, #0x0b
8587 jne pci_pro_f0c
8588 call pci_pro_select_reg
8589 push edx
8590 mov dx, di
8591 and dx, #0x03
8592 add dx, #0x0cfc
8593 mov al, cl
8594 out dx, al
8595 pop edx
8596 jmp pci_pro_ok
8597 pci_pro_f0c: ;; write configuration word
8598 cmp al, #0x0c
8599 jne pci_pro_f0d
8600 call pci_pro_select_reg
8601 push edx
8602 mov dx, di
8603 and dx, #0x02
8604 add dx, #0x0cfc
8605 mov ax, cx
8606 out dx, ax
8607 pop edx
8608 jmp pci_pro_ok
8609 pci_pro_f0d: ;; write configuration dword
8610 cmp al, #0x0d
8611 jne pci_pro_unknown
8612 call pci_pro_select_reg
8613 push edx
8614 mov dx, #0x0cfc
8615 mov eax, ecx
8616 out dx, eax
8617 pop edx
8618 jmp pci_pro_ok
8619 pci_pro_unknown:
8620 mov ah, #0x81
8621 pci_pro_fail:
8623 popf
8625 retf
8626 pci_pro_ok:
8627 xor ah, ah
8629 popf
8631 retf
8633 pci_pro_select_reg:
8634 push edx
8635 mov eax, #0x800000
8636 mov ax, bx
8637 shl eax, #8
8638 and di, #0xff
8639 or ax, di
8640 and al, #0xfc
8641 mov dx, #0x0cf8
8642 out dx, eax
8643 pop edx
8646 use16 386
8648 pcibios_real:
8649 push eax
8650 push dx
8651 mov eax, #0x80000000
8652 mov dx, #0x0cf8
8653 out dx, eax
8654 mov dx, #0x0cfc
8655 in eax, dx
8656 cmp eax, #0x12378086
8657 je pci_present
8658 pop dx
8659 pop eax
8660 mov ah, #0xff
8663 pci_present:
8664 pop dx
8665 pop eax
8666 cmp al, #0x01 ;; installation check
8667 jne pci_real_f02
8668 mov ax, #0x0001
8669 mov bx, #0x0210
8670 mov cx, #0
8671 mov edx, #0x20494350
8672 mov edi, #pcibios_protected
8673 or edi, #0xf0000
8676 pci_real_f02: ;; find pci device
8677 cmp al, #0x02
8678 jne pci_real_f08
8679 shl ecx, #16
8680 or ecx, edx
8681 mov bx, #0x0000
8682 mov di, #0x00
8683 pci_real_devloop:
8684 call pci_real_select_reg
8685 mov dx, #0x0cfc
8686 in eax, dx
8687 cmp eax, ecx
8688 jne pci_real_nextdev
8689 cmp si, #0
8690 je pci_real_ok
8691 dec si
8692 pci_real_nextdev:
8693 inc bx
8694 cmp bx, #0x0100
8695 jne pci_real_devloop
8696 mov dx, cx
8697 shr ecx, #16
8698 mov ah, #0x86
8699 jmp pci_real_fail
8700 pci_real_f08: ;; read configuration byte
8701 cmp al, #0x08
8702 jne pci_real_f09
8703 call pci_real_select_reg
8704 push dx
8705 mov dx, di
8706 and dx, #0x03
8707 add dx, #0x0cfc
8708 in al, dx
8709 pop dx
8710 mov cl, al
8711 jmp pci_real_ok
8712 pci_real_f09: ;; read configuration word
8713 cmp al, #0x09
8714 jne pci_real_f0a
8715 call pci_real_select_reg
8716 push dx
8717 mov dx, di
8718 and dx, #0x02
8719 add dx, #0x0cfc
8720 in ax, dx
8721 pop dx
8722 mov cx, ax
8723 jmp pci_real_ok
8724 pci_real_f0a: ;; read configuration dword
8725 cmp al, #0x0a
8726 jne pci_real_f0b
8727 call pci_real_select_reg
8728 push dx
8729 mov dx, #0x0cfc
8730 in eax, dx
8731 pop dx
8732 mov ecx, eax
8733 jmp pci_real_ok
8734 pci_real_f0b: ;; write configuration byte
8735 cmp al, #0x0b
8736 jne pci_real_f0c
8737 call pci_real_select_reg
8738 push dx
8739 mov dx, di
8740 and dx, #0x03
8741 add dx, #0x0cfc
8742 mov al, cl
8743 out dx, al
8744 pop dx
8745 jmp pci_real_ok
8746 pci_real_f0c: ;; write configuration word
8747 cmp al, #0x0c
8748 jne pci_real_f0d
8749 call pci_real_select_reg
8750 push dx
8751 mov dx, di
8752 and dx, #0x02
8753 add dx, #0x0cfc
8754 mov ax, cx
8755 out dx, ax
8756 pop dx
8757 jmp pci_real_ok
8758 pci_real_f0d: ;; write configuration dword
8759 cmp al, #0x0d
8760 jne pci_real_unknown
8761 call pci_real_select_reg
8762 push dx
8763 mov dx, #0x0cfc
8764 mov eax, ecx
8765 out dx, eax
8766 pop dx
8767 jmp pci_real_ok
8768 pci_real_unknown:
8769 mov ah, #0x81
8770 pci_real_fail:
8773 pci_real_ok:
8774 xor ah, ah
8778 pci_real_select_reg:
8779 push dx
8780 mov eax, #0x800000
8781 mov ax, bx
8782 shl eax, #8
8783 and di, #0xff
8784 or ax, di
8785 and al, #0xfc
8786 mov dx, #0x0cf8
8787 out dx, eax
8788 pop dx
8791 .align 16
8792 pci_routing_table_structure:
8793 db 0x24, 0x50, 0x49, 0x52 ;; "$PIR" signature
8794 db 0, 1 ;; version
8795 dw 32 + (6 * 16) ;; table size
8796 db 0 ;; PCI interrupt router bus
8797 db 0x08 ;; PCI interrupt router DevFunc
8798 dw 0x0000 ;; PCI exclusive IRQs
8799 dw 0x8086 ;; compatible PCI interrupt router vendor ID
8800 dw 0x7000 ;; compatible PCI interrupt router device ID
8801 dw 0,0 ;; Miniport data
8802 db 0,0,0,0,0,0,0,0,0,0,0 ;; reserved
8803 db 0x07 ;; checksum
8804 ;; first slot entry PCI-to-ISA (embedded)
8805 db 0 ;; pci bus number
8806 db 0x08 ;; pci device number (bit 7-3)
8807 db 0x60 ;; link value INTA#: pointer into PCI2ISA config space
8808 dw 0xdef8 ;; IRQ bitmap INTA#
8809 db 0x61 ;; link value INTB#
8810 dw 0xdef8 ;; IRQ bitmap INTB#
8811 db 0x62 ;; link value INTC#
8812 dw 0xdef8 ;; IRQ bitmap INTC#
8813 db 0x63 ;; link value INTD#
8814 dw 0xdef8 ;; IRQ bitmap INTD#
8815 db 0 ;; physical slot (0 = embedded)
8816 db 0 ;; reserved
8817 ;; second slot entry: 1st PCI slot
8818 db 0 ;; pci bus number
8819 db 0x10 ;; pci device number (bit 7-3)
8820 db 0x61 ;; link value INTA#
8821 dw 0xdef8 ;; IRQ bitmap INTA#
8822 db 0x62 ;; link value INTB#
8823 dw 0xdef8 ;; IRQ bitmap INTB#
8824 db 0x63 ;; link value INTC#
8825 dw 0xdef8 ;; IRQ bitmap INTC#
8826 db 0x60 ;; link value INTD#
8827 dw 0xdef8 ;; IRQ bitmap INTD#
8828 db 1 ;; physical slot (0 = embedded)
8829 db 0 ;; reserved
8830 ;; third slot entry: 2nd PCI slot
8831 db 0 ;; pci bus number
8832 db 0x18 ;; pci device number (bit 7-3)
8833 db 0x62 ;; link value INTA#
8834 dw 0xdef8 ;; IRQ bitmap INTA#
8835 db 0x63 ;; link value INTB#
8836 dw 0xdef8 ;; IRQ bitmap INTB#
8837 db 0x60 ;; link value INTC#
8838 dw 0xdef8 ;; IRQ bitmap INTC#
8839 db 0x61 ;; link value INTD#
8840 dw 0xdef8 ;; IRQ bitmap INTD#
8841 db 2 ;; physical slot (0 = embedded)
8842 db 0 ;; reserved
8843 ;; 4th slot entry: 3rd PCI slot
8844 db 0 ;; pci bus number
8845 db 0x20 ;; pci device number (bit 7-3)
8846 db 0x63 ;; link value INTA#
8847 dw 0xdef8 ;; IRQ bitmap INTA#
8848 db 0x60 ;; link value INTB#
8849 dw 0xdef8 ;; IRQ bitmap INTB#
8850 db 0x61 ;; link value INTC#
8851 dw 0xdef8 ;; IRQ bitmap INTC#
8852 db 0x62 ;; link value INTD#
8853 dw 0xdef8 ;; IRQ bitmap INTD#
8854 db 3 ;; physical slot (0 = embedded)
8855 db 0 ;; reserved
8856 ;; 5th slot entry: 4rd PCI slot
8857 db 0 ;; pci bus number
8858 db 0x28 ;; pci device number (bit 7-3)
8859 db 0x60 ;; link value INTA#
8860 dw 0xdef8 ;; IRQ bitmap INTA#
8861 db 0x61 ;; link value INTB#
8862 dw 0xdef8 ;; IRQ bitmap INTB#
8863 db 0x62 ;; link value INTC#
8864 dw 0xdef8 ;; IRQ bitmap INTC#
8865 db 0x63 ;; link value INTD#
8866 dw 0xdef8 ;; IRQ bitmap INTD#
8867 db 4 ;; physical slot (0 = embedded)
8868 db 0 ;; reserved
8869 ;; 6th slot entry: 5rd PCI slot
8870 db 0 ;; pci bus number
8871 db 0x30 ;; pci device number (bit 7-3)
8872 db 0x61 ;; link value INTA#
8873 dw 0xdef8 ;; IRQ bitmap INTA#
8874 db 0x62 ;; link value INTB#
8875 dw 0xdef8 ;; IRQ bitmap INTB#
8876 db 0x63 ;; link value INTC#
8877 dw 0xdef8 ;; IRQ bitmap INTC#
8878 db 0x60 ;; link value INTD#
8879 dw 0xdef8 ;; IRQ bitmap INTD#
8880 db 5 ;; physical slot (0 = embedded)
8881 db 0 ;; reserved
8883 pci_irq_list:
8884 db 11, 10, 9, 5;
8886 pcibios_init_sel_reg:
8887 push eax
8888 mov eax, #0x800000
8889 mov ax, bx
8890 shl eax, #8
8891 and dl, #0xfc
8892 or al, dl
8893 mov dx, #0x0cf8
8894 out dx, eax
8895 pop eax
8898 pcibios_init_set_elcr:
8899 push ax
8900 push cx
8901 mov dx, #0x04d0
8902 test al, #0x08
8903 jz is_master_pic
8904 inc dx
8905 and al, #0x07
8906 is_master_pic:
8907 mov cl, al
8908 mov bl, #0x01
8909 shl bl, cl
8910 in al, dx
8911 or al, bl
8912 out dx, al
8913 pop cx
8914 pop ax
8917 pcibios_init:
8918 push ds
8919 push bp
8920 mov ax, #0xf000
8921 mov ds, ax
8922 mov dx, #0x04d0 ;; reset ELCR1 + ELCR2
8923 mov al, #0x00
8924 out dx, al
8925 inc dx
8926 out dx, al
8927 mov si, #pci_routing_table_structure
8928 mov bh, [si+8]
8929 mov bl, [si+9]
8930 mov dl, #0x00
8931 call pcibios_init_sel_reg
8932 mov dx, #0x0cfc
8933 in eax, dx
8934 cmp eax, [si+12] ;; check irq router
8935 jne pci_init_end
8936 mov dl, [si+34]
8937 call pcibios_init_sel_reg
8938 push bx ;; save irq router bus + devfunc
8939 mov dx, #0x0cfc
8940 mov ax, #0x8080
8941 out dx, ax ;; reset PIRQ route control
8942 inc dx
8943 inc dx
8944 out dx, ax
8945 mov ax, [si+6]
8946 sub ax, #0x20
8947 shr ax, #4
8948 mov cx, ax
8949 add si, #0x20 ;; set pointer to 1st entry
8950 mov bp, sp
8951 mov ax, #pci_irq_list
8952 push ax
8953 xor ax, ax
8954 push ax
8955 pci_init_loop1:
8956 mov bh, [si]
8957 mov bl, [si+1]
8958 pci_init_loop2:
8959 mov dl, #0x00
8960 call pcibios_init_sel_reg
8961 mov dx, #0x0cfc
8962 in ax, dx
8963 cmp ax, #0xffff
8964 jnz pci_test_int_pin
8965 test bl, #0x07
8966 jz next_pir_entry
8967 jmp next_pci_func
8968 pci_test_int_pin:
8969 mov dl, #0x3c
8970 call pcibios_init_sel_reg
8971 mov dx, #0x0cfd
8972 in al, dx
8973 and al, #0x07
8974 jz next_pci_func
8975 dec al ;; determine pirq reg
8976 mov dl, #0x03
8977 mul al, dl
8978 add al, #0x02
8979 xor ah, ah
8980 mov bx, ax
8981 mov al, [si+bx]
8982 mov dl, al
8983 mov bx, [bp]
8984 call pcibios_init_sel_reg
8985 mov dx, #0x0cfc
8986 and al, #0x03
8987 add dl, al
8988 in al, dx
8989 cmp al, #0x80
8990 jb pirq_found
8991 mov bx, [bp-2] ;; pci irq list pointer
8992 mov al, [bx]
8993 out dx, al
8994 inc bx
8995 mov [bp-2], bx
8996 call pcibios_init_set_elcr
8997 pirq_found:
8998 mov bh, [si]
8999 mov bl, [si+1]
9000 add bl, [bp-3] ;; pci function number
9001 mov dl, #0x3c
9002 call pcibios_init_sel_reg
9003 mov dx, #0x0cfc
9004 out dx, al
9005 next_pci_func:
9006 inc byte ptr[bp-3]
9007 inc bl
9008 test bl, #0x07
9009 jnz pci_init_loop2
9010 next_pir_entry:
9011 add si, #0x10
9012 mov byte ptr[bp-3], #0x00
9013 loop pci_init_loop1
9014 mov sp, bp
9015 pop bx
9016 pci_init_end:
9017 pop bp
9018 pop ds
9020 #endif // BX_PCIBIOS
9022 detect_parport:
9023 push dx
9024 add dx, #2
9025 in al, dx
9026 and al, #0xdf ; clear input mode
9027 out dx, al
9028 pop dx
9029 mov al, #0xaa
9030 out dx, al
9031 in al, dx
9032 cmp al, #0xaa
9033 jne no_parport
9034 push bx
9035 shl bx, #1
9036 mov [bx+0x408], dx ; Parallel I/O address
9037 pop bx
9038 mov [bx+0x478], cl ; Parallel printer timeout
9039 inc bx
9040 no_parport:
9043 detect_serial:
9044 push dx
9045 inc dx
9046 mov al, #0x02
9047 out dx, al
9048 in al, dx
9049 cmp al, #0x02
9050 jne no_serial
9051 inc dx
9052 in al, dx
9053 cmp al, #0x02
9054 jne no_serial
9055 dec dx
9056 xor al, al
9057 out dx, al
9058 pop dx
9059 push bx
9060 shl bx, #1
9061 mov [bx+0x400], dx ; Serial I/O address
9062 pop bx
9063 mov [bx+0x47c], cl ; Serial timeout
9064 inc bx
9066 no_serial:
9067 pop dx
9070 rom_checksum:
9071 push ax
9072 push bx
9073 push cx
9074 xor ax, ax
9075 xor bx, bx
9076 xor cx, cx
9077 mov ch, [2]
9078 shl cx, #1
9079 checksum_loop:
9080 add al, [bx]
9081 inc bx
9082 loop checksum_loop
9083 and al, #0xff
9084 pop cx
9085 pop bx
9086 pop ax
9089 rom_scan:
9090 ;; Scan for existence of valid expansion ROMS.
9091 ;; Video ROM: from 0xC0000..0xC7FFF in 2k increments
9092 ;; General ROM: from 0xC8000..0xDFFFF in 2k increments
9093 ;; System ROM: only 0xE0000
9095 ;; Header:
9096 ;; Offset Value
9097 ;; 0 0x55
9098 ;; 1 0xAA
9099 ;; 2 ROM length in 512-byte blocks
9100 ;; 3 ROM initialization entry point (FAR CALL)
9102 mov cx, #0xc000
9103 rom_scan_loop:
9104 mov ds, cx
9105 mov ax, #0x0004 ;; start with increment of 4 (512-byte) blocks = 2k
9106 cmp [0], #0xAA55 ;; look for signature
9107 jne rom_scan_increment
9108 call rom_checksum
9109 jnz rom_scan_increment
9110 mov al, [2] ;; change increment to ROM length in 512-byte blocks
9112 ;; We want our increment in 512-byte quantities, rounded to
9113 ;; the nearest 2k quantity, since we only scan at 2k intervals.
9114 test al, #0x03
9115 jz block_count_rounded
9116 and al, #0xfc ;; needs rounding up
9117 add al, #0x04
9118 block_count_rounded:
9120 xor bx, bx ;; Restore DS back to 0000:
9121 mov ds, bx
9122 push ax ;; Save AX
9123 ;; Push addr of ROM entry point
9124 push cx ;; Push seg
9125 push #0x0003 ;; Push offset
9126 mov bp, sp ;; Call ROM init routine using seg:off on stack
9127 db 0xff ;; call_far ss:[bp+0]
9128 db 0x5e
9129 db 0
9130 cli ;; In case expansion ROM BIOS turns IF on
9131 add sp, #2 ;; Pop offset value
9132 pop cx ;; Pop seg value (restore CX)
9133 pop ax ;; Restore AX
9134 rom_scan_increment:
9135 shl ax, #5 ;; convert 512-bytes blocks to 16-byte increments
9136 ;; because the segment selector is shifted left 4 bits.
9137 add cx, ax
9138 cmp cx, #0xe000
9139 jbe rom_scan_loop
9141 xor ax, ax ;; Restore DS back to 0000:
9142 mov ds, ax
9145 ;; for 'C' strings and other data, insert them here with
9146 ;; a the following hack:
9147 ;; DATA_SEG_DEFS_HERE
9150 ;--------
9151 ;- POST -
9152 ;--------
9153 .org 0xe05b ; POST Entry Point
9154 post:
9156 xor ax, ax
9158 ;; first reset the DMA controllers
9159 out 0x0d,al
9160 out 0xda,al
9162 ;; then initialize the DMA controllers
9163 mov al, #0xC0
9164 out 0xD6, al ; cascade mode of channel 4 enabled
9165 mov al, #0x00
9166 out 0xD4, al ; unmask channel 4
9168 ;; Examine CMOS shutdown status.
9169 mov AL, #0x0f
9170 out 0x70, AL
9171 in AL, 0x71
9173 ;; backup status
9174 mov bl, al
9176 ;; Reset CMOS shutdown status.
9177 mov AL, #0x0f
9178 out 0x70, AL ; select CMOS register Fh
9179 mov AL, #0x00
9180 out 0x71, AL ; set shutdown action to normal
9182 ;; Examine CMOS shutdown status.
9183 mov al, bl
9185 ;; 0x00, 0x09, 0x0D+ = normal startup
9186 cmp AL, #0x00
9187 jz normal_post
9188 cmp AL, #0x0d
9189 jae normal_post
9190 cmp AL, #0x09
9191 je normal_post
9193 ;; 0x05 = eoi + jmp via [0x40:0x67] jump
9194 cmp al, #0x05
9195 je eoi_jmp_post
9197 ;; Examine CMOS shutdown status.
9198 ;; 0x01,0x02,0x03,0x04,0x06,0x07,0x08, 0x0a, 0x0b, 0x0c = Unimplemented shutdown status.
9199 push bx
9200 call _shutdown_status_panic
9202 #if 0
9203 HALT(__LINE__)
9205 ;#if 0
9206 ; 0xb0, 0x20, /* mov al, #0x20 */
9207 ; 0xe6, 0x20, /* out 0x20, al ;send EOI to PIC */
9208 ;#endif
9210 pop es
9211 pop ds
9212 popa
9213 iret
9214 #endif
9216 normal_post:
9217 ; case 0: normal startup
9220 mov ax, #0xfffe
9221 mov sp, ax
9222 mov ax, #0x0000
9223 mov ds, ax
9224 mov ss, ax
9226 ;; zero out BIOS data area (40:00..40:ff)
9227 mov es, ax
9228 mov cx, #0x0080 ;; 128 words
9229 mov di, #0x0400
9232 stosw
9234 call _log_bios_start
9236 ;; set all interrupts to default handler
9237 mov bx, #0x0000 ;; offset index
9238 mov cx, #0x0100 ;; counter (256 interrupts)
9239 mov ax, #dummy_iret_handler
9240 mov dx, #0xF000
9242 post_default_ints:
9243 mov [bx], ax
9244 inc bx
9245 inc bx
9246 mov [bx], dx
9247 inc bx
9248 inc bx
9249 loop post_default_ints
9251 ;; set vector 0x79 to zero
9252 ;; this is used by 'gardian angel' protection system
9253 SET_INT_VECTOR(0x79, #0, #0)
9255 ;; base memory in K 40:13 (word)
9256 mov ax, #BASE_MEM_IN_K
9257 mov 0x0413, ax
9260 ;; Manufacturing Test 40:12
9261 ;; zerod out above
9263 ;; Warm Boot Flag 0040:0072
9264 ;; value of 1234h = skip memory checks
9265 ;; zerod out above
9268 ;; Printer Services vector
9269 SET_INT_VECTOR(0x17, #0xF000, #int17_handler)
9271 ;; Bootstrap failure vector
9272 SET_INT_VECTOR(0x18, #0xF000, #int18_handler)
9274 ;; Bootstrap Loader vector
9275 SET_INT_VECTOR(0x19, #0xF000, #int19_handler)
9277 ;; User Timer Tick vector
9278 SET_INT_VECTOR(0x1c, #0xF000, #int1c_handler)
9280 ;; Memory Size Check vector
9281 SET_INT_VECTOR(0x12, #0xF000, #int12_handler)
9283 ;; Equipment Configuration Check vector
9284 SET_INT_VECTOR(0x11, #0xF000, #int11_handler)
9286 ;; System Services
9287 SET_INT_VECTOR(0x15, #0xF000, #int15_handler)
9289 ;; EBDA setup
9290 call ebda_post
9292 ;; PIT setup
9293 SET_INT_VECTOR(0x08, #0xF000, #int08_handler)
9294 ;; int 1C already points at dummy_iret_handler (above)
9295 mov al, #0x34 ; timer0: binary count, 16bit count, mode 2
9296 out 0x43, al
9297 mov al, #0x00 ; maximum count of 0000H = 18.2Hz
9298 out 0x40, al
9299 out 0x40, al
9301 ;; Keyboard
9302 SET_INT_VECTOR(0x09, #0xF000, #int09_handler)
9303 SET_INT_VECTOR(0x16, #0xF000, #int16_handler)
9305 xor ax, ax
9306 mov ds, ax
9307 mov 0x0417, al /* keyboard shift flags, set 1 */
9308 mov 0x0418, al /* keyboard shift flags, set 2 */
9309 mov 0x0419, al /* keyboard alt-numpad work area */
9310 mov 0x0471, al /* keyboard ctrl-break flag */
9311 mov 0x0497, al /* keyboard status flags 4 */
9312 mov al, #0x10
9313 mov 0x0496, al /* keyboard status flags 3 */
9316 /* keyboard head of buffer pointer */
9317 mov bx, #0x001E
9318 mov 0x041A, bx
9320 /* keyboard end of buffer pointer */
9321 mov 0x041C, bx
9323 /* keyboard pointer to start of buffer */
9324 mov bx, #0x001E
9325 mov 0x0480, bx
9327 /* keyboard pointer to end of buffer */
9328 mov bx, #0x003E
9329 mov 0x0482, bx
9331 /* init the keyboard */
9332 call _keyboard_init
9334 ;; mov CMOS Equipment Byte to BDA Equipment Word
9335 mov ax, 0x0410
9336 mov al, #0x14
9337 out 0x70, al
9338 in al, 0x71
9339 mov 0x0410, ax
9342 ;; Parallel setup
9343 SET_INT_VECTOR(0x0F, #0xF000, #dummy_iret_handler)
9344 xor ax, ax
9345 mov ds, ax
9346 xor bx, bx
9347 mov cl, #0x14 ; timeout value
9348 mov dx, #0x378 ; Parallel I/O address, port 1
9349 call detect_parport
9350 mov dx, #0x278 ; Parallel I/O address, port 2
9351 call detect_parport
9352 shl bx, #0x0e
9353 mov ax, 0x410 ; Equipment word bits 14..15 determing # parallel ports
9354 and ax, #0x3fff
9355 or ax, bx ; set number of parallel ports
9356 mov 0x410, ax
9358 ;; Serial setup
9359 SET_INT_VECTOR(0x0C, #0xF000, #dummy_iret_handler)
9360 SET_INT_VECTOR(0x14, #0xF000, #int14_handler)
9361 xor bx, bx
9362 mov cl, #0x0a ; timeout value
9363 mov dx, #0x03f8 ; Serial I/O address, port 1
9364 call detect_serial
9365 mov dx, #0x02f8 ; Serial I/O address, port 2
9366 call detect_serial
9367 mov dx, #0x03e8 ; Serial I/O address, port 3
9368 call detect_serial
9369 mov dx, #0x02e8 ; Serial I/O address, port 4
9370 call detect_serial
9371 shl bx, #0x09
9372 mov ax, 0x410 ; Equipment word bits 9..11 determing # serial ports
9373 and ax, #0xf1ff
9374 or ax, bx ; set number of serial port
9375 mov 0x410, ax
9377 ;; CMOS RTC
9378 SET_INT_VECTOR(0x1A, #0xF000, #int1a_handler)
9379 SET_INT_VECTOR(0x4A, #0xF000, #dummy_iret_handler)
9380 SET_INT_VECTOR(0x70, #0xF000, #int70_handler)
9381 ;; BIOS DATA AREA 0x4CE ???
9382 call timer_tick_post
9384 ;; PS/2 mouse setup
9385 SET_INT_VECTOR(0x74, #0xF000, #int74_handler)
9387 ;; IRQ13 (FPU exception) setup
9388 SET_INT_VECTOR(0x75, #0xF000, #int75_handler)
9390 ;; Video setup
9391 SET_INT_VECTOR(0x10, #0xF000, #int10_handler)
9393 ;; PIC
9394 mov al, #0x11 ; send initialisation commands
9395 out 0x20, al
9396 out 0xa0, al
9397 mov al, #0x08
9398 out 0x21, al
9399 mov al, #0x70
9400 out 0xa1, al
9401 mov al, #0x04
9402 out 0x21, al
9403 mov al, #0x02
9404 out 0xa1, al
9405 mov al, #0x01
9406 out 0x21, al
9407 out 0xa1, al
9408 mov al, #0xb8
9409 out 0x21, AL ;master pic: unmask IRQ 0, 1, 2, 6
9410 #if BX_USE_PS2_MOUSE
9411 mov al, #0x8f
9412 #else
9413 mov al, #0x9f
9414 #endif
9415 out 0xa1, AL ;slave pic: unmask IRQ 12, 13, 14
9417 call pcibios_init
9419 call rom_scan
9421 call _print_bios_banner
9424 ;; Floppy setup
9426 call floppy_drive_post
9428 #if BX_USE_ATADRV
9431 ;; Hard Drive setup
9433 call hard_drive_post
9436 ;; ATA/ATAPI driver setup
9438 call _ata_init
9439 call _ata_detect
9441 #else // BX_USE_ATADRV
9444 ;; Hard Drive setup
9446 call hard_drive_post
9448 #endif // BX_USE_ATADRV
9450 #if BX_ELTORITO_BOOT
9452 ;; eltorito floppy/harddisk emulation from cd
9454 call _cdemu_init
9456 #endif // BX_ELTORITO_BOOT
9458 int #0x19
9459 //JMP_EP(0x0064) ; INT 19h location
9462 .org 0xe2c3 ; NMI Handler Entry Point
9463 nmi:
9464 ;; FIXME the NMI handler should not panic
9465 ;; but iret when called from int75 (fpu exception)
9466 call _nmi_handler_msg
9467 iret
9469 int75_handler:
9470 out 0xf0, al // clear irq13
9471 call eoi_both_pics // clear interrupt
9472 int 2 // legacy nmi call
9473 iret
9475 ;-------------------------------------------
9476 ;- INT 13h Fixed Disk Services Entry Point -
9477 ;-------------------------------------------
9478 .org 0xe3fe ; INT 13h Fixed Disk Services Entry Point
9479 int13_handler:
9480 //JMPL(int13_relocated)
9481 jmp int13_relocated
9483 .org 0xe401 ; Fixed Disk Parameter Table
9485 ;----------
9486 ;- INT19h -
9487 ;----------
9488 .org 0xe6f2 ; INT 19h Boot Load Service Entry Point
9489 int19_handler:
9491 jmp int19_relocated
9492 ;-------------------------------------------
9493 ;- System BIOS Configuration Data Table
9494 ;-------------------------------------------
9495 .org BIOS_CONFIG_TABLE
9496 db 0x08 ; Table size (bytes) -Lo
9497 db 0x00 ; Table size (bytes) -Hi
9498 db SYS_MODEL_ID
9499 db SYS_SUBMODEL_ID
9500 db BIOS_REVISION
9501 ; Feature byte 1
9502 ; b7: 1=DMA channel 3 used by hard disk
9503 ; b6: 1=2 interrupt controllers present
9504 ; b5: 1=RTC present
9505 ; b4: 1=BIOS calls int 15h/4Fh every key
9506 ; b3: 1=wait for extern event supported (Int 15h/41h)
9507 ; b2: 1=extended BIOS data area used
9508 ; b1: 0=AT or ESDI bus, 1=MicroChannel
9509 ; b0: 1=Dual bus (MicroChannel + ISA)
9510 db (0 << 7) | \
9511 (1 << 6) | \
9512 (1 << 5) | \
9513 (BX_CALL_INT15_4F << 4) | \
9514 (0 << 3) | \
9515 (BX_USE_EBDA << 2) | \
9516 (0 << 1) | \
9517 (0 << 0)
9518 ; Feature byte 2
9519 ; b7: 1=32-bit DMA supported
9520 ; b6: 1=int16h, function 9 supported
9521 ; b5: 1=int15h/C6h (get POS data) supported
9522 ; b4: 1=int15h/C7h (get mem map info) supported
9523 ; b3: 1=int15h/C8h (en/dis CPU) supported
9524 ; b2: 1=non-8042 kb controller
9525 ; b1: 1=data streaming supported
9526 ; b0: reserved
9527 db (0 << 7) | \
9528 (1 << 6) | \
9529 (0 << 5) | \
9530 (0 << 4) | \
9531 (0 << 3) | \
9532 (0 << 2) | \
9533 (0 << 1) | \
9534 (0 << 0)
9535 ; Feature byte 3
9536 ; b7: not used
9537 ; b6: reserved
9538 ; b5: reserved
9539 ; b4: POST supports ROM-to-RAM enable/disable
9540 ; b3: SCSI on system board
9541 ; b2: info panel installed
9542 ; b1: Initial Machine Load (IML) system - BIOS on disk
9543 ; b0: SCSI supported in IML
9544 db 0x00
9545 ; Feature byte 4
9546 ; b7: IBM private
9547 ; b6: EEPROM present
9548 ; b5-3: ABIOS presence (011 = not supported)
9549 ; b2: private
9550 ; b1: memory split above 16Mb supported
9551 ; b0: POSTEXT directly supported by POST
9552 db 0x00
9553 ; Feature byte 5 (IBM)
9554 ; b1: enhanced mouse
9555 ; b0: flash EPROM
9556 db 0x00
9560 .org 0xe729 ; Baud Rate Generator Table
9562 ;----------
9563 ;- INT14h -
9564 ;----------
9565 .org 0xe739 ; INT 14h Serial Communications Service Entry Point
9566 int14_handler:
9567 push ds
9568 pusha
9569 mov ax, #0x0000
9570 mov ds, ax
9571 call _int14_function
9572 popa
9573 pop ds
9574 iret
9577 ;----------------------------------------
9578 ;- INT 16h Keyboard Service Entry Point -
9579 ;----------------------------------------
9580 .org 0xe82e
9581 int16_handler:
9584 push ds
9585 pushf
9586 pusha
9588 cmp ah, #0x00
9589 je int16_F00
9590 cmp ah, #0x10
9591 je int16_F00
9593 mov bx, #0xf000
9594 mov ds, bx
9595 call _int16_function
9596 popa
9597 popf
9598 pop ds
9599 jz int16_zero_set
9601 int16_zero_clear:
9602 push bp
9603 mov bp, sp
9604 //SEG SS
9605 and BYTE [bp + 0x06], #0xbf
9606 pop bp
9607 iret
9609 int16_zero_set:
9610 push bp
9611 mov bp, sp
9612 //SEG SS
9613 or BYTE [bp + 0x06], #0x40
9614 pop bp
9615 iret
9617 int16_F00:
9618 mov bx, #0x0040
9619 mov ds, bx
9621 int16_wait_for_key:
9623 mov bx, 0x001a
9624 cmp bx, 0x001c
9625 jne int16_key_found
9628 #if 0
9629 /* no key yet, call int 15h, function AX=9002 */
9630 0x50, /* push AX */
9631 0xb8, 0x02, 0x90, /* mov AX, #0x9002 */
9632 0xcd, 0x15, /* int 15h */
9633 0x58, /* pop AX */
9634 0xeb, 0xea, /* jmp WAIT_FOR_KEY */
9635 #endif
9636 jmp int16_wait_for_key
9638 int16_key_found:
9639 mov bx, #0xf000
9640 mov ds, bx
9641 call _int16_function
9642 popa
9643 popf
9644 pop ds
9645 #if 0
9646 /* notify int16 complete w/ int 15h, function AX=9102 */
9647 0x50, /* push AX */
9648 0xb8, 0x02, 0x91, /* mov AX, #0x9102 */
9649 0xcd, 0x15, /* int 15h */
9650 0x58, /* pop AX */
9651 #endif
9652 iret
9656 ;-------------------------------------------------
9657 ;- INT09h : Keyboard Hardware Service Entry Point -
9658 ;-------------------------------------------------
9659 .org 0xe987
9660 int09_handler:
9662 push ax
9664 mov al, #0xAD ;;disable keyboard
9665 out #0x64, al
9667 mov al, #0x0B
9668 out #0x20, al
9669 in al, #0x20
9670 and al, #0x02
9671 jz int09_finish
9673 in al, #0x60 ;;read key from keyboard controller
9674 //test al, #0x80 ;;look for key release
9675 //jnz int09_process_key ;; dont pass releases to intercept?
9677 ;; check for extended key
9678 cmp al, #0xe0
9679 jne int09_call_int15_4f
9681 push ds
9682 xor ax, ax
9683 mov ds, ax
9684 mov al, BYTE [0x496] ;; mf2_state |= 0x01
9685 or al, #0x01
9686 mov BYTE [0x496], al
9687 pop ds
9689 in al, #0x60 ;;read another key from keyboard controller
9693 int09_call_int15_4f:
9694 push ds
9695 pusha
9696 #ifdef BX_CALL_INT15_4F
9697 mov ah, #0x4f ;; allow for keyboard intercept
9699 int #0x15
9700 jnc int09_done
9701 #endif
9704 //int09_process_key:
9705 mov bx, #0xf000
9706 mov ds, bx
9707 call _int09_function
9709 int09_done:
9710 popa
9711 pop ds
9713 call eoi_master_pic
9715 int09_finish:
9716 mov al, #0xAE ;;enable keyboard
9717 out #0x64, al
9718 pop ax
9719 iret
9724 ;----------------------------------------
9725 ;- INT 13h Diskette Service Entry Point -
9726 ;----------------------------------------
9727 .org 0xec59
9728 int13_diskette:
9729 pushf
9730 push es
9731 pusha
9732 call _int13_diskette_function
9733 popa
9734 pop es
9735 popf
9736 //JMPL(iret_modify_cf)
9737 jmp iret_modify_cf
9739 #if 0
9740 pushf
9741 cmp ah, #0x01
9742 je i13d_f01
9744 ;; pushf already done
9745 push es
9746 pusha
9747 call _int13_diskette_function
9748 popa
9749 pop es
9750 popf
9751 //JMPL(iret_modify_cf)
9752 jmp iret_modify_cf
9753 i13d_f01:
9754 popf
9755 push ds
9756 push bx
9757 mov bx, #0x0000
9758 mov ds, bx
9759 mov ah, 0x0441
9760 pop bx
9761 pop ds
9763 ;; ??? dont know if this service changes the return status
9764 //JMPL(iret_modify_cf)
9765 jmp iret_modify_cf
9766 #endif
9770 ;---------------------------------------------
9771 ;- INT 0Eh Diskette Hardware ISR Entry Point -
9772 ;---------------------------------------------
9773 .org 0xef57 ; INT 0Eh Diskette Hardware ISR Entry Point
9774 int0e_handler:
9775 push ax
9776 push dx
9777 mov dx, #0x03f4
9778 in al, dx
9779 and al, #0xc0
9780 cmp al, #0xc0
9781 je int0e_normal
9782 mov dx, #0x03f5
9783 mov al, #0x08 ; sense interrupt status
9784 out dx, al
9785 int0e_loop1:
9786 mov dx, #0x03f4
9787 in al, dx
9788 and al, #0xc0
9789 cmp al, #0xc0
9790 jne int0e_loop1
9791 int0e_loop2:
9792 mov dx, #0x03f5
9793 in al, dx
9794 mov dx, #0x03f4
9795 in al, dx
9796 and al, #0xc0
9797 cmp al, #0xc0
9798 je int0e_loop2
9799 int0e_normal:
9800 push ds
9801 mov ax, #0x0000 ;; segment 0000
9802 mov ds, ax
9803 call eoi_master_pic
9804 mov al, 0x043e
9805 or al, #0x80 ;; diskette interrupt has occurred
9806 mov 0x043e, al
9807 pop ds
9808 pop dx
9809 pop ax
9810 iret
9813 .org 0xefc7 ; Diskette Controller Parameter Table
9814 diskette_param_table:
9815 ;; Since no provisions are made for multiple drive types, most
9816 ;; values in this table are ignored. I set parameters for 1.44M
9817 ;; floppy here
9818 db 0xAF
9819 db 0x02 ;; head load time 0000001, DMA used
9820 db 0x25
9821 db 0x02
9822 db 18
9823 db 0x1B
9824 db 0xFF
9825 db 0x6C
9826 db 0xF6
9827 db 0x0F
9828 db 0x08
9831 ;----------------------------------------
9832 ;- INT17h : Printer Service Entry Point -
9833 ;----------------------------------------
9834 .org 0xefd2
9835 int17_handler:
9836 push ds
9837 pusha
9838 mov ax, #0x0000
9839 mov ds, ax
9840 call _int17_function
9841 popa
9842 pop ds
9843 iret
9845 .org 0xf045 ; INT 10 Functions 0-Fh Entry Point
9846 HALT(__LINE__)
9847 iret
9849 ;----------
9850 ;- INT10h -
9851 ;----------
9852 .org 0xf065 ; INT 10h Video Support Service Entry Point
9853 int10_handler:
9854 ;; dont do anything, since the VGA BIOS handles int10h requests
9855 iret
9857 .org 0xf0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
9859 ;----------
9860 ;- INT12h -
9861 ;----------
9862 .org 0xf841 ; INT 12h Memory Size Service Entry Point
9863 ; ??? different for Pentium (machine check)?
9864 int12_handler:
9865 push ds
9866 mov ax, #0x0040
9867 mov ds, ax
9868 mov ax, 0x0013
9869 pop ds
9870 iret
9872 ;----------
9873 ;- INT11h -
9874 ;----------
9875 .org 0xf84d ; INT 11h Equipment List Service Entry Point
9876 int11_handler:
9877 push ds
9878 mov ax, #0x0040
9879 mov ds, ax
9880 mov ax, 0x0010
9881 pop ds
9882 iret
9884 ;----------
9885 ;- INT15h -
9886 ;----------
9887 .org 0xf859 ; INT 15h System Services Entry Point
9888 int15_handler:
9889 pushf
9890 #if BX_APM
9891 cmp ah, #0x53
9892 je apm_call
9893 #endif
9894 push ds
9895 push es
9896 cmp ah, #0x86
9897 je int15_handler32
9898 cmp ah, #0xE8
9899 je int15_handler32
9900 pusha
9901 #if BX_USE_PS2_MOUSE
9902 cmp ah, #0xC2
9903 je int15_handler_mouse
9904 #endif
9905 call _int15_function
9906 int15_handler_mouse_ret:
9907 popa
9908 int15_handler32_ret:
9909 pop es
9910 pop ds
9911 popf
9912 jmp iret_modify_cf
9913 #if BX_APM
9914 apm_call:
9915 jmp _apmreal_entry
9916 #endif
9918 #if BX_USE_PS2_MOUSE
9919 int15_handler_mouse:
9920 call _int15_function_mouse
9921 jmp int15_handler_mouse_ret
9922 #endif
9924 int15_handler32:
9925 pushad
9926 call _int15_function32
9927 popad
9928 jmp int15_handler32_ret
9930 ;; Protected mode IDT descriptor
9932 ;; I just make the limit 0, so the machine will shutdown
9933 ;; if an exception occurs during protected mode memory
9934 ;; transfers.
9936 ;; Set base to f0000 to correspond to beginning of BIOS,
9937 ;; in case I actually define an IDT later
9938 ;; Set limit to 0
9940 pmode_IDT_info:
9941 dw 0x0000 ;; limit 15:00
9942 dw 0x0000 ;; base 15:00
9943 db 0x0f ;; base 23:16
9945 ;; Real mode IDT descriptor
9947 ;; Set to typical real-mode values.
9948 ;; base = 000000
9949 ;; limit = 03ff
9951 rmode_IDT_info:
9952 dw 0x03ff ;; limit 15:00
9953 dw 0x0000 ;; base 15:00
9954 db 0x00 ;; base 23:16
9957 ;----------
9958 ;- INT1Ah -
9959 ;----------
9960 .org 0xfe6e ; INT 1Ah Time-of-day Service Entry Point
9961 int1a_handler:
9962 #if BX_PCIBIOS
9963 cmp ah, #0xb1
9964 jne int1a_normal
9965 call pcibios_real
9966 jc pcibios_error
9967 push ax
9968 mov ax, ss
9969 push ds
9970 mov ds, ax
9971 push bp
9972 mov bp, sp
9973 lahf
9974 mov [bp+10], ah
9975 pop bp
9976 pop ds
9977 pop ax
9978 iret
9979 pcibios_error:
9980 mov al, ah
9981 mov ah, #0xb1
9982 int1a_normal:
9983 #endif
9984 push ds
9985 pusha
9986 xor ax, ax
9987 mov ds, ax
9988 call _int1a_function
9989 popa
9990 pop ds
9991 iret
9994 ;; int70h: IRQ8 - CMOS RTC
9996 int70_handler:
9997 push ds
9998 pusha
9999 xor ax, ax
10000 mov ds, ax
10001 call _int70_function
10002 popa
10003 pop ds
10004 iret
10006 ;---------
10007 ;- INT08 -
10008 ;---------
10009 .org 0xfea5 ; INT 08h System Timer ISR Entry Point
10010 int08_handler:
10012 push eax
10013 push ds
10014 xor ax, ax
10015 mov ds, ax
10017 ;; time to turn off drive(s)?
10018 mov al,0x0440
10019 or al,al
10020 jz int08_floppy_off
10021 dec al
10022 mov 0x0440,al
10023 jnz int08_floppy_off
10024 ;; turn motor(s) off
10025 push dx
10026 mov dx,#0x03f2
10027 in al,dx
10028 and al,#0xcf
10029 out dx,al
10030 pop dx
10031 int08_floppy_off:
10033 mov eax, 0x046c ;; get ticks dword
10034 inc eax
10036 ;; compare eax to one days worth of timer ticks at 18.2 hz
10037 cmp eax, #0x001800B0
10038 jb int08_store_ticks
10039 ;; there has been a midnight rollover at this point
10040 xor eax, eax ;; zero out counter
10041 inc BYTE 0x0470 ;; increment rollover flag
10043 int08_store_ticks:
10044 mov 0x046c, eax ;; store new ticks dword
10045 ;; chain to user timer tick INT #0x1c
10046 //pushf
10047 //;; call_ep [ds:loc]
10048 //CALL_EP( 0x1c << 2 )
10049 int #0x1c
10051 call eoi_master_pic
10052 pop ds
10053 pop eax
10054 iret
10056 .org 0xfef3 ; Initial Interrupt Vector Offsets Loaded by POST
10059 .org 0xff00
10060 .ascii "(c) 2002 MandrakeSoft S.A. Written by Kevin Lawton & the Bochs team."
10062 ;------------------------------------------------
10063 ;- IRET Instruction for Dummy Interrupt Handler -
10064 ;------------------------------------------------
10065 .org 0xff53 ; IRET Instruction for Dummy Interrupt Handler
10066 dummy_iret_handler:
10067 iret
10069 .org 0xff54 ; INT 05h Print Screen Service Entry Point
10070 HALT(__LINE__)
10071 iret
10073 .org 0xfff0 ; Power-up Entry Point
10074 jmp 0xf000:post;
10076 .org 0xfff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
10077 .ascii "06/23/99"
10079 .org 0xfffe ; System Model ID
10080 db SYS_MODEL_ID
10081 db 0x00 ; filler
10083 .org 0xfa6e ;; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
10084 ASM_END
10086 * This font comes from the fntcol16.zip package (c) by Joseph Gil
10087 * found at ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip
10088 * This font is public domain
10090 static Bit8u vgafont8[128*8]=
10092 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
10093 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e,
10094 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e,
10095 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00,
10096 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00,
10097 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c,
10098 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c,
10099 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00,
10100 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff,
10101 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
10102 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff,
10103 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78,
10104 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18,
10105 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0,
10106 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0,
10107 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99,
10108 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00,
10109 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00,
10110 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18,
10111 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
10112 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00,
10113 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78,
10114 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00,
10115 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff,
10116 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00,
10117 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
10118 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00,
10119 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
10120 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00,
10121 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00,
10122 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00,
10123 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00,
10124 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
10125 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00,
10126 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
10127 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00,
10128 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00,
10129 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00,
10130 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00,
10131 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
10132 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00,
10133 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
10134 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
10135 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00,
10136 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60,
10137 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
10138 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00,
10139 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00,
10140 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00,
10141 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00,
10142 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00,
10143 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00,
10144 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00,
10145 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00,
10146 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00,
10147 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00,
10148 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00,
10149 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00,
10150 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00,
10151 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60,
10152 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00,
10153 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00,
10154 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00,
10155 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00,
10156 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00,
10157 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00,
10158 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00,
10159 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00,
10160 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00,
10161 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00,
10162 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00,
10163 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00,
10164 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00,
10165 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
10166 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00,
10167 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00,
10168 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00,
10169 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00,
10170 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00,
10171 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00,
10172 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
10173 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00,
10174 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00,
10175 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00,
10176 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
10177 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00,
10178 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
10179 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00,
10180 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00,
10181 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00,
10182 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00,
10183 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
10184 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00,
10185 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
10186 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
10187 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
10188 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
10189 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00,
10190 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00,
10191 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00,
10192 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00,
10193 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
10194 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00,
10195 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
10196 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00,
10197 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
10198 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78,
10199 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00,
10200 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
10201 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00,
10202 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
10203 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
10204 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0,
10205 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e,
10206 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00,
10207 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00,
10208 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00,
10209 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
10210 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
10211 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00,
10212 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00,
10213 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
10214 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00,
10215 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00,
10216 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
10217 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00,
10218 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
10219 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00,
10222 ASM_START
10223 .org 0xd000
10224 // bcc-generated data will be placed here
10226 // For documentation of this config structure, look on developer.intel.com and
10227 // search for multiprocessor specification. Note that when you change anything
10228 // you must update the checksum (a pain!). It would be better to construct this
10229 // with C structures, or at least fill in the checksum automatically.
10231 // Maybe this structs could be moved elsewhere than d000
10233 #if (BX_SMP_PROCESSORS==1)
10234 // no structure necessary.
10235 #elif (BX_SMP_PROCESSORS==2)
10236 // define the Intel MP Configuration Structure for 2 processors at
10237 // APIC ID 0,1. I/O APIC at ID=2.
10238 .align 16
10239 mp_config_table:
10240 db 0x50, 0x43, 0x4d, 0x50 ;; "PCMP" signature
10241 dw (mp_config_end-mp_config_table) ;; table length
10242 db 4 ;; spec rev
10243 db 0x65 ;; checksum
10244 .ascii "BOCHSCPU" ;; OEM id = "BOCHSCPU"
10245 db 0x30, 0x2e, 0x31, 0x20 ;; vendor id = "0.1 "
10246 db 0x20, 0x20, 0x20, 0x20
10247 db 0x20, 0x20, 0x20, 0x20
10248 dw 0,0 ;; oem table ptr
10249 dw 0 ;; oem table size
10250 dw 20 ;; entry count
10251 dw 0x0000, 0xfee0 ;; memory mapped address of local APIC
10252 dw 0 ;; extended table length
10253 db 0 ;; extended table checksum
10254 db 0 ;; reserved
10255 mp_config_proc0:
10256 db 0 ;; entry type=processor
10257 db 0 ;; local APIC id
10258 db 0x11 ;; local APIC version number
10259 db 3 ;; cpu flags: enabled
10260 db 0,6,0,0 ;; cpu signature
10261 dw 0x201,0 ;; feature flags
10262 dw 0,0 ;; reserved
10263 dw 0,0 ;; reserved
10264 mp_config_proc1:
10265 db 0 ;; entry type=processor
10266 db 1 ;; local APIC id
10267 db 0x11 ;; local APIC version number
10268 db 1 ;; cpu flags: enabled
10269 db 0,6,0,0 ;; cpu signature
10270 dw 0x201,0 ;; feature flags
10271 dw 0,0 ;; reserved
10272 dw 0,0 ;; reserved
10273 mp_config_isa_bus:
10274 db 1 ;; entry type=bus
10275 db 0 ;; bus ID
10276 db 0x49, 0x53, 0x41, 0x20, 0x20, 0x20 ;; bus type="ISA "
10277 mp_config_ioapic:
10278 db 2 ;; entry type=I/O APIC
10279 db 2 ;; apic id=2. linux will set.
10280 db 0x11 ;; I/O APIC version number
10281 db 1 ;; flags=1=enabled
10282 dw 0x0000, 0xfec0 ;; memory mapped address of I/O APIC
10283 mp_config_irqs:
10284 db 3 ;; entry type=I/O interrupt
10285 db 0 ;; interrupt type=vectored interrupt
10286 db 0,0 ;; flags po=0, el=0 (linux uses as default)
10287 db 0 ;; source bus ID is ISA
10288 db 0 ;; source bus IRQ
10289 db 2 ;; destination I/O APIC ID
10290 db 0 ;; destination I/O APIC interrrupt in
10291 ;; repeat pattern for interrupts 0-15
10292 db 3,0,0,0,0,1,2,1
10293 db 3,0,0,0,0,2,2,2
10294 db 3,0,0,0,0,3,2,3
10295 db 3,0,0,0,0,4,2,4
10296 db 3,0,0,0,0,5,2,5
10297 db 3,0,0,0,0,6,2,6
10298 db 3,0,0,0,0,7,2,7
10299 db 3,0,0,0,0,8,2,8
10300 db 3,0,0,0,0,9,2,9
10301 db 3,0,0,0,0,10,2,10
10302 db 3,0,0,0,0,11,2,11
10303 db 3,0,0,0,0,12,2,12
10304 db 3,0,0,0,0,13,2,13
10305 db 3,0,0,0,0,14,2,14
10306 db 3,0,0,0,0,15,2,15
10307 #elif (BX_SMP_PROCESSORS==4)
10308 // define the Intel MP Configuration Structure for 4 processors at
10309 // APIC ID 0,1,2,3. I/O APIC at ID=4.
10310 .align 16
10311 mp_config_table:
10312 db 0x50, 0x43, 0x4d, 0x50 ;; "PCMP" signature
10313 dw (mp_config_end-mp_config_table) ;; table length
10314 db 4 ;; spec rev
10315 db 0xdd ;; checksum
10316 .ascii "BOCHSCPU" ;; OEM id = "BOCHSCPU"
10317 db 0x30, 0x2e, 0x31, 0x20 ;; vendor id = "0.1 "
10318 db 0x20, 0x20, 0x20, 0x20
10319 db 0x20, 0x20, 0x20, 0x20
10320 dw 0,0 ;; oem table ptr
10321 dw 0 ;; oem table size
10322 dw 22 ;; entry count
10323 dw 0x0000, 0xfee0 ;; memory mapped address of local APIC
10324 dw 0 ;; extended table length
10325 db 0 ;; extended table checksum
10326 db 0 ;; reserved
10327 mp_config_proc0:
10328 db 0 ;; entry type=processor
10329 db 0 ;; local APIC id
10330 db 0x11 ;; local APIC version number
10331 db 1 ;; cpu flags: enabled
10332 db 0,6,0,0 ;; cpu signature
10333 dw 0x201,0 ;; feature flags
10334 dw 0,0 ;; reserved
10335 dw 0,0 ;; reserved
10336 mp_config_proc1:
10337 db 0 ;; entry type=processor
10338 db 1 ;; local APIC id
10339 db 0x11 ;; local APIC version number
10340 db 1 ;; cpu flags: enabled
10341 db 0,6,0,0 ;; cpu signature
10342 dw 0x201,0 ;; feature flags
10343 dw 0,0 ;; reserved
10344 dw 0,0 ;; reserved
10345 mp_config_proc2:
10346 db 0 ;; entry type=processor
10347 db 2 ;; local APIC id
10348 db 0x11 ;; local APIC version number
10349 db 3 ;; cpu flags: enabled, bootstrap processor
10350 db 0,6,0,0 ;; cpu signature
10351 dw 0x201,0 ;; feature flags
10352 dw 0,0 ;; reserved
10353 dw 0,0 ;; reserved
10354 mp_config_proc3:
10355 db 0 ;; entry type=processor
10356 db 3 ;; local APIC id
10357 db 0x11 ;; local APIC version number
10358 db 1 ;; cpu flags: enabled
10359 db 0,6,0,0 ;; cpu signature
10360 dw 0x201,0 ;; feature flags
10361 dw 0,0 ;; reserved
10362 dw 0,0 ;; reserved
10363 mp_config_isa_bus:
10364 db 1 ;; entry type=bus
10365 db 0 ;; bus ID
10366 db 0x49, 0x53, 0x41, 0x20, 0x20, 0x20 ;; bus type="ISA "
10367 mp_config_ioapic:
10368 db 2 ;; entry type=I/O APIC
10369 db 4 ;; apic id=2. linux will set.
10370 db 0x11 ;; I/O APIC version number
10371 db 1 ;; flags=1=enabled
10372 dw 0x0000, 0xfec0 ;; memory mapped address of I/O APIC
10373 mp_config_irqs:
10374 db 3 ;; entry type=I/O interrupt
10375 db 0 ;; interrupt type=vectored interrupt
10376 db 0,0 ;; flags po=0, el=0 (linux uses as default)
10377 db 0 ;; source bus ID is ISA
10378 db 0 ;; source bus IRQ
10379 db 4 ;; destination I/O APIC ID
10380 db 0 ;; destination I/O APIC interrrupt in
10381 ;; repeat pattern for interrupts 0-15
10382 db 3,0,0,0,0,1,4,1
10383 db 3,0,0,0,0,2,4,2
10384 db 3,0,0,0,0,3,4,3
10385 db 3,0,0,0,0,4,4,4
10386 db 3,0,0,0,0,5,4,5
10387 db 3,0,0,0,0,6,4,6
10388 db 3,0,0,0,0,7,4,7
10389 db 3,0,0,0,0,8,4,8
10390 db 3,0,0,0,0,9,4,9
10391 db 3,0,0,0,0,10,4,10
10392 db 3,0,0,0,0,11,4,11
10393 db 3,0,0,0,0,12,4,12
10394 db 3,0,0,0,0,13,4,13
10395 db 3,0,0,0,0,14,4,14
10396 db 3,0,0,0,0,15,4,15
10397 #elif (BX_SMP_PROCESSORS==8)
10398 // define the Intel MP Configuration Structure for 8 processors at
10399 // APIC ID 0,1,2,3,4,5,6,7. I/O APIC at ID=8.
10400 .align 16
10401 mp_config_table:
10402 db 0x50, 0x43, 0x4d, 0x50 ;; "PCMP" signature
10403 dw (mp_config_end-mp_config_table) ;; table length
10404 db 4 ;; spec rev
10405 db 0xc3 ;; checksum
10406 .ascii "BOCHSCPU" ;; OEM id = "BOCHSCPU"
10407 db 0x30, 0x2e, 0x31, 0x20 ;; vendor id = "0.1 "
10408 db 0x20, 0x20, 0x20, 0x20
10409 db 0x20, 0x20, 0x20, 0x20
10410 dw 0,0 ;; oem table ptr
10411 dw 0 ;; oem table size
10412 dw 26 ;; entry count
10413 dw 0x0000, 0xfee0 ;; memory mapped address of local APIC
10414 dw 0 ;; extended table length
10415 db 0 ;; extended table checksum
10416 db 0 ;; reserved
10417 mp_config_proc0:
10418 db 0 ;; entry type=processor
10419 db 0 ;; local APIC id
10420 db 0x11 ;; local APIC version number
10421 db 3 ;; cpu flags: bootstrap cpu
10422 db 0,6,0,0 ;; cpu signature
10423 dw 0x201,0 ;; feature flags
10424 dw 0,0 ;; reserved
10425 dw 0,0 ;; reserved
10426 mp_config_proc1:
10427 db 0 ;; entry type=processor
10428 db 1 ;; local APIC id
10429 db 0x11 ;; local APIC version number
10430 db 1 ;; cpu flags: enabled
10431 db 0,6,0,0 ;; cpu signature
10432 dw 0x201,0 ;; feature flags
10433 dw 0,0 ;; reserved
10434 dw 0,0 ;; reserved
10435 mp_config_proc2:
10436 db 0 ;; entry type=processor
10437 db 2 ;; local APIC id
10438 db 0x11 ;; local APIC version number
10439 db 1 ;; cpu flags: enabled
10440 db 0,6,0,0 ;; cpu signature
10441 dw 0x201,0 ;; feature flags
10442 dw 0,0 ;; reserved
10443 dw 0,0 ;; reserved
10444 mp_config_proc3:
10445 db 0 ;; entry type=processor
10446 db 3 ;; local APIC id
10447 db 0x11 ;; local APIC version number
10448 db 1 ;; cpu flags: enabled
10449 db 0,6,0,0 ;; cpu signature
10450 dw 0x201,0 ;; feature flags
10451 dw 0,0 ;; reserved
10452 dw 0,0 ;; reserved
10453 mp_config_proc4:
10454 db 0 ;; entry type=processor
10455 db 4 ;; local APIC id
10456 db 0x11 ;; local APIC version number
10457 db 1 ;; cpu flags: enabled
10458 db 0,6,0,0 ;; cpu signature
10459 dw 0x201,0 ;; feature flags
10460 dw 0,0 ;; reserved
10461 dw 0,0 ;; reserved
10462 mp_config_proc5:
10463 db 0 ;; entry type=processor
10464 db 5 ;; local APIC id
10465 db 0x11 ;; local APIC version number
10466 db 1 ;; cpu flags: enabled
10467 db 0,6,0,0 ;; cpu signature
10468 dw 0x201,0 ;; feature flags
10469 dw 0,0 ;; reserved
10470 dw 0,0 ;; reserved
10471 mp_config_proc6:
10472 db 0 ;; entry type=processor
10473 db 6 ;; local APIC id
10474 db 0x11 ;; local APIC version number
10475 db 1 ;; cpu flags: enabled
10476 db 0,6,0,0 ;; cpu signature
10477 dw 0x201,0 ;; feature flags
10478 dw 0,0 ;; reserved
10479 dw 0,0 ;; reserved
10480 mp_config_proc7:
10481 db 0 ;; entry type=processor
10482 db 7 ;; local APIC id
10483 db 0x11 ;; local APIC version number
10484 db 1 ;; cpu flags: enabled
10485 db 0,6,0,0 ;; cpu signature
10486 dw 0x201,0 ;; feature flags
10487 dw 0,0 ;; reserved
10488 dw 0,0 ;; reserved
10489 mp_config_isa_bus:
10490 db 1 ;; entry type=bus
10491 db 0 ;; bus ID
10492 db 0x49, 0x53, 0x41, 0x20, 0x20, 0x20 ;; bus type="ISA "
10493 mp_config_ioapic:
10494 db 2 ;; entry type=I/O APIC
10495 db 8 ;; apic id=8
10496 db 0x11 ;; I/O APIC version number
10497 db 1 ;; flags=1=enabled
10498 dw 0x0000, 0xfec0 ;; memory mapped address of I/O APIC
10499 mp_config_irqs:
10500 db 3 ;; entry type=I/O interrupt
10501 db 0 ;; interrupt type=vectored interrupt
10502 db 0,0 ;; flags po=0, el=0 (linux uses as default)
10503 db 0 ;; source bus ID is ISA
10504 db 0 ;; source bus IRQ
10505 db 8 ;; destination I/O APIC ID
10506 db 0 ;; destination I/O APIC interrrupt in
10507 ;; repeat pattern for interrupts 0-15
10508 db 3,0,0,0,0,1,8,1
10509 db 3,0,0,0,0,2,8,2
10510 db 3,0,0,0,0,3,8,3
10511 db 3,0,0,0,0,4,8,4
10512 db 3,0,0,0,0,5,8,5
10513 db 3,0,0,0,0,6,8,6
10514 db 3,0,0,0,0,7,8,7
10515 db 3,0,0,0,0,8,8,8
10516 db 3,0,0,0,0,9,8,9
10517 db 3,0,0,0,0,10,8,10
10518 db 3,0,0,0,0,11,8,11
10519 db 3,0,0,0,0,12,8,12
10520 db 3,0,0,0,0,13,8,13
10521 db 3,0,0,0,0,14,8,14
10522 db 3,0,0,0,0,15,8,15
10523 #else
10524 # error Sorry, rombios only has configurations for 1, 2, 4 or 8 processors.
10525 #endif // if (BX_SMP_PROCESSORS==...)
10527 mp_config_end: // this label used to find length of mp structure
10528 db 0
10530 #if (BX_SMP_PROCESSORS>1)
10531 .align 16
10532 mp_floating_pointer_structure:
10533 db 0x5f, 0x4d, 0x50, 0x5f ; "_MP_" signature
10534 dw mp_config_table, 0xf ;; pointer to MP configuration table
10535 db 1 ;; length of this struct in 16-bit byte chunks
10536 db 4 ;; MP spec revision
10537 db 0xc1 ;; checksum
10538 db 0 ;; MP feature byte 1. value 0 means look at the config table
10539 db 0,0,0,0 ;; MP feature bytes 2-5.
10540 #endif
10542 ASM_END