- fixed flag for enhanced keycode 0xE0
[gplbios.git] / rombios.c
blobaa357e8f83768cd3cb7f7e368cd20a95c102d058
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 ");
1832 printf("%s %s\n", bios_cvs_version_string, bios_date_string);
1833 printf("Options: ");
1834 #if BX_SMP_PROCESSORS > 1
1835 printf("smp (%d cpus) ", BX_SMP_PROCESSORS);
1836 #else
1837 printf("1 cpu ");
1838 #endif
1839 printf(
1840 #ifdef BX_APM
1841 "apmbios "
1842 #endif
1843 #ifdef BX_PCIBIOS
1844 "pcibios "
1845 #endif
1846 #ifdef BX_ELTORITO_BOOT
1847 "eltorito "
1848 #endif
1849 "\n\n");
1852 //--------------------------------------------------------------------------
1853 // print_boot_device
1854 // displays the boot device
1855 //--------------------------------------------------------------------------
1857 static char drivetypes[][10]={"Floppy","Hard Disk","CD-Rom"};
1859 void
1860 print_boot_device(cdboot, drive)
1861 Bit8u cdboot; Bit16u drive;
1863 Bit8u i;
1865 // cdboot contains 0 if floppy/harddisk, 1 otherwise
1866 // drive contains real/emulated boot drive
1868 if(cdboot)i=2; // CD-Rom
1869 else if((drive&0x0080)==0x00)i=0; // Floppy
1870 else if((drive&0x0080)==0x80)i=1; // Hard drive
1871 else return;
1873 printf("Booting from %s...\n",drivetypes[i]);
1876 //--------------------------------------------------------------------------
1877 // print_boot_failure
1878 // displays the reason why boot failed
1879 //--------------------------------------------------------------------------
1880 void
1881 print_boot_failure(cdboot, drive, reason, lastdrive)
1882 Bit8u cdboot; Bit8u drive; Bit8u lastdrive;
1884 Bit16u drivenum = drive&0x7f;
1886 // cdboot: 1 if boot from cd, 0 otherwise
1887 // drive : drive number
1888 // reason: 0 signature check failed, 1 read error
1889 // lastdrive: 1 boot drive is the last one in boot sequence
1891 if (cdboot)
1892 bios_printf(BIOS_PRINTF_INFO | BIOS_PRINTF_SCREEN, "Boot from %s failed\n",drivetypes[2]);
1893 else if (drive & 0x80)
1894 bios_printf(BIOS_PRINTF_INFO | BIOS_PRINTF_SCREEN, "Boot from %s %d failed\n", drivetypes[1],drivenum);
1895 else
1896 bios_printf(BIOS_PRINTF_INFO | BIOS_PRINTF_SCREEN, "Boot from %s %d failed\n", drivetypes[0],drivenum);
1898 if (lastdrive==1) {
1899 if (reason==0)
1900 BX_PANIC("Not a bootable disk\n");
1901 else
1902 BX_PANIC("Could not read the boot disk\n");
1906 //--------------------------------------------------------------------------
1907 // print_cdromboot_failure
1908 // displays the reason why boot failed
1909 //--------------------------------------------------------------------------
1910 void
1911 print_cdromboot_failure( code )
1912 Bit16u code;
1914 bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, "CDROM boot failure code : %04x\n",code);
1916 return;
1919 void
1920 nmi_handler_msg()
1922 BX_PANIC("NMI Handler called\n");
1925 void
1926 int18_panic_msg()
1928 BX_PANIC("INT18: BOOT FAILURE\n");
1931 void
1932 log_bios_start()
1934 #if BX_DEBUG_SERIAL
1935 outb(BX_DEBUG_PORT+UART_LCR, 0x03); /* setup for serial logging: 8N1 */
1936 #endif
1937 BX_INFO("%s\n", bios_version_string);
1940 bx_bool
1941 set_enable_a20(val)
1942 bx_bool val;
1944 Bit8u oldval;
1946 // Use PS2 System Control port A to set A20 enable
1948 // get current setting first
1949 oldval = inb(0x92);
1951 // change A20 status
1952 if (val)
1953 outb(0x92, oldval | 0x02);
1954 else
1955 outb(0x92, oldval & 0xfd);
1957 return((oldval & 0x02) != 0);
1960 void
1961 debugger_on()
1963 outb(0xfedc, 0x01);
1966 void
1967 debugger_off()
1969 outb(0xfedc, 0x00);
1972 #if BX_USE_ATADRV
1974 // ---------------------------------------------------------------------------
1975 // Start of ATA/ATAPI Driver
1976 // ---------------------------------------------------------------------------
1978 // Global defines -- ATA register and register bits.
1979 // command block & control block regs
1980 #define ATA_CB_DATA 0 // data reg in/out pio_base_addr1+0
1981 #define ATA_CB_ERR 1 // error in pio_base_addr1+1
1982 #define ATA_CB_FR 1 // feature reg out pio_base_addr1+1
1983 #define ATA_CB_SC 2 // sector count in/out pio_base_addr1+2
1984 #define ATA_CB_SN 3 // sector number in/out pio_base_addr1+3
1985 #define ATA_CB_CL 4 // cylinder low in/out pio_base_addr1+4
1986 #define ATA_CB_CH 5 // cylinder high in/out pio_base_addr1+5
1987 #define ATA_CB_DH 6 // device head in/out pio_base_addr1+6
1988 #define ATA_CB_STAT 7 // primary status in pio_base_addr1+7
1989 #define ATA_CB_CMD 7 // command out pio_base_addr1+7
1990 #define ATA_CB_ASTAT 6 // alternate status in pio_base_addr2+6
1991 #define ATA_CB_DC 6 // device control out pio_base_addr2+6
1992 #define ATA_CB_DA 7 // device address in pio_base_addr2+7
1994 #define ATA_CB_ER_ICRC 0x80 // ATA Ultra DMA bad CRC
1995 #define ATA_CB_ER_BBK 0x80 // ATA bad block
1996 #define ATA_CB_ER_UNC 0x40 // ATA uncorrected error
1997 #define ATA_CB_ER_MC 0x20 // ATA media change
1998 #define ATA_CB_ER_IDNF 0x10 // ATA id not found
1999 #define ATA_CB_ER_MCR 0x08 // ATA media change request
2000 #define ATA_CB_ER_ABRT 0x04 // ATA command aborted
2001 #define ATA_CB_ER_NTK0 0x02 // ATA track 0 not found
2002 #define ATA_CB_ER_NDAM 0x01 // ATA address mark not found
2004 #define ATA_CB_ER_P_SNSKEY 0xf0 // ATAPI sense key (mask)
2005 #define ATA_CB_ER_P_MCR 0x08 // ATAPI Media Change Request
2006 #define ATA_CB_ER_P_ABRT 0x04 // ATAPI command abort
2007 #define ATA_CB_ER_P_EOM 0x02 // ATAPI End of Media
2008 #define ATA_CB_ER_P_ILI 0x01 // ATAPI Illegal Length Indication
2010 // ATAPI Interrupt Reason bits in the Sector Count reg (CB_SC)
2011 #define ATA_CB_SC_P_TAG 0xf8 // ATAPI tag (mask)
2012 #define ATA_CB_SC_P_REL 0x04 // ATAPI release
2013 #define ATA_CB_SC_P_IO 0x02 // ATAPI I/O
2014 #define ATA_CB_SC_P_CD 0x01 // ATAPI C/D
2016 // bits 7-4 of the device/head (CB_DH) reg
2017 #define ATA_CB_DH_DEV0 0xa0 // select device 0
2018 #define ATA_CB_DH_DEV1 0xb0 // select device 1
2020 // status reg (CB_STAT and CB_ASTAT) bits
2021 #define ATA_CB_STAT_BSY 0x80 // busy
2022 #define ATA_CB_STAT_RDY 0x40 // ready
2023 #define ATA_CB_STAT_DF 0x20 // device fault
2024 #define ATA_CB_STAT_WFT 0x20 // write fault (old name)
2025 #define ATA_CB_STAT_SKC 0x10 // seek complete
2026 #define ATA_CB_STAT_SERV 0x10 // service
2027 #define ATA_CB_STAT_DRQ 0x08 // data request
2028 #define ATA_CB_STAT_CORR 0x04 // corrected
2029 #define ATA_CB_STAT_IDX 0x02 // index
2030 #define ATA_CB_STAT_ERR 0x01 // error (ATA)
2031 #define ATA_CB_STAT_CHK 0x01 // check (ATAPI)
2033 // device control reg (CB_DC) bits
2034 #define ATA_CB_DC_HD15 0x08 // bit should always be set to one
2035 #define ATA_CB_DC_SRST 0x04 // soft reset
2036 #define ATA_CB_DC_NIEN 0x02 // disable interrupts
2038 // Most mandtory and optional ATA commands (from ATA-3),
2039 #define ATA_CMD_CFA_ERASE_SECTORS 0xC0
2040 #define ATA_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03
2041 #define ATA_CMD_CFA_TRANSLATE_SECTOR 0x87
2042 #define ATA_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD
2043 #define ATA_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38
2044 #define ATA_CMD_CHECK_POWER_MODE1 0xE5
2045 #define ATA_CMD_CHECK_POWER_MODE2 0x98
2046 #define ATA_CMD_DEVICE_RESET 0x08
2047 #define ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90
2048 #define ATA_CMD_FLUSH_CACHE 0xE7
2049 #define ATA_CMD_FORMAT_TRACK 0x50
2050 #define ATA_CMD_IDENTIFY_DEVICE 0xEC
2051 #define ATA_CMD_IDENTIFY_DEVICE_PACKET 0xA1
2052 #define ATA_CMD_IDENTIFY_PACKET_DEVICE 0xA1
2053 #define ATA_CMD_IDLE1 0xE3
2054 #define ATA_CMD_IDLE2 0x97
2055 #define ATA_CMD_IDLE_IMMEDIATE1 0xE1
2056 #define ATA_CMD_IDLE_IMMEDIATE2 0x95
2057 #define ATA_CMD_INITIALIZE_DRIVE_PARAMETERS 0x91
2058 #define ATA_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91
2059 #define ATA_CMD_NOP 0x00
2060 #define ATA_CMD_PACKET 0xA0
2061 #define ATA_CMD_READ_BUFFER 0xE4
2062 #define ATA_CMD_READ_DMA 0xC8
2063 #define ATA_CMD_READ_DMA_QUEUED 0xC7
2064 #define ATA_CMD_READ_MULTIPLE 0xC4
2065 #define ATA_CMD_READ_SECTORS 0x20
2066 #define ATA_CMD_READ_VERIFY_SECTORS 0x40
2067 #define ATA_CMD_RECALIBRATE 0x10
2068 #define ATA_CMD_SEEK 0x70
2069 #define ATA_CMD_SET_FEATURES 0xEF
2070 #define ATA_CMD_SET_MULTIPLE_MODE 0xC6
2071 #define ATA_CMD_SLEEP1 0xE6
2072 #define ATA_CMD_SLEEP2 0x99
2073 #define ATA_CMD_STANDBY1 0xE2
2074 #define ATA_CMD_STANDBY2 0x96
2075 #define ATA_CMD_STANDBY_IMMEDIATE1 0xE0
2076 #define ATA_CMD_STANDBY_IMMEDIATE2 0x94
2077 #define ATA_CMD_WRITE_BUFFER 0xE8
2078 #define ATA_CMD_WRITE_DMA 0xCA
2079 #define ATA_CMD_WRITE_DMA_QUEUED 0xCC
2080 #define ATA_CMD_WRITE_MULTIPLE 0xC5
2081 #define ATA_CMD_WRITE_SECTORS 0x30
2082 #define ATA_CMD_WRITE_VERIFY 0x3C
2084 #define ATA_IFACE_NONE 0x00
2085 #define ATA_IFACE_ISA 0x00
2086 #define ATA_IFACE_PCI 0x01
2088 #define ATA_TYPE_NONE 0x00
2089 #define ATA_TYPE_UNKNOWN 0x01
2090 #define ATA_TYPE_ATA 0x02
2091 #define ATA_TYPE_ATAPI 0x03
2093 #define ATA_DEVICE_NONE 0x00
2094 #define ATA_DEVICE_HD 0xFF
2095 #define ATA_DEVICE_CDROM 0x05
2097 #define ATA_MODE_NONE 0x00
2098 #define ATA_MODE_PIO16 0x00
2099 #define ATA_MODE_PIO32 0x01
2100 #define ATA_MODE_ISADMA 0x02
2101 #define ATA_MODE_PCIDMA 0x03
2102 #define ATA_MODE_USEIRQ 0x10
2104 #define ATA_TRANSLATION_NONE 0
2105 #define ATA_TRANSLATION_LBA 1
2106 #define ATA_TRANSLATION_LARGE 2
2107 #define ATA_TRANSLATION_RECHS 3
2109 #define ATA_DATA_NO 0x00
2110 #define ATA_DATA_IN 0x01
2111 #define ATA_DATA_OUT 0x02
2113 // ---------------------------------------------------------------------------
2114 // ATA/ATAPI driver : initialization
2115 // ---------------------------------------------------------------------------
2116 void ata_init( )
2118 Bit16u ebda_seg=read_word(0x0040,0x000E);
2119 Bit8u channel, device;
2121 // Channels info init.
2122 for (channel=0; channel<BX_MAX_ATA_INTERFACES; channel++) {
2123 write_byte(ebda_seg,&EbdaData->ata.channels[channel].iface,ATA_IFACE_NONE);
2124 write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1,0x0);
2125 write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2,0x0);
2126 write_byte(ebda_seg,&EbdaData->ata.channels[channel].irq,0);
2129 // Devices info init.
2130 for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2131 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
2132 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_NONE);
2133 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable,0);
2134 write_byte(ebda_seg,&EbdaData->ata.devices[device].lock,0);
2135 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode,ATA_MODE_NONE);
2136 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize,0);
2137 write_byte(ebda_seg,&EbdaData->ata.devices[device].translation,ATA_TRANSLATION_NONE);
2138 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads,0);
2139 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders,0);
2140 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt,0);
2141 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads,0);
2142 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders,0);
2143 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt,0);
2145 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors,0L);
2148 // hdidmap and cdidmap init.
2149 for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2150 write_byte(ebda_seg,&EbdaData->ata.hdidmap[device],BX_MAX_ATA_DEVICES);
2151 write_byte(ebda_seg,&EbdaData->ata.cdidmap[device],BX_MAX_ATA_DEVICES);
2154 write_byte(ebda_seg,&EbdaData->ata.hdcount,0);
2155 write_byte(ebda_seg,&EbdaData->ata.cdcount,0);
2158 // ---------------------------------------------------------------------------
2159 // ATA/ATAPI driver : device detection
2160 // ---------------------------------------------------------------------------
2162 void ata_detect( )
2164 Bit16u ebda_seg=read_word(0x0040,0x000E);
2165 Bit8u hdcount, cdcount, device, type;
2166 Bit8u buffer[0x0200];
2168 #if BX_MAX_ATA_INTERFACES > 0
2169 write_byte(ebda_seg,&EbdaData->ata.channels[0].iface,ATA_IFACE_ISA);
2170 write_word(ebda_seg,&EbdaData->ata.channels[0].iobase1,0x1f0);
2171 write_word(ebda_seg,&EbdaData->ata.channels[0].iobase2,0x3f0);
2172 write_byte(ebda_seg,&EbdaData->ata.channels[0].irq,14);
2173 #endif
2174 #if BX_MAX_ATA_INTERFACES > 1
2175 write_byte(ebda_seg,&EbdaData->ata.channels[1].iface,ATA_IFACE_ISA);
2176 write_word(ebda_seg,&EbdaData->ata.channels[1].iobase1,0x170);
2177 write_word(ebda_seg,&EbdaData->ata.channels[1].iobase2,0x370);
2178 write_byte(ebda_seg,&EbdaData->ata.channels[1].irq,15);
2179 #endif
2180 #if BX_MAX_ATA_INTERFACES > 2
2181 write_byte(ebda_seg,&EbdaData->ata.channels[2].iface,ATA_IFACE_ISA);
2182 write_word(ebda_seg,&EbdaData->ata.channels[2].iobase1,0x1e8);
2183 write_word(ebda_seg,&EbdaData->ata.channels[2].iobase2,0x3e0);
2184 write_byte(ebda_seg,&EbdaData->ata.channels[2].irq,12);
2185 #endif
2186 #if BX_MAX_ATA_INTERFACES > 3
2187 write_byte(ebda_seg,&EbdaData->ata.channels[3].iface,ATA_IFACE_ISA);
2188 write_word(ebda_seg,&EbdaData->ata.channels[3].iobase1,0x168);
2189 write_word(ebda_seg,&EbdaData->ata.channels[3].iobase2,0x360);
2190 write_byte(ebda_seg,&EbdaData->ata.channels[3].irq,11);
2191 #endif
2192 #if BX_MAX_ATA_INTERFACES > 4
2193 #error Please fill the ATA interface informations
2194 #endif
2196 // Device detection
2197 hdcount=cdcount=0;
2199 for(device=0; device<BX_MAX_ATA_DEVICES; device++) {
2200 Bit16u iobase1, iobase2;
2201 Bit8u channel, slave, shift;
2202 Bit8u sc, sn, cl, ch, st;
2204 channel = device / 2;
2205 slave = device % 2;
2207 iobase1 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1);
2208 iobase2 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2);
2210 // Disable interrupts
2211 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2213 // Look for device
2214 outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2215 outb(iobase1+ATA_CB_SC, 0x55);
2216 outb(iobase1+ATA_CB_SN, 0xaa);
2217 outb(iobase1+ATA_CB_SC, 0xaa);
2218 outb(iobase1+ATA_CB_SN, 0x55);
2219 outb(iobase1+ATA_CB_SC, 0x55);
2220 outb(iobase1+ATA_CB_SN, 0xaa);
2222 // If we found something
2223 sc = inb(iobase1+ATA_CB_SC);
2224 sn = inb(iobase1+ATA_CB_SN);
2226 if ( (sc == 0x55) && (sn == 0xaa) ) {
2227 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_UNKNOWN);
2229 // reset the channel
2230 ata_reset (device);
2232 // check for ATA or ATAPI
2233 outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2234 sc = inb(iobase1+ATA_CB_SC);
2235 sn = inb(iobase1+ATA_CB_SN);
2236 if ( (sc==0x01) && (sn==0x01) ) {
2237 cl = inb(iobase1+ATA_CB_CL);
2238 ch = inb(iobase1+ATA_CB_CH);
2239 st = inb(iobase1+ATA_CB_STAT);
2241 if ( (cl==0x14) && (ch==0xeb) ) {
2242 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATAPI);
2244 else if ( (cl==0x00) && (ch==0x00) && (st!=0x00) ) {
2245 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATA);
2250 type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type);
2252 // Now we send a IDENTIFY command to ATA device
2253 if(type == ATA_TYPE_ATA) {
2254 Bit32u sectors;
2255 Bit16u cylinders, heads, spt, blksize;
2256 Bit8u translation, removable, mode;
2258 //Temporary values to do the transfer
2259 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2260 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2262 if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE, 1, 0, 0, 0, 0L, get_SS(),buffer) !=0 )
2263 BX_PANIC("ata-detect: Failed to detect ATA device\n");
2265 removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2266 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2267 blksize = read_word(get_SS(),buffer+10);
2269 cylinders = read_word(get_SS(),buffer+(1*2)); // word 1
2270 heads = read_word(get_SS(),buffer+(3*2)); // word 3
2271 spt = read_word(get_SS(),buffer+(6*2)); // word 6
2273 sectors = read_dword(get_SS(),buffer+(60*2)); // word 60 and word 61
2275 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2276 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2277 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2278 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2279 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads, heads);
2280 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders, cylinders);
2281 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt, spt);
2282 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors, sectors);
2283 BX_INFO("ata%d-%d: PCHS=%u/%d/%d translation=", channel, slave,cylinders, heads, spt);
2285 translation = inb_cmos(0x39 + channel/2);
2286 for (shift=device%4; shift>0; shift--) translation >>= 2;
2287 translation &= 0x03;
2289 write_byte(ebda_seg,&EbdaData->ata.devices[device].translation, translation);
2291 switch (translation) {
2292 case ATA_TRANSLATION_NONE:
2293 BX_INFO("none");
2294 break;
2295 case ATA_TRANSLATION_LBA:
2296 BX_INFO("lba");
2297 break;
2298 case ATA_TRANSLATION_LARGE:
2299 BX_INFO("large");
2300 break;
2301 case ATA_TRANSLATION_RECHS:
2302 BX_INFO("r-echs");
2303 break;
2305 switch (translation) {
2306 case ATA_TRANSLATION_NONE:
2307 break;
2308 case ATA_TRANSLATION_LBA:
2309 spt = 63;
2310 sectors /= 63;
2311 heads = sectors / 1024;
2312 if (heads>128) heads = 255;
2313 else if (heads>64) heads = 128;
2314 else if (heads>32) heads = 64;
2315 else if (heads>16) heads = 32;
2316 else heads=16;
2317 cylinders = sectors / heads;
2318 break;
2319 case ATA_TRANSLATION_RECHS:
2320 // Take care not to overflow
2321 if (heads==16) {
2322 if(cylinders>61439) cylinders=61439;
2323 heads=15;
2324 cylinders = (Bit16u)((Bit32u)(cylinders)*16/15);
2326 // then go through the large bitshift process
2327 case ATA_TRANSLATION_LARGE:
2328 while(cylinders > 1024) {
2329 cylinders >>= 1;
2330 heads <<= 1;
2332 // If we max out the head count
2333 if (heads > 127) break;
2335 break;
2337 // clip to 1024 cylinders in lchs
2338 if (cylinders > 1024) cylinders=1024;
2339 BX_INFO(" LCHS=%d/%d/%d\n", cylinders, heads, spt);
2341 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads, heads);
2342 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders, cylinders);
2343 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt, spt);
2345 // fill hdidmap
2346 write_byte(ebda_seg,&EbdaData->ata.hdidmap[hdcount], device);
2347 hdcount++;
2350 // Now we send a IDENTIFY command to ATAPI device
2351 if(type == ATA_TYPE_ATAPI) {
2353 Bit8u type, removable, mode;
2354 Bit16u blksize;
2356 //Temporary values to do the transfer
2357 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_CDROM);
2358 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2360 if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE_PACKET, 1, 0, 0, 0, 0L, get_SS(),buffer) != 0)
2361 BX_PANIC("ata-detect: Failed to detect ATAPI device\n");
2363 type = read_byte(get_SS(),buffer+1) & 0x1f;
2364 removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2365 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2366 blksize = 2048;
2368 write_byte(ebda_seg,&EbdaData->ata.devices[device].device, type);
2369 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2370 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2371 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2373 // fill cdidmap
2374 write_byte(ebda_seg,&EbdaData->ata.cdidmap[cdcount], device);
2375 cdcount++;
2379 Bit32u sizeinmb;
2380 Bit16u ataversion;
2381 Bit8u c, i, version, model[41];
2383 switch (type) {
2384 case ATA_TYPE_ATA:
2385 sizeinmb = read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors);
2386 sizeinmb >>= 11;
2387 case ATA_TYPE_ATAPI:
2388 // Read ATA/ATAPI version
2389 ataversion=((Bit16u)(read_byte(get_SS(),buffer+161))<<8)|read_byte(get_SS(),buffer+160);
2390 for(version=15;version>0;version--) {
2391 if((ataversion&(1<<version))!=0)
2392 break;
2395 // Read model name
2396 for(i=0;i<20;i++){
2397 write_byte(get_SS(),model+(i*2),read_byte(get_SS(),buffer+(i*2)+54+1));
2398 write_byte(get_SS(),model+(i*2)+1,read_byte(get_SS(),buffer+(i*2)+54));
2401 // Reformat
2402 write_byte(get_SS(),model+40,0x00);
2403 for(i=39;i>0;i--){
2404 if(read_byte(get_SS(),model+i)==0x20)
2405 write_byte(get_SS(),model+i,0x00);
2406 else break;
2408 break;
2411 switch (type) {
2412 case ATA_TYPE_ATA:
2413 printf("ata%d %s: ",channel,slave?" slave":"master");
2414 i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
2415 printf(" ATA-%d Hard-Disk (%d MBytes)\n",version,(Bit16u)sizeinmb);
2416 break;
2417 case ATA_TYPE_ATAPI:
2418 printf("ata%d %s: ",channel,slave?" slave":"master");
2419 i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
2420 if(read_byte(ebda_seg,&EbdaData->ata.devices[device].device)==ATA_DEVICE_CDROM)
2421 printf(" ATAPI-%d CD-Rom/DVD-Rom\n",version);
2422 else
2423 printf(" ATAPI-%d Device\n",version);
2424 break;
2425 case ATA_TYPE_UNKNOWN:
2426 printf("ata%d %s: Unknown device\n",channel,slave?" slave":"master");
2427 break;
2432 // Store the devices counts
2433 write_byte(ebda_seg,&EbdaData->ata.hdcount, hdcount);
2434 write_byte(ebda_seg,&EbdaData->ata.cdcount, cdcount);
2435 write_byte(0x40,0x75, hdcount);
2437 printf("\n");
2439 // FIXME : should use bios=cmos|auto|disable bits
2440 // FIXME : should know about translation bits
2441 // FIXME : move hard_drive_post here
2445 // ---------------------------------------------------------------------------
2446 // ATA/ATAPI driver : software reset
2447 // ---------------------------------------------------------------------------
2448 // ATA-3
2449 // 8.2.1 Software reset - Device 0
2451 void ata_reset(device)
2452 Bit16u device;
2454 Bit16u ebda_seg=read_word(0x0040,0x000E);
2455 Bit16u iobase1, iobase2;
2456 Bit8u channel, slave, sn, sc;
2457 Bit16u max;
2459 channel = device / 2;
2460 slave = device % 2;
2462 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2463 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2465 // Reset
2467 // 8.2.1 (a) -- set SRST in DC
2468 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST);
2470 // 8.2.1 (b) -- wait for BSY
2471 max=0xff;
2472 while(--max>0) {
2473 Bit8u status = inb(iobase1+ATA_CB_STAT);
2474 if ((status & ATA_CB_STAT_BSY) != 0) break;
2477 // 8.2.1 (f) -- clear SRST
2478 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2480 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_NONE) {
2482 // 8.2.1 (g) -- check for sc==sn==0x01
2483 // select device
2484 outb(iobase1+ATA_CB_DH, slave?ATA_CB_DH_DEV1:ATA_CB_DH_DEV0);
2485 sc = inb(iobase1+ATA_CB_SC);
2486 sn = inb(iobase1+ATA_CB_SN);
2488 if ( (sc==0x01) && (sn==0x01) ) {
2490 // 8.2.1 (h) -- wait for not BSY
2491 max=0xff;
2492 while(--max>0) {
2493 Bit8u status = inb(iobase1+ATA_CB_STAT);
2494 if ((status & ATA_CB_STAT_BSY) == 0) break;
2499 // 8.2.1 (i) -- wait for DRDY
2500 max=0xfff;
2501 while(--max>0) {
2502 Bit8u status = inb(iobase1+ATA_CB_STAT);
2503 if ((status & ATA_CB_STAT_RDY) != 0) break;
2506 // Enable interrupts
2507 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2510 // ---------------------------------------------------------------------------
2511 // ATA/ATAPI driver : execute a non data command
2512 // ---------------------------------------------------------------------------
2514 Bit16u ata_cmd_non_data()
2515 {return 0;}
2517 // ---------------------------------------------------------------------------
2518 // ATA/ATAPI driver : execute a data-in command
2519 // ---------------------------------------------------------------------------
2520 // returns
2521 // 0 : no error
2522 // 1 : BUSY bit set
2523 // 2 : read error
2524 // 3 : expected DRQ=1
2525 // 4 : no sectors left to read/verify
2526 // 5 : more sectors to read/verify
2527 // 6 : no sectors left to write
2528 // 7 : more sectors to write
2529 Bit16u ata_cmd_data_in(device, command, count, cylinder, head, sector, lba, segment, offset)
2530 Bit16u device, command, count, cylinder, head, sector, segment, offset;
2531 Bit32u lba;
2533 Bit16u ebda_seg=read_word(0x0040,0x000E);
2534 Bit16u iobase1, iobase2, blksize;
2535 Bit8u channel, slave;
2536 Bit8u status, current, mode;
2538 channel = device / 2;
2539 slave = device % 2;
2541 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2542 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2543 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2544 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
2545 if (mode == ATA_MODE_PIO32) blksize>>=2;
2546 else blksize>>=1;
2548 // sector will be 0 only on lba access. Convert to lba-chs
2549 if (sector == 0) {
2550 sector = (Bit16u) (lba & 0x000000ffL);
2551 lba >>= 8;
2552 cylinder = (Bit16u) (lba & 0x0000ffffL);
2553 lba >>= 16;
2554 head = ((Bit16u) (lba & 0x0000000fL)) | 0x40;
2557 // Reset count of transferred data
2558 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2559 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2560 current = 0;
2562 status = inb(iobase1 + ATA_CB_STAT);
2563 if (status & ATA_CB_STAT_BSY) return 1;
2565 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2566 outb(iobase1 + ATA_CB_FR, 0x00);
2567 outb(iobase1 + ATA_CB_SC, count);
2568 outb(iobase1 + ATA_CB_SN, sector);
2569 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
2570 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
2571 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
2572 outb(iobase1 + ATA_CB_CMD, command);
2574 while (1) {
2575 status = inb(iobase1 + ATA_CB_STAT);
2576 if ( !(status & ATA_CB_STAT_BSY) ) break;
2579 if (status & ATA_CB_STAT_ERR) {
2580 BX_DEBUG_ATA("ata_cmd_data_in : read error\n");
2581 return 2;
2582 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2583 BX_DEBUG_ATA("ata_cmd_data_in : DRQ not set (status %02x)\n", (unsigned) status);
2584 return 3;
2587 // FIXME : move seg/off translation here
2589 ASM_START
2590 sti ;; enable higher priority interrupts
2591 ASM_END
2593 while (1) {
2595 ASM_START
2596 push bp
2597 mov bp, sp
2598 mov di, _ata_cmd_data_in.offset + 2[bp]
2599 mov ax, _ata_cmd_data_in.segment + 2[bp]
2600 mov cx, _ata_cmd_data_in.blksize + 2[bp]
2602 ;; adjust if there will be an overrun. 2K max sector size
2603 cmp di, #0xf800 ;;
2604 jbe ata_in_no_adjust
2606 ata_in_adjust:
2607 sub di, #0x0800 ;; sub 2 kbytes from offset
2608 add ax, #0x0080 ;; add 2 Kbytes to segment
2610 ata_in_no_adjust:
2611 mov es, ax ;; segment in es
2613 mov dx, _ata_cmd_data_in.iobase1 + 2[bp] ;; ATA data read port
2615 mov ah, _ata_cmd_data_in.mode + 2[bp]
2616 cmp ah, #ATA_MODE_PIO32
2617 je ata_in_32
2619 ata_in_16:
2621 insw ;; CX words transfered from port(DX) to ES:[DI]
2622 jmp ata_in_done
2624 ata_in_32:
2626 insd ;; CX dwords transfered from port(DX) to ES:[DI]
2628 ata_in_done:
2629 mov _ata_cmd_data_in.offset + 2[bp], di
2630 mov _ata_cmd_data_in.segment + 2[bp], es
2631 pop bp
2632 ASM_END
2634 current++;
2635 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
2636 count--;
2637 status = inb(iobase1 + ATA_CB_STAT);
2638 if (count == 0) {
2639 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2640 != ATA_CB_STAT_RDY ) {
2641 BX_DEBUG_ATA("ata_cmd_data_in : no sectors left (status %02x)\n", (unsigned) status);
2642 return 4;
2644 break;
2646 else {
2647 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2648 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2649 BX_DEBUG_ATA("ata_cmd_data_in : more sectors left (status %02x)\n", (unsigned) status);
2650 return 5;
2652 continue;
2655 // Enable interrupts
2656 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2657 return 0;
2660 // ---------------------------------------------------------------------------
2661 // ATA/ATAPI driver : execute a data-out command
2662 // ---------------------------------------------------------------------------
2663 // returns
2664 // 0 : no error
2665 // 1 : BUSY bit set
2666 // 2 : read error
2667 // 3 : expected DRQ=1
2668 // 4 : no sectors left to read/verify
2669 // 5 : more sectors to read/verify
2670 // 6 : no sectors left to write
2671 // 7 : more sectors to write
2672 Bit16u ata_cmd_data_out(device, command, count, cylinder, head, sector, lba, segment, offset)
2673 Bit16u device, command, count, cylinder, head, sector, segment, offset;
2674 Bit32u lba;
2676 Bit16u ebda_seg=read_word(0x0040,0x000E);
2677 Bit16u iobase1, iobase2, blksize;
2678 Bit8u channel, slave;
2679 Bit8u status, current, mode;
2681 channel = device / 2;
2682 slave = device % 2;
2684 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2685 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2686 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2687 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
2688 if (mode == ATA_MODE_PIO32) blksize>>=2;
2689 else blksize>>=1;
2691 // sector will be 0 only on lba access. Convert to lba-chs
2692 if (sector == 0) {
2693 sector = (Bit16u) (lba & 0x000000ffL);
2694 lba >>= 8;
2695 cylinder = (Bit16u) (lba & 0x0000ffffL);
2696 lba >>= 16;
2697 head = ((Bit16u) (lba & 0x0000000fL)) | 0x40;
2700 // Reset count of transferred data
2701 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2702 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2703 current = 0;
2705 status = inb(iobase1 + ATA_CB_STAT);
2706 if (status & ATA_CB_STAT_BSY) return 1;
2708 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2709 outb(iobase1 + ATA_CB_FR, 0x00);
2710 outb(iobase1 + ATA_CB_SC, count);
2711 outb(iobase1 + ATA_CB_SN, sector);
2712 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
2713 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
2714 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
2715 outb(iobase1 + ATA_CB_CMD, command);
2717 while (1) {
2718 status = inb(iobase1 + ATA_CB_STAT);
2719 if ( !(status & ATA_CB_STAT_BSY) ) break;
2722 if (status & ATA_CB_STAT_ERR) {
2723 BX_DEBUG_ATA("ata_cmd_data_out : read error\n");
2724 return 2;
2725 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2726 BX_DEBUG_ATA("ata_cmd_data_out : DRQ not set (status %02x)\n", (unsigned) status);
2727 return 3;
2730 // FIXME : move seg/off translation here
2732 ASM_START
2733 sti ;; enable higher priority interrupts
2734 ASM_END
2736 while (1) {
2738 ASM_START
2739 push bp
2740 mov bp, sp
2741 mov si, _ata_cmd_data_out.offset + 2[bp]
2742 mov ax, _ata_cmd_data_out.segment + 2[bp]
2743 mov cx, _ata_cmd_data_out.blksize + 2[bp]
2745 ;; adjust if there will be an overrun. 2K max sector size
2746 cmp si, #0xf800 ;;
2747 jbe ata_out_no_adjust
2749 ata_out_adjust:
2750 sub si, #0x0800 ;; sub 2 kbytes from offset
2751 add ax, #0x0080 ;; add 2 Kbytes to segment
2753 ata_out_no_adjust:
2754 mov es, ax ;; segment in es
2756 mov dx, _ata_cmd_data_out.iobase1 + 2[bp] ;; ATA data write port
2758 mov ah, _ata_cmd_data_out.mode + 2[bp]
2759 cmp ah, #ATA_MODE_PIO32
2760 je ata_out_32
2762 ata_out_16:
2763 seg ES
2765 outsw ;; CX words transfered from port(DX) to ES:[SI]
2766 jmp ata_out_done
2768 ata_out_32:
2769 seg ES
2771 outsd ;; CX dwords transfered from port(DX) to ES:[SI]
2773 ata_out_done:
2774 mov _ata_cmd_data_out.offset + 2[bp], si
2775 mov _ata_cmd_data_out.segment + 2[bp], es
2776 pop bp
2777 ASM_END
2779 current++;
2780 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
2781 count--;
2782 status = inb(iobase1 + ATA_CB_STAT);
2783 if (count == 0) {
2784 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2785 != ATA_CB_STAT_RDY ) {
2786 BX_DEBUG_ATA("ata_cmd_data_out : no sectors left (status %02x)\n", (unsigned) status);
2787 return 6;
2789 break;
2791 else {
2792 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2793 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2794 BX_DEBUG_ATA("ata_cmd_data_out : more sectors left (status %02x)\n", (unsigned) status);
2795 return 7;
2797 continue;
2800 // Enable interrupts
2801 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2802 return 0;
2805 // ---------------------------------------------------------------------------
2806 // ATA/ATAPI driver : execute a packet command
2807 // ---------------------------------------------------------------------------
2808 // returns
2809 // 0 : no error
2810 // 1 : error in parameters
2811 // 2 : BUSY bit set
2812 // 3 : error
2813 // 4 : not ready
2814 Bit16u ata_cmd_packet(device, cmdlen, cmdseg, cmdoff, header, length, inout, bufseg, bufoff)
2815 Bit8u cmdlen,inout;
2816 Bit16u device,cmdseg, cmdoff, bufseg, bufoff;
2817 Bit16u header;
2818 Bit32u length;
2820 Bit16u ebda_seg=read_word(0x0040,0x000E);
2821 Bit16u iobase1, iobase2;
2822 Bit16u lcount, lbefore, lafter, count;
2823 Bit8u channel, slave;
2824 Bit8u status, mode, lmode;
2825 Bit32u total, transfer;
2827 channel = device / 2;
2828 slave = device % 2;
2830 // Data out is not supported yet
2831 if (inout == ATA_DATA_OUT) {
2832 BX_INFO("ata_cmd_packet: DATA_OUT not supported yet\n");
2833 return 1;
2836 // The header length must be even
2837 if (header & 1) {
2838 BX_DEBUG_ATA("ata_cmd_packet : header must be even (%04x)\n",header);
2839 return 1;
2842 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2843 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2844 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2845 transfer= 0L;
2847 if (cmdlen < 12) cmdlen=12;
2848 if (cmdlen > 12) cmdlen=16;
2849 cmdlen>>=1;
2851 // Reset count of transferred data
2852 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2853 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2855 status = inb(iobase1 + ATA_CB_STAT);
2856 if (status & ATA_CB_STAT_BSY) return 2;
2858 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2859 // outb(iobase1 + ATA_CB_FR, 0x00);
2860 // outb(iobase1 + ATA_CB_SC, 0x00);
2861 // outb(iobase1 + ATA_CB_SN, 0x00);
2862 outb(iobase1 + ATA_CB_CL, 0xfff0 & 0x00ff);
2863 outb(iobase1 + ATA_CB_CH, 0xfff0 >> 8);
2864 outb(iobase1 + ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2865 outb(iobase1 + ATA_CB_CMD, ATA_CMD_PACKET);
2867 // Device should ok to receive command
2868 while (1) {
2869 status = inb(iobase1 + ATA_CB_STAT);
2870 if ( !(status & ATA_CB_STAT_BSY) ) break;
2873 if (status & ATA_CB_STAT_ERR) {
2874 BX_DEBUG_ATA("ata_cmd_packet : error, status is %02x\n",status);
2875 return 3;
2876 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2877 BX_DEBUG_ATA("ata_cmd_packet : DRQ not set (status %02x)\n", (unsigned) status);
2878 return 4;
2881 // Normalize address
2882 cmdseg += (cmdoff / 16);
2883 cmdoff %= 16;
2885 // Send command to device
2886 ASM_START
2887 sti ;; enable higher priority interrupts
2889 push bp
2890 mov bp, sp
2892 mov si, _ata_cmd_packet.cmdoff + 2[bp]
2893 mov ax, _ata_cmd_packet.cmdseg + 2[bp]
2894 mov cx, _ata_cmd_packet.cmdlen + 2[bp]
2895 mov es, ax ;; segment in es
2897 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data write port
2899 seg ES
2901 outsw ;; CX words transfered from port(DX) to ES:[SI]
2903 pop bp
2904 ASM_END
2906 if (inout == ATA_DATA_NO) {
2907 status = inb(iobase1 + ATA_CB_STAT);
2909 else {
2910 while (1) {
2912 status = inb(iobase1 + ATA_CB_STAT);
2914 // Check if command completed
2915 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ) ) ==0 ) break;
2917 if (status & ATA_CB_STAT_ERR) {
2918 BX_DEBUG_ATA("ata_cmd_packet : error (status %02x)\n",status);
2919 return 3;
2922 // Device must be ready to send data
2923 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2924 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2925 BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", status);
2926 return 4;
2929 // Normalize address
2930 bufseg += (bufoff / 16);
2931 bufoff %= 16;
2933 // Get the byte count
2934 lcount = ((Bit16u)(inb(iobase1 + ATA_CB_CH))<<8)+inb(iobase1 + ATA_CB_CL);
2936 // adjust to read what we want
2937 if(header>lcount) {
2938 lbefore=lcount;
2939 header-=lcount;
2940 lcount=0;
2942 else {
2943 lbefore=header;
2944 header=0;
2945 lcount-=lbefore;
2948 if(lcount>length) {
2949 lafter=lcount-length;
2950 lcount=length;
2951 length=0;
2953 else {
2954 lafter=0;
2955 length-=lcount;
2958 // Save byte count
2959 count = lcount;
2961 BX_DEBUG_ATA("Trying to read %04x bytes (%04x %04x %04x) ",lbefore+lcount+lafter,lbefore,lcount,lafter);
2962 BX_DEBUG_ATA("to 0x%04x:0x%04x\n",bufseg,bufoff);
2964 // If counts not dividable by 4, use 16bits mode
2965 lmode = mode;
2966 if (lbefore & 0x03) lmode=ATA_MODE_PIO16;
2967 if (lcount & 0x03) lmode=ATA_MODE_PIO16;
2968 if (lafter & 0x03) lmode=ATA_MODE_PIO16;
2970 // adds an extra byte if count are odd. before is always even
2971 if (lcount & 0x01) {
2972 lcount+=1;
2973 if ((lafter > 0) && (lafter & 0x01)) {
2974 lafter-=1;
2978 if (lmode == ATA_MODE_PIO32) {
2979 lcount>>=2; lbefore>>=2; lafter>>=2;
2981 else {
2982 lcount>>=1; lbefore>>=1; lafter>>=1;
2985 ; // FIXME bcc bug
2987 ASM_START
2988 push bp
2989 mov bp, sp
2991 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data read port
2993 mov cx, _ata_cmd_packet.lbefore + 2[bp]
2994 jcxz ata_packet_no_before
2996 mov ah, _ata_cmd_packet.lmode + 2[bp]
2997 cmp ah, #ATA_MODE_PIO32
2998 je ata_packet_in_before_32
3000 ata_packet_in_before_16:
3001 in ax, dx
3002 loop ata_packet_in_before_16
3003 jmp ata_packet_no_before
3005 ata_packet_in_before_32:
3006 push eax
3007 ata_packet_in_before_32_loop:
3008 in eax, dx
3009 loop ata_packet_in_before_32_loop
3010 pop eax
3012 ata_packet_no_before:
3013 mov cx, _ata_cmd_packet.lcount + 2[bp]
3014 jcxz ata_packet_after
3016 mov di, _ata_cmd_packet.bufoff + 2[bp]
3017 mov ax, _ata_cmd_packet.bufseg + 2[bp]
3018 mov es, ax
3020 mov ah, _ata_cmd_packet.lmode + 2[bp]
3021 cmp ah, #ATA_MODE_PIO32
3022 je ata_packet_in_32
3024 ata_packet_in_16:
3026 insw ;; CX words transfered tp port(DX) to ES:[DI]
3027 jmp ata_packet_after
3029 ata_packet_in_32:
3031 insd ;; CX dwords transfered to port(DX) to ES:[DI]
3033 ata_packet_after:
3034 mov cx, _ata_cmd_packet.lafter + 2[bp]
3035 jcxz ata_packet_done
3037 mov ah, _ata_cmd_packet.lmode + 2[bp]
3038 cmp ah, #ATA_MODE_PIO32
3039 je ata_packet_in_after_32
3041 ata_packet_in_after_16:
3042 in ax, dx
3043 loop ata_packet_in_after_16
3044 jmp ata_packet_done
3046 ata_packet_in_after_32:
3047 push eax
3048 ata_packet_in_after_32_loop:
3049 in eax, dx
3050 loop ata_packet_in_after_32_loop
3051 pop eax
3053 ata_packet_done:
3054 pop bp
3055 ASM_END
3057 // Compute new buffer address
3058 bufoff += count;
3060 // Save transferred bytes count
3061 transfer += count;
3062 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,transfer);
3066 // Final check, device must be ready
3067 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
3068 != ATA_CB_STAT_RDY ) {
3069 BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", (unsigned) status);
3070 return 4;
3073 // Enable interrupts
3074 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
3075 return 0;
3078 // ---------------------------------------------------------------------------
3079 // End of ATA/ATAPI Driver
3080 // ---------------------------------------------------------------------------
3082 // ---------------------------------------------------------------------------
3083 // Start of ATA/ATAPI generic functions
3084 // ---------------------------------------------------------------------------
3086 Bit16u
3087 atapi_get_sense(device)
3088 Bit16u device;
3090 Bit8u atacmd[12];
3091 Bit8u buffer[16];
3092 Bit8u i;
3094 memsetb(get_SS(),atacmd,0,12);
3096 // Request SENSE
3097 atacmd[0]=0x03;
3098 atacmd[4]=0x20;
3099 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 16L, ATA_DATA_IN, get_SS(), buffer) != 0)
3100 return 0x0002;
3102 if ((buffer[0] & 0x7e) == 0x70) {
3103 return (((Bit16u)buffer[2]&0x0f)*0x100)+buffer[12];
3106 return 0;
3109 Bit16u
3110 atapi_is_ready(device)
3111 Bit16u device;
3113 Bit8u atacmd[12];
3114 Bit8u buffer[];
3116 memsetb(get_SS(),atacmd,0,12);
3118 // Test Unit Ready
3119 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0)
3120 return 0x000f;
3122 if (atapi_get_sense(device) !=0 ) {
3123 memsetb(get_SS(),atacmd,0,12);
3125 // try to send Test Unit Ready again
3126 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0)
3127 return 0x000f;
3129 return atapi_get_sense(device);
3131 return 0;
3134 Bit16u
3135 atapi_is_cdrom(device)
3136 Bit8u device;
3138 Bit16u ebda_seg=read_word(0x0040,0x000E);
3140 if (device >= BX_MAX_ATA_DEVICES)
3141 return 0;
3143 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI)
3144 return 0;
3146 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].device) != ATA_DEVICE_CDROM)
3147 return 0;
3149 return 1;
3152 // ---------------------------------------------------------------------------
3153 // End of ATA/ATAPI generic functions
3154 // ---------------------------------------------------------------------------
3156 #endif // BX_USE_ATADRV
3158 #if BX_ELTORITO_BOOT
3160 // ---------------------------------------------------------------------------
3161 // Start of El-Torito boot functions
3162 // ---------------------------------------------------------------------------
3164 void
3165 cdemu_init()
3167 Bit16u ebda_seg=read_word(0x0040,0x000E);
3169 // the only important data is this one for now
3170 write_byte(ebda_seg,&EbdaData->cdemu.active,0x00);
3173 Bit8u
3174 cdemu_isactive()
3176 Bit16u ebda_seg=read_word(0x0040,0x000E);
3178 return(read_byte(ebda_seg,&EbdaData->cdemu.active));
3181 Bit8u
3182 cdemu_emulated_drive()
3184 Bit16u ebda_seg=read_word(0x0040,0x000E);
3186 return(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
3189 static char isotag[6]="CD001";
3190 static char eltorito[24]="EL TORITO SPECIFICATION";
3192 // Returns ah: emulated drive, al: error code
3194 Bit16u
3195 cdrom_boot()
3197 Bit16u ebda_seg=read_word(0x0040,0x000E);
3198 Bit8u atacmd[12], buffer[2048];
3199 Bit32u lba;
3200 Bit16u boot_segment, nbsectors, i, error;
3201 Bit8u device;
3203 // Find out the first cdrom
3204 for (device=0; device<BX_MAX_ATA_DEVICES;device++) {
3205 if (atapi_is_cdrom(device)) break;
3208 // if not found
3209 if(device >= BX_MAX_ATA_DEVICES) return 2;
3211 // Read the Boot Record Volume Descriptor
3212 memsetb(get_SS(),atacmd,0,12);
3213 atacmd[0]=0x28; // READ command
3214 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3215 atacmd[8]=(0x01 & 0x00ff); // Sectors
3216 atacmd[2]=(0x11 & 0xff000000) >> 24; // LBA
3217 atacmd[3]=(0x11 & 0x00ff0000) >> 16;
3218 atacmd[4]=(0x11 & 0x0000ff00) >> 8;
3219 atacmd[5]=(0x11 & 0x000000ff);
3220 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3221 return 3;
3223 // Validity checks
3224 if(buffer[0]!=0)return 4;
3225 for(i=0;i<5;i++){
3226 if(buffer[1+i]!=read_byte(0xf000,&isotag[i]))return 5;
3228 for(i=0;i<23;i++)
3229 if(buffer[7+i]!=read_byte(0xf000,&eltorito[i]))return 6;
3231 // ok, now we calculate the Boot catalog address
3232 lba=buffer[0x4A]*0x1000000+buffer[0x49]*0x10000+buffer[0x48]*0x100+buffer[0x47];
3234 // And we read the Boot Catalog
3235 memsetb(get_SS(),atacmd,0,12);
3236 atacmd[0]=0x28; // READ command
3237 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3238 atacmd[8]=(0x01 & 0x00ff); // Sectors
3239 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3240 atacmd[3]=(lba & 0x00ff0000) >> 16;
3241 atacmd[4]=(lba & 0x0000ff00) >> 8;
3242 atacmd[5]=(lba & 0x000000ff);
3243 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3244 return 7;
3246 // Validation entry
3247 if(buffer[0x00]!=0x01)return 8; // Header
3248 if(buffer[0x01]!=0x00)return 9; // Platform
3249 if(buffer[0x1E]!=0x55)return 10; // key 1
3250 if(buffer[0x1F]!=0xAA)return 10; // key 2
3252 // Initial/Default Entry
3253 if(buffer[0x20]!=0x88)return 11; // Bootable
3255 write_byte(ebda_seg,&EbdaData->cdemu.media,buffer[0x21]);
3256 if(buffer[0x21]==0){
3257 // FIXME ElTorito Hardcoded. cdrom is hardcoded as device 0xE0.
3258 // Win2000 cd boot needs to know it booted from cd
3259 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0xE0);
3261 else if(buffer[0x21]<4)
3262 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x00);
3263 else
3264 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x80);
3266 write_byte(ebda_seg,&EbdaData->cdemu.controller_index,device/2);
3267 write_byte(ebda_seg,&EbdaData->cdemu.device_spec,device%2);
3269 boot_segment=buffer[0x23]*0x100+buffer[0x22];
3270 if(boot_segment==0x0000)boot_segment=0x07C0;
3272 write_word(ebda_seg,&EbdaData->cdemu.load_segment,boot_segment);
3273 write_word(ebda_seg,&EbdaData->cdemu.buffer_segment,0x0000);
3275 nbsectors=buffer[0x27]*0x100+buffer[0x26];
3276 write_word(ebda_seg,&EbdaData->cdemu.sector_count,nbsectors);
3278 lba=buffer[0x2B]*0x1000000+buffer[0x2A]*0x10000+buffer[0x29]*0x100+buffer[0x28];
3279 write_dword(ebda_seg,&EbdaData->cdemu.ilba,lba);
3281 // And we read the image in memory
3282 memsetb(get_SS(),atacmd,0,12);
3283 atacmd[0]=0x28; // READ command
3284 atacmd[7]=((1+(nbsectors-1)/4) & 0xff00) >> 8; // Sectors
3285 atacmd[8]=((1+(nbsectors-1)/4) & 0x00ff); // Sectors
3286 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3287 atacmd[3]=(lba & 0x00ff0000) >> 16;
3288 atacmd[4]=(lba & 0x0000ff00) >> 8;
3289 atacmd[5]=(lba & 0x000000ff);
3290 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, nbsectors*512L, ATA_DATA_IN, boot_segment,0)) != 0)
3291 return 12;
3293 // Remember the media type
3294 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
3295 case 0x01: // 1.2M floppy
3296 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,15);
3297 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3298 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3299 break;
3300 case 0x02: // 1.44M floppy
3301 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,18);
3302 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3303 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3304 break;
3305 case 0x03: // 2.88M floppy
3306 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,36);
3307 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3308 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3309 break;
3310 case 0x04: // Harddrive
3311 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,read_byte(boot_segment,446+6)&0x3f);
3312 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,
3313 (read_byte(boot_segment,446+6)<<2) + read_byte(boot_segment,446+7) + 1);
3314 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,read_byte(boot_segment,446+5) + 1);
3315 break;
3318 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0) {
3319 // Increase bios installed hardware number of devices
3320 if(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)==0x00)
3321 write_byte(0x40,0x10,read_byte(0x40,0x10)|0x41);
3322 else
3323 write_byte(ebda_seg, &EbdaData->ata.hdcount, read_byte(ebda_seg, &EbdaData->ata.hdcount) + 1);
3327 // everything is ok, so from now on, the emulation is active
3328 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0)
3329 write_byte(ebda_seg,&EbdaData->cdemu.active,0x01);
3331 // return the boot drive + no error
3332 return (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)*0x100)+0;
3335 // ---------------------------------------------------------------------------
3336 // End of El-Torito boot functions
3337 // ---------------------------------------------------------------------------
3338 #endif // BX_ELTORITO_BOOT
3340 void
3341 int14_function(regs, ds, iret_addr)
3342 pusha_regs_t regs; // regs pushed from PUSHA instruction
3343 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
3344 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
3346 Bit16u addr,timer,val16;
3347 Bit8u timeout;
3349 ASM_START
3351 ASM_END
3353 addr = read_word(0x0040, (regs.u.r16.dx << 1));
3354 timeout = read_byte(0x0040, 0x007C + regs.u.r16.dx);
3355 if ((regs.u.r16.dx < 4) && (addr > 0)) {
3356 switch (regs.u.r8.ah) {
3357 case 0:
3358 outb(addr+3, inb(addr+3) | 0x80);
3359 if (regs.u.r8.al & 0xE0 == 0) {
3360 outb(addr, 0x17);
3361 outb(addr+1, 0x04);
3362 } else {
3363 val16 = 0x600 >> ((regs.u.r8.al & 0xE0) >> 5);
3364 outb(addr, val16 & 0xFF);
3365 outb(addr+1, val16 >> 8);
3367 outb(addr+3, regs.u.r8.al & 0x1F);
3368 regs.u.r8.ah = inb(addr+5);
3369 regs.u.r8.al = inb(addr+6);
3370 ClearCF(iret_addr.flags);
3371 break;
3372 case 1:
3373 timer = read_word(0x0040, 0x006C);
3374 while (((inb(addr+5) & 0x60) != 0x60) && (timeout)) {
3375 val16 = read_word(0x0040, 0x006C);
3376 if (val16 != timer) {
3377 timer = val16;
3378 timeout--;
3381 if (timeout) outb(addr, regs.u.r8.al);
3382 regs.u.r8.ah = inb(addr+5);
3383 if (!timeout) regs.u.r8.ah |= 0x80;
3384 ClearCF(iret_addr.flags);
3385 break;
3386 case 2:
3387 timer = read_word(0x0040, 0x006C);
3388 while (((inb(addr+5) & 0x01) == 0) && (timeout)) {
3389 val16 = read_word(0x0040, 0x006C);
3390 if (val16 != timer) {
3391 timer = val16;
3392 timeout--;
3395 if (timeout) {
3396 regs.u.r8.ah = 0;
3397 regs.u.r8.al = inb(addr);
3398 } else {
3399 regs.u.r8.ah = inb(addr+5);
3401 ClearCF(iret_addr.flags);
3402 break;
3403 case 3:
3404 regs.u.r8.ah = inb(addr+5);
3405 regs.u.r8.al = inb(addr+6);
3406 ClearCF(iret_addr.flags);
3407 break;
3408 default:
3409 SetCF(iret_addr.flags); // Unsupported
3411 } else {
3412 SetCF(iret_addr.flags); // Unsupported
3416 void
3417 int15_function(regs, ES, DS, FLAGS)
3418 pusha_regs_t regs; // REGS pushed via pusha
3419 Bit16u ES, DS, FLAGS;
3421 Bit16u ebda_seg=read_word(0x0040,0x000E);
3422 bx_bool prev_a20_enable;
3423 Bit16u base15_00;
3424 Bit8u base23_16;
3425 Bit16u ss;
3426 Bit16u CX,DX;
3428 Bit16u bRegister;
3429 Bit8u irqDisable;
3431 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3433 switch (regs.u.r8.ah) {
3434 case 0x24: /* A20 Control */
3435 switch (regs.u.r8.al) {
3436 case 0x00:
3437 set_enable_a20(0);
3438 CLEAR_CF();
3439 regs.u.r8.ah = 0;
3440 break;
3441 case 0x01:
3442 set_enable_a20(1);
3443 CLEAR_CF();
3444 regs.u.r8.ah = 0;
3445 break;
3446 case 0x02:
3447 regs.u.r8.al = (inb(0x92) >> 1) & 0x01;
3448 CLEAR_CF();
3449 regs.u.r8.ah = 0;
3450 break;
3451 case 0x03:
3452 CLEAR_CF();
3453 regs.u.r8.ah = 0;
3454 regs.u.r16.bx = 3;
3455 break;
3456 default:
3457 BX_INFO("int15: Func 24h, subfunc %02xh, A20 gate control not supported\n", (unsigned) regs.u.r8.al);
3458 SET_CF();
3459 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3461 break;
3463 case 0x41:
3464 SET_CF();
3465 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3466 break;
3468 case 0x4f:
3469 /* keyboard intercept */
3470 #if BX_CPU < 2
3471 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3472 #else
3473 // nop
3474 #endif
3475 SET_CF();
3476 break;
3478 case 0x52: // removable media eject
3479 CLEAR_CF();
3480 regs.u.r8.ah = 0; // "ok ejection may proceed"
3481 break;
3483 case 0x83: {
3484 if( regs.u.r8.al == 0 ) {
3485 // Set Interval requested.
3486 if( ( read_byte( 0x40, 0xA0 ) & 1 ) == 0 ) {
3487 // Interval not already set.
3488 write_byte( 0x40, 0xA0, 1 ); // Set status byte.
3489 write_word( 0x40, 0x98, ES ); // Byte location, segment
3490 write_word( 0x40, 0x9A, regs.u.r16.bx ); // Byte location, offset
3491 write_word( 0x40, 0x9C, regs.u.r16.dx ); // Low word, delay
3492 write_word( 0x40, 0x9E, regs.u.r16.cx ); // High word, delay.
3493 CLEAR_CF( );
3494 irqDisable = inb( 0xA1 );
3495 outb( 0xA1, irqDisable & 0xFE );
3496 bRegister = inb_cmos( 0xB ); // Unmask IRQ8 so INT70 will get through.
3497 outb_cmos( 0xB, bRegister | 0x40 ); // Turn on the Periodic Interrupt timer
3498 } else {
3499 // Interval already set.
3500 BX_DEBUG_INT15("int15: Func 83h, failed, already waiting.\n" );
3501 SET_CF();
3502 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3504 } else if( regs.u.r8.al == 1 ) {
3505 // Clear Interval requested
3506 write_byte( 0x40, 0xA0, 0 ); // Clear status byte
3507 CLEAR_CF( );
3508 bRegister = inb_cmos( 0xB );
3509 outb_cmos( 0xB, bRegister & ~0x40 ); // Turn off the Periodic Interrupt timer
3510 } else {
3511 BX_DEBUG_INT15("int15: Func 83h, failed.\n" );
3512 SET_CF();
3513 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3514 regs.u.r8.al--;
3517 break;
3520 case 0x87:
3521 #if BX_CPU < 3
3522 # error "Int15 function 87h not supported on < 80386"
3523 #endif
3524 // +++ should probably have descriptor checks
3525 // +++ should have exception handlers
3527 // turn off interrupts
3528 ASM_START
3530 ASM_END
3532 prev_a20_enable = set_enable_a20(1); // enable A20 line
3534 // 128K max of transfer on 386+ ???
3535 // source == destination ???
3537 // ES:SI points to descriptor table
3538 // offset use initially comments
3539 // ==============================================
3540 // 00..07 Unused zeros Null descriptor
3541 // 08..0f GDT zeros filled in by BIOS
3542 // 10..17 source ssssssss source of data
3543 // 18..1f dest dddddddd destination of data
3544 // 20..27 CS zeros filled in by BIOS
3545 // 28..2f SS zeros filled in by BIOS
3547 //es:si
3548 //eeee0
3549 //0ssss
3550 //-----
3552 // check for access rights of source & dest here
3554 // Initialize GDT descriptor
3555 base15_00 = (ES << 4) + regs.u.r16.si;
3556 base23_16 = ES >> 12;
3557 if (base15_00 < (ES<<4))
3558 base23_16++;
3559 write_word(ES, regs.u.r16.si+0x08+0, 47); // limit 15:00 = 6 * 8bytes/descriptor
3560 write_word(ES, regs.u.r16.si+0x08+2, base15_00);// base 15:00
3561 write_byte(ES, regs.u.r16.si+0x08+4, base23_16);// base 23:16
3562 write_byte(ES, regs.u.r16.si+0x08+5, 0x93); // access
3563 write_word(ES, regs.u.r16.si+0x08+6, 0x0000); // base 31:24/reserved/limit 19:16
3565 // Initialize CS descriptor
3566 write_word(ES, regs.u.r16.si+0x20+0, 0xffff);// limit 15:00 = normal 64K limit
3567 write_word(ES, regs.u.r16.si+0x20+2, 0x0000);// base 15:00
3568 write_byte(ES, regs.u.r16.si+0x20+4, 0x000f);// base 23:16
3569 write_byte(ES, regs.u.r16.si+0x20+5, 0x9b); // access
3570 write_word(ES, regs.u.r16.si+0x20+6, 0x0000);// base 31:24/reserved/limit 19:16
3572 // Initialize SS descriptor
3573 ss = get_SS();
3574 base15_00 = ss << 4;
3575 base23_16 = ss >> 12;
3576 write_word(ES, regs.u.r16.si+0x28+0, 0xffff); // limit 15:00 = normal 64K limit
3577 write_word(ES, regs.u.r16.si+0x28+2, base15_00);// base 15:00
3578 write_byte(ES, regs.u.r16.si+0x28+4, base23_16);// base 23:16
3579 write_byte(ES, regs.u.r16.si+0x28+5, 0x93); // access
3580 write_word(ES, regs.u.r16.si+0x28+6, 0x0000); // base 31:24/reserved/limit 19:16
3582 CX = regs.u.r16.cx;
3583 ASM_START
3584 // Compile generates locals offset info relative to SP.
3585 // Get CX (word count) from stack.
3586 mov bx, sp
3587 SEG SS
3588 mov cx, _int15_function.CX [bx]
3590 // since we need to set SS:SP, save them to the BDA
3591 // for future restore
3592 push eax
3593 xor eax, eax
3594 mov ds, ax
3595 mov 0x0469, ss
3596 mov 0x0467, sp
3598 SEG ES
3599 lgdt [si + 0x08]
3600 SEG CS
3601 lidt [pmode_IDT_info]
3602 ;; perhaps do something with IDT here
3604 ;; set PE bit in CR0
3605 mov eax, cr0
3606 or al, #0x01
3607 mov cr0, eax
3608 ;; far jump to flush CPU queue after transition to protected mode
3609 JMP_AP(0x0020, protected_mode)
3611 protected_mode:
3612 ;; GDT points to valid descriptor table, now load SS, DS, ES
3613 mov ax, #0x28 ;; 101 000 = 5th descriptor in table, TI=GDT, RPL=00
3614 mov ss, ax
3615 mov ax, #0x10 ;; 010 000 = 2nd descriptor in table, TI=GDT, RPL=00
3616 mov ds, ax
3617 mov ax, #0x18 ;; 011 000 = 3rd descriptor in table, TI=GDT, RPL=00
3618 mov es, ax
3619 xor si, si
3620 xor di, di
3623 movsw ;; move CX words from DS:SI to ES:DI
3625 ;; make sure DS and ES limits are 64KB
3626 mov ax, #0x28
3627 mov ds, ax
3628 mov es, ax
3630 ;; reset PG bit in CR0 ???
3631 mov eax, cr0
3632 and al, #0xFE
3633 mov cr0, eax
3635 ;; far jump to flush CPU queue after transition to real mode
3636 JMP_AP(0xf000, real_mode)
3638 real_mode:
3639 ;; restore IDT to normal real-mode defaults
3640 SEG CS
3641 lidt [rmode_IDT_info]
3643 // restore SS:SP from the BDA
3644 xor ax, ax
3645 mov ds, ax
3646 mov ss, 0x0469
3647 mov sp, 0x0467
3648 pop eax
3649 ASM_END
3651 set_enable_a20(prev_a20_enable);
3653 // turn back on interrupts
3654 ASM_START
3656 ASM_END
3658 regs.u.r8.ah = 0;
3659 CLEAR_CF();
3660 break;
3663 case 0x88:
3664 // Get the amount of extended memory (above 1M)
3665 #if BX_CPU < 2
3666 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3667 SET_CF();
3668 #else
3669 regs.u.r8.al = inb_cmos(0x30);
3670 regs.u.r8.ah = inb_cmos(0x31);
3672 // limit to 15M
3673 if(regs.u.r16.ax > 0x3c00)
3674 regs.u.r16.ax = 0x3c00;
3676 CLEAR_CF();
3677 #endif
3678 break;
3680 case 0x90:
3681 /* Device busy interrupt. Called by Int 16h when no key available */
3682 break;
3684 case 0x91:
3685 /* Interrupt complete. Called by Int 16h when key becomes available */
3686 break;
3688 case 0xbf:
3689 BX_INFO("*** int 15h function AH=bf not yet supported!\n");
3690 SET_CF();
3691 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3692 break;
3694 case 0xC0:
3695 #if 0
3696 SET_CF();
3697 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3698 break;
3699 #endif
3700 CLEAR_CF();
3701 regs.u.r8.ah = 0;
3702 regs.u.r16.bx = BIOS_CONFIG_TABLE;
3703 ES = 0xF000;
3704 break;
3706 case 0xc1:
3707 ES = ebda_seg;
3708 CLEAR_CF();
3709 break;
3711 case 0xd8:
3712 bios_printf(BIOS_PRINTF_DEBUG, "EISA BIOS not present\n");
3713 SET_CF();
3714 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3715 break;
3717 default:
3718 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
3719 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
3720 SET_CF();
3721 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3722 break;
3726 #if BX_USE_PS2_MOUSE
3727 void
3728 int15_function_mouse(regs, ES, DS, FLAGS)
3729 pusha_regs_t regs; // REGS pushed via pusha
3730 Bit16u ES, DS, FLAGS;
3732 Bit16u ebda_seg=read_word(0x0040,0x000E);
3733 Bit8u mouse_flags_1, mouse_flags_2;
3734 Bit16u mouse_driver_seg;
3735 Bit16u mouse_driver_offset;
3736 Bit8u comm_byte, prev_command_byte;
3737 Bit8u ret, mouse_data1, mouse_data2, mouse_data3;
3739 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3741 switch (regs.u.r8.ah) {
3742 case 0xC2:
3743 // Return Codes status in AH
3744 // =========================
3745 // 00: success
3746 // 01: invalid subfunction (AL > 7)
3747 // 02: invalid input value (out of allowable range)
3748 // 03: interface error
3749 // 04: resend command received from mouse controller,
3750 // device driver should attempt command again
3751 // 05: cannot enable mouse, since no far call has been installed
3752 // 80/86: mouse service not implemented
3754 switch (regs.u.r8.al) {
3755 case 0: // Disable/Enable Mouse
3756 BX_DEBUG_INT15("case 0:\n");
3757 switch (regs.u.r8.bh) {
3758 case 0: // Disable Mouse
3759 BX_DEBUG_INT15("case 0: disable mouse\n");
3760 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3761 ret = send_to_mouse_ctrl(0xF5); // disable mouse command
3762 if (ret == 0) {
3763 ret = get_mouse_data(&mouse_data1);
3764 if ( (ret == 0) || (mouse_data1 == 0xFA) ) {
3765 CLEAR_CF();
3766 regs.u.r8.ah = 0;
3767 return;
3771 // error
3772 SET_CF();
3773 regs.u.r8.ah = ret;
3774 return;
3775 break;
3777 case 1: // Enable Mouse
3778 BX_DEBUG_INT15("case 1: enable mouse\n");
3779 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
3780 if ( (mouse_flags_2 & 0x80) == 0 ) {
3781 BX_DEBUG_INT15("INT 15h C2 Enable Mouse, no far call handler\n");
3782 SET_CF(); // error
3783 regs.u.r8.ah = 5; // no far call installed
3784 return;
3786 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3787 ret = send_to_mouse_ctrl(0xF4); // enable mouse command
3788 if (ret == 0) {
3789 ret = get_mouse_data(&mouse_data1);
3790 if ( (ret == 0) && (mouse_data1 == 0xFA) ) {
3791 enable_mouse_int_and_events(); // turn IRQ12 and packet generation on
3792 CLEAR_CF();
3793 regs.u.r8.ah = 0;
3794 return;
3797 SET_CF();
3798 regs.u.r8.ah = ret;
3799 return;
3801 default: // invalid subfunction
3802 BX_DEBUG_INT15("INT 15h C2 AL=0, BH=%02x\n", (unsigned) regs.u.r8.bh);
3803 SET_CF(); // error
3804 regs.u.r8.ah = 1; // invalid subfunction
3805 return;
3807 break;
3809 case 1: // Reset Mouse
3810 case 5: // Initialize Mouse
3811 BX_DEBUG_INT15("case 1 or 5:\n");
3812 if (regs.u.r8.al == 5) {
3813 if (regs.u.r8.bh != 3) {
3814 SET_CF();
3815 regs.u.r8.ah = 0x02; // invalid input
3816 return;
3818 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
3819 mouse_flags_2 = (mouse_flags_2 & 0x00) | regs.u.r8.bh;
3820 mouse_flags_1 = 0x00;
3821 write_byte(ebda_seg, 0x0026, mouse_flags_1);
3822 write_byte(ebda_seg, 0x0027, mouse_flags_2);
3825 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3826 ret = send_to_mouse_ctrl(0xFF); // reset mouse command
3827 if (ret == 0) {
3828 ret = get_mouse_data(&mouse_data3);
3829 // if no mouse attached, it will return RESEND
3830 if (mouse_data3 == 0xfe) {
3831 SET_CF();
3832 return;
3834 if (mouse_data3 != 0xfa)
3835 BX_PANIC("Mouse reset returned %02x (should be ack)\n", (unsigned)mouse_data3);
3836 if ( ret == 0 ) {
3837 ret = get_mouse_data(&mouse_data1);
3838 if ( ret == 0 ) {
3839 ret = get_mouse_data(&mouse_data2);
3840 if ( ret == 0 ) {
3841 // turn IRQ12 and packet generation on
3842 enable_mouse_int_and_events();
3843 CLEAR_CF();
3844 regs.u.r8.ah = 0;
3845 regs.u.r8.bl = mouse_data1;
3846 regs.u.r8.bh = mouse_data2;
3847 return;
3853 // error
3854 SET_CF();
3855 regs.u.r8.ah = ret;
3856 return;
3858 case 2: // Set Sample Rate
3859 BX_DEBUG_INT15("case 2:\n");
3860 switch (regs.u.r8.bh) {
3861 case 0: mouse_data1 = 10; break; // 10 reports/sec
3862 case 1: mouse_data1 = 20; break; // 20 reports/sec
3863 case 2: mouse_data1 = 40; break; // 40 reports/sec
3864 case 3: mouse_data1 = 60; break; // 60 reports/sec
3865 case 4: mouse_data1 = 80; break; // 80 reports/sec
3866 case 5: mouse_data1 = 100; break; // 100 reports/sec (default)
3867 case 6: mouse_data1 = 200; break; // 200 reports/sec
3868 default: mouse_data1 = 0;
3870 if (mouse_data1 > 0) {
3871 ret = send_to_mouse_ctrl(0xF3); // set sample rate command
3872 if (ret == 0) {
3873 ret = get_mouse_data(&mouse_data2);
3874 ret = send_to_mouse_ctrl(mouse_data1);
3875 ret = get_mouse_data(&mouse_data2);
3876 CLEAR_CF();
3877 regs.u.r8.ah = 0;
3878 } else {
3879 // error
3880 SET_CF();
3881 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3883 } else {
3884 // error
3885 SET_CF();
3886 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3888 break;
3890 case 3: // Set Resolution
3891 BX_DEBUG_INT15("case 3:\n");
3892 // BX:
3893 // 0 = 25 dpi, 1 count per millimeter
3894 // 1 = 50 dpi, 2 counts per millimeter
3895 // 2 = 100 dpi, 4 counts per millimeter
3896 // 3 = 200 dpi, 8 counts per millimeter
3897 CLEAR_CF();
3898 regs.u.r8.ah = 0;
3899 break;
3901 case 4: // Get Device ID
3902 BX_DEBUG_INT15("case 4:\n");
3903 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3904 ret = send_to_mouse_ctrl(0xF2); // get mouse ID command
3905 if (ret == 0) {
3906 ret = get_mouse_data(&mouse_data1);
3907 ret = get_mouse_data(&mouse_data2);
3908 CLEAR_CF();
3909 regs.u.r8.ah = 0;
3910 regs.u.r8.bh = mouse_data2;
3911 } else {
3912 // error
3913 SET_CF();
3914 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3916 break;
3918 case 6: // Return Status & Set Scaling Factor...
3919 BX_DEBUG_INT15("case 6:\n");
3920 switch (regs.u.r8.bh) {
3921 case 0: // Return Status
3922 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3923 ret = send_to_mouse_ctrl(0xE9); // get mouse info command
3924 if (ret == 0) {
3925 ret = get_mouse_data(&mouse_data1);
3926 if (mouse_data1 != 0xfa)
3927 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
3928 if (ret == 0) {
3929 ret = get_mouse_data(&mouse_data1);
3930 if ( ret == 0 ) {
3931 ret = get_mouse_data(&mouse_data2);
3932 if ( ret == 0 ) {
3933 ret = get_mouse_data(&mouse_data3);
3934 if ( ret == 0 ) {
3935 CLEAR_CF();
3936 regs.u.r8.ah = 0;
3937 regs.u.r8.bl = mouse_data1;
3938 regs.u.r8.cl = mouse_data2;
3939 regs.u.r8.dl = mouse_data3;
3940 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
3941 return;
3948 // error
3949 SET_CF();
3950 regs.u.r8.ah = ret;
3951 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
3952 return;
3954 case 1: // Set Scaling Factor to 1:1
3955 case 2: // Set Scaling Factor to 2:1
3956 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3957 if (regs.u.r8.bh == 1) {
3958 ret = send_to_mouse_ctrl(0xE6);
3959 } else {
3960 ret = send_to_mouse_ctrl(0xE7);
3962 if (ret == 0) {
3963 get_mouse_data(&mouse_data1);
3964 ret = (mouse_data1 != 0xFA);
3966 if (ret == 0) {
3967 CLEAR_CF();
3968 regs.u.r8.ah = 0;
3969 } else {
3970 // error
3971 SET_CF();
3972 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3974 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
3975 break;
3977 default:
3978 BX_PANIC("INT 15h C2 AL=6, BH=%02x\n", (unsigned) regs.u.r8.bh);
3980 break;
3982 case 7: // Set Mouse Handler Address
3983 BX_DEBUG_INT15("case 7:\n");
3984 mouse_driver_seg = ES;
3985 mouse_driver_offset = regs.u.r16.bx;
3986 write_word(ebda_seg, 0x0022, mouse_driver_offset);
3987 write_word(ebda_seg, 0x0024, mouse_driver_seg);
3988 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
3989 if (mouse_driver_offset == 0 && mouse_driver_seg == 0) {
3990 /* remove handler */
3991 if ( (mouse_flags_2 & 0x80) != 0 ) {
3992 mouse_flags_2 &= ~0x80;
3993 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3996 else {
3997 /* install handler */
3998 mouse_flags_2 |= 0x80;
4000 write_byte(ebda_seg, 0x0027, mouse_flags_2);
4001 CLEAR_CF();
4002 regs.u.r8.ah = 0;
4003 break;
4005 default:
4006 BX_DEBUG_INT15("case default:\n");
4007 regs.u.r8.ah = 1; // invalid function
4008 SET_CF();
4010 break;
4012 default:
4013 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4014 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4015 SET_CF();
4016 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4017 break;
4020 #endif
4022 void
4023 int15_function32(regs, ES, DS, FLAGS)
4024 pushad_regs_t regs; // REGS pushed via pushad
4025 Bit16u ES, DS, FLAGS;
4027 Bit32u extended_memory_size=0; // 64bits long
4028 Bit16u CX,DX;
4030 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
4032 switch (regs.u.r8.ah) {
4033 case 0x86:
4034 // Wait for CX:DX microseconds. currently using the
4035 // refresh request port 0x61 bit4, toggling every 15usec
4037 CX = regs.u.r16.cx;
4038 DX = regs.u.r16.dx;
4040 ASM_START
4043 ;; Get the count in eax
4044 mov bx, sp
4045 SEG SS
4046 mov ax, _int15_function.CX [bx]
4047 shl eax, #16
4048 SEG SS
4049 mov ax, _int15_function.DX [bx]
4051 ;; convert to numbers of 15usec ticks
4052 mov ebx, #15
4053 xor edx, edx
4054 div eax, ebx
4055 mov ecx, eax
4057 ;; wait for ecx number of refresh requests
4058 in al, #0x61
4059 and al,#0x10
4060 mov ah, al
4062 or ecx, ecx
4063 je int1586_tick_end
4064 int1586_tick:
4065 in al, #0x61
4066 and al,#0x10
4067 cmp al, ah
4068 je int1586_tick
4069 mov ah, al
4070 dec ecx
4071 jnz int1586_tick
4072 int1586_tick_end:
4073 ASM_END
4075 break;
4077 case 0xe8:
4078 switch(regs.u.r8.al)
4080 case 0x20: // coded by osmaker aka K.J.
4081 if(regs.u.r32.edx == 0x534D4150)
4083 switch(regs.u.r16.bx)
4085 case 0:
4086 write_word(ES, regs.u.r16.di, 0x00);
4087 write_word(ES, regs.u.r16.di+2, 0x00);
4088 write_word(ES, regs.u.r16.di+4, 0x00);
4089 write_word(ES, regs.u.r16.di+6, 0x00);
4091 write_word(ES, regs.u.r16.di+8, 0xFC00);
4092 write_word(ES, regs.u.r16.di+10, 0x0009);
4093 write_word(ES, regs.u.r16.di+12, 0x0000);
4094 write_word(ES, regs.u.r16.di+14, 0x0000);
4096 write_word(ES, regs.u.r16.di+16, 0x1);
4097 write_word(ES, regs.u.r16.di+18, 0x0);
4099 regs.u.r32.ebx = 1;
4100 regs.u.r32.eax = 0x534D4150;
4101 regs.u.r32.ecx = 0x14;
4102 CLEAR_CF();
4103 return;
4104 break;
4105 case 1:
4106 extended_memory_size = inb_cmos(0x35);
4107 extended_memory_size <<= 8;
4108 extended_memory_size |= inb_cmos(0x34);
4109 extended_memory_size *= 64;
4110 if(extended_memory_size > 0x3bc000) // greater than EFF00000???
4112 extended_memory_size = 0x3bc000; // everything after this is reserved memory until we get to 0x100000000
4114 extended_memory_size *= 1024;
4115 extended_memory_size += 15728640; // make up for the 16mb of memory that is chopped off
4117 if(extended_memory_size <= 15728640)
4119 extended_memory_size = inb_cmos(0x31);
4120 extended_memory_size <<= 8;
4121 extended_memory_size |= inb_cmos(0x30);
4122 extended_memory_size *= 1024;
4125 write_word(ES, regs.u.r16.di, 0x0000);
4126 write_word(ES, regs.u.r16.di+2, 0x0010);
4127 write_word(ES, regs.u.r16.di+4, 0x0000);
4128 write_word(ES, regs.u.r16.di+6, 0x0000);
4130 write_word(ES, regs.u.r16.di+8, extended_memory_size);
4131 extended_memory_size >>= 16;
4132 write_word(ES, regs.u.r16.di+10, extended_memory_size);
4133 extended_memory_size >>= 16;
4134 write_word(ES, regs.u.r16.di+12, extended_memory_size);
4135 extended_memory_size >>= 16;
4136 write_word(ES, regs.u.r16.di+14, extended_memory_size);
4138 write_word(ES, regs.u.r16.di+16, 0x1);
4139 write_word(ES, regs.u.r16.di+18, 0x0);
4141 regs.u.r32.ebx = 0;
4142 regs.u.r32.eax = 0x534D4150;
4143 regs.u.r32.ecx = 0x14;
4144 CLEAR_CF();
4145 return;
4146 break;
4147 default: /* AX=E820, DX=534D4150, BX unrecognized */
4148 goto int15_unimplemented;
4149 break;
4151 } else {
4152 // if DX != 0x534D4150)
4153 goto int15_unimplemented;
4155 break;
4157 case 0x01:
4158 // do we have any reason to fail here ?
4159 CLEAR_CF();
4161 // my real system sets ax and bx to 0
4162 // this is confirmed by Ralph Brown list
4163 // but syslinux v1.48 is known to behave
4164 // strangely if ax is set to 0
4165 // regs.u.r16.ax = 0;
4166 // regs.u.r16.bx = 0;
4168 // Get the amount of extended memory (above 1M)
4169 regs.u.r8.cl = inb_cmos(0x30);
4170 regs.u.r8.ch = inb_cmos(0x31);
4172 // limit to 15M
4173 if(regs.u.r16.cx > 0x3c00)
4175 regs.u.r16.cx = 0x3c00;
4178 // Get the amount of extended memory above 16M in 64k blocs
4179 regs.u.r8.dl = inb_cmos(0x34);
4180 regs.u.r8.dh = inb_cmos(0x35);
4182 // Set configured memory equal to extended memory
4183 regs.u.r16.ax = regs.u.r16.cx;
4184 regs.u.r16.bx = regs.u.r16.dx;
4185 break;
4186 default: /* AH=0xE8?? but not implemented */
4187 goto int15_unimplemented;
4189 break;
4190 int15_unimplemented:
4191 // fall into the default
4192 default:
4193 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4194 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4195 SET_CF();
4196 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4197 break;
4201 void
4202 int16_function(DI, SI, BP, SP, BX, DX, CX, AX, FLAGS)
4203 Bit16u DI, SI, BP, SP, BX, DX, CX, AX, FLAGS;
4205 Bit8u scan_code, ascii_code, shift_flags, count;
4206 Bit16u kbd_code, max;
4208 BX_DEBUG_INT16("int16: AX=%04x BX=%04x CX=%04x DX=%04x \n", AX, BX, CX, DX);
4210 switch (GET_AH()) {
4211 case 0x00: /* read keyboard input */
4213 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4214 BX_PANIC("KBD: int16h: out of keyboard input\n");
4216 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4217 else if (ascii_code == 0xE0) ascii_code = 0;
4218 AX = (scan_code << 8) | ascii_code;
4219 break;
4221 case 0x01: /* check keyboard status */
4222 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4223 SET_ZF();
4224 return;
4226 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4227 else if (ascii_code == 0xE0) ascii_code = 0;
4228 AX = (scan_code << 8) | ascii_code;
4229 CLEAR_ZF();
4230 break;
4232 case 0x02: /* get shift flag status */
4233 shift_flags = read_byte(0x0040, 0x17);
4234 SET_AL(shift_flags);
4235 break;
4237 case 0x05: /* store key-stroke into buffer */
4238 if ( !enqueue_key(GET_CH(), GET_CL()) ) {
4239 SET_AL(1);
4241 else {
4242 SET_AL(0);
4244 break;
4246 case 0x09: /* GET KEYBOARD FUNCTIONALITY */
4247 // bit Bochs Description
4248 // 7 0 reserved
4249 // 6 0 INT 16/AH=20h-22h supported (122-key keyboard support)
4250 // 5 1 INT 16/AH=10h-12h supported (enhanced keyboard support)
4251 // 4 1 INT 16/AH=0Ah supported
4252 // 3 0 INT 16/AX=0306h supported
4253 // 2 0 INT 16/AX=0305h supported
4254 // 1 0 INT 16/AX=0304h supported
4255 // 0 0 INT 16/AX=0300h supported
4257 SET_AL(0x30);
4258 break;
4260 case 0x0A: /* GET KEYBOARD ID */
4261 count = 2;
4262 kbd_code = 0x0;
4263 outb(0x60, 0xf2);
4264 /* Wait for data */
4265 max=0xffff;
4266 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4267 if (max>0x0) {
4268 if ((inb(0x60) == 0xfa)) {
4269 do {
4270 max=0xffff;
4271 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4272 if (max>0x0) {
4273 kbd_code >>= 8;
4274 kbd_code |= (inb(0x60) << 8);
4276 } while (--count>0);
4279 BX=kbd_code;
4280 break;
4282 case 0x10: /* read MF-II keyboard input */
4284 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4285 BX_PANIC("KBD: int16h: out of keyboard input\n");
4287 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4288 AX = (scan_code << 8) | ascii_code;
4289 break;
4291 case 0x11: /* check MF-II keyboard status */
4292 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4293 SET_ZF();
4294 return;
4296 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4297 AX = (scan_code << 8) | ascii_code;
4298 CLEAR_ZF();
4299 break;
4301 case 0x12: /* get extended keyboard status */
4302 shift_flags = read_byte(0x0040, 0x17);
4303 SET_AL(shift_flags);
4304 shift_flags = read_byte(0x0040, 0x18) & 0x73;
4305 shift_flags |= read_byte(0x0040, 0x96) & 0x0c;
4306 SET_AH(shift_flags);
4307 BX_DEBUG_INT16("int16: func 12 sending %04x\n",AX);
4308 break;
4310 case 0x92: /* keyboard capability check called by DOS 5.0+ keyb */
4311 SET_AH(0x80); // function int16 ah=0x10-0x12 supported
4312 break;
4314 case 0xA2: /* 122 keys capability check called by DOS 5.0+ keyb */
4315 // don't change AH : function int16 ah=0x20-0x22 NOT supported
4316 break;
4318 case 0x6F:
4319 if (GET_AL() == 0x08)
4320 SET_AH(0x02); // unsupported, aka normal keyboard
4322 default:
4323 BX_INFO("KBD: unsupported int 16h function %02x\n", GET_AH());
4327 unsigned int
4328 dequeue_key(scan_code, ascii_code, incr)
4329 Bit8u *scan_code;
4330 Bit8u *ascii_code;
4331 unsigned int incr;
4333 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail;
4334 Bit16u ss;
4335 Bit8u acode, scode;
4337 #if BX_CPU < 2
4338 buffer_start = 0x001E;
4339 buffer_end = 0x003E;
4340 #else
4341 buffer_start = read_word(0x0040, 0x0080);
4342 buffer_end = read_word(0x0040, 0x0082);
4343 #endif
4345 buffer_head = read_word(0x0040, 0x001a);
4346 buffer_tail = read_word(0x0040, 0x001c);
4348 if (buffer_head != buffer_tail) {
4349 ss = get_SS();
4350 acode = read_byte(0x0040, buffer_head);
4351 scode = read_byte(0x0040, buffer_head+1);
4352 write_byte(ss, ascii_code, acode);
4353 write_byte(ss, scan_code, scode);
4355 if (incr) {
4356 buffer_head += 2;
4357 if (buffer_head >= buffer_end)
4358 buffer_head = buffer_start;
4359 write_word(0x0040, 0x001a, buffer_head);
4361 return(1);
4363 else {
4364 return(0);
4368 static char panic_msg_keyb_buffer_full[] = "%s: keyboard input buffer full\n";
4370 Bit8u
4371 inhibit_mouse_int_and_events()
4373 Bit8u command_byte, prev_command_byte;
4375 // Turn off IRQ generation and aux data line
4376 if ( inb(0x64) & 0x02 )
4377 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4378 outb(0x64, 0x20); // get command byte
4379 while ( (inb(0x64) & 0x01) != 0x01 );
4380 prev_command_byte = inb(0x60);
4381 command_byte = prev_command_byte;
4382 //while ( (inb(0x64) & 0x02) );
4383 if ( inb(0x64) & 0x02 )
4384 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4385 command_byte &= 0xfd; // turn off IRQ 12 generation
4386 command_byte |= 0x20; // disable mouse serial clock line
4387 outb(0x64, 0x60); // write command byte
4388 outb(0x60, command_byte);
4389 return(prev_command_byte);
4392 void
4393 enable_mouse_int_and_events()
4395 Bit8u command_byte;
4397 // Turn on IRQ generation and aux data line
4398 if ( inb(0x64) & 0x02 )
4399 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4400 outb(0x64, 0x20); // get command byte
4401 while ( (inb(0x64) & 0x01) != 0x01 );
4402 command_byte = inb(0x60);
4403 //while ( (inb(0x64) & 0x02) );
4404 if ( inb(0x64) & 0x02 )
4405 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4406 command_byte |= 0x02; // turn on IRQ 12 generation
4407 command_byte &= 0xdf; // enable mouse serial clock line
4408 outb(0x64, 0x60); // write command byte
4409 outb(0x60, command_byte);
4412 Bit8u
4413 send_to_mouse_ctrl(sendbyte)
4414 Bit8u sendbyte;
4416 Bit8u response;
4418 // wait for chance to write to ctrl
4419 if ( inb(0x64) & 0x02 )
4420 BX_PANIC(panic_msg_keyb_buffer_full,"sendmouse");
4421 outb(0x64, 0xD4);
4422 outb(0x60, sendbyte);
4423 return(0);
4427 Bit8u
4428 get_mouse_data(data)
4429 Bit8u *data;
4431 Bit8u response;
4432 Bit16u ss;
4434 while ( (inb(0x64) & 0x21) != 0x21 ) {
4437 response = inb(0x60);
4439 ss = get_SS();
4440 write_byte(ss, data, response);
4441 return(0);
4444 void
4445 set_kbd_command_byte(command_byte)
4446 Bit8u command_byte;
4448 if ( inb(0x64) & 0x02 )
4449 BX_PANIC(panic_msg_keyb_buffer_full,"setkbdcomm");
4450 outb(0x64, 0xD4);
4452 outb(0x64, 0x60); // write command byte
4453 outb(0x60, command_byte);
4456 void
4457 int09_function(DI, SI, BP, SP, BX, DX, CX, AX)
4458 Bit16u DI, SI, BP, SP, BX, DX, CX, AX;
4460 Bit8u scancode, asciicode, shift_flags;
4461 Bit8u mf2_flags, mf2_state, led_flags;
4464 // DS has been set to F000 before call
4468 scancode = GET_AL();
4470 if (scancode == 0) {
4471 BX_INFO("KBD: int09 handler: AL=0\n");
4472 return;
4476 shift_flags = read_byte(0x0040, 0x17);
4477 mf2_flags = read_byte(0x0040, 0x18);
4478 mf2_state = read_byte(0x0040, 0x96);
4479 led_flags = read_byte(0x0040, 0x97);
4480 asciicode = 0;
4482 switch (scancode) {
4483 case 0x3a: /* Caps Lock press */
4484 shift_flags ^= 0x40;
4485 write_byte(0x0040, 0x17, shift_flags);
4486 mf2_flags |= 0x40;
4487 led_flags ^= 0x04;
4488 write_byte(0x0040, 0x18, mf2_flags);
4489 write_byte(0x0040, 0x97, led_flags);
4490 break;
4491 case 0xba: /* Caps Lock release */
4492 mf2_flags &= ~0x40;
4493 write_byte(0x0040, 0x18, mf2_flags);
4494 break;
4496 case 0x2a: /* L Shift press */
4497 shift_flags |= 0x02;
4498 write_byte(0x0040, 0x17, shift_flags);
4499 break;
4500 case 0xaa: /* L Shift release */
4501 shift_flags &= ~0x02;
4502 write_byte(0x0040, 0x17, shift_flags);
4503 break;
4505 case 0x36: /* R Shift press */
4506 shift_flags |= 0x01;
4507 write_byte(0x0040, 0x17, shift_flags);
4508 break;
4509 case 0xb6: /* R Shift release */
4510 shift_flags &= ~0x01;
4511 write_byte(0x0040, 0x17, shift_flags);
4512 break;
4514 case 0x1d: /* Ctrl press */
4515 shift_flags |= 0x04;
4516 write_byte(0x0040, 0x17, shift_flags);
4517 if (mf2_state & 0x02) {
4518 mf2_state |= 0x04;
4519 write_byte(0x0040, 0x96, mf2_state);
4520 } else {
4521 mf2_flags |= 0x01;
4522 write_byte(0x0040, 0x18, mf2_flags);
4524 break;
4525 case 0x9d: /* Ctrl release */
4526 shift_flags &= ~0x04;
4527 write_byte(0x0040, 0x17, shift_flags);
4528 if (mf2_state & 0x02) {
4529 mf2_state &= ~0x04;
4530 write_byte(0x0040, 0x96, mf2_state);
4531 } else {
4532 mf2_flags &= ~0x01;
4533 write_byte(0x0040, 0x18, mf2_flags);
4535 break;
4537 case 0x38: /* Alt press */
4538 shift_flags |= 0x08;
4539 write_byte(0x0040, 0x17, shift_flags);
4540 if (mf2_state & 0x02) {
4541 mf2_state |= 0x08;
4542 write_byte(0x0040, 0x96, mf2_state);
4543 } else {
4544 mf2_flags |= 0x02;
4545 write_byte(0x0040, 0x18, mf2_flags);
4547 break;
4548 case 0xb8: /* Alt release */
4549 shift_flags &= ~0x08;
4550 write_byte(0x0040, 0x17, shift_flags);
4551 if (mf2_state & 0x02) {
4552 mf2_state &= ~0x08;
4553 write_byte(0x0040, 0x96, mf2_state);
4554 } else {
4555 mf2_flags &= ~0x02;
4556 write_byte(0x0040, 0x18, mf2_flags);
4558 break;
4560 case 0x45: /* Num Lock press */
4561 if ((mf2_state & 0x02) == 0) {
4562 mf2_flags |= 0x20;
4563 write_byte(0x0040, 0x18, mf2_flags);
4564 shift_flags ^= 0x20;
4565 led_flags ^= 0x02;
4566 write_byte(0x0040, 0x17, shift_flags);
4567 write_byte(0x0040, 0x97, led_flags);
4569 break;
4570 case 0xc5: /* Num Lock release */
4571 if ((mf2_state & 0x02) == 0) {
4572 mf2_flags &= ~0x20;
4573 write_byte(0x0040, 0x18, mf2_flags);
4575 break;
4577 case 0x46: /* Scroll Lock press */
4578 mf2_flags |= 0x10;
4579 write_byte(0x0040, 0x18, mf2_flags);
4580 shift_flags ^= 0x10;
4581 led_flags ^= 0x01;
4582 write_byte(0x0040, 0x17, shift_flags);
4583 write_byte(0x0040, 0x97, led_flags);
4584 break;
4586 case 0xc6: /* Scroll Lock release */
4587 mf2_flags &= ~0x10;
4588 write_byte(0x0040, 0x18, mf2_flags);
4589 break;
4591 default:
4592 if (scancode & 0x80) return; /* toss key releases ... */
4593 if (scancode > MAX_SCAN_CODE) {
4594 BX_INFO("KBD: int09h_handler(): unknown scancode read: 0x%02x!\n", scancode);
4595 return;
4597 if (shift_flags & 0x08) { /* ALT */
4598 asciicode = scan_to_scanascii[scancode].alt;
4599 scancode = scan_to_scanascii[scancode].alt >> 8;
4601 else if (shift_flags & 0x04) { /* CONTROL */
4602 asciicode = scan_to_scanascii[scancode].control;
4603 scancode = scan_to_scanascii[scancode].control >> 8;
4605 else if (shift_flags & 0x03) { /* LSHIFT + RSHIFT */
4606 /* check if lock state should be ignored
4607 * because a SHIFT key are pressed */
4609 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
4610 asciicode = scan_to_scanascii[scancode].normal;
4611 scancode = scan_to_scanascii[scancode].normal >> 8;
4613 else {
4614 asciicode = scan_to_scanascii[scancode].shift;
4615 scancode = scan_to_scanascii[scancode].shift >> 8;
4618 else {
4619 /* check if lock is on */
4620 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
4621 asciicode = scan_to_scanascii[scancode].shift;
4622 scancode = scan_to_scanascii[scancode].shift >> 8;
4624 else {
4625 asciicode = scan_to_scanascii[scancode].normal;
4626 scancode = scan_to_scanascii[scancode].normal >> 8;
4629 if (scancode==0 && asciicode==0) {
4630 BX_INFO("KBD: int09h_handler(): scancode & asciicode are zero?\n");
4632 enqueue_key(scancode, asciicode);
4633 break;
4635 mf2_state &= ~0x02;
4636 write_byte(0x0040, 0x96, mf2_state);
4639 unsigned int
4640 enqueue_key(scan_code, ascii_code)
4641 Bit8u scan_code, ascii_code;
4643 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail, temp_tail;
4645 //BX_INFO("KBD: enqueue_key() called scan:%02x, ascii:%02x\n",
4646 // scan_code, ascii_code);
4648 #if BX_CPU < 2
4649 buffer_start = 0x001E;
4650 buffer_end = 0x003E;
4651 #else
4652 buffer_start = read_word(0x0040, 0x0080);
4653 buffer_end = read_word(0x0040, 0x0082);
4654 #endif
4656 buffer_head = read_word(0x0040, 0x001A);
4657 buffer_tail = read_word(0x0040, 0x001C);
4659 temp_tail = buffer_tail;
4660 buffer_tail += 2;
4661 if (buffer_tail >= buffer_end)
4662 buffer_tail = buffer_start;
4664 if (buffer_tail == buffer_head) {
4665 return(0);
4668 write_byte(0x0040, temp_tail, ascii_code);
4669 write_byte(0x0040, temp_tail+1, scan_code);
4670 write_word(0x0040, 0x001C, buffer_tail);
4671 return(1);
4675 void
4676 int74_function(make_farcall, Z, Y, X, status)
4677 Bit16u make_farcall, Z, Y, X, status;
4679 Bit16u ebda_seg=read_word(0x0040,0x000E);
4680 Bit8u in_byte, index, package_count;
4681 Bit8u mouse_flags_1, mouse_flags_2;
4683 BX_DEBUG_INT74("entering int74_function\n");
4684 make_farcall = 0;
4686 in_byte = inb(0x64);
4687 if ( (in_byte & 0x21) != 0x21 ) {
4688 return;
4690 in_byte = inb(0x60);
4691 BX_DEBUG_INT74("int74: read byte %02x\n", in_byte);
4693 mouse_flags_1 = read_byte(ebda_seg, 0x0026);
4694 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4696 if ( (mouse_flags_2 & 0x80) != 0x80 ) {
4697 // BX_PANIC("int74_function:\n");
4698 return;
4701 package_count = mouse_flags_2 & 0x07;
4702 index = mouse_flags_1 & 0x07;
4703 write_byte(ebda_seg, 0x28 + index, in_byte);
4705 if ( (index+1) >= package_count ) {
4706 BX_DEBUG_INT74("int74_function: make_farcall=1\n");
4707 status = read_byte(ebda_seg, 0x0028 + 0);
4708 X = read_byte(ebda_seg, 0x0028 + 1);
4709 Y = read_byte(ebda_seg, 0x0028 + 2);
4710 Z = 0;
4711 mouse_flags_1 = 0;
4712 // check if far call handler installed
4713 if (mouse_flags_2 & 0x80)
4714 make_farcall = 1;
4716 else {
4717 mouse_flags_1++;
4719 write_byte(ebda_seg, 0x0026, mouse_flags_1);
4722 #define SET_DISK_RET_STATUS(status) write_byte(0x0040, 0x0074, status)
4724 #if BX_USE_ATADRV
4726 void
4727 int13_harddisk(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
4728 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
4730 Bit32u lba;
4731 Bit16u ebda_seg=read_word(0x0040,0x000E);
4732 Bit16u cylinder, head, sector;
4733 Bit16u segment, offset;
4734 Bit16u npc, nph, npspt, nlc, nlh, nlspt;
4735 Bit16u size, count;
4736 Bit8u device, status;
4738 BX_DEBUG_INT13_HD("int13_harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
4740 write_byte(0x0040, 0x008e, 0); // clear completion flag
4742 // basic check : device has to be defined
4743 if ( (GET_ELDL() < 0x80) || (GET_ELDL() >= 0x80 + BX_MAX_ATA_DEVICES) ) {
4744 BX_INFO("int13_harddisk: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
4745 goto int13_fail;
4748 // Get the ata channel
4749 device=read_byte(ebda_seg,&EbdaData->ata.hdidmap[GET_ELDL()-0x80]);
4751 // basic check : device has to be valid
4752 if (device >= BX_MAX_ATA_DEVICES) {
4753 BX_INFO("int13_harddisk: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
4754 goto int13_fail;
4757 switch (GET_AH()) {
4759 case 0x00: /* disk controller reset */
4760 ata_reset (device);
4761 goto int13_success;
4762 break;
4764 case 0x01: /* read disk status */
4765 status = read_byte(0x0040, 0x0074);
4766 SET_AH(status);
4767 SET_DISK_RET_STATUS(0);
4768 /* set CF if error status read */
4769 if (status) goto int13_fail_nostatus;
4770 else goto int13_success_noah;
4771 break;
4773 case 0x02: // read disk sectors
4774 case 0x03: // write disk sectors
4775 case 0x04: // verify disk sectors
4777 count = GET_AL();
4778 cylinder = GET_CH();
4779 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
4780 sector = (GET_CL() & 0x3f);
4781 head = GET_DH();
4783 segment = ES;
4784 offset = BX;
4786 if ( (count > 128) || (count == 0) ) {
4787 BX_INFO("int13_harddisk: function %02x, count out of range!\n",GET_AH());
4788 goto int13_fail;
4791 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
4792 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
4793 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
4795 // sanity check on cyl heads, sec
4796 if( (cylinder >= nlc) || (head >= nlh) || (sector > nlspt )) {
4797 BX_INFO("int13_harddisk: function %02x, parameters out of range %04x/%04x/%04x!\n", GET_AH(), cylinder, head, sector);
4798 goto int13_fail;
4801 // FIXME verify
4802 if ( GET_AH() == 0x04 ) goto int13_success;
4804 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
4805 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
4807 // if needed, translate lchs to lba, and execute command
4808 if ( (nph != nlh) || (npspt != nlspt)) {
4809 lba = ((((Bit32u)cylinder * (Bit32u)nlh) + (Bit32u)head) * (Bit32u)nlspt) + (Bit32u)sector - 1;
4810 sector = 0; // this forces the command to be lba
4813 if ( GET_AH() == 0x02 )
4814 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, cylinder, head, sector, lba, segment, offset);
4815 else
4816 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, cylinder, head, sector, lba, segment, offset);
4818 // Set nb of sector transferred
4819 SET_AL(read_word(ebda_seg, &EbdaData->ata.trsfsectors));
4821 if (status != 0) {
4822 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
4823 SET_AH(0x0c);
4824 goto int13_fail_noah;
4827 goto int13_success;
4828 break;
4830 case 0x05: /* format disk track */
4831 BX_INFO("format disk track called\n");
4832 goto int13_success;
4833 return;
4834 break;
4836 case 0x08: /* read disk drive parameters */
4838 // Get logical geometry from table
4839 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
4840 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
4841 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
4842 count = read_byte(ebda_seg, &EbdaData->ata.hdcount);
4844 nlc = nlc - 2; /* 0 based , last sector not used */
4845 SET_AL(0);
4846 SET_CH(nlc & 0xff);
4847 SET_CL(((nlc >> 2) & 0xc0) | (nlspt & 0x3f));
4848 SET_DH(nlh - 1);
4849 SET_DL(count); /* FIXME returns 0, 1, or n hard drives */
4851 // FIXME should set ES & DI
4853 goto int13_success;
4854 break;
4856 case 0x10: /* check drive ready */
4857 // should look at 40:8E also???
4859 // Read the status from controller
4860 status = inb(read_word(ebda_seg, &EbdaData->ata.channels[device/2].iobase1) + ATA_CB_STAT);
4861 if ( (status & ( ATA_CB_STAT_BSY | ATA_CB_STAT_RDY )) == ATA_CB_STAT_RDY ) {
4862 goto int13_success;
4864 else {
4865 SET_AH(0xAA);
4866 goto int13_fail_noah;
4868 break;
4870 case 0x15: /* read disk drive size */
4872 // Get physical geometry from table
4873 npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
4874 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
4875 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
4877 // Compute sector count seen by int13
4878 lba = (Bit32u)(npc - 1) * (Bit32u)nph * (Bit32u)npspt;
4879 CX = lba >> 16;
4880 DX = lba & 0xffff;
4882 SET_AH(3); // hard disk accessible
4883 goto int13_success_noah;
4884 break;
4886 case 0x41: // IBM/MS installation check
4887 BX=0xaa55; // install check
4888 SET_AH(0x30); // EDD 3.0
4889 CX=0x0007; // ext disk access and edd, removable supported
4890 goto int13_success_noah;
4891 break;
4893 case 0x42: // IBM/MS extended read
4894 case 0x43: // IBM/MS extended write
4895 case 0x44: // IBM/MS verify
4896 case 0x47: // IBM/MS extended seek
4898 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
4899 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
4900 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
4902 // Can't use 64 bits lba
4903 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
4904 if (lba != 0L) {
4905 BX_PANIC("int13_harddisk: function %02x. Can't use 64bits lba\n",GET_AH());
4906 goto int13_fail;
4909 // Get 32 bits lba and check
4910 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
4911 if (lba >= read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors) ) {
4912 BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH());
4913 goto int13_fail;
4916 // If verify or seek
4917 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
4918 goto int13_success;
4920 // Execute the command
4921 if ( GET_AH() == 0x42 )
4922 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, 0, 0, 0, lba, segment, offset);
4923 else
4924 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, 0, 0, 0, lba, segment, offset);
4926 count=read_word(ebda_seg, &EbdaData->ata.trsfsectors);
4927 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
4929 if (status != 0) {
4930 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
4931 SET_AH(0x0c);
4932 goto int13_fail_noah;
4935 goto int13_success;
4936 break;
4938 case 0x45: // IBM/MS lock/unlock drive
4939 case 0x49: // IBM/MS extended media change
4940 goto int13_success; // Always success for HD
4941 break;
4943 case 0x46: // IBM/MS eject media
4944 SET_AH(0xb2); // Volume Not Removable
4945 goto int13_fail_noah; // Always fail for HD
4946 break;
4948 case 0x48: // IBM/MS get drive parameters
4949 size=read_word(DS,SI+(Bit16u)&Int13DPT->size);
4951 // Buffer is too small
4952 if(size < 0x1a)
4953 goto int13_fail;
4955 // EDD 1.x
4956 if(size >= 0x1a) {
4957 Bit16u blksize;
4959 npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
4960 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
4961 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
4962 lba = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors);
4963 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
4965 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
4966 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x02); // geometry is valid
4967 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, (Bit32u)npc);
4968 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, (Bit32u)nph);
4969 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, (Bit32u)npspt);
4970 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, lba); // FIXME should be Bit64
4971 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0L);
4972 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
4975 // EDD 2.x
4976 if(size >= 0x1e) {
4977 Bit8u channel, dev, irq, mode, checksum, i, translation;
4978 Bit16u iobase1, iobase2, options;
4980 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
4982 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
4983 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
4985 // Fill in dpte
4986 channel = device / 2;
4987 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
4988 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
4989 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
4990 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
4991 translation = read_byte(ebda_seg, &EbdaData->ata.devices[device].translation);
4993 options = (translation==ATA_TRANSLATION_NONE?0:1<<3); // chs translation
4994 options |= (1<<4); // lba translation
4995 options |= (mode==ATA_MODE_PIO32?1:0<<7);
4996 options |= (translation==ATA_TRANSLATION_LBA?1:0<<9);
4997 options |= (translation==ATA_TRANSLATION_RECHS?3:0<<9);
4999 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5000 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2);
5001 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5002 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5003 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5004 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5005 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5006 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5007 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5008 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5009 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5011 checksum=0;
5012 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, (&EbdaData->ata.dpte) + i);
5013 checksum = ~checksum;
5014 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5017 // EDD 3.x
5018 if(size >= 0x42) {
5019 Bit8u channel, iface, checksum, i;
5020 Bit16u iobase1;
5022 channel = device / 2;
5023 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5024 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5026 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5027 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5028 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5029 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5030 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5032 if (iface==ATA_IFACE_ISA) {
5033 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5034 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5035 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5036 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5038 else {
5039 // FIXME PCI
5041 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5042 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5043 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5044 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5046 if (iface==ATA_IFACE_ISA) {
5047 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5048 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5049 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5051 else {
5052 // FIXME PCI
5054 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5055 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5056 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5057 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5059 checksum=0;
5060 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5061 checksum = ~checksum;
5062 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5065 goto int13_success;
5066 break;
5068 case 0x4e: // // IBM/MS set hardware configuration
5069 // DMA, prefetch, PIO maximum not supported
5070 switch (GET_AL()) {
5071 case 0x01:
5072 case 0x03:
5073 case 0x04:
5074 case 0x06:
5075 goto int13_success;
5076 break;
5077 default :
5078 goto int13_fail;
5080 break;
5082 case 0x09: /* initialize drive parameters */
5083 case 0x0c: /* seek to specified cylinder */
5084 case 0x0d: /* alternate disk reset */
5085 case 0x11: /* recalibrate */
5086 case 0x14: /* controller internal diagnostic */
5087 BX_INFO("int13h_harddisk function %02xh unimplemented, returns success\n", GET_AH());
5088 goto int13_success;
5089 break;
5091 case 0x0a: /* read disk sectors with ECC */
5092 case 0x0b: /* write disk sectors with ECC */
5093 case 0x18: // set media type for format
5094 case 0x50: // IBM/MS send packet command
5095 default:
5096 BX_INFO("int13_harddisk function %02xh unsupported, returns fail\n", GET_AH());
5097 goto int13_fail;
5098 break;
5101 int13_fail:
5102 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5103 int13_fail_noah:
5104 SET_DISK_RET_STATUS(GET_AH());
5105 int13_fail_nostatus:
5106 SET_CF(); // error occurred
5107 return;
5109 int13_success:
5110 SET_AH(0x00); // no error
5111 int13_success_noah:
5112 SET_DISK_RET_STATUS(0x00);
5113 CLEAR_CF(); // no error
5114 return;
5117 // ---------------------------------------------------------------------------
5118 // Start of int13 for cdrom
5119 // ---------------------------------------------------------------------------
5121 void
5122 int13_cdrom(EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
5123 Bit16u EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
5125 Bit16u ebda_seg=read_word(0x0040,0x000E);
5126 Bit8u device, status, locks;
5127 Bit8u atacmd[12];
5128 Bit32u lba;
5129 Bit16u count, segment, offset, i, size;
5131 BX_DEBUG_INT13_CD("int13_cdrom: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5132 // BX_DEBUG_INT13_CD("int13_cdrom: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
5134 SET_DISK_RET_STATUS(0x00);
5136 /* basic check : device should be 0xE0+ */
5137 if( (GET_ELDL() < 0xE0) || (GET_ELDL() >= 0xE0+BX_MAX_ATA_DEVICES) ) {
5138 BX_INFO("int13_cdrom: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
5139 goto int13_fail;
5142 // Get the ata channel
5143 device=read_byte(ebda_seg,&EbdaData->ata.cdidmap[GET_ELDL()-0xE0]);
5145 /* basic check : device has to be valid */
5146 if (device >= BX_MAX_ATA_DEVICES) {
5147 BX_INFO("int13_cdrom: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
5148 goto int13_fail;
5151 switch (GET_AH()) {
5153 // all those functions return SUCCESS
5154 case 0x00: /* disk controller reset */
5155 case 0x09: /* initialize drive parameters */
5156 case 0x0c: /* seek to specified cylinder */
5157 case 0x0d: /* alternate disk reset */
5158 case 0x10: /* check drive ready */
5159 case 0x11: /* recalibrate */
5160 case 0x14: /* controller internal diagnostic */
5161 case 0x16: /* detect disk change */
5162 goto int13_success;
5163 break;
5165 // all those functions return disk write-protected
5166 case 0x03: /* write disk sectors */
5167 case 0x05: /* format disk track */
5168 case 0x43: // IBM/MS extended write
5169 SET_AH(0x03);
5170 goto int13_fail_noah;
5171 break;
5173 case 0x01: /* read disk status */
5174 status = read_byte(0x0040, 0x0074);
5175 SET_AH(status);
5176 SET_DISK_RET_STATUS(0);
5178 /* set CF if error status read */
5179 if (status) goto int13_fail_nostatus;
5180 else goto int13_success_noah;
5181 break;
5183 case 0x15: /* read disk drive size */
5184 SET_AH(0x02);
5185 goto int13_fail_noah;
5186 break;
5188 case 0x41: // IBM/MS installation check
5189 BX=0xaa55; // install check
5190 SET_AH(0x30); // EDD 2.1
5191 CX=0x0007; // ext disk access, removable and edd
5192 goto int13_success_noah;
5193 break;
5195 case 0x42: // IBM/MS extended read
5196 case 0x44: // IBM/MS verify sectors
5197 case 0x47: // IBM/MS extended seek
5199 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
5200 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
5201 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
5203 // Can't use 64 bits lba
5204 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
5205 if (lba != 0L) {
5206 BX_PANIC("int13_cdrom: function %02x. Can't use 64bits lba\n",GET_AH());
5207 goto int13_fail;
5210 // Get 32 bits lba
5211 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5213 // If verify or seek
5214 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
5215 goto int13_success;
5217 memsetb(get_SS(),atacmd,0,12);
5218 atacmd[0]=0x28; // READ command
5219 atacmd[7]=(count & 0xff00) >> 8; // Sectors
5220 atacmd[8]=(count & 0x00ff); // Sectors
5221 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
5222 atacmd[3]=(lba & 0x00ff0000) >> 16;
5223 atacmd[4]=(lba & 0x0000ff00) >> 8;
5224 atacmd[5]=(lba & 0x000000ff);
5225 status = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, count*2048L, ATA_DATA_IN, segment,offset);
5227 count = (Bit16u)(read_dword(ebda_seg, &EbdaData->ata.trsfbytes) >> 11);
5228 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5230 if (status != 0) {
5231 BX_INFO("int13_cdrom: function %02x, status %02x !\n",GET_AH(),status);
5232 SET_AH(0x0c);
5233 goto int13_fail_noah;
5236 goto int13_success;
5237 break;
5239 case 0x45: // IBM/MS lock/unlock drive
5240 if (GET_AL() > 2) goto int13_fail;
5242 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5244 switch (GET_AL()) {
5245 case 0 : // lock
5246 if (locks == 0xff) {
5247 SET_AH(0xb4);
5248 SET_AL(1);
5249 goto int13_fail_noah;
5251 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, ++locks);
5252 SET_AL(1);
5253 break;
5254 case 1 : // unlock
5255 if (locks == 0x00) {
5256 SET_AH(0xb0);
5257 SET_AL(0);
5258 goto int13_fail_noah;
5260 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, --locks);
5261 SET_AL(locks==0?0:1);
5262 break;
5263 case 2 : // status
5264 SET_AL(locks==0?0:1);
5265 break;
5267 goto int13_success;
5268 break;
5270 case 0x46: // IBM/MS eject media
5271 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5273 if (locks != 0) {
5274 SET_AH(0xb1); // media locked
5275 goto int13_fail_noah;
5277 // FIXME should handle 0x31 no media in device
5278 // FIXME should handle 0xb5 valid request failed
5280 // Call removable media eject
5281 ASM_START
5282 push bp
5283 mov bp, sp
5285 mov ah, #0x52
5286 int 15
5287 mov _int13_cdrom.status + 2[bp], ah
5288 jnc int13_cdrom_rme_end
5289 mov _int13_cdrom.status, #1
5290 int13_cdrom_rme_end:
5291 pop bp
5292 ASM_END
5294 if (status != 0) {
5295 SET_AH(0xb1); // media locked
5296 goto int13_fail_noah;
5299 goto int13_success;
5300 break;
5302 case 0x48: // IBM/MS get drive parameters
5303 size = read_word(DS,SI+(Bit16u)&Int13Ext->size);
5305 // Buffer is too small
5306 if(size < 0x1a)
5307 goto int13_fail;
5309 // EDD 1.x
5310 if(size >= 0x1a) {
5311 Bit16u cylinders, heads, spt, blksize;
5313 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5315 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5316 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x74); // removable, media change, lockable, max values
5317 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0xffffffff);
5318 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, 0xffffffff);
5319 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, 0xffffffff);
5320 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, 0xffffffff); // FIXME should be Bit64
5321 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0xffffffff);
5322 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5325 // EDD 2.x
5326 if(size >= 0x1e) {
5327 Bit8u channel, dev, irq, mode, checksum, i;
5328 Bit16u iobase1, iobase2, options;
5330 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5332 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5333 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5335 // Fill in dpte
5336 channel = device / 2;
5337 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5338 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5339 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5340 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5342 // FIXME atapi device
5343 options = (1<<4); // lba translation
5344 options |= (1<<5); // removable device
5345 options |= (1<<6); // atapi device
5346 options |= (mode==ATA_MODE_PIO32?1:0<<7);
5348 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5349 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2);
5350 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5351 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5352 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5353 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5354 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5355 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5356 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5357 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5358 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5360 checksum=0;
5361 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, (&EbdaData->ata.dpte) + i);
5362 checksum = ~checksum;
5363 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5366 // EDD 3.x
5367 if(size >= 0x42) {
5368 Bit8u channel, iface, checksum, i;
5369 Bit16u iobase1;
5371 channel = device / 2;
5372 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5373 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5375 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5376 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5377 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5378 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5379 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5381 if (iface==ATA_IFACE_ISA) {
5382 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5383 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5384 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5385 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5387 else {
5388 // FIXME PCI
5390 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5391 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5392 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5393 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5395 if (iface==ATA_IFACE_ISA) {
5396 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5397 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5398 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5400 else {
5401 // FIXME PCI
5403 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5404 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5405 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5406 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5408 checksum=0;
5409 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5410 checksum = ~checksum;
5411 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5414 goto int13_success;
5415 break;
5417 case 0x49: // IBM/MS extended media change
5418 // always send changed ??
5419 SET_AH(06);
5420 goto int13_fail_nostatus;
5421 break;
5423 case 0x4e: // // IBM/MS set hardware configuration
5424 // DMA, prefetch, PIO maximum not supported
5425 switch (GET_AL()) {
5426 case 0x01:
5427 case 0x03:
5428 case 0x04:
5429 case 0x06:
5430 goto int13_success;
5431 break;
5432 default :
5433 goto int13_fail;
5435 break;
5437 // all those functions return unimplemented
5438 case 0x02: /* read sectors */
5439 case 0x04: /* verify sectors */
5440 case 0x08: /* read disk drive parameters */
5441 case 0x0a: /* read disk sectors with ECC */
5442 case 0x0b: /* write disk sectors with ECC */
5443 case 0x18: /* set media type for format */
5444 case 0x50: // ? - send packet command
5445 default:
5446 BX_INFO("int13_cdrom: unsupported AH=%02x\n", GET_AH());
5447 goto int13_fail;
5448 break;
5451 int13_fail:
5452 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5453 int13_fail_noah:
5454 SET_DISK_RET_STATUS(GET_AH());
5455 int13_fail_nostatus:
5456 SET_CF(); // error occurred
5457 return;
5459 int13_success:
5460 SET_AH(0x00); // no error
5461 int13_success_noah:
5462 SET_DISK_RET_STATUS(0x00);
5463 CLEAR_CF(); // no error
5464 return;
5467 // ---------------------------------------------------------------------------
5468 // End of int13 for cdrom
5469 // ---------------------------------------------------------------------------
5471 #if BX_ELTORITO_BOOT
5472 // ---------------------------------------------------------------------------
5473 // Start of int13 for eltorito functions
5474 // ---------------------------------------------------------------------------
5476 void
5477 int13_eltorito(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
5478 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
5480 Bit16u ebda_seg=read_word(0x0040,0x000E);
5482 BX_DEBUG_INT13_ET("int13_eltorito: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5483 // BX_DEBUG_INT13_ET("int13_eltorito: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
5485 switch (GET_AH()) {
5487 // FIXME ElTorito Various. Should be implemented
5488 case 0x4a: // ElTorito - Initiate disk emu
5489 case 0x4c: // ElTorito - Initiate disk emu and boot
5490 case 0x4d: // ElTorito - Return Boot catalog
5491 BX_PANIC("Int13 eltorito call with AX=%04x. Please report\n",AX);
5492 goto int13_fail;
5493 break;
5495 case 0x4b: // ElTorito - Terminate disk emu
5496 // FIXME ElTorito Hardcoded
5497 write_byte(DS,SI+0x00,0x13);
5498 write_byte(DS,SI+0x01,read_byte(ebda_seg,&EbdaData->cdemu.media));
5499 write_byte(DS,SI+0x02,read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
5500 write_byte(DS,SI+0x03,read_byte(ebda_seg,&EbdaData->cdemu.controller_index));
5501 write_dword(DS,SI+0x04,read_dword(ebda_seg,&EbdaData->cdemu.ilba));
5502 write_word(DS,SI+0x08,read_word(ebda_seg,&EbdaData->cdemu.device_spec));
5503 write_word(DS,SI+0x0a,read_word(ebda_seg,&EbdaData->cdemu.buffer_segment));
5504 write_word(DS,SI+0x0c,read_word(ebda_seg,&EbdaData->cdemu.load_segment));
5505 write_word(DS,SI+0x0e,read_word(ebda_seg,&EbdaData->cdemu.sector_count));
5506 write_byte(DS,SI+0x10,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.cylinders));
5507 write_byte(DS,SI+0x11,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.spt));
5508 write_byte(DS,SI+0x12,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.heads));
5510 // If we have to terminate emulation
5511 if(GET_AL() == 0x00) {
5512 // FIXME ElTorito Various. Should be handled accordingly to spec
5513 write_byte(ebda_seg,&EbdaData->cdemu.active, 0x00); // bye bye
5516 goto int13_success;
5517 break;
5519 default:
5520 BX_INFO("int13_eltorito: unsupported AH=%02x\n", GET_AH());
5521 goto int13_fail;
5522 break;
5525 int13_fail:
5526 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5527 SET_DISK_RET_STATUS(GET_AH());
5528 SET_CF(); // error occurred
5529 return;
5531 int13_success:
5532 SET_AH(0x00); // no error
5533 SET_DISK_RET_STATUS(0x00);
5534 CLEAR_CF(); // no error
5535 return;
5538 // ---------------------------------------------------------------------------
5539 // End of int13 for eltorito functions
5540 // ---------------------------------------------------------------------------
5542 // ---------------------------------------------------------------------------
5543 // Start of int13 when emulating a device from the cd
5544 // ---------------------------------------------------------------------------
5546 void
5547 int13_cdemu(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
5548 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
5550 Bit16u ebda_seg=read_word(0x0040,0x000E);
5551 Bit8u device, status;
5552 Bit16u vheads, vspt, vcylinders;
5553 Bit16u head, sector, cylinder, nbsectors;
5554 Bit32u vlba, ilba, slba, elba;
5555 Bit16u before, segment, offset;
5556 Bit8u atacmd[12];
5558 BX_DEBUG_INT13_ET("int13_cdemu: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5559 //BX_DEBUG_INT13_ET("int13_cdemu: SS=%04x ES=%04x DI=%04x SI=%04x\n", get_SS(), ES, DI, SI);
5561 /* at this point, we are emulating a floppy/harddisk */
5563 // Recompute the device number
5564 device = read_byte(ebda_seg,&EbdaData->cdemu.controller_index) * 2;
5565 device += read_byte(ebda_seg,&EbdaData->cdemu.device_spec);
5567 SET_DISK_RET_STATUS(0x00);
5569 /* basic checks : emulation should be active, dl should equal the emulated drive */
5570 if( (read_byte(ebda_seg,&EbdaData->cdemu.active) ==0 )
5571 || (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive ) != GET_DL())) {
5572 BX_INFO("int13_cdemu: function %02x, emulation not active for DL= %02x\n", GET_AH(), GET_DL());
5573 goto int13_fail;
5576 switch (GET_AH()) {
5578 // all those functions return SUCCESS
5579 case 0x00: /* disk controller reset */
5580 case 0x09: /* initialize drive parameters */
5581 case 0x0c: /* seek to specified cylinder */
5582 case 0x0d: /* alternate disk reset */ // FIXME ElTorito Various. should really reset ?
5583 case 0x10: /* check drive ready */ // FIXME ElTorito Various. should check if ready ?
5584 case 0x11: /* recalibrate */
5585 case 0x14: /* controller internal diagnostic */
5586 case 0x16: /* detect disk change */
5587 goto int13_success;
5588 break;
5590 // all those functions return disk write-protected
5591 case 0x03: /* write disk sectors */
5592 case 0x05: /* format disk track */
5593 SET_AH(0x03);
5594 goto int13_fail_noah;
5595 break;
5597 case 0x01: /* read disk status */
5598 status=read_byte(0x0040, 0x0074);
5599 SET_AH(status);
5600 SET_DISK_RET_STATUS(0);
5602 /* set CF if error status read */
5603 if (status) goto int13_fail_nostatus;
5604 else goto int13_success_noah;
5605 break;
5607 case 0x02: // read disk sectors
5608 case 0x04: // verify disk sectors
5609 vspt = read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
5610 vcylinders = read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders);
5611 vheads = read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads);
5613 ilba = read_dword(ebda_seg,&EbdaData->cdemu.ilba);
5615 sector = GET_CL() & 0x003f;
5616 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
5617 head = GET_DH();
5618 nbsectors = GET_AL();
5619 segment = ES;
5620 offset = BX;
5622 // no sector to read ?
5623 if(nbsectors==0) goto int13_success;
5625 // sanity checks sco openserver needs this!
5626 if ((sector > vspt)
5627 || (cylinder >= vcylinders)
5628 || (head >= vheads)) {
5629 goto int13_fail;
5632 // After controls, verify do nothing
5633 if (GET_AH() == 0x04) goto int13_success;
5635 segment = ES+(BX / 16);
5636 offset = BX % 16;
5638 // calculate the virtual lba inside the image
5639 vlba=((((Bit32u)cylinder*(Bit32u)vheads)+(Bit32u)head)*(Bit32u)vspt)+((Bit32u)(sector-1));
5641 // In advance so we don't loose the count
5642 SET_AL(nbsectors);
5644 // start lba on cd
5645 slba = (Bit32u)vlba/4;
5646 before= (Bit16u)vlba%4;
5648 // end lba on cd
5649 elba = (Bit32u)(vlba+nbsectors-1)/4;
5651 memsetb(get_SS(),atacmd,0,12);
5652 atacmd[0]=0x28; // READ command
5653 atacmd[7]=((Bit16u)(elba-slba+1) & 0xff00) >> 8; // Sectors
5654 atacmd[8]=((Bit16u)(elba-slba+1) & 0x00ff); // Sectors
5655 atacmd[2]=(ilba+slba & 0xff000000) >> 24; // LBA
5656 atacmd[3]=(ilba+slba & 0x00ff0000) >> 16;
5657 atacmd[4]=(ilba+slba & 0x0000ff00) >> 8;
5658 atacmd[5]=(ilba+slba & 0x000000ff);
5659 if((status = ata_cmd_packet(device, 12, get_SS(), atacmd, before*512, nbsectors*512L, ATA_DATA_IN, segment,offset)) != 0) {
5660 BX_INFO("int13_cdemu: function %02x, error %02x !\n",GET_AH(),status);
5661 SET_AH(0x02);
5662 SET_AL(0);
5663 goto int13_fail_noah;
5666 goto int13_success;
5667 break;
5669 case 0x08: /* read disk drive parameters */
5670 vspt=read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
5671 vcylinders=read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders) - 1;
5672 vheads=read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads) - 1;
5674 SET_AL( 0x00 );
5675 SET_BL( 0x00 );
5676 SET_CH( vcylinders & 0xff );
5677 SET_CL((( vcylinders >> 2) & 0xc0) | ( vspt & 0x3f ));
5678 SET_DH( vheads );
5679 SET_DL( 0x02 ); // FIXME ElTorito Various. should send the real count of drives 1 or 2
5680 // FIXME ElTorito Harddisk. should send the HD count
5682 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
5683 case 0x01: SET_BL( 0x02 ); break;
5684 case 0x02: SET_BL( 0x04 ); break;
5685 case 0x03: SET_BL( 0x06 ); break;
5688 ASM_START
5689 push bp
5690 mov bp, sp
5691 mov ax, #diskette_param_table2
5692 mov _int13_cdemu.DI+2[bp], ax
5693 mov _int13_cdemu.ES+2[bp], cs
5694 pop bp
5695 ASM_END
5696 goto int13_success;
5697 break;
5699 case 0x15: /* read disk drive size */
5700 // FIXME ElTorito Harddisk. What geometry to send ?
5701 SET_AH(0x03);
5702 goto int13_success_noah;
5703 break;
5705 // all those functions return unimplemented
5706 case 0x0a: /* read disk sectors with ECC */
5707 case 0x0b: /* write disk sectors with ECC */
5708 case 0x18: /* set media type for format */
5709 case 0x41: // IBM/MS installation check
5710 // FIXME ElTorito Harddisk. Darwin would like to use EDD
5711 case 0x42: // IBM/MS extended read
5712 case 0x43: // IBM/MS extended write
5713 case 0x44: // IBM/MS verify sectors
5714 case 0x45: // IBM/MS lock/unlock drive
5715 case 0x46: // IBM/MS eject media
5716 case 0x47: // IBM/MS extended seek
5717 case 0x48: // IBM/MS get drive parameters
5718 case 0x49: // IBM/MS extended media change
5719 case 0x4e: // ? - set hardware configuration
5720 case 0x50: // ? - send packet command
5721 default:
5722 BX_INFO("int13_cdemu function AH=%02x unsupported, returns fail\n", GET_AH());
5723 goto int13_fail;
5724 break;
5727 int13_fail:
5728 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5729 int13_fail_noah:
5730 SET_DISK_RET_STATUS(GET_AH());
5731 int13_fail_nostatus:
5732 SET_CF(); // error occurred
5733 return;
5735 int13_success:
5736 SET_AH(0x00); // no error
5737 int13_success_noah:
5738 SET_DISK_RET_STATUS(0x00);
5739 CLEAR_CF(); // no error
5740 return;
5743 // ---------------------------------------------------------------------------
5744 // End of int13 when emulating a device from the cd
5745 // ---------------------------------------------------------------------------
5747 #endif // BX_ELTORITO_BOOT
5749 #else //BX_USE_ATADRV
5751 void
5752 outLBA(cylinder,hd_heads,head,hd_sectors,sector,dl)
5753 Bit16u cylinder;
5754 Bit16u hd_heads;
5755 Bit16u head;
5756 Bit16u hd_sectors;
5757 Bit16u sector;
5758 Bit16u dl;
5760 ASM_START
5761 push bp
5762 mov bp, sp
5763 push eax
5764 push ebx
5765 push edx
5766 xor eax,eax
5767 mov ax,4[bp] // cylinder
5768 xor ebx,ebx
5769 mov bl,6[bp] // hd_heads
5770 imul ebx
5772 mov bl,8[bp] // head
5773 add eax,ebx
5774 mov bl,10[bp] // hd_sectors
5775 imul ebx
5776 mov bl,12[bp] // sector
5777 add eax,ebx
5779 dec eax
5780 mov dx,#0x1f3
5781 out dx,al
5782 mov dx,#0x1f4
5783 mov al,ah
5784 out dx,al
5785 shr eax,#16
5786 mov dx,#0x1f5
5787 out dx,al
5788 and ah,#0xf
5789 mov bl,14[bp] // dl
5790 and bl,#1
5791 shl bl,#4
5792 or ah,bl
5793 or ah,#0xe0
5794 mov al,ah
5795 mov dx,#0x01f6
5796 out dx,al
5797 pop edx
5798 pop ebx
5799 pop eax
5800 pop bp
5801 ASM_END
5804 void
5805 int13_harddisk(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
5806 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
5808 Bit8u drive, num_sectors, sector, head, status, mod;
5809 Bit8u drive_map;
5810 Bit8u n_drives;
5811 Bit16u cyl_mod, ax;
5812 Bit16u max_cylinder, cylinder, total_sectors;
5813 Bit16u hd_cylinders;
5814 Bit8u hd_heads, hd_sectors;
5815 Bit16u val16;
5816 Bit8u sector_count;
5817 unsigned int i;
5818 Bit16u tempbx;
5819 Bit16u dpsize;
5821 Bit16u count, segment, offset;
5822 Bit32u lba;
5823 Bit16u error;
5825 BX_DEBUG_INT13_HD("int13 harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5827 write_byte(0x0040, 0x008e, 0); // clear completion flag
5829 /* at this point, DL is >= 0x80 to be passed from the floppy int13h
5830 handler code */
5831 /* check how many disks first (cmos reg 0x12), return an error if
5832 drive not present */
5833 drive_map = inb_cmos(0x12);
5834 drive_map = (((drive_map & 0xf0)==0) ? 0 : 1) |
5835 (((drive_map & 0x0f)==0) ? 0 : 2);
5836 n_drives = (drive_map==0) ? 0 :
5837 ((drive_map==3) ? 2 : 1);
5839 if (!(drive_map & (1<<(GET_ELDL()&0x7f)))) { /* allow 0, 1, or 2 disks */
5840 SET_AH(0x01);
5841 SET_DISK_RET_STATUS(0x01);
5842 SET_CF(); /* error occurred */
5843 return;
5846 switch (GET_AH()) {
5848 case 0x00: /* disk controller reset */
5849 BX_DEBUG_INT13_HD("int13_f00\n");
5851 SET_AH(0);
5852 SET_DISK_RET_STATUS(0);
5853 set_diskette_ret_status(0);
5854 set_diskette_current_cyl(0, 0); /* current cylinder, diskette 1 */
5855 set_diskette_current_cyl(1, 0); /* current cylinder, diskette 2 */
5856 CLEAR_CF(); /* successful */
5857 return;
5858 break;
5860 case 0x01: /* read disk status */
5861 BX_DEBUG_INT13_HD("int13_f01\n");
5862 status = read_byte(0x0040, 0x0074);
5863 SET_AH(status);
5864 SET_DISK_RET_STATUS(0);
5865 /* set CF if error status read */
5866 if (status) SET_CF();
5867 else CLEAR_CF();
5868 return;
5869 break;
5871 case 0x04: // verify disk sectors
5872 case 0x02: // read disk sectors
5873 drive = GET_ELDL();
5874 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
5876 num_sectors = GET_AL();
5877 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
5878 sector = (GET_CL() & 0x3f);
5879 head = GET_DH();
5882 if (hd_cylinders > 1024) {
5883 if (hd_cylinders <= 2048) {
5884 cylinder <<= 1;
5886 else if (hd_cylinders <= 4096) {
5887 cylinder <<= 2;
5889 else if (hd_cylinders <= 8192) {
5890 cylinder <<= 3;
5892 else { // hd_cylinders <= 16384
5893 cylinder <<= 4;
5896 ax = head / hd_heads;
5897 cyl_mod = ax & 0xff;
5898 head = ax >> 8;
5899 cylinder |= cyl_mod;
5902 if ( (cylinder >= hd_cylinders) ||
5903 (sector > hd_sectors) ||
5904 (head >= hd_heads) ) {
5905 SET_AH(1);
5906 SET_DISK_RET_STATUS(1);
5907 SET_CF(); /* error occurred */
5908 return;
5911 if ( (num_sectors > 128) || (num_sectors == 0) )
5912 BX_PANIC("int13_harddisk(): num_sectors out of range!\n");
5914 if (head > 15)
5915 BX_PANIC("hard drive BIOS:(read/verify) head > 15\n");
5917 if ( GET_AH() == 0x04 ) {
5918 SET_AH(0);
5919 SET_DISK_RET_STATUS(0);
5920 CLEAR_CF();
5921 return;
5924 status = inb(0x1f7);
5925 if (status & 0x80) {
5926 BX_PANIC("hard drive BIOS:(read/verify) BUSY bit set\n");
5928 outb(0x01f2, num_sectors);
5929 /* activate LBA? (tomv) */
5930 if (hd_heads > 16) {
5931 BX_DEBUG_INT13_HD("CHS: %x %x %x\n", cylinder, head, sector);
5932 outLBA(cylinder,hd_heads,head,hd_sectors,sector,drive);
5934 else {
5935 outb(0x01f3, sector);
5936 outb(0x01f4, cylinder & 0x00ff);
5937 outb(0x01f5, cylinder >> 8);
5938 outb(0x01f6, 0xa0 | ((drive & 0x01)<<4) | (head & 0x0f));
5940 outb(0x01f7, 0x20);
5942 while (1) {
5943 status = inb(0x1f7);
5944 if ( !(status & 0x80) ) break;
5947 if (status & 0x01) {
5948 BX_PANIC("hard drive BIOS:(read/verify) read error\n");
5949 } else if ( !(status & 0x08) ) {
5950 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
5951 BX_PANIC("hard drive BIOS:(read/verify) expected DRQ=1\n");
5954 sector_count = 0;
5955 tempbx = BX;
5957 ASM_START
5958 sti ;; enable higher priority interrupts
5959 ASM_END
5961 while (1) {
5962 ASM_START
5963 ;; store temp bx in real DI register
5964 push bp
5965 mov bp, sp
5966 mov di, _int13_harddisk.tempbx + 2 [bp]
5967 pop bp
5969 ;; adjust if there will be an overrun
5970 cmp di, #0xfe00
5971 jbe i13_f02_no_adjust
5972 i13_f02_adjust:
5973 sub di, #0x0200 ; sub 512 bytes from offset
5974 mov ax, es
5975 add ax, #0x0020 ; add 512 to segment
5976 mov es, ax
5978 i13_f02_no_adjust:
5979 mov cx, #0x0100 ;; counter (256 words = 512b)
5980 mov dx, #0x01f0 ;; AT data read port
5983 insw ;; CX words transfered from port(DX) to ES:[DI]
5985 i13_f02_done:
5986 ;; store real DI register back to temp bx
5987 push bp
5988 mov bp, sp
5989 mov _int13_harddisk.tempbx + 2 [bp], di
5990 pop bp
5991 ASM_END
5993 sector_count++;
5994 num_sectors--;
5995 if (num_sectors == 0) {
5996 status = inb(0x1f7);
5997 if ( (status & 0xc9) != 0x40 )
5998 BX_PANIC("no sectors left to read/verify, status is %02x\n", (unsigned) status);
5999 break;
6001 else {
6002 status = inb(0x1f7);
6003 if ( (status & 0xc9) != 0x48 )
6004 BX_PANIC("more sectors left to read/verify, status is %02x\n", (unsigned) status);
6005 continue;
6009 SET_AH(0);
6010 SET_DISK_RET_STATUS(0);
6011 SET_AL(sector_count);
6012 CLEAR_CF(); /* successful */
6013 return;
6014 break;
6017 case 0x03: /* write disk sectors */
6018 BX_DEBUG_INT13_HD("int13_f03\n");
6019 drive = GET_ELDL ();
6020 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6022 num_sectors = GET_AL();
6023 cylinder = GET_CH();
6024 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
6025 sector = (GET_CL() & 0x3f);
6026 head = GET_DH();
6028 if (hd_cylinders > 1024) {
6029 if (hd_cylinders <= 2048) {
6030 cylinder <<= 1;
6032 else if (hd_cylinders <= 4096) {
6033 cylinder <<= 2;
6035 else if (hd_cylinders <= 8192) {
6036 cylinder <<= 3;
6038 else { // hd_cylinders <= 16384
6039 cylinder <<= 4;
6042 ax = head / hd_heads;
6043 cyl_mod = ax & 0xff;
6044 head = ax >> 8;
6045 cylinder |= cyl_mod;
6048 if ( (cylinder >= hd_cylinders) ||
6049 (sector > hd_sectors) ||
6050 (head >= hd_heads) ) {
6051 SET_AH( 1);
6052 SET_DISK_RET_STATUS(1);
6053 SET_CF(); /* error occurred */
6054 return;
6057 if ( (num_sectors > 128) || (num_sectors == 0) )
6058 BX_PANIC("int13_harddisk(): num_sectors out of range!\n");
6060 if (head > 15)
6061 BX_PANIC("hard drive BIOS:(read) head > 15\n");
6063 status = inb(0x1f7);
6064 if (status & 0x80) {
6065 BX_PANIC("hard drive BIOS:(read) BUSY bit set\n");
6067 // should check for Drive Ready Bit also in status reg
6068 outb(0x01f2, num_sectors);
6070 /* activate LBA? (tomv) */
6071 if (hd_heads > 16) {
6072 BX_DEBUG_INT13_HD("CHS (write): %x %x %x\n", cylinder, head, sector);
6073 outLBA(cylinder,hd_heads,head,hd_sectors,sector,GET_ELDL());
6075 else {
6076 outb(0x01f3, sector);
6077 outb(0x01f4, cylinder & 0x00ff);
6078 outb(0x01f5, cylinder >> 8);
6079 outb(0x01f6, 0xa0 | ((GET_ELDL() & 0x01)<<4) | (head & 0x0f));
6081 outb(0x01f7, 0x30);
6083 // wait for busy bit to turn off after seeking
6084 while (1) {
6085 status = inb(0x1f7);
6086 if ( !(status & 0x80) ) break;
6089 if ( !(status & 0x08) ) {
6090 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
6091 BX_PANIC("hard drive BIOS:(write) data-request bit not set\n");
6094 sector_count = 0;
6095 tempbx = BX;
6097 ASM_START
6098 sti ;; enable higher priority interrupts
6099 ASM_END
6101 while (1) {
6102 ASM_START
6103 ;; store temp bx in real SI register
6104 push bp
6105 mov bp, sp
6106 mov si, _int13_harddisk.tempbx + 2 [bp]
6107 pop bp
6109 ;; adjust if there will be an overrun
6110 cmp si, #0xfe00
6111 jbe i13_f03_no_adjust
6112 i13_f03_adjust:
6113 sub si, #0x0200 ; sub 512 bytes from offset
6114 mov ax, es
6115 add ax, #0x0020 ; add 512 to segment
6116 mov es, ax
6118 i13_f03_no_adjust:
6119 mov cx, #0x0100 ;; counter (256 words = 512b)
6120 mov dx, #0x01f0 ;; AT data read port
6122 seg ES
6124 outsw ;; CX words tranfered from ES:[SI] to port(DX)
6126 ;; store real SI register back to temp bx
6127 push bp
6128 mov bp, sp
6129 mov _int13_harddisk.tempbx + 2 [bp], si
6130 pop bp
6131 ASM_END
6133 sector_count++;
6134 num_sectors--;
6135 if (num_sectors == 0) {
6136 status = inb(0x1f7);
6137 if ( (status & 0xe9) != 0x40 )
6138 BX_PANIC("no sectors left to write, status is %02x\n", (unsigned) status);
6139 break;
6141 else {
6142 status = inb(0x1f7);
6143 if ( (status & 0xc9) != 0x48 )
6144 BX_PANIC("more sectors left to write, status is %02x\n", (unsigned) status);
6145 continue;
6149 SET_AH(0);
6150 SET_DISK_RET_STATUS(0);
6151 SET_AL(sector_count);
6152 CLEAR_CF(); /* successful */
6153 return;
6154 break;
6156 case 0x05: /* format disk track */
6157 BX_DEBUG_INT13_HD("int13_f05\n");
6158 BX_PANIC("format disk track called\n");
6159 /* nop */
6160 SET_AH(0);
6161 SET_DISK_RET_STATUS(0);
6162 CLEAR_CF(); /* successful */
6163 return;
6164 break;
6166 case 0x08: /* read disk drive parameters */
6167 BX_DEBUG_INT13_HD("int13_f08\n");
6169 drive = GET_ELDL ();
6170 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6172 // translate CHS
6174 if (hd_cylinders <= 1024) {
6175 // hd_cylinders >>= 0;
6176 // hd_heads <<= 0;
6178 else if (hd_cylinders <= 2048) {
6179 hd_cylinders >>= 1;
6180 hd_heads <<= 1;
6182 else if (hd_cylinders <= 4096) {
6183 hd_cylinders >>= 2;
6184 hd_heads <<= 2;
6186 else if (hd_cylinders <= 8192) {
6187 hd_cylinders >>= 3;
6188 hd_heads <<= 3;
6190 else { // hd_cylinders <= 16384
6191 hd_cylinders >>= 4;
6192 hd_heads <<= 4;
6195 max_cylinder = hd_cylinders - 2; /* 0 based */
6196 SET_AL(0);
6197 SET_CH(max_cylinder & 0xff);
6198 SET_CL(((max_cylinder >> 2) & 0xc0) | (hd_sectors & 0x3f));
6199 SET_DH(hd_heads - 1);
6200 SET_DL(n_drives); /* returns 0, 1, or 2 hard drives */
6201 SET_AH(0);
6202 SET_DISK_RET_STATUS(0);
6203 CLEAR_CF(); /* successful */
6205 return;
6206 break;
6208 case 0x09: /* initialize drive parameters */
6209 BX_DEBUG_INT13_HD("int13_f09\n");
6210 SET_AH(0);
6211 SET_DISK_RET_STATUS(0);
6212 CLEAR_CF(); /* successful */
6213 return;
6214 break;
6216 case 0x0a: /* read disk sectors with ECC */
6217 BX_DEBUG_INT13_HD("int13_f0a\n");
6218 case 0x0b: /* write disk sectors with ECC */
6219 BX_DEBUG_INT13_HD("int13_f0b\n");
6220 BX_PANIC("int13h Functions 0Ah & 0Bh not implemented!\n");
6221 return;
6222 break;
6224 case 0x0c: /* seek to specified cylinder */
6225 BX_DEBUG_INT13_HD("int13_f0c\n");
6226 BX_INFO("int13h function 0ch (seek) not implemented!\n");
6227 SET_AH(0);
6228 SET_DISK_RET_STATUS(0);
6229 CLEAR_CF(); /* successful */
6230 return;
6231 break;
6233 case 0x0d: /* alternate disk reset */
6234 BX_DEBUG_INT13_HD("int13_f0d\n");
6235 SET_AH(0);
6236 SET_DISK_RET_STATUS(0);
6237 CLEAR_CF(); /* successful */
6238 return;
6239 break;
6241 case 0x10: /* check drive ready */
6242 BX_DEBUG_INT13_HD("int13_f10\n");
6243 //SET_AH(0);
6244 //SET_DISK_RET_STATUS(0);
6245 //CLEAR_CF(); /* successful */
6246 //return;
6247 //break;
6249 // should look at 40:8E also???
6250 status = inb(0x01f7);
6251 if ( (status & 0xc0) == 0x40 ) {
6252 SET_AH(0);
6253 SET_DISK_RET_STATUS(0);
6254 CLEAR_CF(); // drive ready
6255 return;
6257 else {
6258 SET_AH(0xAA);
6259 SET_DISK_RET_STATUS(0xAA);
6260 SET_CF(); // not ready
6261 return;
6263 break;
6265 case 0x11: /* recalibrate */
6266 BX_DEBUG_INT13_HD("int13_f11\n");
6267 SET_AH(0);
6268 SET_DISK_RET_STATUS(0);
6269 CLEAR_CF(); /* successful */
6270 return;
6271 break;
6273 case 0x14: /* controller internal diagnostic */
6274 BX_DEBUG_INT13_HD("int13_f14\n");
6275 SET_AH(0);
6276 SET_DISK_RET_STATUS(0);
6277 CLEAR_CF(); /* successful */
6278 SET_AL(0);
6279 return;
6280 break;
6282 case 0x15: /* read disk drive size */
6283 drive = GET_ELDL();
6284 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6285 ASM_START
6286 push bp
6287 mov bp, sp
6288 mov al, _int13_harddisk.hd_heads + 2 [bp]
6289 mov ah, _int13_harddisk.hd_sectors + 2 [bp]
6290 mul al, ah ;; ax = heads * sectors
6291 mov bx, _int13_harddisk.hd_cylinders + 2 [bp]
6292 dec bx ;; use (cylinders - 1) ???
6293 mul ax, bx ;; dx:ax = (cylinders -1) * (heads * sectors)
6294 ;; now we need to move the 32bit result dx:ax to what the
6295 ;; BIOS wants which is cx:dx.
6296 ;; and then into CX:DX on the stack
6297 mov _int13_harddisk.CX + 2 [bp], dx
6298 mov _int13_harddisk.DX + 2 [bp], ax
6299 pop bp
6300 ASM_END
6301 SET_AH(3); // hard disk accessible
6302 SET_DISK_RET_STATUS(0); // ??? should this be 0
6303 CLEAR_CF(); // successful
6304 return;
6305 break;
6307 case 0x18: // set media type for format
6308 case 0x41: // IBM/MS
6309 case 0x42: // IBM/MS
6310 case 0x43: // IBM/MS
6311 case 0x44: // IBM/MS
6312 case 0x45: // IBM/MS lock/unlock drive
6313 case 0x46: // IBM/MS eject media
6314 case 0x47: // IBM/MS extended seek
6315 case 0x49: // IBM/MS extended media change
6316 case 0x50: // IBM/MS send packet command
6317 default:
6318 BX_INFO("int13_harddisk: unsupported AH=%02x\n", GET_AH());
6320 SET_AH(1); // code=invalid function in AH or invalid parameter
6321 SET_DISK_RET_STATUS(1);
6322 SET_CF(); /* unsuccessful */
6323 return;
6324 break;
6328 static char panic_msg_reg12h[] = "HD%d cmos reg 12h not type F\n";
6329 static char panic_msg_reg19h[] = "HD%d cmos reg %02xh not user definable type 47\n";
6331 void
6332 get_hd_geometry(drive, hd_cylinders, hd_heads, hd_sectors)
6333 Bit8u drive;
6334 Bit16u *hd_cylinders;
6335 Bit8u *hd_heads;
6336 Bit8u *hd_sectors;
6338 Bit8u hd_type;
6339 Bit16u ss;
6340 Bit16u cylinders;
6341 Bit8u iobase;
6343 ss = get_SS();
6344 if (drive == 0x80) {
6345 hd_type = inb_cmos(0x12) & 0xf0;
6346 if (hd_type != 0xf0)
6347 BX_INFO(panic_msg_reg12h,0);
6348 hd_type = inb_cmos(0x19); // HD0: extended type
6349 if (hd_type != 47)
6350 BX_INFO(panic_msg_reg19h,0,0x19);
6351 iobase = 0x1b;
6352 } else {
6353 hd_type = inb_cmos(0x12) & 0x0f;
6354 if (hd_type != 0x0f)
6355 BX_INFO(panic_msg_reg12h,1);
6356 hd_type = inb_cmos(0x1a); // HD0: extended type
6357 if (hd_type != 47)
6358 BX_INFO(panic_msg_reg19h,0,0x1a);
6359 iobase = 0x24;
6362 // cylinders
6363 cylinders = inb_cmos(iobase) | (inb_cmos(iobase+1) << 8);
6364 write_word(ss, hd_cylinders, cylinders);
6366 // heads
6367 write_byte(ss, hd_heads, inb_cmos(iobase+2));
6369 // sectors per track
6370 write_byte(ss, hd_sectors, inb_cmos(iobase+8));
6373 #endif //else BX_USE_ATADRV
6376 //////////////////////
6377 // FLOPPY functions //
6378 //////////////////////
6380 void floppy_reset_controller()
6382 Bit8u val8;
6384 // Reset controller
6385 val8 = inb(0x03f2);
6386 outb(0x03f2, val8 & ~0x04);
6387 outb(0x03f2, val8 | 0x04);
6389 // Wait for controller to come out of reset
6390 do {
6391 val8 = inb(0x3f4);
6392 } while ( (val8 & 0xc0) != 0x80 );
6395 void floppy_prepare_controller(drive)
6396 Bit16u drive;
6398 Bit8u val8, dor, prev_reset;
6400 // set 40:3e bit 7 to 0
6401 val8 = read_byte(0x0040, 0x003e);
6402 val8 &= 0x7f;
6403 write_byte(0x0040, 0x003e, val8);
6405 // turn on motor of selected drive, DMA & int enabled, normal operation
6406 prev_reset = inb(0x03f2) & 0x04;
6407 if (drive)
6408 dor = 0x20;
6409 else
6410 dor = 0x10;
6411 dor |= 0x0c;
6412 dor |= drive;
6413 outb(0x03f2, dor);
6415 // reset the disk motor timeout value of INT 08
6416 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
6418 // wait for drive readiness
6419 do {
6420 val8 = inb(0x3f4);
6421 } while ( (val8 & 0xc0) != 0x80 );
6423 if (prev_reset == 0) {
6424 // turn on interrupts
6425 ASM_START
6427 ASM_END
6428 // wait on 40:3e bit 7 to become 1
6429 do {
6430 val8 = read_byte(0x0040, 0x003e);
6431 } while ( (val8 & 0x80) == 0 );
6432 val8 &= 0x7f;
6433 ASM_START
6435 ASM_END
6436 write_byte(0x0040, 0x003e, val8);
6440 bx_bool
6441 floppy_media_known(drive)
6442 Bit16u drive;
6444 Bit8u val8;
6445 Bit16u media_state_offset;
6447 val8 = read_byte(0x0040, 0x003e); // diskette recal status
6448 if (drive)
6449 val8 >>= 1;
6450 val8 &= 0x01;
6451 if (val8 == 0)
6452 return(0);
6454 media_state_offset = 0x0090;
6455 if (drive)
6456 media_state_offset += 1;
6458 val8 = read_byte(0x0040, media_state_offset);
6459 val8 = (val8 >> 4) & 0x01;
6460 if (val8 == 0)
6461 return(0);
6463 // check pass, return KNOWN
6464 return(1);
6467 bx_bool
6468 floppy_media_sense(drive)
6469 Bit16u drive;
6471 bx_bool retval;
6472 Bit16u media_state_offset;
6473 Bit8u drive_type, config_data, media_state;
6475 if (floppy_drive_recal(drive) == 0) {
6476 return(0);
6479 // for now cheat and get drive type from CMOS,
6480 // assume media is same as drive type
6482 // ** config_data **
6483 // Bitfields for diskette media control:
6484 // Bit(s) Description (Table M0028)
6485 // 7-6 last data rate set by controller
6486 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6487 // 5-4 last diskette drive step rate selected
6488 // 00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
6489 // 3-2 {data rate at start of operation}
6490 // 1-0 reserved
6492 // ** media_state **
6493 // Bitfields for diskette drive media state:
6494 // Bit(s) Description (Table M0030)
6495 // 7-6 data rate
6496 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6497 // 5 double stepping required (e.g. 360kB in 1.2MB)
6498 // 4 media type established
6499 // 3 drive capable of supporting 4MB media
6500 // 2-0 on exit from BIOS, contains
6501 // 000 trying 360kB in 360kB
6502 // 001 trying 360kB in 1.2MB
6503 // 010 trying 1.2MB in 1.2MB
6504 // 011 360kB in 360kB established
6505 // 100 360kB in 1.2MB established
6506 // 101 1.2MB in 1.2MB established
6507 // 110 reserved
6508 // 111 all other formats/drives
6510 drive_type = inb_cmos(0x10);
6511 if (drive == 0)
6512 drive_type >>= 4;
6513 else
6514 drive_type &= 0x0f;
6515 if ( drive_type == 1 ) {
6516 // 360K 5.25" drive
6517 config_data = 0x00; // 0000 0000
6518 media_state = 0x25; // 0010 0101
6519 retval = 1;
6521 else if ( drive_type == 2 ) {
6522 // 1.2 MB 5.25" drive
6523 config_data = 0x00; // 0000 0000
6524 media_state = 0x25; // 0010 0101 // need double stepping??? (bit 5)
6525 retval = 1;
6527 else if ( drive_type == 3 ) {
6528 // 720K 3.5" drive
6529 config_data = 0x00; // 0000 0000 ???
6530 media_state = 0x17; // 0001 0111
6531 retval = 1;
6533 else if ( drive_type == 4 ) {
6534 // 1.44 MB 3.5" drive
6535 config_data = 0x00; // 0000 0000
6536 media_state = 0x17; // 0001 0111
6537 retval = 1;
6539 else if ( drive_type == 5 ) {
6540 // 2.88 MB 3.5" drive
6541 config_data = 0xCC; // 1100 1100
6542 media_state = 0xD7; // 1101 0111
6543 retval = 1;
6546 // Extended floppy size uses special cmos setting
6547 else if ( drive_type == 6 ) {
6548 // 160k 5.25" drive
6549 config_data = 0x00; // 0000 0000
6550 media_state = 0x27; // 0010 0111
6551 retval = 1;
6553 else if ( drive_type == 7 ) {
6554 // 180k 5.25" drive
6555 config_data = 0x00; // 0000 0000
6556 media_state = 0x27; // 0010 0111
6557 retval = 1;
6559 else if ( drive_type == 8 ) {
6560 // 320k 5.25" drive
6561 config_data = 0x00; // 0000 0000
6562 media_state = 0x27; // 0010 0111
6563 retval = 1;
6566 else {
6567 // not recognized
6568 config_data = 0x00; // 0000 0000
6569 media_state = 0x00; // 0000 0000
6570 retval = 0;
6573 if (drive == 0)
6574 media_state_offset = 0x90;
6575 else
6576 media_state_offset = 0x91;
6577 write_byte(0x0040, 0x008B, config_data);
6578 write_byte(0x0040, media_state_offset, media_state);
6580 return(retval);
6583 bx_bool
6584 floppy_drive_recal(drive)
6585 Bit16u drive;
6587 Bit8u val8;
6588 Bit16u curr_cyl_offset;
6590 floppy_prepare_controller(drive);
6592 // send Recalibrate command (2 bytes) to controller
6593 outb(0x03f5, 0x07); // 07: Recalibrate
6594 outb(0x03f5, drive); // 0=drive0, 1=drive1
6596 // turn on interrupts
6597 ASM_START
6599 ASM_END
6601 // wait on 40:3e bit 7 to become 1
6602 do {
6603 val8 = (read_byte(0x0040, 0x003e) & 0x80);
6604 } while ( val8 == 0 );
6606 val8 = 0; // separate asm from while() loop
6607 // turn off interrupts
6608 ASM_START
6610 ASM_END
6612 // set 40:3e bit 7 to 0, and calibrated bit
6613 val8 = read_byte(0x0040, 0x003e);
6614 val8 &= 0x7f;
6615 if (drive) {
6616 val8 |= 0x02; // Drive 1 calibrated
6617 curr_cyl_offset = 0x0095;
6618 } else {
6619 val8 |= 0x01; // Drive 0 calibrated
6620 curr_cyl_offset = 0x0094;
6622 write_byte(0x0040, 0x003e, val8);
6623 write_byte(0x0040, curr_cyl_offset, 0); // current cylinder is 0
6625 return(1);
6630 bx_bool
6631 floppy_drive_exists(drive)
6632 Bit16u drive;
6634 Bit8u drive_type;
6636 // check CMOS to see if drive exists
6637 drive_type = inb_cmos(0x10);
6638 if (drive == 0)
6639 drive_type >>= 4;
6640 else
6641 drive_type &= 0x0f;
6642 if ( drive_type == 0 )
6643 return(0);
6644 else
6645 return(1);
6648 #if BX_SUPPORT_FLOPPY
6649 void
6650 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
6651 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
6653 Bit8u drive, num_sectors, track, sector, head, status;
6654 Bit16u base_address, base_count, base_es;
6655 Bit8u page, mode_register, val8, dor;
6656 Bit8u return_status[7];
6657 Bit8u drive_type, num_floppies, ah;
6658 Bit16u es, last_addr;
6660 BX_DEBUG_INT13_FL("int13_diskette: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
6661 // BX_DEBUG_INT13_FL("int13_diskette: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), get_DS(), ES, DI, SI);
6663 ah = GET_AH();
6665 switch ( ah ) {
6666 case 0x00: // diskette controller reset
6667 BX_DEBUG_INT13_FL("floppy f00\n");
6668 drive = GET_ELDL();
6669 if (drive > 1) {
6670 SET_AH(1); // invalid param
6671 set_diskette_ret_status(1);
6672 SET_CF();
6673 return;
6675 drive_type = inb_cmos(0x10);
6677 if (drive == 0)
6678 drive_type >>= 4;
6679 else
6680 drive_type &= 0x0f;
6681 if (drive_type == 0) {
6682 SET_AH(0x80); // drive not responding
6683 set_diskette_ret_status(0x80);
6684 SET_CF();
6685 return;
6687 SET_AH(0);
6688 set_diskette_ret_status(0);
6689 CLEAR_CF(); // successful
6690 set_diskette_current_cyl(drive, 0); // current cylinder
6691 return;
6693 case 0x01: // Read Diskette Status
6694 CLEAR_CF();
6695 val8 = read_byte(0x0000, 0x0441);
6696 SET_AH(val8);
6697 if (val8) {
6698 SET_CF();
6700 return;
6702 case 0x02: // Read Diskette Sectors
6703 case 0x03: // Write Diskette Sectors
6704 case 0x04: // Verify Diskette Sectors
6705 num_sectors = GET_AL();
6706 track = GET_CH();
6707 sector = GET_CL();
6708 head = GET_DH();
6709 drive = GET_ELDL();
6711 if ( (drive > 1) || (head > 1) ||
6712 (num_sectors == 0) || (num_sectors > 72) ) {
6713 BX_INFO("floppy: drive>1 || head>1 ...\n");
6714 SET_AH(1);
6715 set_diskette_ret_status(1);
6716 SET_AL(0); // no sectors read
6717 SET_CF(); // error occurred
6718 return;
6721 // see if drive exists
6722 if (floppy_drive_exists(drive) == 0) {
6723 SET_AH(0x80); // not responding
6724 set_diskette_ret_status(0x80);
6725 SET_AL(0); // no sectors read
6726 SET_CF(); // error occurred
6727 return;
6730 // see if media in drive, and type is known
6731 if (floppy_media_known(drive) == 0) {
6732 if (floppy_media_sense(drive) == 0) {
6733 SET_AH(0x0C); // Media type not found
6734 set_diskette_ret_status(0x0C);
6735 SET_AL(0); // no sectors read
6736 SET_CF(); // error occurred
6737 return;
6741 if (ah == 0x02) {
6742 // Read Diskette Sectors
6744 //-----------------------------------
6745 // set up DMA controller for transfer
6746 //-----------------------------------
6748 // es:bx = pointer to where to place information from diskette
6749 // port 04: DMA-1 base and current address, channel 2
6750 // port 05: DMA-1 base and current count, channel 2
6751 page = (ES >> 12); // upper 4 bits
6752 base_es = (ES << 4); // lower 16bits contributed by ES
6753 base_address = base_es + BX; // lower 16 bits of address
6754 // contributed by ES:BX
6755 if ( base_address < base_es ) {
6756 // in case of carry, adjust page by 1
6757 page++;
6759 base_count = (num_sectors * 512) - 1;
6761 // check for 64K boundary overrun
6762 last_addr = base_address + base_count;
6763 if (last_addr < base_address) {
6764 SET_AH(0x09);
6765 set_diskette_ret_status(0x09);
6766 SET_AL(0); // no sectors read
6767 SET_CF(); // error occurred
6768 return;
6771 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
6772 outb(0x000a, 0x06);
6774 BX_DEBUG_INT13_FL("clear flip-flop\n");
6775 outb(0x000c, 0x00); // clear flip-flop
6776 outb(0x0004, base_address);
6777 outb(0x0004, base_address>>8);
6778 BX_DEBUG_INT13_FL("clear flip-flop\n");
6779 outb(0x000c, 0x00); // clear flip-flop
6780 outb(0x0005, base_count);
6781 outb(0x0005, base_count>>8);
6783 // port 0b: DMA-1 Mode Register
6784 mode_register = 0x46; // single mode, increment, autoinit disable,
6785 // transfer type=write, channel 2
6786 BX_DEBUG_INT13_FL("setting mode register\n");
6787 outb(0x000b, mode_register);
6789 BX_DEBUG_INT13_FL("setting page register\n");
6790 // port 81: DMA-1 Page Register, channel 2
6791 outb(0x0081, page);
6793 BX_DEBUG_INT13_FL("unmask chan 2\n");
6794 outb(0x000a, 0x02); // unmask channel 2
6796 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
6797 outb(0x000a, 0x02);
6799 //--------------------------------------
6800 // set up floppy controller for transfer
6801 //--------------------------------------
6802 floppy_prepare_controller(drive);
6804 // send read-normal-data command (9 bytes) to controller
6805 outb(0x03f5, 0xe6); // e6: read normal data
6806 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
6807 outb(0x03f5, track);
6808 outb(0x03f5, head);
6809 outb(0x03f5, sector);
6810 outb(0x03f5, 2); // 512 byte sector size
6811 outb(0x03f5, 0); // last sector number possible on track
6812 outb(0x03f5, 0); // Gap length
6813 outb(0x03f5, 0xff); // Gap length
6815 // turn on interrupts
6816 ASM_START
6818 ASM_END
6820 // wait on 40:3e bit 7 to become 1
6821 do {
6822 val8 = read_byte(0x0040, 0x0040);
6823 if (val8 == 0) {
6824 floppy_reset_controller();
6825 SET_AH(0x80); // drive not ready (timeout)
6826 set_diskette_ret_status(0x80);
6827 SET_AL(0); // no sectors read
6828 SET_CF(); // error occurred
6829 return;
6831 val8 = (read_byte(0x0040, 0x003e) & 0x80);
6832 } while ( val8 == 0 );
6834 val8 = 0; // separate asm from while() loop
6835 // turn off interrupts
6836 ASM_START
6838 ASM_END
6840 // set 40:3e bit 7 to 0
6841 val8 = read_byte(0x0040, 0x003e);
6842 val8 &= 0x7f;
6843 write_byte(0x0040, 0x003e, val8);
6845 // check port 3f4 for accessibility to status bytes
6846 val8 = inb(0x3f4);
6847 if ( (val8 & 0xc0) != 0xc0 )
6848 BX_PANIC("int13_diskette: ctrl not ready\n");
6850 // read 7 return status bytes from controller
6851 // using loop index broken, have to unroll...
6852 return_status[0] = inb(0x3f5);
6853 return_status[1] = inb(0x3f5);
6854 return_status[2] = inb(0x3f5);
6855 return_status[3] = inb(0x3f5);
6856 return_status[4] = inb(0x3f5);
6857 return_status[5] = inb(0x3f5);
6858 return_status[6] = inb(0x3f5);
6859 // record in BIOS Data Area
6860 write_byte(0x0040, 0x0042, return_status[0]);
6861 write_byte(0x0040, 0x0043, return_status[1]);
6862 write_byte(0x0040, 0x0044, return_status[2]);
6863 write_byte(0x0040, 0x0045, return_status[3]);
6864 write_byte(0x0040, 0x0046, return_status[4]);
6865 write_byte(0x0040, 0x0047, return_status[5]);
6866 write_byte(0x0040, 0x0048, return_status[6]);
6868 if ( (return_status[0] & 0xc0) != 0 ) {
6869 SET_AH(0x20);
6870 set_diskette_ret_status(0x20);
6871 SET_AL(0); // no sectors read
6872 SET_CF(); // error occurred
6873 return;
6876 // ??? should track be new val from return_status[3] ?
6877 set_diskette_current_cyl(drive, track);
6878 // AL = number of sectors read (same value as passed)
6879 SET_AH(0x00); // success
6880 CLEAR_CF(); // success
6881 return;
6882 } else if (ah == 0x03) {
6883 // Write Diskette Sectors
6885 //-----------------------------------
6886 // set up DMA controller for transfer
6887 //-----------------------------------
6889 // es:bx = pointer to where to place information from diskette
6890 // port 04: DMA-1 base and current address, channel 2
6891 // port 05: DMA-1 base and current count, channel 2
6892 page = (ES >> 12); // upper 4 bits
6893 base_es = (ES << 4); // lower 16bits contributed by ES
6894 base_address = base_es + BX; // lower 16 bits of address
6895 // contributed by ES:BX
6896 if ( base_address < base_es ) {
6897 // in case of carry, adjust page by 1
6898 page++;
6900 base_count = (num_sectors * 512) - 1;
6902 // check for 64K boundary overrun
6903 last_addr = base_address + base_count;
6904 if (last_addr < base_address) {
6905 SET_AH(0x09);
6906 set_diskette_ret_status(0x09);
6907 SET_AL(0); // no sectors read
6908 SET_CF(); // error occurred
6909 return;
6912 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
6913 outb(0x000a, 0x06);
6915 outb(0x000c, 0x00); // clear flip-flop
6916 outb(0x0004, base_address);
6917 outb(0x0004, base_address>>8);
6918 outb(0x000c, 0x00); // clear flip-flop
6919 outb(0x0005, base_count);
6920 outb(0x0005, base_count>>8);
6922 // port 0b: DMA-1 Mode Register
6923 mode_register = 0x4a; // single mode, increment, autoinit disable,
6924 // transfer type=read, channel 2
6925 outb(0x000b, mode_register);
6927 // port 81: DMA-1 Page Register, channel 2
6928 outb(0x0081, page);
6930 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
6931 outb(0x000a, 0x02);
6933 //--------------------------------------
6934 // set up floppy controller for transfer
6935 //--------------------------------------
6936 floppy_prepare_controller(drive);
6938 // send write-normal-data command (9 bytes) to controller
6939 outb(0x03f5, 0xc5); // c5: write normal data
6940 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
6941 outb(0x03f5, track);
6942 outb(0x03f5, head);
6943 outb(0x03f5, sector);
6944 outb(0x03f5, 2); // 512 byte sector size
6945 outb(0x03f5, 0); // last sector number possible on track
6946 outb(0x03f5, 0); // Gap length
6947 outb(0x03f5, 0xff); // Gap length
6949 // turn on interrupts
6950 ASM_START
6952 ASM_END
6954 // wait on 40:3e bit 7 to become 1
6955 do {
6956 val8 = read_byte(0x0040, 0x0040);
6957 if (val8 == 0) {
6958 floppy_reset_controller();
6959 SET_AH(0x80); // drive not ready (timeout)
6960 set_diskette_ret_status(0x80);
6961 SET_AL(0); // no sectors written
6962 SET_CF(); // error occurred
6963 return;
6965 val8 = (read_byte(0x0040, 0x003e) & 0x80);
6966 } while ( val8 == 0 );
6968 val8 = 0; // separate asm from while() loop
6969 // turn off interrupts
6970 ASM_START
6972 ASM_END
6974 // set 40:3e bit 7 to 0
6975 val8 = read_byte(0x0040, 0x003e);
6976 val8 &= 0x7f;
6977 write_byte(0x0040, 0x003e, val8);
6979 // check port 3f4 for accessibility to status bytes
6980 val8 = inb(0x3f4);
6981 if ( (val8 & 0xc0) != 0xc0 )
6982 BX_PANIC("int13_diskette: ctrl not ready\n");
6984 // read 7 return status bytes from controller
6985 // using loop index broken, have to unroll...
6986 return_status[0] = inb(0x3f5);
6987 return_status[1] = inb(0x3f5);
6988 return_status[2] = inb(0x3f5);
6989 return_status[3] = inb(0x3f5);
6990 return_status[4] = inb(0x3f5);
6991 return_status[5] = inb(0x3f5);
6992 return_status[6] = inb(0x3f5);
6993 // record in BIOS Data Area
6994 write_byte(0x0040, 0x0042, return_status[0]);
6995 write_byte(0x0040, 0x0043, return_status[1]);
6996 write_byte(0x0040, 0x0044, return_status[2]);
6997 write_byte(0x0040, 0x0045, return_status[3]);
6998 write_byte(0x0040, 0x0046, return_status[4]);
6999 write_byte(0x0040, 0x0047, return_status[5]);
7000 write_byte(0x0040, 0x0048, return_status[6]);
7002 if ( (return_status[0] & 0xc0) != 0 ) {
7003 if ( (return_status[1] & 0x02) != 0 ) {
7004 // diskette not writable.
7005 // AH=status code=0x03 (tried to write on write-protected disk)
7006 // AL=number of sectors written=0
7007 AX = 0x0300;
7008 SET_CF();
7009 return;
7010 } else {
7011 BX_PANIC("int13_diskette_function: read error\n");
7015 // ??? should track be new val from return_status[3] ?
7016 set_diskette_current_cyl(drive, track);
7017 // AL = number of sectors read (same value as passed)
7018 SET_AH(0x00); // success
7019 CLEAR_CF(); // success
7020 return;
7021 } else { // if (ah == 0x04)
7022 // Verify Diskette Sectors
7024 // ??? should track be new val from return_status[3] ?
7025 set_diskette_current_cyl(drive, track);
7026 // AL = number of sectors verified (same value as passed)
7027 CLEAR_CF(); // success
7028 SET_AH(0x00); // success
7029 return;
7031 break;
7033 case 0x05: // format diskette track
7034 BX_DEBUG_INT13_FL("floppy f05\n");
7036 num_sectors = GET_AL();
7037 track = GET_CH();
7038 head = GET_DH();
7039 drive = GET_ELDL();
7041 if ((drive > 1) || (head > 1) || (track > 79) ||
7042 (num_sectors == 0) || (num_sectors > 18)) {
7043 SET_AH(1);
7044 set_diskette_ret_status(1);
7045 SET_CF(); // error occurred
7048 // see if drive exists
7049 if (floppy_drive_exists(drive) == 0) {
7050 SET_AH(0x80); // drive not responding
7051 set_diskette_ret_status(0x80);
7052 SET_CF(); // error occurred
7053 return;
7056 // see if media in drive, and type is known
7057 if (floppy_media_known(drive) == 0) {
7058 if (floppy_media_sense(drive) == 0) {
7059 SET_AH(0x0C); // Media type not found
7060 set_diskette_ret_status(0x0C);
7061 SET_AL(0); // no sectors read
7062 SET_CF(); // error occurred
7063 return;
7067 // set up DMA controller for transfer
7068 page = (ES >> 12); // upper 4 bits
7069 base_es = (ES << 4); // lower 16bits contributed by ES
7070 base_address = base_es + BX; // lower 16 bits of address
7071 // contributed by ES:BX
7072 if ( base_address < base_es ) {
7073 // in case of carry, adjust page by 1
7074 page++;
7076 base_count = (num_sectors * 4) - 1;
7078 // check for 64K boundary overrun
7079 last_addr = base_address + base_count;
7080 if (last_addr < base_address) {
7081 SET_AH(0x09);
7082 set_diskette_ret_status(0x09);
7083 SET_AL(0); // no sectors read
7084 SET_CF(); // error occurred
7085 return;
7088 outb(0x000a, 0x06);
7089 outb(0x000c, 0x00); // clear flip-flop
7090 outb(0x0004, base_address);
7091 outb(0x0004, base_address>>8);
7092 outb(0x000c, 0x00); // clear flip-flop
7093 outb(0x0005, base_count);
7094 outb(0x0005, base_count>>8);
7095 mode_register = 0x4a; // single mode, increment, autoinit disable,
7096 // transfer type=read, channel 2
7097 outb(0x000b, mode_register);
7098 // port 81: DMA-1 Page Register, channel 2
7099 outb(0x0081, page);
7100 outb(0x000a, 0x02);
7102 // set up floppy controller for transfer
7103 floppy_prepare_controller(drive);
7105 // send format-track command (6 bytes) to controller
7106 outb(0x03f5, 0x4d); // 4d: format track
7107 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7108 outb(0x03f5, 2); // 512 byte sector size
7109 outb(0x03f5, num_sectors); // number of sectors per track
7110 outb(0x03f5, 0); // Gap length
7111 outb(0x03f5, 0xf6); // Fill byte
7112 // turn on interrupts
7113 ASM_START
7115 ASM_END
7117 // wait on 40:3e bit 7 to become 1
7118 do {
7119 val8 = read_byte(0x0040, 0x0040);
7120 if (val8 == 0) {
7121 floppy_reset_controller();
7122 SET_AH(0x80); // drive not ready (timeout)
7123 set_diskette_ret_status(0x80);
7124 SET_CF(); // error occurred
7125 return;
7127 val8 = (read_byte(0x0040, 0x003e) & 0x80);
7128 } while ( val8 == 0 );
7130 val8 = 0; // separate asm from while() loop
7131 // turn off interrupts
7132 ASM_START
7134 ASM_END
7135 // set 40:3e bit 7 to 0
7136 val8 = read_byte(0x0040, 0x003e);
7137 val8 &= 0x7f;
7138 write_byte(0x0040, 0x003e, val8);
7139 // check port 3f4 for accessibility to status bytes
7140 val8 = inb(0x3f4);
7141 if ( (val8 & 0xc0) != 0xc0 )
7142 BX_PANIC("int13_diskette: ctrl not ready\n");
7144 // read 7 return status bytes from controller
7145 // using loop index broken, have to unroll...
7146 return_status[0] = inb(0x3f5);
7147 return_status[1] = inb(0x3f5);
7148 return_status[2] = inb(0x3f5);
7149 return_status[3] = inb(0x3f5);
7150 return_status[4] = inb(0x3f5);
7151 return_status[5] = inb(0x3f5);
7152 return_status[6] = inb(0x3f5);
7153 // record in BIOS Data Area
7154 write_byte(0x0040, 0x0042, return_status[0]);
7155 write_byte(0x0040, 0x0043, return_status[1]);
7156 write_byte(0x0040, 0x0044, return_status[2]);
7157 write_byte(0x0040, 0x0045, return_status[3]);
7158 write_byte(0x0040, 0x0046, return_status[4]);
7159 write_byte(0x0040, 0x0047, return_status[5]);
7160 write_byte(0x0040, 0x0048, return_status[6]);
7162 if ( (return_status[0] & 0xc0) != 0 ) {
7163 if ( (return_status[1] & 0x02) != 0 ) {
7164 // diskette not writable.
7165 // AH=status code=0x03 (tried to write on write-protected disk)
7166 // AL=number of sectors written=0
7167 AX = 0x0300;
7168 SET_CF();
7169 return;
7170 } else {
7171 BX_PANIC("int13_diskette_function: write error\n");
7175 SET_AH(0);
7176 set_diskette_ret_status(0);
7177 set_diskette_current_cyl(drive, 0);
7178 CLEAR_CF(); // successful
7179 return;
7182 case 0x08: // read diskette drive parameters
7183 BX_DEBUG_INT13_FL("floppy f08\n");
7184 drive = GET_ELDL();
7186 if (drive > 1) {
7187 AX = 0;
7188 BX = 0;
7189 CX = 0;
7190 DX = 0;
7191 ES = 0;
7192 DI = 0;
7193 SET_DL(num_floppies);
7194 SET_CF();
7195 return;
7198 drive_type = inb_cmos(0x10);
7199 num_floppies = 0;
7200 if (drive_type & 0xf0)
7201 num_floppies++;
7202 if (drive_type & 0x0f)
7203 num_floppies++;
7205 if (drive == 0)
7206 drive_type >>= 4;
7207 else
7208 drive_type &= 0x0f;
7210 SET_BH(0);
7211 SET_BL(drive_type);
7212 SET_AH(0);
7213 SET_AL(0);
7214 SET_DL(num_floppies);
7216 switch (drive_type) {
7217 case 0: // none
7218 CX = 0;
7219 SET_DH(0); // max head #
7220 break;
7222 case 1: // 360KB, 5.25"
7223 CX = 0x2709; // 40 tracks, 9 sectors
7224 SET_DH(1); // max head #
7225 break;
7227 case 2: // 1.2MB, 5.25"
7228 CX = 0x4f0f; // 80 tracks, 15 sectors
7229 SET_DH(1); // max head #
7230 break;
7232 case 3: // 720KB, 3.5"
7233 CX = 0x4f09; // 80 tracks, 9 sectors
7234 SET_DH(1); // max head #
7235 break;
7237 case 4: // 1.44MB, 3.5"
7238 CX = 0x4f12; // 80 tracks, 18 sectors
7239 SET_DH(1); // max head #
7240 break;
7242 case 5: // 2.88MB, 3.5"
7243 CX = 0x4f24; // 80 tracks, 36 sectors
7244 SET_DH(1); // max head #
7245 break;
7247 case 6: // 160k, 5.25"
7248 CX = 0x2708; // 40 tracks, 8 sectors
7249 SET_DH(0); // max head #
7250 break;
7252 case 7: // 180k, 5.25"
7253 CX = 0x2709; // 40 tracks, 9 sectors
7254 SET_DH(0); // max head #
7255 break;
7257 case 8: // 320k, 5.25"
7258 CX = 0x2708; // 40 tracks, 8 sectors
7259 SET_DH(1); // max head #
7260 break;
7262 default: // ?
7263 BX_PANIC("floppy: int13: bad floppy type\n");
7266 /* set es & di to point to 11 byte diskette param table in ROM */
7267 ASM_START
7268 push bp
7269 mov bp, sp
7270 mov ax, #diskette_param_table2
7271 mov _int13_diskette_function.DI+2[bp], ax
7272 mov _int13_diskette_function.ES+2[bp], cs
7273 pop bp
7274 ASM_END
7275 CLEAR_CF(); // success
7276 /* disk status not changed upon success */
7277 return;
7280 case 0x15: // read diskette drive type
7281 BX_DEBUG_INT13_FL("floppy f15\n");
7282 drive = GET_ELDL();
7283 if (drive > 1) {
7284 SET_AH(0); // only 2 drives supported
7285 // set_diskette_ret_status here ???
7286 SET_CF();
7287 return;
7289 drive_type = inb_cmos(0x10);
7291 if (drive == 0)
7292 drive_type >>= 4;
7293 else
7294 drive_type &= 0x0f;
7295 CLEAR_CF(); // successful, not present
7296 if (drive_type==0) {
7297 SET_AH(0); // drive not present
7299 else {
7300 SET_AH(1); // drive present, does not support change line
7303 return;
7305 case 0x16: // get diskette change line status
7306 BX_DEBUG_INT13_FL("floppy f16\n");
7307 drive = GET_ELDL();
7308 if (drive > 1) {
7309 SET_AH(0x01); // invalid drive
7310 set_diskette_ret_status(0x01);
7311 SET_CF();
7312 return;
7315 SET_AH(0x06); // change line not supported
7316 set_diskette_ret_status(0x06);
7317 SET_CF();
7318 return;
7320 case 0x17: // set diskette type for format(old)
7321 BX_DEBUG_INT13_FL("floppy f17\n");
7322 /* not used for 1.44M floppies */
7323 SET_AH(0x01); // not supported
7324 set_diskette_ret_status(1); /* not supported */
7325 SET_CF();
7326 return;
7328 case 0x18: // set diskette type for format(new)
7329 BX_DEBUG_INT13_FL("floppy f18\n");
7330 SET_AH(0x01); // do later
7331 set_diskette_ret_status(1);
7332 SET_CF();
7333 return;
7335 default:
7336 BX_INFO("int13_diskette: unsupported AH=%02x\n", GET_AH());
7338 // if ( (ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e) ) {
7339 SET_AH(0x01); // ???
7340 set_diskette_ret_status(1);
7341 SET_CF();
7342 return;
7343 // }
7346 #else // #if BX_SUPPORT_FLOPPY
7347 void
7348 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
7349 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
7351 Bit8u val8;
7353 switch ( GET_AH() ) {
7355 case 0x01: // Read Diskette Status
7356 CLEAR_CF();
7357 val8 = read_byte(0x0000, 0x0441);
7358 SET_AH(val8);
7359 if (val8) {
7360 SET_CF();
7362 return;
7364 default:
7365 SET_CF();
7366 write_byte(0x0000, 0x0441, 0x01);
7367 SET_AH(0x01);
7370 #endif // #if BX_SUPPORT_FLOPPY
7372 void
7373 set_diskette_ret_status(value)
7374 Bit8u value;
7376 write_byte(0x0040, 0x0041, value);
7379 void
7380 set_diskette_current_cyl(drive, cyl)
7381 Bit8u drive;
7382 Bit8u cyl;
7384 if (drive > 1)
7385 BX_PANIC("set_diskette_current_cyl(): drive > 1\n");
7386 write_byte(0x0040, 0x0094+drive, cyl);
7389 void
7390 determine_floppy_media(drive)
7391 Bit16u drive;
7393 #if 0
7394 Bit8u val8, DOR, ctrl_info;
7396 ctrl_info = read_byte(0x0040, 0x008F);
7397 if (drive==1)
7398 ctrl_info >>= 4;
7399 else
7400 ctrl_info &= 0x0f;
7402 #if 0
7403 if (drive == 0) {
7404 DOR = 0x1c; // DOR: drive0 motor on, DMA&int enabled, normal op, drive select 0
7406 else {
7407 DOR = 0x2d; // DOR: drive1 motor on, DMA&int enabled, normal op, drive select 1
7409 #endif
7411 if ( (ctrl_info & 0x04) != 0x04 ) {
7412 // Drive not determined means no drive exists, done.
7413 return;
7416 #if 0
7417 // check Main Status Register for readiness
7418 val8 = inb(0x03f4) & 0x80; // Main Status Register
7419 if (val8 != 0x80)
7420 BX_PANIC("d_f_m: MRQ bit not set\n");
7422 // change line
7424 // existing BDA values
7426 // turn on drive motor
7427 outb(0x03f2, DOR); // Digital Output Register
7429 #endif
7430 BX_PANIC("d_f_m: OK so far\n");
7431 #endif
7434 void
7435 int17_function(regs, ds, iret_addr)
7436 pusha_regs_t regs; // regs pushed from PUSHA instruction
7437 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7438 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7440 Bit16u addr,timeout;
7441 Bit8u val8;
7443 ASM_START
7445 ASM_END
7447 addr = read_word(0x0040, (regs.u.r16.dx << 1) + 8);
7448 if ((regs.u.r8.ah < 3) && (regs.u.r16.dx < 3) && (addr > 0)) {
7449 timeout = read_byte(0x0040, 0x0078 + regs.u.r16.dx) << 8;
7450 if (regs.u.r8.ah == 0) {
7451 outb(addr, regs.u.r8.al);
7452 val8 = inb(addr+2);
7453 outb(addr+2, val8 | 0x01); // send strobe
7454 ASM_START
7456 ASM_END
7457 outb(addr+2, val8 & ~0x01);
7458 while (((inb(addr+1) & 0x40) == 0x40) && (timeout)) {
7459 timeout--;
7462 if (regs.u.r8.ah == 1) {
7463 val8 = inb(addr+2);
7464 outb(addr+2, val8 & ~0x04); // send init
7465 ASM_START
7467 ASM_END
7468 outb(addr+2, val8 | 0x04);
7470 val8 = inb(addr+1);
7471 regs.u.r8.ah = (val8 ^ 0x48);
7472 if (!timeout) regs.u.r8.ah |= 0x01;
7473 ClearCF(iret_addr.flags);
7474 } else {
7475 SetCF(iret_addr.flags); // Unsupported
7479 // returns bootsegment in ax, drive in bl
7480 Bit32u
7481 int19_function(bseqnr)
7482 Bit8u bseqnr;
7484 Bit16u ebda_seg=read_word(0x0040,0x000E);
7485 Bit16u bootseq;
7486 Bit8u bootdrv;
7487 Bit8u bootcd;
7488 Bit8u bootchk;
7489 Bit16u bootseg;
7490 Bit16u status;
7491 Bit8u lastdrive=0;
7493 // if BX_ELTORITO_BOOT is not defined, old behavior
7494 // check bit 5 in CMOS reg 0x2d. load either 0x00 or 0x80 into DL
7495 // in preparation for the intial INT 13h (0=floppy A:, 0x80=C:)
7496 // 0: system boot sequence, first drive C: then A:
7497 // 1: system boot sequence, first drive A: then C:
7498 // else BX_ELTORITO_BOOT is defined
7499 // CMOS regs 0x3D and 0x38 contain the boot sequence:
7500 // CMOS reg 0x3D & 0x0f : 1st boot device
7501 // CMOS reg 0x3D & 0xf0 : 2nd boot device
7502 // CMOS reg 0x38 & 0xf0 : 3rd boot device
7503 // boot device codes:
7504 // 0x00 : not defined
7505 // 0x01 : first floppy
7506 // 0x02 : first harddrive
7507 // 0x03 : first cdrom
7508 // else : boot failure
7510 // Get the boot sequence
7511 #if BX_ELTORITO_BOOT
7512 bootseq=inb_cmos(0x3d);
7513 bootseq|=((inb_cmos(0x38) & 0xf0) << 4);
7515 if (bseqnr==2) bootseq >>= 4;
7516 if (bseqnr==3) bootseq >>= 8;
7517 if (bootseq<0x10) lastdrive = 1;
7518 bootdrv=0x00; bootcd=0;
7519 switch(bootseq & 0x0f) {
7520 case 0x01: bootdrv=0x00; bootcd=0; break;
7521 case 0x02: bootdrv=0x80; bootcd=0; break;
7522 case 0x03: bootdrv=0x00; bootcd=1; break;
7523 default: return 0x00000000;
7525 #else
7526 bootseq=inb_cmos(0x2d);
7528 if (bseqnr==2) {
7529 bootseq ^= 0x20;
7530 lastdrive = 1;
7532 bootdrv=0x00; bootcd=0;
7533 if((bootseq&0x20)==0) bootdrv=0x80;
7534 #endif // BX_ELTORITO_BOOT
7536 #if BX_ELTORITO_BOOT
7537 // We have to boot from cd
7538 if (bootcd != 0) {
7539 status = cdrom_boot();
7541 // If failure
7542 if ( (status & 0x00ff) !=0 ) {
7543 print_cdromboot_failure(status);
7544 print_boot_failure(bootcd, bootdrv, 1, lastdrive);
7545 return 0x00000000;
7548 bootseg = read_word(ebda_seg,&EbdaData->cdemu.load_segment);
7549 bootdrv = (Bit8u)(status>>8);
7552 #endif // BX_ELTORITO_BOOT
7554 // We have to boot from harddisk or floppy
7555 if (bootcd == 0) {
7556 bootseg=0x07c0;
7558 ASM_START
7559 push bp
7560 mov bp, sp
7562 mov ax, #0x0000
7563 mov _int19_function.status + 2[bp], ax
7564 mov dl, _int19_function.bootdrv + 2[bp]
7565 mov ax, _int19_function.bootseg + 2[bp]
7566 mov es, ax ;; segment
7567 mov bx, #0x0000 ;; offset
7568 mov ah, #0x02 ;; function 2, read diskette sector
7569 mov al, #0x01 ;; read 1 sector
7570 mov ch, #0x00 ;; track 0
7571 mov cl, #0x01 ;; sector 1
7572 mov dh, #0x00 ;; head 0
7573 int #0x13 ;; read sector
7574 jnc int19_load_done
7575 mov ax, #0x0001
7576 mov _int19_function.status + 2[bp], ax
7578 int19_load_done:
7579 pop bp
7580 ASM_END
7582 if (status != 0) {
7583 print_boot_failure(bootcd, bootdrv, 1, lastdrive);
7584 return 0x00000000;
7588 // check signature if instructed by cmos reg 0x38, only for floppy
7589 // bootchk = 1 : signature check disabled
7590 // bootchk = 0 : signature check enabled
7591 if (bootdrv != 0) bootchk = 0;
7592 else bootchk = inb_cmos(0x38) & 0x01;
7594 #if BX_ELTORITO_BOOT
7595 // if boot from cd, no signature check
7596 if (bootcd != 0)
7597 bootchk = 1;
7598 #endif // BX_ELTORITO_BOOT
7600 if (bootchk == 0) {
7601 if (read_word(bootseg,0x1fe) != 0xaa55) {
7602 print_boot_failure(bootcd, bootdrv, 0, lastdrive);
7603 return 0x00000000;
7607 #if BX_ELTORITO_BOOT
7608 // Print out the boot string
7609 print_boot_device(bootcd, bootdrv);
7610 #else // BX_ELTORITO_BOOT
7611 print_boot_device(0, bootdrv);
7612 #endif // BX_ELTORITO_BOOT
7614 // return the boot segment
7615 return (((Bit32u)bootdrv) << 16) + bootseg;
7618 void
7619 int1a_function(regs, ds, iret_addr)
7620 pusha_regs_t regs; // regs pushed from PUSHA instruction
7621 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7622 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7624 Bit8u val8;
7626 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);
7628 ASM_START
7630 ASM_END
7632 switch (regs.u.r8.ah) {
7633 case 0: // get current clock count
7634 ASM_START
7636 ASM_END
7637 regs.u.r16.cx = BiosData->ticks_high;
7638 regs.u.r16.dx = BiosData->ticks_low;
7639 regs.u.r8.al = BiosData->midnight_flag;
7640 BiosData->midnight_flag = 0; // reset flag
7641 ASM_START
7643 ASM_END
7644 // AH already 0
7645 ClearCF(iret_addr.flags); // OK
7646 break;
7648 case 1: // Set Current Clock Count
7649 ASM_START
7651 ASM_END
7652 BiosData->ticks_high = regs.u.r16.cx;
7653 BiosData->ticks_low = regs.u.r16.dx;
7654 BiosData->midnight_flag = 0; // reset flag
7655 ASM_START
7657 ASM_END
7658 regs.u.r8.ah = 0;
7659 ClearCF(iret_addr.flags); // OK
7660 break;
7663 case 2: // Read CMOS Time
7664 if (rtc_updating()) {
7665 SetCF(iret_addr.flags);
7666 break;
7669 regs.u.r8.dh = inb_cmos(0x00); // Seconds
7670 regs.u.r8.cl = inb_cmos(0x02); // Minutes
7671 regs.u.r8.ch = inb_cmos(0x04); // Hours
7672 regs.u.r8.dl = inb_cmos(0x0b) & 0x01; // Stat Reg B
7673 regs.u.r8.ah = 0;
7674 regs.u.r8.al = regs.u.r8.ch;
7675 ClearCF(iret_addr.flags); // OK
7676 break;
7678 case 3: // Set CMOS Time
7679 // Using a debugger, I notice the following masking/setting
7680 // of bits in Status Register B, by setting Reg B to
7681 // a few values and getting its value after INT 1A was called.
7683 // try#1 try#2 try#3
7684 // before 1111 1101 0111 1101 0000 0000
7685 // after 0110 0010 0110 0010 0000 0010
7687 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7688 // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
7689 if (rtc_updating()) {
7690 init_rtc();
7691 // fall through as if an update were not in progress
7693 outb_cmos(0x00, regs.u.r8.dh); // Seconds
7694 outb_cmos(0x02, regs.u.r8.cl); // Minutes
7695 outb_cmos(0x04, regs.u.r8.ch); // Hours
7696 // Set Daylight Savings time enabled bit to requested value
7697 val8 = (inb_cmos(0x0b) & 0x60) | 0x02 | (regs.u.r8.dl & 0x01);
7698 // (reg B already selected)
7699 outb_cmos(0x0b, val8);
7700 regs.u.r8.ah = 0;
7701 regs.u.r8.al = val8; // val last written to Reg B
7702 ClearCF(iret_addr.flags); // OK
7703 break;
7705 case 4: // Read CMOS Date
7706 regs.u.r8.ah = 0;
7707 if (rtc_updating()) {
7708 SetCF(iret_addr.flags);
7709 break;
7711 regs.u.r8.cl = inb_cmos(0x09); // Year
7712 regs.u.r8.dh = inb_cmos(0x08); // Month
7713 regs.u.r8.dl = inb_cmos(0x07); // Day of Month
7714 regs.u.r8.ch = inb_cmos(0x32); // Century
7715 regs.u.r8.al = regs.u.r8.ch;
7716 ClearCF(iret_addr.flags); // OK
7717 break;
7719 case 5: // Set CMOS Date
7720 // Using a debugger, I notice the following masking/setting
7721 // of bits in Status Register B, by setting Reg B to
7722 // a few values and getting its value after INT 1A was called.
7724 // try#1 try#2 try#3 try#4
7725 // before 1111 1101 0111 1101 0000 0010 0000 0000
7726 // after 0110 1101 0111 1101 0000 0010 0000 0000
7728 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7729 // My assumption: RegB = (RegB & 01111111b)
7730 if (rtc_updating()) {
7731 init_rtc();
7732 SetCF(iret_addr.flags);
7733 break;
7735 outb_cmos(0x09, regs.u.r8.cl); // Year
7736 outb_cmos(0x08, regs.u.r8.dh); // Month
7737 outb_cmos(0x07, regs.u.r8.dl); // Day of Month
7738 outb_cmos(0x32, regs.u.r8.ch); // Century
7739 val8 = inb_cmos(0x0b) & 0x7f; // clear halt-clock bit
7740 outb_cmos(0x0b, val8);
7741 regs.u.r8.ah = 0;
7742 regs.u.r8.al = val8; // AL = val last written to Reg B
7743 ClearCF(iret_addr.flags); // OK
7744 break;
7746 case 6: // Set Alarm Time in CMOS
7747 // Using a debugger, I notice the following masking/setting
7748 // of bits in Status Register B, by setting Reg B to
7749 // a few values and getting its value after INT 1A was called.
7751 // try#1 try#2 try#3
7752 // before 1101 1111 0101 1111 0000 0000
7753 // after 0110 1111 0111 1111 0010 0000
7755 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7756 // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
7757 val8 = inb_cmos(0x0b); // Get Status Reg B
7758 regs.u.r16.ax = 0;
7759 if (val8 & 0x20) {
7760 // Alarm interrupt enabled already
7761 SetCF(iret_addr.flags); // Error: alarm in use
7762 break;
7764 if (rtc_updating()) {
7765 init_rtc();
7766 // fall through as if an update were not in progress
7768 outb_cmos(0x01, regs.u.r8.dh); // Seconds alarm
7769 outb_cmos(0x03, regs.u.r8.cl); // Minutes alarm
7770 outb_cmos(0x05, regs.u.r8.ch); // Hours alarm
7771 outb(0xa1, inb(0xa1) & 0xfe); // enable IRQ 8
7772 // enable Status Reg B alarm bit, clear halt clock bit
7773 outb_cmos(0x0b, (val8 & 0x7f) | 0x20);
7774 ClearCF(iret_addr.flags); // OK
7775 break;
7777 case 7: // Turn off Alarm
7778 // Using a debugger, I notice the following masking/setting
7779 // of bits in Status Register B, by setting Reg B to
7780 // a few values and getting its value after INT 1A was called.
7782 // try#1 try#2 try#3 try#4
7783 // before 1111 1101 0111 1101 0010 0000 0010 0010
7784 // after 0100 0101 0101 0101 0000 0000 0000 0010
7786 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7787 // My assumption: RegB = (RegB & 01010111b)
7788 val8 = inb_cmos(0x0b); // Get Status Reg B
7789 // clear clock-halt bit, disable alarm bit
7790 outb_cmos(0x0b, val8 & 0x57); // disable alarm bit
7791 regs.u.r8.ah = 0;
7792 regs.u.r8.al = val8; // val last written to Reg B
7793 ClearCF(iret_addr.flags); // OK
7794 break;
7795 #if BX_PCIBIOS
7796 case 0xb1:
7797 // real mode PCI BIOS functions now handled in assembler code
7798 // this C code handles the error code for information only
7799 if (regs.u.r8.bl == 0xff) {
7800 BX_INFO("PCI BIOS: PCI not present\n");
7801 } else if (regs.u.r8.bl == 0x81) {
7802 BX_INFO("unsupported PCI BIOS function 0x%02x\n", regs.u.r8.al);
7803 } else if (regs.u.r8.bl == 0x83) {
7804 BX_INFO("bad PCI vendor ID %04x\n", regs.u.r16.dx);
7805 } else if (regs.u.r8.bl == 0x86) {
7806 BX_INFO("PCI device %04x:%04x not found\n", regs.u.r16.dx, regs.u.r16.cx);
7808 regs.u.r8.ah = regs.u.r8.bl;
7809 SetCF(iret_addr.flags);
7810 break;
7811 #endif
7813 default:
7814 SetCF(iret_addr.flags); // Unsupported
7818 void
7819 int70_function(regs, ds, iret_addr)
7820 pusha_regs_t regs; // regs pushed from PUSHA instruction
7821 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7822 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7824 // INT 70h: IRQ 8 - CMOS RTC interrupt from periodic or alarm modes
7825 Bit8u registerB = 0, registerC = 0;
7827 // Check which modes are enabled and have occurred.
7828 registerB = inb_cmos( 0xB );
7829 registerC = inb_cmos( 0xC );
7831 if( ( registerB & 0x60 ) != 0 ) {
7832 if( ( registerC & 0x20 ) != 0 ) {
7833 // Handle Alarm Interrupt.
7834 ASM_START
7836 int #0x4a
7838 ASM_END
7840 if( ( registerC & 0x40 ) != 0 ) {
7841 // Handle Periodic Interrupt.
7843 if( read_byte( 0x40, 0xA0 ) != 0 ) {
7844 // Wait Interval (Int 15, AH=83) active.
7845 Bit32u time, toggle;
7847 time = read_dword( 0x40, 0x9C ); // Time left in microseconds.
7848 if( time < 0x3D1 ) {
7849 // Done waiting.
7850 Bit16u segment, offset;
7852 segment = read_word( 0x40, 0x98 );
7853 offset = read_word( 0x40, 0x9A );
7854 write_byte( 0x40, 0xA0, 0 ); // Turn of status byte.
7855 outb_cmos( 0xB, registerB & 0x37 ); // Clear the Periodic Interrupt.
7856 write_byte(segment, offset, read_byte(segment, offset) | 0x80 ); // Write to specified flag byte.
7857 } else {
7858 // Continue waiting.
7859 time -= 0x3D1;
7860 write_dword( 0x40, 0x9C, time );
7866 ASM_START
7867 call eoi_both_pics
7868 ASM_END
7872 ASM_START
7873 ;------------------------------------------
7874 ;- INT74h : PS/2 mouse hardware interrupt -
7875 ;------------------------------------------
7876 int74_handler:
7878 pusha
7879 push ds ;; save DS
7880 push #0x00 ;; placeholder for status
7881 push #0x00 ;; placeholder for X
7882 push #0x00 ;; placeholder for Y
7883 push #0x00 ;; placeholder for Z
7884 push #0x00 ;; placeholder for make_far_call boolean
7885 call _int74_function
7886 pop cx ;; remove make_far_call from stack
7887 jcxz int74_done
7889 ;; make far call to EBDA:0022
7890 push #0x00
7891 pop ds
7892 push 0x040E ;; push 0000:040E (opcodes 0xff, 0x36, 0x0E, 0x04)
7893 pop ds
7894 //CALL_EP(0x0022) ;; call far routine (call_Ep DS:0022 :opcodes 0xff, 0x1e, 0x22, 0x00)
7895 call far ptr[0x22]
7896 int74_done:
7898 call eoi_both_pics
7899 add sp, #8 ;; pop status, x, y, z
7901 pop ds ;; restore DS
7902 popa
7903 iret
7906 ;; This will perform an IRET, but will retain value of current CF
7907 ;; by altering flags on stack. Better than RETF #02.
7908 iret_modify_cf:
7909 jc carry_set
7910 push bp
7911 mov bp, sp
7912 and BYTE [bp + 0x06], #0xfe
7913 pop bp
7914 iret
7915 carry_set:
7916 push bp
7917 mov bp, sp
7918 or BYTE [bp + 0x06], #0x01
7919 pop bp
7920 iret
7923 ;----------------------
7924 ;- INT13h (relocated) -
7925 ;----------------------
7927 ; int13_relocated is a little bit messed up since I played with it
7928 ; I have to rewrite it:
7929 ; - call a function that detect which function to call
7930 ; - make all called C function get the same parameters list
7932 int13_relocated:
7934 #if BX_ELTORITO_BOOT
7935 ;; check for an eltorito function
7936 cmp ah,#0x4a
7937 jb int13_not_eltorito
7938 cmp ah,#0x4d
7939 ja int13_not_eltorito
7941 pusha
7942 push es
7943 push ds
7944 push ss
7945 pop ds
7947 push #int13_out
7948 jmp _int13_eltorito ;; ELDX not used
7950 int13_not_eltorito:
7951 push ax
7952 push bx
7953 push cx
7954 push dx
7956 ;; check if emulation active
7957 call _cdemu_isactive
7958 cmp al,#0x00
7959 je int13_cdemu_inactive
7961 ;; check if access to the emulated drive
7962 call _cdemu_emulated_drive
7963 pop dx
7964 push dx
7965 cmp al,dl ;; int13 on emulated drive
7966 jne int13_nocdemu
7968 pop dx
7969 pop cx
7970 pop bx
7971 pop ax
7973 pusha
7974 push es
7975 push ds
7976 push ss
7977 pop ds
7979 push #int13_out
7980 jmp _int13_cdemu ;; ELDX not used
7982 int13_nocdemu:
7983 and dl,#0xE0 ;; mask to get device class, including cdroms
7984 cmp al,dl ;; al is 0x00 or 0x80
7985 jne int13_cdemu_inactive ;; inactive for device class
7987 pop dx
7988 pop cx
7989 pop bx
7990 pop ax
7992 push ax
7993 push cx
7994 push dx
7995 push bx
7997 dec dl ;; real drive is dl - 1
7998 jmp int13_legacy
8000 int13_cdemu_inactive:
8001 pop dx
8002 pop cx
8003 pop bx
8004 pop ax
8006 #endif // BX_ELTORITO_BOOT
8008 int13_noeltorito:
8010 push ax
8011 push cx
8012 push dx
8013 push bx
8015 int13_legacy:
8017 push dx ;; push eltorito value of dx instead of sp
8019 push bp
8020 push si
8021 push di
8023 push es
8024 push ds
8025 push ss
8026 pop ds
8028 ;; now the 16-bit registers can be restored with:
8029 ;; pop ds; pop es; popa; iret
8030 ;; arguments passed to functions should be
8031 ;; DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS
8033 test dl, #0x80
8034 jnz int13_notfloppy
8036 push #int13_out
8037 jmp _int13_diskette_function
8039 int13_notfloppy:
8041 #if BX_USE_ATADRV
8043 cmp dl, #0xE0
8044 jb int13_notcdrom
8046 // ebx is modified: BSD 5.2.1 boot loader problem
8047 // someone should figure out which 32 bit register that actually are used
8049 shr ebx, #16
8050 push bx
8052 call _int13_cdrom
8054 pop bx
8055 shl ebx, #16
8057 jmp int13_out
8059 int13_notcdrom:
8061 #endif
8063 int13_disk:
8064 call _int13_harddisk
8066 int13_out:
8067 pop ds
8068 pop es
8069 popa
8070 iret
8073 ;----------
8074 ;- INT18h -
8075 ;----------
8076 int18_handler: ;; Boot Failure routing
8077 call _int18_panic_msg
8079 iret
8081 ;----------
8082 ;- INT19h -
8083 ;----------
8084 int19_relocated: ;; Boot function, relocated
8086 ;; int19 was beginning to be really complex, so now it
8087 ;; just calls an C function, that does the work
8088 ;; it returns in BL the boot drive, and in AX the boot segment
8089 ;; the boot segment will be 0x0000 if something has failed
8091 push bp
8092 mov bp, sp
8094 ;; drop ds
8095 xor ax, ax
8096 mov ds, ax
8098 ;; 1st boot device
8099 mov ax, #0x0001
8100 push ax
8101 call _int19_function
8102 inc sp
8103 inc sp
8104 ;; bl contains the boot drive
8105 ;; ax contains the boot segment or 0 if failure
8107 test ax, ax ;; if ax is 0 try next boot device
8108 jnz boot_setup
8110 ;; 2nd boot device
8111 mov ax, #0x0002
8112 push ax
8113 call _int19_function
8114 inc sp
8115 inc sp
8116 test ax, ax ;; if ax is 0 try next boot device
8117 jnz boot_setup
8119 ;; 3rd boot device
8120 mov ax, #0x0003
8121 push ax
8122 call _int19_function
8123 inc sp
8124 inc sp
8125 test ax, ax ;; if ax is 0 call int18
8126 jz int18_handler
8128 boot_setup:
8129 mov dl, bl ;; set drive so guest os find it
8130 shl eax, #0x04 ;; convert seg to ip
8131 mov 2[bp], ax ;; set ip
8133 shr eax, #0x04 ;; get cs back
8134 and ax, #0xF000 ;; remove what went in ip
8135 mov 4[bp], ax ;; set cs
8136 xor ax, ax
8137 mov es, ax ;; set es to zero fixes [ 549815 ]
8138 mov [bp], ax ;; set bp to zero
8139 mov ax, #0xaa55 ;; set ok flag
8141 pop bp
8142 iret ;; Beam me up Scotty
8144 ;----------
8145 ;- INT1Ch -
8146 ;----------
8147 int1c_handler: ;; User Timer Tick
8148 iret
8151 ;----------------------
8152 ;- POST: Floppy Drive -
8153 ;----------------------
8154 floppy_drive_post:
8155 mov ax, #0x0000
8156 mov ds, ax
8158 mov al, #0x00
8159 mov 0x043e, al ;; drive 0 & 1 uncalibrated, no interrupt has occurred
8161 mov 0x043f, al ;; diskette motor status: read op, drive0, motors off
8163 mov 0x0440, al ;; diskette motor timeout counter: not active
8164 mov 0x0441, al ;; diskette controller status return code
8166 mov 0x0442, al ;; disk & diskette controller status register 0
8167 mov 0x0443, al ;; diskette controller status register 1
8168 mov 0x0444, al ;; diskette controller status register 2
8169 mov 0x0445, al ;; diskette controller cylinder number
8170 mov 0x0446, al ;; diskette controller head number
8171 mov 0x0447, al ;; diskette controller sector number
8172 mov 0x0448, al ;; diskette controller bytes written
8174 mov 0x048b, al ;; diskette configuration data
8176 ;; -----------------------------------------------------------------
8177 ;; (048F) diskette controller information
8179 mov al, #0x10 ;; get CMOS diskette drive type
8180 out 0x70, AL
8181 in AL, 0x71
8182 mov ah, al ;; save byte to AH
8184 look_drive0:
8185 shr al, #4 ;; look at top 4 bits for drive 0
8186 jz f0_missing ;; jump if no drive0
8187 mov bl, #0x07 ;; drive0 determined, multi-rate, has changed line
8188 jmp look_drive1
8189 f0_missing:
8190 mov bl, #0x00 ;; no drive0
8192 look_drive1:
8193 mov al, ah ;; restore from AH
8194 and al, #0x0f ;; look at bottom 4 bits for drive 1
8195 jz f1_missing ;; jump if no drive1
8196 or bl, #0x70 ;; drive1 determined, multi-rate, has changed line
8197 f1_missing:
8198 ;; leave high bits in BL zerod
8199 mov 0x048f, bl ;; put new val in BDA (diskette controller information)
8200 ;; -----------------------------------------------------------------
8202 mov al, #0x00
8203 mov 0x0490, al ;; diskette 0 media state
8204 mov 0x0491, al ;; diskette 1 media state
8206 ;; diskette 0,1 operational starting state
8207 ;; drive type has not been determined,
8208 ;; has no changed detection line
8209 mov 0x0492, al
8210 mov 0x0493, al
8212 mov 0x0494, al ;; diskette 0 current cylinder
8213 mov 0x0495, al ;; diskette 1 current cylinder
8215 mov al, #0x02
8216 out #0x0a, al ;; clear DMA-1 channel 2 mask bit
8218 SET_INT_VECTOR(0x1E, #0xF000, #diskette_param_table2)
8219 SET_INT_VECTOR(0x40, #0xF000, #int13_diskette)
8220 SET_INT_VECTOR(0x0E, #0xF000, #int0e_handler) ;; IRQ 6
8225 ;--------------------
8226 ;- POST: HARD DRIVE -
8227 ;--------------------
8228 ; relocated here because the primary POST area isnt big enough.
8229 hard_drive_post:
8230 // IRQ 14 = INT 76h
8231 // INT 76h calls INT 15h function ax=9100
8233 mov al, #0x0a ; 0000 1010 = reserved, disable IRQ 14
8234 mov dx, #0x03f6
8235 out dx, al
8237 mov ax, #0x0000
8238 mov ds, ax
8239 mov 0x0474, al /* hard disk status of last operation */
8240 mov 0x0477, al /* hard disk port offset (XT only ???) */
8241 mov 0x048c, al /* hard disk status register */
8242 mov 0x048d, al /* hard disk error register */
8243 mov 0x048e, al /* hard disk task complete flag */
8244 mov al, #0x01
8245 mov 0x0475, al /* hard disk number attached */
8246 mov al, #0xc0
8247 mov 0x0476, al /* hard disk control byte */
8248 SET_INT_VECTOR(0x13, #0xF000, #int13_handler)
8249 SET_INT_VECTOR(0x76, #0xF000, #int76_handler)
8250 ;; INT 41h: hard disk 0 configuration pointer
8251 ;; INT 46h: hard disk 1 configuration pointer
8252 SET_INT_VECTOR(0x41, #EBDA_SEG, #0x003D)
8253 SET_INT_VECTOR(0x46, #EBDA_SEG, #0x004D)
8255 ;; move disk geometry data from CMOS to EBDA disk parameter table(s)
8256 mov al, #0x12
8257 out #0x70, al
8258 in al, #0x71
8259 and al, #0xf0
8260 cmp al, #0xf0
8261 je post_d0_extended
8262 jmp check_for_hd1
8263 post_d0_extended:
8264 mov al, #0x19
8265 out #0x70, al
8266 in al, #0x71
8267 cmp al, #47 ;; decimal 47 - user definable
8268 je post_d0_type47
8269 HALT(__LINE__)
8270 post_d0_type47:
8271 ;; CMOS purpose param table offset
8272 ;; 1b cylinders low 0
8273 ;; 1c cylinders high 1
8274 ;; 1d heads 2
8275 ;; 1e write pre-comp low 5
8276 ;; 1f write pre-comp high 6
8277 ;; 20 retries/bad map/heads>8 8
8278 ;; 21 landing zone low C
8279 ;; 22 landing zone high D
8280 ;; 23 sectors/track E
8282 mov ax, #EBDA_SEG
8283 mov ds, ax
8285 ;;; Filling EBDA table for hard disk 0.
8286 mov al, #0x1f
8287 out #0x70, al
8288 in al, #0x71
8289 mov ah, al
8290 mov al, #0x1e
8291 out #0x70, al
8292 in al, #0x71
8293 mov (0x003d + 0x05), ax ;; write precomp word
8295 mov al, #0x20
8296 out #0x70, al
8297 in al, #0x71
8298 mov (0x003d + 0x08), al ;; drive control byte
8300 mov al, #0x22
8301 out #0x70, al
8302 in al, #0x71
8303 mov ah, al
8304 mov al, #0x21
8305 out #0x70, al
8306 in al, #0x71
8307 mov (0x003d + 0x0C), ax ;; landing zone word
8309 mov al, #0x1c ;; get cylinders word in AX
8310 out #0x70, al
8311 in al, #0x71 ;; high byte
8312 mov ah, al
8313 mov al, #0x1b
8314 out #0x70, al
8315 in al, #0x71 ;; low byte
8316 mov bx, ax ;; BX = cylinders
8318 mov al, #0x1d
8319 out #0x70, al
8320 in al, #0x71
8321 mov cl, al ;; CL = heads
8323 mov al, #0x23
8324 out #0x70, al
8325 in al, #0x71
8326 mov dl, al ;; DL = sectors
8328 cmp bx, #1024
8329 jnbe hd0_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8331 hd0_post_physical_chs:
8332 ;; no logical CHS mapping used, just physical CHS
8333 ;; use Standard Fixed Disk Parameter Table (FDPT)
8334 mov (0x003d + 0x00), bx ;; number of physical cylinders
8335 mov (0x003d + 0x02), cl ;; number of physical heads
8336 mov (0x003d + 0x0E), dl ;; number of physical sectors
8337 jmp check_for_hd1
8339 hd0_post_logical_chs:
8340 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8341 mov (0x003d + 0x09), bx ;; number of physical cylinders
8342 mov (0x003d + 0x0b), cl ;; number of physical heads
8343 mov (0x003d + 0x04), dl ;; number of physical sectors
8344 mov (0x003d + 0x0e), dl ;; number of logical sectors (same)
8345 mov al, #0xa0
8346 mov (0x003d + 0x03), al ;; A0h signature, indicates translated table
8348 cmp bx, #2048
8349 jnbe hd0_post_above_2048
8350 ;; 1024 < c <= 2048 cylinders
8351 shr bx, #0x01
8352 shl cl, #0x01
8353 jmp hd0_post_store_logical
8355 hd0_post_above_2048:
8356 cmp bx, #4096
8357 jnbe hd0_post_above_4096
8358 ;; 2048 < c <= 4096 cylinders
8359 shr bx, #0x02
8360 shl cl, #0x02
8361 jmp hd0_post_store_logical
8363 hd0_post_above_4096:
8364 cmp bx, #8192
8365 jnbe hd0_post_above_8192
8366 ;; 4096 < c <= 8192 cylinders
8367 shr bx, #0x03
8368 shl cl, #0x03
8369 jmp hd0_post_store_logical
8371 hd0_post_above_8192:
8372 ;; 8192 < c <= 16384 cylinders
8373 shr bx, #0x04
8374 shl cl, #0x04
8376 hd0_post_store_logical:
8377 mov (0x003d + 0x00), bx ;; number of physical cylinders
8378 mov (0x003d + 0x02), cl ;; number of physical heads
8379 ;; checksum
8380 mov cl, #0x0f ;; repeat count
8381 mov si, #0x003d ;; offset to disk0 FDPT
8382 mov al, #0x00 ;; sum
8383 hd0_post_checksum_loop:
8384 add al, [si]
8385 inc si
8386 dec cl
8387 jnz hd0_post_checksum_loop
8388 not al ;; now take 2s complement
8389 inc al
8390 mov [si], al
8391 ;;; Done filling EBDA table for hard disk 0.
8394 check_for_hd1:
8395 ;; is there really a second hard disk? if not, return now
8396 mov al, #0x12
8397 out #0x70, al
8398 in al, #0x71
8399 and al, #0x0f
8400 jnz post_d1_exists
8402 post_d1_exists:
8403 ;; check that the hd type is really 0x0f.
8404 cmp al, #0x0f
8405 jz post_d1_extended
8406 HALT(__LINE__)
8407 post_d1_extended:
8408 ;; check that the extended type is 47 - user definable
8409 mov al, #0x1a
8410 out #0x70, al
8411 in al, #0x71
8412 cmp al, #47 ;; decimal 47 - user definable
8413 je post_d1_type47
8414 HALT(__LINE__)
8415 post_d1_type47:
8416 ;; Table for disk1.
8417 ;; CMOS purpose param table offset
8418 ;; 0x24 cylinders low 0
8419 ;; 0x25 cylinders high 1
8420 ;; 0x26 heads 2
8421 ;; 0x27 write pre-comp low 5
8422 ;; 0x28 write pre-comp high 6
8423 ;; 0x29 heads>8 8
8424 ;; 0x2a landing zone low C
8425 ;; 0x2b landing zone high D
8426 ;; 0x2c sectors/track E
8427 ;;; Fill EBDA table for hard disk 1.
8428 mov ax, #EBDA_SEG
8429 mov ds, ax
8430 mov al, #0x28
8431 out #0x70, al
8432 in al, #0x71
8433 mov ah, al
8434 mov al, #0x27
8435 out #0x70, al
8436 in al, #0x71
8437 mov (0x004d + 0x05), ax ;; write precomp word
8439 mov al, #0x29
8440 out #0x70, al
8441 in al, #0x71
8442 mov (0x004d + 0x08), al ;; drive control byte
8444 mov al, #0x2b
8445 out #0x70, al
8446 in al, #0x71
8447 mov ah, al
8448 mov al, #0x2a
8449 out #0x70, al
8450 in al, #0x71
8451 mov (0x004d + 0x0C), ax ;; landing zone word
8453 mov al, #0x25 ;; get cylinders word in AX
8454 out #0x70, al
8455 in al, #0x71 ;; high byte
8456 mov ah, al
8457 mov al, #0x24
8458 out #0x70, al
8459 in al, #0x71 ;; low byte
8460 mov bx, ax ;; BX = cylinders
8462 mov al, #0x26
8463 out #0x70, al
8464 in al, #0x71
8465 mov cl, al ;; CL = heads
8467 mov al, #0x2c
8468 out #0x70, al
8469 in al, #0x71
8470 mov dl, al ;; DL = sectors
8472 cmp bx, #1024
8473 jnbe hd1_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8475 hd1_post_physical_chs:
8476 ;; no logical CHS mapping used, just physical CHS
8477 ;; use Standard Fixed Disk Parameter Table (FDPT)
8478 mov (0x004d + 0x00), bx ;; number of physical cylinders
8479 mov (0x004d + 0x02), cl ;; number of physical heads
8480 mov (0x004d + 0x0E), dl ;; number of physical sectors
8483 hd1_post_logical_chs:
8484 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8485 mov (0x004d + 0x09), bx ;; number of physical cylinders
8486 mov (0x004d + 0x0b), cl ;; number of physical heads
8487 mov (0x004d + 0x04), dl ;; number of physical sectors
8488 mov (0x004d + 0x0e), dl ;; number of logical sectors (same)
8489 mov al, #0xa0
8490 mov (0x004d + 0x03), al ;; A0h signature, indicates translated table
8492 cmp bx, #2048
8493 jnbe hd1_post_above_2048
8494 ;; 1024 < c <= 2048 cylinders
8495 shr bx, #0x01
8496 shl cl, #0x01
8497 jmp hd1_post_store_logical
8499 hd1_post_above_2048:
8500 cmp bx, #4096
8501 jnbe hd1_post_above_4096
8502 ;; 2048 < c <= 4096 cylinders
8503 shr bx, #0x02
8504 shl cl, #0x02
8505 jmp hd1_post_store_logical
8507 hd1_post_above_4096:
8508 cmp bx, #8192
8509 jnbe hd1_post_above_8192
8510 ;; 4096 < c <= 8192 cylinders
8511 shr bx, #0x03
8512 shl cl, #0x03
8513 jmp hd1_post_store_logical
8515 hd1_post_above_8192:
8516 ;; 8192 < c <= 16384 cylinders
8517 shr bx, #0x04
8518 shl cl, #0x04
8520 hd1_post_store_logical:
8521 mov (0x004d + 0x00), bx ;; number of physical cylinders
8522 mov (0x004d + 0x02), cl ;; number of physical heads
8523 ;; checksum
8524 mov cl, #0x0f ;; repeat count
8525 mov si, #0x004d ;; offset to disk0 FDPT
8526 mov al, #0x00 ;; sum
8527 hd1_post_checksum_loop:
8528 add al, [si]
8529 inc si
8530 dec cl
8531 jnz hd1_post_checksum_loop
8532 not al ;; now take 2s complement
8533 inc al
8534 mov [si], al
8535 ;;; Done filling EBDA table for hard disk 1.
8539 ;--------------------
8540 ;- POST: EBDA segment
8541 ;--------------------
8542 ; relocated here because the primary POST area isnt big enough.
8543 ebda_post:
8544 #if BX_USE_EBDA
8545 mov ax, #EBDA_SEG
8546 mov ds, ax
8547 mov byte ptr [0x0], #EBDA_SIZE
8548 #endif
8549 xor ax, ax ; mov EBDA seg into 40E
8550 mov ds, ax
8551 mov word ptr [0x40E], #EBDA_SEG
8552 ret;;
8554 ;--------------------
8555 ;- POST: EOI + jmp via [0x40:67)
8556 ;--------------------
8557 ; relocated here because the primary POST area isnt big enough.
8558 eoi_jmp_post:
8559 call eoi_both_pics
8561 xor ax, ax
8562 mov ds, ax
8564 jmp far ptr [0x467]
8567 ;--------------------
8568 eoi_both_pics:
8569 mov al, #0x20
8570 out #0xA0, al ;; slave PIC EOI
8571 eoi_master_pic:
8572 mov al, #0x20
8573 out #0x20, al ;; master PIC EOI
8576 ;--------------------
8577 BcdToBin:
8578 ;; in: AL in BCD format
8579 ;; out: AL in binary format, AH will always be 0
8580 ;; trashes BX
8581 mov bl, al
8582 and bl, #0x0f ;; bl has low digit
8583 shr al, #4 ;; al has high digit
8584 mov bh, #10
8585 mul al, bh ;; multiply high digit by 10 (result in AX)
8586 add al, bl ;; then add low digit
8589 ;--------------------
8590 timer_tick_post:
8591 ;; Setup the Timer Ticks Count (0x46C:dword) and
8592 ;; Timer Ticks Roller Flag (0x470:byte)
8593 ;; The Timer Ticks Count needs to be set according to
8594 ;; the current CMOS time, as if ticks have been occurring
8595 ;; at 18.2hz since midnight up to this point. Calculating
8596 ;; this is a little complicated. Here are the factors I gather
8597 ;; regarding this. 14,318,180 hz was the original clock speed,
8598 ;; chosen so it could be divided by either 3 to drive the 5Mhz CPU
8599 ;; at the time, or 4 to drive the CGA video adapter. The div3
8600 ;; source was divided again by 4 to feed a 1.193Mhz signal to
8601 ;; the timer. With a maximum 16bit timer count, this is again
8602 ;; divided down by 65536 to 18.2hz.
8604 ;; 14,318,180 Hz clock
8605 ;; /3 = 4,772,726 Hz fed to orginal 5Mhz CPU
8606 ;; /4 = 1,193,181 Hz fed to timer
8607 ;; /65536 (maximum timer count) = 18.20650736 ticks/second
8608 ;; 1 second = 18.20650736 ticks
8609 ;; 1 minute = 1092.390442 ticks
8610 ;; 1 hour = 65543.42651 ticks
8612 ;; Given the values in the CMOS clock, one could calculate
8613 ;; the number of ticks by the following:
8614 ;; ticks = (BcdToBin(seconds) * 18.206507) +
8615 ;; (BcdToBin(minutes) * 1092.3904)
8616 ;; (BcdToBin(hours) * 65543.427)
8617 ;; To get a little more accuracy, since Im using integer
8618 ;; arithmatic, I use:
8619 ;; ticks = (BcdToBin(seconds) * 18206507) / 1000000 +
8620 ;; (BcdToBin(minutes) * 10923904) / 10000 +
8621 ;; (BcdToBin(hours) * 65543427) / 1000
8623 ;; assuming DS=0000
8625 ;; get CMOS seconds
8626 xor eax, eax ;; clear EAX
8627 mov al, #0x00
8628 out #0x70, al
8629 in al, #0x71 ;; AL has CMOS seconds in BCD
8630 call BcdToBin ;; EAX now has seconds in binary
8631 mov edx, #18206507
8632 mul eax, edx
8633 mov ebx, #1000000
8634 xor edx, edx
8635 div eax, ebx
8636 mov ecx, eax ;; ECX will accumulate total ticks
8638 ;; get CMOS minutes
8639 xor eax, eax ;; clear EAX
8640 mov al, #0x02
8641 out #0x70, al
8642 in al, #0x71 ;; AL has CMOS minutes in BCD
8643 call BcdToBin ;; EAX now has minutes in binary
8644 mov edx, #10923904
8645 mul eax, edx
8646 mov ebx, #10000
8647 xor edx, edx
8648 div eax, ebx
8649 add ecx, eax ;; add to total ticks
8651 ;; get CMOS hours
8652 xor eax, eax ;; clear EAX
8653 mov al, #0x04
8654 out #0x70, al
8655 in al, #0x71 ;; AL has CMOS hours in BCD
8656 call BcdToBin ;; EAX now has hours in binary
8657 mov edx, #65543427
8658 mul eax, edx
8659 mov ebx, #1000
8660 xor edx, edx
8661 div eax, ebx
8662 add ecx, eax ;; add to total ticks
8664 mov 0x46C, ecx ;; Timer Ticks Count
8665 xor al, al
8666 mov 0x470, al ;; Timer Ticks Rollover Flag
8669 ;--------------------
8670 int76_handler:
8671 ;; record completion in BIOS task complete flag
8672 push ax
8673 push ds
8674 mov ax, #0x0040
8675 mov ds, ax
8676 mov 0x008E, #0xff
8677 call eoi_both_pics
8678 pop ds
8679 pop ax
8680 iret
8683 ;--------------------
8684 #if BX_APM
8686 use32 386
8687 #define APM_PROT32
8688 #include "apmbios.S"
8690 use16 386
8691 #define APM_PROT16
8692 #include "apmbios.S"
8694 #define APM_REAL
8695 #include "apmbios.S"
8697 #endif
8699 ;--------------------
8700 #if BX_PCIBIOS
8701 use32 386
8702 .align 16
8703 bios32_structure:
8704 db 0x5f, 0x33, 0x32, 0x5f ;; "_32_" signature
8705 dw bios32_entry_point, 0xf ;; 32 bit physical address
8706 db 0 ;; revision level
8707 ;; length in paragraphs and checksum stored in a word to prevent errors
8708 dw (~(((bios32_entry_point >> 8) + (bios32_entry_point & 0xff) + 0x32) \
8709 & 0xff) << 8) + 0x01
8710 db 0,0,0,0,0 ;; reserved
8712 .align 16
8713 bios32_entry_point:
8714 pushf
8715 cmp eax, #0x49435024 ;; "$PCI"
8716 jne unknown_service
8717 mov eax, #0x80000000
8718 mov dx, #0x0cf8
8719 out dx, eax
8720 mov dx, #0x0cfc
8721 in eax, dx
8722 #ifdef PCI_FIXED_HOST_BRIDGE
8723 cmp eax, #PCI_FIXED_HOST_BRIDGE
8724 jne unknown_service
8725 #else
8726 ;; say ok if a device is present
8727 cmp eax, #0xffffffff
8728 je unknown_service
8729 #endif
8730 mov ebx, #0x000f0000
8731 mov ecx, #0
8732 mov edx, #pcibios_protected
8733 xor al, al
8734 jmp bios32_end
8735 unknown_service:
8736 mov al, #0x80
8737 bios32_end:
8738 popf
8739 retf
8741 .align 16
8742 pcibios_protected:
8743 pushf
8745 push esi
8746 push edi
8747 cmp al, #0x01 ;; installation check
8748 jne pci_pro_f02
8749 mov bx, #0x0210
8750 mov cx, #0
8751 mov edx, #0x20494350 ;; "PCI "
8752 mov al, #0x01
8753 jmp pci_pro_ok
8754 pci_pro_f02: ;; find pci device
8755 cmp al, #0x02
8756 jne pci_pro_f08
8757 shl ecx, #16
8758 mov cx, dx
8759 mov bx, #0x0000
8760 mov di, #0x00
8761 pci_pro_devloop:
8762 call pci_pro_select_reg
8763 mov dx, #0x0cfc
8764 in eax, dx
8765 cmp eax, ecx
8766 jne pci_pro_nextdev
8767 cmp si, #0
8768 je pci_pro_ok
8769 dec si
8770 pci_pro_nextdev:
8771 inc bx
8772 cmp bx, #0x0100
8773 jne pci_pro_devloop
8774 mov ah, #0x86
8775 jmp pci_pro_fail
8776 pci_pro_f08: ;; read configuration byte
8777 cmp al, #0x08
8778 jne pci_pro_f09
8779 call pci_pro_select_reg
8780 push edx
8781 mov dx, di
8782 and dx, #0x03
8783 add dx, #0x0cfc
8784 in al, dx
8785 pop edx
8786 mov cl, al
8787 jmp pci_pro_ok
8788 pci_pro_f09: ;; read configuration word
8789 cmp al, #0x09
8790 jne pci_pro_f0a
8791 call pci_pro_select_reg
8792 push edx
8793 mov dx, di
8794 and dx, #0x02
8795 add dx, #0x0cfc
8796 in ax, dx
8797 pop edx
8798 mov cx, ax
8799 jmp pci_pro_ok
8800 pci_pro_f0a: ;; read configuration dword
8801 cmp al, #0x0a
8802 jne pci_pro_f0b
8803 call pci_pro_select_reg
8804 push edx
8805 mov dx, #0x0cfc
8806 in eax, dx
8807 pop edx
8808 mov ecx, eax
8809 jmp pci_pro_ok
8810 pci_pro_f0b: ;; write configuration byte
8811 cmp al, #0x0b
8812 jne pci_pro_f0c
8813 call pci_pro_select_reg
8814 push edx
8815 mov dx, di
8816 and dx, #0x03
8817 add dx, #0x0cfc
8818 mov al, cl
8819 out dx, al
8820 pop edx
8821 jmp pci_pro_ok
8822 pci_pro_f0c: ;; write configuration word
8823 cmp al, #0x0c
8824 jne pci_pro_f0d
8825 call pci_pro_select_reg
8826 push edx
8827 mov dx, di
8828 and dx, #0x02
8829 add dx, #0x0cfc
8830 mov ax, cx
8831 out dx, ax
8832 pop edx
8833 jmp pci_pro_ok
8834 pci_pro_f0d: ;; write configuration dword
8835 cmp al, #0x0d
8836 jne pci_pro_unknown
8837 call pci_pro_select_reg
8838 push edx
8839 mov dx, #0x0cfc
8840 mov eax, ecx
8841 out dx, eax
8842 pop edx
8843 jmp pci_pro_ok
8844 pci_pro_unknown:
8845 mov ah, #0x81
8846 pci_pro_fail:
8847 pop edi
8848 pop esi
8850 popf
8852 retf
8853 pci_pro_ok:
8854 xor ah, ah
8855 pop edi
8856 pop esi
8858 popf
8860 retf
8862 pci_pro_select_reg:
8863 push edx
8864 mov eax, #0x800000
8865 mov ax, bx
8866 shl eax, #8
8867 and di, #0xff
8868 or ax, di
8869 and al, #0xfc
8870 mov dx, #0x0cf8
8871 out dx, eax
8872 pop edx
8875 use16 386
8877 pcibios_real:
8878 push eax
8879 push dx
8880 mov eax, #0x80000000
8881 mov dx, #0x0cf8
8882 out dx, eax
8883 mov dx, #0x0cfc
8884 in eax, dx
8885 #ifdef PCI_FIXED_HOST_BRIDGE
8886 cmp eax, #PCI_FIXED_HOST_BRIDGE
8887 je pci_present
8888 #else
8889 ;; say ok if a device is present
8890 cmp eax, #0xffffffff
8891 jne pci_present
8892 #endif
8893 pop dx
8894 pop eax
8895 mov ah, #0xff
8898 pci_present:
8899 pop dx
8900 pop eax
8901 cmp al, #0x01 ;; installation check
8902 jne pci_real_f02
8903 mov ax, #0x0001
8904 mov bx, #0x0210
8905 mov cx, #0
8906 mov edx, #0x20494350 ;; "PCI "
8907 mov edi, #0xf0000
8908 mov di, #pcibios_protected
8911 pci_real_f02: ;; find pci device
8912 push esi
8913 push edi
8914 cmp al, #0x02
8915 jne pci_real_f08
8916 shl ecx, #16
8917 mov cx, dx
8918 mov bx, #0x0000
8919 mov di, #0x00
8920 pci_real_devloop:
8921 call pci_real_select_reg
8922 mov dx, #0x0cfc
8923 in eax, dx
8924 cmp eax, ecx
8925 jne pci_real_nextdev
8926 cmp si, #0
8927 je pci_real_ok
8928 dec si
8929 pci_real_nextdev:
8930 inc bx
8931 cmp bx, #0x0100
8932 jne pci_real_devloop
8933 mov dx, cx
8934 shr ecx, #16
8935 mov ah, #0x86
8936 jmp pci_real_fail
8937 pci_real_f08: ;; read configuration byte
8938 cmp al, #0x08
8939 jne pci_real_f09
8940 call pci_real_select_reg
8941 push dx
8942 mov dx, di
8943 and dx, #0x03
8944 add dx, #0x0cfc
8945 in al, dx
8946 pop dx
8947 mov cl, al
8948 jmp pci_real_ok
8949 pci_real_f09: ;; read configuration word
8950 cmp al, #0x09
8951 jne pci_real_f0a
8952 call pci_real_select_reg
8953 push dx
8954 mov dx, di
8955 and dx, #0x02
8956 add dx, #0x0cfc
8957 in ax, dx
8958 pop dx
8959 mov cx, ax
8960 jmp pci_real_ok
8961 pci_real_f0a: ;; read configuration dword
8962 cmp al, #0x0a
8963 jne pci_real_f0b
8964 call pci_real_select_reg
8965 push dx
8966 mov dx, #0x0cfc
8967 in eax, dx
8968 pop dx
8969 mov ecx, eax
8970 jmp pci_real_ok
8971 pci_real_f0b: ;; write configuration byte
8972 cmp al, #0x0b
8973 jne pci_real_f0c
8974 call pci_real_select_reg
8975 push dx
8976 mov dx, di
8977 and dx, #0x03
8978 add dx, #0x0cfc
8979 mov al, cl
8980 out dx, al
8981 pop dx
8982 jmp pci_real_ok
8983 pci_real_f0c: ;; write configuration word
8984 cmp al, #0x0c
8985 jne pci_real_f0d
8986 call pci_real_select_reg
8987 push dx
8988 mov dx, di
8989 and dx, #0x02
8990 add dx, #0x0cfc
8991 mov ax, cx
8992 out dx, ax
8993 pop dx
8994 jmp pci_real_ok
8995 pci_real_f0d: ;; write configuration dword
8996 cmp al, #0x0d
8997 jne pci_real_unknown
8998 call pci_real_select_reg
8999 push dx
9000 mov dx, #0x0cfc
9001 mov eax, ecx
9002 out dx, eax
9003 pop dx
9004 jmp pci_real_ok
9005 pci_real_unknown:
9006 mov ah, #0x81
9007 pci_real_fail:
9008 pop edi
9009 pop esi
9012 pci_real_ok:
9013 xor ah, ah
9014 pop edi
9015 pop esi
9019 pci_real_select_reg:
9020 push dx
9021 mov eax, #0x800000
9022 mov ax, bx
9023 shl eax, #8
9024 and di, #0xff
9025 or ax, di
9026 and al, #0xfc
9027 mov dx, #0x0cf8
9028 out dx, eax
9029 pop dx
9032 .align 16
9033 pci_routing_table_structure:
9034 db 0x24, 0x50, 0x49, 0x52 ;; "$PIR" signature
9035 db 0, 1 ;; version
9036 dw 32 + (6 * 16) ;; table size
9037 db 0 ;; PCI interrupt router bus
9038 db 0x08 ;; PCI interrupt router DevFunc
9039 dw 0x0000 ;; PCI exclusive IRQs
9040 dw 0x8086 ;; compatible PCI interrupt router vendor ID
9041 dw 0x7000 ;; compatible PCI interrupt router device ID
9042 dw 0,0 ;; Miniport data
9043 db 0,0,0,0,0,0,0,0,0,0,0 ;; reserved
9044 db 0x07 ;; checksum
9045 ;; first slot entry PCI-to-ISA (embedded)
9046 db 0 ;; pci bus number
9047 db 0x08 ;; pci device number (bit 7-3)
9048 db 0x60 ;; link value INTA#: pointer into PCI2ISA config space
9049 dw 0xdef8 ;; IRQ bitmap INTA#
9050 db 0x61 ;; link value INTB#
9051 dw 0xdef8 ;; IRQ bitmap INTB#
9052 db 0x62 ;; link value INTC#
9053 dw 0xdef8 ;; IRQ bitmap INTC#
9054 db 0x63 ;; link value INTD#
9055 dw 0xdef8 ;; IRQ bitmap INTD#
9056 db 0 ;; physical slot (0 = embedded)
9057 db 0 ;; reserved
9058 ;; second slot entry: 1st PCI slot
9059 db 0 ;; pci bus number
9060 db 0x10 ;; pci device number (bit 7-3)
9061 db 0x61 ;; link value INTA#
9062 dw 0xdef8 ;; IRQ bitmap INTA#
9063 db 0x62 ;; link value INTB#
9064 dw 0xdef8 ;; IRQ bitmap INTB#
9065 db 0x63 ;; link value INTC#
9066 dw 0xdef8 ;; IRQ bitmap INTC#
9067 db 0x60 ;; link value INTD#
9068 dw 0xdef8 ;; IRQ bitmap INTD#
9069 db 1 ;; physical slot (0 = embedded)
9070 db 0 ;; reserved
9071 ;; third slot entry: 2nd PCI slot
9072 db 0 ;; pci bus number
9073 db 0x18 ;; pci device number (bit 7-3)
9074 db 0x62 ;; link value INTA#
9075 dw 0xdef8 ;; IRQ bitmap INTA#
9076 db 0x63 ;; link value INTB#
9077 dw 0xdef8 ;; IRQ bitmap INTB#
9078 db 0x60 ;; link value INTC#
9079 dw 0xdef8 ;; IRQ bitmap INTC#
9080 db 0x61 ;; link value INTD#
9081 dw 0xdef8 ;; IRQ bitmap INTD#
9082 db 2 ;; physical slot (0 = embedded)
9083 db 0 ;; reserved
9084 ;; 4th slot entry: 3rd PCI slot
9085 db 0 ;; pci bus number
9086 db 0x20 ;; pci device number (bit 7-3)
9087 db 0x63 ;; link value INTA#
9088 dw 0xdef8 ;; IRQ bitmap INTA#
9089 db 0x60 ;; link value INTB#
9090 dw 0xdef8 ;; IRQ bitmap INTB#
9091 db 0x61 ;; link value INTC#
9092 dw 0xdef8 ;; IRQ bitmap INTC#
9093 db 0x62 ;; link value INTD#
9094 dw 0xdef8 ;; IRQ bitmap INTD#
9095 db 3 ;; physical slot (0 = embedded)
9096 db 0 ;; reserved
9097 ;; 5th slot entry: 4rd PCI slot
9098 db 0 ;; pci bus number
9099 db 0x28 ;; pci device number (bit 7-3)
9100 db 0x60 ;; link value INTA#
9101 dw 0xdef8 ;; IRQ bitmap INTA#
9102 db 0x61 ;; link value INTB#
9103 dw 0xdef8 ;; IRQ bitmap INTB#
9104 db 0x62 ;; link value INTC#
9105 dw 0xdef8 ;; IRQ bitmap INTC#
9106 db 0x63 ;; link value INTD#
9107 dw 0xdef8 ;; IRQ bitmap INTD#
9108 db 4 ;; physical slot (0 = embedded)
9109 db 0 ;; reserved
9110 ;; 6th slot entry: 5rd PCI slot
9111 db 0 ;; pci bus number
9112 db 0x30 ;; pci device number (bit 7-3)
9113 db 0x61 ;; link value INTA#
9114 dw 0xdef8 ;; IRQ bitmap INTA#
9115 db 0x62 ;; link value INTB#
9116 dw 0xdef8 ;; IRQ bitmap INTB#
9117 db 0x63 ;; link value INTC#
9118 dw 0xdef8 ;; IRQ bitmap INTC#
9119 db 0x60 ;; link value INTD#
9120 dw 0xdef8 ;; IRQ bitmap INTD#
9121 db 5 ;; physical slot (0 = embedded)
9122 db 0 ;; reserved
9124 pci_irq_list:
9125 db 11, 10, 9, 5;
9127 pcibios_init_sel_reg:
9128 push eax
9129 mov eax, #0x800000
9130 mov ax, bx
9131 shl eax, #8
9132 and dl, #0xfc
9133 or al, dl
9134 mov dx, #0x0cf8
9135 out dx, eax
9136 pop eax
9139 pcibios_init_set_elcr:
9140 push ax
9141 push cx
9142 mov dx, #0x04d0
9143 test al, #0x08
9144 jz is_master_pic
9145 inc dx
9146 and al, #0x07
9147 is_master_pic:
9148 mov cl, al
9149 mov bl, #0x01
9150 shl bl, cl
9151 in al, dx
9152 or al, bl
9153 out dx, al
9154 pop cx
9155 pop ax
9158 pcibios_init:
9159 push ds
9160 push bp
9161 mov ax, #0xf000
9162 mov ds, ax
9163 mov dx, #0x04d0 ;; reset ELCR1 + ELCR2
9164 mov al, #0x00
9165 out dx, al
9166 inc dx
9167 out dx, al
9168 mov si, #pci_routing_table_structure
9169 mov bh, [si+8]
9170 mov bl, [si+9]
9171 mov dl, #0x00
9172 call pcibios_init_sel_reg
9173 mov dx, #0x0cfc
9174 in eax, dx
9175 cmp eax, [si+12] ;; check irq router
9176 jne pci_init_end
9177 mov dl, [si+34]
9178 call pcibios_init_sel_reg
9179 push bx ;; save irq router bus + devfunc
9180 mov dx, #0x0cfc
9181 mov ax, #0x8080
9182 out dx, ax ;; reset PIRQ route control
9183 inc dx
9184 inc dx
9185 out dx, ax
9186 mov ax, [si+6]
9187 sub ax, #0x20
9188 shr ax, #4
9189 mov cx, ax
9190 add si, #0x20 ;; set pointer to 1st entry
9191 mov bp, sp
9192 mov ax, #pci_irq_list
9193 push ax
9194 xor ax, ax
9195 push ax
9196 pci_init_loop1:
9197 mov bh, [si]
9198 mov bl, [si+1]
9199 pci_init_loop2:
9200 mov dl, #0x00
9201 call pcibios_init_sel_reg
9202 mov dx, #0x0cfc
9203 in ax, dx
9204 cmp ax, #0xffff
9205 jnz pci_test_int_pin
9206 test bl, #0x07
9207 jz next_pir_entry
9208 jmp next_pci_func
9209 pci_test_int_pin:
9210 mov dl, #0x3c
9211 call pcibios_init_sel_reg
9212 mov dx, #0x0cfd
9213 in al, dx
9214 and al, #0x07
9215 jz next_pci_func
9216 dec al ;; determine pirq reg
9217 mov dl, #0x03
9218 mul al, dl
9219 add al, #0x02
9220 xor ah, ah
9221 mov bx, ax
9222 mov al, [si+bx]
9223 mov dl, al
9224 mov bx, [bp]
9225 call pcibios_init_sel_reg
9226 mov dx, #0x0cfc
9227 and al, #0x03
9228 add dl, al
9229 in al, dx
9230 cmp al, #0x80
9231 jb pirq_found
9232 mov bx, [bp-2] ;; pci irq list pointer
9233 mov al, [bx]
9234 out dx, al
9235 inc bx
9236 mov [bp-2], bx
9237 call pcibios_init_set_elcr
9238 pirq_found:
9239 mov bh, [si]
9240 mov bl, [si+1]
9241 add bl, [bp-3] ;; pci function number
9242 mov dl, #0x3c
9243 call pcibios_init_sel_reg
9244 mov dx, #0x0cfc
9245 out dx, al
9246 next_pci_func:
9247 inc byte ptr[bp-3]
9248 inc bl
9249 test bl, #0x07
9250 jnz pci_init_loop2
9251 next_pir_entry:
9252 add si, #0x10
9253 mov byte ptr[bp-3], #0x00
9254 loop pci_init_loop1
9255 mov sp, bp
9256 pop bx
9257 pci_init_end:
9258 pop bp
9259 pop ds
9261 #endif // BX_PCIBIOS
9263 ; parallel port detection: base address in DX, index in BX, timeout in CL
9264 detect_parport:
9265 push dx
9266 add dx, #2
9267 in al, dx
9268 and al, #0xdf ; clear input mode
9269 out dx, al
9270 pop dx
9271 mov al, #0xaa
9272 out dx, al
9273 in al, dx
9274 cmp al, #0xaa
9275 jne no_parport
9276 push bx
9277 shl bx, #1
9278 mov [bx+0x408], dx ; Parallel I/O address
9279 pop bx
9280 mov [bx+0x478], cl ; Parallel printer timeout
9281 inc bx
9282 no_parport:
9285 ; serial port detection: base address in DX, index in BX, timeout in CL
9286 detect_serial:
9287 push dx
9288 inc dx
9289 mov al, #0x02
9290 out dx, al
9291 in al, dx
9292 cmp al, #0x02
9293 jne no_serial
9294 inc dx
9295 in al, dx
9296 cmp al, #0x02
9297 jne no_serial
9298 dec dx
9299 xor al, al
9300 out dx, al
9301 pop dx
9302 push bx
9303 shl bx, #1
9304 mov [bx+0x400], dx ; Serial I/O address
9305 pop bx
9306 mov [bx+0x47c], cl ; Serial timeout
9307 inc bx
9309 no_serial:
9310 pop dx
9313 rom_checksum:
9314 push ax
9315 push bx
9316 push cx
9317 xor ax, ax
9318 xor bx, bx
9319 xor cx, cx
9320 mov ch, [2]
9321 shl cx, #1
9322 checksum_loop:
9323 add al, [bx]
9324 inc bx
9325 loop checksum_loop
9326 and al, #0xff
9327 pop cx
9328 pop bx
9329 pop ax
9332 rom_scan:
9333 ;; Scan for existence of valid expansion ROMS.
9334 ;; Video ROM: from 0xC0000..0xC7FFF in 2k increments
9335 ;; General ROM: from 0xC8000..0xDFFFF in 2k increments
9336 ;; System ROM: only 0xE0000
9338 ;; Header:
9339 ;; Offset Value
9340 ;; 0 0x55
9341 ;; 1 0xAA
9342 ;; 2 ROM length in 512-byte blocks
9343 ;; 3 ROM initialization entry point (FAR CALL)
9345 mov cx, #0xc000
9346 rom_scan_loop:
9347 mov ds, cx
9348 mov ax, #0x0004 ;; start with increment of 4 (512-byte) blocks = 2k
9349 cmp [0], #0xAA55 ;; look for signature
9350 jne rom_scan_increment
9351 call rom_checksum
9352 jnz rom_scan_increment
9353 mov al, [2] ;; change increment to ROM length in 512-byte blocks
9355 ;; We want our increment in 512-byte quantities, rounded to
9356 ;; the nearest 2k quantity, since we only scan at 2k intervals.
9357 test al, #0x03
9358 jz block_count_rounded
9359 and al, #0xfc ;; needs rounding up
9360 add al, #0x04
9361 block_count_rounded:
9363 xor bx, bx ;; Restore DS back to 0000:
9364 mov ds, bx
9365 push ax ;; Save AX
9366 ;; Push addr of ROM entry point
9367 push cx ;; Push seg
9368 push #0x0003 ;; Push offset
9369 mov bp, sp ;; Call ROM init routine using seg:off on stack
9370 db 0xff ;; call_far ss:[bp+0]
9371 db 0x5e
9372 db 0
9373 cli ;; In case expansion ROM BIOS turns IF on
9374 add sp, #2 ;; Pop offset value
9375 pop cx ;; Pop seg value (restore CX)
9376 pop ax ;; Restore AX
9377 rom_scan_increment:
9378 shl ax, #5 ;; convert 512-bytes blocks to 16-byte increments
9379 ;; because the segment selector is shifted left 4 bits.
9380 add cx, ax
9381 cmp cx, #0xe000
9382 jbe rom_scan_loop
9384 xor ax, ax ;; Restore DS back to 0000:
9385 mov ds, ax
9388 ;; for 'C' strings and other data, insert them here with
9389 ;; a the following hack:
9390 ;; DATA_SEG_DEFS_HERE
9393 ;--------
9394 ;- POST -
9395 ;--------
9396 .org 0xe05b ; POST Entry Point
9397 post:
9399 xor ax, ax
9401 ;; first reset the DMA controllers
9402 out 0x0d,al
9403 out 0xda,al
9405 ;; then initialize the DMA controllers
9406 mov al, #0xC0
9407 out 0xD6, al ; cascade mode of channel 4 enabled
9408 mov al, #0x00
9409 out 0xD4, al ; unmask channel 4
9411 ;; Examine CMOS shutdown status.
9412 mov AL, #0x0f
9413 out 0x70, AL
9414 in AL, 0x71
9416 ;; backup status
9417 mov bl, al
9419 ;; Reset CMOS shutdown status.
9420 mov AL, #0x0f
9421 out 0x70, AL ; select CMOS register Fh
9422 mov AL, #0x00
9423 out 0x71, AL ; set shutdown action to normal
9425 ;; Examine CMOS shutdown status.
9426 mov al, bl
9428 ;; 0x00, 0x09, 0x0D+ = normal startup
9429 cmp AL, #0x00
9430 jz normal_post
9431 cmp AL, #0x0d
9432 jae normal_post
9433 cmp AL, #0x09
9434 je normal_post
9436 ;; 0x05 = eoi + jmp via [0x40:0x67] jump
9437 cmp al, #0x05
9438 je eoi_jmp_post
9440 ;; Examine CMOS shutdown status.
9441 ;; 0x01,0x02,0x03,0x04,0x06,0x07,0x08, 0x0a, 0x0b, 0x0c = Unimplemented shutdown status.
9442 push bx
9443 call _shutdown_status_panic
9445 #if 0
9446 HALT(__LINE__)
9448 ;#if 0
9449 ; 0xb0, 0x20, /* mov al, #0x20 */
9450 ; 0xe6, 0x20, /* out 0x20, al ;send EOI to PIC */
9451 ;#endif
9453 pop es
9454 pop ds
9455 popa
9456 iret
9457 #endif
9459 normal_post:
9460 ; case 0: normal startup
9463 mov ax, #0xfffe
9464 mov sp, ax
9465 mov ax, #0x0000
9466 mov ds, ax
9467 mov ss, ax
9469 ;; zero out BIOS data area (40:00..40:ff)
9470 mov es, ax
9471 mov cx, #0x0080 ;; 128 words
9472 mov di, #0x0400
9475 stosw
9477 call _log_bios_start
9479 ;; set all interrupts to default handler
9480 mov bx, #0x0000 ;; offset index
9481 mov cx, #0x0100 ;; counter (256 interrupts)
9482 mov ax, #dummy_iret_handler
9483 mov dx, #0xF000
9485 post_default_ints:
9486 mov [bx], ax
9487 inc bx
9488 inc bx
9489 mov [bx], dx
9490 inc bx
9491 inc bx
9492 loop post_default_ints
9494 ;; set vector 0x79 to zero
9495 ;; this is used by 'gardian angel' protection system
9496 SET_INT_VECTOR(0x79, #0, #0)
9498 ;; base memory in K 40:13 (word)
9499 mov ax, #BASE_MEM_IN_K
9500 mov 0x0413, ax
9503 ;; Manufacturing Test 40:12
9504 ;; zerod out above
9506 ;; Warm Boot Flag 0040:0072
9507 ;; value of 1234h = skip memory checks
9508 ;; zerod out above
9511 ;; Printer Services vector
9512 SET_INT_VECTOR(0x17, #0xF000, #int17_handler)
9514 ;; Bootstrap failure vector
9515 SET_INT_VECTOR(0x18, #0xF000, #int18_handler)
9517 ;; Bootstrap Loader vector
9518 SET_INT_VECTOR(0x19, #0xF000, #int19_handler)
9520 ;; User Timer Tick vector
9521 SET_INT_VECTOR(0x1c, #0xF000, #int1c_handler)
9523 ;; Memory Size Check vector
9524 SET_INT_VECTOR(0x12, #0xF000, #int12_handler)
9526 ;; Equipment Configuration Check vector
9527 SET_INT_VECTOR(0x11, #0xF000, #int11_handler)
9529 ;; System Services
9530 SET_INT_VECTOR(0x15, #0xF000, #int15_handler)
9532 ;; EBDA setup
9533 call ebda_post
9535 ;; PIT setup
9536 SET_INT_VECTOR(0x08, #0xF000, #int08_handler)
9537 ;; int 1C already points at dummy_iret_handler (above)
9538 mov al, #0x34 ; timer0: binary count, 16bit count, mode 2
9539 out 0x43, al
9540 mov al, #0x00 ; maximum count of 0000H = 18.2Hz
9541 out 0x40, al
9542 out 0x40, al
9544 ;; Keyboard
9545 SET_INT_VECTOR(0x09, #0xF000, #int09_handler)
9546 SET_INT_VECTOR(0x16, #0xF000, #int16_handler)
9548 xor ax, ax
9549 mov ds, ax
9550 mov 0x0417, al /* keyboard shift flags, set 1 */
9551 mov 0x0418, al /* keyboard shift flags, set 2 */
9552 mov 0x0419, al /* keyboard alt-numpad work area */
9553 mov 0x0471, al /* keyboard ctrl-break flag */
9554 mov 0x0497, al /* keyboard status flags 4 */
9555 mov al, #0x10
9556 mov 0x0496, al /* keyboard status flags 3 */
9559 /* keyboard head of buffer pointer */
9560 mov bx, #0x001E
9561 mov 0x041A, bx
9563 /* keyboard end of buffer pointer */
9564 mov 0x041C, bx
9566 /* keyboard pointer to start of buffer */
9567 mov bx, #0x001E
9568 mov 0x0480, bx
9570 /* keyboard pointer to end of buffer */
9571 mov bx, #0x003E
9572 mov 0x0482, bx
9574 /* init the keyboard */
9575 call _keyboard_init
9577 ;; mov CMOS Equipment Byte to BDA Equipment Word
9578 mov ax, 0x0410
9579 mov al, #0x14
9580 out 0x70, al
9581 in al, 0x71
9582 mov 0x0410, ax
9585 ;; Parallel setup
9586 SET_INT_VECTOR(0x0F, #0xF000, #dummy_iret_handler)
9587 xor ax, ax
9588 mov ds, ax
9589 xor bx, bx
9590 mov cl, #0x14 ; timeout value
9591 mov dx, #0x378 ; Parallel I/O address, port 1
9592 call detect_parport
9593 mov dx, #0x278 ; Parallel I/O address, port 2
9594 call detect_parport
9595 shl bx, #0x0e
9596 mov ax, 0x410 ; Equipment word bits 14..15 determing # parallel ports
9597 and ax, #0x3fff
9598 or ax, bx ; set number of parallel ports
9599 mov 0x410, ax
9601 ;; Serial setup
9602 SET_INT_VECTOR(0x0C, #0xF000, #dummy_iret_handler)
9603 SET_INT_VECTOR(0x14, #0xF000, #int14_handler)
9604 xor bx, bx
9605 mov cl, #0x0a ; timeout value
9606 mov dx, #0x03f8 ; Serial I/O address, port 1
9607 call detect_serial
9608 mov dx, #0x02f8 ; Serial I/O address, port 2
9609 call detect_serial
9610 mov dx, #0x03e8 ; Serial I/O address, port 3
9611 call detect_serial
9612 mov dx, #0x02e8 ; Serial I/O address, port 4
9613 call detect_serial
9614 shl bx, #0x09
9615 mov ax, 0x410 ; Equipment word bits 9..11 determing # serial ports
9616 and ax, #0xf1ff
9617 or ax, bx ; set number of serial port
9618 mov 0x410, ax
9620 ;; CMOS RTC
9621 SET_INT_VECTOR(0x1A, #0xF000, #int1a_handler)
9622 SET_INT_VECTOR(0x4A, #0xF000, #dummy_iret_handler)
9623 SET_INT_VECTOR(0x70, #0xF000, #int70_handler)
9624 ;; BIOS DATA AREA 0x4CE ???
9625 call timer_tick_post
9627 ;; PS/2 mouse setup
9628 SET_INT_VECTOR(0x74, #0xF000, #int74_handler)
9630 ;; IRQ13 (FPU exception) setup
9631 SET_INT_VECTOR(0x75, #0xF000, #int75_handler)
9633 ;; Video setup
9634 SET_INT_VECTOR(0x10, #0xF000, #int10_handler)
9636 ;; PIC
9637 mov al, #0x11 ; send initialisation commands
9638 out 0x20, al
9639 out 0xa0, al
9640 mov al, #0x08
9641 out 0x21, al
9642 mov al, #0x70
9643 out 0xa1, al
9644 mov al, #0x04
9645 out 0x21, al
9646 mov al, #0x02
9647 out 0xa1, al
9648 mov al, #0x01
9649 out 0x21, al
9650 out 0xa1, al
9651 mov al, #0xb8
9652 out 0x21, AL ;master pic: unmask IRQ 0, 1, 2, 6
9653 #if BX_USE_PS2_MOUSE
9654 mov al, #0x8f
9655 #else
9656 mov al, #0x9f
9657 #endif
9658 out 0xa1, AL ;slave pic: unmask IRQ 12, 13, 14
9660 call pcibios_init
9662 call rom_scan
9664 call _print_bios_banner
9667 ;; Floppy setup
9669 call floppy_drive_post
9671 #if BX_USE_ATADRV
9674 ;; Hard Drive setup
9676 call hard_drive_post
9679 ;; ATA/ATAPI driver setup
9681 call _ata_init
9682 call _ata_detect
9684 #else // BX_USE_ATADRV
9687 ;; Hard Drive setup
9689 call hard_drive_post
9691 #endif // BX_USE_ATADRV
9693 #if BX_ELTORITO_BOOT
9695 ;; eltorito floppy/harddisk emulation from cd
9697 call _cdemu_init
9699 #endif // BX_ELTORITO_BOOT
9701 int #0x19
9702 //JMP_EP(0x0064) ; INT 19h location
9705 .org 0xe2c3 ; NMI Handler Entry Point
9706 nmi:
9707 ;; FIXME the NMI handler should not panic
9708 ;; but iret when called from int75 (fpu exception)
9709 call _nmi_handler_msg
9710 iret
9712 int75_handler:
9713 out 0xf0, al // clear irq13
9714 call eoi_both_pics // clear interrupt
9715 int 2 // legacy nmi call
9716 iret
9718 ;-------------------------------------------
9719 ;- INT 13h Fixed Disk Services Entry Point -
9720 ;-------------------------------------------
9721 .org 0xe3fe ; INT 13h Fixed Disk Services Entry Point
9722 int13_handler:
9723 //JMPL(int13_relocated)
9724 jmp int13_relocated
9726 .org 0xe401 ; Fixed Disk Parameter Table
9728 ;----------
9729 ;- INT19h -
9730 ;----------
9731 .org 0xe6f2 ; INT 19h Boot Load Service Entry Point
9732 int19_handler:
9734 jmp int19_relocated
9735 ;-------------------------------------------
9736 ;- System BIOS Configuration Data Table
9737 ;-------------------------------------------
9738 .org BIOS_CONFIG_TABLE
9739 db 0x08 ; Table size (bytes) -Lo
9740 db 0x00 ; Table size (bytes) -Hi
9741 db SYS_MODEL_ID
9742 db SYS_SUBMODEL_ID
9743 db BIOS_REVISION
9744 ; Feature byte 1
9745 ; b7: 1=DMA channel 3 used by hard disk
9746 ; b6: 1=2 interrupt controllers present
9747 ; b5: 1=RTC present
9748 ; b4: 1=BIOS calls int 15h/4Fh every key
9749 ; b3: 1=wait for extern event supported (Int 15h/41h)
9750 ; b2: 1=extended BIOS data area used
9751 ; b1: 0=AT or ESDI bus, 1=MicroChannel
9752 ; b0: 1=Dual bus (MicroChannel + ISA)
9753 db (0 << 7) | \
9754 (1 << 6) | \
9755 (1 << 5) | \
9756 (BX_CALL_INT15_4F << 4) | \
9757 (0 << 3) | \
9758 (BX_USE_EBDA << 2) | \
9759 (0 << 1) | \
9760 (0 << 0)
9761 ; Feature byte 2
9762 ; b7: 1=32-bit DMA supported
9763 ; b6: 1=int16h, function 9 supported
9764 ; b5: 1=int15h/C6h (get POS data) supported
9765 ; b4: 1=int15h/C7h (get mem map info) supported
9766 ; b3: 1=int15h/C8h (en/dis CPU) supported
9767 ; b2: 1=non-8042 kb controller
9768 ; b1: 1=data streaming supported
9769 ; b0: reserved
9770 db (0 << 7) | \
9771 (1 << 6) | \
9772 (0 << 5) | \
9773 (0 << 4) | \
9774 (0 << 3) | \
9775 (0 << 2) | \
9776 (0 << 1) | \
9777 (0 << 0)
9778 ; Feature byte 3
9779 ; b7: not used
9780 ; b6: reserved
9781 ; b5: reserved
9782 ; b4: POST supports ROM-to-RAM enable/disable
9783 ; b3: SCSI on system board
9784 ; b2: info panel installed
9785 ; b1: Initial Machine Load (IML) system - BIOS on disk
9786 ; b0: SCSI supported in IML
9787 db 0x00
9788 ; Feature byte 4
9789 ; b7: IBM private
9790 ; b6: EEPROM present
9791 ; b5-3: ABIOS presence (011 = not supported)
9792 ; b2: private
9793 ; b1: memory split above 16Mb supported
9794 ; b0: POSTEXT directly supported by POST
9795 db 0x00
9796 ; Feature byte 5 (IBM)
9797 ; b1: enhanced mouse
9798 ; b0: flash EPROM
9799 db 0x00
9803 .org 0xe729 ; Baud Rate Generator Table
9805 ;----------
9806 ;- INT14h -
9807 ;----------
9808 .org 0xe739 ; INT 14h Serial Communications Service Entry Point
9809 int14_handler:
9810 push ds
9811 pusha
9812 mov ax, #0x0000
9813 mov ds, ax
9814 call _int14_function
9815 popa
9816 pop ds
9817 iret
9820 ;----------------------------------------
9821 ;- INT 16h Keyboard Service Entry Point -
9822 ;----------------------------------------
9823 .org 0xe82e
9824 int16_handler:
9827 push ds
9828 pushf
9829 pusha
9831 cmp ah, #0x00
9832 je int16_F00
9833 cmp ah, #0x10
9834 je int16_F00
9836 mov bx, #0xf000
9837 mov ds, bx
9838 call _int16_function
9839 popa
9840 popf
9841 pop ds
9842 jz int16_zero_set
9844 int16_zero_clear:
9845 push bp
9846 mov bp, sp
9847 //SEG SS
9848 and BYTE [bp + 0x06], #0xbf
9849 pop bp
9850 iret
9852 int16_zero_set:
9853 push bp
9854 mov bp, sp
9855 //SEG SS
9856 or BYTE [bp + 0x06], #0x40
9857 pop bp
9858 iret
9860 int16_F00:
9861 mov bx, #0x0040
9862 mov ds, bx
9864 int16_wait_for_key:
9866 mov bx, 0x001a
9867 cmp bx, 0x001c
9868 jne int16_key_found
9871 #if 0
9872 /* no key yet, call int 15h, function AX=9002 */
9873 0x50, /* push AX */
9874 0xb8, 0x02, 0x90, /* mov AX, #0x9002 */
9875 0xcd, 0x15, /* int 15h */
9876 0x58, /* pop AX */
9877 0xeb, 0xea, /* jmp WAIT_FOR_KEY */
9878 #endif
9879 jmp int16_wait_for_key
9881 int16_key_found:
9882 mov bx, #0xf000
9883 mov ds, bx
9884 call _int16_function
9885 popa
9886 popf
9887 pop ds
9888 #if 0
9889 /* notify int16 complete w/ int 15h, function AX=9102 */
9890 0x50, /* push AX */
9891 0xb8, 0x02, 0x91, /* mov AX, #0x9102 */
9892 0xcd, 0x15, /* int 15h */
9893 0x58, /* pop AX */
9894 #endif
9895 iret
9899 ;-------------------------------------------------
9900 ;- INT09h : Keyboard Hardware Service Entry Point -
9901 ;-------------------------------------------------
9902 .org 0xe987
9903 int09_handler:
9905 push ax
9907 mov al, #0xAD ;;disable keyboard
9908 out #0x64, al
9910 mov al, #0x0B
9911 out #0x20, al
9912 in al, #0x20
9913 and al, #0x02
9914 jz int09_finish
9916 in al, #0x60 ;;read key from keyboard controller
9917 //test al, #0x80 ;;look for key release
9918 //jnz int09_process_key ;; dont pass releases to intercept?
9920 ;; check for extended key
9921 cmp al, #0xe0
9922 jne int09_call_int15_4f
9924 push ds
9925 xor ax, ax
9926 mov ds, ax
9927 mov al, BYTE [0x496] ;; mf2_state |= 0x02
9928 or al, #0x01
9929 mov BYTE [0x496], al
9930 pop ds
9932 in al, #0x60 ;;read another key from keyboard controller
9936 int09_call_int15_4f:
9937 push ds
9938 pusha
9939 #ifdef BX_CALL_INT15_4F
9940 mov ah, #0x4f ;; allow for keyboard intercept
9942 int #0x15
9943 jnc int09_done
9944 #endif
9947 //int09_process_key:
9948 mov bx, #0xf000
9949 mov ds, bx
9950 call _int09_function
9952 int09_done:
9953 popa
9954 pop ds
9956 call eoi_master_pic
9958 int09_finish:
9959 mov al, #0xAE ;;enable keyboard
9960 out #0x64, al
9961 pop ax
9962 iret
9967 ;----------------------------------------
9968 ;- INT 13h Diskette Service Entry Point -
9969 ;----------------------------------------
9970 .org 0xec59
9971 int13_diskette:
9972 jmp int13_noeltorito
9974 ;---------------------------------------------
9975 ;- INT 0Eh Diskette Hardware ISR Entry Point -
9976 ;---------------------------------------------
9977 .org 0xef57 ; INT 0Eh Diskette Hardware ISR Entry Point
9978 int0e_handler:
9979 push ax
9980 push dx
9981 mov dx, #0x03f4
9982 in al, dx
9983 and al, #0xc0
9984 cmp al, #0xc0
9985 je int0e_normal
9986 mov dx, #0x03f5
9987 mov al, #0x08 ; sense interrupt status
9988 out dx, al
9989 int0e_loop1:
9990 mov dx, #0x03f4
9991 in al, dx
9992 and al, #0xc0
9993 cmp al, #0xc0
9994 jne int0e_loop1
9995 int0e_loop2:
9996 mov dx, #0x03f5
9997 in al, dx
9998 mov dx, #0x03f4
9999 in al, dx
10000 and al, #0xc0
10001 cmp al, #0xc0
10002 je int0e_loop2
10003 int0e_normal:
10004 push ds
10005 mov ax, #0x0000 ;; segment 0000
10006 mov ds, ax
10007 call eoi_master_pic
10008 mov al, 0x043e
10009 or al, #0x80 ;; diskette interrupt has occurred
10010 mov 0x043e, al
10011 pop ds
10012 pop dx
10013 pop ax
10014 iret
10017 .org 0xefc7 ; Diskette Controller Parameter Table
10018 diskette_param_table:
10019 ;; Since no provisions are made for multiple drive types, most
10020 ;; values in this table are ignored. I set parameters for 1.44M
10021 ;; floppy here
10022 db 0xAF
10023 db 0x02 ;; head load time 0000001, DMA used
10024 db 0x25
10025 db 0x02
10026 db 18
10027 db 0x1B
10028 db 0xFF
10029 db 0x6C
10030 db 0xF6
10031 db 0x0F
10032 db 0x08
10035 ;----------------------------------------
10036 ;- INT17h : Printer Service Entry Point -
10037 ;----------------------------------------
10038 .org 0xefd2
10039 int17_handler:
10040 push ds
10041 pusha
10042 mov ax, #0x0000
10043 mov ds, ax
10044 call _int17_function
10045 popa
10046 pop ds
10047 iret
10049 diskette_param_table2:
10050 ;; New diskette parameter table adding 3 parameters from IBM
10051 ;; Since no provisions are made for multiple drive types, most
10052 ;; values in this table are ignored. I set parameters for 1.44M
10053 ;; floppy here
10054 db 0xAF
10055 db 0x02 ;; head load time 0000001, DMA used
10056 db 0x25
10057 db 0x02
10058 db 18
10059 db 0x1B
10060 db 0xFF
10061 db 0x6C
10062 db 0xF6
10063 db 0x0F
10064 db 0x08
10065 db 79 ;; maximum track
10066 db 0 ;; data transfer rate
10067 db 4 ;; drive type in cmos
10069 .org 0xf045 ; INT 10 Functions 0-Fh Entry Point
10070 HALT(__LINE__)
10071 iret
10073 ;----------
10074 ;- INT10h -
10075 ;----------
10076 .org 0xf065 ; INT 10h Video Support Service Entry Point
10077 int10_handler:
10078 ;; dont do anything, since the VGA BIOS handles int10h requests
10079 iret
10081 .org 0xf0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
10083 ;----------
10084 ;- INT12h -
10085 ;----------
10086 .org 0xf841 ; INT 12h Memory Size Service Entry Point
10087 ; ??? different for Pentium (machine check)?
10088 int12_handler:
10089 push ds
10090 mov ax, #0x0040
10091 mov ds, ax
10092 mov ax, 0x0013
10093 pop ds
10094 iret
10096 ;----------
10097 ;- INT11h -
10098 ;----------
10099 .org 0xf84d ; INT 11h Equipment List Service Entry Point
10100 int11_handler:
10101 push ds
10102 mov ax, #0x0040
10103 mov ds, ax
10104 mov ax, 0x0010
10105 pop ds
10106 iret
10108 ;----------
10109 ;- INT15h -
10110 ;----------
10111 .org 0xf859 ; INT 15h System Services Entry Point
10112 int15_handler:
10113 pushf
10114 #if BX_APM
10115 cmp ah, #0x53
10116 je apm_call
10117 #endif
10118 push ds
10119 push es
10120 cmp ah, #0x86
10121 je int15_handler32
10122 cmp ah, #0xE8
10123 je int15_handler32
10124 pusha
10125 #if BX_USE_PS2_MOUSE
10126 cmp ah, #0xC2
10127 je int15_handler_mouse
10128 #endif
10129 call _int15_function
10130 int15_handler_mouse_ret:
10131 popa
10132 int15_handler32_ret:
10133 pop es
10134 pop ds
10135 popf
10136 jmp iret_modify_cf
10137 #if BX_APM
10138 apm_call:
10139 jmp _apmreal_entry
10140 #endif
10142 #if BX_USE_PS2_MOUSE
10143 int15_handler_mouse:
10144 call _int15_function_mouse
10145 jmp int15_handler_mouse_ret
10146 #endif
10148 int15_handler32:
10149 pushad
10150 call _int15_function32
10151 popad
10152 jmp int15_handler32_ret
10154 ;; Protected mode IDT descriptor
10156 ;; I just make the limit 0, so the machine will shutdown
10157 ;; if an exception occurs during protected mode memory
10158 ;; transfers.
10160 ;; Set base to f0000 to correspond to beginning of BIOS,
10161 ;; in case I actually define an IDT later
10162 ;; Set limit to 0
10164 pmode_IDT_info:
10165 dw 0x0000 ;; limit 15:00
10166 dw 0x0000 ;; base 15:00
10167 db 0x0f ;; base 23:16
10169 ;; Real mode IDT descriptor
10171 ;; Set to typical real-mode values.
10172 ;; base = 000000
10173 ;; limit = 03ff
10175 rmode_IDT_info:
10176 dw 0x03ff ;; limit 15:00
10177 dw 0x0000 ;; base 15:00
10178 db 0x00 ;; base 23:16
10181 ;----------
10182 ;- INT1Ah -
10183 ;----------
10184 .org 0xfe6e ; INT 1Ah Time-of-day Service Entry Point
10185 int1a_handler:
10186 #if BX_PCIBIOS
10187 cmp ah, #0xb1
10188 jne int1a_normal
10189 call pcibios_real
10190 jc pcibios_error
10191 retf 2
10192 pcibios_error:
10193 mov bl, ah
10194 mov ah, #0xb1
10195 push ds
10196 pusha
10197 mov ax, ss ; set readable descriptor to ds, for calling pcibios
10198 mov ds, ax ; on 16bit protected mode.
10199 jmp int1a_callfunction
10200 int1a_normal:
10201 #endif
10202 push ds
10203 pusha
10204 xor ax, ax
10205 mov ds, ax
10206 int1a_callfunction:
10207 call _int1a_function
10208 popa
10209 pop ds
10210 iret
10213 ;; int70h: IRQ8 - CMOS RTC
10215 int70_handler:
10216 push ds
10217 pushad
10218 xor ax, ax
10219 mov ds, ax
10220 call _int70_function
10221 popad
10222 pop ds
10223 iret
10225 ;---------
10226 ;- INT08 -
10227 ;---------
10228 .org 0xfea5 ; INT 08h System Timer ISR Entry Point
10229 int08_handler:
10231 push eax
10232 push ds
10233 xor ax, ax
10234 mov ds, ax
10236 ;; time to turn off drive(s)?
10237 mov al,0x0440
10238 or al,al
10239 jz int08_floppy_off
10240 dec al
10241 mov 0x0440,al
10242 jnz int08_floppy_off
10243 ;; turn motor(s) off
10244 push dx
10245 mov dx,#0x03f2
10246 in al,dx
10247 and al,#0xcf
10248 out dx,al
10249 pop dx
10250 int08_floppy_off:
10252 mov eax, 0x046c ;; get ticks dword
10253 inc eax
10255 ;; compare eax to one days worth of timer ticks at 18.2 hz
10256 cmp eax, #0x001800B0
10257 jb int08_store_ticks
10258 ;; there has been a midnight rollover at this point
10259 xor eax, eax ;; zero out counter
10260 inc BYTE 0x0470 ;; increment rollover flag
10262 int08_store_ticks:
10263 mov 0x046c, eax ;; store new ticks dword
10264 ;; chain to user timer tick INT #0x1c
10265 //pushf
10266 //;; call_ep [ds:loc]
10267 //CALL_EP( 0x1c << 2 )
10268 int #0x1c
10270 call eoi_master_pic
10271 pop ds
10272 pop eax
10273 iret
10275 .org 0xfef3 ; Initial Interrupt Vector Offsets Loaded by POST
10278 .org 0xff00
10279 .ascii BIOS_COPYRIGHT_STRING
10281 ;------------------------------------------------
10282 ;- IRET Instruction for Dummy Interrupt Handler -
10283 ;------------------------------------------------
10284 .org 0xff53 ; IRET Instruction for Dummy Interrupt Handler
10285 dummy_iret_handler:
10286 iret
10288 .org 0xff54 ; INT 05h Print Screen Service Entry Point
10289 HALT(__LINE__)
10290 iret
10292 .org 0xfff0 ; Power-up Entry Point
10293 jmp 0xf000:post
10295 .org 0xfff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
10296 .ascii BIOS_BUILD_DATE
10298 .org 0xfffe ; System Model ID
10299 db SYS_MODEL_ID
10300 db 0x00 ; filler
10302 .org 0xfa6e ;; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
10303 ASM_END
10305 * This font comes from the fntcol16.zip package (c) by Joseph Gil
10306 * found at ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip
10307 * This font is public domain
10309 static Bit8u vgafont8[128*8]=
10311 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
10312 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e,
10313 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e,
10314 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00,
10315 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00,
10316 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c,
10317 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c,
10318 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00,
10319 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff,
10320 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
10321 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff,
10322 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78,
10323 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18,
10324 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0,
10325 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0,
10326 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99,
10327 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00,
10328 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00,
10329 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18,
10330 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
10331 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00,
10332 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78,
10333 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00,
10334 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff,
10335 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00,
10336 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
10337 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00,
10338 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
10339 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00,
10340 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00,
10341 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00,
10342 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00,
10343 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
10344 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00,
10345 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
10346 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00,
10347 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00,
10348 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00,
10349 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00,
10350 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
10351 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00,
10352 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
10353 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
10354 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00,
10355 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60,
10356 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
10357 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00,
10358 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00,
10359 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00,
10360 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00,
10361 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00,
10362 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00,
10363 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00,
10364 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00,
10365 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00,
10366 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00,
10367 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00,
10368 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00,
10369 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00,
10370 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60,
10371 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00,
10372 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00,
10373 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00,
10374 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00,
10375 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00,
10376 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00,
10377 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00,
10378 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00,
10379 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00,
10380 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00,
10381 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00,
10382 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00,
10383 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00,
10384 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
10385 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00,
10386 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00,
10387 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00,
10388 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00,
10389 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00,
10390 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00,
10391 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
10392 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00,
10393 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00,
10394 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00,
10395 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
10396 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00,
10397 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
10398 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00,
10399 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00,
10400 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00,
10401 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00,
10402 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
10403 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00,
10404 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
10405 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
10406 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
10407 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
10408 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00,
10409 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00,
10410 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00,
10411 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00,
10412 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
10413 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00,
10414 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
10415 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00,
10416 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
10417 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78,
10418 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00,
10419 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
10420 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00,
10421 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
10422 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
10423 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0,
10424 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e,
10425 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00,
10426 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00,
10427 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00,
10428 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
10429 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
10430 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00,
10431 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00,
10432 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
10433 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00,
10434 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00,
10435 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
10436 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00,
10437 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
10438 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00,
10441 ASM_START
10442 .org 0xcc00
10443 // bcc-generated data will be placed here
10445 // For documentation of this config structure, look on developer.intel.com and
10446 // search for multiprocessor specification. Note that when you change anything
10447 // you must update the checksum (a pain!). It would be better to construct this
10448 // with C structures, or at least fill in the checksum automatically.
10450 // Maybe this structs could be moved elsewhere than d000
10452 #if (BX_SMP_PROCESSORS==1)
10453 // no structure necessary.
10454 #elif (BX_SMP_PROCESSORS==2)
10455 // define the Intel MP Configuration Structure for 2 processors at
10456 // APIC ID 0,1. I/O APIC at ID=2.
10457 .align 16
10458 mp_config_table:
10459 db 0x50, 0x43, 0x4d, 0x50 ;; "PCMP" signature
10460 dw (mp_config_end-mp_config_table) ;; table length
10461 db 4 ;; spec rev
10462 db 0x65 ;; checksum
10463 .ascii "BOCHSCPU" ;; OEM id = "BOCHSCPU"
10464 db 0x30, 0x2e, 0x31, 0x20 ;; vendor id = "0.1 "
10465 db 0x20, 0x20, 0x20, 0x20
10466 db 0x20, 0x20, 0x20, 0x20
10467 dw 0,0 ;; oem table ptr
10468 dw 0 ;; oem table size
10469 dw 20 ;; entry count
10470 dw 0x0000, 0xfee0 ;; memory mapped address of local APIC
10471 dw 0 ;; extended table length
10472 db 0 ;; extended table checksum
10473 db 0 ;; reserved
10474 mp_config_proc0:
10475 db 0 ;; entry type=processor
10476 db 0 ;; local APIC id
10477 db 0x11 ;; local APIC version number
10478 db 3 ;; cpu flags: enabled, bootstrap processor
10479 db 0,6,0,0 ;; cpu signature
10480 dw 0x201,0 ;; feature flags
10481 dw 0,0 ;; reserved
10482 dw 0,0 ;; reserved
10483 mp_config_proc1:
10484 db 0 ;; entry type=processor
10485 db 1 ;; local APIC id
10486 db 0x11 ;; local APIC version number
10487 db 1 ;; cpu flags: enabled
10488 db 0,6,0,0 ;; cpu signature
10489 dw 0x201,0 ;; feature flags
10490 dw 0,0 ;; reserved
10491 dw 0,0 ;; reserved
10492 mp_config_isa_bus:
10493 db 1 ;; entry type=bus
10494 db 0 ;; bus ID
10495 db 0x49, 0x53, 0x41, 0x20, 0x20, 0x20 ;; bus type="ISA "
10496 mp_config_ioapic:
10497 db 2 ;; entry type=I/O APIC
10498 db 2 ;; apic id=2. linux will set.
10499 db 0x11 ;; I/O APIC version number
10500 db 1 ;; flags=1=enabled
10501 dw 0x0000, 0xfec0 ;; memory mapped address of I/O APIC
10502 mp_config_irqs:
10503 db 3 ;; entry type=I/O interrupt
10504 db 0 ;; interrupt type=vectored interrupt
10505 db 0,0 ;; flags po=0, el=0 (linux uses as default)
10506 db 0 ;; source bus ID is ISA
10507 db 0 ;; source bus IRQ
10508 db 2 ;; destination I/O APIC ID
10509 db 0 ;; destination I/O APIC interrrupt in
10510 ;; repeat pattern for interrupts 0-15
10511 db 3,0,0,0,0,1,2,1
10512 db 3,0,0,0,0,2,2,2
10513 db 3,0,0,0,0,3,2,3
10514 db 3,0,0,0,0,4,2,4
10515 db 3,0,0,0,0,5,2,5
10516 db 3,0,0,0,0,6,2,6
10517 db 3,0,0,0,0,7,2,7
10518 db 3,0,0,0,0,8,2,8
10519 db 3,0,0,0,0,9,2,9
10520 db 3,0,0,0,0,10,2,10
10521 db 3,0,0,0,0,11,2,11
10522 db 3,0,0,0,0,12,2,12
10523 db 3,0,0,0,0,13,2,13
10524 db 3,0,0,0,0,14,2,14
10525 db 3,0,0,0,0,15,2,15
10526 #elif (BX_SMP_PROCESSORS==4)
10527 // define the Intel MP Configuration Structure for 4 processors at
10528 // APIC ID 0,1,2,3. I/O APIC at ID=4.
10529 .align 16
10530 mp_config_table:
10531 db 0x50, 0x43, 0x4d, 0x50 ;; "PCMP" signature
10532 dw (mp_config_end-mp_config_table) ;; table length
10533 db 4 ;; spec rev
10534 db 0xdd ;; checksum
10535 .ascii "BOCHSCPU" ;; OEM id = "BOCHSCPU"
10536 db 0x30, 0x2e, 0x31, 0x20 ;; vendor id = "0.1 "
10537 db 0x20, 0x20, 0x20, 0x20
10538 db 0x20, 0x20, 0x20, 0x20
10539 dw 0,0 ;; oem table ptr
10540 dw 0 ;; oem table size
10541 dw 22 ;; entry count
10542 dw 0x0000, 0xfee0 ;; memory mapped address of local APIC
10543 dw 0 ;; extended table length
10544 db 0 ;; extended table checksum
10545 db 0 ;; reserved
10546 mp_config_proc0:
10547 db 0 ;; entry type=processor
10548 db 0 ;; local APIC id
10549 db 0x11 ;; local APIC version number
10550 db 3 ;; cpu flags: enabled, bootstrap processor
10551 db 0,6,0,0 ;; cpu signature
10552 dw 0x201,0 ;; feature flags
10553 dw 0,0 ;; reserved
10554 dw 0,0 ;; reserved
10555 mp_config_proc1:
10556 db 0 ;; entry type=processor
10557 db 1 ;; local APIC id
10558 db 0x11 ;; local APIC version number
10559 db 1 ;; cpu flags: enabled
10560 db 0,6,0,0 ;; cpu signature
10561 dw 0x201,0 ;; feature flags
10562 dw 0,0 ;; reserved
10563 dw 0,0 ;; reserved
10564 mp_config_proc2:
10565 db 0 ;; entry type=processor
10566 db 2 ;; local APIC id
10567 db 0x11 ;; local APIC version number
10568 db 1 ;; cpu flags: enabled
10569 db 0,6,0,0 ;; cpu signature
10570 dw 0x201,0 ;; feature flags
10571 dw 0,0 ;; reserved
10572 dw 0,0 ;; reserved
10573 mp_config_proc3:
10574 db 0 ;; entry type=processor
10575 db 3 ;; local APIC id
10576 db 0x11 ;; local APIC version number
10577 db 1 ;; cpu flags: enabled
10578 db 0,6,0,0 ;; cpu signature
10579 dw 0x201,0 ;; feature flags
10580 dw 0,0 ;; reserved
10581 dw 0,0 ;; reserved
10582 mp_config_isa_bus:
10583 db 1 ;; entry type=bus
10584 db 0 ;; bus ID
10585 db 0x49, 0x53, 0x41, 0x20, 0x20, 0x20 ;; bus type="ISA "
10586 mp_config_ioapic:
10587 db 2 ;; entry type=I/O APIC
10588 db 4 ;; apic id=4. linux will set.
10589 db 0x11 ;; I/O APIC version number
10590 db 1 ;; flags=1=enabled
10591 dw 0x0000, 0xfec0 ;; memory mapped address of I/O APIC
10592 mp_config_irqs:
10593 db 3 ;; entry type=I/O interrupt
10594 db 0 ;; interrupt type=vectored interrupt
10595 db 0,0 ;; flags po=0, el=0 (linux uses as default)
10596 db 0 ;; source bus ID is ISA
10597 db 0 ;; source bus IRQ
10598 db 4 ;; destination I/O APIC ID
10599 db 0 ;; destination I/O APIC interrrupt in
10600 ;; repeat pattern for interrupts 0-15
10601 db 3,0,0,0,0,1,4,1
10602 db 3,0,0,0,0,2,4,2
10603 db 3,0,0,0,0,3,4,3
10604 db 3,0,0,0,0,4,4,4
10605 db 3,0,0,0,0,5,4,5
10606 db 3,0,0,0,0,6,4,6
10607 db 3,0,0,0,0,7,4,7
10608 db 3,0,0,0,0,8,4,8
10609 db 3,0,0,0,0,9,4,9
10610 db 3,0,0,0,0,10,4,10
10611 db 3,0,0,0,0,11,4,11
10612 db 3,0,0,0,0,12,4,12
10613 db 3,0,0,0,0,13,4,13
10614 db 3,0,0,0,0,14,4,14
10615 db 3,0,0,0,0,15,4,15
10616 #elif (BX_SMP_PROCESSORS==8)
10617 // define the Intel MP Configuration Structure for 8 processors at
10618 // APIC ID 0,1,2,3,4,5,6,7. I/O APIC at ID=8.
10619 .align 16
10620 mp_config_table:
10621 db 0x50, 0x43, 0x4d, 0x50 ;; "PCMP" signature
10622 dw (mp_config_end-mp_config_table) ;; table length
10623 db 4 ;; spec rev
10624 db 0xc3 ;; checksum
10625 .ascii "BOCHSCPU" ;; OEM id = "BOCHSCPU"
10626 db 0x30, 0x2e, 0x31, 0x20 ;; vendor id = "0.1 "
10627 db 0x20, 0x20, 0x20, 0x20
10628 db 0x20, 0x20, 0x20, 0x20
10629 dw 0,0 ;; oem table ptr
10630 dw 0 ;; oem table size
10631 dw 26 ;; entry count
10632 dw 0x0000, 0xfee0 ;; memory mapped address of local APIC
10633 dw 0 ;; extended table length
10634 db 0 ;; extended table checksum
10635 db 0 ;; reserved
10636 mp_config_proc0:
10637 db 0 ;; entry type=processor
10638 db 0 ;; local APIC id
10639 db 0x11 ;; local APIC version number
10640 db 3 ;; cpu flags: enabled, bootstrap processor
10641 db 0,6,0,0 ;; cpu signature
10642 dw 0x201,0 ;; feature flags
10643 dw 0,0 ;; reserved
10644 dw 0,0 ;; reserved
10645 mp_config_proc1:
10646 db 0 ;; entry type=processor
10647 db 1 ;; local APIC id
10648 db 0x11 ;; local APIC version number
10649 db 1 ;; cpu flags: enabled
10650 db 0,6,0,0 ;; cpu signature
10651 dw 0x201,0 ;; feature flags
10652 dw 0,0 ;; reserved
10653 dw 0,0 ;; reserved
10654 mp_config_proc2:
10655 db 0 ;; entry type=processor
10656 db 2 ;; local APIC id
10657 db 0x11 ;; local APIC version number
10658 db 1 ;; cpu flags: enabled
10659 db 0,6,0,0 ;; cpu signature
10660 dw 0x201,0 ;; feature flags
10661 dw 0,0 ;; reserved
10662 dw 0,0 ;; reserved
10663 mp_config_proc3:
10664 db 0 ;; entry type=processor
10665 db 3 ;; local APIC id
10666 db 0x11 ;; local APIC version number
10667 db 1 ;; cpu flags: enabled
10668 db 0,6,0,0 ;; cpu signature
10669 dw 0x201,0 ;; feature flags
10670 dw 0,0 ;; reserved
10671 dw 0,0 ;; reserved
10672 mp_config_proc4:
10673 db 0 ;; entry type=processor
10674 db 4 ;; local APIC id
10675 db 0x11 ;; local APIC version number
10676 db 1 ;; cpu flags: enabled
10677 db 0,6,0,0 ;; cpu signature
10678 dw 0x201,0 ;; feature flags
10679 dw 0,0 ;; reserved
10680 dw 0,0 ;; reserved
10681 mp_config_proc5:
10682 db 0 ;; entry type=processor
10683 db 5 ;; local APIC id
10684 db 0x11 ;; local APIC version number
10685 db 1 ;; cpu flags: enabled
10686 db 0,6,0,0 ;; cpu signature
10687 dw 0x201,0 ;; feature flags
10688 dw 0,0 ;; reserved
10689 dw 0,0 ;; reserved
10690 mp_config_proc6:
10691 db 0 ;; entry type=processor
10692 db 6 ;; local APIC id
10693 db 0x11 ;; local APIC version number
10694 db 1 ;; cpu flags: enabled
10695 db 0,6,0,0 ;; cpu signature
10696 dw 0x201,0 ;; feature flags
10697 dw 0,0 ;; reserved
10698 dw 0,0 ;; reserved
10699 mp_config_proc7:
10700 db 0 ;; entry type=processor
10701 db 7 ;; local APIC id
10702 db 0x11 ;; local APIC version number
10703 db 1 ;; cpu flags: enabled
10704 db 0,6,0,0 ;; cpu signature
10705 dw 0x201,0 ;; feature flags
10706 dw 0,0 ;; reserved
10707 dw 0,0 ;; reserved
10708 mp_config_isa_bus:
10709 db 1 ;; entry type=bus
10710 db 0 ;; bus ID
10711 db 0x49, 0x53, 0x41, 0x20, 0x20, 0x20 ;; bus type="ISA "
10712 mp_config_ioapic:
10713 db 2 ;; entry type=I/O APIC
10714 db 8 ;; apic id=8
10715 db 0x11 ;; I/O APIC version number
10716 db 1 ;; flags=1=enabled
10717 dw 0x0000, 0xfec0 ;; memory mapped address of I/O APIC
10718 mp_config_irqs:
10719 db 3 ;; entry type=I/O interrupt
10720 db 0 ;; interrupt type=vectored interrupt
10721 db 0,0 ;; flags po=0, el=0 (linux uses as default)
10722 db 0 ;; source bus ID is ISA
10723 db 0 ;; source bus IRQ
10724 db 8 ;; destination I/O APIC ID
10725 db 0 ;; destination I/O APIC interrrupt in
10726 ;; repeat pattern for interrupts 0-15
10727 db 3,0,0,0,0,1,8,1
10728 db 3,0,0,0,0,2,8,2
10729 db 3,0,0,0,0,3,8,3
10730 db 3,0,0,0,0,4,8,4
10731 db 3,0,0,0,0,5,8,5
10732 db 3,0,0,0,0,6,8,6
10733 db 3,0,0,0,0,7,8,7
10734 db 3,0,0,0,0,8,8,8
10735 db 3,0,0,0,0,9,8,9
10736 db 3,0,0,0,0,10,8,10
10737 db 3,0,0,0,0,11,8,11
10738 db 3,0,0,0,0,12,8,12
10739 db 3,0,0,0,0,13,8,13
10740 db 3,0,0,0,0,14,8,14
10741 db 3,0,0,0,0,15,8,15
10742 #else
10743 # error Sorry, rombios only has configurations for 1, 2, 4 or 8 processors.
10744 #endif // if (BX_SMP_PROCESSORS==...)
10746 mp_config_end: // this label used to find length of mp structure
10747 db 0
10749 #if (BX_SMP_PROCESSORS>1)
10750 .align 16
10751 mp_floating_pointer_structure:
10752 db 0x5f, 0x4d, 0x50, 0x5f ; "_MP_" signature
10753 dw mp_config_table, 0xf ;; pointer to MP configuration table
10754 db 1 ;; length of this struct in 16-bit byte chunks
10755 db 4 ;; MP spec revision
10756 db 0xc1 ;; checksum
10757 db 0 ;; MP feature byte 1. value 0 means look at the config table
10758 db 0,0,0,0 ;; MP feature bytes 2-5.
10759 #endif
10761 ASM_END