- fixed URLs for mtools on win32 and rombios documentation
[gplbios.git] / rombios.c
blob52fb70221a428279ccf455d37a1ff0309e89c7e1
1 /////////////////////////////////////////////////////////////////////////
2 // $Id$
3 /////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (C) 2002 MandrakeSoft S.A.
6 //
7 // MandrakeSoft S.A.
8 // 43, rue d'Aboukir
9 // 75002 Paris - France
10 // http://www.linux-mandrake.com/
11 // http://www.mandrakesoft.com/
13 // This library is free software; you can redistribute it and/or
14 // modify it under the terms of the GNU Lesser General Public
15 // License as published by the Free Software Foundation; either
16 // version 2 of the License, or (at your option) any later version.
18 // This library is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 // Lesser General Public License for more details.
23 // You should have received a copy of the GNU Lesser General Public
24 // License along with this library; if not, write to the Free Software
25 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27 // ROM BIOS for use with Bochs/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 // BCC Bug: find a generic way to handle the bug of #asm after an "if" (fixed in 0.16.7)
125 #define DEBUG_ROMBIOS 0
127 #define DEBUG_ATA 0
128 #define DEBUG_INT13_HD 0
129 #define DEBUG_INT13_CD 0
130 #define DEBUG_INT13_ET 0
131 #define DEBUG_INT13_FL 0
132 #define DEBUG_INT15 0
133 #define DEBUG_INT16 0
134 #define DEBUG_INT1A 0
135 #define DEBUG_INT74 0
136 #define DEBUG_APM 0
138 #define BX_CPU 3
139 #define BX_USE_PS2_MOUSE 1
140 #define BX_CALL_INT15_4F 1
141 #define BX_USE_EBDA 1
142 #define BX_SUPPORT_FLOPPY 1
143 #define BX_FLOPPY_ON_CNT 37 /* 2 seconds */
144 #define BX_PCIBIOS 1
145 #define BX_APM 1
147 #define BX_USE_ATADRV 1
148 #define BX_ELTORITO_BOOT 1
150 #define BX_MAX_ATA_INTERFACES 4
151 #define BX_MAX_ATA_DEVICES (BX_MAX_ATA_INTERFACES*2)
153 #define BX_VIRTUAL_PORTS 1 /* normal output to Bochs ports */
154 #define BX_DEBUG_SERIAL 0 /* output to COM1 */
156 /* model byte 0xFC = AT */
157 #define SYS_MODEL_ID 0xFC
158 #define SYS_SUBMODEL_ID 0x00
159 #define BIOS_REVISION 1
160 #define BIOS_CONFIG_TABLE 0xe6f5
162 #ifndef BIOS_BUILD_DATE
163 # define BIOS_BUILD_DATE "06/23/99"
164 #endif
166 // 1K of base memory used for Extended Bios Data Area (EBDA)
167 // EBDA is used for PS/2 mouse support, and IDE BIOS, etc.
168 #define EBDA_SEG 0x9FC0
169 #define EBDA_SIZE 1 // In KiB
170 #define BASE_MEM_IN_K (640 - EBDA_SIZE)
172 // Define the application NAME
173 #ifdef PLEX86
174 # define BX_APPNAME "Plex86"
175 #else
176 # define BX_APPNAME "Bochs"
177 #endif
179 // Sanity Checks
180 #if BX_USE_ATADRV && BX_CPU<3
181 # error The ATA/ATAPI Driver can only to be used with a 386+ cpu
182 #endif
183 #if BX_USE_ATADRV && !BX_USE_EBDA
184 # error ATA/ATAPI Driver can only be used if EBDA is available
185 #endif
186 #if BX_ELTORITO_BOOT && !BX_USE_ATADRV
187 # error El-Torito Boot can only be use if ATA/ATAPI Driver is available
188 #endif
189 #if BX_PCIBIOS && BX_CPU<3
190 # error PCI BIOS can only be used with 386+ cpu
191 #endif
192 #if BX_APM && BX_CPU<3
193 # error APM BIOS can only be used with 386+ cpu
194 #endif
196 #ifndef BX_SMP_PROCESSORS
197 #define BX_SMP_PROCESSORS 1
198 # warning BX_SMP_PROCESSORS not defined, defaulting to 1
199 #endif
201 #define PANIC_PORT 0x400
202 #define PANIC_PORT2 0x401
203 #define INFO_PORT 0x402
204 #define DEBUG_PORT 0x403
206 // define this if you want to make PCIBIOS working on a specific bridge only
207 // undef enables PCIBIOS when at least one PCI device is found
208 // i440FX is emulated by Bochs and QEMU
209 #define PCI_FIXED_HOST_BRIDGE 0x12378086 ;; i440FX PCI bridge
211 // #20 is dec 20
212 // #$20 is hex 20 = 32
213 // #0x20 is hex 20 = 32
214 // LDA #$20
215 // JSR $E820
216 // LDD .i,S
217 // JSR $C682
218 // mov al, #$20
220 // all hex literals should be prefixed with '0x'
221 // grep "#[0-9a-fA-F][0-9a-fA-F]" rombios.c
222 // no mov SEG-REG, #value, must mov register into seg-reg
223 // grep -i "mov[ ]*.s" rombios.c
225 // This is for compiling with gcc2 and gcc3
226 #define ASM_START #asm
227 #define ASM_END #endasm
229 ASM_START
230 .rom
232 .org 0x0000
234 #if BX_CPU >= 3
235 use16 386
236 #else
237 use16 286
238 #endif
240 MACRO HALT
241 ;; the HALT macro is called with the line number of the HALT call.
242 ;; The line number is then sent to the PANIC_PORT, causing Bochs/Plex
243 ;; to print a BX_PANIC message. This will normally halt the simulation
244 ;; with a message such as "BIOS panic at rombios.c, line 4091".
245 ;; However, users can choose to make panics non-fatal and continue.
246 #if BX_VIRTUAL_PORTS
247 mov dx,#PANIC_PORT
248 mov ax,#?1
249 out dx,ax
250 #else
251 mov dx,#0x80
252 mov ax,#?1
253 out dx,al
254 #endif
255 MEND
257 MACRO JMP_AP
258 db 0xea
259 dw ?2
260 dw ?1
261 MEND
263 MACRO SET_INT_VECTOR
264 mov ax, ?3
265 mov ?1*4, ax
266 mov ax, ?2
267 mov ?1*4+2, ax
268 MEND
270 ASM_END
272 typedef unsigned char Bit8u;
273 typedef unsigned short Bit16u;
274 typedef unsigned short bx_bool;
275 typedef unsigned long Bit32u;
277 #if BX_USE_ATADRV
279 void memsetb(seg,offset,value,count);
280 void memcpyb(dseg,doffset,sseg,soffset,count);
281 void memcpyd(dseg,doffset,sseg,soffset,count);
283 // memset of count bytes
284 void
285 memsetb(seg,offset,value,count)
286 Bit16u seg;
287 Bit16u offset;
288 Bit16u value;
289 Bit16u count;
291 ASM_START
292 push bp
293 mov bp, sp
295 push ax
296 push cx
297 push es
298 push di
300 mov cx, 10[bp] ; count
301 cmp cx, #0x00
302 je memsetb_end
303 mov ax, 4[bp] ; segment
304 mov es, ax
305 mov ax, 6[bp] ; offset
306 mov di, ax
307 mov al, 8[bp] ; value
310 stosb
312 memsetb_end:
313 pop di
314 pop es
315 pop cx
316 pop ax
318 pop bp
319 ASM_END
322 #if 0
323 // memcpy of count bytes
324 void
325 memcpyb(dseg,doffset,sseg,soffset,count)
326 Bit16u dseg;
327 Bit16u doffset;
328 Bit16u sseg;
329 Bit16u soffset;
330 Bit16u count;
332 ASM_START
333 push bp
334 mov bp, sp
336 push ax
337 push cx
338 push es
339 push di
340 push ds
341 push si
343 mov cx, 12[bp] ; count
344 cmp cx, #0x0000
345 je memcpyb_end
346 mov ax, 4[bp] ; dsegment
347 mov es, ax
348 mov ax, 6[bp] ; doffset
349 mov di, ax
350 mov ax, 8[bp] ; ssegment
351 mov ds, ax
352 mov ax, 10[bp] ; soffset
353 mov si, ax
356 movsb
358 memcpyb_end:
359 pop si
360 pop ds
361 pop di
362 pop es
363 pop cx
364 pop ax
366 pop bp
367 ASM_END
370 // memcpy of count dword
371 void
372 memcpyd(dseg,doffset,sseg,soffset,count)
373 Bit16u dseg;
374 Bit16u doffset;
375 Bit16u sseg;
376 Bit16u soffset;
377 Bit16u count;
379 ASM_START
380 push bp
381 mov bp, sp
383 push ax
384 push cx
385 push es
386 push di
387 push ds
388 push si
390 mov cx, 12[bp] ; count
391 cmp cx, #0x0000
392 je memcpyd_end
393 mov ax, 4[bp] ; dsegment
394 mov es, ax
395 mov ax, 6[bp] ; doffset
396 mov di, ax
397 mov ax, 8[bp] ; ssegment
398 mov ds, ax
399 mov ax, 10[bp] ; soffset
400 mov si, ax
403 movsd
405 memcpyd_end:
406 pop si
407 pop ds
408 pop di
409 pop es
410 pop cx
411 pop ax
413 pop bp
414 ASM_END
416 #endif
417 #endif //BX_USE_ATADRV
419 // read_dword and write_dword functions
420 static Bit32u read_dword();
421 static void write_dword();
423 Bit32u
424 read_dword(seg, offset)
425 Bit16u seg;
426 Bit16u offset;
428 ASM_START
429 push bp
430 mov bp, sp
432 push bx
433 push ds
434 mov ax, 4[bp] ; segment
435 mov ds, ax
436 mov bx, 6[bp] ; offset
437 mov ax, [bx]
438 inc bx
439 inc bx
440 mov dx, [bx]
441 ;; ax = return value (word)
442 ;; dx = return value (word)
443 pop ds
444 pop bx
446 pop bp
447 ASM_END
450 void
451 write_dword(seg, offset, data)
452 Bit16u seg;
453 Bit16u offset;
454 Bit32u data;
456 ASM_START
457 push bp
458 mov bp, sp
460 push ax
461 push bx
462 push ds
463 mov ax, 4[bp] ; segment
464 mov ds, ax
465 mov bx, 6[bp] ; offset
466 mov ax, 8[bp] ; data word
467 mov [bx], ax ; write data word
468 inc bx
469 inc bx
470 mov ax, 10[bp] ; data word
471 mov [bx], ax ; write data word
472 pop ds
473 pop bx
474 pop ax
476 pop bp
477 ASM_END
480 // Bit32u (unsigned long) and long helper functions
481 ASM_START
483 ;; and function
484 landl:
485 landul:
486 SEG SS
487 and ax,[di]
488 SEG SS
489 and bx,2[di]
492 ;; add function
493 laddl:
494 laddul:
495 SEG SS
496 add ax,[di]
497 SEG SS
498 adc bx,2[di]
501 ;; cmp function
502 lcmpl:
503 lcmpul:
504 and eax, #0x0000FFFF
505 shl ebx, #16
506 add eax, ebx
507 shr ebx, #16
508 SEG SS
509 cmp eax, dword ptr [di]
512 ;; sub function
513 lsubl:
514 lsubul:
515 SEG SS
516 sub ax,[di]
517 SEG SS
518 sbb bx,2[di]
521 ;; mul function
522 lmull:
523 lmulul:
524 and eax, #0x0000FFFF
525 shl ebx, #16
526 add eax, ebx
527 SEG SS
528 mul eax, dword ptr [di]
529 mov ebx, eax
530 shr ebx, #16
533 ;; dec function
534 ldecl:
535 ldecul:
536 SEG SS
537 dec dword ptr [bx]
540 ;; or function
541 lorl:
542 lorul:
543 SEG SS
544 or ax,[di]
545 SEG SS
546 or bx,2[di]
549 ;; inc function
550 lincl:
551 lincul:
552 SEG SS
553 inc dword ptr [bx]
556 ;; tst function
557 ltstl:
558 ltstul:
559 and eax, #0x0000FFFF
560 shl ebx, #16
561 add eax, ebx
562 shr ebx, #16
563 test eax, eax
566 ;; sr function
567 lsrul:
568 mov cx,di
569 jcxz lsr_exit
570 and eax, #0x0000FFFF
571 shl ebx, #16
572 add eax, ebx
573 lsr_loop:
574 shr eax, #1
575 loop lsr_loop
576 mov ebx, eax
577 shr ebx, #16
578 lsr_exit:
581 ;; sl function
582 lsll:
583 lslul:
584 mov cx,di
585 jcxz lsl_exit
586 and eax, #0x0000FFFF
587 shl ebx, #16
588 add eax, ebx
589 lsl_loop:
590 shl eax, #1
591 loop lsl_loop
592 mov ebx, eax
593 shr ebx, #16
594 lsl_exit:
597 idiv_:
599 idiv bx
602 idiv_u:
603 xor dx,dx
604 div bx
607 ldivul:
608 and eax, #0x0000FFFF
609 shl ebx, #16
610 add eax, ebx
611 xor edx, edx
612 SEG SS
613 mov bx, 2[di]
614 shl ebx, #16
615 SEG SS
616 mov bx, [di]
617 div ebx
618 mov ebx, eax
619 shr ebx, #16
622 ASM_END
624 // for access to RAM area which is used by interrupt vectors
625 // and BIOS Data Area
627 typedef struct {
628 unsigned char filler1[0x400];
629 unsigned char filler2[0x6c];
630 Bit16u ticks_low;
631 Bit16u ticks_high;
632 Bit8u midnight_flag;
633 } bios_data_t;
635 #define BiosData ((bios_data_t *) 0)
637 #if BX_USE_ATADRV
638 typedef struct {
639 Bit16u heads; // # heads
640 Bit16u cylinders; // # cylinders
641 Bit16u spt; // # sectors / track
642 } chs_t;
644 // DPTE definition
645 typedef struct {
646 Bit16u iobase1;
647 Bit16u iobase2;
648 Bit8u prefix;
649 Bit8u unused;
650 Bit8u irq;
651 Bit8u blkcount;
652 Bit8u dma;
653 Bit8u pio;
654 Bit16u options;
655 Bit16u reserved;
656 Bit8u revision;
657 Bit8u checksum;
658 } dpte_t;
660 typedef struct {
661 Bit8u iface; // ISA or PCI
662 Bit16u iobase1; // IO Base 1
663 Bit16u iobase2; // IO Base 2
664 Bit8u irq; // IRQ
665 } ata_channel_t;
667 typedef struct {
668 Bit8u type; // Detected type of ata (ata/atapi/none/unknown)
669 Bit8u device; // Detected type of attached devices (hd/cd/none)
670 Bit8u removable; // Removable device flag
671 Bit8u lock; // Locks for removable devices
672 // Bit8u lba_capable; // LBA capable flag - always yes for bochs devices
673 Bit8u mode; // transfert mode : PIO 16/32 bits - IRQ - ISADMA - PCIDMA
674 Bit16u blksize; // block size
676 Bit8u translation; // type of translation
677 chs_t lchs; // Logical CHS
678 chs_t pchs; // Physical CHS
680 Bit32u sectors; // Total sectors count
681 } ata_device_t;
683 typedef struct {
684 // ATA channels info
685 ata_channel_t channels[BX_MAX_ATA_INTERFACES];
687 // ATA devices info
688 ata_device_t devices[BX_MAX_ATA_DEVICES];
690 // map between (bios hd id - 0x80) and ata channels
691 Bit8u hdcount, hdidmap[BX_MAX_ATA_DEVICES];
693 // map between (bios cd id - 0xE0) and ata channels
694 Bit8u cdcount, cdidmap[BX_MAX_ATA_DEVICES];
696 // Buffer for DPTE table
697 dpte_t dpte;
699 // Count of transferred sectors and bytes
700 Bit16u trsfsectors;
701 Bit32u trsfbytes;
703 } ata_t;
705 #if BX_ELTORITO_BOOT
706 // ElTorito Device Emulation data
707 typedef struct {
708 Bit8u active;
709 Bit8u media;
710 Bit8u emulated_drive;
711 Bit8u controller_index;
712 Bit16u device_spec;
713 Bit32u ilba;
714 Bit16u buffer_segment;
715 Bit16u load_segment;
716 Bit16u sector_count;
718 // Virtual device
719 chs_t vdevice;
720 } cdemu_t;
721 #endif // BX_ELTORITO_BOOT
723 // for access to EBDA area
724 // The EBDA structure should conform to
725 // http://www.frontiernet.net/~fys/rombios.htm document
726 // I made the ata and cdemu structs begin at 0x121 in the EBDA seg
727 typedef struct {
728 unsigned char filler1[0x3D];
730 // FDPT - Can be splitted in data members if needed
731 unsigned char fdpt0[0x10];
732 unsigned char fdpt1[0x10];
734 unsigned char filler2[0xC4];
736 // ATA Driver data
737 ata_t ata;
739 #if BX_ELTORITO_BOOT
740 // El Torito Emulation data
741 cdemu_t cdemu;
742 #endif // BX_ELTORITO_BOOT
744 } ebda_data_t;
746 #define EbdaData ((ebda_data_t *) 0)
748 // for access to the int13ext structure
749 typedef struct {
750 Bit8u size;
751 Bit8u reserved;
752 Bit16u count;
753 Bit16u offset;
754 Bit16u segment;
755 Bit32u lba1;
756 Bit32u lba2;
757 } int13ext_t;
759 #define Int13Ext ((int13ext_t *) 0)
761 // Disk Physical Table definition
762 typedef struct {
763 Bit16u size;
764 Bit16u infos;
765 Bit32u cylinders;
766 Bit32u heads;
767 Bit32u spt;
768 Bit32u sector_count1;
769 Bit32u sector_count2;
770 Bit16u blksize;
771 Bit16u dpte_segment;
772 Bit16u dpte_offset;
773 Bit16u key;
774 Bit8u dpi_length;
775 Bit8u reserved1;
776 Bit16u reserved2;
777 Bit8u host_bus[4];
778 Bit8u iface_type[8];
779 Bit8u iface_path[8];
780 Bit8u device_path[8];
781 Bit8u reserved3;
782 Bit8u checksum;
783 } dpt_t;
785 #define Int13DPT ((dpt_t *) 0)
787 #endif // BX_USE_ATADRV
789 typedef struct {
790 union {
791 struct {
792 Bit16u di, si, bp, sp;
793 Bit16u bx, dx, cx, ax;
794 } r16;
795 struct {
796 Bit16u filler[4];
797 Bit8u bl, bh, dl, dh, cl, ch, al, ah;
798 } r8;
799 } u;
800 } pusha_regs_t;
802 typedef struct {
803 union {
804 struct {
805 Bit32u edi, esi, ebp, esp;
806 Bit32u ebx, edx, ecx, eax;
807 } r32;
808 struct {
809 Bit16u di, filler1, si, filler2, bp, filler3, sp, filler4;
810 Bit16u bx, filler5, dx, filler6, cx, filler7, ax, filler8;
811 } r16;
812 struct {
813 Bit32u filler[4];
814 Bit8u bl, bh;
815 Bit16u filler1;
816 Bit8u dl, dh;
817 Bit16u filler2;
818 Bit8u cl, ch;
819 Bit16u filler3;
820 Bit8u al, ah;
821 Bit16u filler4;
822 } r8;
823 } u;
824 } pushad_regs_t;
826 typedef struct {
827 union {
828 struct {
829 Bit16u flags;
830 } r16;
831 struct {
832 Bit8u flagsl;
833 Bit8u flagsh;
834 } r8;
835 } u;
836 } flags_t;
838 #define SetCF(x) x.u.r8.flagsl |= 0x01
839 #define SetZF(x) x.u.r8.flagsl |= 0x40
840 #define ClearCF(x) x.u.r8.flagsl &= 0xfe
841 #define ClearZF(x) x.u.r8.flagsl &= 0xbf
842 #define GetCF(x) (x.u.r8.flagsl & 0x01)
844 typedef struct {
845 Bit16u ip;
846 Bit16u cs;
847 flags_t flags;
848 } iret_addr_t;
852 static Bit8u inb();
853 static Bit8u inb_cmos();
854 static void outb();
855 static void outb_cmos();
856 static Bit16u inw();
857 static void outw();
858 static void init_rtc();
859 static bx_bool rtc_updating();
861 static Bit8u read_byte();
862 static Bit16u read_word();
863 static void write_byte();
864 static void write_word();
865 static void bios_printf();
867 static Bit8u inhibit_mouse_int_and_events();
868 static void enable_mouse_int_and_events();
869 static Bit8u send_to_mouse_ctrl();
870 static Bit8u get_mouse_data();
871 static void set_kbd_command_byte();
873 static void int09_function();
874 static void int13_harddisk();
875 static void int13_cdrom();
876 static void int13_cdemu();
877 static void int13_eltorito();
878 static void int13_diskette_function();
879 static void int14_function();
880 static void int15_function();
881 static void int16_function();
882 static void int17_function();
883 static Bit32u int19_function();
884 static void int1a_function();
885 static void int70_function();
886 static void int74_function();
887 static Bit16u get_CS();
888 //static Bit16u get_DS();
889 //static void set_DS();
890 static Bit16u get_SS();
891 static unsigned int enqueue_key();
892 static unsigned int dequeue_key();
893 static void get_hd_geometry();
894 static void set_diskette_ret_status();
895 static void set_diskette_current_cyl();
896 static void determine_floppy_media();
897 static bx_bool floppy_drive_exists();
898 static bx_bool floppy_drive_recal();
899 static bx_bool floppy_media_known();
900 static bx_bool floppy_media_sense();
901 static bx_bool set_enable_a20();
902 static void debugger_on();
903 static void debugger_off();
904 static void keyboard_init();
905 static void keyboard_panic();
906 static void shutdown_status_panic();
907 static void nmi_handler_msg();
909 static void print_bios_banner();
910 static void print_boot_device();
911 static void print_boot_failure();
912 static void print_cdromboot_failure();
914 # if BX_USE_ATADRV
916 // ATA / ATAPI driver
917 void ata_init();
918 void ata_detect();
919 void ata_reset();
921 Bit16u ata_cmd_non_data();
922 Bit16u ata_cmd_data_in();
923 Bit16u ata_cmd_data_out();
924 Bit16u ata_cmd_packet();
926 Bit16u atapi_get_sense();
927 Bit16u atapi_is_ready();
928 Bit16u atapi_is_cdrom();
930 #endif // BX_USE_ATADRV
932 #if BX_ELTORITO_BOOT
934 void cdemu_init();
935 Bit8u cdemu_isactive();
936 Bit8u cdemu_emulated_drive();
938 Bit16u cdrom_boot();
940 #endif // BX_ELTORITO_BOOT
942 static char bios_cvs_version_string[] = "$Revision$";
943 static char bios_date_string[] = "$Date$";
945 static char CVSID[] = "$Id$";
947 /* Offset to skip the CVS $Id: prefix */
948 #define bios_version_string (CVSID + 4)
949 #define BIOS_COPYRIGHT_STRING "(c) 2002 MandrakeSoft S.A. Written by Kevin Lawton & the Bochs team."
951 #define BIOS_PRINTF_HALT 1
952 #define BIOS_PRINTF_SCREEN 2
953 #define BIOS_PRINTF_INFO 4
954 #define BIOS_PRINTF_DEBUG 8
955 #define BIOS_PRINTF_ALL (BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO)
956 #define BIOS_PRINTF_DEBHALT (BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO | BIOS_PRINTF_HALT)
958 #define printf(format, p...) bios_printf(BIOS_PRINTF_SCREEN, format, ##p)
960 // Defines the output macros.
961 // BX_DEBUG goes to INFO port until we can easily choose debug info on a
962 // per-device basis. Debug info are sent only in debug mode
963 #if DEBUG_ROMBIOS
964 # define BX_DEBUG(format, p...) bios_printf(BIOS_PRINTF_INFO, format, ##p)
965 #else
966 # define BX_DEBUG(format, p...)
967 #endif
968 #define BX_INFO(format, p...) bios_printf(BIOS_PRINTF_INFO, format, ##p)
969 #define BX_PANIC(format, p...) bios_printf(BIOS_PRINTF_DEBHALT, format, ##p)
971 #if DEBUG_ATA
972 # define BX_DEBUG_ATA(a...) BX_DEBUG(a)
973 #else
974 # define BX_DEBUG_ATA(a...)
975 #endif
976 #if DEBUG_INT13_HD
977 # define BX_DEBUG_INT13_HD(a...) BX_DEBUG(a)
978 #else
979 # define BX_DEBUG_INT13_HD(a...)
980 #endif
981 #if DEBUG_INT13_CD
982 # define BX_DEBUG_INT13_CD(a...) BX_DEBUG(a)
983 #else
984 # define BX_DEBUG_INT13_CD(a...)
985 #endif
986 #if DEBUG_INT13_ET
987 # define BX_DEBUG_INT13_ET(a...) BX_DEBUG(a)
988 #else
989 # define BX_DEBUG_INT13_ET(a...)
990 #endif
991 #if DEBUG_INT13_FL
992 # define BX_DEBUG_INT13_FL(a...) BX_DEBUG(a)
993 #else
994 # define BX_DEBUG_INT13_FL(a...)
995 #endif
996 #if DEBUG_INT15
997 # define BX_DEBUG_INT15(a...) BX_DEBUG(a)
998 #else
999 # define BX_DEBUG_INT15(a...)
1000 #endif
1001 #if DEBUG_INT16
1002 # define BX_DEBUG_INT16(a...) BX_DEBUG(a)
1003 #else
1004 # define BX_DEBUG_INT16(a...)
1005 #endif
1006 #if DEBUG_INT1A
1007 # define BX_DEBUG_INT1A(a...) BX_DEBUG(a)
1008 #else
1009 # define BX_DEBUG_INT1A(a...)
1010 #endif
1011 #if DEBUG_INT74
1012 # define BX_DEBUG_INT74(a...) BX_DEBUG(a)
1013 #else
1014 # define BX_DEBUG_INT74(a...)
1015 #endif
1017 #define SET_AL(val8) AX = ((AX & 0xff00) | (val8))
1018 #define SET_BL(val8) BX = ((BX & 0xff00) | (val8))
1019 #define SET_CL(val8) CX = ((CX & 0xff00) | (val8))
1020 #define SET_DL(val8) DX = ((DX & 0xff00) | (val8))
1021 #define SET_AH(val8) AX = ((AX & 0x00ff) | ((val8) << 8))
1022 #define SET_BH(val8) BX = ((BX & 0x00ff) | ((val8) << 8))
1023 #define SET_CH(val8) CX = ((CX & 0x00ff) | ((val8) << 8))
1024 #define SET_DH(val8) DX = ((DX & 0x00ff) | ((val8) << 8))
1026 #define GET_AL() ( AX & 0x00ff )
1027 #define GET_BL() ( BX & 0x00ff )
1028 #define GET_CL() ( CX & 0x00ff )
1029 #define GET_DL() ( DX & 0x00ff )
1030 #define GET_AH() ( AX >> 8 )
1031 #define GET_BH() ( BX >> 8 )
1032 #define GET_CH() ( CX >> 8 )
1033 #define GET_DH() ( DX >> 8 )
1035 #define GET_ELDL() ( ELDX & 0x00ff )
1036 #define GET_ELDH() ( ELDX >> 8 )
1038 #define SET_CF() FLAGS |= 0x0001
1039 #define CLEAR_CF() FLAGS &= 0xfffe
1040 #define GET_CF() (FLAGS & 0x0001)
1042 #define SET_ZF() FLAGS |= 0x0040
1043 #define CLEAR_ZF() FLAGS &= 0xffbf
1044 #define GET_ZF() (FLAGS & 0x0040)
1046 #define UNSUPPORTED_FUNCTION 0x86
1048 #define none 0
1049 #define MAX_SCAN_CODE 0x58
1051 static struct {
1052 Bit16u normal;
1053 Bit16u shift;
1054 Bit16u control;
1055 Bit16u alt;
1056 Bit8u lock_flags;
1057 } scan_to_scanascii[MAX_SCAN_CODE + 1] = {
1058 { none, none, none, none, none },
1059 { 0x011b, 0x011b, 0x011b, 0x0100, none }, /* escape */
1060 { 0x0231, 0x0221, none, 0x7800, none }, /* 1! */
1061 { 0x0332, 0x0340, 0x0300, 0x7900, none }, /* 2@ */
1062 { 0x0433, 0x0423, none, 0x7a00, none }, /* 3# */
1063 { 0x0534, 0x0524, none, 0x7b00, none }, /* 4$ */
1064 { 0x0635, 0x0625, none, 0x7c00, none }, /* 5% */
1065 { 0x0736, 0x075e, 0x071e, 0x7d00, none }, /* 6^ */
1066 { 0x0837, 0x0826, none, 0x7e00, none }, /* 7& */
1067 { 0x0938, 0x092a, none, 0x7f00, none }, /* 8* */
1068 { 0x0a39, 0x0a28, none, 0x8000, none }, /* 9( */
1069 { 0x0b30, 0x0b29, none, 0x8100, none }, /* 0) */
1070 { 0x0c2d, 0x0c5f, 0x0c1f, 0x8200, none }, /* -_ */
1071 { 0x0d3d, 0x0d2b, none, 0x8300, none }, /* =+ */
1072 { 0x0e08, 0x0e08, 0x0e7f, none, none }, /* backspace */
1073 { 0x0f09, 0x0f00, none, none, none }, /* tab */
1074 { 0x1071, 0x1051, 0x1011, 0x1000, 0x40 }, /* Q */
1075 { 0x1177, 0x1157, 0x1117, 0x1100, 0x40 }, /* W */
1076 { 0x1265, 0x1245, 0x1205, 0x1200, 0x40 }, /* E */
1077 { 0x1372, 0x1352, 0x1312, 0x1300, 0x40 }, /* R */
1078 { 0x1474, 0x1454, 0x1414, 0x1400, 0x40 }, /* T */
1079 { 0x1579, 0x1559, 0x1519, 0x1500, 0x40 }, /* Y */
1080 { 0x1675, 0x1655, 0x1615, 0x1600, 0x40 }, /* U */
1081 { 0x1769, 0x1749, 0x1709, 0x1700, 0x40 }, /* I */
1082 { 0x186f, 0x184f, 0x180f, 0x1800, 0x40 }, /* O */
1083 { 0x1970, 0x1950, 0x1910, 0x1900, 0x40 }, /* P */
1084 { 0x1a5b, 0x1a7b, 0x1a1b, none, none }, /* [{ */
1085 { 0x1b5d, 0x1b7d, 0x1b1d, none, none }, /* ]} */
1086 { 0x1c0d, 0x1c0d, 0x1c0a, none, none }, /* Enter */
1087 { none, none, none, none, none }, /* L Ctrl */
1088 { 0x1e61, 0x1e41, 0x1e01, 0x1e00, 0x40 }, /* A */
1089 { 0x1f73, 0x1f53, 0x1f13, 0x1f00, 0x40 }, /* S */
1090 { 0x2064, 0x2044, 0x2004, 0x2000, 0x40 }, /* D */
1091 { 0x2166, 0x2146, 0x2106, 0x2100, 0x40 }, /* F */
1092 { 0x2267, 0x2247, 0x2207, 0x2200, 0x40 }, /* G */
1093 { 0x2368, 0x2348, 0x2308, 0x2300, 0x40 }, /* H */
1094 { 0x246a, 0x244a, 0x240a, 0x2400, 0x40 }, /* J */
1095 { 0x256b, 0x254b, 0x250b, 0x2500, 0x40 }, /* K */
1096 { 0x266c, 0x264c, 0x260c, 0x2600, 0x40 }, /* L */
1097 { 0x273b, 0x273a, none, none, none }, /* ;: */
1098 { 0x2827, 0x2822, none, none, none }, /* '" */
1099 { 0x2960, 0x297e, none, none, none }, /* `~ */
1100 { none, none, none, none, none }, /* L shift */
1101 { 0x2b5c, 0x2b7c, 0x2b1c, none, none }, /* |\ */
1102 { 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00, 0x40 }, /* Z */
1103 { 0x2d78, 0x2d58, 0x2d18, 0x2d00, 0x40 }, /* X */
1104 { 0x2e63, 0x2e43, 0x2e03, 0x2e00, 0x40 }, /* C */
1105 { 0x2f76, 0x2f56, 0x2f16, 0x2f00, 0x40 }, /* V */
1106 { 0x3062, 0x3042, 0x3002, 0x3000, 0x40 }, /* B */
1107 { 0x316e, 0x314e, 0x310e, 0x3100, 0x40 }, /* N */
1108 { 0x326d, 0x324d, 0x320d, 0x3200, 0x40 }, /* M */
1109 { 0x332c, 0x333c, none, none, none }, /* ,< */
1110 { 0x342e, 0x343e, none, none, none }, /* .> */
1111 { 0x352f, 0x353f, none, none, none }, /* /? */
1112 { none, none, none, none, none }, /* R Shift */
1113 { 0x372a, 0x372a, none, none, none }, /* * */
1114 { none, none, none, none, none }, /* L Alt */
1115 { 0x3920, 0x3920, 0x3920, 0x3920, none }, /* space */
1116 { none, none, none, none, none }, /* caps lock */
1117 { 0x3b00, 0x5400, 0x5e00, 0x6800, none }, /* F1 */
1118 { 0x3c00, 0x5500, 0x5f00, 0x6900, none }, /* F2 */
1119 { 0x3d00, 0x5600, 0x6000, 0x6a00, none }, /* F3 */
1120 { 0x3e00, 0x5700, 0x6100, 0x6b00, none }, /* F4 */
1121 { 0x3f00, 0x5800, 0x6200, 0x6c00, none }, /* F5 */
1122 { 0x4000, 0x5900, 0x6300, 0x6d00, none }, /* F6 */
1123 { 0x4100, 0x5a00, 0x6400, 0x6e00, none }, /* F7 */
1124 { 0x4200, 0x5b00, 0x6500, 0x6f00, none }, /* F8 */
1125 { 0x4300, 0x5c00, 0x6600, 0x7000, none }, /* F9 */
1126 { 0x4400, 0x5d00, 0x6700, 0x7100, none }, /* F10 */
1127 { none, none, none, none, none }, /* Num Lock */
1128 { none, none, none, none, none }, /* Scroll Lock */
1129 { 0x4700, 0x4737, 0x7700, none, 0x20 }, /* 7 Home */
1130 { 0x4800, 0x4838, none, none, 0x20 }, /* 8 UP */
1131 { 0x4900, 0x4939, 0x8400, none, 0x20 }, /* 9 PgUp */
1132 { 0x4a2d, 0x4a2d, none, none, none }, /* - */
1133 { 0x4b00, 0x4b34, 0x7300, none, 0x20 }, /* 4 Left */
1134 { 0x4c00, 0x4c35, none, none, 0x20 }, /* 5 */
1135 { 0x4d00, 0x4d36, 0x7400, none, 0x20 }, /* 6 Right */
1136 { 0x4e2b, 0x4e2b, none, none, none }, /* + */
1137 { 0x4f00, 0x4f31, 0x7500, none, 0x20 }, /* 1 End */
1138 { 0x5000, 0x5032, none, none, 0x20 }, /* 2 Down */
1139 { 0x5100, 0x5133, 0x7600, none, 0x20 }, /* 3 PgDn */
1140 { 0x5200, 0x5230, none, none, 0x20 }, /* 0 Ins */
1141 { 0x5300, 0x532e, none, none, 0x20 }, /* Del */
1142 { none, none, none, none, none },
1143 { none, none, none, none, none },
1144 { none, none, none, none, none },
1145 { 0x5700, 0x5700, none, none, none }, /* F11 */
1146 { 0x5800, 0x5800, none, none, none } /* F12 */
1149 Bit8u
1150 inb(port)
1151 Bit16u port;
1153 ASM_START
1154 push bp
1155 mov bp, sp
1157 push dx
1158 mov dx, 4[bp]
1159 in al, dx
1160 pop dx
1162 pop bp
1163 ASM_END
1166 #if BX_USE_ATADRV
1167 Bit16u
1168 inw(port)
1169 Bit16u port;
1171 ASM_START
1172 push bp
1173 mov bp, sp
1175 push dx
1176 mov dx, 4[bp]
1177 in ax, dx
1178 pop dx
1180 pop bp
1181 ASM_END
1183 #endif
1185 void
1186 outb(port, val)
1187 Bit16u port;
1188 Bit8u val;
1190 ASM_START
1191 push bp
1192 mov bp, sp
1194 push ax
1195 push dx
1196 mov dx, 4[bp]
1197 mov al, 6[bp]
1198 out dx, al
1199 pop dx
1200 pop ax
1202 pop bp
1203 ASM_END
1206 #if BX_USE_ATADRV
1207 void
1208 outw(port, val)
1209 Bit16u port;
1210 Bit16u val;
1212 ASM_START
1213 push bp
1214 mov bp, sp
1216 push ax
1217 push dx
1218 mov dx, 4[bp]
1219 mov ax, 6[bp]
1220 out dx, ax
1221 pop dx
1222 pop ax
1224 pop bp
1225 ASM_END
1227 #endif
1229 void
1230 outb_cmos(cmos_reg, val)
1231 Bit8u cmos_reg;
1232 Bit8u val;
1234 ASM_START
1235 push bp
1236 mov bp, sp
1238 mov al, 4[bp] ;; cmos_reg
1239 out 0x70, al
1240 mov al, 6[bp] ;; val
1241 out 0x71, al
1243 pop bp
1244 ASM_END
1247 Bit8u
1248 inb_cmos(cmos_reg)
1249 Bit8u cmos_reg;
1251 ASM_START
1252 push bp
1253 mov bp, sp
1255 mov al, 4[bp] ;; cmos_reg
1256 out 0x70, al
1257 in al, 0x71
1259 pop bp
1260 ASM_END
1263 void
1264 init_rtc()
1266 outb_cmos(0x0a, 0x26);
1267 outb_cmos(0x0b, 0x02);
1268 inb_cmos(0x0c);
1269 inb_cmos(0x0d);
1272 bx_bool
1273 rtc_updating()
1275 // This function checks to see if the update-in-progress bit
1276 // is set in CMOS Status Register A. If not, it returns 0.
1277 // If it is set, it tries to wait until there is a transition
1278 // to 0, and will return 0 if such a transition occurs. A 1
1279 // is returned only after timing out. The maximum period
1280 // that this bit should be set is constrained to 244useconds.
1281 // The count I use below guarantees coverage or more than
1282 // this time, with any reasonable IPS setting.
1284 Bit16u count;
1286 count = 25000;
1287 while (--count != 0) {
1288 if ( (inb_cmos(0x0a) & 0x80) == 0 )
1289 return(0);
1291 return(1); // update-in-progress never transitioned to 0
1295 Bit8u
1296 read_byte(seg, offset)
1297 Bit16u seg;
1298 Bit16u offset;
1300 ASM_START
1301 push bp
1302 mov bp, sp
1304 push bx
1305 push ds
1306 mov ax, 4[bp] ; segment
1307 mov ds, ax
1308 mov bx, 6[bp] ; offset
1309 mov al, [bx]
1310 ;; al = return value (byte)
1311 pop ds
1312 pop bx
1314 pop bp
1315 ASM_END
1318 Bit16u
1319 read_word(seg, offset)
1320 Bit16u seg;
1321 Bit16u offset;
1323 ASM_START
1324 push bp
1325 mov bp, sp
1327 push bx
1328 push ds
1329 mov ax, 4[bp] ; segment
1330 mov ds, ax
1331 mov bx, 6[bp] ; offset
1332 mov ax, [bx]
1333 ;; ax = return value (word)
1334 pop ds
1335 pop bx
1337 pop bp
1338 ASM_END
1341 void
1342 write_byte(seg, offset, data)
1343 Bit16u seg;
1344 Bit16u offset;
1345 Bit8u data;
1347 ASM_START
1348 push bp
1349 mov bp, sp
1351 push ax
1352 push bx
1353 push ds
1354 mov ax, 4[bp] ; segment
1355 mov ds, ax
1356 mov bx, 6[bp] ; offset
1357 mov al, 8[bp] ; data byte
1358 mov [bx], al ; write data byte
1359 pop ds
1360 pop bx
1361 pop ax
1363 pop bp
1364 ASM_END
1367 void
1368 write_word(seg, offset, data)
1369 Bit16u seg;
1370 Bit16u offset;
1371 Bit16u data;
1373 ASM_START
1374 push bp
1375 mov bp, sp
1377 push ax
1378 push bx
1379 push ds
1380 mov ax, 4[bp] ; segment
1381 mov ds, ax
1382 mov bx, 6[bp] ; offset
1383 mov ax, 8[bp] ; data word
1384 mov [bx], ax ; write data word
1385 pop ds
1386 pop bx
1387 pop ax
1389 pop bp
1390 ASM_END
1393 Bit16u
1394 get_CS()
1396 ASM_START
1397 mov ax, cs
1398 ASM_END
1401 // Bit16u
1402 //get_DS()
1404 //ASM_START
1405 // mov ax, ds
1406 //ASM_END
1409 // void
1410 //set_DS(ds_selector)
1411 // Bit16u ds_selector;
1413 //ASM_START
1414 // push bp
1415 // mov bp, sp
1417 // push ax
1418 // mov ax, 4[bp] ; ds_selector
1419 // mov ds, ax
1420 // pop ax
1422 // pop bp
1423 //ASM_END
1426 Bit16u
1427 get_SS()
1429 ASM_START
1430 mov ax, ss
1431 ASM_END
1434 #if BX_DEBUG_SERIAL
1435 /* serial debug port*/
1436 #define BX_DEBUG_PORT 0x03f8
1438 /* data */
1439 #define UART_RBR 0x00
1440 #define UART_THR 0x00
1442 /* control */
1443 #define UART_IER 0x01
1444 #define UART_IIR 0x02
1445 #define UART_FCR 0x02
1446 #define UART_LCR 0x03
1447 #define UART_MCR 0x04
1448 #define UART_DLL 0x00
1449 #define UART_DLM 0x01
1451 /* status */
1452 #define UART_LSR 0x05
1453 #define UART_MSR 0x06
1454 #define UART_SCR 0x07
1456 int uart_can_tx_byte(base_port)
1457 Bit16u base_port;
1459 return inb(base_port + UART_LSR) & 0x20;
1462 void uart_wait_to_tx_byte(base_port)
1463 Bit16u base_port;
1465 while (!uart_can_tx_byte(base_port));
1468 void uart_wait_until_sent(base_port)
1469 Bit16u base_port;
1471 while (!(inb(base_port + UART_LSR) & 0x40));
1474 void uart_tx_byte(base_port, data)
1475 Bit16u base_port;
1476 Bit8u data;
1478 uart_wait_to_tx_byte(base_port);
1479 outb(base_port + UART_THR, data);
1480 uart_wait_until_sent(base_port);
1482 #endif
1484 void
1485 wrch(c)
1486 Bit8u c;
1488 ASM_START
1489 push bp
1490 mov bp, sp
1492 push bx
1493 mov ah, #0x0e
1494 mov al, 4[bp]
1495 xor bx,bx
1496 int #0x10
1497 pop bx
1499 pop bp
1500 ASM_END
1503 void
1504 send(action, c)
1505 Bit16u action;
1506 Bit8u c;
1508 #if BX_DEBUG_SERIAL
1509 if (c == '\n') uart_tx_byte(BX_DEBUG_PORT, '\r');
1510 uart_tx_byte(BX_DEBUG_PORT, c);
1511 #endif
1512 #if BX_VIRTUAL_PORTS
1513 if (action & BIOS_PRINTF_DEBUG) outb(DEBUG_PORT, c);
1514 if (action & BIOS_PRINTF_INFO) outb(INFO_PORT, c);
1515 #endif
1516 if (action & BIOS_PRINTF_SCREEN) {
1517 if (c == '\n') wrch('\r');
1518 wrch(c);
1522 void
1523 put_int(action, val, width, neg)
1524 Bit16u action;
1525 short val, width;
1526 bx_bool neg;
1528 short nval = val / 10;
1529 if (nval)
1530 put_int(action, nval, width - 1, neg);
1531 else {
1532 while (--width > 0) send(action, ' ');
1533 if (neg) send(action, '-');
1535 send(action, val - (nval * 10) + '0');
1538 void
1539 put_uint(action, val, width, neg)
1540 Bit16u action;
1541 unsigned short val;
1542 short width;
1543 bx_bool neg;
1545 unsigned short nval = val / 10;
1546 if (nval)
1547 put_uint(action, nval, width - 1, neg);
1548 else {
1549 while (--width > 0) send(action, ' ');
1550 if (neg) send(action, '-');
1552 send(action, val - (nval * 10) + '0');
1555 //--------------------------------------------------------------------------
1556 // bios_printf()
1557 // A compact variable argument printf function which prints its output via
1558 // an I/O port so that it can be logged by Bochs/Plex.
1559 // Currently, only %x is supported (or %02x, %04x, etc).
1561 // Supports %[format_width][format]
1562 // where format can be d,x,c,s
1563 //--------------------------------------------------------------------------
1564 void
1565 bios_printf(action, s)
1566 Bit16u action;
1567 Bit8u *s;
1569 Bit8u c, format_char;
1570 bx_bool in_format;
1571 short i;
1572 Bit16u *arg_ptr;
1573 Bit16u arg_seg, arg, nibble, shift_count, format_width;
1575 arg_ptr = &s;
1576 arg_seg = get_SS();
1578 in_format = 0;
1579 format_width = 0;
1581 if ((action & BIOS_PRINTF_DEBHALT) == BIOS_PRINTF_DEBHALT) {
1582 #if BX_VIRTUAL_PORTS
1583 outb(PANIC_PORT2, 0x00);
1584 #endif
1585 bios_printf (BIOS_PRINTF_SCREEN, "FATAL: ");
1588 while (c = read_byte(get_CS(), s)) {
1589 if ( c == '%' ) {
1590 in_format = 1;
1591 format_width = 0;
1593 else if (in_format) {
1594 if ( (c>='0') && (c<='9') ) {
1595 format_width = (format_width * 10) + (c - '0');
1597 else {
1598 arg_ptr++; // increment to next arg
1599 arg = read_word(arg_seg, arg_ptr);
1600 if (c == 'x') {
1601 if (format_width == 0)
1602 format_width = 4;
1603 for (i=format_width-1; i>=0; i--) {
1604 nibble = (arg >> (4 * i)) & 0x000f;
1605 send (action, (nibble<=9)? (nibble+'0') : (nibble-10+'A'));
1608 else if (c == 'u') {
1609 put_uint(action, arg, format_width, 0);
1611 else if (c == 'd') {
1612 if (arg & 0x8000)
1613 put_int(action, -arg, format_width - 1, 1);
1614 else
1615 put_int(action, arg, format_width, 0);
1617 else if (c == 's') {
1618 bios_printf(action & (~BIOS_PRINTF_HALT), arg);
1620 else if (c == 'c') {
1621 send(action, arg);
1623 else
1624 BX_PANIC("bios_printf: unknown format\n");
1625 in_format = 0;
1628 else {
1629 send(action, c);
1631 s ++;
1634 if (action & BIOS_PRINTF_HALT) {
1635 // freeze in a busy loop.
1636 ASM_START
1638 halt2_loop:
1640 jmp halt2_loop
1641 ASM_END
1645 //--------------------------------------------------------------------------
1646 // keyboard_init
1647 //--------------------------------------------------------------------------
1648 // this file is based on LinuxBIOS implementation of keyboard.c
1649 // could convert to #asm to gain space
1650 void
1651 keyboard_init()
1653 Bit16u max;
1655 /* ------------------- Flush buffers ------------------------*/
1656 /* Wait until buffer is empty */
1657 max=0xffff;
1658 while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
1660 /* flush incoming keys */
1661 max=0x2000;
1662 while (--max > 0) {
1663 outb(0x80, 0x00);
1664 if (inb(0x64) & 0x01) {
1665 inb(0x60);
1666 max = 0x2000;
1670 // Due to timer issues, and if the IPS setting is > 15000000,
1671 // the incoming keys might not be flushed here. That will
1672 // cause a panic a few lines below. See sourceforge bug report :
1673 // [ 642031 ] FATAL: Keyboard RESET error:993
1675 /* ------------------- controller side ----------------------*/
1676 /* send cmd = 0xAA, self test 8042 */
1677 outb(0x64, 0xaa);
1679 /* Wait until buffer is empty */
1680 max=0xffff;
1681 while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
1682 if (max==0x0) keyboard_panic(00);
1684 /* Wait for data */
1685 max=0xffff;
1686 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x01);
1687 if (max==0x0) keyboard_panic(01);
1689 /* read self-test result, 0x55 should be returned from 0x60 */
1690 if ((inb(0x60) != 0x55)){
1691 keyboard_panic(991);
1694 /* send cmd = 0xAB, keyboard interface test */
1695 outb(0x64,0xab);
1697 /* Wait until buffer is empty */
1698 max=0xffff;
1699 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x10);
1700 if (max==0x0) keyboard_panic(10);
1702 /* Wait for data */
1703 max=0xffff;
1704 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x11);
1705 if (max==0x0) keyboard_panic(11);
1707 /* read keyboard interface test result, */
1708 /* 0x00 should be returned form 0x60 */
1709 if ((inb(0x60) != 0x00)) {
1710 keyboard_panic(992);
1713 /* Enable Keyboard clock */
1714 outb(0x64,0xae);
1715 outb(0x64,0xa8);
1717 /* ------------------- keyboard side ------------------------*/
1718 /* reset kerboard and self test (keyboard side) */
1719 outb(0x60, 0xff);
1721 /* Wait until buffer is empty */
1722 max=0xffff;
1723 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x20);
1724 if (max==0x0) keyboard_panic(20);
1726 /* Wait for data */
1727 max=0xffff;
1728 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x21);
1729 if (max==0x0) keyboard_panic(21);
1731 /* keyboard should return ACK */
1732 if ((inb(0x60) != 0xfa)) {
1733 keyboard_panic(993);
1736 /* Wait for data */
1737 max=0xffff;
1738 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x31);
1739 if (max==0x0) keyboard_panic(31);
1741 if ((inb(0x60) != 0xaa)) {
1742 keyboard_panic(994);
1745 /* Disable keyboard */
1746 outb(0x60, 0xf5);
1748 /* Wait until buffer is empty */
1749 max=0xffff;
1750 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x40);
1751 if (max==0x0) keyboard_panic(40);
1753 /* Wait for data */
1754 max=0xffff;
1755 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x41);
1756 if (max==0x0) keyboard_panic(41);
1758 /* keyboard should return ACK */
1759 if ((inb(0x60) != 0xfa)) {
1760 keyboard_panic(995);
1763 /* Write Keyboard Mode */
1764 outb(0x64, 0x60);
1766 /* Wait until buffer is empty */
1767 max=0xffff;
1768 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x50);
1769 if (max==0x0) keyboard_panic(50);
1771 /* send cmd: scan code convert, disable mouse, enable IRQ 1 */
1772 outb(0x60, 0x61);
1774 /* Wait until buffer is empty */
1775 max=0xffff;
1776 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x60);
1777 if (max==0x0) keyboard_panic(60);
1779 /* Enable keyboard */
1780 outb(0x60, 0xf4);
1782 /* Wait until buffer is empty */
1783 max=0xffff;
1784 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x70);
1785 if (max==0x0) keyboard_panic(70);
1787 /* Wait for data */
1788 max=0xffff;
1789 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x71);
1790 if (max==0x0) keyboard_panic(70);
1792 /* keyboard should return ACK */
1793 if ((inb(0x60) != 0xfa)) {
1794 keyboard_panic(996);
1797 outb(0x80, 0x77);
1800 //--------------------------------------------------------------------------
1801 // keyboard_panic
1802 //--------------------------------------------------------------------------
1803 void
1804 keyboard_panic(status)
1805 Bit16u status;
1807 // If you're getting a 993 keyboard panic here,
1808 // please see the comment in keyboard_init
1810 BX_PANIC("Keyboard error:%u\n",status);
1813 //--------------------------------------------------------------------------
1814 // shutdown_status_panic
1815 // called when the shutdown statsu is not implemented, displays the status
1816 //--------------------------------------------------------------------------
1817 void
1818 shutdown_status_panic(status)
1819 Bit16u status;
1821 BX_PANIC("Unimplemented shutdown status: %02x\n",(Bit8u)status);
1824 //--------------------------------------------------------------------------
1825 // print_bios_banner
1826 // displays a the bios version
1827 //--------------------------------------------------------------------------
1828 void
1829 print_bios_banner()
1831 printf(BX_APPNAME" BIOS, %d cpu%s, ", BX_SMP_PROCESSORS, BX_SMP_PROCESSORS>1?"s":"");
1832 printf("%s %s\n", bios_cvs_version_string, bios_date_string);
1833 printf("\n");
1836 //--------------------------------------------------------------------------
1837 // print_boot_device
1838 // displays the boot device
1839 //--------------------------------------------------------------------------
1841 static char drivetypes[][10]={"Floppy","Hard Disk","CD-Rom"};
1843 void
1844 print_boot_device(cdboot, drive)
1845 Bit8u cdboot; Bit16u drive;
1847 Bit8u i;
1849 // cdboot contains 0 if floppy/harddisk, 1 otherwise
1850 // drive contains real/emulated boot drive
1852 if(cdboot)i=2; // CD-Rom
1853 else if((drive&0x0080)==0x00)i=0; // Floppy
1854 else if((drive&0x0080)==0x80)i=1; // Hard drive
1855 else return;
1857 printf("Booting from %s...\n",drivetypes[i]);
1860 //--------------------------------------------------------------------------
1861 // print_boot_failure
1862 // displays the reason why boot failed
1863 //--------------------------------------------------------------------------
1864 void
1865 print_boot_failure(cdboot, drive, reason, lastdrive)
1866 Bit8u cdboot; Bit8u drive; Bit8u lastdrive;
1868 Bit16u drivenum = drive&0x7f;
1870 // cdboot: 1 if boot from cd, 0 otherwise
1871 // drive : drive number
1872 // reason: 0 signature check failed, 1 read error
1873 // lastdrive: 1 boot drive is the last one in boot sequence
1875 if (cdboot)
1876 bios_printf(BIOS_PRINTF_INFO | BIOS_PRINTF_SCREEN, "Boot from %s failed\n",drivetypes[2]);
1877 else if (drive & 0x80)
1878 bios_printf(BIOS_PRINTF_INFO | BIOS_PRINTF_SCREEN, "Boot from %s %d failed\n", drivetypes[1],drivenum);
1879 else
1880 bios_printf(BIOS_PRINTF_INFO | BIOS_PRINTF_SCREEN, "Boot from %s %d failed\n", drivetypes[0],drivenum);
1882 if (lastdrive==1) {
1883 if (reason==0)
1884 BX_PANIC("Not a bootable disk\n");
1885 else
1886 BX_PANIC("Could not read the boot disk\n");
1890 //--------------------------------------------------------------------------
1891 // print_cdromboot_failure
1892 // displays the reason why boot failed
1893 //--------------------------------------------------------------------------
1894 void
1895 print_cdromboot_failure( code )
1896 Bit16u code;
1898 bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, "CDROM boot failure code : %04x\n",code);
1900 return;
1903 void
1904 nmi_handler_msg()
1906 BX_PANIC("NMI Handler called\n");
1909 void
1910 int18_panic_msg()
1912 BX_PANIC("INT18: BOOT FAILURE\n");
1915 void
1916 log_bios_start()
1918 #if BX_DEBUG_SERIAL
1919 outb(BX_DEBUG_PORT+UART_LCR, 0x03); /* setup for serial logging: 8N1 */
1920 #endif
1921 BX_INFO("%s\n", bios_version_string);
1924 bx_bool
1925 set_enable_a20(val)
1926 bx_bool val;
1928 Bit8u oldval;
1930 // Use PS2 System Control port A to set A20 enable
1932 // get current setting first
1933 oldval = inb(0x92);
1935 // change A20 status
1936 if (val)
1937 outb(0x92, oldval | 0x02);
1938 else
1939 outb(0x92, oldval & 0xfd);
1941 return((oldval & 0x02) != 0);
1944 void
1945 debugger_on()
1947 outb(0xfedc, 0x01);
1950 void
1951 debugger_off()
1953 outb(0xfedc, 0x00);
1956 #if BX_USE_ATADRV
1958 // ---------------------------------------------------------------------------
1959 // Start of ATA/ATAPI Driver
1960 // ---------------------------------------------------------------------------
1962 // Global defines -- ATA register and register bits.
1963 // command block & control block regs
1964 #define ATA_CB_DATA 0 // data reg in/out pio_base_addr1+0
1965 #define ATA_CB_ERR 1 // error in pio_base_addr1+1
1966 #define ATA_CB_FR 1 // feature reg out pio_base_addr1+1
1967 #define ATA_CB_SC 2 // sector count in/out pio_base_addr1+2
1968 #define ATA_CB_SN 3 // sector number in/out pio_base_addr1+3
1969 #define ATA_CB_CL 4 // cylinder low in/out pio_base_addr1+4
1970 #define ATA_CB_CH 5 // cylinder high in/out pio_base_addr1+5
1971 #define ATA_CB_DH 6 // device head in/out pio_base_addr1+6
1972 #define ATA_CB_STAT 7 // primary status in pio_base_addr1+7
1973 #define ATA_CB_CMD 7 // command out pio_base_addr1+7
1974 #define ATA_CB_ASTAT 6 // alternate status in pio_base_addr2+6
1975 #define ATA_CB_DC 6 // device control out pio_base_addr2+6
1976 #define ATA_CB_DA 7 // device address in pio_base_addr2+7
1978 #define ATA_CB_ER_ICRC 0x80 // ATA Ultra DMA bad CRC
1979 #define ATA_CB_ER_BBK 0x80 // ATA bad block
1980 #define ATA_CB_ER_UNC 0x40 // ATA uncorrected error
1981 #define ATA_CB_ER_MC 0x20 // ATA media change
1982 #define ATA_CB_ER_IDNF 0x10 // ATA id not found
1983 #define ATA_CB_ER_MCR 0x08 // ATA media change request
1984 #define ATA_CB_ER_ABRT 0x04 // ATA command aborted
1985 #define ATA_CB_ER_NTK0 0x02 // ATA track 0 not found
1986 #define ATA_CB_ER_NDAM 0x01 // ATA address mark not found
1988 #define ATA_CB_ER_P_SNSKEY 0xf0 // ATAPI sense key (mask)
1989 #define ATA_CB_ER_P_MCR 0x08 // ATAPI Media Change Request
1990 #define ATA_CB_ER_P_ABRT 0x04 // ATAPI command abort
1991 #define ATA_CB_ER_P_EOM 0x02 // ATAPI End of Media
1992 #define ATA_CB_ER_P_ILI 0x01 // ATAPI Illegal Length Indication
1994 // ATAPI Interrupt Reason bits in the Sector Count reg (CB_SC)
1995 #define ATA_CB_SC_P_TAG 0xf8 // ATAPI tag (mask)
1996 #define ATA_CB_SC_P_REL 0x04 // ATAPI release
1997 #define ATA_CB_SC_P_IO 0x02 // ATAPI I/O
1998 #define ATA_CB_SC_P_CD 0x01 // ATAPI C/D
2000 // bits 7-4 of the device/head (CB_DH) reg
2001 #define ATA_CB_DH_DEV0 0xa0 // select device 0
2002 #define ATA_CB_DH_DEV1 0xb0 // select device 1
2004 // status reg (CB_STAT and CB_ASTAT) bits
2005 #define ATA_CB_STAT_BSY 0x80 // busy
2006 #define ATA_CB_STAT_RDY 0x40 // ready
2007 #define ATA_CB_STAT_DF 0x20 // device fault
2008 #define ATA_CB_STAT_WFT 0x20 // write fault (old name)
2009 #define ATA_CB_STAT_SKC 0x10 // seek complete
2010 #define ATA_CB_STAT_SERV 0x10 // service
2011 #define ATA_CB_STAT_DRQ 0x08 // data request
2012 #define ATA_CB_STAT_CORR 0x04 // corrected
2013 #define ATA_CB_STAT_IDX 0x02 // index
2014 #define ATA_CB_STAT_ERR 0x01 // error (ATA)
2015 #define ATA_CB_STAT_CHK 0x01 // check (ATAPI)
2017 // device control reg (CB_DC) bits
2018 #define ATA_CB_DC_HD15 0x08 // bit should always be set to one
2019 #define ATA_CB_DC_SRST 0x04 // soft reset
2020 #define ATA_CB_DC_NIEN 0x02 // disable interrupts
2022 // Most mandtory and optional ATA commands (from ATA-3),
2023 #define ATA_CMD_CFA_ERASE_SECTORS 0xC0
2024 #define ATA_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03
2025 #define ATA_CMD_CFA_TRANSLATE_SECTOR 0x87
2026 #define ATA_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD
2027 #define ATA_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38
2028 #define ATA_CMD_CHECK_POWER_MODE1 0xE5
2029 #define ATA_CMD_CHECK_POWER_MODE2 0x98
2030 #define ATA_CMD_DEVICE_RESET 0x08
2031 #define ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90
2032 #define ATA_CMD_FLUSH_CACHE 0xE7
2033 #define ATA_CMD_FORMAT_TRACK 0x50
2034 #define ATA_CMD_IDENTIFY_DEVICE 0xEC
2035 #define ATA_CMD_IDENTIFY_DEVICE_PACKET 0xA1
2036 #define ATA_CMD_IDENTIFY_PACKET_DEVICE 0xA1
2037 #define ATA_CMD_IDLE1 0xE3
2038 #define ATA_CMD_IDLE2 0x97
2039 #define ATA_CMD_IDLE_IMMEDIATE1 0xE1
2040 #define ATA_CMD_IDLE_IMMEDIATE2 0x95
2041 #define ATA_CMD_INITIALIZE_DRIVE_PARAMETERS 0x91
2042 #define ATA_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91
2043 #define ATA_CMD_NOP 0x00
2044 #define ATA_CMD_PACKET 0xA0
2045 #define ATA_CMD_READ_BUFFER 0xE4
2046 #define ATA_CMD_READ_DMA 0xC8
2047 #define ATA_CMD_READ_DMA_QUEUED 0xC7
2048 #define ATA_CMD_READ_MULTIPLE 0xC4
2049 #define ATA_CMD_READ_SECTORS 0x20
2050 #define ATA_CMD_READ_VERIFY_SECTORS 0x40
2051 #define ATA_CMD_RECALIBRATE 0x10
2052 #define ATA_CMD_SEEK 0x70
2053 #define ATA_CMD_SET_FEATURES 0xEF
2054 #define ATA_CMD_SET_MULTIPLE_MODE 0xC6
2055 #define ATA_CMD_SLEEP1 0xE6
2056 #define ATA_CMD_SLEEP2 0x99
2057 #define ATA_CMD_STANDBY1 0xE2
2058 #define ATA_CMD_STANDBY2 0x96
2059 #define ATA_CMD_STANDBY_IMMEDIATE1 0xE0
2060 #define ATA_CMD_STANDBY_IMMEDIATE2 0x94
2061 #define ATA_CMD_WRITE_BUFFER 0xE8
2062 #define ATA_CMD_WRITE_DMA 0xCA
2063 #define ATA_CMD_WRITE_DMA_QUEUED 0xCC
2064 #define ATA_CMD_WRITE_MULTIPLE 0xC5
2065 #define ATA_CMD_WRITE_SECTORS 0x30
2066 #define ATA_CMD_WRITE_VERIFY 0x3C
2068 #define ATA_IFACE_NONE 0x00
2069 #define ATA_IFACE_ISA 0x00
2070 #define ATA_IFACE_PCI 0x01
2072 #define ATA_TYPE_NONE 0x00
2073 #define ATA_TYPE_UNKNOWN 0x01
2074 #define ATA_TYPE_ATA 0x02
2075 #define ATA_TYPE_ATAPI 0x03
2077 #define ATA_DEVICE_NONE 0x00
2078 #define ATA_DEVICE_HD 0xFF
2079 #define ATA_DEVICE_CDROM 0x05
2081 #define ATA_MODE_NONE 0x00
2082 #define ATA_MODE_PIO16 0x00
2083 #define ATA_MODE_PIO32 0x01
2084 #define ATA_MODE_ISADMA 0x02
2085 #define ATA_MODE_PCIDMA 0x03
2086 #define ATA_MODE_USEIRQ 0x10
2088 #define ATA_TRANSLATION_NONE 0
2089 #define ATA_TRANSLATION_LBA 1
2090 #define ATA_TRANSLATION_LARGE 2
2091 #define ATA_TRANSLATION_RECHS 3
2093 #define ATA_DATA_NO 0x00
2094 #define ATA_DATA_IN 0x01
2095 #define ATA_DATA_OUT 0x02
2097 // ---------------------------------------------------------------------------
2098 // ATA/ATAPI driver : initialization
2099 // ---------------------------------------------------------------------------
2100 void ata_init( )
2102 Bit16u ebda_seg=read_word(0x0040,0x000E);
2103 Bit8u channel, device;
2105 // Channels info init.
2106 for (channel=0; channel<BX_MAX_ATA_INTERFACES; channel++) {
2107 write_byte(ebda_seg,&EbdaData->ata.channels[channel].iface,ATA_IFACE_NONE);
2108 write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1,0x0);
2109 write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2,0x0);
2110 write_byte(ebda_seg,&EbdaData->ata.channels[channel].irq,0);
2113 // Devices info init.
2114 for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2115 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
2116 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_NONE);
2117 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable,0);
2118 write_byte(ebda_seg,&EbdaData->ata.devices[device].lock,0);
2119 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode,ATA_MODE_NONE);
2120 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize,0);
2121 write_byte(ebda_seg,&EbdaData->ata.devices[device].translation,ATA_TRANSLATION_NONE);
2122 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads,0);
2123 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders,0);
2124 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt,0);
2125 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads,0);
2126 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders,0);
2127 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt,0);
2129 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors,0L);
2132 // hdidmap and cdidmap init.
2133 for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2134 write_byte(ebda_seg,&EbdaData->ata.hdidmap[device],BX_MAX_ATA_DEVICES);
2135 write_byte(ebda_seg,&EbdaData->ata.cdidmap[device],BX_MAX_ATA_DEVICES);
2138 write_byte(ebda_seg,&EbdaData->ata.hdcount,0);
2139 write_byte(ebda_seg,&EbdaData->ata.cdcount,0);
2142 // ---------------------------------------------------------------------------
2143 // ATA/ATAPI driver : device detection
2144 // ---------------------------------------------------------------------------
2146 void ata_detect( )
2148 Bit16u ebda_seg=read_word(0x0040,0x000E);
2149 Bit8u hdcount, cdcount, device, type;
2150 Bit8u buffer[0x0200];
2152 #if BX_MAX_ATA_INTERFACES > 0
2153 write_byte(ebda_seg,&EbdaData->ata.channels[0].iface,ATA_IFACE_ISA);
2154 write_word(ebda_seg,&EbdaData->ata.channels[0].iobase1,0x1f0);
2155 write_word(ebda_seg,&EbdaData->ata.channels[0].iobase2,0x3f0);
2156 write_byte(ebda_seg,&EbdaData->ata.channels[0].irq,14);
2157 #endif
2158 #if BX_MAX_ATA_INTERFACES > 1
2159 write_byte(ebda_seg,&EbdaData->ata.channels[1].iface,ATA_IFACE_ISA);
2160 write_word(ebda_seg,&EbdaData->ata.channels[1].iobase1,0x170);
2161 write_word(ebda_seg,&EbdaData->ata.channels[1].iobase2,0x370);
2162 write_byte(ebda_seg,&EbdaData->ata.channels[1].irq,15);
2163 #endif
2164 #if BX_MAX_ATA_INTERFACES > 2
2165 write_byte(ebda_seg,&EbdaData->ata.channels[2].iface,ATA_IFACE_ISA);
2166 write_word(ebda_seg,&EbdaData->ata.channels[2].iobase1,0x1e8);
2167 write_word(ebda_seg,&EbdaData->ata.channels[2].iobase2,0x3e0);
2168 write_byte(ebda_seg,&EbdaData->ata.channels[2].irq,12);
2169 #endif
2170 #if BX_MAX_ATA_INTERFACES > 3
2171 write_byte(ebda_seg,&EbdaData->ata.channels[3].iface,ATA_IFACE_ISA);
2172 write_word(ebda_seg,&EbdaData->ata.channels[3].iobase1,0x168);
2173 write_word(ebda_seg,&EbdaData->ata.channels[3].iobase2,0x360);
2174 write_byte(ebda_seg,&EbdaData->ata.channels[3].irq,11);
2175 #endif
2176 #if BX_MAX_ATA_INTERFACES > 4
2177 #error Please fill the ATA interface informations
2178 #endif
2180 // Device detection
2181 hdcount=cdcount=0;
2183 for(device=0; device<BX_MAX_ATA_DEVICES; device++) {
2184 Bit16u iobase1, iobase2;
2185 Bit8u channel, slave, shift;
2186 Bit8u sc, sn, cl, ch, st;
2188 channel = device / 2;
2189 slave = device % 2;
2191 iobase1 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1);
2192 iobase2 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2);
2194 // Disable interrupts
2195 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2197 // Look for device
2198 outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2199 outb(iobase1+ATA_CB_SC, 0x55);
2200 outb(iobase1+ATA_CB_SN, 0xaa);
2201 outb(iobase1+ATA_CB_SC, 0xaa);
2202 outb(iobase1+ATA_CB_SN, 0x55);
2203 outb(iobase1+ATA_CB_SC, 0x55);
2204 outb(iobase1+ATA_CB_SN, 0xaa);
2206 // If we found something
2207 sc = inb(iobase1+ATA_CB_SC);
2208 sn = inb(iobase1+ATA_CB_SN);
2210 if ( (sc == 0x55) && (sn == 0xaa) ) {
2211 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_UNKNOWN);
2213 // reset the channel
2214 ata_reset (device);
2216 // check for ATA or ATAPI
2217 outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2218 sc = inb(iobase1+ATA_CB_SC);
2219 sn = inb(iobase1+ATA_CB_SN);
2220 if ( (sc==0x01) && (sn==0x01) ) {
2221 cl = inb(iobase1+ATA_CB_CL);
2222 ch = inb(iobase1+ATA_CB_CH);
2223 st = inb(iobase1+ATA_CB_STAT);
2225 if ( (cl==0x14) && (ch==0xeb) ) {
2226 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATAPI);
2228 else if ( (cl==0x00) && (ch==0x00) && (st!=0x00) ) {
2229 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATA);
2234 type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type);
2236 // Now we send a IDENTIFY command to ATA device
2237 if(type == ATA_TYPE_ATA) {
2238 Bit32u sectors;
2239 Bit16u cylinders, heads, spt, blksize;
2240 Bit8u translation, removable, mode;
2242 //Temporary values to do the transfer
2243 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2244 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2246 if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE, 1, 0, 0, 0, 0L, get_SS(),buffer) !=0 )
2247 BX_PANIC("ata-detect: Failed to detect ATA device\n");
2249 removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2250 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2251 blksize = read_word(get_SS(),buffer+10);
2253 cylinders = read_word(get_SS(),buffer+(1*2)); // word 1
2254 heads = read_word(get_SS(),buffer+(3*2)); // word 3
2255 spt = read_word(get_SS(),buffer+(6*2)); // word 6
2257 sectors = read_dword(get_SS(),buffer+(60*2)); // word 60 and word 61
2259 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2260 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2261 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2262 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2263 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads, heads);
2264 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders, cylinders);
2265 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt, spt);
2266 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors, sectors);
2267 BX_INFO("ata%d-%d: PCHS=%u/%d/%d translation=", channel, slave,cylinders, heads, spt);
2269 translation = inb_cmos(0x39 + channel/2);
2270 for (shift=device%4; shift>0; shift--) translation >>= 2;
2271 translation &= 0x03;
2273 write_byte(ebda_seg,&EbdaData->ata.devices[device].translation, translation);
2275 switch (translation) {
2276 case ATA_TRANSLATION_NONE:
2277 BX_INFO("none");
2278 break;
2279 case ATA_TRANSLATION_LBA:
2280 BX_INFO("lba");
2281 break;
2282 case ATA_TRANSLATION_LARGE:
2283 BX_INFO("large");
2284 break;
2285 case ATA_TRANSLATION_RECHS:
2286 BX_INFO("r-echs");
2287 break;
2289 switch (translation) {
2290 case ATA_TRANSLATION_NONE:
2291 break;
2292 case ATA_TRANSLATION_LBA:
2293 spt = 63;
2294 sectors /= 63;
2295 heads = sectors / 1024;
2296 if (heads>128) heads = 255;
2297 else if (heads>64) heads = 128;
2298 else if (heads>32) heads = 64;
2299 else if (heads>16) heads = 32;
2300 else heads=16;
2301 cylinders = sectors / heads;
2302 break;
2303 case ATA_TRANSLATION_RECHS:
2304 // Take care not to overflow
2305 if (heads==16) {
2306 if(cylinders>61439) cylinders=61439;
2307 heads=15;
2308 cylinders = (Bit16u)((Bit32u)(cylinders)*16/15);
2310 // then go through the large bitshift process
2311 case ATA_TRANSLATION_LARGE:
2312 while(cylinders > 1024) {
2313 cylinders >>= 1;
2314 heads <<= 1;
2316 // If we max out the head count
2317 if (heads > 127) break;
2319 break;
2321 // clip to 1024 cylinders in lchs
2322 if (cylinders > 1024) cylinders=1024;
2323 BX_INFO(" LCHS=%d/%d/%d\n", cylinders, heads, spt);
2325 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads, heads);
2326 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders, cylinders);
2327 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt, spt);
2329 // fill hdidmap
2330 write_byte(ebda_seg,&EbdaData->ata.hdidmap[hdcount], device);
2331 hdcount++;
2334 // Now we send a IDENTIFY command to ATAPI device
2335 if(type == ATA_TYPE_ATAPI) {
2337 Bit8u type, removable, mode;
2338 Bit16u blksize;
2340 //Temporary values to do the transfer
2341 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_CDROM);
2342 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2344 if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE_PACKET, 1, 0, 0, 0, 0L, get_SS(),buffer) != 0)
2345 BX_PANIC("ata-detect: Failed to detect ATAPI device\n");
2347 type = read_byte(get_SS(),buffer+1) & 0x1f;
2348 removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2349 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2350 blksize = 2048;
2352 write_byte(ebda_seg,&EbdaData->ata.devices[device].device, type);
2353 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2354 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2355 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2357 // fill cdidmap
2358 write_byte(ebda_seg,&EbdaData->ata.cdidmap[cdcount], device);
2359 cdcount++;
2363 Bit32u sizeinmb;
2364 Bit16u ataversion;
2365 Bit8u c, i, version, model[41];
2367 switch (type) {
2368 case ATA_TYPE_ATA:
2369 sizeinmb = read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors);
2370 sizeinmb >>= 11;
2371 case ATA_TYPE_ATAPI:
2372 // Read ATA/ATAPI version
2373 ataversion=((Bit16u)(read_byte(get_SS(),buffer+161))<<8)|read_byte(get_SS(),buffer+160);
2374 for(version=15;version>0;version--) {
2375 if((ataversion&(1<<version))!=0)
2376 break;
2379 // Read model name
2380 for(i=0;i<20;i++){
2381 write_byte(get_SS(),model+(i*2),read_byte(get_SS(),buffer+(i*2)+54+1));
2382 write_byte(get_SS(),model+(i*2)+1,read_byte(get_SS(),buffer+(i*2)+54));
2385 // Reformat
2386 write_byte(get_SS(),model+40,0x00);
2387 for(i=39;i>0;i--){
2388 if(read_byte(get_SS(),model+i)==0x20)
2389 write_byte(get_SS(),model+i,0x00);
2390 else break;
2392 break;
2395 switch (type) {
2396 case ATA_TYPE_ATA:
2397 printf("ata%d %s: ",channel,slave?" slave":"master");
2398 i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
2399 printf(" ATA-%d Hard-Disk (%d MBytes)\n",version,(Bit16u)sizeinmb);
2400 break;
2401 case ATA_TYPE_ATAPI:
2402 printf("ata%d %s: ",channel,slave?" slave":"master");
2403 i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
2404 if(read_byte(ebda_seg,&EbdaData->ata.devices[device].device)==ATA_DEVICE_CDROM)
2405 printf(" ATAPI-%d CD-Rom/DVD-Rom\n",version);
2406 else
2407 printf(" ATAPI-%d Device\n",version);
2408 break;
2409 case ATA_TYPE_UNKNOWN:
2410 printf("ata%d %s: Unknown device\n",channel,slave?" slave":"master");
2411 break;
2416 // Store the devices counts
2417 write_byte(ebda_seg,&EbdaData->ata.hdcount, hdcount);
2418 write_byte(ebda_seg,&EbdaData->ata.cdcount, cdcount);
2419 write_byte(0x40,0x75, hdcount);
2421 printf("\n");
2423 // FIXME : should use bios=cmos|auto|disable bits
2424 // FIXME : should know about translation bits
2425 // FIXME : move hard_drive_post here
2429 // ---------------------------------------------------------------------------
2430 // ATA/ATAPI driver : software reset
2431 // ---------------------------------------------------------------------------
2432 // ATA-3
2433 // 8.2.1 Software reset - Device 0
2435 void ata_reset(device)
2436 Bit16u device;
2438 Bit16u ebda_seg=read_word(0x0040,0x000E);
2439 Bit16u iobase1, iobase2;
2440 Bit8u channel, slave, sn, sc;
2441 Bit16u max;
2443 channel = device / 2;
2444 slave = device % 2;
2446 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2447 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2449 // Reset
2451 // 8.2.1 (a) -- set SRST in DC
2452 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST);
2454 // 8.2.1 (b) -- wait for BSY
2455 max=0xff;
2456 while(--max>0) {
2457 Bit8u status = inb(iobase1+ATA_CB_STAT);
2458 if ((status & ATA_CB_STAT_BSY) != 0) break;
2461 // 8.2.1 (f) -- clear SRST
2462 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2464 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_NONE) {
2466 // 8.2.1 (g) -- check for sc==sn==0x01
2467 // select device
2468 outb(iobase1+ATA_CB_DH, slave?ATA_CB_DH_DEV1:ATA_CB_DH_DEV0);
2469 sc = inb(iobase1+ATA_CB_SC);
2470 sn = inb(iobase1+ATA_CB_SN);
2472 if ( (sc==0x01) && (sn==0x01) ) {
2474 // 8.2.1 (h) -- wait for not BSY
2475 max=0xff;
2476 while(--max>0) {
2477 Bit8u status = inb(iobase1+ATA_CB_STAT);
2478 if ((status & ATA_CB_STAT_BSY) == 0) break;
2483 // 8.2.1 (i) -- wait for DRDY
2484 max=0xfff;
2485 while(--max>0) {
2486 Bit8u status = inb(iobase1+ATA_CB_STAT);
2487 if ((status & ATA_CB_STAT_RDY) != 0) break;
2490 // Enable interrupts
2491 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2494 // ---------------------------------------------------------------------------
2495 // ATA/ATAPI driver : execute a non data command
2496 // ---------------------------------------------------------------------------
2498 Bit16u ata_cmd_non_data()
2499 {return 0;}
2501 // ---------------------------------------------------------------------------
2502 // ATA/ATAPI driver : execute a data-in command
2503 // ---------------------------------------------------------------------------
2504 // returns
2505 // 0 : no error
2506 // 1 : BUSY bit set
2507 // 2 : read error
2508 // 3 : expected DRQ=1
2509 // 4 : no sectors left to read/verify
2510 // 5 : more sectors to read/verify
2511 // 6 : no sectors left to write
2512 // 7 : more sectors to write
2513 Bit16u ata_cmd_data_in(device, command, count, cylinder, head, sector, lba, segment, offset)
2514 Bit16u device, command, count, cylinder, head, sector, segment, offset;
2515 Bit32u lba;
2517 Bit16u ebda_seg=read_word(0x0040,0x000E);
2518 Bit16u iobase1, iobase2, blksize;
2519 Bit8u channel, slave;
2520 Bit8u status, current, mode;
2522 channel = device / 2;
2523 slave = device % 2;
2525 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2526 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2527 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2528 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
2529 if (mode == ATA_MODE_PIO32) blksize>>=2;
2530 else blksize>>=1;
2532 // sector will be 0 only on lba access. Convert to lba-chs
2533 if (sector == 0) {
2534 sector = (Bit16u) (lba & 0x000000ffL);
2535 lba >>= 8;
2536 cylinder = (Bit16u) (lba & 0x0000ffffL);
2537 lba >>= 16;
2538 head = ((Bit16u) (lba & 0x0000000fL)) | 0x40;
2541 // Reset count of transferred data
2542 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2543 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2544 current = 0;
2546 status = inb(iobase1 + ATA_CB_STAT);
2547 if (status & ATA_CB_STAT_BSY) return 1;
2549 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2550 outb(iobase1 + ATA_CB_FR, 0x00);
2551 outb(iobase1 + ATA_CB_SC, count);
2552 outb(iobase1 + ATA_CB_SN, sector);
2553 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
2554 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
2555 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
2556 outb(iobase1 + ATA_CB_CMD, command);
2558 while (1) {
2559 status = inb(iobase1 + ATA_CB_STAT);
2560 if ( !(status & ATA_CB_STAT_BSY) ) break;
2563 if (status & ATA_CB_STAT_ERR) {
2564 BX_DEBUG_ATA("ata_cmd_data_in : read error\n");
2565 return 2;
2566 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2567 BX_DEBUG_ATA("ata_cmd_data_in : DRQ not set (status %02x)\n", (unsigned) status);
2568 return 3;
2571 // FIXME : move seg/off translation here
2573 ASM_START
2574 sti ;; enable higher priority interrupts
2575 ASM_END
2577 while (1) {
2579 ASM_START
2580 push bp
2581 mov bp, sp
2582 mov di, _ata_cmd_data_in.offset + 2[bp]
2583 mov ax, _ata_cmd_data_in.segment + 2[bp]
2584 mov cx, _ata_cmd_data_in.blksize + 2[bp]
2586 ;; adjust if there will be an overrun. 2K max sector size
2587 cmp di, #0xf800 ;;
2588 jbe ata_in_no_adjust
2590 ata_in_adjust:
2591 sub di, #0x0800 ;; sub 2 kbytes from offset
2592 add ax, #0x0080 ;; add 2 Kbytes to segment
2594 ata_in_no_adjust:
2595 mov es, ax ;; segment in es
2597 mov dx, _ata_cmd_data_in.iobase1 + 2[bp] ;; ATA data read port
2599 mov ah, _ata_cmd_data_in.mode + 2[bp]
2600 cmp ah, #ATA_MODE_PIO32
2601 je ata_in_32
2603 ata_in_16:
2605 insw ;; CX words transfered from port(DX) to ES:[DI]
2606 jmp ata_in_done
2608 ata_in_32:
2610 insd ;; CX dwords transfered from port(DX) to ES:[DI]
2612 ata_in_done:
2613 mov _ata_cmd_data_in.offset + 2[bp], di
2614 mov _ata_cmd_data_in.segment + 2[bp], es
2615 pop bp
2616 ASM_END
2618 current++;
2619 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
2620 count--;
2621 status = inb(iobase1 + ATA_CB_STAT);
2622 if (count == 0) {
2623 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2624 != ATA_CB_STAT_RDY ) {
2625 BX_DEBUG_ATA("ata_cmd_data_in : no sectors left (status %02x)\n", (unsigned) status);
2626 return 4;
2628 break;
2630 else {
2631 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2632 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2633 BX_DEBUG_ATA("ata_cmd_data_in : more sectors left (status %02x)\n", (unsigned) status);
2634 return 5;
2636 continue;
2639 // Enable interrupts
2640 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2641 return 0;
2644 // ---------------------------------------------------------------------------
2645 // ATA/ATAPI driver : execute a data-out command
2646 // ---------------------------------------------------------------------------
2647 // returns
2648 // 0 : no error
2649 // 1 : BUSY bit set
2650 // 2 : read error
2651 // 3 : expected DRQ=1
2652 // 4 : no sectors left to read/verify
2653 // 5 : more sectors to read/verify
2654 // 6 : no sectors left to write
2655 // 7 : more sectors to write
2656 Bit16u ata_cmd_data_out(device, command, count, cylinder, head, sector, lba, segment, offset)
2657 Bit16u device, command, count, cylinder, head, sector, segment, offset;
2658 Bit32u lba;
2660 Bit16u ebda_seg=read_word(0x0040,0x000E);
2661 Bit16u iobase1, iobase2, blksize;
2662 Bit8u channel, slave;
2663 Bit8u status, current, mode;
2665 channel = device / 2;
2666 slave = device % 2;
2668 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2669 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2670 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2671 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
2672 if (mode == ATA_MODE_PIO32) blksize>>=2;
2673 else blksize>>=1;
2675 // sector will be 0 only on lba access. Convert to lba-chs
2676 if (sector == 0) {
2677 sector = (Bit16u) (lba & 0x000000ffL);
2678 lba >>= 8;
2679 cylinder = (Bit16u) (lba & 0x0000ffffL);
2680 lba >>= 16;
2681 head = ((Bit16u) (lba & 0x0000000fL)) | 0x40;
2684 // Reset count of transferred data
2685 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2686 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2687 current = 0;
2689 status = inb(iobase1 + ATA_CB_STAT);
2690 if (status & ATA_CB_STAT_BSY) return 1;
2692 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2693 outb(iobase1 + ATA_CB_FR, 0x00);
2694 outb(iobase1 + ATA_CB_SC, count);
2695 outb(iobase1 + ATA_CB_SN, sector);
2696 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
2697 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
2698 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
2699 outb(iobase1 + ATA_CB_CMD, command);
2701 while (1) {
2702 status = inb(iobase1 + ATA_CB_STAT);
2703 if ( !(status & ATA_CB_STAT_BSY) ) break;
2706 if (status & ATA_CB_STAT_ERR) {
2707 BX_DEBUG_ATA("ata_cmd_data_out : read error\n");
2708 return 2;
2709 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2710 BX_DEBUG_ATA("ata_cmd_data_out : DRQ not set (status %02x)\n", (unsigned) status);
2711 return 3;
2714 // FIXME : move seg/off translation here
2716 ASM_START
2717 sti ;; enable higher priority interrupts
2718 ASM_END
2720 while (1) {
2722 ASM_START
2723 push bp
2724 mov bp, sp
2725 mov si, _ata_cmd_data_out.offset + 2[bp]
2726 mov ax, _ata_cmd_data_out.segment + 2[bp]
2727 mov cx, _ata_cmd_data_out.blksize + 2[bp]
2729 ;; adjust if there will be an overrun. 2K max sector size
2730 cmp si, #0xf800 ;;
2731 jbe ata_out_no_adjust
2733 ata_out_adjust:
2734 sub si, #0x0800 ;; sub 2 kbytes from offset
2735 add ax, #0x0080 ;; add 2 Kbytes to segment
2737 ata_out_no_adjust:
2738 mov es, ax ;; segment in es
2740 mov dx, _ata_cmd_data_out.iobase1 + 2[bp] ;; ATA data write port
2742 mov ah, _ata_cmd_data_out.mode + 2[bp]
2743 cmp ah, #ATA_MODE_PIO32
2744 je ata_out_32
2746 ata_out_16:
2747 seg ES
2749 outsw ;; CX words transfered from port(DX) to ES:[SI]
2750 jmp ata_out_done
2752 ata_out_32:
2753 seg ES
2755 outsd ;; CX dwords transfered from port(DX) to ES:[SI]
2757 ata_out_done:
2758 mov _ata_cmd_data_out.offset + 2[bp], si
2759 mov _ata_cmd_data_out.segment + 2[bp], es
2760 pop bp
2761 ASM_END
2763 current++;
2764 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
2765 count--;
2766 status = inb(iobase1 + ATA_CB_STAT);
2767 if (count == 0) {
2768 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2769 != ATA_CB_STAT_RDY ) {
2770 BX_DEBUG_ATA("ata_cmd_data_out : no sectors left (status %02x)\n", (unsigned) status);
2771 return 6;
2773 break;
2775 else {
2776 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2777 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2778 BX_DEBUG_ATA("ata_cmd_data_out : more sectors left (status %02x)\n", (unsigned) status);
2779 return 7;
2781 continue;
2784 // Enable interrupts
2785 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2786 return 0;
2789 // ---------------------------------------------------------------------------
2790 // ATA/ATAPI driver : execute a packet command
2791 // ---------------------------------------------------------------------------
2792 // returns
2793 // 0 : no error
2794 // 1 : error in parameters
2795 // 2 : BUSY bit set
2796 // 3 : error
2797 // 4 : not ready
2798 Bit16u ata_cmd_packet(device, cmdlen, cmdseg, cmdoff, header, length, inout, bufseg, bufoff)
2799 Bit8u cmdlen,inout;
2800 Bit16u device,cmdseg, cmdoff, bufseg, bufoff;
2801 Bit16u header;
2802 Bit32u length;
2804 Bit16u ebda_seg=read_word(0x0040,0x000E);
2805 Bit16u iobase1, iobase2;
2806 Bit16u lcount, lbefore, lafter, count;
2807 Bit8u channel, slave;
2808 Bit8u status, mode, lmode;
2809 Bit32u total, transfer;
2811 channel = device / 2;
2812 slave = device % 2;
2814 // Data out is not supported yet
2815 if (inout == ATA_DATA_OUT) {
2816 BX_INFO("ata_cmd_packet: DATA_OUT not supported yet\n");
2817 return 1;
2820 // The header length must be even
2821 if (header & 1) {
2822 BX_DEBUG_ATA("ata_cmd_packet : header must be even (%04x)\n",header);
2823 return 1;
2826 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2827 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2828 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2829 transfer= 0L;
2831 if (cmdlen < 12) cmdlen=12;
2832 if (cmdlen > 12) cmdlen=16;
2833 cmdlen>>=1;
2835 // Reset count of transferred data
2836 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2837 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2839 status = inb(iobase1 + ATA_CB_STAT);
2840 if (status & ATA_CB_STAT_BSY) return 2;
2842 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2843 // outb(iobase1 + ATA_CB_FR, 0x00);
2844 // outb(iobase1 + ATA_CB_SC, 0x00);
2845 // outb(iobase1 + ATA_CB_SN, 0x00);
2846 outb(iobase1 + ATA_CB_CL, 0xfff0 & 0x00ff);
2847 outb(iobase1 + ATA_CB_CH, 0xfff0 >> 8);
2848 outb(iobase1 + ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2849 outb(iobase1 + ATA_CB_CMD, ATA_CMD_PACKET);
2851 // Device should ok to receive command
2852 while (1) {
2853 status = inb(iobase1 + ATA_CB_STAT);
2854 if ( !(status & ATA_CB_STAT_BSY) ) break;
2857 if (status & ATA_CB_STAT_ERR) {
2858 BX_DEBUG_ATA("ata_cmd_packet : error, status is %02x\n",status);
2859 return 3;
2860 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2861 BX_DEBUG_ATA("ata_cmd_packet : DRQ not set (status %02x)\n", (unsigned) status);
2862 return 4;
2865 // Normalize address
2866 cmdseg += (cmdoff / 16);
2867 cmdoff %= 16;
2869 // Send command to device
2870 ASM_START
2871 sti ;; enable higher priority interrupts
2873 push bp
2874 mov bp, sp
2876 mov si, _ata_cmd_packet.cmdoff + 2[bp]
2877 mov ax, _ata_cmd_packet.cmdseg + 2[bp]
2878 mov cx, _ata_cmd_packet.cmdlen + 2[bp]
2879 mov es, ax ;; segment in es
2881 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data write port
2883 seg ES
2885 outsw ;; CX words transfered from port(DX) to ES:[SI]
2887 pop bp
2888 ASM_END
2890 if (inout == ATA_DATA_NO) {
2891 status = inb(iobase1 + ATA_CB_STAT);
2893 else {
2894 while (1) {
2896 status = inb(iobase1 + ATA_CB_STAT);
2898 // Check if command completed
2899 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ) ) ==0 ) break;
2901 if (status & ATA_CB_STAT_ERR) {
2902 BX_DEBUG_ATA("ata_cmd_packet : error (status %02x)\n",status);
2903 return 3;
2906 // Device must be ready to send data
2907 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2908 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2909 BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", status);
2910 return 4;
2913 // Normalize address
2914 bufseg += (bufoff / 16);
2915 bufoff %= 16;
2917 // Get the byte count
2918 lcount = ((Bit16u)(inb(iobase1 + ATA_CB_CH))<<8)+inb(iobase1 + ATA_CB_CL);
2920 // adjust to read what we want
2921 if(header>lcount) {
2922 lbefore=lcount;
2923 header-=lcount;
2924 lcount=0;
2926 else {
2927 lbefore=header;
2928 header=0;
2929 lcount-=lbefore;
2932 if(lcount>length) {
2933 lafter=lcount-length;
2934 lcount=length;
2935 length=0;
2937 else {
2938 lafter=0;
2939 length-=lcount;
2942 // Save byte count
2943 count = lcount;
2945 BX_DEBUG_ATA("Trying to read %04x bytes (%04x %04x %04x) ",lbefore+lcount+lafter,lbefore,lcount,lafter);
2946 BX_DEBUG_ATA("to 0x%04x:0x%04x\n",bufseg,bufoff);
2948 // If counts not dividable by 4, use 16bits mode
2949 lmode = mode;
2950 if (lbefore & 0x03) lmode=ATA_MODE_PIO16;
2951 if (lcount & 0x03) lmode=ATA_MODE_PIO16;
2952 if (lafter & 0x03) lmode=ATA_MODE_PIO16;
2954 // adds an extra byte if count are odd. before is always even
2955 if (lcount & 0x01) {
2956 lcount+=1;
2957 if ((lafter > 0) && (lafter & 0x01)) {
2958 lafter-=1;
2962 if (lmode == ATA_MODE_PIO32) {
2963 lcount>>=2; lbefore>>=2; lafter>>=2;
2965 else {
2966 lcount>>=1; lbefore>>=1; lafter>>=1;
2969 ; // FIXME bcc bug
2971 ASM_START
2972 push bp
2973 mov bp, sp
2975 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data read port
2977 mov cx, _ata_cmd_packet.lbefore + 2[bp]
2978 jcxz ata_packet_no_before
2980 mov ah, _ata_cmd_packet.lmode + 2[bp]
2981 cmp ah, #ATA_MODE_PIO32
2982 je ata_packet_in_before_32
2984 ata_packet_in_before_16:
2985 in ax, dx
2986 loop ata_packet_in_before_16
2987 jmp ata_packet_no_before
2989 ata_packet_in_before_32:
2990 push eax
2991 ata_packet_in_before_32_loop:
2992 in eax, dx
2993 loop ata_packet_in_before_32_loop
2994 pop eax
2996 ata_packet_no_before:
2997 mov cx, _ata_cmd_packet.lcount + 2[bp]
2998 jcxz ata_packet_after
3000 mov di, _ata_cmd_packet.bufoff + 2[bp]
3001 mov ax, _ata_cmd_packet.bufseg + 2[bp]
3002 mov es, ax
3004 mov ah, _ata_cmd_packet.lmode + 2[bp]
3005 cmp ah, #ATA_MODE_PIO32
3006 je ata_packet_in_32
3008 ata_packet_in_16:
3010 insw ;; CX words transfered tp port(DX) to ES:[DI]
3011 jmp ata_packet_after
3013 ata_packet_in_32:
3015 insd ;; CX dwords transfered to port(DX) to ES:[DI]
3017 ata_packet_after:
3018 mov cx, _ata_cmd_packet.lafter + 2[bp]
3019 jcxz ata_packet_done
3021 mov ah, _ata_cmd_packet.lmode + 2[bp]
3022 cmp ah, #ATA_MODE_PIO32
3023 je ata_packet_in_after_32
3025 ata_packet_in_after_16:
3026 in ax, dx
3027 loop ata_packet_in_after_16
3028 jmp ata_packet_done
3030 ata_packet_in_after_32:
3031 push eax
3032 ata_packet_in_after_32_loop:
3033 in eax, dx
3034 loop ata_packet_in_after_32_loop
3035 pop eax
3037 ata_packet_done:
3038 pop bp
3039 ASM_END
3041 // Compute new buffer address
3042 bufoff += count;
3044 // Save transferred bytes count
3045 transfer += count;
3046 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,transfer);
3050 // Final check, device must be ready
3051 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
3052 != ATA_CB_STAT_RDY ) {
3053 BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", (unsigned) status);
3054 return 4;
3057 // Enable interrupts
3058 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
3059 return 0;
3062 // ---------------------------------------------------------------------------
3063 // End of ATA/ATAPI Driver
3064 // ---------------------------------------------------------------------------
3066 // ---------------------------------------------------------------------------
3067 // Start of ATA/ATAPI generic functions
3068 // ---------------------------------------------------------------------------
3070 Bit16u
3071 atapi_get_sense(device)
3072 Bit16u device;
3074 Bit8u atacmd[12];
3075 Bit8u buffer[16];
3076 Bit8u i;
3078 memsetb(get_SS(),atacmd,0,12);
3080 // Request SENSE
3081 atacmd[0]=0x03;
3082 atacmd[4]=0x20;
3083 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 16L, ATA_DATA_IN, get_SS(), buffer) != 0)
3084 return 0x0002;
3086 if ((buffer[0] & 0x7e) == 0x70) {
3087 return (((Bit16u)buffer[2]&0x0f)*0x100)+buffer[12];
3090 return 0;
3093 Bit16u
3094 atapi_is_ready(device)
3095 Bit16u device;
3097 Bit8u atacmd[12];
3098 Bit8u buffer[];
3100 memsetb(get_SS(),atacmd,0,12);
3102 // Test Unit Ready
3103 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0)
3104 return 0x000f;
3106 if (atapi_get_sense(device) !=0 ) {
3107 memsetb(get_SS(),atacmd,0,12);
3109 // try to send Test Unit Ready again
3110 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0)
3111 return 0x000f;
3113 return atapi_get_sense(device);
3115 return 0;
3118 Bit16u
3119 atapi_is_cdrom(device)
3120 Bit8u device;
3122 Bit16u ebda_seg=read_word(0x0040,0x000E);
3124 if (device >= BX_MAX_ATA_DEVICES)
3125 return 0;
3127 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI)
3128 return 0;
3130 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].device) != ATA_DEVICE_CDROM)
3131 return 0;
3133 return 1;
3136 // ---------------------------------------------------------------------------
3137 // End of ATA/ATAPI generic functions
3138 // ---------------------------------------------------------------------------
3140 #endif // BX_USE_ATADRV
3142 #if BX_ELTORITO_BOOT
3144 // ---------------------------------------------------------------------------
3145 // Start of El-Torito boot functions
3146 // ---------------------------------------------------------------------------
3148 void
3149 cdemu_init()
3151 Bit16u ebda_seg=read_word(0x0040,0x000E);
3153 // the only important data is this one for now
3154 write_byte(ebda_seg,&EbdaData->cdemu.active,0x00);
3157 Bit8u
3158 cdemu_isactive()
3160 Bit16u ebda_seg=read_word(0x0040,0x000E);
3162 return(read_byte(ebda_seg,&EbdaData->cdemu.active));
3165 Bit8u
3166 cdemu_emulated_drive()
3168 Bit16u ebda_seg=read_word(0x0040,0x000E);
3170 return(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
3173 static char isotag[6]="CD001";
3174 static char eltorito[24]="EL TORITO SPECIFICATION";
3176 // Returns ah: emulated drive, al: error code
3178 Bit16u
3179 cdrom_boot()
3181 Bit16u ebda_seg=read_word(0x0040,0x000E);
3182 Bit8u atacmd[12], buffer[2048];
3183 Bit32u lba;
3184 Bit16u boot_segment, nbsectors, i, error;
3185 Bit8u device;
3187 // Find out the first cdrom
3188 for (device=0; device<BX_MAX_ATA_DEVICES;device++) {
3189 if (atapi_is_cdrom(device)) break;
3192 // if not found
3193 if(device >= BX_MAX_ATA_DEVICES) return 2;
3195 // Read the Boot Record Volume Descriptor
3196 memsetb(get_SS(),atacmd,0,12);
3197 atacmd[0]=0x28; // READ command
3198 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3199 atacmd[8]=(0x01 & 0x00ff); // Sectors
3200 atacmd[2]=(0x11 & 0xff000000) >> 24; // LBA
3201 atacmd[3]=(0x11 & 0x00ff0000) >> 16;
3202 atacmd[4]=(0x11 & 0x0000ff00) >> 8;
3203 atacmd[5]=(0x11 & 0x000000ff);
3204 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3205 return 3;
3207 // Validity checks
3208 if(buffer[0]!=0)return 4;
3209 for(i=0;i<5;i++){
3210 if(buffer[1+i]!=read_byte(0xf000,&isotag[i]))return 5;
3212 for(i=0;i<23;i++)
3213 if(buffer[7+i]!=read_byte(0xf000,&eltorito[i]))return 6;
3215 // ok, now we calculate the Boot catalog address
3216 lba=buffer[0x4A]*0x1000000+buffer[0x49]*0x10000+buffer[0x48]*0x100+buffer[0x47];
3218 // And we read the Boot Catalog
3219 memsetb(get_SS(),atacmd,0,12);
3220 atacmd[0]=0x28; // READ command
3221 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3222 atacmd[8]=(0x01 & 0x00ff); // Sectors
3223 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3224 atacmd[3]=(lba & 0x00ff0000) >> 16;
3225 atacmd[4]=(lba & 0x0000ff00) >> 8;
3226 atacmd[5]=(lba & 0x000000ff);
3227 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3228 return 7;
3230 // Validation entry
3231 if(buffer[0x00]!=0x01)return 8; // Header
3232 if(buffer[0x01]!=0x00)return 9; // Platform
3233 if(buffer[0x1E]!=0x55)return 10; // key 1
3234 if(buffer[0x1F]!=0xAA)return 10; // key 2
3236 // Initial/Default Entry
3237 if(buffer[0x20]!=0x88)return 11; // Bootable
3239 write_byte(ebda_seg,&EbdaData->cdemu.media,buffer[0x21]);
3240 if(buffer[0x21]==0){
3241 // FIXME ElTorito Hardcoded. cdrom is hardcoded as device 0xE0.
3242 // Win2000 cd boot needs to know it booted from cd
3243 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0xE0);
3245 else if(buffer[0x21]<4)
3246 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x00);
3247 else
3248 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x80);
3250 write_byte(ebda_seg,&EbdaData->cdemu.controller_index,device/2);
3251 write_byte(ebda_seg,&EbdaData->cdemu.device_spec,device%2);
3253 boot_segment=buffer[0x23]*0x100+buffer[0x22];
3254 if(boot_segment==0x0000)boot_segment=0x07C0;
3256 write_word(ebda_seg,&EbdaData->cdemu.load_segment,boot_segment);
3257 write_word(ebda_seg,&EbdaData->cdemu.buffer_segment,0x0000);
3259 nbsectors=buffer[0x27]*0x100+buffer[0x26];
3260 write_word(ebda_seg,&EbdaData->cdemu.sector_count,nbsectors);
3262 lba=buffer[0x2B]*0x1000000+buffer[0x2A]*0x10000+buffer[0x29]*0x100+buffer[0x28];
3263 write_dword(ebda_seg,&EbdaData->cdemu.ilba,lba);
3265 // And we read the image in memory
3266 memsetb(get_SS(),atacmd,0,12);
3267 atacmd[0]=0x28; // READ command
3268 atacmd[7]=((1+(nbsectors-1)/4) & 0xff00) >> 8; // Sectors
3269 atacmd[8]=((1+(nbsectors-1)/4) & 0x00ff); // Sectors
3270 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3271 atacmd[3]=(lba & 0x00ff0000) >> 16;
3272 atacmd[4]=(lba & 0x0000ff00) >> 8;
3273 atacmd[5]=(lba & 0x000000ff);
3274 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, nbsectors*512L, ATA_DATA_IN, boot_segment,0)) != 0)
3275 return 12;
3277 // Remember the media type
3278 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
3279 case 0x01: // 1.2M floppy
3280 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,15);
3281 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3282 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3283 break;
3284 case 0x02: // 1.44M floppy
3285 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,18);
3286 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3287 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3288 break;
3289 case 0x03: // 2.88M floppy
3290 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,36);
3291 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3292 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3293 break;
3294 case 0x04: // Harddrive
3295 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,read_byte(boot_segment,446+6)&0x3f);
3296 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,
3297 (read_byte(boot_segment,446+6)<<2) + read_byte(boot_segment,446+7) + 1);
3298 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,read_byte(boot_segment,446+5) + 1);
3299 break;
3302 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0) {
3303 // Increase bios installed hardware number of devices
3304 if(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)==0x00)
3305 write_byte(0x40,0x10,read_byte(0x40,0x10)|0x41);
3306 else
3307 write_byte(ebda_seg, &EbdaData->ata.hdcount, read_byte(ebda_seg, &EbdaData->ata.hdcount) + 1);
3311 // everything is ok, so from now on, the emulation is active
3312 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0)
3313 write_byte(ebda_seg,&EbdaData->cdemu.active,0x01);
3315 // return the boot drive + no error
3316 return (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)*0x100)+0;
3319 // ---------------------------------------------------------------------------
3320 // End of El-Torito boot functions
3321 // ---------------------------------------------------------------------------
3322 #endif // BX_ELTORITO_BOOT
3324 void
3325 int14_function(regs, ds, iret_addr)
3326 pusha_regs_t regs; // regs pushed from PUSHA instruction
3327 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
3328 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
3330 Bit16u addr,timer,val16;
3331 Bit8u timeout;
3333 ASM_START
3335 ASM_END
3337 addr = read_word(0x0040, (regs.u.r16.dx << 1));
3338 timeout = read_byte(0x0040, 0x007C + regs.u.r16.dx);
3339 if ((regs.u.r16.dx < 4) && (addr > 0)) {
3340 switch (regs.u.r8.ah) {
3341 case 0:
3342 outb(addr+3, inb(addr+3) | 0x80);
3343 if (regs.u.r8.al & 0xE0 == 0) {
3344 outb(addr, 0x17);
3345 outb(addr+1, 0x04);
3346 } else {
3347 val16 = 0x600 >> ((regs.u.r8.al & 0xE0) >> 5);
3348 outb(addr, val16 & 0xFF);
3349 outb(addr+1, val16 >> 8);
3351 outb(addr+3, regs.u.r8.al & 0x1F);
3352 regs.u.r8.ah = inb(addr+5);
3353 regs.u.r8.al = inb(addr+6);
3354 ClearCF(iret_addr.flags);
3355 break;
3356 case 1:
3357 timer = read_word(0x0040, 0x006C);
3358 while (((inb(addr+5) & 0x60) != 0x60) && (timeout)) {
3359 val16 = read_word(0x0040, 0x006C);
3360 if (val16 != timer) {
3361 timer = val16;
3362 timeout--;
3365 if (timeout) outb(addr, regs.u.r8.al);
3366 regs.u.r8.ah = inb(addr+5);
3367 if (!timeout) regs.u.r8.ah |= 0x80;
3368 ClearCF(iret_addr.flags);
3369 break;
3370 case 2:
3371 timer = read_word(0x0040, 0x006C);
3372 while (((inb(addr+5) & 0x01) == 0) && (timeout)) {
3373 val16 = read_word(0x0040, 0x006C);
3374 if (val16 != timer) {
3375 timer = val16;
3376 timeout--;
3379 if (timeout) {
3380 regs.u.r8.ah = 0;
3381 regs.u.r8.al = inb(addr);
3382 } else {
3383 regs.u.r8.ah = inb(addr+5);
3385 ClearCF(iret_addr.flags);
3386 break;
3387 case 3:
3388 regs.u.r8.ah = inb(addr+5);
3389 regs.u.r8.al = inb(addr+6);
3390 ClearCF(iret_addr.flags);
3391 break;
3392 default:
3393 SetCF(iret_addr.flags); // Unsupported
3395 } else {
3396 SetCF(iret_addr.flags); // Unsupported
3400 void
3401 int15_function(regs, ES, DS, FLAGS)
3402 pusha_regs_t regs; // REGS pushed via pusha
3403 Bit16u ES, DS, FLAGS;
3405 Bit16u ebda_seg=read_word(0x0040,0x000E);
3406 bx_bool prev_a20_enable;
3407 Bit16u base15_00;
3408 Bit8u base23_16;
3409 Bit16u ss;
3410 Bit16u CX,DX;
3412 Bit16u bRegister;
3413 Bit8u irqDisable;
3415 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3417 switch (regs.u.r8.ah) {
3418 case 0x24: /* A20 Control */
3419 switch (regs.u.r8.al) {
3420 case 0x00:
3421 set_enable_a20(0);
3422 CLEAR_CF();
3423 regs.u.r8.ah = 0;
3424 break;
3425 case 0x01:
3426 set_enable_a20(1);
3427 CLEAR_CF();
3428 regs.u.r8.ah = 0;
3429 break;
3430 case 0x02:
3431 regs.u.r8.al = (inb(0x92) >> 1) & 0x01;
3432 CLEAR_CF();
3433 regs.u.r8.ah = 0;
3434 break;
3435 case 0x03:
3436 CLEAR_CF();
3437 regs.u.r8.ah = 0;
3438 regs.u.r16.bx = 3;
3439 break;
3440 default:
3441 BX_INFO("int15: Func 24h, subfunc %02xh, A20 gate control not supported\n", (unsigned) regs.u.r8.al);
3442 SET_CF();
3443 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3445 break;
3447 case 0x41:
3448 SET_CF();
3449 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3450 break;
3452 case 0x4f:
3453 /* keyboard intercept */
3454 #if BX_CPU < 2
3455 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3456 #else
3457 // nop
3458 #endif
3459 SET_CF();
3460 break;
3462 case 0x52: // removable media eject
3463 CLEAR_CF();
3464 regs.u.r8.ah = 0; // "ok ejection may proceed"
3465 break;
3467 case 0x83: {
3468 if( regs.u.r8.al == 0 ) {
3469 // Set Interval requested.
3470 if( ( read_byte( 0x40, 0xA0 ) & 1 ) == 0 ) {
3471 // Interval not already set.
3472 write_byte( 0x40, 0xA0, 1 ); // Set status byte.
3473 write_word( 0x40, 0x98, ES ); // Byte location, segment
3474 write_word( 0x40, 0x9A, regs.u.r16.bx ); // Byte location, offset
3475 write_word( 0x40, 0x9C, regs.u.r16.dx ); // Low word, delay
3476 write_word( 0x40, 0x9E, regs.u.r16.cx ); // High word, delay.
3477 CLEAR_CF( );
3478 irqDisable = inb( 0xA1 );
3479 outb( 0xA1, irqDisable & 0xFE );
3480 bRegister = inb_cmos( 0xB ); // Unmask IRQ8 so INT70 will get through.
3481 outb_cmos( 0xB, bRegister | 0x40 ); // Turn on the Periodic Interrupt timer
3482 } else {
3483 // Interval already set.
3484 BX_DEBUG_INT15("int15: Func 83h, failed, already waiting.\n" );
3485 SET_CF();
3486 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3488 } else if( regs.u.r8.al == 1 ) {
3489 // Clear Interval requested
3490 write_byte( 0x40, 0xA0, 0 ); // Clear status byte
3491 CLEAR_CF( );
3492 bRegister = inb_cmos( 0xB );
3493 outb_cmos( 0xB, bRegister & ~0x40 ); // Turn off the Periodic Interrupt timer
3494 } else {
3495 BX_DEBUG_INT15("int15: Func 83h, failed.\n" );
3496 SET_CF();
3497 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3498 regs.u.r8.al--;
3501 break;
3504 case 0x87:
3505 #if BX_CPU < 3
3506 # error "Int15 function 87h not supported on < 80386"
3507 #endif
3508 // +++ should probably have descriptor checks
3509 // +++ should have exception handlers
3511 // turn off interrupts
3512 ASM_START
3514 ASM_END
3516 prev_a20_enable = set_enable_a20(1); // enable A20 line
3518 // 128K max of transfer on 386+ ???
3519 // source == destination ???
3521 // ES:SI points to descriptor table
3522 // offset use initially comments
3523 // ==============================================
3524 // 00..07 Unused zeros Null descriptor
3525 // 08..0f GDT zeros filled in by BIOS
3526 // 10..17 source ssssssss source of data
3527 // 18..1f dest dddddddd destination of data
3528 // 20..27 CS zeros filled in by BIOS
3529 // 28..2f SS zeros filled in by BIOS
3531 //es:si
3532 //eeee0
3533 //0ssss
3534 //-----
3536 // check for access rights of source & dest here
3538 // Initialize GDT descriptor
3539 base15_00 = (ES << 4) + regs.u.r16.si;
3540 base23_16 = ES >> 12;
3541 if (base15_00 < (ES<<4))
3542 base23_16++;
3543 write_word(ES, regs.u.r16.si+0x08+0, 47); // limit 15:00 = 6 * 8bytes/descriptor
3544 write_word(ES, regs.u.r16.si+0x08+2, base15_00);// base 15:00
3545 write_byte(ES, regs.u.r16.si+0x08+4, base23_16);// base 23:16
3546 write_byte(ES, regs.u.r16.si+0x08+5, 0x93); // access
3547 write_word(ES, regs.u.r16.si+0x08+6, 0x0000); // base 31:24/reserved/limit 19:16
3549 // Initialize CS descriptor
3550 write_word(ES, regs.u.r16.si+0x20+0, 0xffff);// limit 15:00 = normal 64K limit
3551 write_word(ES, regs.u.r16.si+0x20+2, 0x0000);// base 15:00
3552 write_byte(ES, regs.u.r16.si+0x20+4, 0x000f);// base 23:16
3553 write_byte(ES, regs.u.r16.si+0x20+5, 0x9b); // access
3554 write_word(ES, regs.u.r16.si+0x20+6, 0x0000);// base 31:24/reserved/limit 19:16
3556 // Initialize SS descriptor
3557 ss = get_SS();
3558 base15_00 = ss << 4;
3559 base23_16 = ss >> 12;
3560 write_word(ES, regs.u.r16.si+0x28+0, 0xffff); // limit 15:00 = normal 64K limit
3561 write_word(ES, regs.u.r16.si+0x28+2, base15_00);// base 15:00
3562 write_byte(ES, regs.u.r16.si+0x28+4, base23_16);// base 23:16
3563 write_byte(ES, regs.u.r16.si+0x28+5, 0x93); // access
3564 write_word(ES, regs.u.r16.si+0x28+6, 0x0000); // base 31:24/reserved/limit 19:16
3566 CX = regs.u.r16.cx;
3567 ASM_START
3568 // Compile generates locals offset info relative to SP.
3569 // Get CX (word count) from stack.
3570 mov bx, sp
3571 SEG SS
3572 mov cx, _int15_function.CX [bx]
3574 // since we need to set SS:SP, save them to the BDA
3575 // for future restore
3576 push eax
3577 xor eax, eax
3578 mov ds, ax
3579 mov 0x0469, ss
3580 mov 0x0467, sp
3582 SEG ES
3583 lgdt [si + 0x08]
3584 SEG CS
3585 lidt [pmode_IDT_info]
3586 ;; perhaps do something with IDT here
3588 ;; set PE bit in CR0
3589 mov eax, cr0
3590 or al, #0x01
3591 mov cr0, eax
3592 ;; far jump to flush CPU queue after transition to protected mode
3593 JMP_AP(0x0020, protected_mode)
3595 protected_mode:
3596 ;; GDT points to valid descriptor table, now load SS, DS, ES
3597 mov ax, #0x28 ;; 101 000 = 5th descriptor in table, TI=GDT, RPL=00
3598 mov ss, ax
3599 mov ax, #0x10 ;; 010 000 = 2nd descriptor in table, TI=GDT, RPL=00
3600 mov ds, ax
3601 mov ax, #0x18 ;; 011 000 = 3rd descriptor in table, TI=GDT, RPL=00
3602 mov es, ax
3603 xor si, si
3604 xor di, di
3607 movsw ;; move CX words from DS:SI to ES:DI
3609 ;; make sure DS and ES limits are 64KB
3610 mov ax, #0x28
3611 mov ds, ax
3612 mov es, ax
3614 ;; reset PG bit in CR0 ???
3615 mov eax, cr0
3616 and al, #0xFE
3617 mov cr0, eax
3619 ;; far jump to flush CPU queue after transition to real mode
3620 JMP_AP(0xf000, real_mode)
3622 real_mode:
3623 ;; restore IDT to normal real-mode defaults
3624 SEG CS
3625 lidt [rmode_IDT_info]
3627 // restore SS:SP from the BDA
3628 xor ax, ax
3629 mov ds, ax
3630 mov ss, 0x0469
3631 mov sp, 0x0467
3632 pop eax
3633 ASM_END
3635 set_enable_a20(prev_a20_enable);
3637 // turn back on interrupts
3638 ASM_START
3640 ASM_END
3642 regs.u.r8.ah = 0;
3643 CLEAR_CF();
3644 break;
3647 case 0x88:
3648 // Get the amount of extended memory (above 1M)
3649 #if BX_CPU < 2
3650 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3651 SET_CF();
3652 #else
3653 regs.u.r8.al = inb_cmos(0x30);
3654 regs.u.r8.ah = inb_cmos(0x31);
3656 // limit to 15M
3657 if(regs.u.r16.ax > 0x3c00)
3658 regs.u.r16.ax = 0x3c00;
3660 CLEAR_CF();
3661 #endif
3662 break;
3664 case 0x90:
3665 /* Device busy interrupt. Called by Int 16h when no key available */
3666 break;
3668 case 0x91:
3669 /* Interrupt complete. Called by Int 16h when key becomes available */
3670 break;
3672 case 0xbf:
3673 BX_INFO("*** int 15h function AH=bf not yet supported!\n");
3674 SET_CF();
3675 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3676 break;
3678 case 0xC0:
3679 #if 0
3680 SET_CF();
3681 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3682 break;
3683 #endif
3684 CLEAR_CF();
3685 regs.u.r8.ah = 0;
3686 regs.u.r16.bx = BIOS_CONFIG_TABLE;
3687 ES = 0xF000;
3688 break;
3690 case 0xc1:
3691 ES = ebda_seg;
3692 CLEAR_CF();
3693 break;
3695 case 0xd8:
3696 bios_printf(BIOS_PRINTF_DEBUG, "EISA BIOS not present\n");
3697 SET_CF();
3698 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3699 break;
3701 default:
3702 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
3703 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
3704 SET_CF();
3705 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3706 break;
3710 #if BX_USE_PS2_MOUSE
3711 void
3712 int15_function_mouse(regs, ES, DS, FLAGS)
3713 pusha_regs_t regs; // REGS pushed via pusha
3714 Bit16u ES, DS, FLAGS;
3716 Bit16u ebda_seg=read_word(0x0040,0x000E);
3717 Bit8u mouse_flags_1, mouse_flags_2;
3718 Bit16u mouse_driver_seg;
3719 Bit16u mouse_driver_offset;
3720 Bit8u comm_byte, prev_command_byte;
3721 Bit8u ret, mouse_data1, mouse_data2, mouse_data3;
3723 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3725 switch (regs.u.r8.ah) {
3726 case 0xC2:
3727 // Return Codes status in AH
3728 // =========================
3729 // 00: success
3730 // 01: invalid subfunction (AL > 7)
3731 // 02: invalid input value (out of allowable range)
3732 // 03: interface error
3733 // 04: resend command received from mouse controller,
3734 // device driver should attempt command again
3735 // 05: cannot enable mouse, since no far call has been installed
3736 // 80/86: mouse service not implemented
3738 switch (regs.u.r8.al) {
3739 case 0: // Disable/Enable Mouse
3740 BX_DEBUG_INT15("case 0:\n");
3741 switch (regs.u.r8.bh) {
3742 case 0: // Disable Mouse
3743 BX_DEBUG_INT15("case 0: disable mouse\n");
3744 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3745 ret = send_to_mouse_ctrl(0xF5); // disable mouse command
3746 if (ret == 0) {
3747 ret = get_mouse_data(&mouse_data1);
3748 if ( (ret == 0) || (mouse_data1 == 0xFA) ) {
3749 CLEAR_CF();
3750 regs.u.r8.ah = 0;
3751 return;
3755 // error
3756 SET_CF();
3757 regs.u.r8.ah = ret;
3758 return;
3759 break;
3761 case 1: // Enable Mouse
3762 BX_DEBUG_INT15("case 1: enable mouse\n");
3763 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
3764 if ( (mouse_flags_2 & 0x80) == 0 ) {
3765 BX_DEBUG_INT15("INT 15h C2 Enable Mouse, no far call handler\n");
3766 SET_CF(); // error
3767 regs.u.r8.ah = 5; // no far call installed
3768 return;
3770 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3771 ret = send_to_mouse_ctrl(0xF4); // enable mouse command
3772 if (ret == 0) {
3773 ret = get_mouse_data(&mouse_data1);
3774 if ( (ret == 0) && (mouse_data1 == 0xFA) ) {
3775 enable_mouse_int_and_events(); // turn IRQ12 and packet generation on
3776 CLEAR_CF();
3777 regs.u.r8.ah = 0;
3778 return;
3781 SET_CF();
3782 regs.u.r8.ah = ret;
3783 return;
3785 default: // invalid subfunction
3786 BX_DEBUG_INT15("INT 15h C2 AL=0, BH=%02x\n", (unsigned) regs.u.r8.bh);
3787 SET_CF(); // error
3788 regs.u.r8.ah = 1; // invalid subfunction
3789 return;
3791 break;
3793 case 1: // Reset Mouse
3794 case 5: // Initialize Mouse
3795 BX_DEBUG_INT15("case 1 or 5:\n");
3796 if (regs.u.r8.al == 5) {
3797 if (regs.u.r8.bh != 3) {
3798 SET_CF();
3799 regs.u.r8.ah = 0x02; // invalid input
3800 return;
3802 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
3803 mouse_flags_2 = (mouse_flags_2 & 0x00) | regs.u.r8.bh;
3804 mouse_flags_1 = 0x00;
3805 write_byte(ebda_seg, 0x0026, mouse_flags_1);
3806 write_byte(ebda_seg, 0x0027, mouse_flags_2);
3809 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3810 ret = send_to_mouse_ctrl(0xFF); // reset mouse command
3811 if (ret == 0) {
3812 ret = get_mouse_data(&mouse_data3);
3813 // if no mouse attached, it will return RESEND
3814 if (mouse_data3 == 0xfe) {
3815 SET_CF();
3816 return;
3818 if (mouse_data3 != 0xfa)
3819 BX_PANIC("Mouse reset returned %02x (should be ack)\n", (unsigned)mouse_data3);
3820 if ( ret == 0 ) {
3821 ret = get_mouse_data(&mouse_data1);
3822 if ( ret == 0 ) {
3823 ret = get_mouse_data(&mouse_data2);
3824 if ( ret == 0 ) {
3825 // turn IRQ12 and packet generation on
3826 enable_mouse_int_and_events();
3827 CLEAR_CF();
3828 regs.u.r8.ah = 0;
3829 regs.u.r8.bl = mouse_data1;
3830 regs.u.r8.bh = mouse_data2;
3831 return;
3837 // error
3838 SET_CF();
3839 regs.u.r8.ah = ret;
3840 return;
3842 case 2: // Set Sample Rate
3843 BX_DEBUG_INT15("case 2:\n");
3844 switch (regs.u.r8.bh) {
3845 case 0: mouse_data1 = 10; break; // 10 reports/sec
3846 case 1: mouse_data1 = 20; break; // 20 reports/sec
3847 case 2: mouse_data1 = 40; break; // 40 reports/sec
3848 case 3: mouse_data1 = 60; break; // 60 reports/sec
3849 case 4: mouse_data1 = 80; break; // 80 reports/sec
3850 case 5: mouse_data1 = 100; break; // 100 reports/sec (default)
3851 case 6: mouse_data1 = 200; break; // 200 reports/sec
3852 default: mouse_data1 = 0;
3854 if (mouse_data1 > 0) {
3855 ret = send_to_mouse_ctrl(0xF3); // set sample rate command
3856 if (ret == 0) {
3857 ret = get_mouse_data(&mouse_data2);
3858 ret = send_to_mouse_ctrl(mouse_data1);
3859 ret = get_mouse_data(&mouse_data2);
3860 CLEAR_CF();
3861 regs.u.r8.ah = 0;
3862 } else {
3863 // error
3864 SET_CF();
3865 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3867 } else {
3868 // error
3869 SET_CF();
3870 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3872 break;
3874 case 3: // Set Resolution
3875 BX_DEBUG_INT15("case 3:\n");
3876 // BX:
3877 // 0 = 25 dpi, 1 count per millimeter
3878 // 1 = 50 dpi, 2 counts per millimeter
3879 // 2 = 100 dpi, 4 counts per millimeter
3880 // 3 = 200 dpi, 8 counts per millimeter
3881 CLEAR_CF();
3882 regs.u.r8.ah = 0;
3883 break;
3885 case 4: // Get Device ID
3886 BX_DEBUG_INT15("case 4:\n");
3887 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3888 ret = send_to_mouse_ctrl(0xF2); // get mouse ID command
3889 if (ret == 0) {
3890 ret = get_mouse_data(&mouse_data1);
3891 ret = get_mouse_data(&mouse_data2);
3892 CLEAR_CF();
3893 regs.u.r8.ah = 0;
3894 regs.u.r8.bh = mouse_data2;
3895 } else {
3896 // error
3897 SET_CF();
3898 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3900 break;
3902 case 6: // Return Status & Set Scaling Factor...
3903 BX_DEBUG_INT15("case 6:\n");
3904 switch (regs.u.r8.bh) {
3905 case 0: // Return Status
3906 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3907 ret = send_to_mouse_ctrl(0xE9); // get mouse info command
3908 if (ret == 0) {
3909 ret = get_mouse_data(&mouse_data1);
3910 if (mouse_data1 != 0xfa)
3911 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
3912 if (ret == 0) {
3913 ret = get_mouse_data(&mouse_data1);
3914 if ( ret == 0 ) {
3915 ret = get_mouse_data(&mouse_data2);
3916 if ( ret == 0 ) {
3917 ret = get_mouse_data(&mouse_data3);
3918 if ( ret == 0 ) {
3919 CLEAR_CF();
3920 regs.u.r8.ah = 0;
3921 regs.u.r8.bl = mouse_data1;
3922 regs.u.r8.cl = mouse_data2;
3923 regs.u.r8.dl = mouse_data3;
3924 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
3925 return;
3932 // error
3933 SET_CF();
3934 regs.u.r8.ah = ret;
3935 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
3936 return;
3938 case 1: // Set Scaling Factor to 1:1
3939 case 2: // Set Scaling Factor to 2:1
3940 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3941 if (regs.u.r8.bh == 1) {
3942 ret = send_to_mouse_ctrl(0xE6);
3943 } else {
3944 ret = send_to_mouse_ctrl(0xE7);
3946 if (ret == 0) {
3947 get_mouse_data(&mouse_data1);
3948 ret = (mouse_data1 != 0xFA);
3950 if (ret == 0) {
3951 CLEAR_CF();
3952 regs.u.r8.ah = 0;
3953 } else {
3954 // error
3955 SET_CF();
3956 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3958 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
3959 break;
3961 default:
3962 BX_PANIC("INT 15h C2 AL=6, BH=%02x\n", (unsigned) regs.u.r8.bh);
3964 break;
3966 case 7: // Set Mouse Handler Address
3967 BX_DEBUG_INT15("case 7:\n");
3968 mouse_driver_seg = ES;
3969 mouse_driver_offset = regs.u.r16.bx;
3970 write_word(ebda_seg, 0x0022, mouse_driver_offset);
3971 write_word(ebda_seg, 0x0024, mouse_driver_seg);
3972 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
3973 if (mouse_driver_offset == 0 && mouse_driver_seg == 0) {
3974 /* remove handler */
3975 if ( (mouse_flags_2 & 0x80) != 0 ) {
3976 mouse_flags_2 &= ~0x80;
3977 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3980 else {
3981 /* install handler */
3982 mouse_flags_2 |= 0x80;
3984 write_byte(ebda_seg, 0x0027, mouse_flags_2);
3985 CLEAR_CF();
3986 regs.u.r8.ah = 0;
3987 break;
3989 default:
3990 BX_DEBUG_INT15("case default:\n");
3991 regs.u.r8.ah = 1; // invalid function
3992 SET_CF();
3994 break;
3996 default:
3997 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
3998 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
3999 SET_CF();
4000 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4001 break;
4004 #endif
4006 void
4007 int15_function32(regs, ES, DS, FLAGS)
4008 pushad_regs_t regs; // REGS pushed via pushad
4009 Bit16u ES, DS, FLAGS;
4011 Bit32u extended_memory_size=0; // 64bits long
4012 Bit16u CX,DX;
4014 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
4016 switch (regs.u.r8.ah) {
4017 case 0x86:
4018 // Wait for CX:DX microseconds. currently using the
4019 // refresh request port 0x61 bit4, toggling every 15usec
4021 CX = regs.u.r16.cx;
4022 DX = regs.u.r16.dx;
4024 ASM_START
4027 ;; Get the count in eax
4028 mov bx, sp
4029 SEG SS
4030 mov ax, _int15_function.CX [bx]
4031 shl eax, #16
4032 SEG SS
4033 mov ax, _int15_function.DX [bx]
4035 ;; convert to numbers of 15usec ticks
4036 mov ebx, #15
4037 xor edx, edx
4038 div eax, ebx
4039 mov ecx, eax
4041 ;; wait for ecx number of refresh requests
4042 in al, #0x61
4043 and al,#0x10
4044 mov ah, al
4046 or ecx, ecx
4047 je int1586_tick_end
4048 int1586_tick:
4049 in al, #0x61
4050 and al,#0x10
4051 cmp al, ah
4052 je int1586_tick
4053 mov ah, al
4054 dec ecx
4055 jnz int1586_tick
4056 int1586_tick_end:
4057 ASM_END
4059 break;
4061 case 0xe8:
4062 switch(regs.u.r8.al)
4064 case 0x20: // coded by osmaker aka K.J.
4065 if(regs.u.r32.edx == 0x534D4150)
4067 switch(regs.u.r16.bx)
4069 case 0:
4070 write_word(ES, regs.u.r16.di, 0x00);
4071 write_word(ES, regs.u.r16.di+2, 0x00);
4072 write_word(ES, regs.u.r16.di+4, 0x00);
4073 write_word(ES, regs.u.r16.di+6, 0x00);
4075 write_word(ES, regs.u.r16.di+8, 0xFC00);
4076 write_word(ES, regs.u.r16.di+10, 0x0009);
4077 write_word(ES, regs.u.r16.di+12, 0x0000);
4078 write_word(ES, regs.u.r16.di+14, 0x0000);
4080 write_word(ES, regs.u.r16.di+16, 0x1);
4081 write_word(ES, regs.u.r16.di+18, 0x0);
4083 regs.u.r32.ebx = 1;
4084 regs.u.r32.eax = 0x534D4150;
4085 regs.u.r32.ecx = 0x14;
4086 CLEAR_CF();
4087 return;
4088 break;
4089 case 1:
4090 extended_memory_size = inb_cmos(0x35);
4091 extended_memory_size <<= 8;
4092 extended_memory_size |= inb_cmos(0x34);
4093 extended_memory_size *= 64;
4094 if(extended_memory_size > 0x3bc000) // greater than EFF00000???
4096 extended_memory_size = 0x3bc000; // everything after this is reserved memory until we get to 0x100000000
4098 extended_memory_size *= 1024;
4099 extended_memory_size += 15728640; // make up for the 16mb of memory that is chopped off
4101 if(extended_memory_size <= 15728640)
4103 extended_memory_size = inb_cmos(0x31);
4104 extended_memory_size <<= 8;
4105 extended_memory_size |= inb_cmos(0x30);
4106 extended_memory_size *= 1024;
4109 write_word(ES, regs.u.r16.di, 0x0000);
4110 write_word(ES, regs.u.r16.di+2, 0x0010);
4111 write_word(ES, regs.u.r16.di+4, 0x0000);
4112 write_word(ES, regs.u.r16.di+6, 0x0000);
4114 write_word(ES, regs.u.r16.di+8, extended_memory_size);
4115 extended_memory_size >>= 16;
4116 write_word(ES, regs.u.r16.di+10, extended_memory_size);
4117 extended_memory_size >>= 16;
4118 write_word(ES, regs.u.r16.di+12, extended_memory_size);
4119 extended_memory_size >>= 16;
4120 write_word(ES, regs.u.r16.di+14, extended_memory_size);
4122 write_word(ES, regs.u.r16.di+16, 0x1);
4123 write_word(ES, regs.u.r16.di+18, 0x0);
4125 regs.u.r32.ebx = 0;
4126 regs.u.r32.eax = 0x534D4150;
4127 regs.u.r32.ecx = 0x14;
4128 CLEAR_CF();
4129 return;
4130 break;
4131 default: /* AX=E820, DX=534D4150, BX unrecognized */
4132 goto int15_unimplemented;
4133 break;
4135 } else {
4136 // if DX != 0x534D4150)
4137 goto int15_unimplemented;
4139 break;
4141 case 0x01:
4142 // do we have any reason to fail here ?
4143 CLEAR_CF();
4145 // my real system sets ax and bx to 0
4146 // this is confirmed by Ralph Brown list
4147 // but syslinux v1.48 is known to behave
4148 // strangely if ax is set to 0
4149 // regs.u.r16.ax = 0;
4150 // regs.u.r16.bx = 0;
4152 // Get the amount of extended memory (above 1M)
4153 regs.u.r8.cl = inb_cmos(0x30);
4154 regs.u.r8.ch = inb_cmos(0x31);
4156 // limit to 15M
4157 if(regs.u.r16.cx > 0x3c00)
4159 regs.u.r16.cx = 0x3c00;
4162 // Get the amount of extended memory above 16M in 64k blocs
4163 regs.u.r8.dl = inb_cmos(0x34);
4164 regs.u.r8.dh = inb_cmos(0x35);
4166 // Set configured memory equal to extended memory
4167 regs.u.r16.ax = regs.u.r16.cx;
4168 regs.u.r16.bx = regs.u.r16.dx;
4169 break;
4170 default: /* AH=0xE8?? but not implemented */
4171 goto int15_unimplemented;
4173 break;
4174 int15_unimplemented:
4175 // fall into the default
4176 default:
4177 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4178 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4179 SET_CF();
4180 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4181 break;
4185 void
4186 int16_function(DI, SI, BP, SP, BX, DX, CX, AX, FLAGS)
4187 Bit16u DI, SI, BP, SP, BX, DX, CX, AX, FLAGS;
4189 Bit8u scan_code, ascii_code, shift_flags, count;
4190 Bit16u kbd_code, max;
4192 BX_DEBUG_INT16("int16: AX=%04x BX=%04x CX=%04x DX=%04x \n", AX, BX, CX, DX);
4194 switch (GET_AH()) {
4195 case 0x00: /* read keyboard input */
4197 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4198 BX_PANIC("KBD: int16h: out of keyboard input\n");
4200 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4201 else if (ascii_code == 0xE0) ascii_code = 0;
4202 AX = (scan_code << 8) | ascii_code;
4203 break;
4205 case 0x01: /* check keyboard status */
4206 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4207 SET_ZF();
4208 return;
4210 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4211 else if (ascii_code == 0xE0) ascii_code = 0;
4212 AX = (scan_code << 8) | ascii_code;
4213 CLEAR_ZF();
4214 break;
4216 case 0x02: /* get shift flag status */
4217 shift_flags = read_byte(0x0040, 0x17);
4218 SET_AL(shift_flags);
4219 break;
4221 case 0x05: /* store key-stroke into buffer */
4222 if ( !enqueue_key(GET_CH(), GET_CL()) ) {
4223 SET_AL(1);
4225 else {
4226 SET_AL(0);
4228 break;
4230 case 0x09: /* GET KEYBOARD FUNCTIONALITY */
4231 // bit Bochs Description
4232 // 7 0 reserved
4233 // 6 0 INT 16/AH=20h-22h supported (122-key keyboard support)
4234 // 5 1 INT 16/AH=10h-12h supported (enhanced keyboard support)
4235 // 4 1 INT 16/AH=0Ah supported
4236 // 3 0 INT 16/AX=0306h supported
4237 // 2 0 INT 16/AX=0305h supported
4238 // 1 0 INT 16/AX=0304h supported
4239 // 0 0 INT 16/AX=0300h supported
4241 SET_AL(0x30);
4242 break;
4244 case 0x0A: /* GET KEYBOARD ID */
4245 count = 2;
4246 kbd_code = 0x0;
4247 outb(0x60, 0xf2);
4248 /* Wait for data */
4249 max=0xffff;
4250 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4251 if (max>0x0) {
4252 if ((inb(0x60) == 0xfa)) {
4253 do {
4254 max=0xffff;
4255 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4256 if (max>0x0) {
4257 kbd_code >>= 8;
4258 kbd_code |= (inb(0x60) << 8);
4260 } while (--count>0);
4263 BX=kbd_code;
4264 break;
4266 case 0x10: /* read MF-II keyboard input */
4268 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4269 BX_PANIC("KBD: int16h: out of keyboard input\n");
4271 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4272 AX = (scan_code << 8) | ascii_code;
4273 break;
4275 case 0x11: /* check MF-II keyboard status */
4276 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4277 SET_ZF();
4278 return;
4280 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4281 AX = (scan_code << 8) | ascii_code;
4282 CLEAR_ZF();
4283 break;
4285 case 0x12: /* get extended keyboard status */
4286 shift_flags = read_byte(0x0040, 0x17);
4287 SET_AL(shift_flags);
4288 shift_flags = read_byte(0x0040, 0x18) & 0x73;
4289 shift_flags |= read_byte(0x0040, 0x96) & 0x0c;
4290 SET_AH(shift_flags);
4291 BX_DEBUG_INT16("int16: func 12 sending %04x\n",AX);
4292 break;
4294 case 0x92: /* keyboard capability check called by DOS 5.0+ keyb */
4295 SET_AH(0x80); // function int16 ah=0x10-0x12 supported
4296 break;
4298 case 0xA2: /* 122 keys capability check called by DOS 5.0+ keyb */
4299 // don't change AH : function int16 ah=0x20-0x22 NOT supported
4300 break;
4302 case 0x6F:
4303 if (GET_AL() == 0x08)
4304 SET_AH(0x02); // unsupported, aka normal keyboard
4306 default:
4307 BX_INFO("KBD: unsupported int 16h function %02x\n", GET_AH());
4311 unsigned int
4312 dequeue_key(scan_code, ascii_code, incr)
4313 Bit8u *scan_code;
4314 Bit8u *ascii_code;
4315 unsigned int incr;
4317 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail;
4318 Bit16u ss;
4319 Bit8u acode, scode;
4321 #if BX_CPU < 2
4322 buffer_start = 0x001E;
4323 buffer_end = 0x003E;
4324 #else
4325 buffer_start = read_word(0x0040, 0x0080);
4326 buffer_end = read_word(0x0040, 0x0082);
4327 #endif
4329 buffer_head = read_word(0x0040, 0x001a);
4330 buffer_tail = read_word(0x0040, 0x001c);
4332 if (buffer_head != buffer_tail) {
4333 ss = get_SS();
4334 acode = read_byte(0x0040, buffer_head);
4335 scode = read_byte(0x0040, buffer_head+1);
4336 write_byte(ss, ascii_code, acode);
4337 write_byte(ss, scan_code, scode);
4339 if (incr) {
4340 buffer_head += 2;
4341 if (buffer_head >= buffer_end)
4342 buffer_head = buffer_start;
4343 write_word(0x0040, 0x001a, buffer_head);
4345 return(1);
4347 else {
4348 return(0);
4352 static char panic_msg_keyb_buffer_full[] = "%s: keyboard input buffer full\n";
4354 Bit8u
4355 inhibit_mouse_int_and_events()
4357 Bit8u command_byte, prev_command_byte;
4359 // Turn off IRQ generation and aux data line
4360 if ( inb(0x64) & 0x02 )
4361 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4362 outb(0x64, 0x20); // get command byte
4363 while ( (inb(0x64) & 0x01) != 0x01 );
4364 prev_command_byte = inb(0x60);
4365 command_byte = prev_command_byte;
4366 //while ( (inb(0x64) & 0x02) );
4367 if ( inb(0x64) & 0x02 )
4368 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4369 command_byte &= 0xfd; // turn off IRQ 12 generation
4370 command_byte |= 0x20; // disable mouse serial clock line
4371 outb(0x64, 0x60); // write command byte
4372 outb(0x60, command_byte);
4373 return(prev_command_byte);
4376 void
4377 enable_mouse_int_and_events()
4379 Bit8u command_byte;
4381 // Turn on IRQ generation and aux data line
4382 if ( inb(0x64) & 0x02 )
4383 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4384 outb(0x64, 0x20); // get command byte
4385 while ( (inb(0x64) & 0x01) != 0x01 );
4386 command_byte = inb(0x60);
4387 //while ( (inb(0x64) & 0x02) );
4388 if ( inb(0x64) & 0x02 )
4389 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4390 command_byte |= 0x02; // turn on IRQ 12 generation
4391 command_byte &= 0xdf; // enable mouse serial clock line
4392 outb(0x64, 0x60); // write command byte
4393 outb(0x60, command_byte);
4396 Bit8u
4397 send_to_mouse_ctrl(sendbyte)
4398 Bit8u sendbyte;
4400 Bit8u response;
4402 // wait for chance to write to ctrl
4403 if ( inb(0x64) & 0x02 )
4404 BX_PANIC(panic_msg_keyb_buffer_full,"sendmouse");
4405 outb(0x64, 0xD4);
4406 outb(0x60, sendbyte);
4407 return(0);
4411 Bit8u
4412 get_mouse_data(data)
4413 Bit8u *data;
4415 Bit8u response;
4416 Bit16u ss;
4418 while ( (inb(0x64) & 0x21) != 0x21 ) {
4421 response = inb(0x60);
4423 ss = get_SS();
4424 write_byte(ss, data, response);
4425 return(0);
4428 void
4429 set_kbd_command_byte(command_byte)
4430 Bit8u command_byte;
4432 if ( inb(0x64) & 0x02 )
4433 BX_PANIC(panic_msg_keyb_buffer_full,"setkbdcomm");
4434 outb(0x64, 0xD4);
4436 outb(0x64, 0x60); // write command byte
4437 outb(0x60, command_byte);
4440 void
4441 int09_function(DI, SI, BP, SP, BX, DX, CX, AX)
4442 Bit16u DI, SI, BP, SP, BX, DX, CX, AX;
4444 Bit8u scancode, asciicode, shift_flags;
4445 Bit8u mf2_flags, mf2_state, led_flags;
4448 // DS has been set to F000 before call
4452 scancode = GET_AL();
4454 if (scancode == 0) {
4455 BX_INFO("KBD: int09 handler: AL=0\n");
4456 return;
4460 shift_flags = read_byte(0x0040, 0x17);
4461 mf2_flags = read_byte(0x0040, 0x18);
4462 mf2_state = read_byte(0x0040, 0x96);
4463 led_flags = read_byte(0x0040, 0x97);
4464 asciicode = 0;
4466 switch (scancode) {
4467 case 0x3a: /* Caps Lock press */
4468 shift_flags ^= 0x40;
4469 write_byte(0x0040, 0x17, shift_flags);
4470 mf2_flags |= 0x40;
4471 led_flags ^= 0x04;
4472 write_byte(0x0040, 0x18, mf2_flags);
4473 write_byte(0x0040, 0x97, led_flags);
4474 break;
4475 case 0xba: /* Caps Lock release */
4476 mf2_flags &= ~0x40;
4477 write_byte(0x0040, 0x18, mf2_flags);
4478 break;
4480 case 0x2a: /* L Shift press */
4481 shift_flags |= 0x02;
4482 write_byte(0x0040, 0x17, shift_flags);
4483 break;
4484 case 0xaa: /* L Shift release */
4485 shift_flags &= ~0x02;
4486 write_byte(0x0040, 0x17, shift_flags);
4487 break;
4489 case 0x36: /* R Shift press */
4490 shift_flags |= 0x01;
4491 write_byte(0x0040, 0x17, shift_flags);
4492 break;
4493 case 0xb6: /* R Shift release */
4494 shift_flags &= ~0x01;
4495 write_byte(0x0040, 0x17, shift_flags);
4496 break;
4498 case 0x1d: /* Ctrl press */
4499 shift_flags |= 0x04;
4500 write_byte(0x0040, 0x17, shift_flags);
4501 if (mf2_state & 0x01) {
4502 mf2_state |= 0x04;
4503 write_byte(0x0040, 0x96, mf2_state);
4504 } else {
4505 mf2_flags |= 0x01;
4506 write_byte(0x0040, 0x18, mf2_flags);
4508 break;
4509 case 0x9d: /* Ctrl release */
4510 shift_flags &= ~0x04;
4511 write_byte(0x0040, 0x17, shift_flags);
4512 if (mf2_state & 0x01) {
4513 mf2_state &= ~0x04;
4514 write_byte(0x0040, 0x96, mf2_state);
4515 } else {
4516 mf2_flags &= ~0x01;
4517 write_byte(0x0040, 0x18, mf2_flags);
4519 break;
4521 case 0x38: /* Alt press */
4522 shift_flags |= 0x08;
4523 write_byte(0x0040, 0x17, shift_flags);
4524 if (mf2_state & 0x01) {
4525 mf2_state |= 0x08;
4526 write_byte(0x0040, 0x96, mf2_state);
4527 } else {
4528 mf2_flags |= 0x02;
4529 write_byte(0x0040, 0x18, mf2_flags);
4531 break;
4532 case 0xb8: /* Alt release */
4533 shift_flags &= ~0x08;
4534 write_byte(0x0040, 0x17, shift_flags);
4535 if (mf2_state & 0x01) {
4536 mf2_state &= ~0x08;
4537 write_byte(0x0040, 0x96, mf2_state);
4538 } else {
4539 mf2_flags &= ~0x02;
4540 write_byte(0x0040, 0x18, mf2_flags);
4542 break;
4544 case 0x45: /* Num Lock press */
4545 if ((mf2_state & 0x01) == 0) {
4546 mf2_flags |= 0x20;
4547 write_byte(0x0040, 0x18, mf2_flags);
4548 shift_flags ^= 0x20;
4549 led_flags ^= 0x02;
4550 write_byte(0x0040, 0x17, shift_flags);
4551 write_byte(0x0040, 0x97, led_flags);
4553 break;
4554 case 0xc5: /* Num Lock release */
4555 if ((mf2_state & 0x01) == 0) {
4556 mf2_flags &= ~0x20;
4557 write_byte(0x0040, 0x18, mf2_flags);
4559 break;
4561 case 0x46: /* Scroll Lock press */
4562 mf2_flags |= 0x10;
4563 write_byte(0x0040, 0x18, mf2_flags);
4564 shift_flags ^= 0x10;
4565 led_flags ^= 0x01;
4566 write_byte(0x0040, 0x17, shift_flags);
4567 write_byte(0x0040, 0x97, led_flags);
4568 break;
4570 case 0xc6: /* Scroll Lock release */
4571 mf2_flags &= ~0x10;
4572 write_byte(0x0040, 0x18, mf2_flags);
4573 break;
4575 default:
4576 if (scancode & 0x80) return; /* toss key releases ... */
4577 if (scancode > MAX_SCAN_CODE) {
4578 BX_INFO("KBD: int09h_handler(): unknown scancode read: 0x%02x!\n", scancode);
4579 return;
4581 if (shift_flags & 0x08) { /* ALT */
4582 asciicode = scan_to_scanascii[scancode].alt;
4583 scancode = scan_to_scanascii[scancode].alt >> 8;
4585 else if (shift_flags & 0x04) { /* CONTROL */
4586 asciicode = scan_to_scanascii[scancode].control;
4587 scancode = scan_to_scanascii[scancode].control >> 8;
4589 else if (shift_flags & 0x03) { /* LSHIFT + RSHIFT */
4590 /* check if lock state should be ignored
4591 * because a SHIFT key are pressed */
4593 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
4594 asciicode = scan_to_scanascii[scancode].normal;
4595 scancode = scan_to_scanascii[scancode].normal >> 8;
4597 else {
4598 asciicode = scan_to_scanascii[scancode].shift;
4599 scancode = scan_to_scanascii[scancode].shift >> 8;
4602 else {
4603 /* check if lock is on */
4604 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
4605 asciicode = scan_to_scanascii[scancode].shift;
4606 scancode = scan_to_scanascii[scancode].shift >> 8;
4608 else {
4609 asciicode = scan_to_scanascii[scancode].normal;
4610 scancode = scan_to_scanascii[scancode].normal >> 8;
4613 if (scancode==0 && asciicode==0) {
4614 BX_INFO("KBD: int09h_handler(): scancode & asciicode are zero?\n");
4616 enqueue_key(scancode, asciicode);
4617 break;
4619 mf2_state &= ~0x01;
4620 write_byte(0x0040, 0x96, mf2_state);
4623 unsigned int
4624 enqueue_key(scan_code, ascii_code)
4625 Bit8u scan_code, ascii_code;
4627 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail, temp_tail;
4629 //BX_INFO("KBD: enqueue_key() called scan:%02x, ascii:%02x\n",
4630 // scan_code, ascii_code);
4632 #if BX_CPU < 2
4633 buffer_start = 0x001E;
4634 buffer_end = 0x003E;
4635 #else
4636 buffer_start = read_word(0x0040, 0x0080);
4637 buffer_end = read_word(0x0040, 0x0082);
4638 #endif
4640 buffer_head = read_word(0x0040, 0x001A);
4641 buffer_tail = read_word(0x0040, 0x001C);
4643 temp_tail = buffer_tail;
4644 buffer_tail += 2;
4645 if (buffer_tail >= buffer_end)
4646 buffer_tail = buffer_start;
4648 if (buffer_tail == buffer_head) {
4649 return(0);
4652 write_byte(0x0040, temp_tail, ascii_code);
4653 write_byte(0x0040, temp_tail+1, scan_code);
4654 write_word(0x0040, 0x001C, buffer_tail);
4655 return(1);
4659 void
4660 int74_function(make_farcall, Z, Y, X, status)
4661 Bit16u make_farcall, Z, Y, X, status;
4663 Bit16u ebda_seg=read_word(0x0040,0x000E);
4664 Bit8u in_byte, index, package_count;
4665 Bit8u mouse_flags_1, mouse_flags_2;
4667 BX_DEBUG_INT74("entering int74_function\n");
4668 make_farcall = 0;
4670 in_byte = inb(0x64);
4671 if ( (in_byte & 0x21) != 0x21 ) {
4672 return;
4674 in_byte = inb(0x60);
4675 BX_DEBUG_INT74("int74: read byte %02x\n", in_byte);
4677 mouse_flags_1 = read_byte(ebda_seg, 0x0026);
4678 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4680 if ( (mouse_flags_2 & 0x80) != 0x80 ) {
4681 // BX_PANIC("int74_function:\n");
4682 return;
4685 package_count = mouse_flags_2 & 0x07;
4686 index = mouse_flags_1 & 0x07;
4687 write_byte(ebda_seg, 0x28 + index, in_byte);
4689 if ( (index+1) >= package_count ) {
4690 BX_DEBUG_INT74("int74_function: make_farcall=1\n");
4691 status = read_byte(ebda_seg, 0x0028 + 0);
4692 X = read_byte(ebda_seg, 0x0028 + 1);
4693 Y = read_byte(ebda_seg, 0x0028 + 2);
4694 Z = 0;
4695 mouse_flags_1 = 0;
4696 // check if far call handler installed
4697 if (mouse_flags_2 & 0x80)
4698 make_farcall = 1;
4700 else {
4701 mouse_flags_1++;
4703 write_byte(ebda_seg, 0x0026, mouse_flags_1);
4706 #define SET_DISK_RET_STATUS(status) write_byte(0x0040, 0x0074, status)
4708 #if BX_USE_ATADRV
4710 void
4711 int13_harddisk(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
4712 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
4714 Bit32u lba;
4715 Bit16u ebda_seg=read_word(0x0040,0x000E);
4716 Bit16u cylinder, head, sector;
4717 Bit16u segment, offset;
4718 Bit16u npc, nph, npspt, nlc, nlh, nlspt;
4719 Bit16u size, count;
4720 Bit8u device, status;
4722 BX_DEBUG_INT13_HD("int13_harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
4724 write_byte(0x0040, 0x008e, 0); // clear completion flag
4726 // basic check : device has to be defined
4727 if ( (GET_ELDL() < 0x80) || (GET_ELDL() >= 0x80 + BX_MAX_ATA_DEVICES) ) {
4728 BX_INFO("int13_harddisk: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
4729 goto int13_fail;
4732 // Get the ata channel
4733 device=read_byte(ebda_seg,&EbdaData->ata.hdidmap[GET_ELDL()-0x80]);
4735 // basic check : device has to be valid
4736 if (device >= BX_MAX_ATA_DEVICES) {
4737 BX_INFO("int13_harddisk: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
4738 goto int13_fail;
4741 switch (GET_AH()) {
4743 case 0x00: /* disk controller reset */
4744 ata_reset (device);
4745 goto int13_success;
4746 break;
4748 case 0x01: /* read disk status */
4749 status = read_byte(0x0040, 0x0074);
4750 SET_AH(status);
4751 SET_DISK_RET_STATUS(0);
4752 /* set CF if error status read */
4753 if (status) goto int13_fail_nostatus;
4754 else goto int13_success_noah;
4755 break;
4757 case 0x02: // read disk sectors
4758 case 0x03: // write disk sectors
4759 case 0x04: // verify disk sectors
4761 count = GET_AL();
4762 cylinder = GET_CH();
4763 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
4764 sector = (GET_CL() & 0x3f);
4765 head = GET_DH();
4767 segment = ES;
4768 offset = BX;
4770 if ( (count > 128) || (count == 0) ) {
4771 BX_INFO("int13_harddisk: function %02x, count out of range!\n",GET_AH());
4772 goto int13_fail;
4775 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
4776 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
4777 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
4779 // sanity check on cyl heads, sec
4780 if( (cylinder >= nlc) || (head >= nlh) || (sector > nlspt )) {
4781 BX_INFO("int13_harddisk: function %02x, parameters out of range %04x/%04x/%04x!\n", GET_AH(), cylinder, head, sector);
4782 goto int13_fail;
4785 // FIXME verify
4786 if ( GET_AH() == 0x04 ) goto int13_success;
4788 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
4789 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
4791 // if needed, translate lchs to lba, and execute command
4792 if ( (nph != nlh) || (npspt != nlspt)) {
4793 lba = ((((Bit32u)cylinder * (Bit32u)nlh) + (Bit32u)head) * (Bit32u)nlspt) + (Bit32u)sector - 1;
4794 sector = 0; // this forces the command to be lba
4797 if ( GET_AH() == 0x02 )
4798 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, cylinder, head, sector, lba, segment, offset);
4799 else
4800 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, cylinder, head, sector, lba, segment, offset);
4802 // Set nb of sector transferred
4803 SET_AL(read_word(ebda_seg, &EbdaData->ata.trsfsectors));
4805 if (status != 0) {
4806 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
4807 SET_AH(0x0c);
4808 goto int13_fail_noah;
4811 goto int13_success;
4812 break;
4814 case 0x05: /* format disk track */
4815 BX_INFO("format disk track called\n");
4816 goto int13_success;
4817 return;
4818 break;
4820 case 0x08: /* read disk drive parameters */
4822 // Get logical geometry from table
4823 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
4824 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
4825 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
4826 count = read_byte(ebda_seg, &EbdaData->ata.hdcount);
4828 nlc = nlc - 2; /* 0 based , last sector not used */
4829 SET_AL(0);
4830 SET_CH(nlc & 0xff);
4831 SET_CL(((nlc >> 2) & 0xc0) | (nlspt & 0x3f));
4832 SET_DH(nlh - 1);
4833 SET_DL(count); /* FIXME returns 0, 1, or n hard drives */
4835 // FIXME should set ES & DI
4837 goto int13_success;
4838 break;
4840 case 0x10: /* check drive ready */
4841 // should look at 40:8E also???
4843 // Read the status from controller
4844 status = inb(read_word(ebda_seg, &EbdaData->ata.channels[device/2].iobase1) + ATA_CB_STAT);
4845 if ( (status & ( ATA_CB_STAT_BSY | ATA_CB_STAT_RDY )) == ATA_CB_STAT_RDY ) {
4846 goto int13_success;
4848 else {
4849 SET_AH(0xAA);
4850 goto int13_fail_noah;
4852 break;
4854 case 0x15: /* read disk drive size */
4856 // Get physical geometry from table
4857 npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
4858 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
4859 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
4861 // Compute sector count seen by int13
4862 lba = (Bit32u)(npc - 1) * (Bit32u)nph * (Bit32u)npspt;
4863 CX = lba >> 16;
4864 DX = lba & 0xffff;
4866 SET_AH(3); // hard disk accessible
4867 goto int13_success_noah;
4868 break;
4870 case 0x41: // IBM/MS installation check
4871 BX=0xaa55; // install check
4872 SET_AH(0x30); // EDD 3.0
4873 CX=0x0007; // ext disk access and edd, removable supported
4874 goto int13_success_noah;
4875 break;
4877 case 0x42: // IBM/MS extended read
4878 case 0x43: // IBM/MS extended write
4879 case 0x44: // IBM/MS verify
4880 case 0x47: // IBM/MS extended seek
4882 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
4883 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
4884 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
4886 // Can't use 64 bits lba
4887 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
4888 if (lba != 0L) {
4889 BX_PANIC("int13_harddisk: function %02x. Can't use 64bits lba\n",GET_AH());
4890 goto int13_fail;
4893 // Get 32 bits lba and check
4894 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
4895 if (lba >= read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors) ) {
4896 BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH());
4897 goto int13_fail;
4900 // If verify or seek
4901 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
4902 goto int13_success;
4904 // Execute the command
4905 if ( GET_AH() == 0x42 )
4906 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, 0, 0, 0, lba, segment, offset);
4907 else
4908 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, 0, 0, 0, lba, segment, offset);
4910 count=read_word(ebda_seg, &EbdaData->ata.trsfsectors);
4911 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
4913 if (status != 0) {
4914 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
4915 SET_AH(0x0c);
4916 goto int13_fail_noah;
4919 goto int13_success;
4920 break;
4922 case 0x45: // IBM/MS lock/unlock drive
4923 case 0x49: // IBM/MS extended media change
4924 goto int13_success; // Always success for HD
4925 break;
4927 case 0x46: // IBM/MS eject media
4928 SET_AH(0xb2); // Volume Not Removable
4929 goto int13_fail_noah; // Always fail for HD
4930 break;
4932 case 0x48: // IBM/MS get drive parameters
4933 size=read_word(DS,SI+(Bit16u)&Int13DPT->size);
4935 // Buffer is too small
4936 if(size < 0x1a)
4937 goto int13_fail;
4939 // EDD 1.x
4940 if(size >= 0x1a) {
4941 Bit16u blksize;
4943 npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
4944 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
4945 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
4946 lba = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors);
4947 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
4949 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
4950 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x02); // geometry is valid
4951 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, (Bit32u)npc);
4952 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, (Bit32u)nph);
4953 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, (Bit32u)npspt);
4954 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, lba); // FIXME should be Bit64
4955 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0L);
4956 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
4959 // EDD 2.x
4960 if(size >= 0x1e) {
4961 Bit8u channel, dev, irq, mode, checksum, i, translation;
4962 Bit16u iobase1, iobase2, options;
4964 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
4966 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
4967 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
4969 // Fill in dpte
4970 channel = device / 2;
4971 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
4972 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
4973 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
4974 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
4975 translation = read_byte(ebda_seg, &EbdaData->ata.devices[device].translation);
4977 options = (translation==ATA_TRANSLATION_NONE?0:1<<3); // chs translation
4978 options |= (1<<4); // lba translation
4979 options |= (mode==ATA_MODE_PIO32?1:0<<7);
4980 options |= (translation==ATA_TRANSLATION_LBA?1:0<<9);
4981 options |= (translation==ATA_TRANSLATION_RECHS?3:0<<9);
4983 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
4984 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2);
4985 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
4986 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
4987 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
4988 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
4989 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
4990 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
4991 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
4992 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
4993 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
4995 checksum=0;
4996 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, (&EbdaData->ata.dpte) + i);
4997 checksum = ~checksum;
4998 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5001 // EDD 3.x
5002 if(size >= 0x42) {
5003 Bit8u channel, iface, checksum, i;
5004 Bit16u iobase1;
5006 channel = device / 2;
5007 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5008 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5010 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5011 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5012 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5013 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5014 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5016 if (iface==ATA_IFACE_ISA) {
5017 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5018 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5019 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5020 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5022 else {
5023 // FIXME PCI
5025 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5026 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5027 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5028 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5030 if (iface==ATA_IFACE_ISA) {
5031 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5032 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5033 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5035 else {
5036 // FIXME PCI
5038 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5039 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5040 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5041 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5043 checksum=0;
5044 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5045 checksum = ~checksum;
5046 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5049 goto int13_success;
5050 break;
5052 case 0x4e: // // IBM/MS set hardware configuration
5053 // DMA, prefetch, PIO maximum not supported
5054 switch (GET_AL()) {
5055 case 0x01:
5056 case 0x03:
5057 case 0x04:
5058 case 0x06:
5059 goto int13_success;
5060 break;
5061 default :
5062 goto int13_fail;
5064 break;
5066 case 0x09: /* initialize drive parameters */
5067 case 0x0c: /* seek to specified cylinder */
5068 case 0x0d: /* alternate disk reset */
5069 case 0x11: /* recalibrate */
5070 case 0x14: /* controller internal diagnostic */
5071 BX_INFO("int13h_harddisk function %02xh unimplemented, returns success\n", GET_AH());
5072 goto int13_success;
5073 break;
5075 case 0x0a: /* read disk sectors with ECC */
5076 case 0x0b: /* write disk sectors with ECC */
5077 case 0x18: // set media type for format
5078 case 0x50: // IBM/MS send packet command
5079 default:
5080 BX_INFO("int13_harddisk function %02xh unsupported, returns fail\n", GET_AH());
5081 goto int13_fail;
5082 break;
5085 int13_fail:
5086 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5087 int13_fail_noah:
5088 SET_DISK_RET_STATUS(GET_AH());
5089 int13_fail_nostatus:
5090 SET_CF(); // error occurred
5091 return;
5093 int13_success:
5094 SET_AH(0x00); // no error
5095 int13_success_noah:
5096 SET_DISK_RET_STATUS(0x00);
5097 CLEAR_CF(); // no error
5098 return;
5101 // ---------------------------------------------------------------------------
5102 // Start of int13 for cdrom
5103 // ---------------------------------------------------------------------------
5105 void
5106 int13_cdrom(EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
5107 Bit16u EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
5109 Bit16u ebda_seg=read_word(0x0040,0x000E);
5110 Bit8u device, status, locks;
5111 Bit8u atacmd[12];
5112 Bit32u lba;
5113 Bit16u count, segment, offset, i, size;
5115 BX_DEBUG_INT13_CD("int13_cdrom: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5116 // BX_DEBUG_INT13_CD("int13_cdrom: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
5118 SET_DISK_RET_STATUS(0x00);
5120 /* basic check : device should be 0xE0+ */
5121 if( (GET_ELDL() < 0xE0) || (GET_ELDL() >= 0xE0+BX_MAX_ATA_DEVICES) ) {
5122 BX_INFO("int13_cdrom: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
5123 goto int13_fail;
5126 // Get the ata channel
5127 device=read_byte(ebda_seg,&EbdaData->ata.cdidmap[GET_ELDL()-0xE0]);
5129 /* basic check : device has to be valid */
5130 if (device >= BX_MAX_ATA_DEVICES) {
5131 BX_INFO("int13_cdrom: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
5132 goto int13_fail;
5135 switch (GET_AH()) {
5137 // all those functions return SUCCESS
5138 case 0x00: /* disk controller reset */
5139 case 0x09: /* initialize drive parameters */
5140 case 0x0c: /* seek to specified cylinder */
5141 case 0x0d: /* alternate disk reset */
5142 case 0x10: /* check drive ready */
5143 case 0x11: /* recalibrate */
5144 case 0x14: /* controller internal diagnostic */
5145 case 0x16: /* detect disk change */
5146 goto int13_success;
5147 break;
5149 // all those functions return disk write-protected
5150 case 0x03: /* write disk sectors */
5151 case 0x05: /* format disk track */
5152 case 0x43: // IBM/MS extended write
5153 SET_AH(0x03);
5154 goto int13_fail_noah;
5155 break;
5157 case 0x01: /* read disk status */
5158 status = read_byte(0x0040, 0x0074);
5159 SET_AH(status);
5160 SET_DISK_RET_STATUS(0);
5162 /* set CF if error status read */
5163 if (status) goto int13_fail_nostatus;
5164 else goto int13_success_noah;
5165 break;
5167 case 0x15: /* read disk drive size */
5168 SET_AH(0x02);
5169 goto int13_fail_noah;
5170 break;
5172 case 0x41: // IBM/MS installation check
5173 BX=0xaa55; // install check
5174 SET_AH(0x30); // EDD 2.1
5175 CX=0x0007; // ext disk access, removable and edd
5176 goto int13_success_noah;
5177 break;
5179 case 0x42: // IBM/MS extended read
5180 case 0x44: // IBM/MS verify sectors
5181 case 0x47: // IBM/MS extended seek
5183 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
5184 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
5185 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
5187 // Can't use 64 bits lba
5188 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
5189 if (lba != 0L) {
5190 BX_PANIC("int13_cdrom: function %02x. Can't use 64bits lba\n",GET_AH());
5191 goto int13_fail;
5194 // Get 32 bits lba
5195 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5197 // If verify or seek
5198 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
5199 goto int13_success;
5201 memsetb(get_SS(),atacmd,0,12);
5202 atacmd[0]=0x28; // READ command
5203 atacmd[7]=(count & 0xff00) >> 8; // Sectors
5204 atacmd[8]=(count & 0x00ff); // Sectors
5205 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
5206 atacmd[3]=(lba & 0x00ff0000) >> 16;
5207 atacmd[4]=(lba & 0x0000ff00) >> 8;
5208 atacmd[5]=(lba & 0x000000ff);
5209 status = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, count*2048L, ATA_DATA_IN, segment,offset);
5211 count = (Bit16u)(read_dword(ebda_seg, &EbdaData->ata.trsfbytes) >> 11);
5212 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5214 if (status != 0) {
5215 BX_INFO("int13_cdrom: function %02x, status %02x !\n",GET_AH(),status);
5216 SET_AH(0x0c);
5217 goto int13_fail_noah;
5220 goto int13_success;
5221 break;
5223 case 0x45: // IBM/MS lock/unlock drive
5224 if (GET_AL() > 2) goto int13_fail;
5226 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5228 switch (GET_AL()) {
5229 case 0 : // lock
5230 if (locks == 0xff) {
5231 SET_AH(0xb4);
5232 SET_AL(1);
5233 goto int13_fail_noah;
5235 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, ++locks);
5236 SET_AL(1);
5237 break;
5238 case 1 : // unlock
5239 if (locks == 0x00) {
5240 SET_AH(0xb0);
5241 SET_AL(0);
5242 goto int13_fail_noah;
5244 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, --locks);
5245 SET_AL(locks==0?0:1);
5246 break;
5247 case 2 : // status
5248 SET_AL(locks==0?0:1);
5249 break;
5251 goto int13_success;
5252 break;
5254 case 0x46: // IBM/MS eject media
5255 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5257 if (locks != 0) {
5258 SET_AH(0xb1); // media locked
5259 goto int13_fail_noah;
5261 // FIXME should handle 0x31 no media in device
5262 // FIXME should handle 0xb5 valid request failed
5264 // Call removable media eject
5265 ASM_START
5266 push bp
5267 mov bp, sp
5269 mov ah, #0x52
5270 int 15
5271 mov _int13_cdrom.status + 2[bp], ah
5272 jnc int13_cdrom_rme_end
5273 mov _int13_cdrom.status, #1
5274 int13_cdrom_rme_end:
5275 pop bp
5276 ASM_END
5278 if (status != 0) {
5279 SET_AH(0xb1); // media locked
5280 goto int13_fail_noah;
5283 goto int13_success;
5284 break;
5286 case 0x48: // IBM/MS get drive parameters
5287 size = read_word(DS,SI+(Bit16u)&Int13Ext->size);
5289 // Buffer is too small
5290 if(size < 0x1a)
5291 goto int13_fail;
5293 // EDD 1.x
5294 if(size >= 0x1a) {
5295 Bit16u cylinders, heads, spt, blksize;
5297 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5299 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5300 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x74); // removable, media change, lockable, max values
5301 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0xffffffff);
5302 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, 0xffffffff);
5303 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, 0xffffffff);
5304 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, 0xffffffff); // FIXME should be Bit64
5305 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0xffffffff);
5306 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5309 // EDD 2.x
5310 if(size >= 0x1e) {
5311 Bit8u channel, dev, irq, mode, checksum, i;
5312 Bit16u iobase1, iobase2, options;
5314 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5316 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5317 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5319 // Fill in dpte
5320 channel = device / 2;
5321 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5322 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5323 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5324 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5326 // FIXME atapi device
5327 options = (1<<4); // lba translation
5328 options |= (1<<5); // removable device
5329 options |= (1<<6); // atapi device
5330 options |= (mode==ATA_MODE_PIO32?1:0<<7);
5332 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5333 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2);
5334 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5335 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5336 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5337 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5338 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5339 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5340 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5341 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5342 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5344 checksum=0;
5345 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, (&EbdaData->ata.dpte) + i);
5346 checksum = ~checksum;
5347 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5350 // EDD 3.x
5351 if(size >= 0x42) {
5352 Bit8u channel, iface, checksum, i;
5353 Bit16u iobase1;
5355 channel = device / 2;
5356 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5357 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5359 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5360 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5361 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5362 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5363 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5365 if (iface==ATA_IFACE_ISA) {
5366 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5367 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5368 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5369 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5371 else {
5372 // FIXME PCI
5374 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5375 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5376 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5377 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5379 if (iface==ATA_IFACE_ISA) {
5380 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5381 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5382 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5384 else {
5385 // FIXME PCI
5387 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5388 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5389 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5390 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5392 checksum=0;
5393 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5394 checksum = ~checksum;
5395 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5398 goto int13_success;
5399 break;
5401 case 0x49: // IBM/MS extended media change
5402 // always send changed ??
5403 SET_AH(06);
5404 goto int13_fail_nostatus;
5405 break;
5407 case 0x4e: // // IBM/MS set hardware configuration
5408 // DMA, prefetch, PIO maximum not supported
5409 switch (GET_AL()) {
5410 case 0x01:
5411 case 0x03:
5412 case 0x04:
5413 case 0x06:
5414 goto int13_success;
5415 break;
5416 default :
5417 goto int13_fail;
5419 break;
5421 // all those functions return unimplemented
5422 case 0x02: /* read sectors */
5423 case 0x04: /* verify sectors */
5424 case 0x08: /* read disk drive parameters */
5425 case 0x0a: /* read disk sectors with ECC */
5426 case 0x0b: /* write disk sectors with ECC */
5427 case 0x18: /* set media type for format */
5428 case 0x50: // ? - send packet command
5429 default:
5430 BX_INFO("int13_cdrom: unsupported AH=%02x\n", GET_AH());
5431 goto int13_fail;
5432 break;
5435 int13_fail:
5436 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5437 int13_fail_noah:
5438 SET_DISK_RET_STATUS(GET_AH());
5439 int13_fail_nostatus:
5440 SET_CF(); // error occurred
5441 return;
5443 int13_success:
5444 SET_AH(0x00); // no error
5445 int13_success_noah:
5446 SET_DISK_RET_STATUS(0x00);
5447 CLEAR_CF(); // no error
5448 return;
5451 // ---------------------------------------------------------------------------
5452 // End of int13 for cdrom
5453 // ---------------------------------------------------------------------------
5455 #if BX_ELTORITO_BOOT
5456 // ---------------------------------------------------------------------------
5457 // Start of int13 for eltorito functions
5458 // ---------------------------------------------------------------------------
5460 void
5461 int13_eltorito(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
5462 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
5464 Bit16u ebda_seg=read_word(0x0040,0x000E);
5466 BX_DEBUG_INT13_ET("int13_eltorito: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5467 // BX_DEBUG_INT13_ET("int13_eltorito: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
5469 switch (GET_AH()) {
5471 // FIXME ElTorito Various. Should be implemented
5472 case 0x4a: // ElTorito - Initiate disk emu
5473 case 0x4c: // ElTorito - Initiate disk emu and boot
5474 case 0x4d: // ElTorito - Return Boot catalog
5475 BX_PANIC("Int13 eltorito call with AX=%04x. Please report\n",AX);
5476 goto int13_fail;
5477 break;
5479 case 0x4b: // ElTorito - Terminate disk emu
5480 // FIXME ElTorito Hardcoded
5481 write_byte(DS,SI+0x00,0x13);
5482 write_byte(DS,SI+0x01,read_byte(ebda_seg,&EbdaData->cdemu.media));
5483 write_byte(DS,SI+0x02,read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
5484 write_byte(DS,SI+0x03,read_byte(ebda_seg,&EbdaData->cdemu.controller_index));
5485 write_dword(DS,SI+0x04,read_dword(ebda_seg,&EbdaData->cdemu.ilba));
5486 write_word(DS,SI+0x08,read_word(ebda_seg,&EbdaData->cdemu.device_spec));
5487 write_word(DS,SI+0x0a,read_word(ebda_seg,&EbdaData->cdemu.buffer_segment));
5488 write_word(DS,SI+0x0c,read_word(ebda_seg,&EbdaData->cdemu.load_segment));
5489 write_word(DS,SI+0x0e,read_word(ebda_seg,&EbdaData->cdemu.sector_count));
5490 write_byte(DS,SI+0x10,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.cylinders));
5491 write_byte(DS,SI+0x11,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.spt));
5492 write_byte(DS,SI+0x12,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.heads));
5494 // If we have to terminate emulation
5495 if(GET_AL() == 0x00) {
5496 // FIXME ElTorito Various. Should be handled accordingly to spec
5497 write_byte(ebda_seg,&EbdaData->cdemu.active, 0x00); // bye bye
5500 goto int13_success;
5501 break;
5503 default:
5504 BX_INFO("int13_eltorito: unsupported AH=%02x\n", GET_AH());
5505 goto int13_fail;
5506 break;
5509 int13_fail:
5510 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5511 SET_DISK_RET_STATUS(GET_AH());
5512 SET_CF(); // error occurred
5513 return;
5515 int13_success:
5516 SET_AH(0x00); // no error
5517 SET_DISK_RET_STATUS(0x00);
5518 CLEAR_CF(); // no error
5519 return;
5522 // ---------------------------------------------------------------------------
5523 // End of int13 for eltorito functions
5524 // ---------------------------------------------------------------------------
5526 // ---------------------------------------------------------------------------
5527 // Start of int13 when emulating a device from the cd
5528 // ---------------------------------------------------------------------------
5530 void
5531 int13_cdemu(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
5532 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
5534 Bit16u ebda_seg=read_word(0x0040,0x000E);
5535 Bit8u device, status;
5536 Bit16u vheads, vspt, vcylinders;
5537 Bit16u head, sector, cylinder, nbsectors;
5538 Bit32u vlba, ilba, slba, elba;
5539 Bit16u before, segment, offset;
5540 Bit8u atacmd[12];
5542 BX_DEBUG_INT13_ET("int13_cdemu: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5543 //BX_DEBUG_INT13_ET("int13_cdemu: SS=%04x ES=%04x DI=%04x SI=%04x\n", get_SS(), ES, DI, SI);
5545 /* at this point, we are emulating a floppy/harddisk */
5547 // Recompute the device number
5548 device = read_byte(ebda_seg,&EbdaData->cdemu.controller_index) * 2;
5549 device += read_byte(ebda_seg,&EbdaData->cdemu.device_spec);
5551 SET_DISK_RET_STATUS(0x00);
5553 /* basic checks : emulation should be active, dl should equal the emulated drive */
5554 if( (read_byte(ebda_seg,&EbdaData->cdemu.active) ==0 )
5555 || (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive ) != GET_DL())) {
5556 BX_INFO("int13_cdemu: function %02x, emulation not active for DL= %02x\n", GET_AH(), GET_DL());
5557 goto int13_fail;
5560 switch (GET_AH()) {
5562 // all those functions return SUCCESS
5563 case 0x00: /* disk controller reset */
5564 case 0x09: /* initialize drive parameters */
5565 case 0x0c: /* seek to specified cylinder */
5566 case 0x0d: /* alternate disk reset */ // FIXME ElTorito Various. should really reset ?
5567 case 0x10: /* check drive ready */ // FIXME ElTorito Various. should check if ready ?
5568 case 0x11: /* recalibrate */
5569 case 0x14: /* controller internal diagnostic */
5570 case 0x16: /* detect disk change */
5571 goto int13_success;
5572 break;
5574 // all those functions return disk write-protected
5575 case 0x03: /* write disk sectors */
5576 case 0x05: /* format disk track */
5577 SET_AH(0x03);
5578 goto int13_fail_noah;
5579 break;
5581 case 0x01: /* read disk status */
5582 status=read_byte(0x0040, 0x0074);
5583 SET_AH(status);
5584 SET_DISK_RET_STATUS(0);
5586 /* set CF if error status read */
5587 if (status) goto int13_fail_nostatus;
5588 else goto int13_success_noah;
5589 break;
5591 case 0x02: // read disk sectors
5592 case 0x04: // verify disk sectors
5593 vspt = read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
5594 vcylinders = read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders);
5595 vheads = read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads);
5597 ilba = read_dword(ebda_seg,&EbdaData->cdemu.ilba);
5599 sector = GET_CL() & 0x003f;
5600 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
5601 head = GET_DH();
5602 nbsectors = GET_AL();
5603 segment = ES;
5604 offset = BX;
5606 // no sector to read ?
5607 if(nbsectors==0) goto int13_success;
5609 // sanity checks sco openserver needs this!
5610 if ((sector > vspt)
5611 || (cylinder >= vcylinders)
5612 || (head >= vheads)) {
5613 goto int13_fail;
5616 // After controls, verify do nothing
5617 if (GET_AH() == 0x04) goto int13_success;
5619 segment = ES+(BX / 16);
5620 offset = BX % 16;
5622 // calculate the virtual lba inside the image
5623 vlba=((((Bit32u)cylinder*(Bit32u)vheads)+(Bit32u)head)*(Bit32u)vspt)+((Bit32u)(sector-1));
5625 // In advance so we don't loose the count
5626 SET_AL(nbsectors);
5628 // start lba on cd
5629 slba = (Bit32u)vlba/4;
5630 before= (Bit16u)vlba%4;
5632 // end lba on cd
5633 elba = (Bit32u)(vlba+nbsectors-1)/4;
5635 memsetb(get_SS(),atacmd,0,12);
5636 atacmd[0]=0x28; // READ command
5637 atacmd[7]=((Bit16u)(elba-slba+1) & 0xff00) >> 8; // Sectors
5638 atacmd[8]=((Bit16u)(elba-slba+1) & 0x00ff); // Sectors
5639 atacmd[2]=(ilba+slba & 0xff000000) >> 24; // LBA
5640 atacmd[3]=(ilba+slba & 0x00ff0000) >> 16;
5641 atacmd[4]=(ilba+slba & 0x0000ff00) >> 8;
5642 atacmd[5]=(ilba+slba & 0x000000ff);
5643 if((status = ata_cmd_packet(device, 12, get_SS(), atacmd, before*512, nbsectors*512L, ATA_DATA_IN, segment,offset)) != 0) {
5644 BX_INFO("int13_cdemu: function %02x, error %02x !\n",GET_AH(),status);
5645 SET_AH(0x02);
5646 SET_AL(0);
5647 goto int13_fail_noah;
5650 goto int13_success;
5651 break;
5653 case 0x08: /* read disk drive parameters */
5654 vspt=read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
5655 vcylinders=read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders) - 1;
5656 vheads=read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads) - 1;
5658 SET_AL( 0x00 );
5659 SET_BL( 0x00 );
5660 SET_CH( vcylinders & 0xff );
5661 SET_CL((( vcylinders >> 2) & 0xc0) | ( vspt & 0x3f ));
5662 SET_DH( vheads );
5663 SET_DL( 0x02 ); // FIXME ElTorito Various. should send the real count of drives 1 or 2
5664 // FIXME ElTorito Harddisk. should send the HD count
5666 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
5667 case 0x01: SET_BL( 0x02 ); break;
5668 case 0x02: SET_BL( 0x04 ); break;
5669 case 0x03: SET_BL( 0x06 ); break;
5672 ASM_START
5673 push bp
5674 mov bp, sp
5675 mov ax, #diskette_param_table2
5676 mov _int13_cdemu.DI+2[bp], ax
5677 mov _int13_cdemu.ES+2[bp], cs
5678 pop bp
5679 ASM_END
5680 goto int13_success;
5681 break;
5683 case 0x15: /* read disk drive size */
5684 // FIXME ElTorito Harddisk. What geometry to send ?
5685 SET_AH(0x03);
5686 goto int13_success_noah;
5687 break;
5689 // all those functions return unimplemented
5690 case 0x0a: /* read disk sectors with ECC */
5691 case 0x0b: /* write disk sectors with ECC */
5692 case 0x18: /* set media type for format */
5693 case 0x41: // IBM/MS installation check
5694 // FIXME ElTorito Harddisk. Darwin would like to use EDD
5695 case 0x42: // IBM/MS extended read
5696 case 0x43: // IBM/MS extended write
5697 case 0x44: // IBM/MS verify sectors
5698 case 0x45: // IBM/MS lock/unlock drive
5699 case 0x46: // IBM/MS eject media
5700 case 0x47: // IBM/MS extended seek
5701 case 0x48: // IBM/MS get drive parameters
5702 case 0x49: // IBM/MS extended media change
5703 case 0x4e: // ? - set hardware configuration
5704 case 0x50: // ? - send packet command
5705 default:
5706 BX_INFO("int13_cdemu function AH=%02x unsupported, returns fail\n", GET_AH());
5707 goto int13_fail;
5708 break;
5711 int13_fail:
5712 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5713 int13_fail_noah:
5714 SET_DISK_RET_STATUS(GET_AH());
5715 int13_fail_nostatus:
5716 SET_CF(); // error occurred
5717 return;
5719 int13_success:
5720 SET_AH(0x00); // no error
5721 int13_success_noah:
5722 SET_DISK_RET_STATUS(0x00);
5723 CLEAR_CF(); // no error
5724 return;
5727 // ---------------------------------------------------------------------------
5728 // End of int13 when emulating a device from the cd
5729 // ---------------------------------------------------------------------------
5731 #endif // BX_ELTORITO_BOOT
5733 #else //BX_USE_ATADRV
5735 void
5736 outLBA(cylinder,hd_heads,head,hd_sectors,sector,dl)
5737 Bit16u cylinder;
5738 Bit16u hd_heads;
5739 Bit16u head;
5740 Bit16u hd_sectors;
5741 Bit16u sector;
5742 Bit16u dl;
5744 ASM_START
5745 push bp
5746 mov bp, sp
5747 push eax
5748 push ebx
5749 push edx
5750 xor eax,eax
5751 mov ax,4[bp] // cylinder
5752 xor ebx,ebx
5753 mov bl,6[bp] // hd_heads
5754 imul ebx
5756 mov bl,8[bp] // head
5757 add eax,ebx
5758 mov bl,10[bp] // hd_sectors
5759 imul ebx
5760 mov bl,12[bp] // sector
5761 add eax,ebx
5763 dec eax
5764 mov dx,#0x1f3
5765 out dx,al
5766 mov dx,#0x1f4
5767 mov al,ah
5768 out dx,al
5769 shr eax,#16
5770 mov dx,#0x1f5
5771 out dx,al
5772 and ah,#0xf
5773 mov bl,14[bp] // dl
5774 and bl,#1
5775 shl bl,#4
5776 or ah,bl
5777 or ah,#0xe0
5778 mov al,ah
5779 mov dx,#0x01f6
5780 out dx,al
5781 pop edx
5782 pop ebx
5783 pop eax
5784 pop bp
5785 ASM_END
5788 void
5789 int13_harddisk(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
5790 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
5792 Bit8u drive, num_sectors, sector, head, status, mod;
5793 Bit8u drive_map;
5794 Bit8u n_drives;
5795 Bit16u cyl_mod, ax;
5796 Bit16u max_cylinder, cylinder, total_sectors;
5797 Bit16u hd_cylinders;
5798 Bit8u hd_heads, hd_sectors;
5799 Bit16u val16;
5800 Bit8u sector_count;
5801 unsigned int i;
5802 Bit16u tempbx;
5803 Bit16u dpsize;
5805 Bit16u count, segment, offset;
5806 Bit32u lba;
5807 Bit16u error;
5809 BX_DEBUG_INT13_HD("int13 harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5811 write_byte(0x0040, 0x008e, 0); // clear completion flag
5813 /* at this point, DL is >= 0x80 to be passed from the floppy int13h
5814 handler code */
5815 /* check how many disks first (cmos reg 0x12), return an error if
5816 drive not present */
5817 drive_map = inb_cmos(0x12);
5818 drive_map = (((drive_map & 0xf0)==0) ? 0 : 1) |
5819 (((drive_map & 0x0f)==0) ? 0 : 2);
5820 n_drives = (drive_map==0) ? 0 :
5821 ((drive_map==3) ? 2 : 1);
5823 if (!(drive_map & (1<<(GET_ELDL()&0x7f)))) { /* allow 0, 1, or 2 disks */
5824 SET_AH(0x01);
5825 SET_DISK_RET_STATUS(0x01);
5826 SET_CF(); /* error occurred */
5827 return;
5830 switch (GET_AH()) {
5832 case 0x00: /* disk controller reset */
5833 BX_DEBUG_INT13_HD("int13_f00\n");
5835 SET_AH(0);
5836 SET_DISK_RET_STATUS(0);
5837 set_diskette_ret_status(0);
5838 set_diskette_current_cyl(0, 0); /* current cylinder, diskette 1 */
5839 set_diskette_current_cyl(1, 0); /* current cylinder, diskette 2 */
5840 CLEAR_CF(); /* successful */
5841 return;
5842 break;
5844 case 0x01: /* read disk status */
5845 BX_DEBUG_INT13_HD("int13_f01\n");
5846 status = read_byte(0x0040, 0x0074);
5847 SET_AH(status);
5848 SET_DISK_RET_STATUS(0);
5849 /* set CF if error status read */
5850 if (status) SET_CF();
5851 else CLEAR_CF();
5852 return;
5853 break;
5855 case 0x04: // verify disk sectors
5856 case 0x02: // read disk sectors
5857 drive = GET_ELDL();
5858 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
5860 num_sectors = GET_AL();
5861 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
5862 sector = (GET_CL() & 0x3f);
5863 head = GET_DH();
5866 if (hd_cylinders > 1024) {
5867 if (hd_cylinders <= 2048) {
5868 cylinder <<= 1;
5870 else if (hd_cylinders <= 4096) {
5871 cylinder <<= 2;
5873 else if (hd_cylinders <= 8192) {
5874 cylinder <<= 3;
5876 else { // hd_cylinders <= 16384
5877 cylinder <<= 4;
5880 ax = head / hd_heads;
5881 cyl_mod = ax & 0xff;
5882 head = ax >> 8;
5883 cylinder |= cyl_mod;
5886 if ( (cylinder >= hd_cylinders) ||
5887 (sector > hd_sectors) ||
5888 (head >= hd_heads) ) {
5889 SET_AH(1);
5890 SET_DISK_RET_STATUS(1);
5891 SET_CF(); /* error occurred */
5892 return;
5895 if ( (num_sectors > 128) || (num_sectors == 0) )
5896 BX_PANIC("int13_harddisk(): num_sectors out of range!\n");
5898 if (head > 15)
5899 BX_PANIC("hard drive BIOS:(read/verify) head > 15\n");
5901 if ( GET_AH() == 0x04 ) {
5902 SET_AH(0);
5903 SET_DISK_RET_STATUS(0);
5904 CLEAR_CF();
5905 return;
5908 status = inb(0x1f7);
5909 if (status & 0x80) {
5910 BX_PANIC("hard drive BIOS:(read/verify) BUSY bit set\n");
5912 outb(0x01f2, num_sectors);
5913 /* activate LBA? (tomv) */
5914 if (hd_heads > 16) {
5915 BX_DEBUG_INT13_HD("CHS: %x %x %x\n", cylinder, head, sector);
5916 outLBA(cylinder,hd_heads,head,hd_sectors,sector,drive);
5918 else {
5919 outb(0x01f3, sector);
5920 outb(0x01f4, cylinder & 0x00ff);
5921 outb(0x01f5, cylinder >> 8);
5922 outb(0x01f6, 0xa0 | ((drive & 0x01)<<4) | (head & 0x0f));
5924 outb(0x01f7, 0x20);
5926 while (1) {
5927 status = inb(0x1f7);
5928 if ( !(status & 0x80) ) break;
5931 if (status & 0x01) {
5932 BX_PANIC("hard drive BIOS:(read/verify) read error\n");
5933 } else if ( !(status & 0x08) ) {
5934 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
5935 BX_PANIC("hard drive BIOS:(read/verify) expected DRQ=1\n");
5938 sector_count = 0;
5939 tempbx = BX;
5941 ASM_START
5942 sti ;; enable higher priority interrupts
5943 ASM_END
5945 while (1) {
5946 ASM_START
5947 ;; store temp bx in real DI register
5948 push bp
5949 mov bp, sp
5950 mov di, _int13_harddisk.tempbx + 2 [bp]
5951 pop bp
5953 ;; adjust if there will be an overrun
5954 cmp di, #0xfe00
5955 jbe i13_f02_no_adjust
5956 i13_f02_adjust:
5957 sub di, #0x0200 ; sub 512 bytes from offset
5958 mov ax, es
5959 add ax, #0x0020 ; add 512 to segment
5960 mov es, ax
5962 i13_f02_no_adjust:
5963 mov cx, #0x0100 ;; counter (256 words = 512b)
5964 mov dx, #0x01f0 ;; AT data read port
5967 insw ;; CX words transfered from port(DX) to ES:[DI]
5969 i13_f02_done:
5970 ;; store real DI register back to temp bx
5971 push bp
5972 mov bp, sp
5973 mov _int13_harddisk.tempbx + 2 [bp], di
5974 pop bp
5975 ASM_END
5977 sector_count++;
5978 num_sectors--;
5979 if (num_sectors == 0) {
5980 status = inb(0x1f7);
5981 if ( (status & 0xc9) != 0x40 )
5982 BX_PANIC("no sectors left to read/verify, status is %02x\n", (unsigned) status);
5983 break;
5985 else {
5986 status = inb(0x1f7);
5987 if ( (status & 0xc9) != 0x48 )
5988 BX_PANIC("more sectors left to read/verify, status is %02x\n", (unsigned) status);
5989 continue;
5993 SET_AH(0);
5994 SET_DISK_RET_STATUS(0);
5995 SET_AL(sector_count);
5996 CLEAR_CF(); /* successful */
5997 return;
5998 break;
6001 case 0x03: /* write disk sectors */
6002 BX_DEBUG_INT13_HD("int13_f03\n");
6003 drive = GET_ELDL ();
6004 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6006 num_sectors = GET_AL();
6007 cylinder = GET_CH();
6008 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
6009 sector = (GET_CL() & 0x3f);
6010 head = GET_DH();
6012 if (hd_cylinders > 1024) {
6013 if (hd_cylinders <= 2048) {
6014 cylinder <<= 1;
6016 else if (hd_cylinders <= 4096) {
6017 cylinder <<= 2;
6019 else if (hd_cylinders <= 8192) {
6020 cylinder <<= 3;
6022 else { // hd_cylinders <= 16384
6023 cylinder <<= 4;
6026 ax = head / hd_heads;
6027 cyl_mod = ax & 0xff;
6028 head = ax >> 8;
6029 cylinder |= cyl_mod;
6032 if ( (cylinder >= hd_cylinders) ||
6033 (sector > hd_sectors) ||
6034 (head >= hd_heads) ) {
6035 SET_AH( 1);
6036 SET_DISK_RET_STATUS(1);
6037 SET_CF(); /* error occurred */
6038 return;
6041 if ( (num_sectors > 128) || (num_sectors == 0) )
6042 BX_PANIC("int13_harddisk(): num_sectors out of range!\n");
6044 if (head > 15)
6045 BX_PANIC("hard drive BIOS:(read) head > 15\n");
6047 status = inb(0x1f7);
6048 if (status & 0x80) {
6049 BX_PANIC("hard drive BIOS:(read) BUSY bit set\n");
6051 // should check for Drive Ready Bit also in status reg
6052 outb(0x01f2, num_sectors);
6054 /* activate LBA? (tomv) */
6055 if (hd_heads > 16) {
6056 BX_DEBUG_INT13_HD("CHS (write): %x %x %x\n", cylinder, head, sector);
6057 outLBA(cylinder,hd_heads,head,hd_sectors,sector,GET_ELDL());
6059 else {
6060 outb(0x01f3, sector);
6061 outb(0x01f4, cylinder & 0x00ff);
6062 outb(0x01f5, cylinder >> 8);
6063 outb(0x01f6, 0xa0 | ((GET_ELDL() & 0x01)<<4) | (head & 0x0f));
6065 outb(0x01f7, 0x30);
6067 // wait for busy bit to turn off after seeking
6068 while (1) {
6069 status = inb(0x1f7);
6070 if ( !(status & 0x80) ) break;
6073 if ( !(status & 0x08) ) {
6074 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
6075 BX_PANIC("hard drive BIOS:(write) data-request bit not set\n");
6078 sector_count = 0;
6079 tempbx = BX;
6081 ASM_START
6082 sti ;; enable higher priority interrupts
6083 ASM_END
6085 while (1) {
6086 ASM_START
6087 ;; store temp bx in real SI register
6088 push bp
6089 mov bp, sp
6090 mov si, _int13_harddisk.tempbx + 2 [bp]
6091 pop bp
6093 ;; adjust if there will be an overrun
6094 cmp si, #0xfe00
6095 jbe i13_f03_no_adjust
6096 i13_f03_adjust:
6097 sub si, #0x0200 ; sub 512 bytes from offset
6098 mov ax, es
6099 add ax, #0x0020 ; add 512 to segment
6100 mov es, ax
6102 i13_f03_no_adjust:
6103 mov cx, #0x0100 ;; counter (256 words = 512b)
6104 mov dx, #0x01f0 ;; AT data read port
6106 seg ES
6108 outsw ;; CX words tranfered from ES:[SI] to port(DX)
6110 ;; store real SI register back to temp bx
6111 push bp
6112 mov bp, sp
6113 mov _int13_harddisk.tempbx + 2 [bp], si
6114 pop bp
6115 ASM_END
6117 sector_count++;
6118 num_sectors--;
6119 if (num_sectors == 0) {
6120 status = inb(0x1f7);
6121 if ( (status & 0xe9) != 0x40 )
6122 BX_PANIC("no sectors left to write, status is %02x\n", (unsigned) status);
6123 break;
6125 else {
6126 status = inb(0x1f7);
6127 if ( (status & 0xc9) != 0x48 )
6128 BX_PANIC("more sectors left to write, status is %02x\n", (unsigned) status);
6129 continue;
6133 SET_AH(0);
6134 SET_DISK_RET_STATUS(0);
6135 SET_AL(sector_count);
6136 CLEAR_CF(); /* successful */
6137 return;
6138 break;
6140 case 0x05: /* format disk track */
6141 BX_DEBUG_INT13_HD("int13_f05\n");
6142 BX_PANIC("format disk track called\n");
6143 /* nop */
6144 SET_AH(0);
6145 SET_DISK_RET_STATUS(0);
6146 CLEAR_CF(); /* successful */
6147 return;
6148 break;
6150 case 0x08: /* read disk drive parameters */
6151 BX_DEBUG_INT13_HD("int13_f08\n");
6153 drive = GET_ELDL ();
6154 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6156 // translate CHS
6158 if (hd_cylinders <= 1024) {
6159 // hd_cylinders >>= 0;
6160 // hd_heads <<= 0;
6162 else if (hd_cylinders <= 2048) {
6163 hd_cylinders >>= 1;
6164 hd_heads <<= 1;
6166 else if (hd_cylinders <= 4096) {
6167 hd_cylinders >>= 2;
6168 hd_heads <<= 2;
6170 else if (hd_cylinders <= 8192) {
6171 hd_cylinders >>= 3;
6172 hd_heads <<= 3;
6174 else { // hd_cylinders <= 16384
6175 hd_cylinders >>= 4;
6176 hd_heads <<= 4;
6179 max_cylinder = hd_cylinders - 2; /* 0 based */
6180 SET_AL(0);
6181 SET_CH(max_cylinder & 0xff);
6182 SET_CL(((max_cylinder >> 2) & 0xc0) | (hd_sectors & 0x3f));
6183 SET_DH(hd_heads - 1);
6184 SET_DL(n_drives); /* returns 0, 1, or 2 hard drives */
6185 SET_AH(0);
6186 SET_DISK_RET_STATUS(0);
6187 CLEAR_CF(); /* successful */
6189 return;
6190 break;
6192 case 0x09: /* initialize drive parameters */
6193 BX_DEBUG_INT13_HD("int13_f09\n");
6194 SET_AH(0);
6195 SET_DISK_RET_STATUS(0);
6196 CLEAR_CF(); /* successful */
6197 return;
6198 break;
6200 case 0x0a: /* read disk sectors with ECC */
6201 BX_DEBUG_INT13_HD("int13_f0a\n");
6202 case 0x0b: /* write disk sectors with ECC */
6203 BX_DEBUG_INT13_HD("int13_f0b\n");
6204 BX_PANIC("int13h Functions 0Ah & 0Bh not implemented!\n");
6205 return;
6206 break;
6208 case 0x0c: /* seek to specified cylinder */
6209 BX_DEBUG_INT13_HD("int13_f0c\n");
6210 BX_INFO("int13h function 0ch (seek) not implemented!\n");
6211 SET_AH(0);
6212 SET_DISK_RET_STATUS(0);
6213 CLEAR_CF(); /* successful */
6214 return;
6215 break;
6217 case 0x0d: /* alternate disk reset */
6218 BX_DEBUG_INT13_HD("int13_f0d\n");
6219 SET_AH(0);
6220 SET_DISK_RET_STATUS(0);
6221 CLEAR_CF(); /* successful */
6222 return;
6223 break;
6225 case 0x10: /* check drive ready */
6226 BX_DEBUG_INT13_HD("int13_f10\n");
6227 //SET_AH(0);
6228 //SET_DISK_RET_STATUS(0);
6229 //CLEAR_CF(); /* successful */
6230 //return;
6231 //break;
6233 // should look at 40:8E also???
6234 status = inb(0x01f7);
6235 if ( (status & 0xc0) == 0x40 ) {
6236 SET_AH(0);
6237 SET_DISK_RET_STATUS(0);
6238 CLEAR_CF(); // drive ready
6239 return;
6241 else {
6242 SET_AH(0xAA);
6243 SET_DISK_RET_STATUS(0xAA);
6244 SET_CF(); // not ready
6245 return;
6247 break;
6249 case 0x11: /* recalibrate */
6250 BX_DEBUG_INT13_HD("int13_f11\n");
6251 SET_AH(0);
6252 SET_DISK_RET_STATUS(0);
6253 CLEAR_CF(); /* successful */
6254 return;
6255 break;
6257 case 0x14: /* controller internal diagnostic */
6258 BX_DEBUG_INT13_HD("int13_f14\n");
6259 SET_AH(0);
6260 SET_DISK_RET_STATUS(0);
6261 CLEAR_CF(); /* successful */
6262 SET_AL(0);
6263 return;
6264 break;
6266 case 0x15: /* read disk drive size */
6267 drive = GET_ELDL();
6268 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6269 ASM_START
6270 push bp
6271 mov bp, sp
6272 mov al, _int13_harddisk.hd_heads + 2 [bp]
6273 mov ah, _int13_harddisk.hd_sectors + 2 [bp]
6274 mul al, ah ;; ax = heads * sectors
6275 mov bx, _int13_harddisk.hd_cylinders + 2 [bp]
6276 dec bx ;; use (cylinders - 1) ???
6277 mul ax, bx ;; dx:ax = (cylinders -1) * (heads * sectors)
6278 ;; now we need to move the 32bit result dx:ax to what the
6279 ;; BIOS wants which is cx:dx.
6280 ;; and then into CX:DX on the stack
6281 mov _int13_harddisk.CX + 2 [bp], dx
6282 mov _int13_harddisk.DX + 2 [bp], ax
6283 pop bp
6284 ASM_END
6285 SET_AH(3); // hard disk accessible
6286 SET_DISK_RET_STATUS(0); // ??? should this be 0
6287 CLEAR_CF(); // successful
6288 return;
6289 break;
6291 case 0x18: // set media type for format
6292 case 0x41: // IBM/MS
6293 case 0x42: // IBM/MS
6294 case 0x43: // IBM/MS
6295 case 0x44: // IBM/MS
6296 case 0x45: // IBM/MS lock/unlock drive
6297 case 0x46: // IBM/MS eject media
6298 case 0x47: // IBM/MS extended seek
6299 case 0x49: // IBM/MS extended media change
6300 case 0x50: // IBM/MS send packet command
6301 default:
6302 BX_INFO("int13_harddisk: unsupported AH=%02x\n", GET_AH());
6304 SET_AH(1); // code=invalid function in AH or invalid parameter
6305 SET_DISK_RET_STATUS(1);
6306 SET_CF(); /* unsuccessful */
6307 return;
6308 break;
6312 static char panic_msg_reg12h[] = "HD%d cmos reg 12h not type F\n";
6313 static char panic_msg_reg19h[] = "HD%d cmos reg %02xh not user definable type 47\n";
6315 void
6316 get_hd_geometry(drive, hd_cylinders, hd_heads, hd_sectors)
6317 Bit8u drive;
6318 Bit16u *hd_cylinders;
6319 Bit8u *hd_heads;
6320 Bit8u *hd_sectors;
6322 Bit8u hd_type;
6323 Bit16u ss;
6324 Bit16u cylinders;
6325 Bit8u iobase;
6327 ss = get_SS();
6328 if (drive == 0x80) {
6329 hd_type = inb_cmos(0x12) & 0xf0;
6330 if (hd_type != 0xf0)
6331 BX_INFO(panic_msg_reg12h,0);
6332 hd_type = inb_cmos(0x19); // HD0: extended type
6333 if (hd_type != 47)
6334 BX_INFO(panic_msg_reg19h,0,0x19);
6335 iobase = 0x1b;
6336 } else {
6337 hd_type = inb_cmos(0x12) & 0x0f;
6338 if (hd_type != 0x0f)
6339 BX_INFO(panic_msg_reg12h,1);
6340 hd_type = inb_cmos(0x1a); // HD0: extended type
6341 if (hd_type != 47)
6342 BX_INFO(panic_msg_reg19h,0,0x1a);
6343 iobase = 0x24;
6346 // cylinders
6347 cylinders = inb_cmos(iobase) | (inb_cmos(iobase+1) << 8);
6348 write_word(ss, hd_cylinders, cylinders);
6350 // heads
6351 write_byte(ss, hd_heads, inb_cmos(iobase+2));
6353 // sectors per track
6354 write_byte(ss, hd_sectors, inb_cmos(iobase+8));
6357 #endif //else BX_USE_ATADRV
6360 //////////////////////
6361 // FLOPPY functions //
6362 //////////////////////
6364 void
6365 floppy_reset_controller()
6367 Bit8u val8;
6369 // Reset controller
6370 val8 = inb(0x03f2);
6371 outb(0x03f2, val8 & ~0x04);
6372 outb(0x03f2, val8 | 0x04);
6374 // Wait for controller to come out of reset
6375 val8 = inb(0x3f4);
6376 while ( (val8 & 0xc0) != 0x80 ) {
6377 val8 = inb(0x3f4);
6381 bx_bool
6382 floppy_media_known(drive)
6383 Bit16u drive;
6385 Bit8u val8;
6386 Bit16u media_state_offset;
6388 val8 = read_byte(0x0040, 0x003e); // diskette recal status
6389 if (drive)
6390 val8 >>= 1;
6391 val8 &= 0x01;
6392 if (val8 == 0)
6393 return(0);
6395 media_state_offset = 0x0090;
6396 if (drive)
6397 media_state_offset += 1;
6399 val8 = read_byte(0x0040, media_state_offset);
6400 val8 = (val8 >> 4) & 0x01;
6401 if (val8 == 0)
6402 return(0);
6404 // check pass, return KNOWN
6405 return(1);
6408 bx_bool
6409 floppy_media_sense(drive)
6410 Bit16u drive;
6412 bx_bool retval;
6413 Bit16u media_state_offset;
6414 Bit8u drive_type, config_data, media_state;
6416 if (floppy_drive_recal(drive) == 0) {
6417 return(0);
6420 // for now cheat and get drive type from CMOS,
6421 // assume media is same as drive type
6423 // ** config_data **
6424 // Bitfields for diskette media control:
6425 // Bit(s) Description (Table M0028)
6426 // 7-6 last data rate set by controller
6427 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6428 // 5-4 last diskette drive step rate selected
6429 // 00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
6430 // 3-2 {data rate at start of operation}
6431 // 1-0 reserved
6433 // ** media_state **
6434 // Bitfields for diskette drive media state:
6435 // Bit(s) Description (Table M0030)
6436 // 7-6 data rate
6437 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6438 // 5 double stepping required (e.g. 360kB in 1.2MB)
6439 // 4 media type established
6440 // 3 drive capable of supporting 4MB media
6441 // 2-0 on exit from BIOS, contains
6442 // 000 trying 360kB in 360kB
6443 // 001 trying 360kB in 1.2MB
6444 // 010 trying 1.2MB in 1.2MB
6445 // 011 360kB in 360kB established
6446 // 100 360kB in 1.2MB established
6447 // 101 1.2MB in 1.2MB established
6448 // 110 reserved
6449 // 111 all other formats/drives
6451 drive_type = inb_cmos(0x10);
6452 if (drive == 0)
6453 drive_type >>= 4;
6454 else
6455 drive_type &= 0x0f;
6456 if ( drive_type == 1 ) {
6457 // 360K 5.25" drive
6458 config_data = 0x00; // 0000 0000
6459 media_state = 0x25; // 0010 0101
6460 retval = 1;
6462 else if ( drive_type == 2 ) {
6463 // 1.2 MB 5.25" drive
6464 config_data = 0x00; // 0000 0000
6465 media_state = 0x25; // 0010 0101 // need double stepping??? (bit 5)
6466 retval = 1;
6468 else if ( drive_type == 3 ) {
6469 // 720K 3.5" drive
6470 config_data = 0x00; // 0000 0000 ???
6471 media_state = 0x17; // 0001 0111
6472 retval = 1;
6474 else if ( drive_type == 4 ) {
6475 // 1.44 MB 3.5" drive
6476 config_data = 0x00; // 0000 0000
6477 media_state = 0x17; // 0001 0111
6478 retval = 1;
6480 else if ( drive_type == 5 ) {
6481 // 2.88 MB 3.5" drive
6482 config_data = 0xCC; // 1100 1100
6483 media_state = 0xD7; // 1101 0111
6484 retval = 1;
6487 // Extended floppy size uses special cmos setting
6488 else if ( drive_type == 6 ) {
6489 // 160k 5.25" drive
6490 config_data = 0x00; // 0000 0000
6491 media_state = 0x27; // 0010 0111
6492 retval = 1;
6494 else if ( drive_type == 7 ) {
6495 // 180k 5.25" drive
6496 config_data = 0x00; // 0000 0000
6497 media_state = 0x27; // 0010 0111
6498 retval = 1;
6500 else if ( drive_type == 8 ) {
6501 // 320k 5.25" drive
6502 config_data = 0x00; // 0000 0000
6503 media_state = 0x27; // 0010 0111
6504 retval = 1;
6507 else {
6508 // not recognized
6509 config_data = 0x00; // 0000 0000
6510 media_state = 0x00; // 0000 0000
6511 retval = 0;
6514 if (drive == 0)
6515 media_state_offset = 0x90;
6516 else
6517 media_state_offset = 0x91;
6518 write_byte(0x0040, 0x008B, config_data);
6519 write_byte(0x0040, media_state_offset, media_state);
6521 return(retval);
6524 bx_bool
6525 floppy_drive_recal(drive)
6526 Bit16u drive;
6528 Bit8u val8, dor, prev_reset;
6529 Bit16u curr_cyl_offset;
6531 // set 40:3e bit 7 to 0
6532 val8 = read_byte(0x0040, 0x003e);
6533 val8 &= 0x7f;
6534 write_byte(0x0040, 0x003e, val8);
6536 // turn on motor of selected drive, DMA & int enabled, normal operation
6537 prev_reset = inb(0x03f2) & 0x04;
6538 if (drive)
6539 dor = 0x20;
6540 else
6541 dor = 0x10;
6542 dor |= 0x0c;
6543 dor |= drive;
6544 outb(0x03f2, dor);
6546 // reset the disk motor timeout value of INT 08
6547 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
6549 // wait for drive readiness
6550 do {
6551 val8 = inb(0x3f4);
6552 } while ( (val8 & 0xc0) != 0x80 );
6554 if (prev_reset == 0) {
6555 // turn on interrupts
6556 ASM_START
6558 ASM_END
6559 // wait on 40:3e bit 7 to become 1
6560 do {
6561 val8 = read_byte(0x0040, 0x003e);
6562 } while ( (val8 & 0x80) == 0 );
6563 val8 &= 0x7f;
6564 write_byte(0x0040, 0x003e, val8);
6567 // send Recalibrate command (2 bytes) to controller
6568 outb(0x03f5, 0x07); // 07: Recalibrate
6569 outb(0x03f5, drive); // 0=drive0, 1=drive1
6571 // turn on interrupts
6572 ASM_START
6574 ASM_END
6576 // wait on 40:3e bit 7 to become 1
6577 do {
6578 val8 = (read_byte(0x0040, 0x003e) & 0x80);
6579 } while ( val8 == 0 );
6581 val8 = 0; // separate asm from while() loop
6582 // turn off interrupts
6583 ASM_START
6585 ASM_END
6587 // set 40:3e bit 7 to 0, and calibrated bit
6588 val8 = read_byte(0x0040, 0x003e);
6589 val8 &= 0x7f;
6590 if (drive) {
6591 val8 |= 0x02; // Drive 1 calibrated
6592 curr_cyl_offset = 0x0095;
6594 else {
6595 val8 |= 0x01; // Drive 0 calibrated
6596 curr_cyl_offset = 0x0094;
6598 write_byte(0x0040, 0x003e, val8);
6599 write_byte(0x0040, curr_cyl_offset, 0); // current cylinder is 0
6601 return(1);
6606 bx_bool
6607 floppy_drive_exists(drive)
6608 Bit16u drive;
6610 Bit8u drive_type;
6612 // check CMOS to see if drive exists
6613 drive_type = inb_cmos(0x10);
6614 if (drive == 0)
6615 drive_type >>= 4;
6616 else
6617 drive_type &= 0x0f;
6618 if ( drive_type == 0 )
6619 return(0);
6620 else
6621 return(1);
6624 #if BX_SUPPORT_FLOPPY
6625 void
6626 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
6627 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
6629 Bit8u drive, num_sectors, track, sector, head, status;
6630 Bit16u base_address, base_count, base_es;
6631 Bit8u page, mode_register, val8, dor;
6632 Bit8u return_status[7];
6633 Bit8u drive_type, num_floppies, ah;
6634 Bit16u es, last_addr;
6636 BX_DEBUG_INT13_FL("int13_diskette: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
6637 // BX_DEBUG_INT13_FL("int13_diskette: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), get_DS(), ES, DI, SI);
6639 ah = GET_AH();
6641 switch ( ah ) {
6642 case 0x00: // diskette controller reset
6643 BX_DEBUG_INT13_FL("floppy f00\n");
6644 drive = GET_ELDL();
6645 if (drive > 1) {
6646 SET_AH(1); // invalid param
6647 set_diskette_ret_status(1);
6648 SET_CF();
6649 return;
6651 drive_type = inb_cmos(0x10);
6653 if (drive == 0)
6654 drive_type >>= 4;
6655 else
6656 drive_type &= 0x0f;
6657 if (drive_type == 0) {
6658 SET_AH(0x80); // drive not responding
6659 set_diskette_ret_status(0x80);
6660 SET_CF();
6661 return;
6663 SET_AH(0);
6664 set_diskette_ret_status(0);
6665 CLEAR_CF(); // successful
6666 set_diskette_current_cyl(drive, 0); // current cylinder
6667 return;
6669 case 0x01: // Read Diskette Status
6670 CLEAR_CF();
6671 val8 = read_byte(0x0000, 0x0441);
6672 SET_AH(val8);
6673 if (val8) {
6674 SET_CF();
6676 return;
6678 case 0x02: // Read Diskette Sectors
6679 case 0x03: // Write Diskette Sectors
6680 case 0x04: // Verify Diskette Sectors
6681 num_sectors = GET_AL();
6682 track = GET_CH();
6683 sector = GET_CL();
6684 head = GET_DH();
6685 drive = GET_ELDL();
6687 if ( (drive > 1) || (head > 1) ||
6688 (num_sectors == 0) || (num_sectors > 72) ) {
6689 BX_INFO("floppy: drive>1 || head>1 ...\n");
6690 SET_AH(1);
6691 set_diskette_ret_status(1);
6692 SET_AL(0); // no sectors read
6693 SET_CF(); // error occurred
6694 return;
6697 // see if drive exists
6698 if (floppy_drive_exists(drive) == 0) {
6699 SET_AH(0x80); // not responding
6700 set_diskette_ret_status(0x80);
6701 SET_AL(0); // no sectors read
6702 SET_CF(); // error occurred
6703 return;
6706 // see if media in drive, and type is known
6707 if (floppy_media_known(drive) == 0) {
6708 if (floppy_media_sense(drive) == 0) {
6709 SET_AH(0x0C); // Media type not found
6710 set_diskette_ret_status(0x0C);
6711 SET_AL(0); // no sectors read
6712 SET_CF(); // error occurred
6713 return;
6717 if (ah == 0x02) {
6718 // Read Diskette Sectors
6720 //-----------------------------------
6721 // set up DMA controller for transfer
6722 //-----------------------------------
6724 // es:bx = pointer to where to place information from diskette
6725 // port 04: DMA-1 base and current address, channel 2
6726 // port 05: DMA-1 base and current count, channel 2
6727 page = (ES >> 12); // upper 4 bits
6728 base_es = (ES << 4); // lower 16bits contributed by ES
6729 base_address = base_es + BX; // lower 16 bits of address
6730 // contributed by ES:BX
6731 if ( base_address < base_es ) {
6732 // in case of carry, adjust page by 1
6733 page++;
6735 base_count = (num_sectors * 512) - 1;
6737 // check for 64K boundary overrun
6738 last_addr = base_address + base_count;
6739 if (last_addr < base_address) {
6740 SET_AH(0x09);
6741 set_diskette_ret_status(0x09);
6742 SET_AL(0); // no sectors read
6743 SET_CF(); // error occurred
6744 return;
6747 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
6748 outb(0x000a, 0x06);
6750 BX_DEBUG_INT13_FL("clear flip-flop\n");
6751 outb(0x000c, 0x00); // clear flip-flop
6752 outb(0x0004, base_address);
6753 outb(0x0004, base_address>>8);
6754 BX_DEBUG_INT13_FL("clear flip-flop\n");
6755 outb(0x000c, 0x00); // clear flip-flop
6756 outb(0x0005, base_count);
6757 outb(0x0005, base_count>>8);
6759 // port 0b: DMA-1 Mode Register
6760 mode_register = 0x46; // single mode, increment, autoinit disable,
6761 // transfer type=write, channel 2
6762 BX_DEBUG_INT13_FL("setting mode register\n");
6763 outb(0x000b, mode_register);
6765 BX_DEBUG_INT13_FL("setting page register\n");
6766 // port 81: DMA-1 Page Register, channel 2
6767 outb(0x0081, page);
6769 BX_DEBUG_INT13_FL("unmask chan 2\n");
6770 outb(0x000a, 0x02); // unmask channel 2
6772 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
6773 outb(0x000a, 0x02);
6775 //--------------------------------------
6776 // set up floppy controller for transfer
6777 //--------------------------------------
6779 // set 40:3e bit 7 to 0
6780 val8 = read_byte(0x0040, 0x003e);
6781 val8 &= 0x7f;
6782 write_byte(0x0040, 0x003e, val8);
6784 // turn on motor of selected drive, DMA & int enabled, normal operation
6785 if (drive)
6786 dor = 0x20;
6787 else
6788 dor = 0x10;
6789 dor |= 0x0c;
6790 dor |= drive;
6791 outb(0x03f2, dor);
6793 // reset the disk motor timeout value of INT 08
6794 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
6796 // check port 3f4 for drive readiness
6797 val8 = inb(0x3f4);
6798 if ( (val8 & 0xf0) != 0x80 )
6799 BX_PANIC("int13_diskette:f02: ctrl not ready\n");
6801 // send read-normal-data command (9 bytes) to controller
6802 outb(0x03f5, 0xe6); // e6: read normal data
6803 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
6804 outb(0x03f5, track);
6805 outb(0x03f5, head);
6806 outb(0x03f5, sector);
6807 outb(0x03f5, 2); // 512 byte sector size
6808 outb(0x03f5, 0); // last sector number possible on track
6809 outb(0x03f5, 0); // Gap length
6810 outb(0x03f5, 0xff); // Gap length
6812 // turn on interrupts
6813 ASM_START
6815 ASM_END
6817 // wait on 40:3e bit 7 to become 1
6818 do {
6819 val8 = read_byte(0x0040, 0x0040);
6820 if (val8 == 0) {
6821 floppy_reset_controller();
6822 SET_AH(0x80); // drive not ready (timeout)
6823 set_diskette_ret_status(0x80);
6824 SET_AL(0); // no sectors read
6825 SET_CF(); // error occurred
6826 return;
6828 val8 = (read_byte(0x0040, 0x003e) & 0x80);
6829 } while ( val8 == 0 );
6831 val8 = 0; // separate asm from while() loop
6832 // turn off interrupts
6833 ASM_START
6835 ASM_END
6837 // set 40:3e bit 7 to 0
6838 val8 = read_byte(0x0040, 0x003e);
6839 val8 &= 0x7f;
6840 write_byte(0x0040, 0x003e, val8);
6842 // check port 3f4 for accessibility to status bytes
6843 val8 = inb(0x3f4);
6844 if ( (val8 & 0xc0) != 0xc0 )
6845 BX_PANIC("int13_diskette: ctrl not ready\n");
6847 // read 7 return status bytes from controller
6848 // using loop index broken, have to unroll...
6849 return_status[0] = inb(0x3f5);
6850 return_status[1] = inb(0x3f5);
6851 return_status[2] = inb(0x3f5);
6852 return_status[3] = inb(0x3f5);
6853 return_status[4] = inb(0x3f5);
6854 return_status[5] = inb(0x3f5);
6855 return_status[6] = inb(0x3f5);
6856 // record in BIOS Data Area
6857 write_byte(0x0040, 0x0042, return_status[0]);
6858 write_byte(0x0040, 0x0043, return_status[1]);
6859 write_byte(0x0040, 0x0044, return_status[2]);
6860 write_byte(0x0040, 0x0045, return_status[3]);
6861 write_byte(0x0040, 0x0046, return_status[4]);
6862 write_byte(0x0040, 0x0047, return_status[5]);
6863 write_byte(0x0040, 0x0048, return_status[6]);
6865 if ( (return_status[0] & 0xc0) != 0 ) {
6866 SET_AH(0x20);
6867 set_diskette_ret_status(0x20);
6868 SET_AL(0); // no sectors read
6869 SET_CF(); // error occurred
6870 return;
6873 // ??? should track be new val from return_status[3] ?
6874 set_diskette_current_cyl(drive, track);
6875 // AL = number of sectors read (same value as passed)
6876 SET_AH(0x00); // success
6877 CLEAR_CF(); // success
6878 return;
6880 else if (ah == 0x03) {
6881 // Write Diskette Sectors
6883 //-----------------------------------
6884 // set up DMA controller for transfer
6885 //-----------------------------------
6887 // es:bx = pointer to where to place information from diskette
6888 // port 04: DMA-1 base and current address, channel 2
6889 // port 05: DMA-1 base and current count, channel 2
6890 page = (ES >> 12); // upper 4 bits
6891 base_es = (ES << 4); // lower 16bits contributed by ES
6892 base_address = base_es + BX; // lower 16 bits of address
6893 // contributed by ES:BX
6894 if ( base_address < base_es ) {
6895 // in case of carry, adjust page by 1
6896 page++;
6898 base_count = (num_sectors * 512) - 1;
6900 // check for 64K boundary overrun
6901 last_addr = base_address + base_count;
6902 if (last_addr < base_address) {
6903 SET_AH(0x09);
6904 set_diskette_ret_status(0x09);
6905 SET_AL(0); // no sectors read
6906 SET_CF(); // error occurred
6907 return;
6910 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
6911 outb(0x000a, 0x06);
6913 outb(0x000c, 0x00); // clear flip-flop
6914 outb(0x0004, base_address);
6915 outb(0x0004, base_address>>8);
6916 outb(0x000c, 0x00); // clear flip-flop
6917 outb(0x0005, base_count);
6918 outb(0x0005, base_count>>8);
6920 // port 0b: DMA-1 Mode Register
6921 mode_register = 0x4a; // single mode, increment, autoinit disable,
6922 // transfer type=read, channel 2
6923 outb(0x000b, mode_register);
6925 // port 81: DMA-1 Page Register, channel 2
6926 outb(0x0081, page);
6928 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
6929 outb(0x000a, 0x02);
6931 //--------------------------------------
6932 // set up floppy controller for transfer
6933 //--------------------------------------
6935 // set 40:3e bit 7 to 0
6936 val8 = read_byte(0x0040, 0x003e);
6937 val8 &= 0x7f;
6938 write_byte(0x0040, 0x003e, val8);
6940 // turn on motor of selected drive, DMA & int enabled, normal operation
6941 if (drive)
6942 dor = 0x20;
6943 else
6944 dor = 0x10;
6945 dor |= 0x0c;
6946 dor |= drive;
6947 outb(0x03f2, dor);
6949 // reset the disk motor timeout value of INT 08
6950 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
6952 // check port 3f4 for drive readiness
6953 val8 = inb(0x3f4);
6954 if ( (val8 & 0xf0) != 0x80 )
6955 BX_PANIC("int13_diskette:f03: ctrl not ready\n");
6957 // send read-normal-data command (9 bytes) to controller
6958 outb(0x03f5, 0xc5); // c5: write normal data
6959 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
6960 outb(0x03f5, track);
6961 outb(0x03f5, head);
6962 outb(0x03f5, sector);
6963 outb(0x03f5, 2); // 512 byte sector size
6964 outb(0x03f5, 0); // last sector number possible on track
6965 outb(0x03f5, 0); // Gap length
6966 outb(0x03f5, 0xff); // Gap length
6968 // turn on interrupts
6969 ASM_START
6971 ASM_END
6973 // wait on 40:3e bit 7 to become 1
6974 do {
6975 val8 = read_byte(0x0040, 0x0040);
6976 if (val8 == 0) {
6977 floppy_reset_controller();
6978 SET_AH(0x80); // drive not ready (timeout)
6979 set_diskette_ret_status(0x80);
6980 SET_AL(0); // no sectors written
6981 SET_CF(); // error occurred
6982 return;
6984 val8 = (read_byte(0x0040, 0x003e) & 0x80);
6985 } while ( val8 == 0 );
6987 val8 = 0; // separate asm from while() loop
6988 // turn off interrupts
6989 ASM_START
6991 ASM_END
6993 // set 40:3e bit 7 to 0
6994 val8 = read_byte(0x0040, 0x003e);
6995 val8 &= 0x7f;
6996 write_byte(0x0040, 0x003e, val8);
6998 // check port 3f4 for accessibility to status bytes
6999 val8 = inb(0x3f4);
7000 if ( (val8 & 0xc0) != 0xc0 )
7001 BX_PANIC("int13_diskette: ctrl not ready\n");
7003 // read 7 return status bytes from controller
7004 // using loop index broken, have to unroll...
7005 return_status[0] = inb(0x3f5);
7006 return_status[1] = inb(0x3f5);
7007 return_status[2] = inb(0x3f5);
7008 return_status[3] = inb(0x3f5);
7009 return_status[4] = inb(0x3f5);
7010 return_status[5] = inb(0x3f5);
7011 return_status[6] = inb(0x3f5);
7012 // record in BIOS Data Area
7013 write_byte(0x0040, 0x0042, return_status[0]);
7014 write_byte(0x0040, 0x0043, return_status[1]);
7015 write_byte(0x0040, 0x0044, return_status[2]);
7016 write_byte(0x0040, 0x0045, return_status[3]);
7017 write_byte(0x0040, 0x0046, return_status[4]);
7018 write_byte(0x0040, 0x0047, return_status[5]);
7019 write_byte(0x0040, 0x0048, return_status[6]);
7021 if ( (return_status[0] & 0xc0) != 0 ) {
7022 if ( (return_status[1] & 0x02) != 0 ) {
7023 // diskette not writable.
7024 // AH=status code=0x03 (tried to write on write-protected disk)
7025 // AL=number of sectors written=0
7026 AX = 0x0300;
7027 SET_CF();
7028 return;
7029 } else {
7030 BX_PANIC("int13_diskette_function: read error\n");
7034 // ??? should track be new val from return_status[3] ?
7035 set_diskette_current_cyl(drive, track);
7036 // AL = number of sectors read (same value as passed)
7037 SET_AH(0x00); // success
7038 CLEAR_CF(); // success
7039 return;
7041 else { // if (ah == 0x04)
7042 // Verify Diskette Sectors
7044 // ??? should track be new val from return_status[3] ?
7045 set_diskette_current_cyl(drive, track);
7046 // AL = number of sectors verified (same value as passed)
7047 CLEAR_CF(); // success
7048 SET_AH(0x00); // success
7049 return;
7053 case 0x05: // format diskette track
7054 BX_DEBUG_INT13_FL("floppy f05\n");
7056 num_sectors = GET_AL();
7057 track = GET_CH();
7058 head = GET_DH();
7059 drive = GET_ELDL();
7061 if ((drive > 1) || (head > 1) || (track > 79) ||
7062 (num_sectors == 0) || (num_sectors > 18)) {
7063 SET_AH(1);
7064 set_diskette_ret_status(1);
7065 SET_CF(); // error occurred
7068 // see if drive exists
7069 if (floppy_drive_exists(drive) == 0) {
7070 SET_AH(0x80); // drive not responding
7071 set_diskette_ret_status(0x80);
7072 SET_CF(); // error occurred
7073 return;
7076 // see if media in drive, and type is known
7077 if (floppy_media_known(drive) == 0) {
7078 if (floppy_media_sense(drive) == 0) {
7079 SET_AH(0x0C); // Media type not found
7080 set_diskette_ret_status(0x0C);
7081 SET_AL(0); // no sectors read
7082 SET_CF(); // error occurred
7083 return;
7087 // set up DMA controller for transfer
7088 page = (ES >> 12); // upper 4 bits
7089 base_es = (ES << 4); // lower 16bits contributed by ES
7090 base_address = base_es + BX; // lower 16 bits of address
7091 // contributed by ES:BX
7092 if ( base_address < base_es ) {
7093 // in case of carry, adjust page by 1
7094 page++;
7096 base_count = (num_sectors * 4) - 1;
7098 // check for 64K boundary overrun
7099 last_addr = base_address + base_count;
7100 if (last_addr < base_address) {
7101 SET_AH(0x09);
7102 set_diskette_ret_status(0x09);
7103 SET_AL(0); // no sectors read
7104 SET_CF(); // error occurred
7105 return;
7108 outb(0x000a, 0x06);
7109 outb(0x000c, 0x00); // clear flip-flop
7110 outb(0x0004, base_address);
7111 outb(0x0004, base_address>>8);
7112 outb(0x000c, 0x00); // clear flip-flop
7113 outb(0x0005, base_count);
7114 outb(0x0005, base_count>>8);
7115 mode_register = 0x4a; // single mode, increment, autoinit disable,
7116 // transfer type=read, channel 2
7117 outb(0x000b, mode_register);
7118 // port 81: DMA-1 Page Register, channel 2
7119 outb(0x0081, page);
7120 outb(0x000a, 0x02);
7122 // set up floppy controller for transfer
7123 val8 = read_byte(0x0040, 0x003e);
7124 val8 &= 0x7f;
7125 write_byte(0x0040, 0x003e, val8);
7126 // turn on motor of selected drive, DMA & int enabled, normal operation
7127 if (drive)
7128 dor = 0x20;
7129 else
7130 dor = 0x10;
7131 dor |= 0x0c;
7132 dor |= drive;
7133 outb(0x03f2, dor);
7135 // reset the disk motor timeout value of INT 08
7136 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
7138 // check port 3f4 for drive readiness
7139 val8 = inb(0x3f4);
7140 if ( (val8 & 0xf0) != 0x80 )
7141 BX_PANIC("int13_diskette:f05: ctrl not ready\n");
7143 // send read-normal-data command (6 bytes) to controller
7144 outb(0x03f5, 0x4d); // 4d: format track
7145 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7146 outb(0x03f5, 2); // 512 byte sector size
7147 outb(0x03f5, num_sectors); // number of sectors per track
7148 outb(0x03f5, 0); // Gap length
7149 outb(0x03f5, 0xf6); // Fill byte
7150 // turn on interrupts
7151 ASM_START
7153 ASM_END
7155 // wait on 40:3e bit 7 to become 1
7156 do {
7157 val8 = read_byte(0x0040, 0x0040);
7158 if (val8 == 0) {
7159 floppy_reset_controller();
7160 SET_AH(0x80); // drive not ready (timeout)
7161 set_diskette_ret_status(0x80);
7162 SET_CF(); // error occurred
7163 return;
7165 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7166 } while ( val8 == 0 );
7168 val8 = 0; // separate asm from while() loop
7169 // turn off interrupts
7170 ASM_START
7172 ASM_END
7173 // set 40:3e bit 7 to 0
7174 val8 = read_byte(0x0040, 0x003e);
7175 val8 &= 0x7f;
7176 write_byte(0x0040, 0x003e, val8);
7177 // check port 3f4 for accessibility to status bytes
7178 val8 = inb(0x3f4);
7179 if ( (val8 & 0xc0) != 0xc0 )
7180 BX_PANIC("int13_diskette: ctrl not ready\n");
7182 // read 7 return status bytes from controller
7183 // using loop index broken, have to unroll...
7184 return_status[0] = inb(0x3f5);
7185 return_status[1] = inb(0x3f5);
7186 return_status[2] = inb(0x3f5);
7187 return_status[3] = inb(0x3f5);
7188 return_status[4] = inb(0x3f5);
7189 return_status[5] = inb(0x3f5);
7190 return_status[6] = inb(0x3f5);
7191 // record in BIOS Data Area
7192 write_byte(0x0040, 0x0042, return_status[0]);
7193 write_byte(0x0040, 0x0043, return_status[1]);
7194 write_byte(0x0040, 0x0044, return_status[2]);
7195 write_byte(0x0040, 0x0045, return_status[3]);
7196 write_byte(0x0040, 0x0046, return_status[4]);
7197 write_byte(0x0040, 0x0047, return_status[5]);
7198 write_byte(0x0040, 0x0048, return_status[6]);
7200 if ( (return_status[0] & 0xc0) != 0 ) {
7201 if ( (return_status[1] & 0x02) != 0 ) {
7202 // diskette not writable.
7203 // AH=status code=0x03 (tried to write on write-protected disk)
7204 // AL=number of sectors written=0
7205 AX = 0x0300;
7206 SET_CF();
7207 return;
7208 } else {
7209 BX_PANIC("int13_diskette_function: write error\n");
7213 SET_AH(0);
7214 set_diskette_ret_status(0);
7215 set_diskette_current_cyl(drive, 0);
7216 CLEAR_CF(); // successful
7217 return;
7220 case 0x08: // read diskette drive parameters
7221 BX_DEBUG_INT13_FL("floppy f08\n");
7222 drive = GET_ELDL();
7224 if (drive > 1) {
7225 AX = 0;
7226 BX = 0;
7227 CX = 0;
7228 DX = 0;
7229 ES = 0;
7230 DI = 0;
7231 SET_DL(num_floppies);
7232 SET_CF();
7233 return;
7236 drive_type = inb_cmos(0x10);
7237 num_floppies = 0;
7238 if (drive_type & 0xf0)
7239 num_floppies++;
7240 if (drive_type & 0x0f)
7241 num_floppies++;
7243 if (drive == 0)
7244 drive_type >>= 4;
7245 else
7246 drive_type &= 0x0f;
7248 SET_BH(0);
7249 SET_BL(drive_type);
7250 SET_AH(0);
7251 SET_AL(0);
7252 SET_DL(num_floppies);
7254 switch (drive_type) {
7255 case 0: // none
7256 CX = 0;
7257 SET_DH(0); // max head #
7258 break;
7260 case 1: // 360KB, 5.25"
7261 CX = 0x2709; // 40 tracks, 9 sectors
7262 SET_DH(1); // max head #
7263 break;
7265 case 2: // 1.2MB, 5.25"
7266 CX = 0x4f0f; // 80 tracks, 15 sectors
7267 SET_DH(1); // max head #
7268 break;
7270 case 3: // 720KB, 3.5"
7271 CX = 0x4f09; // 80 tracks, 9 sectors
7272 SET_DH(1); // max head #
7273 break;
7275 case 4: // 1.44MB, 3.5"
7276 CX = 0x4f12; // 80 tracks, 18 sectors
7277 SET_DH(1); // max head #
7278 break;
7280 case 5: // 2.88MB, 3.5"
7281 CX = 0x4f24; // 80 tracks, 36 sectors
7282 SET_DH(1); // max head #
7283 break;
7285 case 6: // 160k, 5.25"
7286 CX = 0x2708; // 40 tracks, 8 sectors
7287 SET_DH(0); // max head #
7288 break;
7290 case 7: // 180k, 5.25"
7291 CX = 0x2709; // 40 tracks, 9 sectors
7292 SET_DH(0); // max head #
7293 break;
7295 case 8: // 320k, 5.25"
7296 CX = 0x2708; // 40 tracks, 8 sectors
7297 SET_DH(1); // max head #
7298 break;
7300 default: // ?
7301 BX_PANIC("floppy: int13: bad floppy type\n");
7304 /* set es & di to point to 11 byte diskette param table in ROM */
7305 ASM_START
7306 push bp
7307 mov bp, sp
7308 mov ax, #diskette_param_table2
7309 mov _int13_diskette_function.DI+2[bp], ax
7310 mov _int13_diskette_function.ES+2[bp], cs
7311 pop bp
7312 ASM_END
7313 CLEAR_CF(); // success
7314 /* disk status not changed upon success */
7315 return;
7318 case 0x15: // read diskette drive type
7319 BX_DEBUG_INT13_FL("floppy f15\n");
7320 drive = GET_ELDL();
7321 if (drive > 1) {
7322 SET_AH(0); // only 2 drives supported
7323 // set_diskette_ret_status here ???
7324 SET_CF();
7325 return;
7327 drive_type = inb_cmos(0x10);
7329 if (drive == 0)
7330 drive_type >>= 4;
7331 else
7332 drive_type &= 0x0f;
7333 CLEAR_CF(); // successful, not present
7334 if (drive_type==0) {
7335 SET_AH(0); // drive not present
7337 else {
7338 SET_AH(1); // drive present, does not support change line
7341 return;
7343 case 0x16: // get diskette change line status
7344 BX_DEBUG_INT13_FL("floppy f16\n");
7345 drive = GET_ELDL();
7346 if (drive > 1) {
7347 SET_AH(0x01); // invalid drive
7348 set_diskette_ret_status(0x01);
7349 SET_CF();
7350 return;
7353 SET_AH(0x06); // change line not supported
7354 set_diskette_ret_status(0x06);
7355 SET_CF();
7356 return;
7358 case 0x17: // set diskette type for format(old)
7359 BX_DEBUG_INT13_FL("floppy f17\n");
7360 /* not used for 1.44M floppies */
7361 SET_AH(0x01); // not supported
7362 set_diskette_ret_status(1); /* not supported */
7363 SET_CF();
7364 return;
7366 case 0x18: // set diskette type for format(new)
7367 BX_DEBUG_INT13_FL("floppy f18\n");
7368 SET_AH(0x01); // do later
7369 set_diskette_ret_status(1);
7370 SET_CF();
7371 return;
7373 default:
7374 BX_INFO("int13_diskette: unsupported AH=%02x\n", GET_AH());
7376 // if ( (ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e) ) {
7377 SET_AH(0x01); // ???
7378 set_diskette_ret_status(1);
7379 SET_CF();
7380 return;
7381 // }
7384 #else // #if BX_SUPPORT_FLOPPY
7385 void
7386 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
7387 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
7389 Bit8u val8;
7391 switch ( GET_AH() ) {
7393 case 0x01: // Read Diskette Status
7394 CLEAR_CF();
7395 val8 = read_byte(0x0000, 0x0441);
7396 SET_AH(val8);
7397 if (val8) {
7398 SET_CF();
7400 return;
7402 default:
7403 SET_CF();
7404 write_byte(0x0000, 0x0441, 0x01);
7405 SET_AH(0x01);
7408 #endif // #if BX_SUPPORT_FLOPPY
7410 void
7411 set_diskette_ret_status(value)
7412 Bit8u value;
7414 write_byte(0x0040, 0x0041, value);
7417 void
7418 set_diskette_current_cyl(drive, cyl)
7419 Bit8u drive;
7420 Bit8u cyl;
7422 if (drive > 1)
7423 BX_PANIC("set_diskette_current_cyl(): drive > 1\n");
7424 write_byte(0x0040, 0x0094+drive, cyl);
7427 void
7428 determine_floppy_media(drive)
7429 Bit16u drive;
7431 #if 0
7432 Bit8u val8, DOR, ctrl_info;
7434 ctrl_info = read_byte(0x0040, 0x008F);
7435 if (drive==1)
7436 ctrl_info >>= 4;
7437 else
7438 ctrl_info &= 0x0f;
7440 #if 0
7441 if (drive == 0) {
7442 DOR = 0x1c; // DOR: drive0 motor on, DMA&int enabled, normal op, drive select 0
7444 else {
7445 DOR = 0x2d; // DOR: drive1 motor on, DMA&int enabled, normal op, drive select 1
7447 #endif
7449 if ( (ctrl_info & 0x04) != 0x04 ) {
7450 // Drive not determined means no drive exists, done.
7451 return;
7454 #if 0
7455 // check Main Status Register for readiness
7456 val8 = inb(0x03f4) & 0x80; // Main Status Register
7457 if (val8 != 0x80)
7458 BX_PANIC("d_f_m: MRQ bit not set\n");
7460 // change line
7462 // existing BDA values
7464 // turn on drive motor
7465 outb(0x03f2, DOR); // Digital Output Register
7467 #endif
7468 BX_PANIC("d_f_m: OK so far\n");
7469 #endif
7472 void
7473 int17_function(regs, ds, iret_addr)
7474 pusha_regs_t regs; // regs pushed from PUSHA instruction
7475 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7476 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7478 Bit16u addr,timeout;
7479 Bit8u val8;
7481 ASM_START
7483 ASM_END
7485 addr = read_word(0x0040, (regs.u.r16.dx << 1) + 8);
7486 if ((regs.u.r8.ah < 3) && (regs.u.r16.dx < 3) && (addr > 0)) {
7487 timeout = read_byte(0x0040, 0x0078 + regs.u.r16.dx) << 8;
7488 if (regs.u.r8.ah == 0) {
7489 outb(addr, regs.u.r8.al);
7490 val8 = inb(addr+2);
7491 outb(addr+2, val8 | 0x01); // send strobe
7492 ASM_START
7494 ASM_END
7495 outb(addr+2, val8 & ~0x01);
7496 while (((inb(addr+1) & 0x40) == 0x40) && (timeout)) {
7497 timeout--;
7500 if (regs.u.r8.ah == 1) {
7501 val8 = inb(addr+2);
7502 outb(addr+2, val8 & ~0x04); // send init
7503 ASM_START
7505 ASM_END
7506 outb(addr+2, val8 | 0x04);
7508 val8 = inb(addr+1);
7509 regs.u.r8.ah = (val8 ^ 0x48);
7510 if (!timeout) regs.u.r8.ah |= 0x01;
7511 ClearCF(iret_addr.flags);
7512 } else {
7513 SetCF(iret_addr.flags); // Unsupported
7517 // returns bootsegment in ax, drive in bl
7518 Bit32u
7519 int19_function(bseqnr)
7520 Bit8u bseqnr;
7522 Bit16u ebda_seg=read_word(0x0040,0x000E);
7523 Bit16u bootseq;
7524 Bit8u bootdrv;
7525 Bit8u bootcd;
7526 Bit8u bootchk;
7527 Bit16u bootseg;
7528 Bit16u status;
7529 Bit8u lastdrive=0;
7531 // if BX_ELTORITO_BOOT is not defined, old behavior
7532 // check bit 5 in CMOS reg 0x2d. load either 0x00 or 0x80 into DL
7533 // in preparation for the intial INT 13h (0=floppy A:, 0x80=C:)
7534 // 0: system boot sequence, first drive C: then A:
7535 // 1: system boot sequence, first drive A: then C:
7536 // else BX_ELTORITO_BOOT is defined
7537 // CMOS regs 0x3D and 0x38 contain the boot sequence:
7538 // CMOS reg 0x3D & 0x0f : 1st boot device
7539 // CMOS reg 0x3D & 0xf0 : 2nd boot device
7540 // CMOS reg 0x38 & 0xf0 : 3rd boot device
7541 // boot device codes:
7542 // 0x00 : not defined
7543 // 0x01 : first floppy
7544 // 0x02 : first harddrive
7545 // 0x03 : first cdrom
7546 // else : boot failure
7548 // Get the boot sequence
7549 #if BX_ELTORITO_BOOT
7550 bootseq=inb_cmos(0x3d);
7551 bootseq|=((inb_cmos(0x38) & 0xf0) << 4);
7553 if (bseqnr==2) bootseq >>= 4;
7554 if (bseqnr==3) bootseq >>= 8;
7555 if (bootseq<0x10) lastdrive = 1;
7556 bootdrv=0x00; bootcd=0;
7557 switch(bootseq & 0x0f) {
7558 case 0x01: bootdrv=0x00; bootcd=0; break;
7559 case 0x02: bootdrv=0x80; bootcd=0; break;
7560 case 0x03: bootdrv=0x00; bootcd=1; break;
7561 default: return 0x00000000;
7563 #else
7564 bootseq=inb_cmos(0x2d);
7566 if (bseqnr==2) {
7567 bootseq ^= 0x20;
7568 lastdrive = 1;
7570 bootdrv=0x00; bootcd=0;
7571 if((bootseq&0x20)==0) bootdrv=0x80;
7572 #endif // BX_ELTORITO_BOOT
7574 #if BX_ELTORITO_BOOT
7575 // We have to boot from cd
7576 if (bootcd != 0) {
7577 status = cdrom_boot();
7579 // If failure
7580 if ( (status & 0x00ff) !=0 ) {
7581 print_cdromboot_failure(status);
7582 print_boot_failure(bootcd, bootdrv, 1, lastdrive);
7583 return 0x00000000;
7586 bootseg = read_word(ebda_seg,&EbdaData->cdemu.load_segment);
7587 bootdrv = (Bit8u)(status>>8);
7590 #endif // BX_ELTORITO_BOOT
7592 // We have to boot from harddisk or floppy
7593 if (bootcd == 0) {
7594 bootseg=0x07c0;
7596 ASM_START
7597 push bp
7598 mov bp, sp
7600 mov ax, #0x0000
7601 mov _int19_function.status + 2[bp], ax
7602 mov dl, _int19_function.bootdrv + 2[bp]
7603 mov ax, _int19_function.bootseg + 2[bp]
7604 mov es, ax ;; segment
7605 mov bx, #0x0000 ;; offset
7606 mov ah, #0x02 ;; function 2, read diskette sector
7607 mov al, #0x01 ;; read 1 sector
7608 mov ch, #0x00 ;; track 0
7609 mov cl, #0x01 ;; sector 1
7610 mov dh, #0x00 ;; head 0
7611 int #0x13 ;; read sector
7612 jnc int19_load_done
7613 mov ax, #0x0001
7614 mov _int19_function.status + 2[bp], ax
7616 int19_load_done:
7617 pop bp
7618 ASM_END
7620 if (status != 0) {
7621 print_boot_failure(bootcd, bootdrv, 1, lastdrive);
7622 return 0x00000000;
7626 // check signature if instructed by cmos reg 0x38, only for floppy
7627 // bootchk = 1 : signature check disabled
7628 // bootchk = 0 : signature check enabled
7629 if (bootdrv != 0) bootchk = 0;
7630 else bootchk = inb_cmos(0x38) & 0x01;
7632 #if BX_ELTORITO_BOOT
7633 // if boot from cd, no signature check
7634 if (bootcd != 0)
7635 bootchk = 1;
7636 #endif // BX_ELTORITO_BOOT
7638 if (bootchk == 0) {
7639 if (read_word(bootseg,0x1fe) != 0xaa55) {
7640 print_boot_failure(bootcd, bootdrv, 0, lastdrive);
7641 return 0x00000000;
7645 #if BX_ELTORITO_BOOT
7646 // Print out the boot string
7647 print_boot_device(bootcd, bootdrv);
7648 #else // BX_ELTORITO_BOOT
7649 print_boot_device(0, bootdrv);
7650 #endif // BX_ELTORITO_BOOT
7652 // return the boot segment
7653 return (((Bit32u)bootdrv) << 16) + bootseg;
7656 void
7657 int1a_function(regs, ds, iret_addr)
7658 pusha_regs_t regs; // regs pushed from PUSHA instruction
7659 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7660 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7662 Bit8u val8;
7664 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);
7666 ASM_START
7668 ASM_END
7670 switch (regs.u.r8.ah) {
7671 case 0: // get current clock count
7672 ASM_START
7674 ASM_END
7675 regs.u.r16.cx = BiosData->ticks_high;
7676 regs.u.r16.dx = BiosData->ticks_low;
7677 regs.u.r8.al = BiosData->midnight_flag;
7678 BiosData->midnight_flag = 0; // reset flag
7679 ASM_START
7681 ASM_END
7682 // AH already 0
7683 ClearCF(iret_addr.flags); // OK
7684 break;
7686 case 1: // Set Current Clock Count
7687 ASM_START
7689 ASM_END
7690 BiosData->ticks_high = regs.u.r16.cx;
7691 BiosData->ticks_low = regs.u.r16.dx;
7692 BiosData->midnight_flag = 0; // reset flag
7693 ASM_START
7695 ASM_END
7696 regs.u.r8.ah = 0;
7697 ClearCF(iret_addr.flags); // OK
7698 break;
7701 case 2: // Read CMOS Time
7702 if (rtc_updating()) {
7703 SetCF(iret_addr.flags);
7704 break;
7707 regs.u.r8.dh = inb_cmos(0x00); // Seconds
7708 regs.u.r8.cl = inb_cmos(0x02); // Minutes
7709 regs.u.r8.ch = inb_cmos(0x04); // Hours
7710 regs.u.r8.dl = inb_cmos(0x0b) & 0x01; // Stat Reg B
7711 regs.u.r8.ah = 0;
7712 regs.u.r8.al = regs.u.r8.ch;
7713 ClearCF(iret_addr.flags); // OK
7714 break;
7716 case 3: // Set CMOS Time
7717 // Using a debugger, I notice the following masking/setting
7718 // of bits in Status Register B, by setting Reg B to
7719 // a few values and getting its value after INT 1A was called.
7721 // try#1 try#2 try#3
7722 // before 1111 1101 0111 1101 0000 0000
7723 // after 0110 0010 0110 0010 0000 0010
7725 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7726 // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
7727 if (rtc_updating()) {
7728 init_rtc();
7729 // fall through as if an update were not in progress
7731 outb_cmos(0x00, regs.u.r8.dh); // Seconds
7732 outb_cmos(0x02, regs.u.r8.cl); // Minutes
7733 outb_cmos(0x04, regs.u.r8.ch); // Hours
7734 // Set Daylight Savings time enabled bit to requested value
7735 val8 = (inb_cmos(0x0b) & 0x60) | 0x02 | (regs.u.r8.dl & 0x01);
7736 // (reg B already selected)
7737 outb_cmos(0x0b, val8);
7738 regs.u.r8.ah = 0;
7739 regs.u.r8.al = val8; // val last written to Reg B
7740 ClearCF(iret_addr.flags); // OK
7741 break;
7743 case 4: // Read CMOS Date
7744 regs.u.r8.ah = 0;
7745 if (rtc_updating()) {
7746 SetCF(iret_addr.flags);
7747 break;
7749 regs.u.r8.cl = inb_cmos(0x09); // Year
7750 regs.u.r8.dh = inb_cmos(0x08); // Month
7751 regs.u.r8.dl = inb_cmos(0x07); // Day of Month
7752 regs.u.r8.ch = inb_cmos(0x32); // Century
7753 regs.u.r8.al = regs.u.r8.ch;
7754 ClearCF(iret_addr.flags); // OK
7755 break;
7757 case 5: // Set CMOS Date
7758 // Using a debugger, I notice the following masking/setting
7759 // of bits in Status Register B, by setting Reg B to
7760 // a few values and getting its value after INT 1A was called.
7762 // try#1 try#2 try#3 try#4
7763 // before 1111 1101 0111 1101 0000 0010 0000 0000
7764 // after 0110 1101 0111 1101 0000 0010 0000 0000
7766 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7767 // My assumption: RegB = (RegB & 01111111b)
7768 if (rtc_updating()) {
7769 init_rtc();
7770 SetCF(iret_addr.flags);
7771 break;
7773 outb_cmos(0x09, regs.u.r8.cl); // Year
7774 outb_cmos(0x08, regs.u.r8.dh); // Month
7775 outb_cmos(0x07, regs.u.r8.dl); // Day of Month
7776 outb_cmos(0x32, regs.u.r8.ch); // Century
7777 val8 = inb_cmos(0x0b) & 0x7f; // clear halt-clock bit
7778 outb_cmos(0x0b, val8);
7779 regs.u.r8.ah = 0;
7780 regs.u.r8.al = val8; // AL = val last written to Reg B
7781 ClearCF(iret_addr.flags); // OK
7782 break;
7784 case 6: // Set Alarm Time in CMOS
7785 // Using a debugger, I notice the following masking/setting
7786 // of bits in Status Register B, by setting Reg B to
7787 // a few values and getting its value after INT 1A was called.
7789 // try#1 try#2 try#3
7790 // before 1101 1111 0101 1111 0000 0000
7791 // after 0110 1111 0111 1111 0010 0000
7793 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7794 // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
7795 val8 = inb_cmos(0x0b); // Get Status Reg B
7796 regs.u.r16.ax = 0;
7797 if (val8 & 0x20) {
7798 // Alarm interrupt enabled already
7799 SetCF(iret_addr.flags); // Error: alarm in use
7800 break;
7802 if (rtc_updating()) {
7803 init_rtc();
7804 // fall through as if an update were not in progress
7806 outb_cmos(0x01, regs.u.r8.dh); // Seconds alarm
7807 outb_cmos(0x03, regs.u.r8.cl); // Minutes alarm
7808 outb_cmos(0x05, regs.u.r8.ch); // Hours alarm
7809 outb(0xa1, inb(0xa1) & 0xfe); // enable IRQ 8
7810 // enable Status Reg B alarm bit, clear halt clock bit
7811 outb_cmos(0x0b, (val8 & 0x7f) | 0x20);
7812 ClearCF(iret_addr.flags); // OK
7813 break;
7815 case 7: // Turn off Alarm
7816 // Using a debugger, I notice the following masking/setting
7817 // of bits in Status Register B, by setting Reg B to
7818 // a few values and getting its value after INT 1A was called.
7820 // try#1 try#2 try#3 try#4
7821 // before 1111 1101 0111 1101 0010 0000 0010 0010
7822 // after 0100 0101 0101 0101 0000 0000 0000 0010
7824 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7825 // My assumption: RegB = (RegB & 01010111b)
7826 val8 = inb_cmos(0x0b); // Get Status Reg B
7827 // clear clock-halt bit, disable alarm bit
7828 outb_cmos(0x0b, val8 & 0x57); // disable alarm bit
7829 regs.u.r8.ah = 0;
7830 regs.u.r8.al = val8; // val last written to Reg B
7831 ClearCF(iret_addr.flags); // OK
7832 break;
7833 #if BX_PCIBIOS
7834 case 0xb1:
7835 // real mode PCI BIOS functions now handled in assembler code
7836 // this C code handles the error code for information only
7837 if (regs.u.r8.bl == 0xff) {
7838 BX_INFO("PCI BIOS: PCI not present\n");
7839 } else if (regs.u.r8.bl == 0x81) {
7840 BX_INFO("unsupported PCI BIOS function 0x%02x\n", regs.u.r8.al);
7841 } else if (regs.u.r8.bl == 0x83) {
7842 BX_INFO("bad PCI vendor ID %04x\n", regs.u.r16.dx);
7843 } else if (regs.u.r8.bl == 0x86) {
7844 BX_INFO("PCI device %04x:%04x not found\n", regs.u.r16.dx, regs.u.r16.cx);
7846 regs.u.r8.ah = regs.u.r8.bl;
7847 SetCF(iret_addr.flags);
7848 break;
7849 #endif
7851 default:
7852 SetCF(iret_addr.flags); // Unsupported
7856 void
7857 int70_function(regs, ds, iret_addr)
7858 pusha_regs_t regs; // regs pushed from PUSHA instruction
7859 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7860 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7862 // INT 70h: IRQ 8 - CMOS RTC interrupt from periodic or alarm modes
7863 Bit8u registerB = 0, registerC = 0;
7865 // Check which modes are enabled and have occurred.
7866 registerB = inb_cmos( 0xB );
7867 registerC = inb_cmos( 0xC );
7869 if( ( registerB & 0x60 ) != 0 ) {
7870 if( ( registerC & 0x20 ) != 0 ) {
7871 // Handle Alarm Interrupt.
7872 ASM_START
7874 int #0x4a
7876 ASM_END
7878 if( ( registerC & 0x40 ) != 0 ) {
7879 // Handle Periodic Interrupt.
7881 if( read_byte( 0x40, 0xA0 ) != 0 ) {
7882 // Wait Interval (Int 15, AH=83) active.
7883 Bit32u time, toggle;
7885 time = read_dword( 0x40, 0x9C ); // Time left in microseconds.
7886 if( time < 0x3D1 ) {
7887 // Done waiting.
7888 Bit16u segment, offset;
7890 segment = read_word( 0x40, 0x98 );
7891 offset = read_word( 0x40, 0x9A );
7892 write_byte( 0x40, 0xA0, 0 ); // Turn of status byte.
7893 outb_cmos( 0xB, registerB & 0x37 ); // Clear the Periodic Interrupt.
7894 write_byte(segment, offset, read_byte(segment, offset) | 0x80 ); // Write to specified flag byte.
7895 } else {
7896 // Continue waiting.
7897 time -= 0x3D1;
7898 write_dword( 0x40, 0x9C, time );
7904 ASM_START
7905 call eoi_both_pics
7906 ASM_END
7910 ASM_START
7911 ;------------------------------------------
7912 ;- INT74h : PS/2 mouse hardware interrupt -
7913 ;------------------------------------------
7914 int74_handler:
7916 pusha
7917 push ds ;; save DS
7918 push #0x00 ;; placeholder for status
7919 push #0x00 ;; placeholder for X
7920 push #0x00 ;; placeholder for Y
7921 push #0x00 ;; placeholder for Z
7922 push #0x00 ;; placeholder for make_far_call boolean
7923 call _int74_function
7924 pop cx ;; remove make_far_call from stack
7925 jcxz int74_done
7927 ;; make far call to EBDA:0022
7928 push #0x00
7929 pop ds
7930 push 0x040E ;; push 0000:040E (opcodes 0xff, 0x36, 0x0E, 0x04)
7931 pop ds
7932 //CALL_EP(0x0022) ;; call far routine (call_Ep DS:0022 :opcodes 0xff, 0x1e, 0x22, 0x00)
7933 call far ptr[0x22]
7934 int74_done:
7936 call eoi_both_pics
7937 add sp, #8 ;; pop status, x, y, z
7939 pop ds ;; restore DS
7940 popa
7941 iret
7944 ;; This will perform an IRET, but will retain value of current CF
7945 ;; by altering flags on stack. Better than RETF #02.
7946 iret_modify_cf:
7947 jc carry_set
7948 push bp
7949 mov bp, sp
7950 and BYTE [bp + 0x06], #0xfe
7951 pop bp
7952 iret
7953 carry_set:
7954 push bp
7955 mov bp, sp
7956 or BYTE [bp + 0x06], #0x01
7957 pop bp
7958 iret
7961 ;----------------------
7962 ;- INT13h (relocated) -
7963 ;----------------------
7965 ; int13_relocated is a little bit messed up since I played with it
7966 ; I have to rewrite it:
7967 ; - call a function that detect which function to call
7968 ; - make all called C function get the same parameters list
7970 int13_relocated:
7972 #if BX_ELTORITO_BOOT
7973 ;; check for an eltorito function
7974 cmp ah,#0x4a
7975 jb int13_not_eltorito
7976 cmp ah,#0x4d
7977 ja int13_not_eltorito
7979 pusha
7980 push es
7981 push ds
7982 push ss
7983 pop ds
7985 push #int13_out
7986 jmp _int13_eltorito ;; ELDX not used
7988 int13_not_eltorito:
7989 push ax
7990 push bx
7991 push cx
7992 push dx
7994 ;; check if emulation active
7995 call _cdemu_isactive
7996 cmp al,#0x00
7997 je int13_cdemu_inactive
7999 ;; check if access to the emulated drive
8000 call _cdemu_emulated_drive
8001 pop dx
8002 push dx
8003 cmp al,dl ;; int13 on emulated drive
8004 jne int13_nocdemu
8006 pop dx
8007 pop cx
8008 pop bx
8009 pop ax
8011 pusha
8012 push es
8013 push ds
8014 push ss
8015 pop ds
8017 push #int13_out
8018 jmp _int13_cdemu ;; ELDX not used
8020 int13_nocdemu:
8021 and dl,#0xE0 ;; mask to get device class, including cdroms
8022 cmp al,dl ;; al is 0x00 or 0x80
8023 jne int13_cdemu_inactive ;; inactive for device class
8025 pop dx
8026 pop cx
8027 pop bx
8028 pop ax
8030 push ax
8031 push cx
8032 push dx
8033 push bx
8035 dec dl ;; real drive is dl - 1
8036 jmp int13_legacy
8038 int13_cdemu_inactive:
8039 pop dx
8040 pop cx
8041 pop bx
8042 pop ax
8044 #endif // BX_ELTORITO_BOOT
8046 int13_noeltorito:
8048 push ax
8049 push cx
8050 push dx
8051 push bx
8053 int13_legacy:
8055 push dx ;; push eltorito value of dx instead of sp
8057 push bp
8058 push si
8059 push di
8061 push es
8062 push ds
8063 push ss
8064 pop ds
8066 ;; now the 16-bit registers can be restored with:
8067 ;; pop ds; pop es; popa; iret
8068 ;; arguments passed to functions should be
8069 ;; DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS
8071 test dl, #0x80
8072 jnz int13_notfloppy
8074 push #int13_out
8075 jmp _int13_diskette_function
8077 int13_notfloppy:
8079 #if BX_USE_ATADRV
8081 cmp dl, #0xE0
8082 jb int13_notcdrom
8084 // ebx is modified: BSD 5.2.1 boot loader problem
8085 // someone should figure out which 32 bit register that actually are used
8087 shr ebx, #16
8088 push bx
8090 call _int13_cdrom
8092 pop bx
8093 shl ebx, #16
8095 jmp int13_out
8097 int13_notcdrom:
8099 #endif
8101 int13_disk:
8102 call _int13_harddisk
8104 int13_out:
8105 pop ds
8106 pop es
8107 popa
8108 iret
8111 ;----------
8112 ;- INT18h -
8113 ;----------
8114 int18_handler: ;; Boot Failure routing
8115 call _int18_panic_msg
8117 iret
8119 ;----------
8120 ;- INT19h -
8121 ;----------
8122 int19_relocated: ;; Boot function, relocated
8124 ;; int19 was beginning to be really complex, so now it
8125 ;; just calls an C function, that does the work
8126 ;; it returns in BL the boot drive, and in AX the boot segment
8127 ;; the boot segment will be 0x0000 if something has failed
8129 push bp
8130 mov bp, sp
8132 ;; drop ds
8133 xor ax, ax
8134 mov ds, ax
8136 ;; 1st boot device
8137 mov ax, #0x0001
8138 push ax
8139 call _int19_function
8140 inc sp
8141 inc sp
8142 ;; bl contains the boot drive
8143 ;; ax contains the boot segment or 0 if failure
8145 test ax, ax ;; if ax is 0 try next boot device
8146 jnz boot_setup
8148 ;; 2nd boot device
8149 mov ax, #0x0002
8150 push ax
8151 call _int19_function
8152 inc sp
8153 inc sp
8154 test ax, ax ;; if ax is 0 try next boot device
8155 jnz boot_setup
8157 ;; 3rd boot device
8158 mov ax, #0x0003
8159 push ax
8160 call _int19_function
8161 inc sp
8162 inc sp
8163 test ax, ax ;; if ax is 0 call int18
8164 jz int18_handler
8166 boot_setup:
8167 mov dl, bl ;; set drive so guest os find it
8168 shl eax, #0x04 ;; convert seg to ip
8169 mov 2[bp], ax ;; set ip
8171 shr eax, #0x04 ;; get cs back
8172 and ax, #0xF000 ;; remove what went in ip
8173 mov 4[bp], ax ;; set cs
8174 xor ax, ax
8175 mov es, ax ;; set es to zero fixes [ 549815 ]
8176 mov [bp], ax ;; set bp to zero
8177 mov ax, #0xaa55 ;; set ok flag
8179 pop bp
8180 iret ;; Beam me up Scotty
8182 ;----------
8183 ;- INT1Ch -
8184 ;----------
8185 int1c_handler: ;; User Timer Tick
8186 iret
8189 ;----------------------
8190 ;- POST: Floppy Drive -
8191 ;----------------------
8192 floppy_drive_post:
8193 mov ax, #0x0000
8194 mov ds, ax
8196 mov al, #0x00
8197 mov 0x043e, al ;; drive 0 & 1 uncalibrated, no interrupt has occurred
8199 mov 0x043f, al ;; diskette motor status: read op, drive0, motors off
8201 mov 0x0440, al ;; diskette motor timeout counter: not active
8202 mov 0x0441, al ;; diskette controller status return code
8204 mov 0x0442, al ;; disk & diskette controller status register 0
8205 mov 0x0443, al ;; diskette controller status register 1
8206 mov 0x0444, al ;; diskette controller status register 2
8207 mov 0x0445, al ;; diskette controller cylinder number
8208 mov 0x0446, al ;; diskette controller head number
8209 mov 0x0447, al ;; diskette controller sector number
8210 mov 0x0448, al ;; diskette controller bytes written
8212 mov 0x048b, al ;; diskette configuration data
8214 ;; -----------------------------------------------------------------
8215 ;; (048F) diskette controller information
8217 mov al, #0x10 ;; get CMOS diskette drive type
8218 out 0x70, AL
8219 in AL, 0x71
8220 mov ah, al ;; save byte to AH
8222 look_drive0:
8223 shr al, #4 ;; look at top 4 bits for drive 0
8224 jz f0_missing ;; jump if no drive0
8225 mov bl, #0x07 ;; drive0 determined, multi-rate, has changed line
8226 jmp look_drive1
8227 f0_missing:
8228 mov bl, #0x00 ;; no drive0
8230 look_drive1:
8231 mov al, ah ;; restore from AH
8232 and al, #0x0f ;; look at bottom 4 bits for drive 1
8233 jz f1_missing ;; jump if no drive1
8234 or bl, #0x70 ;; drive1 determined, multi-rate, has changed line
8235 f1_missing:
8236 ;; leave high bits in BL zerod
8237 mov 0x048f, bl ;; put new val in BDA (diskette controller information)
8238 ;; -----------------------------------------------------------------
8240 mov al, #0x00
8241 mov 0x0490, al ;; diskette 0 media state
8242 mov 0x0491, al ;; diskette 1 media state
8244 ;; diskette 0,1 operational starting state
8245 ;; drive type has not been determined,
8246 ;; has no changed detection line
8247 mov 0x0492, al
8248 mov 0x0493, al
8250 mov 0x0494, al ;; diskette 0 current cylinder
8251 mov 0x0495, al ;; diskette 1 current cylinder
8253 mov al, #0x02
8254 out #0x0a, al ;; clear DMA-1 channel 2 mask bit
8256 SET_INT_VECTOR(0x1E, #0xF000, #diskette_param_table2)
8257 SET_INT_VECTOR(0x40, #0xF000, #int13_diskette)
8258 SET_INT_VECTOR(0x0E, #0xF000, #int0e_handler) ;; IRQ 6
8263 ;--------------------
8264 ;- POST: HARD DRIVE -
8265 ;--------------------
8266 ; relocated here because the primary POST area isnt big enough.
8267 hard_drive_post:
8268 // IRQ 14 = INT 76h
8269 // INT 76h calls INT 15h function ax=9100
8271 mov al, #0x0a ; 0000 1010 = reserved, disable IRQ 14
8272 mov dx, #0x03f6
8273 out dx, al
8275 mov ax, #0x0000
8276 mov ds, ax
8277 mov 0x0474, al /* hard disk status of last operation */
8278 mov 0x0477, al /* hard disk port offset (XT only ???) */
8279 mov 0x048c, al /* hard disk status register */
8280 mov 0x048d, al /* hard disk error register */
8281 mov 0x048e, al /* hard disk task complete flag */
8282 mov al, #0x01
8283 mov 0x0475, al /* hard disk number attached */
8284 mov al, #0xc0
8285 mov 0x0476, al /* hard disk control byte */
8286 SET_INT_VECTOR(0x13, #0xF000, #int13_handler)
8287 SET_INT_VECTOR(0x76, #0xF000, #int76_handler)
8288 ;; INT 41h: hard disk 0 configuration pointer
8289 ;; INT 46h: hard disk 1 configuration pointer
8290 SET_INT_VECTOR(0x41, #EBDA_SEG, #0x003D)
8291 SET_INT_VECTOR(0x46, #EBDA_SEG, #0x004D)
8293 ;; move disk geometry data from CMOS to EBDA disk parameter table(s)
8294 mov al, #0x12
8295 out #0x70, al
8296 in al, #0x71
8297 and al, #0xf0
8298 cmp al, #0xf0
8299 je post_d0_extended
8300 jmp check_for_hd1
8301 post_d0_extended:
8302 mov al, #0x19
8303 out #0x70, al
8304 in al, #0x71
8305 cmp al, #47 ;; decimal 47 - user definable
8306 je post_d0_type47
8307 HALT(__LINE__)
8308 post_d0_type47:
8309 ;; CMOS purpose param table offset
8310 ;; 1b cylinders low 0
8311 ;; 1c cylinders high 1
8312 ;; 1d heads 2
8313 ;; 1e write pre-comp low 5
8314 ;; 1f write pre-comp high 6
8315 ;; 20 retries/bad map/heads>8 8
8316 ;; 21 landing zone low C
8317 ;; 22 landing zone high D
8318 ;; 23 sectors/track E
8320 mov ax, #EBDA_SEG
8321 mov ds, ax
8323 ;;; Filling EBDA table for hard disk 0.
8324 mov al, #0x1f
8325 out #0x70, al
8326 in al, #0x71
8327 mov ah, al
8328 mov al, #0x1e
8329 out #0x70, al
8330 in al, #0x71
8331 mov (0x003d + 0x05), ax ;; write precomp word
8333 mov al, #0x20
8334 out #0x70, al
8335 in al, #0x71
8336 mov (0x003d + 0x08), al ;; drive control byte
8338 mov al, #0x22
8339 out #0x70, al
8340 in al, #0x71
8341 mov ah, al
8342 mov al, #0x21
8343 out #0x70, al
8344 in al, #0x71
8345 mov (0x003d + 0x0C), ax ;; landing zone word
8347 mov al, #0x1c ;; get cylinders word in AX
8348 out #0x70, al
8349 in al, #0x71 ;; high byte
8350 mov ah, al
8351 mov al, #0x1b
8352 out #0x70, al
8353 in al, #0x71 ;; low byte
8354 mov bx, ax ;; BX = cylinders
8356 mov al, #0x1d
8357 out #0x70, al
8358 in al, #0x71
8359 mov cl, al ;; CL = heads
8361 mov al, #0x23
8362 out #0x70, al
8363 in al, #0x71
8364 mov dl, al ;; DL = sectors
8366 cmp bx, #1024
8367 jnbe hd0_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8369 hd0_post_physical_chs:
8370 ;; no logical CHS mapping used, just physical CHS
8371 ;; use Standard Fixed Disk Parameter Table (FDPT)
8372 mov (0x003d + 0x00), bx ;; number of physical cylinders
8373 mov (0x003d + 0x02), cl ;; number of physical heads
8374 mov (0x003d + 0x0E), dl ;; number of physical sectors
8375 jmp check_for_hd1
8377 hd0_post_logical_chs:
8378 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8379 mov (0x003d + 0x09), bx ;; number of physical cylinders
8380 mov (0x003d + 0x0b), cl ;; number of physical heads
8381 mov (0x003d + 0x04), dl ;; number of physical sectors
8382 mov (0x003d + 0x0e), dl ;; number of logical sectors (same)
8383 mov al, #0xa0
8384 mov (0x003d + 0x03), al ;; A0h signature, indicates translated table
8386 cmp bx, #2048
8387 jnbe hd0_post_above_2048
8388 ;; 1024 < c <= 2048 cylinders
8389 shr bx, #0x01
8390 shl cl, #0x01
8391 jmp hd0_post_store_logical
8393 hd0_post_above_2048:
8394 cmp bx, #4096
8395 jnbe hd0_post_above_4096
8396 ;; 2048 < c <= 4096 cylinders
8397 shr bx, #0x02
8398 shl cl, #0x02
8399 jmp hd0_post_store_logical
8401 hd0_post_above_4096:
8402 cmp bx, #8192
8403 jnbe hd0_post_above_8192
8404 ;; 4096 < c <= 8192 cylinders
8405 shr bx, #0x03
8406 shl cl, #0x03
8407 jmp hd0_post_store_logical
8409 hd0_post_above_8192:
8410 ;; 8192 < c <= 16384 cylinders
8411 shr bx, #0x04
8412 shl cl, #0x04
8414 hd0_post_store_logical:
8415 mov (0x003d + 0x00), bx ;; number of physical cylinders
8416 mov (0x003d + 0x02), cl ;; number of physical heads
8417 ;; checksum
8418 mov cl, #0x0f ;; repeat count
8419 mov si, #0x003d ;; offset to disk0 FDPT
8420 mov al, #0x00 ;; sum
8421 hd0_post_checksum_loop:
8422 add al, [si]
8423 inc si
8424 dec cl
8425 jnz hd0_post_checksum_loop
8426 not al ;; now take 2s complement
8427 inc al
8428 mov [si], al
8429 ;;; Done filling EBDA table for hard disk 0.
8432 check_for_hd1:
8433 ;; is there really a second hard disk? if not, return now
8434 mov al, #0x12
8435 out #0x70, al
8436 in al, #0x71
8437 and al, #0x0f
8438 jnz post_d1_exists
8440 post_d1_exists:
8441 ;; check that the hd type is really 0x0f.
8442 cmp al, #0x0f
8443 jz post_d1_extended
8444 HALT(__LINE__)
8445 post_d1_extended:
8446 ;; check that the extended type is 47 - user definable
8447 mov al, #0x1a
8448 out #0x70, al
8449 in al, #0x71
8450 cmp al, #47 ;; decimal 47 - user definable
8451 je post_d1_type47
8452 HALT(__LINE__)
8453 post_d1_type47:
8454 ;; Table for disk1.
8455 ;; CMOS purpose param table offset
8456 ;; 0x24 cylinders low 0
8457 ;; 0x25 cylinders high 1
8458 ;; 0x26 heads 2
8459 ;; 0x27 write pre-comp low 5
8460 ;; 0x28 write pre-comp high 6
8461 ;; 0x29 heads>8 8
8462 ;; 0x2a landing zone low C
8463 ;; 0x2b landing zone high D
8464 ;; 0x2c sectors/track E
8465 ;;; Fill EBDA table for hard disk 1.
8466 mov ax, #EBDA_SEG
8467 mov ds, ax
8468 mov al, #0x28
8469 out #0x70, al
8470 in al, #0x71
8471 mov ah, al
8472 mov al, #0x27
8473 out #0x70, al
8474 in al, #0x71
8475 mov (0x004d + 0x05), ax ;; write precomp word
8477 mov al, #0x29
8478 out #0x70, al
8479 in al, #0x71
8480 mov (0x004d + 0x08), al ;; drive control byte
8482 mov al, #0x2b
8483 out #0x70, al
8484 in al, #0x71
8485 mov ah, al
8486 mov al, #0x2a
8487 out #0x70, al
8488 in al, #0x71
8489 mov (0x004d + 0x0C), ax ;; landing zone word
8491 mov al, #0x25 ;; get cylinders word in AX
8492 out #0x70, al
8493 in al, #0x71 ;; high byte
8494 mov ah, al
8495 mov al, #0x24
8496 out #0x70, al
8497 in al, #0x71 ;; low byte
8498 mov bx, ax ;; BX = cylinders
8500 mov al, #0x26
8501 out #0x70, al
8502 in al, #0x71
8503 mov cl, al ;; CL = heads
8505 mov al, #0x2c
8506 out #0x70, al
8507 in al, #0x71
8508 mov dl, al ;; DL = sectors
8510 cmp bx, #1024
8511 jnbe hd1_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8513 hd1_post_physical_chs:
8514 ;; no logical CHS mapping used, just physical CHS
8515 ;; use Standard Fixed Disk Parameter Table (FDPT)
8516 mov (0x004d + 0x00), bx ;; number of physical cylinders
8517 mov (0x004d + 0x02), cl ;; number of physical heads
8518 mov (0x004d + 0x0E), dl ;; number of physical sectors
8521 hd1_post_logical_chs:
8522 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8523 mov (0x004d + 0x09), bx ;; number of physical cylinders
8524 mov (0x004d + 0x0b), cl ;; number of physical heads
8525 mov (0x004d + 0x04), dl ;; number of physical sectors
8526 mov (0x004d + 0x0e), dl ;; number of logical sectors (same)
8527 mov al, #0xa0
8528 mov (0x004d + 0x03), al ;; A0h signature, indicates translated table
8530 cmp bx, #2048
8531 jnbe hd1_post_above_2048
8532 ;; 1024 < c <= 2048 cylinders
8533 shr bx, #0x01
8534 shl cl, #0x01
8535 jmp hd1_post_store_logical
8537 hd1_post_above_2048:
8538 cmp bx, #4096
8539 jnbe hd1_post_above_4096
8540 ;; 2048 < c <= 4096 cylinders
8541 shr bx, #0x02
8542 shl cl, #0x02
8543 jmp hd1_post_store_logical
8545 hd1_post_above_4096:
8546 cmp bx, #8192
8547 jnbe hd1_post_above_8192
8548 ;; 4096 < c <= 8192 cylinders
8549 shr bx, #0x03
8550 shl cl, #0x03
8551 jmp hd1_post_store_logical
8553 hd1_post_above_8192:
8554 ;; 8192 < c <= 16384 cylinders
8555 shr bx, #0x04
8556 shl cl, #0x04
8558 hd1_post_store_logical:
8559 mov (0x004d + 0x00), bx ;; number of physical cylinders
8560 mov (0x004d + 0x02), cl ;; number of physical heads
8561 ;; checksum
8562 mov cl, #0x0f ;; repeat count
8563 mov si, #0x004d ;; offset to disk0 FDPT
8564 mov al, #0x00 ;; sum
8565 hd1_post_checksum_loop:
8566 add al, [si]
8567 inc si
8568 dec cl
8569 jnz hd1_post_checksum_loop
8570 not al ;; now take 2s complement
8571 inc al
8572 mov [si], al
8573 ;;; Done filling EBDA table for hard disk 1.
8577 ;--------------------
8578 ;- POST: EBDA segment
8579 ;--------------------
8580 ; relocated here because the primary POST area isnt big enough.
8581 ebda_post:
8582 #if BX_USE_EBDA
8583 mov ax, #EBDA_SEG
8584 mov ds, ax
8585 mov byte ptr [0x0], #EBDA_SIZE
8586 #endif
8587 xor ax, ax ; mov EBDA seg into 40E
8588 mov ds, ax
8589 mov word ptr [0x40E], #EBDA_SEG
8590 ret;;
8592 ;--------------------
8593 ;- POST: EOI + jmp via [0x40:67)
8594 ;--------------------
8595 ; relocated here because the primary POST area isnt big enough.
8596 eoi_jmp_post:
8597 call eoi_both_pics
8599 xor ax, ax
8600 mov ds, ax
8602 jmp far ptr [0x467]
8605 ;--------------------
8606 eoi_both_pics:
8607 mov al, #0x20
8608 out #0xA0, al ;; slave PIC EOI
8609 eoi_master_pic:
8610 mov al, #0x20
8611 out #0x20, al ;; master PIC EOI
8614 ;--------------------
8615 BcdToBin:
8616 ;; in: AL in BCD format
8617 ;; out: AL in binary format, AH will always be 0
8618 ;; trashes BX
8619 mov bl, al
8620 and bl, #0x0f ;; bl has low digit
8621 shr al, #4 ;; al has high digit
8622 mov bh, #10
8623 mul al, bh ;; multiply high digit by 10 (result in AX)
8624 add al, bl ;; then add low digit
8627 ;--------------------
8628 timer_tick_post:
8629 ;; Setup the Timer Ticks Count (0x46C:dword) and
8630 ;; Timer Ticks Roller Flag (0x470:byte)
8631 ;; The Timer Ticks Count needs to be set according to
8632 ;; the current CMOS time, as if ticks have been occurring
8633 ;; at 18.2hz since midnight up to this point. Calculating
8634 ;; this is a little complicated. Here are the factors I gather
8635 ;; regarding this. 14,318,180 hz was the original clock speed,
8636 ;; chosen so it could be divided by either 3 to drive the 5Mhz CPU
8637 ;; at the time, or 4 to drive the CGA video adapter. The div3
8638 ;; source was divided again by 4 to feed a 1.193Mhz signal to
8639 ;; the timer. With a maximum 16bit timer count, this is again
8640 ;; divided down by 65536 to 18.2hz.
8642 ;; 14,318,180 Hz clock
8643 ;; /3 = 4,772,726 Hz fed to orginal 5Mhz CPU
8644 ;; /4 = 1,193,181 Hz fed to timer
8645 ;; /65536 (maximum timer count) = 18.20650736 ticks/second
8646 ;; 1 second = 18.20650736 ticks
8647 ;; 1 minute = 1092.390442 ticks
8648 ;; 1 hour = 65543.42651 ticks
8650 ;; Given the values in the CMOS clock, one could calculate
8651 ;; the number of ticks by the following:
8652 ;; ticks = (BcdToBin(seconds) * 18.206507) +
8653 ;; (BcdToBin(minutes) * 1092.3904)
8654 ;; (BcdToBin(hours) * 65543.427)
8655 ;; To get a little more accuracy, since Im using integer
8656 ;; arithmatic, I use:
8657 ;; ticks = (BcdToBin(seconds) * 18206507) / 1000000 +
8658 ;; (BcdToBin(minutes) * 10923904) / 10000 +
8659 ;; (BcdToBin(hours) * 65543427) / 1000
8661 ;; assuming DS=0000
8663 ;; get CMOS seconds
8664 xor eax, eax ;; clear EAX
8665 mov al, #0x00
8666 out #0x70, al
8667 in al, #0x71 ;; AL has CMOS seconds in BCD
8668 call BcdToBin ;; EAX now has seconds in binary
8669 mov edx, #18206507
8670 mul eax, edx
8671 mov ebx, #1000000
8672 xor edx, edx
8673 div eax, ebx
8674 mov ecx, eax ;; ECX will accumulate total ticks
8676 ;; get CMOS minutes
8677 xor eax, eax ;; clear EAX
8678 mov al, #0x02
8679 out #0x70, al
8680 in al, #0x71 ;; AL has CMOS minutes in BCD
8681 call BcdToBin ;; EAX now has minutes in binary
8682 mov edx, #10923904
8683 mul eax, edx
8684 mov ebx, #10000
8685 xor edx, edx
8686 div eax, ebx
8687 add ecx, eax ;; add to total ticks
8689 ;; get CMOS hours
8690 xor eax, eax ;; clear EAX
8691 mov al, #0x04
8692 out #0x70, al
8693 in al, #0x71 ;; AL has CMOS hours in BCD
8694 call BcdToBin ;; EAX now has hours in binary
8695 mov edx, #65543427
8696 mul eax, edx
8697 mov ebx, #1000
8698 xor edx, edx
8699 div eax, ebx
8700 add ecx, eax ;; add to total ticks
8702 mov 0x46C, ecx ;; Timer Ticks Count
8703 xor al, al
8704 mov 0x470, al ;; Timer Ticks Rollover Flag
8707 ;--------------------
8708 int76_handler:
8709 ;; record completion in BIOS task complete flag
8710 push ax
8711 push ds
8712 mov ax, #0x0040
8713 mov ds, ax
8714 mov 0x008E, #0xff
8715 call eoi_both_pics
8716 pop ds
8717 pop ax
8718 iret
8721 ;--------------------
8722 #if BX_APM
8724 use32 386
8725 #define APM_PROT32
8726 #include "apmbios.S"
8728 use16 386
8729 #define APM_PROT16
8730 #include "apmbios.S"
8732 #define APM_REAL
8733 #include "apmbios.S"
8735 #endif
8737 ;--------------------
8738 #if BX_PCIBIOS
8739 use32 386
8740 .align 16
8741 bios32_structure:
8742 db 0x5f, 0x33, 0x32, 0x5f ;; "_32_" signature
8743 dw bios32_entry_point, 0xf ;; 32 bit physical address
8744 db 0 ;; revision level
8745 ;; length in paragraphs and checksum stored in a word to prevent errors
8746 dw (~(((bios32_entry_point >> 8) + (bios32_entry_point & 0xff) + 0x32) \
8747 & 0xff) << 8) + 0x01
8748 db 0,0,0,0,0 ;; reserved
8750 .align 16
8751 bios32_entry_point:
8752 pushf
8753 cmp eax, #0x49435024 ;; "$PCI"
8754 jne unknown_service
8755 mov eax, #0x80000000
8756 mov dx, #0x0cf8
8757 out dx, eax
8758 mov dx, #0x0cfc
8759 in eax, dx
8760 #ifdef PCI_FIXED_HOST_BRIDGE
8761 cmp eax, #PCI_FIXED_HOST_BRIDGE
8762 jne unknown_service
8763 #else
8764 ;; say ok if a device is present
8765 cmp eax, #0xffffffff
8766 je unknown_service
8767 #endif
8768 mov ebx, #0x000f0000
8769 mov ecx, #0
8770 mov edx, #pcibios_protected
8771 xor al, al
8772 jmp bios32_end
8773 unknown_service:
8774 mov al, #0x80
8775 bios32_end:
8776 popf
8777 retf
8779 .align 16
8780 pcibios_protected:
8781 pushf
8783 push esi
8784 push edi
8785 cmp al, #0x01 ;; installation check
8786 jne pci_pro_f02
8787 mov bx, #0x0210
8788 mov cx, #0
8789 mov edx, #0x20494350 ;; "PCI "
8790 mov al, #0x01
8791 jmp pci_pro_ok
8792 pci_pro_f02: ;; find pci device
8793 cmp al, #0x02
8794 jne pci_pro_f08
8795 shl ecx, #16
8796 mov cx, dx
8797 mov bx, #0x0000
8798 mov di, #0x00
8799 pci_pro_devloop:
8800 call pci_pro_select_reg
8801 mov dx, #0x0cfc
8802 in eax, dx
8803 cmp eax, ecx
8804 jne pci_pro_nextdev
8805 cmp si, #0
8806 je pci_pro_ok
8807 dec si
8808 pci_pro_nextdev:
8809 inc bx
8810 cmp bx, #0x0100
8811 jne pci_pro_devloop
8812 mov ah, #0x86
8813 jmp pci_pro_fail
8814 pci_pro_f08: ;; read configuration byte
8815 cmp al, #0x08
8816 jne pci_pro_f09
8817 call pci_pro_select_reg
8818 push edx
8819 mov dx, di
8820 and dx, #0x03
8821 add dx, #0x0cfc
8822 in al, dx
8823 pop edx
8824 mov cl, al
8825 jmp pci_pro_ok
8826 pci_pro_f09: ;; read configuration word
8827 cmp al, #0x09
8828 jne pci_pro_f0a
8829 call pci_pro_select_reg
8830 push edx
8831 mov dx, di
8832 and dx, #0x02
8833 add dx, #0x0cfc
8834 in ax, dx
8835 pop edx
8836 mov cx, ax
8837 jmp pci_pro_ok
8838 pci_pro_f0a: ;; read configuration dword
8839 cmp al, #0x0a
8840 jne pci_pro_f0b
8841 call pci_pro_select_reg
8842 push edx
8843 mov dx, #0x0cfc
8844 in eax, dx
8845 pop edx
8846 mov ecx, eax
8847 jmp pci_pro_ok
8848 pci_pro_f0b: ;; write configuration byte
8849 cmp al, #0x0b
8850 jne pci_pro_f0c
8851 call pci_pro_select_reg
8852 push edx
8853 mov dx, di
8854 and dx, #0x03
8855 add dx, #0x0cfc
8856 mov al, cl
8857 out dx, al
8858 pop edx
8859 jmp pci_pro_ok
8860 pci_pro_f0c: ;; write configuration word
8861 cmp al, #0x0c
8862 jne pci_pro_f0d
8863 call pci_pro_select_reg
8864 push edx
8865 mov dx, di
8866 and dx, #0x02
8867 add dx, #0x0cfc
8868 mov ax, cx
8869 out dx, ax
8870 pop edx
8871 jmp pci_pro_ok
8872 pci_pro_f0d: ;; write configuration dword
8873 cmp al, #0x0d
8874 jne pci_pro_unknown
8875 call pci_pro_select_reg
8876 push edx
8877 mov dx, #0x0cfc
8878 mov eax, ecx
8879 out dx, eax
8880 pop edx
8881 jmp pci_pro_ok
8882 pci_pro_unknown:
8883 mov ah, #0x81
8884 pci_pro_fail:
8885 pop edi
8886 pop esi
8888 popf
8890 retf
8891 pci_pro_ok:
8892 xor ah, ah
8893 pop edi
8894 pop esi
8896 popf
8898 retf
8900 pci_pro_select_reg:
8901 push edx
8902 mov eax, #0x800000
8903 mov ax, bx
8904 shl eax, #8
8905 and di, #0xff
8906 or ax, di
8907 and al, #0xfc
8908 mov dx, #0x0cf8
8909 out dx, eax
8910 pop edx
8913 use16 386
8915 pcibios_real:
8916 push eax
8917 push dx
8918 mov eax, #0x80000000
8919 mov dx, #0x0cf8
8920 out dx, eax
8921 mov dx, #0x0cfc
8922 in eax, dx
8923 #ifdef PCI_FIXED_HOST_BRIDGE
8924 cmp eax, #PCI_FIXED_HOST_BRIDGE
8925 je pci_present
8926 #else
8927 ;; say ok if a device is present
8928 cmp eax, #0xffffffff
8929 jne pci_present
8930 #endif
8931 pop dx
8932 pop eax
8933 mov ah, #0xff
8936 pci_present:
8937 pop dx
8938 pop eax
8939 cmp al, #0x01 ;; installation check
8940 jne pci_real_f02
8941 mov ax, #0x0001
8942 mov bx, #0x0210
8943 mov cx, #0
8944 mov edx, #0x20494350 ;; "PCI "
8945 mov edi, #0xf0000
8946 mov di, #pcibios_protected
8949 pci_real_f02: ;; find pci device
8950 push esi
8951 push edi
8952 cmp al, #0x02
8953 jne pci_real_f08
8954 shl ecx, #16
8955 mov cx, dx
8956 mov bx, #0x0000
8957 mov di, #0x00
8958 pci_real_devloop:
8959 call pci_real_select_reg
8960 mov dx, #0x0cfc
8961 in eax, dx
8962 cmp eax, ecx
8963 jne pci_real_nextdev
8964 cmp si, #0
8965 je pci_real_ok
8966 dec si
8967 pci_real_nextdev:
8968 inc bx
8969 cmp bx, #0x0100
8970 jne pci_real_devloop
8971 mov dx, cx
8972 shr ecx, #16
8973 mov ah, #0x86
8974 jmp pci_real_fail
8975 pci_real_f08: ;; read configuration byte
8976 cmp al, #0x08
8977 jne pci_real_f09
8978 call pci_real_select_reg
8979 push dx
8980 mov dx, di
8981 and dx, #0x03
8982 add dx, #0x0cfc
8983 in al, dx
8984 pop dx
8985 mov cl, al
8986 jmp pci_real_ok
8987 pci_real_f09: ;; read configuration word
8988 cmp al, #0x09
8989 jne pci_real_f0a
8990 call pci_real_select_reg
8991 push dx
8992 mov dx, di
8993 and dx, #0x02
8994 add dx, #0x0cfc
8995 in ax, dx
8996 pop dx
8997 mov cx, ax
8998 jmp pci_real_ok
8999 pci_real_f0a: ;; read configuration dword
9000 cmp al, #0x0a
9001 jne pci_real_f0b
9002 call pci_real_select_reg
9003 push dx
9004 mov dx, #0x0cfc
9005 in eax, dx
9006 pop dx
9007 mov ecx, eax
9008 jmp pci_real_ok
9009 pci_real_f0b: ;; write configuration byte
9010 cmp al, #0x0b
9011 jne pci_real_f0c
9012 call pci_real_select_reg
9013 push dx
9014 mov dx, di
9015 and dx, #0x03
9016 add dx, #0x0cfc
9017 mov al, cl
9018 out dx, al
9019 pop dx
9020 jmp pci_real_ok
9021 pci_real_f0c: ;; write configuration word
9022 cmp al, #0x0c
9023 jne pci_real_f0d
9024 call pci_real_select_reg
9025 push dx
9026 mov dx, di
9027 and dx, #0x02
9028 add dx, #0x0cfc
9029 mov ax, cx
9030 out dx, ax
9031 pop dx
9032 jmp pci_real_ok
9033 pci_real_f0d: ;; write configuration dword
9034 cmp al, #0x0d
9035 jne pci_real_unknown
9036 call pci_real_select_reg
9037 push dx
9038 mov dx, #0x0cfc
9039 mov eax, ecx
9040 out dx, eax
9041 pop dx
9042 jmp pci_real_ok
9043 pci_real_unknown:
9044 mov ah, #0x81
9045 pci_real_fail:
9046 pop edi
9047 pop esi
9050 pci_real_ok:
9051 xor ah, ah
9052 pop edi
9053 pop esi
9057 pci_real_select_reg:
9058 push dx
9059 mov eax, #0x800000
9060 mov ax, bx
9061 shl eax, #8
9062 and di, #0xff
9063 or ax, di
9064 and al, #0xfc
9065 mov dx, #0x0cf8
9066 out dx, eax
9067 pop dx
9070 .align 16
9071 pci_routing_table_structure:
9072 db 0x24, 0x50, 0x49, 0x52 ;; "$PIR" signature
9073 db 0, 1 ;; version
9074 dw 32 + (6 * 16) ;; table size
9075 db 0 ;; PCI interrupt router bus
9076 db 0x08 ;; PCI interrupt router DevFunc
9077 dw 0x0000 ;; PCI exclusive IRQs
9078 dw 0x8086 ;; compatible PCI interrupt router vendor ID
9079 dw 0x7000 ;; compatible PCI interrupt router device ID
9080 dw 0,0 ;; Miniport data
9081 db 0,0,0,0,0,0,0,0,0,0,0 ;; reserved
9082 db 0x07 ;; checksum
9083 ;; first slot entry PCI-to-ISA (embedded)
9084 db 0 ;; pci bus number
9085 db 0x08 ;; pci device number (bit 7-3)
9086 db 0x60 ;; link value INTA#: pointer into PCI2ISA config space
9087 dw 0xdef8 ;; IRQ bitmap INTA#
9088 db 0x61 ;; link value INTB#
9089 dw 0xdef8 ;; IRQ bitmap INTB#
9090 db 0x62 ;; link value INTC#
9091 dw 0xdef8 ;; IRQ bitmap INTC#
9092 db 0x63 ;; link value INTD#
9093 dw 0xdef8 ;; IRQ bitmap INTD#
9094 db 0 ;; physical slot (0 = embedded)
9095 db 0 ;; reserved
9096 ;; second slot entry: 1st PCI slot
9097 db 0 ;; pci bus number
9098 db 0x10 ;; pci device number (bit 7-3)
9099 db 0x61 ;; link value INTA#
9100 dw 0xdef8 ;; IRQ bitmap INTA#
9101 db 0x62 ;; link value INTB#
9102 dw 0xdef8 ;; IRQ bitmap INTB#
9103 db 0x63 ;; link value INTC#
9104 dw 0xdef8 ;; IRQ bitmap INTC#
9105 db 0x60 ;; link value INTD#
9106 dw 0xdef8 ;; IRQ bitmap INTD#
9107 db 1 ;; physical slot (0 = embedded)
9108 db 0 ;; reserved
9109 ;; third slot entry: 2nd PCI slot
9110 db 0 ;; pci bus number
9111 db 0x18 ;; pci device number (bit 7-3)
9112 db 0x62 ;; link value INTA#
9113 dw 0xdef8 ;; IRQ bitmap INTA#
9114 db 0x63 ;; link value INTB#
9115 dw 0xdef8 ;; IRQ bitmap INTB#
9116 db 0x60 ;; link value INTC#
9117 dw 0xdef8 ;; IRQ bitmap INTC#
9118 db 0x61 ;; link value INTD#
9119 dw 0xdef8 ;; IRQ bitmap INTD#
9120 db 2 ;; physical slot (0 = embedded)
9121 db 0 ;; reserved
9122 ;; 4th slot entry: 3rd PCI slot
9123 db 0 ;; pci bus number
9124 db 0x20 ;; pci device number (bit 7-3)
9125 db 0x63 ;; link value INTA#
9126 dw 0xdef8 ;; IRQ bitmap INTA#
9127 db 0x60 ;; link value INTB#
9128 dw 0xdef8 ;; IRQ bitmap INTB#
9129 db 0x61 ;; link value INTC#
9130 dw 0xdef8 ;; IRQ bitmap INTC#
9131 db 0x62 ;; link value INTD#
9132 dw 0xdef8 ;; IRQ bitmap INTD#
9133 db 3 ;; physical slot (0 = embedded)
9134 db 0 ;; reserved
9135 ;; 5th slot entry: 4rd PCI slot
9136 db 0 ;; pci bus number
9137 db 0x28 ;; pci device number (bit 7-3)
9138 db 0x60 ;; link value INTA#
9139 dw 0xdef8 ;; IRQ bitmap INTA#
9140 db 0x61 ;; link value INTB#
9141 dw 0xdef8 ;; IRQ bitmap INTB#
9142 db 0x62 ;; link value INTC#
9143 dw 0xdef8 ;; IRQ bitmap INTC#
9144 db 0x63 ;; link value INTD#
9145 dw 0xdef8 ;; IRQ bitmap INTD#
9146 db 4 ;; physical slot (0 = embedded)
9147 db 0 ;; reserved
9148 ;; 6th slot entry: 5rd PCI slot
9149 db 0 ;; pci bus number
9150 db 0x30 ;; pci device number (bit 7-3)
9151 db 0x61 ;; link value INTA#
9152 dw 0xdef8 ;; IRQ bitmap INTA#
9153 db 0x62 ;; link value INTB#
9154 dw 0xdef8 ;; IRQ bitmap INTB#
9155 db 0x63 ;; link value INTC#
9156 dw 0xdef8 ;; IRQ bitmap INTC#
9157 db 0x60 ;; link value INTD#
9158 dw 0xdef8 ;; IRQ bitmap INTD#
9159 db 5 ;; physical slot (0 = embedded)
9160 db 0 ;; reserved
9162 pci_irq_list:
9163 db 11, 10, 9, 5;
9165 pcibios_init_sel_reg:
9166 push eax
9167 mov eax, #0x800000
9168 mov ax, bx
9169 shl eax, #8
9170 and dl, #0xfc
9171 or al, dl
9172 mov dx, #0x0cf8
9173 out dx, eax
9174 pop eax
9177 pcibios_init_set_elcr:
9178 push ax
9179 push cx
9180 mov dx, #0x04d0
9181 test al, #0x08
9182 jz is_master_pic
9183 inc dx
9184 and al, #0x07
9185 is_master_pic:
9186 mov cl, al
9187 mov bl, #0x01
9188 shl bl, cl
9189 in al, dx
9190 or al, bl
9191 out dx, al
9192 pop cx
9193 pop ax
9196 pcibios_init:
9197 push ds
9198 push bp
9199 mov ax, #0xf000
9200 mov ds, ax
9201 mov dx, #0x04d0 ;; reset ELCR1 + ELCR2
9202 mov al, #0x00
9203 out dx, al
9204 inc dx
9205 out dx, al
9206 mov si, #pci_routing_table_structure
9207 mov bh, [si+8]
9208 mov bl, [si+9]
9209 mov dl, #0x00
9210 call pcibios_init_sel_reg
9211 mov dx, #0x0cfc
9212 in eax, dx
9213 cmp eax, [si+12] ;; check irq router
9214 jne pci_init_end
9215 mov dl, [si+34]
9216 call pcibios_init_sel_reg
9217 push bx ;; save irq router bus + devfunc
9218 mov dx, #0x0cfc
9219 mov ax, #0x8080
9220 out dx, ax ;; reset PIRQ route control
9221 inc dx
9222 inc dx
9223 out dx, ax
9224 mov ax, [si+6]
9225 sub ax, #0x20
9226 shr ax, #4
9227 mov cx, ax
9228 add si, #0x20 ;; set pointer to 1st entry
9229 mov bp, sp
9230 mov ax, #pci_irq_list
9231 push ax
9232 xor ax, ax
9233 push ax
9234 pci_init_loop1:
9235 mov bh, [si]
9236 mov bl, [si+1]
9237 pci_init_loop2:
9238 mov dl, #0x00
9239 call pcibios_init_sel_reg
9240 mov dx, #0x0cfc
9241 in ax, dx
9242 cmp ax, #0xffff
9243 jnz pci_test_int_pin
9244 test bl, #0x07
9245 jz next_pir_entry
9246 jmp next_pci_func
9247 pci_test_int_pin:
9248 mov dl, #0x3c
9249 call pcibios_init_sel_reg
9250 mov dx, #0x0cfd
9251 in al, dx
9252 and al, #0x07
9253 jz next_pci_func
9254 dec al ;; determine pirq reg
9255 mov dl, #0x03
9256 mul al, dl
9257 add al, #0x02
9258 xor ah, ah
9259 mov bx, ax
9260 mov al, [si+bx]
9261 mov dl, al
9262 mov bx, [bp]
9263 call pcibios_init_sel_reg
9264 mov dx, #0x0cfc
9265 and al, #0x03
9266 add dl, al
9267 in al, dx
9268 cmp al, #0x80
9269 jb pirq_found
9270 mov bx, [bp-2] ;; pci irq list pointer
9271 mov al, [bx]
9272 out dx, al
9273 inc bx
9274 mov [bp-2], bx
9275 call pcibios_init_set_elcr
9276 pirq_found:
9277 mov bh, [si]
9278 mov bl, [si+1]
9279 add bl, [bp-3] ;; pci function number
9280 mov dl, #0x3c
9281 call pcibios_init_sel_reg
9282 mov dx, #0x0cfc
9283 out dx, al
9284 next_pci_func:
9285 inc byte ptr[bp-3]
9286 inc bl
9287 test bl, #0x07
9288 jnz pci_init_loop2
9289 next_pir_entry:
9290 add si, #0x10
9291 mov byte ptr[bp-3], #0x00
9292 loop pci_init_loop1
9293 mov sp, bp
9294 pop bx
9295 pci_init_end:
9296 pop bp
9297 pop ds
9299 #endif // BX_PCIBIOS
9301 ; parallel port detection: base address in DX, index in BX, timeout in CL
9302 detect_parport:
9303 push dx
9304 add dx, #2
9305 in al, dx
9306 and al, #0xdf ; clear input mode
9307 out dx, al
9308 pop dx
9309 mov al, #0xaa
9310 out dx, al
9311 in al, dx
9312 cmp al, #0xaa
9313 jne no_parport
9314 push bx
9315 shl bx, #1
9316 mov [bx+0x408], dx ; Parallel I/O address
9317 pop bx
9318 mov [bx+0x478], cl ; Parallel printer timeout
9319 inc bx
9320 no_parport:
9323 ; serial port detection: base address in DX, index in BX, timeout in CL
9324 detect_serial:
9325 push dx
9326 inc dx
9327 mov al, #0x02
9328 out dx, al
9329 in al, dx
9330 cmp al, #0x02
9331 jne no_serial
9332 inc dx
9333 in al, dx
9334 cmp al, #0x02
9335 jne no_serial
9336 dec dx
9337 xor al, al
9338 out dx, al
9339 pop dx
9340 push bx
9341 shl bx, #1
9342 mov [bx+0x400], dx ; Serial I/O address
9343 pop bx
9344 mov [bx+0x47c], cl ; Serial timeout
9345 inc bx
9347 no_serial:
9348 pop dx
9351 rom_checksum:
9352 push ax
9353 push bx
9354 push cx
9355 xor ax, ax
9356 xor bx, bx
9357 xor cx, cx
9358 mov ch, [2]
9359 shl cx, #1
9360 checksum_loop:
9361 add al, [bx]
9362 inc bx
9363 loop checksum_loop
9364 and al, #0xff
9365 pop cx
9366 pop bx
9367 pop ax
9370 rom_scan:
9371 ;; Scan for existence of valid expansion ROMS.
9372 ;; Video ROM: from 0xC0000..0xC7FFF in 2k increments
9373 ;; General ROM: from 0xC8000..0xDFFFF in 2k increments
9374 ;; System ROM: only 0xE0000
9376 ;; Header:
9377 ;; Offset Value
9378 ;; 0 0x55
9379 ;; 1 0xAA
9380 ;; 2 ROM length in 512-byte blocks
9381 ;; 3 ROM initialization entry point (FAR CALL)
9383 mov cx, #0xc000
9384 rom_scan_loop:
9385 mov ds, cx
9386 mov ax, #0x0004 ;; start with increment of 4 (512-byte) blocks = 2k
9387 cmp [0], #0xAA55 ;; look for signature
9388 jne rom_scan_increment
9389 call rom_checksum
9390 jnz rom_scan_increment
9391 mov al, [2] ;; change increment to ROM length in 512-byte blocks
9393 ;; We want our increment in 512-byte quantities, rounded to
9394 ;; the nearest 2k quantity, since we only scan at 2k intervals.
9395 test al, #0x03
9396 jz block_count_rounded
9397 and al, #0xfc ;; needs rounding up
9398 add al, #0x04
9399 block_count_rounded:
9401 xor bx, bx ;; Restore DS back to 0000:
9402 mov ds, bx
9403 push ax ;; Save AX
9404 ;; Push addr of ROM entry point
9405 push cx ;; Push seg
9406 push #0x0003 ;; Push offset
9407 mov bp, sp ;; Call ROM init routine using seg:off on stack
9408 db 0xff ;; call_far ss:[bp+0]
9409 db 0x5e
9410 db 0
9411 cli ;; In case expansion ROM BIOS turns IF on
9412 add sp, #2 ;; Pop offset value
9413 pop cx ;; Pop seg value (restore CX)
9414 pop ax ;; Restore AX
9415 rom_scan_increment:
9416 shl ax, #5 ;; convert 512-bytes blocks to 16-byte increments
9417 ;; because the segment selector is shifted left 4 bits.
9418 add cx, ax
9419 cmp cx, #0xe000
9420 jbe rom_scan_loop
9422 xor ax, ax ;; Restore DS back to 0000:
9423 mov ds, ax
9426 ;; for 'C' strings and other data, insert them here with
9427 ;; a the following hack:
9428 ;; DATA_SEG_DEFS_HERE
9431 ;--------
9432 ;- POST -
9433 ;--------
9434 .org 0xe05b ; POST Entry Point
9435 post:
9437 xor ax, ax
9439 ;; first reset the DMA controllers
9440 out 0x0d,al
9441 out 0xda,al
9443 ;; then initialize the DMA controllers
9444 mov al, #0xC0
9445 out 0xD6, al ; cascade mode of channel 4 enabled
9446 mov al, #0x00
9447 out 0xD4, al ; unmask channel 4
9449 ;; Examine CMOS shutdown status.
9450 mov AL, #0x0f
9451 out 0x70, AL
9452 in AL, 0x71
9454 ;; backup status
9455 mov bl, al
9457 ;; Reset CMOS shutdown status.
9458 mov AL, #0x0f
9459 out 0x70, AL ; select CMOS register Fh
9460 mov AL, #0x00
9461 out 0x71, AL ; set shutdown action to normal
9463 ;; Examine CMOS shutdown status.
9464 mov al, bl
9466 ;; 0x00, 0x09, 0x0D+ = normal startup
9467 cmp AL, #0x00
9468 jz normal_post
9469 cmp AL, #0x0d
9470 jae normal_post
9471 cmp AL, #0x09
9472 je normal_post
9474 ;; 0x05 = eoi + jmp via [0x40:0x67] jump
9475 cmp al, #0x05
9476 je eoi_jmp_post
9478 ;; Examine CMOS shutdown status.
9479 ;; 0x01,0x02,0x03,0x04,0x06,0x07,0x08, 0x0a, 0x0b, 0x0c = Unimplemented shutdown status.
9480 push bx
9481 call _shutdown_status_panic
9483 #if 0
9484 HALT(__LINE__)
9486 ;#if 0
9487 ; 0xb0, 0x20, /* mov al, #0x20 */
9488 ; 0xe6, 0x20, /* out 0x20, al ;send EOI to PIC */
9489 ;#endif
9491 pop es
9492 pop ds
9493 popa
9494 iret
9495 #endif
9497 normal_post:
9498 ; case 0: normal startup
9501 mov ax, #0xfffe
9502 mov sp, ax
9503 mov ax, #0x0000
9504 mov ds, ax
9505 mov ss, ax
9507 ;; zero out BIOS data area (40:00..40:ff)
9508 mov es, ax
9509 mov cx, #0x0080 ;; 128 words
9510 mov di, #0x0400
9513 stosw
9515 call _log_bios_start
9517 ;; set all interrupts to default handler
9518 mov bx, #0x0000 ;; offset index
9519 mov cx, #0x0100 ;; counter (256 interrupts)
9520 mov ax, #dummy_iret_handler
9521 mov dx, #0xF000
9523 post_default_ints:
9524 mov [bx], ax
9525 inc bx
9526 inc bx
9527 mov [bx], dx
9528 inc bx
9529 inc bx
9530 loop post_default_ints
9532 ;; set vector 0x79 to zero
9533 ;; this is used by 'gardian angel' protection system
9534 SET_INT_VECTOR(0x79, #0, #0)
9536 ;; base memory in K 40:13 (word)
9537 mov ax, #BASE_MEM_IN_K
9538 mov 0x0413, ax
9541 ;; Manufacturing Test 40:12
9542 ;; zerod out above
9544 ;; Warm Boot Flag 0040:0072
9545 ;; value of 1234h = skip memory checks
9546 ;; zerod out above
9549 ;; Printer Services vector
9550 SET_INT_VECTOR(0x17, #0xF000, #int17_handler)
9552 ;; Bootstrap failure vector
9553 SET_INT_VECTOR(0x18, #0xF000, #int18_handler)
9555 ;; Bootstrap Loader vector
9556 SET_INT_VECTOR(0x19, #0xF000, #int19_handler)
9558 ;; User Timer Tick vector
9559 SET_INT_VECTOR(0x1c, #0xF000, #int1c_handler)
9561 ;; Memory Size Check vector
9562 SET_INT_VECTOR(0x12, #0xF000, #int12_handler)
9564 ;; Equipment Configuration Check vector
9565 SET_INT_VECTOR(0x11, #0xF000, #int11_handler)
9567 ;; System Services
9568 SET_INT_VECTOR(0x15, #0xF000, #int15_handler)
9570 ;; EBDA setup
9571 call ebda_post
9573 ;; PIT setup
9574 SET_INT_VECTOR(0x08, #0xF000, #int08_handler)
9575 ;; int 1C already points at dummy_iret_handler (above)
9576 mov al, #0x34 ; timer0: binary count, 16bit count, mode 2
9577 out 0x43, al
9578 mov al, #0x00 ; maximum count of 0000H = 18.2Hz
9579 out 0x40, al
9580 out 0x40, al
9582 ;; Keyboard
9583 SET_INT_VECTOR(0x09, #0xF000, #int09_handler)
9584 SET_INT_VECTOR(0x16, #0xF000, #int16_handler)
9586 xor ax, ax
9587 mov ds, ax
9588 mov 0x0417, al /* keyboard shift flags, set 1 */
9589 mov 0x0418, al /* keyboard shift flags, set 2 */
9590 mov 0x0419, al /* keyboard alt-numpad work area */
9591 mov 0x0471, al /* keyboard ctrl-break flag */
9592 mov 0x0497, al /* keyboard status flags 4 */
9593 mov al, #0x10
9594 mov 0x0496, al /* keyboard status flags 3 */
9597 /* keyboard head of buffer pointer */
9598 mov bx, #0x001E
9599 mov 0x041A, bx
9601 /* keyboard end of buffer pointer */
9602 mov 0x041C, bx
9604 /* keyboard pointer to start of buffer */
9605 mov bx, #0x001E
9606 mov 0x0480, bx
9608 /* keyboard pointer to end of buffer */
9609 mov bx, #0x003E
9610 mov 0x0482, bx
9612 /* init the keyboard */
9613 call _keyboard_init
9615 ;; mov CMOS Equipment Byte to BDA Equipment Word
9616 mov ax, 0x0410
9617 mov al, #0x14
9618 out 0x70, al
9619 in al, 0x71
9620 mov 0x0410, ax
9623 ;; Parallel setup
9624 SET_INT_VECTOR(0x0F, #0xF000, #dummy_iret_handler)
9625 xor ax, ax
9626 mov ds, ax
9627 xor bx, bx
9628 mov cl, #0x14 ; timeout value
9629 mov dx, #0x378 ; Parallel I/O address, port 1
9630 call detect_parport
9631 mov dx, #0x278 ; Parallel I/O address, port 2
9632 call detect_parport
9633 shl bx, #0x0e
9634 mov ax, 0x410 ; Equipment word bits 14..15 determing # parallel ports
9635 and ax, #0x3fff
9636 or ax, bx ; set number of parallel ports
9637 mov 0x410, ax
9639 ;; Serial setup
9640 SET_INT_VECTOR(0x0C, #0xF000, #dummy_iret_handler)
9641 SET_INT_VECTOR(0x14, #0xF000, #int14_handler)
9642 xor bx, bx
9643 mov cl, #0x0a ; timeout value
9644 mov dx, #0x03f8 ; Serial I/O address, port 1
9645 call detect_serial
9646 mov dx, #0x02f8 ; Serial I/O address, port 2
9647 call detect_serial
9648 mov dx, #0x03e8 ; Serial I/O address, port 3
9649 call detect_serial
9650 mov dx, #0x02e8 ; Serial I/O address, port 4
9651 call detect_serial
9652 shl bx, #0x09
9653 mov ax, 0x410 ; Equipment word bits 9..11 determing # serial ports
9654 and ax, #0xf1ff
9655 or ax, bx ; set number of serial port
9656 mov 0x410, ax
9658 ;; CMOS RTC
9659 SET_INT_VECTOR(0x1A, #0xF000, #int1a_handler)
9660 SET_INT_VECTOR(0x4A, #0xF000, #dummy_iret_handler)
9661 SET_INT_VECTOR(0x70, #0xF000, #int70_handler)
9662 ;; BIOS DATA AREA 0x4CE ???
9663 call timer_tick_post
9665 ;; PS/2 mouse setup
9666 SET_INT_VECTOR(0x74, #0xF000, #int74_handler)
9668 ;; IRQ13 (FPU exception) setup
9669 SET_INT_VECTOR(0x75, #0xF000, #int75_handler)
9671 ;; Video setup
9672 SET_INT_VECTOR(0x10, #0xF000, #int10_handler)
9674 ;; PIC
9675 mov al, #0x11 ; send initialisation commands
9676 out 0x20, al
9677 out 0xa0, al
9678 mov al, #0x08
9679 out 0x21, al
9680 mov al, #0x70
9681 out 0xa1, al
9682 mov al, #0x04
9683 out 0x21, al
9684 mov al, #0x02
9685 out 0xa1, al
9686 mov al, #0x01
9687 out 0x21, al
9688 out 0xa1, al
9689 mov al, #0xb8
9690 out 0x21, AL ;master pic: unmask IRQ 0, 1, 2, 6
9691 #if BX_USE_PS2_MOUSE
9692 mov al, #0x8f
9693 #else
9694 mov al, #0x9f
9695 #endif
9696 out 0xa1, AL ;slave pic: unmask IRQ 12, 13, 14
9698 call pcibios_init
9700 call rom_scan
9702 call _print_bios_banner
9705 ;; Floppy setup
9707 call floppy_drive_post
9709 #if BX_USE_ATADRV
9712 ;; Hard Drive setup
9714 call hard_drive_post
9717 ;; ATA/ATAPI driver setup
9719 call _ata_init
9720 call _ata_detect
9722 #else // BX_USE_ATADRV
9725 ;; Hard Drive setup
9727 call hard_drive_post
9729 #endif // BX_USE_ATADRV
9731 #if BX_ELTORITO_BOOT
9733 ;; eltorito floppy/harddisk emulation from cd
9735 call _cdemu_init
9737 #endif // BX_ELTORITO_BOOT
9739 int #0x19
9740 //JMP_EP(0x0064) ; INT 19h location
9743 .org 0xe2c3 ; NMI Handler Entry Point
9744 nmi:
9745 ;; FIXME the NMI handler should not panic
9746 ;; but iret when called from int75 (fpu exception)
9747 call _nmi_handler_msg
9748 iret
9750 int75_handler:
9751 out 0xf0, al // clear irq13
9752 call eoi_both_pics // clear interrupt
9753 int 2 // legacy nmi call
9754 iret
9756 ;-------------------------------------------
9757 ;- INT 13h Fixed Disk Services Entry Point -
9758 ;-------------------------------------------
9759 .org 0xe3fe ; INT 13h Fixed Disk Services Entry Point
9760 int13_handler:
9761 //JMPL(int13_relocated)
9762 jmp int13_relocated
9764 .org 0xe401 ; Fixed Disk Parameter Table
9766 ;----------
9767 ;- INT19h -
9768 ;----------
9769 .org 0xe6f2 ; INT 19h Boot Load Service Entry Point
9770 int19_handler:
9772 jmp int19_relocated
9773 ;-------------------------------------------
9774 ;- System BIOS Configuration Data Table
9775 ;-------------------------------------------
9776 .org BIOS_CONFIG_TABLE
9777 db 0x08 ; Table size (bytes) -Lo
9778 db 0x00 ; Table size (bytes) -Hi
9779 db SYS_MODEL_ID
9780 db SYS_SUBMODEL_ID
9781 db BIOS_REVISION
9782 ; Feature byte 1
9783 ; b7: 1=DMA channel 3 used by hard disk
9784 ; b6: 1=2 interrupt controllers present
9785 ; b5: 1=RTC present
9786 ; b4: 1=BIOS calls int 15h/4Fh every key
9787 ; b3: 1=wait for extern event supported (Int 15h/41h)
9788 ; b2: 1=extended BIOS data area used
9789 ; b1: 0=AT or ESDI bus, 1=MicroChannel
9790 ; b0: 1=Dual bus (MicroChannel + ISA)
9791 db (0 << 7) | \
9792 (1 << 6) | \
9793 (1 << 5) | \
9794 (BX_CALL_INT15_4F << 4) | \
9795 (0 << 3) | \
9796 (BX_USE_EBDA << 2) | \
9797 (0 << 1) | \
9798 (0 << 0)
9799 ; Feature byte 2
9800 ; b7: 1=32-bit DMA supported
9801 ; b6: 1=int16h, function 9 supported
9802 ; b5: 1=int15h/C6h (get POS data) supported
9803 ; b4: 1=int15h/C7h (get mem map info) supported
9804 ; b3: 1=int15h/C8h (en/dis CPU) supported
9805 ; b2: 1=non-8042 kb controller
9806 ; b1: 1=data streaming supported
9807 ; b0: reserved
9808 db (0 << 7) | \
9809 (1 << 6) | \
9810 (0 << 5) | \
9811 (0 << 4) | \
9812 (0 << 3) | \
9813 (0 << 2) | \
9814 (0 << 1) | \
9815 (0 << 0)
9816 ; Feature byte 3
9817 ; b7: not used
9818 ; b6: reserved
9819 ; b5: reserved
9820 ; b4: POST supports ROM-to-RAM enable/disable
9821 ; b3: SCSI on system board
9822 ; b2: info panel installed
9823 ; b1: Initial Machine Load (IML) system - BIOS on disk
9824 ; b0: SCSI supported in IML
9825 db 0x00
9826 ; Feature byte 4
9827 ; b7: IBM private
9828 ; b6: EEPROM present
9829 ; b5-3: ABIOS presence (011 = not supported)
9830 ; b2: private
9831 ; b1: memory split above 16Mb supported
9832 ; b0: POSTEXT directly supported by POST
9833 db 0x00
9834 ; Feature byte 5 (IBM)
9835 ; b1: enhanced mouse
9836 ; b0: flash EPROM
9837 db 0x00
9841 .org 0xe729 ; Baud Rate Generator Table
9843 ;----------
9844 ;- INT14h -
9845 ;----------
9846 .org 0xe739 ; INT 14h Serial Communications Service Entry Point
9847 int14_handler:
9848 push ds
9849 pusha
9850 mov ax, #0x0000
9851 mov ds, ax
9852 call _int14_function
9853 popa
9854 pop ds
9855 iret
9858 ;----------------------------------------
9859 ;- INT 16h Keyboard Service Entry Point -
9860 ;----------------------------------------
9861 .org 0xe82e
9862 int16_handler:
9865 push ds
9866 pushf
9867 pusha
9869 cmp ah, #0x00
9870 je int16_F00
9871 cmp ah, #0x10
9872 je int16_F00
9874 mov bx, #0xf000
9875 mov ds, bx
9876 call _int16_function
9877 popa
9878 popf
9879 pop ds
9880 jz int16_zero_set
9882 int16_zero_clear:
9883 push bp
9884 mov bp, sp
9885 //SEG SS
9886 and BYTE [bp + 0x06], #0xbf
9887 pop bp
9888 iret
9890 int16_zero_set:
9891 push bp
9892 mov bp, sp
9893 //SEG SS
9894 or BYTE [bp + 0x06], #0x40
9895 pop bp
9896 iret
9898 int16_F00:
9899 mov bx, #0x0040
9900 mov ds, bx
9902 int16_wait_for_key:
9904 mov bx, 0x001a
9905 cmp bx, 0x001c
9906 jne int16_key_found
9909 #if 0
9910 /* no key yet, call int 15h, function AX=9002 */
9911 0x50, /* push AX */
9912 0xb8, 0x02, 0x90, /* mov AX, #0x9002 */
9913 0xcd, 0x15, /* int 15h */
9914 0x58, /* pop AX */
9915 0xeb, 0xea, /* jmp WAIT_FOR_KEY */
9916 #endif
9917 jmp int16_wait_for_key
9919 int16_key_found:
9920 mov bx, #0xf000
9921 mov ds, bx
9922 call _int16_function
9923 popa
9924 popf
9925 pop ds
9926 #if 0
9927 /* notify int16 complete w/ int 15h, function AX=9102 */
9928 0x50, /* push AX */
9929 0xb8, 0x02, 0x91, /* mov AX, #0x9102 */
9930 0xcd, 0x15, /* int 15h */
9931 0x58, /* pop AX */
9932 #endif
9933 iret
9937 ;-------------------------------------------------
9938 ;- INT09h : Keyboard Hardware Service Entry Point -
9939 ;-------------------------------------------------
9940 .org 0xe987
9941 int09_handler:
9943 push ax
9945 mov al, #0xAD ;;disable keyboard
9946 out #0x64, al
9948 mov al, #0x0B
9949 out #0x20, al
9950 in al, #0x20
9951 and al, #0x02
9952 jz int09_finish
9954 in al, #0x60 ;;read key from keyboard controller
9955 //test al, #0x80 ;;look for key release
9956 //jnz int09_process_key ;; dont pass releases to intercept?
9958 ;; check for extended key
9959 cmp al, #0xe0
9960 jne int09_call_int15_4f
9962 push ds
9963 xor ax, ax
9964 mov ds, ax
9965 mov al, BYTE [0x496] ;; mf2_state |= 0x01
9966 or al, #0x01
9967 mov BYTE [0x496], al
9968 pop ds
9970 in al, #0x60 ;;read another key from keyboard controller
9974 int09_call_int15_4f:
9975 push ds
9976 pusha
9977 #ifdef BX_CALL_INT15_4F
9978 mov ah, #0x4f ;; allow for keyboard intercept
9980 int #0x15
9981 jnc int09_done
9982 #endif
9985 //int09_process_key:
9986 mov bx, #0xf000
9987 mov ds, bx
9988 call _int09_function
9990 int09_done:
9991 popa
9992 pop ds
9994 call eoi_master_pic
9996 int09_finish:
9997 mov al, #0xAE ;;enable keyboard
9998 out #0x64, al
9999 pop ax
10000 iret
10005 ;----------------------------------------
10006 ;- INT 13h Diskette Service Entry Point -
10007 ;----------------------------------------
10008 .org 0xec59
10009 int13_diskette:
10010 jmp int13_noeltorito
10012 ;---------------------------------------------
10013 ;- INT 0Eh Diskette Hardware ISR Entry Point -
10014 ;---------------------------------------------
10015 .org 0xef57 ; INT 0Eh Diskette Hardware ISR Entry Point
10016 int0e_handler:
10017 push ax
10018 push dx
10019 mov dx, #0x03f4
10020 in al, dx
10021 and al, #0xc0
10022 cmp al, #0xc0
10023 je int0e_normal
10024 mov dx, #0x03f5
10025 mov al, #0x08 ; sense interrupt status
10026 out dx, al
10027 int0e_loop1:
10028 mov dx, #0x03f4
10029 in al, dx
10030 and al, #0xc0
10031 cmp al, #0xc0
10032 jne int0e_loop1
10033 int0e_loop2:
10034 mov dx, #0x03f5
10035 in al, dx
10036 mov dx, #0x03f4
10037 in al, dx
10038 and al, #0xc0
10039 cmp al, #0xc0
10040 je int0e_loop2
10041 int0e_normal:
10042 push ds
10043 mov ax, #0x0000 ;; segment 0000
10044 mov ds, ax
10045 call eoi_master_pic
10046 mov al, 0x043e
10047 or al, #0x80 ;; diskette interrupt has occurred
10048 mov 0x043e, al
10049 pop ds
10050 pop dx
10051 pop ax
10052 iret
10055 .org 0xefc7 ; Diskette Controller Parameter Table
10056 diskette_param_table:
10057 ;; Since no provisions are made for multiple drive types, most
10058 ;; values in this table are ignored. I set parameters for 1.44M
10059 ;; floppy here
10060 db 0xAF
10061 db 0x02 ;; head load time 0000001, DMA used
10062 db 0x25
10063 db 0x02
10064 db 18
10065 db 0x1B
10066 db 0xFF
10067 db 0x6C
10068 db 0xF6
10069 db 0x0F
10070 db 0x08
10073 ;----------------------------------------
10074 ;- INT17h : Printer Service Entry Point -
10075 ;----------------------------------------
10076 .org 0xefd2
10077 int17_handler:
10078 push ds
10079 pusha
10080 mov ax, #0x0000
10081 mov ds, ax
10082 call _int17_function
10083 popa
10084 pop ds
10085 iret
10087 diskette_param_table2:
10088 ;; New diskette parameter table adding 3 parameters from IBM
10089 ;; Since no provisions are made for multiple drive types, most
10090 ;; values in this table are ignored. I set parameters for 1.44M
10091 ;; floppy here
10092 db 0xAF
10093 db 0x02 ;; head load time 0000001, DMA used
10094 db 0x25
10095 db 0x02
10096 db 18
10097 db 0x1B
10098 db 0xFF
10099 db 0x6C
10100 db 0xF6
10101 db 0x0F
10102 db 0x08
10103 db 79 ;; maximum track
10104 db 0 ;; data transfer rate
10105 db 4 ;; drive type in cmos
10107 .org 0xf045 ; INT 10 Functions 0-Fh Entry Point
10108 HALT(__LINE__)
10109 iret
10111 ;----------
10112 ;- INT10h -
10113 ;----------
10114 .org 0xf065 ; INT 10h Video Support Service Entry Point
10115 int10_handler:
10116 ;; dont do anything, since the VGA BIOS handles int10h requests
10117 iret
10119 .org 0xf0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
10121 ;----------
10122 ;- INT12h -
10123 ;----------
10124 .org 0xf841 ; INT 12h Memory Size Service Entry Point
10125 ; ??? different for Pentium (machine check)?
10126 int12_handler:
10127 push ds
10128 mov ax, #0x0040
10129 mov ds, ax
10130 mov ax, 0x0013
10131 pop ds
10132 iret
10134 ;----------
10135 ;- INT11h -
10136 ;----------
10137 .org 0xf84d ; INT 11h Equipment List Service Entry Point
10138 int11_handler:
10139 push ds
10140 mov ax, #0x0040
10141 mov ds, ax
10142 mov ax, 0x0010
10143 pop ds
10144 iret
10146 ;----------
10147 ;- INT15h -
10148 ;----------
10149 .org 0xf859 ; INT 15h System Services Entry Point
10150 int15_handler:
10151 pushf
10152 #if BX_APM
10153 cmp ah, #0x53
10154 je apm_call
10155 #endif
10156 push ds
10157 push es
10158 cmp ah, #0x86
10159 je int15_handler32
10160 cmp ah, #0xE8
10161 je int15_handler32
10162 pusha
10163 #if BX_USE_PS2_MOUSE
10164 cmp ah, #0xC2
10165 je int15_handler_mouse
10166 #endif
10167 call _int15_function
10168 int15_handler_mouse_ret:
10169 popa
10170 int15_handler32_ret:
10171 pop es
10172 pop ds
10173 popf
10174 jmp iret_modify_cf
10175 #if BX_APM
10176 apm_call:
10177 jmp _apmreal_entry
10178 #endif
10180 #if BX_USE_PS2_MOUSE
10181 int15_handler_mouse:
10182 call _int15_function_mouse
10183 jmp int15_handler_mouse_ret
10184 #endif
10186 int15_handler32:
10187 pushad
10188 call _int15_function32
10189 popad
10190 jmp int15_handler32_ret
10192 ;; Protected mode IDT descriptor
10194 ;; I just make the limit 0, so the machine will shutdown
10195 ;; if an exception occurs during protected mode memory
10196 ;; transfers.
10198 ;; Set base to f0000 to correspond to beginning of BIOS,
10199 ;; in case I actually define an IDT later
10200 ;; Set limit to 0
10202 pmode_IDT_info:
10203 dw 0x0000 ;; limit 15:00
10204 dw 0x0000 ;; base 15:00
10205 db 0x0f ;; base 23:16
10207 ;; Real mode IDT descriptor
10209 ;; Set to typical real-mode values.
10210 ;; base = 000000
10211 ;; limit = 03ff
10213 rmode_IDT_info:
10214 dw 0x03ff ;; limit 15:00
10215 dw 0x0000 ;; base 15:00
10216 db 0x00 ;; base 23:16
10219 ;----------
10220 ;- INT1Ah -
10221 ;----------
10222 .org 0xfe6e ; INT 1Ah Time-of-day Service Entry Point
10223 int1a_handler:
10224 #if BX_PCIBIOS
10225 cmp ah, #0xb1
10226 jne int1a_normal
10227 call pcibios_real
10228 jc pcibios_error
10229 retf 2
10230 pcibios_error:
10231 mov bl, ah
10232 mov ah, #0xb1
10233 push ds
10234 pusha
10235 mov ax, ss ; set readable descriptor to ds, for calling pcibios
10236 mov ds, ax ; on 16bit protected mode.
10237 jmp int1a_callfunction
10238 int1a_normal:
10239 #endif
10240 push ds
10241 pusha
10242 xor ax, ax
10243 mov ds, ax
10244 int1a_callfunction:
10245 call _int1a_function
10246 popa
10247 pop ds
10248 iret
10251 ;; int70h: IRQ8 - CMOS RTC
10253 int70_handler:
10254 push ds
10255 pushad
10256 xor ax, ax
10257 mov ds, ax
10258 call _int70_function
10259 popad
10260 pop ds
10261 iret
10263 ;---------
10264 ;- INT08 -
10265 ;---------
10266 .org 0xfea5 ; INT 08h System Timer ISR Entry Point
10267 int08_handler:
10269 push eax
10270 push ds
10271 xor ax, ax
10272 mov ds, ax
10274 ;; time to turn off drive(s)?
10275 mov al,0x0440
10276 or al,al
10277 jz int08_floppy_off
10278 dec al
10279 mov 0x0440,al
10280 jnz int08_floppy_off
10281 ;; turn motor(s) off
10282 push dx
10283 mov dx,#0x03f2
10284 in al,dx
10285 and al,#0xcf
10286 out dx,al
10287 pop dx
10288 int08_floppy_off:
10290 mov eax, 0x046c ;; get ticks dword
10291 inc eax
10293 ;; compare eax to one days worth of timer ticks at 18.2 hz
10294 cmp eax, #0x001800B0
10295 jb int08_store_ticks
10296 ;; there has been a midnight rollover at this point
10297 xor eax, eax ;; zero out counter
10298 inc BYTE 0x0470 ;; increment rollover flag
10300 int08_store_ticks:
10301 mov 0x046c, eax ;; store new ticks dword
10302 ;; chain to user timer tick INT #0x1c
10303 //pushf
10304 //;; call_ep [ds:loc]
10305 //CALL_EP( 0x1c << 2 )
10306 int #0x1c
10308 call eoi_master_pic
10309 pop ds
10310 pop eax
10311 iret
10313 .org 0xfef3 ; Initial Interrupt Vector Offsets Loaded by POST
10316 .org 0xff00
10317 .ascii BIOS_COPYRIGHT_STRING
10319 ;------------------------------------------------
10320 ;- IRET Instruction for Dummy Interrupt Handler -
10321 ;------------------------------------------------
10322 .org 0xff53 ; IRET Instruction for Dummy Interrupt Handler
10323 dummy_iret_handler:
10324 iret
10326 .org 0xff54 ; INT 05h Print Screen Service Entry Point
10327 HALT(__LINE__)
10328 iret
10330 .org 0xfff0 ; Power-up Entry Point
10331 jmp 0xf000:post
10333 .org 0xfff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
10334 .ascii BIOS_BUILD_DATE
10336 .org 0xfffe ; System Model ID
10337 db SYS_MODEL_ID
10338 db 0x00 ; filler
10340 .org 0xfa6e ;; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
10341 ASM_END
10343 * This font comes from the fntcol16.zip package (c) by Joseph Gil
10344 * found at ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip
10345 * This font is public domain
10347 static Bit8u vgafont8[128*8]=
10349 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
10350 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e,
10351 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e,
10352 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00,
10353 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00,
10354 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c,
10355 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c,
10356 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00,
10357 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff,
10358 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
10359 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff,
10360 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78,
10361 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18,
10362 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0,
10363 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0,
10364 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99,
10365 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00,
10366 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00,
10367 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18,
10368 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
10369 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00,
10370 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78,
10371 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00,
10372 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff,
10373 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00,
10374 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
10375 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00,
10376 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
10377 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00,
10378 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00,
10379 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00,
10380 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00,
10381 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
10382 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00,
10383 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
10384 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00,
10385 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00,
10386 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00,
10387 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00,
10388 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
10389 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00,
10390 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
10391 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
10392 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00,
10393 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60,
10394 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
10395 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00,
10396 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00,
10397 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00,
10398 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00,
10399 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00,
10400 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00,
10401 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00,
10402 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00,
10403 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00,
10404 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00,
10405 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00,
10406 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00,
10407 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00,
10408 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60,
10409 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00,
10410 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00,
10411 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00,
10412 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00,
10413 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00,
10414 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00,
10415 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00,
10416 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00,
10417 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00,
10418 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00,
10419 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00,
10420 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00,
10421 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00,
10422 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
10423 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00,
10424 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00,
10425 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00,
10426 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00,
10427 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00,
10428 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00,
10429 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
10430 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00,
10431 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00,
10432 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00,
10433 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
10434 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00,
10435 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
10436 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00,
10437 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00,
10438 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00,
10439 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00,
10440 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
10441 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00,
10442 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
10443 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
10444 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
10445 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
10446 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00,
10447 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00,
10448 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00,
10449 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00,
10450 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
10451 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00,
10452 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
10453 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00,
10454 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
10455 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78,
10456 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00,
10457 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
10458 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00,
10459 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
10460 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
10461 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0,
10462 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e,
10463 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00,
10464 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00,
10465 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00,
10466 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
10467 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
10468 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00,
10469 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00,
10470 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
10471 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00,
10472 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00,
10473 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
10474 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00,
10475 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
10476 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00,
10479 ASM_START
10480 .org 0xcc00
10481 // bcc-generated data will be placed here
10483 // For documentation of this config structure, look on developer.intel.com and
10484 // search for multiprocessor specification. Note that when you change anything
10485 // you must update the checksum (a pain!). It would be better to construct this
10486 // with C structures, or at least fill in the checksum automatically.
10488 // Maybe this structs could be moved elsewhere than d000
10490 #if (BX_SMP_PROCESSORS==1)
10491 // no structure necessary.
10492 #elif (BX_SMP_PROCESSORS==2)
10493 // define the Intel MP Configuration Structure for 2 processors at
10494 // APIC ID 0,1. I/O APIC at ID=2.
10495 .align 16
10496 mp_config_table:
10497 db 0x50, 0x43, 0x4d, 0x50 ;; "PCMP" signature
10498 dw (mp_config_end-mp_config_table) ;; table length
10499 db 4 ;; spec rev
10500 db 0x65 ;; checksum
10501 .ascii "BOCHSCPU" ;; OEM id = "BOCHSCPU"
10502 db 0x30, 0x2e, 0x31, 0x20 ;; vendor id = "0.1 "
10503 db 0x20, 0x20, 0x20, 0x20
10504 db 0x20, 0x20, 0x20, 0x20
10505 dw 0,0 ;; oem table ptr
10506 dw 0 ;; oem table size
10507 dw 20 ;; entry count
10508 dw 0x0000, 0xfee0 ;; memory mapped address of local APIC
10509 dw 0 ;; extended table length
10510 db 0 ;; extended table checksum
10511 db 0 ;; reserved
10512 mp_config_proc0:
10513 db 0 ;; entry type=processor
10514 db 0 ;; local APIC id
10515 db 0x11 ;; local APIC version number
10516 db 3 ;; cpu flags: enabled, bootstrap processor
10517 db 0,6,0,0 ;; cpu signature
10518 dw 0x201,0 ;; feature flags
10519 dw 0,0 ;; reserved
10520 dw 0,0 ;; reserved
10521 mp_config_proc1:
10522 db 0 ;; entry type=processor
10523 db 1 ;; local APIC id
10524 db 0x11 ;; local APIC version number
10525 db 1 ;; cpu flags: enabled
10526 db 0,6,0,0 ;; cpu signature
10527 dw 0x201,0 ;; feature flags
10528 dw 0,0 ;; reserved
10529 dw 0,0 ;; reserved
10530 mp_config_isa_bus:
10531 db 1 ;; entry type=bus
10532 db 0 ;; bus ID
10533 db 0x49, 0x53, 0x41, 0x20, 0x20, 0x20 ;; bus type="ISA "
10534 mp_config_ioapic:
10535 db 2 ;; entry type=I/O APIC
10536 db 2 ;; apic id=2. linux will set.
10537 db 0x11 ;; I/O APIC version number
10538 db 1 ;; flags=1=enabled
10539 dw 0x0000, 0xfec0 ;; memory mapped address of I/O APIC
10540 mp_config_irqs:
10541 db 3 ;; entry type=I/O interrupt
10542 db 0 ;; interrupt type=vectored interrupt
10543 db 0,0 ;; flags po=0, el=0 (linux uses as default)
10544 db 0 ;; source bus ID is ISA
10545 db 0 ;; source bus IRQ
10546 db 2 ;; destination I/O APIC ID
10547 db 0 ;; destination I/O APIC interrrupt in
10548 ;; repeat pattern for interrupts 0-15
10549 db 3,0,0,0,0,1,2,1
10550 db 3,0,0,0,0,2,2,2
10551 db 3,0,0,0,0,3,2,3
10552 db 3,0,0,0,0,4,2,4
10553 db 3,0,0,0,0,5,2,5
10554 db 3,0,0,0,0,6,2,6
10555 db 3,0,0,0,0,7,2,7
10556 db 3,0,0,0,0,8,2,8
10557 db 3,0,0,0,0,9,2,9
10558 db 3,0,0,0,0,10,2,10
10559 db 3,0,0,0,0,11,2,11
10560 db 3,0,0,0,0,12,2,12
10561 db 3,0,0,0,0,13,2,13
10562 db 3,0,0,0,0,14,2,14
10563 db 3,0,0,0,0,15,2,15
10564 #elif (BX_SMP_PROCESSORS==4)
10565 // define the Intel MP Configuration Structure for 4 processors at
10566 // APIC ID 0,1,2,3. I/O APIC at ID=4.
10567 .align 16
10568 mp_config_table:
10569 db 0x50, 0x43, 0x4d, 0x50 ;; "PCMP" signature
10570 dw (mp_config_end-mp_config_table) ;; table length
10571 db 4 ;; spec rev
10572 db 0xdd ;; checksum
10573 .ascii "BOCHSCPU" ;; OEM id = "BOCHSCPU"
10574 db 0x30, 0x2e, 0x31, 0x20 ;; vendor id = "0.1 "
10575 db 0x20, 0x20, 0x20, 0x20
10576 db 0x20, 0x20, 0x20, 0x20
10577 dw 0,0 ;; oem table ptr
10578 dw 0 ;; oem table size
10579 dw 22 ;; entry count
10580 dw 0x0000, 0xfee0 ;; memory mapped address of local APIC
10581 dw 0 ;; extended table length
10582 db 0 ;; extended table checksum
10583 db 0 ;; reserved
10584 mp_config_proc0:
10585 db 0 ;; entry type=processor
10586 db 0 ;; local APIC id
10587 db 0x11 ;; local APIC version number
10588 db 3 ;; cpu flags: enabled, bootstrap processor
10589 db 0,6,0,0 ;; cpu signature
10590 dw 0x201,0 ;; feature flags
10591 dw 0,0 ;; reserved
10592 dw 0,0 ;; reserved
10593 mp_config_proc1:
10594 db 0 ;; entry type=processor
10595 db 1 ;; local APIC id
10596 db 0x11 ;; local APIC version number
10597 db 1 ;; cpu flags: enabled
10598 db 0,6,0,0 ;; cpu signature
10599 dw 0x201,0 ;; feature flags
10600 dw 0,0 ;; reserved
10601 dw 0,0 ;; reserved
10602 mp_config_proc2:
10603 db 0 ;; entry type=processor
10604 db 2 ;; local APIC id
10605 db 0x11 ;; local APIC version number
10606 db 1 ;; cpu flags: enabled
10607 db 0,6,0,0 ;; cpu signature
10608 dw 0x201,0 ;; feature flags
10609 dw 0,0 ;; reserved
10610 dw 0,0 ;; reserved
10611 mp_config_proc3:
10612 db 0 ;; entry type=processor
10613 db 3 ;; local APIC id
10614 db 0x11 ;; local APIC version number
10615 db 1 ;; cpu flags: enabled
10616 db 0,6,0,0 ;; cpu signature
10617 dw 0x201,0 ;; feature flags
10618 dw 0,0 ;; reserved
10619 dw 0,0 ;; reserved
10620 mp_config_isa_bus:
10621 db 1 ;; entry type=bus
10622 db 0 ;; bus ID
10623 db 0x49, 0x53, 0x41, 0x20, 0x20, 0x20 ;; bus type="ISA "
10624 mp_config_ioapic:
10625 db 2 ;; entry type=I/O APIC
10626 db 4 ;; apic id=4. linux will set.
10627 db 0x11 ;; I/O APIC version number
10628 db 1 ;; flags=1=enabled
10629 dw 0x0000, 0xfec0 ;; memory mapped address of I/O APIC
10630 mp_config_irqs:
10631 db 3 ;; entry type=I/O interrupt
10632 db 0 ;; interrupt type=vectored interrupt
10633 db 0,0 ;; flags po=0, el=0 (linux uses as default)
10634 db 0 ;; source bus ID is ISA
10635 db 0 ;; source bus IRQ
10636 db 4 ;; destination I/O APIC ID
10637 db 0 ;; destination I/O APIC interrrupt in
10638 ;; repeat pattern for interrupts 0-15
10639 db 3,0,0,0,0,1,4,1
10640 db 3,0,0,0,0,2,4,2
10641 db 3,0,0,0,0,3,4,3
10642 db 3,0,0,0,0,4,4,4
10643 db 3,0,0,0,0,5,4,5
10644 db 3,0,0,0,0,6,4,6
10645 db 3,0,0,0,0,7,4,7
10646 db 3,0,0,0,0,8,4,8
10647 db 3,0,0,0,0,9,4,9
10648 db 3,0,0,0,0,10,4,10
10649 db 3,0,0,0,0,11,4,11
10650 db 3,0,0,0,0,12,4,12
10651 db 3,0,0,0,0,13,4,13
10652 db 3,0,0,0,0,14,4,14
10653 db 3,0,0,0,0,15,4,15
10654 #elif (BX_SMP_PROCESSORS==8)
10655 // define the Intel MP Configuration Structure for 8 processors at
10656 // APIC ID 0,1,2,3,4,5,6,7. I/O APIC at ID=8.
10657 .align 16
10658 mp_config_table:
10659 db 0x50, 0x43, 0x4d, 0x50 ;; "PCMP" signature
10660 dw (mp_config_end-mp_config_table) ;; table length
10661 db 4 ;; spec rev
10662 db 0xc3 ;; checksum
10663 .ascii "BOCHSCPU" ;; OEM id = "BOCHSCPU"
10664 db 0x30, 0x2e, 0x31, 0x20 ;; vendor id = "0.1 "
10665 db 0x20, 0x20, 0x20, 0x20
10666 db 0x20, 0x20, 0x20, 0x20
10667 dw 0,0 ;; oem table ptr
10668 dw 0 ;; oem table size
10669 dw 26 ;; entry count
10670 dw 0x0000, 0xfee0 ;; memory mapped address of local APIC
10671 dw 0 ;; extended table length
10672 db 0 ;; extended table checksum
10673 db 0 ;; reserved
10674 mp_config_proc0:
10675 db 0 ;; entry type=processor
10676 db 0 ;; local APIC id
10677 db 0x11 ;; local APIC version number
10678 db 3 ;; cpu flags: enabled, bootstrap processor
10679 db 0,6,0,0 ;; cpu signature
10680 dw 0x201,0 ;; feature flags
10681 dw 0,0 ;; reserved
10682 dw 0,0 ;; reserved
10683 mp_config_proc1:
10684 db 0 ;; entry type=processor
10685 db 1 ;; local APIC id
10686 db 0x11 ;; local APIC version number
10687 db 1 ;; cpu flags: enabled
10688 db 0,6,0,0 ;; cpu signature
10689 dw 0x201,0 ;; feature flags
10690 dw 0,0 ;; reserved
10691 dw 0,0 ;; reserved
10692 mp_config_proc2:
10693 db 0 ;; entry type=processor
10694 db 2 ;; local APIC id
10695 db 0x11 ;; local APIC version number
10696 db 1 ;; cpu flags: enabled
10697 db 0,6,0,0 ;; cpu signature
10698 dw 0x201,0 ;; feature flags
10699 dw 0,0 ;; reserved
10700 dw 0,0 ;; reserved
10701 mp_config_proc3:
10702 db 0 ;; entry type=processor
10703 db 3 ;; local APIC id
10704 db 0x11 ;; local APIC version number
10705 db 1 ;; cpu flags: enabled
10706 db 0,6,0,0 ;; cpu signature
10707 dw 0x201,0 ;; feature flags
10708 dw 0,0 ;; reserved
10709 dw 0,0 ;; reserved
10710 mp_config_proc4:
10711 db 0 ;; entry type=processor
10712 db 4 ;; local APIC id
10713 db 0x11 ;; local APIC version number
10714 db 1 ;; cpu flags: enabled
10715 db 0,6,0,0 ;; cpu signature
10716 dw 0x201,0 ;; feature flags
10717 dw 0,0 ;; reserved
10718 dw 0,0 ;; reserved
10719 mp_config_proc5:
10720 db 0 ;; entry type=processor
10721 db 5 ;; local APIC id
10722 db 0x11 ;; local APIC version number
10723 db 1 ;; cpu flags: enabled
10724 db 0,6,0,0 ;; cpu signature
10725 dw 0x201,0 ;; feature flags
10726 dw 0,0 ;; reserved
10727 dw 0,0 ;; reserved
10728 mp_config_proc6:
10729 db 0 ;; entry type=processor
10730 db 6 ;; local APIC id
10731 db 0x11 ;; local APIC version number
10732 db 1 ;; cpu flags: enabled
10733 db 0,6,0,0 ;; cpu signature
10734 dw 0x201,0 ;; feature flags
10735 dw 0,0 ;; reserved
10736 dw 0,0 ;; reserved
10737 mp_config_proc7:
10738 db 0 ;; entry type=processor
10739 db 7 ;; local APIC id
10740 db 0x11 ;; local APIC version number
10741 db 1 ;; cpu flags: enabled
10742 db 0,6,0,0 ;; cpu signature
10743 dw 0x201,0 ;; feature flags
10744 dw 0,0 ;; reserved
10745 dw 0,0 ;; reserved
10746 mp_config_isa_bus:
10747 db 1 ;; entry type=bus
10748 db 0 ;; bus ID
10749 db 0x49, 0x53, 0x41, 0x20, 0x20, 0x20 ;; bus type="ISA "
10750 mp_config_ioapic:
10751 db 2 ;; entry type=I/O APIC
10752 db 8 ;; apic id=8
10753 db 0x11 ;; I/O APIC version number
10754 db 1 ;; flags=1=enabled
10755 dw 0x0000, 0xfec0 ;; memory mapped address of I/O APIC
10756 mp_config_irqs:
10757 db 3 ;; entry type=I/O interrupt
10758 db 0 ;; interrupt type=vectored interrupt
10759 db 0,0 ;; flags po=0, el=0 (linux uses as default)
10760 db 0 ;; source bus ID is ISA
10761 db 0 ;; source bus IRQ
10762 db 8 ;; destination I/O APIC ID
10763 db 0 ;; destination I/O APIC interrrupt in
10764 ;; repeat pattern for interrupts 0-15
10765 db 3,0,0,0,0,1,8,1
10766 db 3,0,0,0,0,2,8,2
10767 db 3,0,0,0,0,3,8,3
10768 db 3,0,0,0,0,4,8,4
10769 db 3,0,0,0,0,5,8,5
10770 db 3,0,0,0,0,6,8,6
10771 db 3,0,0,0,0,7,8,7
10772 db 3,0,0,0,0,8,8,8
10773 db 3,0,0,0,0,9,8,9
10774 db 3,0,0,0,0,10,8,10
10775 db 3,0,0,0,0,11,8,11
10776 db 3,0,0,0,0,12,8,12
10777 db 3,0,0,0,0,13,8,13
10778 db 3,0,0,0,0,14,8,14
10779 db 3,0,0,0,0,15,8,15
10780 #else
10781 # error Sorry, rombios only has configurations for 1, 2, 4 or 8 processors.
10782 #endif // if (BX_SMP_PROCESSORS==...)
10784 mp_config_end: // this label used to find length of mp structure
10785 db 0
10787 #if (BX_SMP_PROCESSORS>1)
10788 .align 16
10789 mp_floating_pointer_structure:
10790 db 0x5f, 0x4d, 0x50, 0x5f ; "_MP_" signature
10791 dw mp_config_table, 0xf ;; pointer to MP configuration table
10792 db 1 ;; length of this struct in 16-bit byte chunks
10793 db 4 ;; MP spec revision
10794 db 0xc1 ;; checksum
10795 db 0 ;; MP feature byte 1. value 0 means look at the config table
10796 db 0,0,0,0 ;; MP feature bytes 2-5.
10797 #endif
10799 ASM_END