ia64/xen-unstable

view tools/firmware/rombios/rombios.c @ 18913:68555b9a7d98

rombios: fix rom_scan (ja->jmp)

Signed-off-by: Akio Takebe <takebe_akio@jp.fujitsu.com>
Signed-off-by: Kouya Shimura <kouya@jp.fujitsu.com>
author Keir Fraser <keir.fraser@citrix.com>
date Thu Dec 11 11:32:39 2008 +0000 (2008-12-11)
parents 76099ad24cbd
children 65bbc9ec3849
line source
1 /////////////////////////////////////////////////////////////////////////
2 // $Id: rombios.c,v 1.138 2005/05/07 15:55:26 vruppert Exp $
3 /////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (C) 2002 MandrakeSoft S.A.
6 //
7 // MandrakeSoft S.A.
8 // 43, rue d'Aboukir
9 // 75002 Paris - France
10 // http://www.linux-mandrake.com/
11 // http://www.mandrakesoft.com/
12 //
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.
17 //
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.
22 //
23 // You should have received a copy of the GNU Lesser General Public
24 // License along with this library; if not, write to the Free Software
25 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 // ROM BIOS for use with Bochs/Plex x86 emulation environment
29 #define uint8_t unsigned char
30 #define uint16_t unsigned short
31 #define uint32_t unsigned long
32 #include "../hvmloader/config.h"
34 #define HVMASSIST
35 #undef HVMTEST
37 // Xen full virtualization does not handle unaligned IO with page crossing.
38 // Disable 32-bit PIO as a workaround.
39 #undef NO_PIO32
42 // ROM BIOS compatability entry points:
43 // ===================================
44 // $e05b ; POST Entry Point
45 // $e2c3 ; NMI Handler Entry Point
46 // $e3fe ; INT 13h Fixed Disk Services Entry Point
47 // $e401 ; Fixed Disk Parameter Table
48 // $e6f2 ; INT 19h Boot Load Service Entry Point
49 // $e6f5 ; Configuration Data Table
50 // $e729 ; Baud Rate Generator Table
51 // $e739 ; INT 14h Serial Communications Service Entry Point
52 // $e82e ; INT 16h Keyboard Service Entry Point
53 // $e987 ; INT 09h Keyboard Service Entry Point
54 // $ec59 ; INT 13h Diskette Service Entry Point
55 // $ef57 ; INT 0Eh Diskette Hardware ISR Entry Point
56 // $efc7 ; Diskette Controller Parameter Table
57 // $efd2 ; INT 17h Printer Service Entry Point
58 // $f045 ; INT 10 Functions 0-Fh Entry Point
59 // $f065 ; INT 10h Video Support Service Entry Point
60 // $f0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
61 // $f841 ; INT 12h Memory Size Service Entry Point
62 // $f84d ; INT 11h Equipment List Service Entry Point
63 // $f859 ; INT 15h System Services Entry Point
64 // $fa6e ; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
65 // $fe6e ; INT 1Ah Time-of-day Service Entry Point
66 // $fea5 ; INT 08h System Timer ISR Entry Point
67 // $fef3 ; Initial Interrupt Vector Offsets Loaded by POST
68 // $ff53 ; IRET Instruction for Dummy Interrupt Handler
69 // $ff54 ; INT 05h Print Screen Service Entry Point
70 // $fff0 ; Power-up Entry Point
71 // $fff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
72 // $fffe ; System Model ID
74 // NOTES for ATA/ATAPI driver (cbbochs@free.fr)
75 // Features
76 // - supports up to 4 ATA interfaces
77 // - device/geometry detection
78 // - 16bits/32bits device access
79 // - pchs/lba access
80 // - datain/dataout/packet command support
81 //
82 // NOTES for El-Torito Boot (cbbochs@free.fr)
83 // - CD-ROM booting is only available if ATA/ATAPI Driver is available
84 // - Current code is only able to boot mono-session cds
85 // - Current code can not boot and emulate a hard-disk
86 // the bios will panic otherwise
87 // - Current code also use memory in EBDA segement.
88 // - I used cmos byte 0x3D to store extended information on boot-device
89 // - Code has to be modified modified to handle multiple cdrom drives
90 // - Here are the cdrom boot failure codes:
91 // 1 : no atapi device found
92 // 2 : no atapi cdrom found
93 // 3 : can not read cd - BRVD
94 // 4 : cd is not eltorito (BRVD)
95 // 5 : cd is not eltorito (ISO TAG)
96 // 6 : cd is not eltorito (ELTORITO TAG)
97 // 7 : can not read cd - boot catalog
98 // 8 : boot catalog : bad header
99 // 9 : boot catalog : bad platform
100 // 10 : boot catalog : bad signature
101 // 11 : boot catalog : bootable flag not set
102 // 12 : can not read cd - boot image
103 //
104 // ATA driver
105 // - EBDA segment.
106 // I used memory starting at 0x121 in the segment
107 // - the translation policy is defined in cmos regs 0x39 & 0x3a
108 //
109 // TODO :
110 //
111 // int74
112 // - needs to be reworked. Uses direct [bp] offsets. (?)
113 //
114 // int13:
115 // - f04 (verify sectors) isn't complete (?)
116 // - f02/03/04 should set current cyl,etc in BDA (?)
117 // - rewrite int13_relocated & clean up int13 entry code
118 //
119 // NOTES:
120 // - NMI access (bit7 of addr written to 70h)
121 //
122 // ATA driver
123 // - should handle the "don't detect" bit (cmos regs 0x3b & 0x3c)
124 // - could send the multiple-sector read/write commands
125 //
126 // El-Torito
127 // - Emulate a Hard-disk (currently only diskette can be emulated) see "FIXME ElTorito Harddisk"
128 // - Implement remaining int13_cdemu functions (as defined by El-Torito specs)
129 // - cdrom drive is hardcoded to ide 0 device 1 in several places. see "FIXME ElTorito Hardcoded"
130 // - int13 Fix DL when emulating a cd. In that case DL is decremented before calling real int13.
131 // This is ok. But DL should be reincremented afterwards.
132 // - Fix all "FIXME ElTorito Various"
133 // - should be able to boot any cdrom instead of the first one
134 //
135 // BCC Bug: find a generic way to handle the bug of #asm after an "if" (fixed in 0.16.7)
137 #define DEBUG_ROMBIOS 0
139 #define DEBUG_ATA 0
140 #define DEBUG_INT13_HD 0
141 #define DEBUG_INT13_CD 0
142 #define DEBUG_INT13_ET 0
143 #define DEBUG_INT13_FL 0
144 #define DEBUG_INT15 0
145 #define DEBUG_INT16 0
146 #define DEBUG_INT1A 0
147 #define DEBUG_INT74 0
148 #define DEBUG_APM 0
150 #define BX_CPU 3
151 #define BX_USE_PS2_MOUSE 1
152 #define BX_CALL_INT15_4F 1
153 #define BX_USE_EBDA 1
154 #define BX_SUPPORT_FLOPPY 1
155 #define BX_FLOPPY_ON_CNT 37 /* 2 seconds */
156 #define BX_PCIBIOS 1
157 #define BX_APM 1
159 #define BX_USE_ATADRV 1
160 #define BX_ELTORITO_BOOT 1
162 #define BX_TCGBIOS 0 /* main switch for TCG BIOS ext. */
164 #define BX_MAX_ATA_INTERFACES 4
165 #define BX_MAX_ATA_DEVICES (BX_MAX_ATA_INTERFACES*2)
167 #define BX_VIRTUAL_PORTS 1 /* normal output to Bochs ports */
168 #define BX_DEBUG_SERIAL 0 /* output to COM1 */
170 /* model byte 0xFC = AT */
171 #define SYS_MODEL_ID 0xFC
172 #define SYS_SUBMODEL_ID 0x00
173 #define BIOS_REVISION 1
174 #define BIOS_CONFIG_TABLE 0xe6f5
176 #ifndef BIOS_BUILD_DATE
177 # define BIOS_BUILD_DATE "06/23/99"
178 #endif
180 // 1K of base memory used for Extended Bios Data Area (EBDA)
181 // EBDA is used for PS/2 mouse support, and IDE BIOS, etc.
182 #define EBDA_SEG 0x9FC0
183 #define EBDA_SIZE 1 // In KiB
184 #define BASE_MEM_IN_K (640 - EBDA_SIZE)
186 // Define the application NAME
187 #ifdef HVMASSIST
188 # define BX_APPNAME "HVMAssist"
189 #elif PLEX86
190 # define BX_APPNAME "Plex86"
191 #else
192 # define BX_APPNAME "Bochs"
193 #endif
195 // Sanity Checks
196 #if BX_USE_ATADRV && BX_CPU<3
197 # error The ATA/ATAPI Driver can only to be used with a 386+ cpu
198 #endif
199 #if BX_USE_ATADRV && !BX_USE_EBDA
200 # error ATA/ATAPI Driver can only be used if EBDA is available
201 #endif
202 #if BX_ELTORITO_BOOT && !BX_USE_ATADRV
203 # error El-Torito Boot can only be use if ATA/ATAPI Driver is available
204 #endif
205 #if BX_PCIBIOS && BX_CPU<3
206 # error PCI BIOS can only be used with 386+ cpu
207 #endif
208 #if BX_APM && BX_CPU<3
209 # error APM BIOS can only be used with 386+ cpu
210 #endif
212 #ifndef BX_SMP_PROCESSORS
213 #define BX_SMP_PROCESSORS 1
214 # warning BX_SMP_PROCESSORS not defined, defaulting to 1
215 #endif
217 #define PANIC_PORT 0x400
218 #define PANIC_PORT2 0x401
219 #define INFO_PORT 0x402
220 #define DEBUG_PORT 0x403
222 // #20 is dec 20
223 // #$20 is hex 20 = 32
224 // #0x20 is hex 20 = 32
225 // LDA #$20
226 // JSR $E820
227 // LDD .i,S
228 // JSR $C682
229 // mov al, #$20
231 // all hex literals should be prefixed with '0x'
232 // grep "#[0-9a-fA-F][0-9a-fA-F]" rombios.c
233 // no mov SEG-REG, #value, must mov register into seg-reg
234 // grep -i "mov[ ]*.s" rombios.c
236 // This is for compiling with gcc2 and gcc3
237 #define ASM_START #asm
238 #define ASM_END #endasm
240 ASM_START
241 .rom
243 .org 0x0000
245 #if BX_CPU >= 3
246 use16 386
247 #else
248 use16 286
249 #endif
251 MACRO HALT
252 ;; the HALT macro is called with the line number of the HALT call.
253 ;; The line number is then sent to the PANIC_PORT, causing Bochs/Plex
254 ;; to print a BX_PANIC message. This will normally halt the simulation
255 ;; with a message such as "BIOS panic at rombios.c, line 4091".
256 ;; However, users can choose to make panics non-fatal and continue.
257 #if BX_VIRTUAL_PORTS
258 mov dx,#PANIC_PORT
259 mov ax,#?1
260 out dx,ax
261 #else
262 mov dx,#0x80
263 mov ax,#?1
264 out dx,al
265 #endif
266 MEND
268 MACRO JMP_AP
269 db 0xea
270 dw ?2
271 dw ?1
272 MEND
274 MACRO SET_INT_VECTOR
275 mov ax, ?3
276 mov ?1*4, ax
277 mov ax, ?2
278 mov ?1*4+2, ax
279 MEND
281 ASM_END
283 typedef unsigned char Bit8u;
284 typedef unsigned short Bit16u;
285 typedef unsigned short bx_bool;
286 typedef unsigned long Bit32u;
289 void memsetb(seg,offset,value,count);
290 void memcpyb(dseg,doffset,sseg,soffset,count);
291 void memcpyd(dseg,doffset,sseg,soffset,count);
293 // memset of count bytes
294 void
295 memsetb(seg,offset,value,count)
296 Bit16u seg;
297 Bit16u offset;
298 Bit16u value;
299 Bit16u count;
300 {
301 ASM_START
302 push bp
303 mov bp, sp
305 push ax
306 push cx
307 push es
308 push di
310 mov cx, 10[bp] ; count
311 cmp cx, #0x00
312 je memsetb_end
313 mov ax, 4[bp] ; segment
314 mov es, ax
315 mov ax, 6[bp] ; offset
316 mov di, ax
317 mov al, 8[bp] ; value
318 cld
319 rep
320 stosb
322 memsetb_end:
323 pop di
324 pop es
325 pop cx
326 pop ax
328 pop bp
329 ASM_END
330 }
332 // memcpy of count bytes
333 void
334 memcpyb(dseg,doffset,sseg,soffset,count)
335 Bit16u dseg;
336 Bit16u doffset;
337 Bit16u sseg;
338 Bit16u soffset;
339 Bit16u count;
340 {
341 ASM_START
342 push bp
343 mov bp, sp
345 push ax
346 push cx
347 push es
348 push di
349 push ds
350 push si
352 mov cx, 12[bp] ; count
353 cmp cx, #0x0000
354 je memcpyb_end
355 mov ax, 4[bp] ; dsegment
356 mov es, ax
357 mov ax, 6[bp] ; doffset
358 mov di, ax
359 mov ax, 8[bp] ; ssegment
360 mov ds, ax
361 mov ax, 10[bp] ; soffset
362 mov si, ax
363 cld
364 rep
365 movsb
367 memcpyb_end:
368 pop si
369 pop ds
370 pop di
371 pop es
372 pop cx
373 pop ax
375 pop bp
376 ASM_END
377 }
379 #if 0
380 // memcpy of count dword
381 void
382 memcpyd(dseg,doffset,sseg,soffset,count)
383 Bit16u dseg;
384 Bit16u doffset;
385 Bit16u sseg;
386 Bit16u soffset;
387 Bit16u count;
388 {
389 ASM_START
390 push bp
391 mov bp, sp
393 push ax
394 push cx
395 push es
396 push di
397 push ds
398 push si
400 mov cx, 12[bp] ; count
401 cmp cx, #0x0000
402 je memcpyd_end
403 mov ax, 4[bp] ; dsegment
404 mov es, ax
405 mov ax, 6[bp] ; doffset
406 mov di, ax
407 mov ax, 8[bp] ; ssegment
408 mov ds, ax
409 mov ax, 10[bp] ; soffset
410 mov si, ax
411 cld
412 rep
413 movsd
415 memcpyd_end:
416 pop si
417 pop ds
418 pop di
419 pop es
420 pop cx
421 pop ax
423 pop bp
424 ASM_END
425 }
426 #endif
428 // read_dword and write_dword functions
429 static Bit32u read_dword();
430 static void write_dword();
432 Bit32u
433 read_dword(seg, offset)
434 Bit16u seg;
435 Bit16u offset;
436 {
437 ASM_START
438 push bp
439 mov bp, sp
441 push bx
442 push ds
443 mov ax, 4[bp] ; segment
444 mov ds, ax
445 mov bx, 6[bp] ; offset
446 mov ax, [bx]
447 inc bx
448 inc bx
449 mov dx, [bx]
450 ;; ax = return value (word)
451 ;; dx = return value (word)
452 pop ds
453 pop bx
455 pop bp
456 ASM_END
457 }
459 void
460 write_dword(seg, offset, data)
461 Bit16u seg;
462 Bit16u offset;
463 Bit32u data;
464 {
465 ASM_START
466 push bp
467 mov bp, sp
469 push ax
470 push bx
471 push ds
472 mov ax, 4[bp] ; segment
473 mov ds, ax
474 mov bx, 6[bp] ; offset
475 mov ax, 8[bp] ; data word
476 mov [bx], ax ; write data word
477 inc bx
478 inc bx
479 mov ax, 10[bp] ; data word
480 mov [bx], ax ; write data word
481 pop ds
482 pop bx
483 pop ax
485 pop bp
486 ASM_END
487 }
489 // Bit32u (unsigned long) and long helper functions
490 ASM_START
492 ;; and function
493 landl:
494 landul:
495 SEG SS
496 and ax,[di]
497 SEG SS
498 and bx,2[di]
499 ret
501 ;; add function
502 laddl:
503 laddul:
504 SEG SS
505 add ax,[di]
506 SEG SS
507 adc bx,2[di]
508 ret
510 ;; cmp function
511 lcmpl:
512 lcmpul:
513 and eax, #0x0000FFFF
514 shl ebx, #16
515 add eax, ebx
516 shr ebx, #16
517 SEG SS
518 cmp eax, dword ptr [di]
519 ret
521 ;; sub function
522 lsubl:
523 lsubul:
524 SEG SS
525 sub ax,[di]
526 SEG SS
527 sbb bx,2[di]
528 ret
530 ;; mul function
531 lmull:
532 lmulul:
533 and eax, #0x0000FFFF
534 shl ebx, #16
535 add eax, ebx
536 SEG SS
537 mul eax, dword ptr [di]
538 mov ebx, eax
539 shr ebx, #16
540 ret
542 ;; dec function
543 ldecl:
544 ldecul:
545 SEG SS
546 dec dword ptr [bx]
547 ret
549 ;; or function
550 lorl:
551 lorul:
552 SEG SS
553 or ax,[di]
554 SEG SS
555 or bx,2[di]
556 ret
558 ;; inc function
559 lincl:
560 lincul:
561 SEG SS
562 inc dword ptr [bx]
563 ret
565 ;; tst function
566 ltstl:
567 ltstul:
568 and eax, #0x0000FFFF
569 shl ebx, #16
570 add eax, ebx
571 shr ebx, #16
572 test eax, eax
573 ret
575 ;; sr function
576 lsrul:
577 mov cx,di
578 jcxz lsr_exit
579 and eax, #0x0000FFFF
580 shl ebx, #16
581 add eax, ebx
582 lsr_loop:
583 shr eax, #1
584 loop lsr_loop
585 mov ebx, eax
586 shr ebx, #16
587 lsr_exit:
588 ret
590 ;; sl function
591 lsll:
592 lslul:
593 mov cx,di
594 jcxz lsl_exit
595 and eax, #0x0000FFFF
596 shl ebx, #16
597 add eax, ebx
598 lsl_loop:
599 shl eax, #1
600 loop lsl_loop
601 mov ebx, eax
602 shr ebx, #16
603 lsl_exit:
604 ret
606 idiv_:
607 cwd
608 idiv bx
609 ret
611 idiv_u:
612 xor dx,dx
613 div bx
614 ret
616 ldivul:
617 and eax, #0x0000FFFF
618 shl ebx, #16
619 add eax, ebx
620 xor edx, edx
621 SEG SS
622 mov bx, 2[di]
623 shl ebx, #16
624 SEG SS
625 mov bx, [di]
626 div ebx
627 mov ebx, eax
628 shr ebx, #16
629 ret
631 ASM_END
633 // for access to RAM area which is used by interrupt vectors
634 // and BIOS Data Area
636 typedef struct {
637 unsigned char filler1[0x400];
638 unsigned char filler2[0x6c];
639 Bit16u ticks_low;
640 Bit16u ticks_high;
641 Bit8u midnight_flag;
642 } bios_data_t;
644 #define BiosData ((bios_data_t *) 0)
646 #if BX_USE_ATADRV
647 typedef struct {
648 Bit16u heads; // # heads
649 Bit16u cylinders; // # cylinders
650 Bit16u spt; // # sectors / track
651 } chs_t;
653 // DPTE definition
654 typedef struct {
655 Bit16u iobase1;
656 Bit16u iobase2;
657 Bit8u prefix;
658 Bit8u unused;
659 Bit8u irq;
660 Bit8u blkcount;
661 Bit8u dma;
662 Bit8u pio;
663 Bit16u options;
664 Bit16u reserved;
665 Bit8u revision;
666 Bit8u checksum;
667 } dpte_t;
669 typedef struct {
670 Bit8u iface; // ISA or PCI
671 Bit16u iobase1; // IO Base 1
672 Bit16u iobase2; // IO Base 2
673 Bit8u irq; // IRQ
674 } ata_channel_t;
676 typedef struct {
677 Bit8u type; // Detected type of ata (ata/atapi/none/unknown)
678 Bit8u device; // Detected type of attached devices (hd/cd/none)
679 Bit8u removable; // Removable device flag
680 Bit8u lock; // Locks for removable devices
681 // Bit8u lba_capable; // LBA capable flag - always yes for bochs devices
682 Bit8u mode; // transfert mode : PIO 16/32 bits - IRQ - ISADMA - PCIDMA
683 Bit16u blksize; // block size
685 Bit8u translation; // type of translation
686 chs_t lchs; // Logical CHS
687 chs_t pchs; // Physical CHS
689 Bit32u sectors; // Total sectors count
690 } ata_device_t;
692 typedef struct {
693 // ATA channels info
694 ata_channel_t channels[BX_MAX_ATA_INTERFACES];
696 // ATA devices info
697 ata_device_t devices[BX_MAX_ATA_DEVICES];
698 //
699 // map between (bios hd id - 0x80) and ata channels
700 Bit8u hdcount, hdidmap[BX_MAX_ATA_DEVICES];
702 // map between (bios cd id - 0xE0) and ata channels
703 Bit8u cdcount, cdidmap[BX_MAX_ATA_DEVICES];
705 // Buffer for DPTE table
706 dpte_t dpte;
708 // Count of transferred sectors and bytes
709 Bit16u trsfsectors;
710 Bit32u trsfbytes;
712 } ata_t;
714 #if BX_ELTORITO_BOOT
715 // ElTorito Device Emulation data
716 typedef struct {
717 Bit8u active;
718 Bit8u media;
719 Bit8u emulated_drive;
720 Bit8u controller_index;
721 Bit16u device_spec;
722 Bit32u ilba;
723 Bit16u buffer_segment;
724 Bit16u load_segment;
725 Bit16u sector_count;
727 // Virtual device
728 chs_t vdevice;
729 } cdemu_t;
730 #endif // BX_ELTORITO_BOOT
732 #include "32bitgateway.h"
734 // for access to EBDA area
735 // The EBDA structure should conform to
736 // http://www.cybertrails.com/~fys/rombios.htm document
737 // I made the ata and cdemu structs begin at 0x121 in the EBDA seg
738 // EBDA must be at most 768 bytes; it lives at 0x9fc00, and the boot
739 // device tables are at 0x9ff00 -- 0x9ffff
740 typedef struct {
741 unsigned char ebda_size;
742 unsigned char cmos_shutdown_status;
743 unsigned char filler1[0x3B];
745 // FDPT - Can be splitted in data members if needed
746 unsigned char fdpt0[0x10];
747 unsigned char fdpt1[0x10];
749 unsigned char filler2[0xC4];
751 // ATA Driver data
752 ata_t ata;
754 #if BX_ELTORITO_BOOT
755 // El Torito Emulation data
756 cdemu_t cdemu;
757 #endif // BX_ELTORITO_BOOT
759 upcall_t upcall;
760 } ebda_data_t;
762 #define EBDA_CMOS_SHUTDOWN_STATUS_OFFSET 1
763 #define EbdaData ((ebda_data_t *) 0)
765 // for access to the int13ext structure
766 typedef struct {
767 Bit8u size;
768 Bit8u reserved;
769 Bit16u count;
770 Bit16u offset;
771 Bit16u segment;
772 Bit32u lba1;
773 Bit32u lba2;
774 } int13ext_t;
776 #define Int13Ext ((int13ext_t *) 0)
778 // Disk Physical Table definition
779 typedef struct {
780 Bit16u size;
781 Bit16u infos;
782 Bit32u cylinders;
783 Bit32u heads;
784 Bit32u spt;
785 Bit32u sector_count1;
786 Bit32u sector_count2;
787 Bit16u blksize;
788 Bit16u dpte_offset;
789 Bit16u dpte_segment;
790 Bit16u key;
791 Bit8u dpi_length;
792 Bit8u reserved1;
793 Bit16u reserved2;
794 Bit8u host_bus[4];
795 Bit8u iface_type[8];
796 Bit8u iface_path[8];
797 Bit8u device_path[8];
798 Bit8u reserved3;
799 Bit8u checksum;
800 } dpt_t;
802 #define Int13DPT ((dpt_t *) 0)
804 #endif // BX_USE_ATADRV
806 typedef struct {
807 union {
808 struct {
809 Bit16u di, si, bp, sp;
810 Bit16u bx, dx, cx, ax;
811 } r16;
812 struct {
813 Bit16u filler[4];
814 Bit8u bl, bh, dl, dh, cl, ch, al, ah;
815 } r8;
816 } u;
817 } pusha_regs_t;
819 typedef struct {
820 union {
821 struct {
822 Bit32u edi, esi, ebp, esp;
823 Bit32u ebx, edx, ecx, eax;
824 } r32;
825 struct {
826 Bit16u di, filler1, si, filler2, bp, filler3, sp, filler4;
827 Bit16u bx, filler5, dx, filler6, cx, filler7, ax, filler8;
828 } r16;
829 struct {
830 Bit32u filler[4];
831 Bit8u bl, bh;
832 Bit16u filler1;
833 Bit8u dl, dh;
834 Bit16u filler2;
835 Bit8u cl, ch;
836 Bit16u filler3;
837 Bit8u al, ah;
838 Bit16u filler4;
839 } r8;
840 } u;
841 } pushad_regs_t;
843 typedef struct {
844 union {
845 struct {
846 Bit16u flags;
847 } r16;
848 struct {
849 Bit8u flagsl;
850 Bit8u flagsh;
851 } r8;
852 } u;
853 } flags_t;
855 #define SetCF(x) x.u.r8.flagsl |= 0x01
856 #define SetZF(x) x.u.r8.flagsl |= 0x40
857 #define ClearCF(x) x.u.r8.flagsl &= 0xfe
858 #define ClearZF(x) x.u.r8.flagsl &= 0xbf
859 #define GetCF(x) (x.u.r8.flagsl & 0x01)
861 typedef struct {
862 Bit16u ip;
863 Bit16u cs;
864 flags_t flags;
865 } iret_addr_t;
869 static Bit8u inb();
870 static Bit8u inb_cmos();
871 static void outb();
872 static void outb_cmos();
873 static Bit16u inw();
874 static void outw();
875 static void init_rtc();
876 static bx_bool rtc_updating();
878 static Bit8u read_byte();
879 static Bit16u read_word();
880 static void write_byte();
881 static void write_word();
882 static void bios_printf();
883 static void copy_e820_table();
885 static Bit8u inhibit_mouse_int_and_events();
886 static void enable_mouse_int_and_events();
887 static Bit8u send_to_mouse_ctrl();
888 static Bit8u get_mouse_data();
889 static void set_kbd_command_byte();
891 static void int09_function();
892 static void int13_harddisk();
893 static void int13_cdrom();
894 static void int13_cdemu();
895 static void int13_eltorito();
896 static void int13_diskette_function();
897 static void int14_function();
898 static void int15_function();
899 static void int16_function();
900 static void int17_function();
901 static void int18_function();
902 static void int1a_function();
903 static void int70_function();
904 static void int74_function();
905 static Bit16u get_CS();
906 //static Bit16u get_DS();
907 //static void set_DS();
908 static Bit16u get_SS();
909 static unsigned int enqueue_key();
910 static unsigned int dequeue_key();
911 static void get_hd_geometry();
912 static void set_diskette_ret_status();
913 static void set_diskette_current_cyl();
914 static void determine_floppy_media();
915 static bx_bool floppy_drive_exists();
916 static bx_bool floppy_drive_recal();
917 static bx_bool floppy_media_known();
918 static bx_bool floppy_media_sense();
919 static bx_bool set_enable_a20();
920 static void debugger_on();
921 static void debugger_off();
922 static void keyboard_init();
923 static void keyboard_panic();
924 static void shutdown_status_panic();
925 static void nmi_handler_msg();
927 static void print_bios_banner();
928 static void print_boot_device();
929 static void print_boot_failure();
930 static void print_cdromboot_failure();
932 # if BX_USE_ATADRV
934 // ATA / ATAPI driver
935 void ata_init();
936 void ata_detect();
937 void ata_reset();
939 Bit16u ata_cmd_non_data();
940 Bit16u ata_cmd_data_in();
941 Bit16u ata_cmd_data_out();
942 Bit16u ata_cmd_packet();
944 Bit16u atapi_get_sense();
945 Bit16u atapi_is_ready();
946 Bit16u atapi_is_cdrom();
948 #endif // BX_USE_ATADRV
950 #if BX_ELTORITO_BOOT
952 void cdemu_init();
953 Bit8u cdemu_isactive();
954 Bit8u cdemu_emulated_drive();
956 Bit16u cdrom_boot();
958 #endif // BX_ELTORITO_BOOT
960 static char bios_cvs_version_string[] = "$Revision: 1.138 $";
961 static char bios_date_string[] = "$Date: 2005/05/07 15:55:26 $";
963 static char CVSID[] = "$Id: rombios.c,v 1.138 2005/05/07 15:55:26 vruppert Exp $";
965 /* Offset to skip the CVS $Id: prefix */
966 #define bios_version_string (CVSID + 4)
968 #define BIOS_PRINTF_HALT 1
969 #define BIOS_PRINTF_SCREEN 2
970 #define BIOS_PRINTF_INFO 4
971 #define BIOS_PRINTF_DEBUG 8
972 #define BIOS_PRINTF_ALL (BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO)
973 #define BIOS_PRINTF_DEBHALT (BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO | BIOS_PRINTF_HALT)
975 #define printf(format, p...) bios_printf(BIOS_PRINTF_SCREEN, format, ##p)
977 // Defines the output macros.
978 // BX_DEBUG goes to INFO port until we can easily choose debug info on a
979 // per-device basis. Debug info are sent only in debug mode
980 #if DEBUG_ROMBIOS
981 # define BX_DEBUG(format, p...) bios_printf(BIOS_PRINTF_INFO, format, ##p)
982 #else
983 # define BX_DEBUG(format, p...)
984 #endif
985 #define BX_INFO(format, p...) bios_printf(BIOS_PRINTF_INFO, format, ##p)
986 #define BX_PANIC(format, p...) bios_printf(BIOS_PRINTF_DEBHALT, format, ##p)
988 #if DEBUG_ATA
989 # define BX_DEBUG_ATA(a...) BX_DEBUG(a)
990 #else
991 # define BX_DEBUG_ATA(a...)
992 #endif
993 #if DEBUG_INT13_HD
994 # define BX_DEBUG_INT13_HD(a...) BX_DEBUG(a)
995 #else
996 # define BX_DEBUG_INT13_HD(a...)
997 #endif
998 #if DEBUG_INT13_CD
999 # define BX_DEBUG_INT13_CD(a...) BX_DEBUG(a)
1000 #else
1001 # define BX_DEBUG_INT13_CD(a...)
1002 #endif
1003 #if DEBUG_INT13_ET
1004 # define BX_DEBUG_INT13_ET(a...) BX_DEBUG(a)
1005 #else
1006 # define BX_DEBUG_INT13_ET(a...)
1007 #endif
1008 #if DEBUG_INT13_FL
1009 # define BX_DEBUG_INT13_FL(a...) BX_DEBUG(a)
1010 #else
1011 # define BX_DEBUG_INT13_FL(a...)
1012 #endif
1013 #if DEBUG_INT15
1014 # define BX_DEBUG_INT15(a...) BX_DEBUG(a)
1015 #else
1016 # define BX_DEBUG_INT15(a...)
1017 #endif
1018 #if DEBUG_INT16
1019 # define BX_DEBUG_INT16(a...) BX_DEBUG(a)
1020 #else
1021 # define BX_DEBUG_INT16(a...)
1022 #endif
1023 #if DEBUG_INT1A
1024 # define BX_DEBUG_INT1A(a...) BX_DEBUG(a)
1025 #else
1026 # define BX_DEBUG_INT1A(a...)
1027 #endif
1028 #if DEBUG_INT74
1029 # define BX_DEBUG_INT74(a...) BX_DEBUG(a)
1030 #else
1031 # define BX_DEBUG_INT74(a...)
1032 #endif
1034 #define SET_AL(val8) AX = ((AX & 0xff00) | (val8))
1035 #define SET_BL(val8) BX = ((BX & 0xff00) | (val8))
1036 #define SET_CL(val8) CX = ((CX & 0xff00) | (val8))
1037 #define SET_DL(val8) DX = ((DX & 0xff00) | (val8))
1038 #define SET_AH(val8) AX = ((AX & 0x00ff) | ((val8) << 8))
1039 #define SET_BH(val8) BX = ((BX & 0x00ff) | ((val8) << 8))
1040 #define SET_CH(val8) CX = ((CX & 0x00ff) | ((val8) << 8))
1041 #define SET_DH(val8) DX = ((DX & 0x00ff) | ((val8) << 8))
1043 #define GET_AL() ( AX & 0x00ff )
1044 #define GET_BL() ( BX & 0x00ff )
1045 #define GET_CL() ( CX & 0x00ff )
1046 #define GET_DL() ( DX & 0x00ff )
1047 #define GET_AH() ( AX >> 8 )
1048 #define GET_BH() ( BX >> 8 )
1049 #define GET_CH() ( CX >> 8 )
1050 #define GET_DH() ( DX >> 8 )
1052 #define GET_ELDL() ( ELDX & 0x00ff )
1053 #define GET_ELDH() ( ELDX >> 8 )
1055 #define SET_CF() FLAGS |= 0x0001
1056 #define CLEAR_CF() FLAGS &= 0xfffe
1057 #define GET_CF() (FLAGS & 0x0001)
1059 #define SET_ZF() FLAGS |= 0x0040
1060 #define CLEAR_ZF() FLAGS &= 0xffbf
1061 #define GET_ZF() (FLAGS & 0x0040)
1063 #define UNSUPPORTED_FUNCTION 0x86
1065 #define none 0
1066 #define MAX_SCAN_CODE 0x58
1068 static struct {
1069 Bit16u normal;
1070 Bit16u shift;
1071 Bit16u control;
1072 Bit16u alt;
1073 Bit8u lock_flags;
1074 } scan_to_scanascii[MAX_SCAN_CODE + 1] = {
1075 { none, none, none, none, none },
1076 { 0x011b, 0x011b, 0x011b, 0x0100, none }, /* escape */
1077 { 0x0231, 0x0221, none, 0x7800, none }, /* 1! */
1078 { 0x0332, 0x0340, 0x0300, 0x7900, none }, /* 2@ */
1079 { 0x0433, 0x0423, none, 0x7a00, none }, /* 3# */
1080 { 0x0534, 0x0524, none, 0x7b00, none }, /* 4$ */
1081 { 0x0635, 0x0625, none, 0x7c00, none }, /* 5% */
1082 { 0x0736, 0x075e, 0x071e, 0x7d00, none }, /* 6^ */
1083 { 0x0837, 0x0826, none, 0x7e00, none }, /* 7& */
1084 { 0x0938, 0x092a, none, 0x7f00, none }, /* 8* */
1085 { 0x0a39, 0x0a28, none, 0x8000, none }, /* 9( */
1086 { 0x0b30, 0x0b29, none, 0x8100, none }, /* 0) */
1087 { 0x0c2d, 0x0c5f, 0x0c1f, 0x8200, none }, /* -_ */
1088 { 0x0d3d, 0x0d2b, none, 0x8300, none }, /* =+ */
1089 { 0x0e08, 0x0e08, 0x0e7f, none, none }, /* backspace */
1090 { 0x0f09, 0x0f00, none, none, none }, /* tab */
1091 { 0x1071, 0x1051, 0x1011, 0x1000, 0x40 }, /* Q */
1092 { 0x1177, 0x1157, 0x1117, 0x1100, 0x40 }, /* W */
1093 { 0x1265, 0x1245, 0x1205, 0x1200, 0x40 }, /* E */
1094 { 0x1372, 0x1352, 0x1312, 0x1300, 0x40 }, /* R */
1095 { 0x1474, 0x1454, 0x1414, 0x1400, 0x40 }, /* T */
1096 { 0x1579, 0x1559, 0x1519, 0x1500, 0x40 }, /* Y */
1097 { 0x1675, 0x1655, 0x1615, 0x1600, 0x40 }, /* U */
1098 { 0x1769, 0x1749, 0x1709, 0x1700, 0x40 }, /* I */
1099 { 0x186f, 0x184f, 0x180f, 0x1800, 0x40 }, /* O */
1100 { 0x1970, 0x1950, 0x1910, 0x1900, 0x40 }, /* P */
1101 { 0x1a5b, 0x1a7b, 0x1a1b, none, none }, /* [{ */
1102 { 0x1b5d, 0x1b7d, 0x1b1d, none, none }, /* ]} */
1103 { 0x1c0d, 0x1c0d, 0x1c0a, none, none }, /* Enter */
1104 { none, none, none, none, none }, /* L Ctrl */
1105 { 0x1e61, 0x1e41, 0x1e01, 0x1e00, 0x40 }, /* A */
1106 { 0x1f73, 0x1f53, 0x1f13, 0x1f00, 0x40 }, /* S */
1107 { 0x2064, 0x2044, 0x2004, 0x2000, 0x40 }, /* D */
1108 { 0x2166, 0x2146, 0x2106, 0x2100, 0x40 }, /* F */
1109 { 0x2267, 0x2247, 0x2207, 0x2200, 0x40 }, /* G */
1110 { 0x2368, 0x2348, 0x2308, 0x2300, 0x40 }, /* H */
1111 { 0x246a, 0x244a, 0x240a, 0x2400, 0x40 }, /* J */
1112 { 0x256b, 0x254b, 0x250b, 0x2500, 0x40 }, /* K */
1113 { 0x266c, 0x264c, 0x260c, 0x2600, 0x40 }, /* L */
1114 { 0x273b, 0x273a, none, none, none }, /* ;: */
1115 { 0x2827, 0x2822, none, none, none }, /* '" */
1116 { 0x2960, 0x297e, none, none, none }, /* `~ */
1117 { none, none, none, none, none }, /* L shift */
1118 { 0x2b5c, 0x2b7c, 0x2b1c, none, none }, /* |\ */
1119 { 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00, 0x40 }, /* Z */
1120 { 0x2d78, 0x2d58, 0x2d18, 0x2d00, 0x40 }, /* X */
1121 { 0x2e63, 0x2e43, 0x2e03, 0x2e00, 0x40 }, /* C */
1122 { 0x2f76, 0x2f56, 0x2f16, 0x2f00, 0x40 }, /* V */
1123 { 0x3062, 0x3042, 0x3002, 0x3000, 0x40 }, /* B */
1124 { 0x316e, 0x314e, 0x310e, 0x3100, 0x40 }, /* N */
1125 { 0x326d, 0x324d, 0x320d, 0x3200, 0x40 }, /* M */
1126 { 0x332c, 0x333c, none, none, none }, /* ,< */
1127 { 0x342e, 0x343e, none, none, none }, /* .> */
1128 { 0x352f, 0x353f, none, none, none }, /* /? */
1129 { none, none, none, none, none }, /* R Shift */
1130 { 0x372a, 0x372a, none, none, none }, /* * */
1131 { none, none, none, none, none }, /* L Alt */
1132 { 0x3920, 0x3920, 0x3920, 0x3920, none }, /* space */
1133 { none, none, none, none, none }, /* caps lock */
1134 { 0x3b00, 0x5400, 0x5e00, 0x6800, none }, /* F1 */
1135 { 0x3c00, 0x5500, 0x5f00, 0x6900, none }, /* F2 */
1136 { 0x3d00, 0x5600, 0x6000, 0x6a00, none }, /* F3 */
1137 { 0x3e00, 0x5700, 0x6100, 0x6b00, none }, /* F4 */
1138 { 0x3f00, 0x5800, 0x6200, 0x6c00, none }, /* F5 */
1139 { 0x4000, 0x5900, 0x6300, 0x6d00, none }, /* F6 */
1140 { 0x4100, 0x5a00, 0x6400, 0x6e00, none }, /* F7 */
1141 { 0x4200, 0x5b00, 0x6500, 0x6f00, none }, /* F8 */
1142 { 0x4300, 0x5c00, 0x6600, 0x7000, none }, /* F9 */
1143 { 0x4400, 0x5d00, 0x6700, 0x7100, none }, /* F10 */
1144 { none, none, none, none, none }, /* Num Lock */
1145 { none, none, none, none, none }, /* Scroll Lock */
1146 { 0x4700, 0x4737, 0x7700, none, 0x20 }, /* 7 Home */
1147 { 0x4800, 0x4838, none, none, 0x20 }, /* 8 UP */
1148 { 0x4900, 0x4939, 0x8400, none, 0x20 }, /* 9 PgUp */
1149 { 0x4a2d, 0x4a2d, none, none, none }, /* - */
1150 { 0x4b00, 0x4b34, 0x7300, none, 0x20 }, /* 4 Left */
1151 { 0x4c00, 0x4c35, none, none, 0x20 }, /* 5 */
1152 { 0x4d00, 0x4d36, 0x7400, none, 0x20 }, /* 6 Right */
1153 { 0x4e2b, 0x4e2b, none, none, none }, /* + */
1154 { 0x4f00, 0x4f31, 0x7500, none, 0x20 }, /* 1 End */
1155 { 0x5000, 0x5032, none, none, 0x20 }, /* 2 Down */
1156 { 0x5100, 0x5133, 0x7600, none, 0x20 }, /* 3 PgDn */
1157 { 0x5200, 0x5230, none, none, 0x20 }, /* 0 Ins */
1158 { 0x5300, 0x532e, none, none, 0x20 }, /* Del */
1159 { none, none, none, none, none }, /* ??? */
1160 { none, none, none, none, none }, /* ??? */
1161 { none, none, none, none, none }, /* ??? */
1162 { 0x8500, 0x8700, 0x8900, 0x8b00, none }, /* F11 */
1163 { 0x8600, 0x8800, 0x8a00, 0x8c00, none }, /* F12 */
1164 };
1166 Bit8u
1167 inb(port)
1168 Bit16u port;
1170 ASM_START
1171 push bp
1172 mov bp, sp
1174 push dx
1175 mov dx, 4[bp]
1176 in al, dx
1177 pop dx
1179 pop bp
1180 ASM_END
1183 #if BX_USE_ATADRV
1184 Bit16u
1185 inw(port)
1186 Bit16u port;
1188 ASM_START
1189 push bp
1190 mov bp, sp
1192 push dx
1193 mov dx, 4[bp]
1194 in ax, dx
1195 pop dx
1197 pop bp
1198 ASM_END
1200 #endif
1202 void
1203 outb(port, val)
1204 Bit16u port;
1205 Bit8u val;
1207 ASM_START
1208 push bp
1209 mov bp, sp
1211 push ax
1212 push dx
1213 mov dx, 4[bp]
1214 mov al, 6[bp]
1215 out dx, al
1216 pop dx
1217 pop ax
1219 pop bp
1220 ASM_END
1223 #if BX_USE_ATADRV
1224 void
1225 outw(port, val)
1226 Bit16u port;
1227 Bit16u val;
1229 ASM_START
1230 push bp
1231 mov bp, sp
1233 push ax
1234 push dx
1235 mov dx, 4[bp]
1236 mov ax, 6[bp]
1237 out dx, ax
1238 pop dx
1239 pop ax
1241 pop bp
1242 ASM_END
1244 #endif
1246 void
1247 outb_cmos(cmos_reg, val)
1248 Bit8u cmos_reg;
1249 Bit8u val;
1251 ASM_START
1252 push bp
1253 mov bp, sp
1255 mov al, 4[bp] ;; cmos_reg
1256 out 0x70, al
1257 mov al, 6[bp] ;; val
1258 out 0x71, al
1260 pop bp
1261 ASM_END
1264 Bit8u
1265 inb_cmos(cmos_reg)
1266 Bit8u cmos_reg;
1268 ASM_START
1269 push bp
1270 mov bp, sp
1272 mov al, 4[bp] ;; cmos_reg
1273 out 0x70, al
1274 in al, 0x71
1276 pop bp
1277 ASM_END
1280 void
1281 init_rtc()
1283 outb_cmos(0x0a, 0x26);
1284 outb_cmos(0x0b, 0x02);
1285 inb_cmos(0x0c);
1286 inb_cmos(0x0d);
1289 bx_bool
1290 rtc_updating()
1292 // This function checks to see if the update-in-progress bit
1293 // is set in CMOS Status Register A. If not, it returns 0.
1294 // If it is set, it tries to wait until there is a transition
1295 // to 0, and will return 0 if such a transition occurs. A 1
1296 // is returned only after timing out. The maximum period
1297 // that this bit should be set is constrained to 244useconds.
1298 // The count I use below guarantees coverage or more than
1299 // this time, with any reasonable IPS setting.
1301 Bit16u count;
1303 count = 25000;
1304 while (--count != 0) {
1305 if ( (inb_cmos(0x0a) & 0x80) == 0 )
1306 return(0);
1308 return(1); // update-in-progress never transitioned to 0
1312 Bit8u
1313 read_byte(seg, offset)
1314 Bit16u seg;
1315 Bit16u offset;
1317 ASM_START
1318 push bp
1319 mov bp, sp
1321 push bx
1322 push ds
1323 mov ax, 4[bp] ; segment
1324 mov ds, ax
1325 mov bx, 6[bp] ; offset
1326 mov al, [bx]
1327 ;; al = return value (byte)
1328 pop ds
1329 pop bx
1331 pop bp
1332 ASM_END
1335 Bit16u
1336 read_word(seg, offset)
1337 Bit16u seg;
1338 Bit16u offset;
1340 ASM_START
1341 push bp
1342 mov bp, sp
1344 push bx
1345 push ds
1346 mov ax, 4[bp] ; segment
1347 mov ds, ax
1348 mov bx, 6[bp] ; offset
1349 mov ax, [bx]
1350 ;; ax = return value (word)
1351 pop ds
1352 pop bx
1354 pop bp
1355 ASM_END
1358 void
1359 write_byte(seg, offset, data)
1360 Bit16u seg;
1361 Bit16u offset;
1362 Bit8u data;
1364 ASM_START
1365 push bp
1366 mov bp, sp
1368 push ax
1369 push bx
1370 push ds
1371 mov ax, 4[bp] ; segment
1372 mov ds, ax
1373 mov bx, 6[bp] ; offset
1374 mov al, 8[bp] ; data byte
1375 mov [bx], al ; write data byte
1376 pop ds
1377 pop bx
1378 pop ax
1380 pop bp
1381 ASM_END
1384 void
1385 write_word(seg, offset, data)
1386 Bit16u seg;
1387 Bit16u offset;
1388 Bit16u data;
1390 ASM_START
1391 push bp
1392 mov bp, sp
1394 push ax
1395 push bx
1396 push ds
1397 mov ax, 4[bp] ; segment
1398 mov ds, ax
1399 mov bx, 6[bp] ; offset
1400 mov ax, 8[bp] ; data word
1401 mov [bx], ax ; write data word
1402 pop ds
1403 pop bx
1404 pop ax
1406 pop bp
1407 ASM_END
1410 Bit16u
1411 get_CS()
1413 ASM_START
1414 mov ax, cs
1415 ASM_END
1418 // Bit16u
1419 //get_DS()
1420 //{
1421 //ASM_START
1422 // mov ax, ds
1423 //ASM_END
1424 //}
1425 //
1426 // void
1427 //set_DS(ds_selector)
1428 // Bit16u ds_selector;
1429 //{
1430 //ASM_START
1431 // push bp
1432 // mov bp, sp
1433 //
1434 // push ax
1435 // mov ax, 4[bp] ; ds_selector
1436 // mov ds, ax
1437 // pop ax
1438 //
1439 // pop bp
1440 //ASM_END
1441 //}
1443 Bit16u
1444 get_SS()
1446 ASM_START
1447 mov ax, ss
1448 ASM_END
1451 #ifdef HVMASSIST
1452 void
1453 copy_e820_table()
1455 Bit8u nr_entries = read_byte(0x9000, 0x1e8);
1456 Bit32u base_mem;
1457 if (nr_entries > 32)
1458 nr_entries = 32;
1459 write_word(0xe000, 0x8, nr_entries);
1460 memcpyb(0xe000, 0x10, 0x9000, 0x2d0, nr_entries * 0x14);
1461 /* Report the proper base memory size at address 0x0413: otherwise
1462 * non-e820 code will clobber things if BASE_MEM_IN_K is bigger than
1463 * the first e820 entry. Get the size by reading the second 64bit
1464 * field of the first e820 slot. */
1465 base_mem = read_dword(0x9000, 0x2d0 + 8);
1466 write_word(0x40, 0x13, base_mem >> 10);
1469 void
1470 set_rom_write_access(action)
1471 Bit16u action;
1473 Bit16u off = (Bit16u)&((struct bios_info *)0)->xen_pfiob;
1474 ASM_START
1475 mov si,.set_rom_write_access.off[bp]
1476 push ds
1477 mov ax,#(ACPI_PHYSICAL_ADDRESS >> 4)
1478 mov ds,ax
1479 mov dx,[si]
1480 pop ds
1481 mov ax,.set_rom_write_access.action[bp]
1482 out dx,al
1483 ASM_END
1486 void enable_rom_write_access()
1488 set_rom_write_access(0);
1491 void disable_rom_write_access()
1493 set_rom_write_access(PFFLAG_ROM_LOCK);
1496 #endif /* HVMASSIST */
1498 #if BX_DEBUG_SERIAL
1499 /* serial debug port*/
1500 #define BX_DEBUG_PORT 0x03f8
1502 /* data */
1503 #define UART_RBR 0x00
1504 #define UART_THR 0x00
1506 /* control */
1507 #define UART_IER 0x01
1508 #define UART_IIR 0x02
1509 #define UART_FCR 0x02
1510 #define UART_LCR 0x03
1511 #define UART_MCR 0x04
1512 #define UART_DLL 0x00
1513 #define UART_DLM 0x01
1515 /* status */
1516 #define UART_LSR 0x05
1517 #define UART_MSR 0x06
1518 #define UART_SCR 0x07
1520 int uart_can_tx_byte(base_port)
1521 Bit16u base_port;
1523 return inb(base_port + UART_LSR) & 0x20;
1526 void uart_wait_to_tx_byte(base_port)
1527 Bit16u base_port;
1529 while (!uart_can_tx_byte(base_port));
1532 void uart_wait_until_sent(base_port)
1533 Bit16u base_port;
1535 while (!(inb(base_port + UART_LSR) & 0x40));
1538 void uart_tx_byte(base_port, data)
1539 Bit16u base_port;
1540 Bit8u data;
1542 uart_wait_to_tx_byte(base_port);
1543 outb(base_port + UART_THR, data);
1544 uart_wait_until_sent(base_port);
1546 #endif
1548 void
1549 wrch(c)
1550 Bit8u c;
1552 ASM_START
1553 push bp
1554 mov bp, sp
1556 push bx
1557 mov ah, #0x0e
1558 mov al, 4[bp]
1559 xor bx,bx
1560 int #0x10
1561 pop bx
1563 pop bp
1564 ASM_END
1567 void
1568 send(action, c)
1569 Bit16u action;
1570 Bit8u c;
1572 #if BX_DEBUG_SERIAL
1573 if (c == '\n') uart_tx_byte(BX_DEBUG_PORT, '\r');
1574 uart_tx_byte(BX_DEBUG_PORT, c);
1575 #endif
1576 #ifdef HVMASSIST
1577 outb(0xE9, c);
1578 #endif
1579 #if BX_VIRTUAL_PORTS
1580 if (action & BIOS_PRINTF_DEBUG) outb(DEBUG_PORT, c);
1581 if (action & BIOS_PRINTF_INFO) outb(INFO_PORT, c);
1582 #endif
1583 if (action & BIOS_PRINTF_SCREEN) {
1584 if (c == '\n') wrch('\r');
1585 wrch(c);
1589 void
1590 put_int(action, val, width, neg)
1591 Bit16u action;
1592 short val, width;
1593 bx_bool neg;
1595 short nval = val / 10;
1596 if (nval)
1597 put_int(action, nval, width - 1, neg);
1598 else {
1599 while (--width > 0) send(action, ' ');
1600 if (neg) send(action, '-');
1602 send(action, val - (nval * 10) + '0');
1605 void
1606 put_uint(action, val, width, neg)
1607 Bit16u action;
1608 unsigned short val;
1609 short width;
1610 bx_bool neg;
1612 unsigned short nval = val / 10;
1613 if (nval)
1614 put_uint(action, nval, width - 1, neg);
1615 else {
1616 while (--width > 0) send(action, ' ');
1617 if (neg) send(action, '-');
1619 send(action, val - (nval * 10) + '0');
1622 //--------------------------------------------------------------------------
1623 // bios_printf()
1624 // A compact variable argument printf function which prints its output via
1625 // an I/O port so that it can be logged by Bochs/Plex.
1626 // Currently, only %x is supported (or %02x, %04x, etc).
1627 //
1628 // Supports %[format_width][format]
1629 // where format can be d,x,c,s
1630 //--------------------------------------------------------------------------
1631 void
1632 bios_printf(action, s)
1633 Bit16u action;
1634 Bit8u *s;
1636 Bit8u c, format_char;
1637 bx_bool in_format;
1638 short i;
1639 Bit16u *arg_ptr;
1640 Bit16u arg_seg, arg, nibble, shift_count, format_width;
1642 arg_ptr = &s;
1643 arg_seg = get_SS();
1645 in_format = 0;
1646 format_width = 0;
1648 if ((action & BIOS_PRINTF_DEBHALT) == BIOS_PRINTF_DEBHALT) {
1649 #if BX_VIRTUAL_PORTS
1650 outb(PANIC_PORT2, 0x00);
1651 #endif
1652 bios_printf (BIOS_PRINTF_SCREEN, "FATAL: ");
1655 while (c = read_byte(get_CS(), s)) {
1656 if ( c == '%' ) {
1657 in_format = 1;
1658 format_width = 0;
1660 else if (in_format) {
1661 if ( (c>='0') && (c<='9') ) {
1662 format_width = (format_width * 10) + (c - '0');
1664 else {
1665 arg_ptr++; // increment to next arg
1666 arg = read_word(arg_seg, arg_ptr);
1667 if (c == 'x') {
1668 if (format_width == 0)
1669 format_width = 4;
1670 for (i=format_width-1; i>=0; i--) {
1671 nibble = (arg >> (4 * i)) & 0x000f;
1672 send (action, (nibble<=9)? (nibble+'0') : (nibble-10+'A'));
1675 else if (c == 'u') {
1676 put_uint(action, arg, format_width, 0);
1678 else if (c == 'd') {
1679 if (arg & 0x8000)
1680 put_int(action, -arg, format_width - 1, 1);
1681 else
1682 put_int(action, arg, format_width, 0);
1684 else if (c == 's') {
1685 bios_printf(action & (~BIOS_PRINTF_HALT), arg);
1687 else if (c == 'c') {
1688 send(action, arg);
1690 else
1691 BX_PANIC("bios_printf: unknown format\n");
1692 in_format = 0;
1695 else {
1696 send(action, c);
1698 s ++;
1701 if (action & BIOS_PRINTF_HALT) {
1702 // freeze in a busy loop.
1703 ASM_START
1704 cli
1705 halt2_loop:
1706 hlt
1707 jmp halt2_loop
1708 ASM_END
1712 //--------------------------------------------------------------------------
1713 // keyboard_init
1714 //--------------------------------------------------------------------------
1715 // this file is based on LinuxBIOS implementation of keyboard.c
1716 // could convert to #asm to gain space
1717 void
1718 keyboard_init()
1720 Bit16u max;
1722 /* ------------------- Flush buffers ------------------------*/
1723 /* Wait until buffer is empty */
1724 max=0xffff;
1725 while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
1727 /* flush incoming keys */
1728 max=0x2000;
1729 while (--max > 0) {
1730 outb(0x80, 0x00);
1731 if (inb(0x64) & 0x01) {
1732 inb(0x60);
1733 max = 0x2000;
1737 // Due to timer issues, and if the IPS setting is > 15000000,
1738 // the incoming keys might not be flushed here. That will
1739 // cause a panic a few lines below. See sourceforge bug report :
1740 // [ 642031 ] FATAL: Keyboard RESET error:993
1742 /* ------------------- controller side ----------------------*/
1743 /* send cmd = 0xAA, self test 8042 */
1744 outb(0x64, 0xaa);
1746 /* Wait until buffer is empty */
1747 max=0xffff;
1748 while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
1749 if (max==0x0) keyboard_panic(00);
1751 /* Wait for data */
1752 max=0xffff;
1753 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x01);
1754 if (max==0x0) keyboard_panic(01);
1756 /* read self-test result, 0x55 should be returned from 0x60 */
1757 if ((inb(0x60) != 0x55)){
1758 keyboard_panic(991);
1761 /* send cmd = 0xAB, keyboard interface test */
1762 outb(0x64,0xab);
1764 /* Wait until buffer is empty */
1765 max=0xffff;
1766 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x10);
1767 if (max==0x0) keyboard_panic(10);
1769 /* Wait for data */
1770 max=0xffff;
1771 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x11);
1772 if (max==0x0) keyboard_panic(11);
1774 /* read keyboard interface test result, */
1775 /* 0x00 should be returned form 0x60 */
1776 if ((inb(0x60) != 0x00)) {
1777 keyboard_panic(992);
1780 /* Enable Keyboard clock */
1781 outb(0x64,0xae);
1782 outb(0x64,0xa8);
1784 /* ------------------- keyboard side ------------------------*/
1785 /* reset kerboard and self test (keyboard side) */
1786 outb(0x60, 0xff);
1788 /* Wait until buffer is empty */
1789 max=0xffff;
1790 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x20);
1791 if (max==0x0) keyboard_panic(20);
1793 /* Wait for data */
1794 max=0xffff;
1795 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x21);
1796 if (max==0x0) keyboard_panic(21);
1798 /* keyboard should return ACK */
1799 if ((inb(0x60) != 0xfa)) {
1800 keyboard_panic(993);
1803 /* Wait for data */
1804 max=0xffff;
1805 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x31);
1806 if (max==0x0) keyboard_panic(31);
1808 if ((inb(0x60) != 0xaa)) {
1809 keyboard_panic(994);
1812 /* Disable keyboard */
1813 outb(0x60, 0xf5);
1815 /* Wait until buffer is empty */
1816 max=0xffff;
1817 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x40);
1818 if (max==0x0) keyboard_panic(40);
1820 /* Wait for data */
1821 max=0xffff;
1822 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x41);
1823 if (max==0x0) keyboard_panic(41);
1825 /* keyboard should return ACK */
1826 if ((inb(0x60) != 0xfa)) {
1827 keyboard_panic(995);
1830 /* Write Keyboard Mode */
1831 outb(0x64, 0x60);
1833 /* Wait until buffer is empty */
1834 max=0xffff;
1835 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x50);
1836 if (max==0x0) keyboard_panic(50);
1838 /* send cmd: scan code convert, disable mouse, enable IRQ 1 */
1839 outb(0x60, 0x61);
1841 /* Wait until buffer is empty */
1842 max=0xffff;
1843 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x60);
1844 if (max==0x0) keyboard_panic(60);
1846 /* Enable keyboard */
1847 outb(0x60, 0xf4);
1849 /* Wait until buffer is empty */
1850 max=0xffff;
1851 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x70);
1852 if (max==0x0) keyboard_panic(70);
1854 /* Wait for data */
1855 max=0xffff;
1856 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x71);
1857 if (max==0x0) keyboard_panic(70);
1859 /* keyboard should return ACK */
1860 if ((inb(0x60) != 0xfa)) {
1861 keyboard_panic(996);
1864 outb(0x80, 0x77);
1867 //--------------------------------------------------------------------------
1868 // keyboard_panic
1869 //--------------------------------------------------------------------------
1870 void
1871 keyboard_panic(status)
1872 Bit16u status;
1874 // If you're getting a 993 keyboard panic here,
1875 // please see the comment in keyboard_init
1877 BX_PANIC("Keyboard error:%u\n",status);
1881 #define CMOS_SHUTDOWN_S3 0xFE
1882 //--------------------------------------------------------------------------
1883 // machine_reset
1884 //--------------------------------------------------------------------------
1885 void
1886 machine_reset()
1888 ASM_START
1889 ;we must check whether CMOS_SHUTDOWN_S3 is set or not
1890 ;if it is s3 resume, just jmp back to normal Post Entry
1891 ;below port io will prevent s3 resume
1892 mov al, #0x0f
1893 out 0x70, al
1894 in al, 0x71
1895 cmp al, #0xFE
1896 jz post
1897 ASM_END
1898 /* Frob the keyboard reset line to reset the processor */
1899 outb(0x64, 0x60); /* Map the flags register at data port (0x60) */
1900 outb(0x60, 0x14); /* Set the flags to system|disable */
1901 outb(0x64, 0xfe); /* Pulse output 0 (system reset) low */
1902 BX_PANIC("Couldn't reset the machine\n");
1905 //--------------------------------------------------------------------------
1906 // clobber_entry_point
1907 // Because PV drivers in HVM guests detach some of the emulated devices,
1908 // it is not safe to do a soft reboot by just dropping to real mode and
1909 // jumping at ffff:0000. -- the boot drives might have disappeared!
1910 // This rather foul function overwrites(!) the BIOS entry point
1911 // to point at machine-reset, which will cause the Xen tools to
1912 // rebuild the whole machine from scratch.
1913 //--------------------------------------------------------------------------
1914 void
1915 clobber_entry_point()
1917 /* The instruction at the entry point is one byte (0xea) for the
1918 * jump opcode, then two bytes of address, then two of segment.
1919 * Overwrite the address bytes.*/
1920 write_word(0xffff, 0x0001, machine_reset);
1924 //--------------------------------------------------------------------------
1925 // shutdown_status_panic
1926 // called when the shutdown statsu is not implemented, displays the status
1927 //--------------------------------------------------------------------------
1928 void
1929 shutdown_status_panic(status)
1930 Bit16u status;
1932 BX_PANIC("Unimplemented shutdown status: %02x\n",(Bit8u)status);
1935 //--------------------------------------------------------------------------
1936 // print_bios_banner
1937 // displays a the bios version
1938 //--------------------------------------------------------------------------
1939 void
1940 print_bios_banner()
1942 printf(BX_APPNAME" BIOS, %d cpu%s, ", BX_SMP_PROCESSORS, BX_SMP_PROCESSORS>1?"s":"");
1943 printf("%s %s\n", bios_cvs_version_string, bios_date_string);
1944 #if BX_TCGBIOS
1945 printf("TCG-enabled BIOS.\n");
1946 #endif
1947 printf("\n");
1951 //--------------------------------------------------------------------------
1952 // BIOS Boot Specification 1.0.1 compatibility
1953 //
1954 // Very basic support for the BIOS Boot Specification, which allows expansion
1955 // ROMs to register themselves as boot devices, instead of just stealing the
1956 // INT 19h boot vector.
1957 //
1958 // This is a hack: to do it properly requires a proper PnP BIOS and we aren't
1959 // one; we just lie to the option ROMs to make them behave correctly.
1960 // We also don't support letting option ROMs register as bootable disk
1961 // drives (BCVs), only as bootable devices (BEVs).
1962 //
1963 // http://www.phoenix.com/en/Customer+Services/White+Papers-Specs/pc+industry+specifications.htm
1964 //--------------------------------------------------------------------------
1966 /* 256 bytes at 0x9ff00 -- 0x9ffff is used for the IPL boot table. */
1967 #define IPL_SEG 0x9ff0
1968 #define IPL_TABLE_OFFSET 0x0000
1969 #define IPL_TABLE_ENTRIES 8
1970 #define IPL_COUNT_OFFSET 0x0080 /* u16: number of valid table entries */
1971 #define IPL_SEQUENCE_OFFSET 0x0082 /* u16: next boot device */
1973 struct ipl_entry {
1974 Bit16u type;
1975 Bit16u flags;
1976 Bit32u vector;
1977 Bit32u description;
1978 Bit32u reserved;
1979 };
1981 static void
1982 init_boot_vectors()
1984 struct ipl_entry e;
1985 Bit16u count = 0;
1986 Bit16u ss = get_SS();
1988 /* Clear out the IPL table. */
1989 memsetb(IPL_SEG, IPL_TABLE_OFFSET, 0, 0xff);
1991 /* Floppy drive */
1992 e.type = 1; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
1993 memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
1994 count++;
1996 /* First HDD */
1997 e.type = 2; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
1998 memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
1999 count++;
2001 #if BX_ELTORITO_BOOT
2002 /* CDROM */
2003 e.type = 3; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
2004 memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
2005 count++;
2006 #endif
2008 /* Remember how many devices we have */
2009 write_word(IPL_SEG, IPL_COUNT_OFFSET, count);
2010 /* Not tried booting anything yet */
2011 write_word(IPL_SEG, IPL_SEQUENCE_OFFSET, 0xffff);
2014 static Bit8u
2015 get_boot_vector(i, e)
2016 Bit16u i; struct ipl_entry *e;
2018 Bit16u count;
2019 Bit16u ss = get_SS();
2020 /* Get the count of boot devices, and refuse to overrun the array */
2021 count = read_word(IPL_SEG, IPL_COUNT_OFFSET);
2022 if (i >= count) return 0;
2023 /* OK to read this device */
2024 memcpyb(ss, e, IPL_SEG, IPL_TABLE_OFFSET + i * sizeof (*e), sizeof (*e));
2025 return 1;
2029 //--------------------------------------------------------------------------
2030 // print_boot_device
2031 // displays the boot device
2032 //--------------------------------------------------------------------------
2034 static char drivetypes[][10]={"", "Floppy","Hard Disk","CD-Rom", "Network"};
2036 void
2037 print_boot_device(type)
2038 Bit16u type;
2040 /* NIC appears as type 0x80 */
2041 if (type == 0x80 ) type = 0x4;
2042 if (type == 0 || type > 0x4) BX_PANIC("Bad drive type\n");
2043 printf("Booting from %s...\n", drivetypes[type]);
2046 //--------------------------------------------------------------------------
2047 // print_boot_failure
2048 // displays the reason why boot failed
2049 //--------------------------------------------------------------------------
2050 void
2051 print_boot_failure(type, reason)
2052 Bit16u type; Bit8u reason;
2054 if (type == 0 || type > 0x3) BX_PANIC("Bad drive type\n");
2056 printf("Boot from %s failed", drivetypes[type]);
2057 if (type < 4) {
2058 /* Report the reason too */
2059 if (reason==0)
2060 printf(": not a bootable disk");
2061 else
2062 printf(": could not read the boot disk");
2064 printf("\n");
2067 //--------------------------------------------------------------------------
2068 // print_cdromboot_failure
2069 // displays the reason why boot failed
2070 //--------------------------------------------------------------------------
2071 void
2072 print_cdromboot_failure( code )
2073 Bit16u code;
2075 bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, "CDROM boot failure code : %04x\n",code);
2077 return;
2080 #define WAIT_HZ 18
2081 /**
2082 * Check for keystroke.
2083 * @returns True if keystroke available, False if not.
2084 */
2085 Bit8u check_for_keystroke()
2087 ASM_START
2088 mov ax, #0x100
2089 int #0x16
2090 jz no_key
2091 mov al, #1
2092 jmp done
2093 no_key:
2094 xor al, al
2095 done:
2096 ASM_END
2099 /**
2100 * Get keystroke.
2101 * @returns BIOS scan code.
2102 */
2103 Bit8u get_keystroke()
2105 ASM_START
2106 mov ax, #0x0
2107 int #0x16
2108 xchg ah, al
2109 ASM_END
2112 /**
2113 * Waits (sleeps) for the given number of ticks.
2114 * Checks for keystroke.
2116 * @returns BIOS scan code if available, 0 if not.
2117 * @param ticks Number of ticks to sleep.
2118 * @param stop_on_key Whether to stop immediately upon keypress.
2119 */
2120 Bit8u wait(ticks, stop_on_key)
2121 Bit16u ticks;
2122 Bit8u stop_on_key;
2124 long ticks_to_wait, delta;
2125 Bit32u prev_ticks, t;
2126 Bit8u scan_code = 0;
2128 /*
2129 * The 0:046c wraps around at 'midnight' according to a 18.2Hz clock.
2130 * We also have to be careful about interrupt storms.
2131 */
2132 ticks_to_wait = ticks;
2133 prev_ticks = read_dword(0x0, 0x46c);
2134 do
2136 t = read_dword(0x0, 0x46c);
2137 if (t > prev_ticks)
2139 delta = t - prev_ticks; /* The temp var is required or bcc screws up. */
2140 ticks_to_wait -= delta;
2142 else if (t < prev_ticks)
2143 ticks_to_wait -= t; /* wrapped */
2144 prev_ticks = t;
2146 if (check_for_keystroke())
2148 scan_code = get_keystroke();
2149 bios_printf(BIOS_PRINTF_DEBUG, "Key pressed: %x\n", scan_code);
2150 if (stop_on_key)
2151 return scan_code;
2153 } while (ticks_to_wait > 0);
2154 return scan_code;
2157 static void clearscreen() {
2158 /* Hide cursor, clear screen and move cursor to starting position */
2159 ASM_START
2160 push bx
2161 push cx
2162 push dx
2164 mov ax, #0x100
2165 mov cx, #0x1000
2166 int #0x10
2168 mov ax, #0x700
2169 mov bh, #7
2170 xor cx, cx
2171 mov dx, #0x184f
2172 int #0x10
2174 mov ax, #0x200
2175 xor bx, bx
2176 xor dx, dx
2177 int #0x10
2179 pop dx
2180 pop cx
2181 pop bx
2182 ASM_END
2185 int bootmenu(selected)
2186 int selected;
2188 Bit8u scode;
2189 int max;
2191 /* get the number of boot devices */
2192 max = read_word(IPL_SEG, IPL_COUNT_OFFSET);
2194 for(;;) {
2195 if (selected > max || selected < 1) selected = 1;
2196 clearscreen();
2197 bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, "\n\n\n\n\n\n\n");
2198 bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, " Select boot device\n\n");
2199 bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, " 1. Floppy\n");
2200 bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, " 2. Hard drive\n");
2201 bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, " 3. CD-ROM\n");
2202 if (max == 4)
2203 bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, " 4. Network\n");
2204 bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, "\n\n Currently selected: %d\n", selected);
2206 do {
2207 scode = wait(WAIT_HZ, 1);
2208 } while (scode == 0);
2209 switch(scode) {
2210 case 0x02:
2211 case 0x03:
2212 case 0x04:
2213 selected = scode - 1;
2214 break;
2215 case 0x05:
2216 if (max == 4)
2217 selected = scode -1 ;
2218 else
2219 scode = 0;
2220 break;
2221 case 0x48:
2222 selected -= 1;
2223 if (selected < 1)
2224 selected = 1;
2225 scode = 0;
2226 break;
2227 case 0x50:
2228 selected += 1;
2229 if (selected > max)
2230 selected = max;
2231 scode = 0;
2232 break;
2233 case 0x1c:
2234 break;
2235 default:
2236 scode = 0;
2237 break;
2239 if (scode != 0)
2240 break;
2243 switch (selected) {
2244 case 1:
2245 return 0x3D;
2246 case 2:
2247 return 0x3E;
2248 case 3:
2249 return 0x3F;
2250 case 4:
2251 return 0x58;
2252 default:
2253 return 0;
2257 void interactive_bootkey()
2259 Bit16u i;
2260 Bit8u scan = 0;
2262 bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO,
2263 "\n\nPress F10 to select boot device.\n");
2265 scan = wait(1, 0);
2266 if (scan == 0x44)
2267 scan = bootmenu(inb_cmos(0x3d) & 0x0f);
2269 /* set the default based on the keypress or menu */
2270 switch(scan) {
2271 case 0x3D:
2272 outb_cmos(0x3d, 0x01);
2273 break;
2274 case 0x3E:
2275 outb_cmos(0x3d, 0x02);
2276 break;
2277 case 0x3F:
2278 outb_cmos(0x3d, 0x03);
2279 break;
2280 case 0x58:
2281 outb_cmos(0x3d, 0x04);
2282 break;
2283 default:
2284 break;
2289 void
2290 nmi_handler_msg()
2292 BX_PANIC("NMI Handler called\n");
2295 void
2296 int18_panic_msg()
2298 BX_PANIC("INT18: BOOT FAILURE\n");
2301 void
2302 log_bios_start()
2304 #if BX_DEBUG_SERIAL
2305 outb(BX_DEBUG_PORT+UART_LCR, 0x03); /* setup for serial logging: 8N1 */
2306 #endif
2307 BX_INFO("%s\n", bios_version_string);
2310 bx_bool
2311 set_enable_a20(val)
2312 bx_bool val;
2314 Bit8u oldval;
2316 // Use PS2 System Control port A to set A20 enable
2318 // get current setting first
2319 oldval = inb(0x92);
2321 // change A20 status
2322 if (val)
2323 outb(0x92, oldval | 0x02);
2324 else
2325 outb(0x92, oldval & 0xfd);
2327 return((oldval & 0x02) != 0);
2330 void
2331 debugger_on()
2333 outb(0xfedc, 0x01);
2336 void
2337 debugger_off()
2339 outb(0xfedc, 0x00);
2342 void
2343 s3_resume()
2345 Bit32u s3_wakeup_vector;
2346 Bit16u s3_wakeup_ip, s3_wakeup_cs;
2347 Bit8u cmos_shutdown_status;
2349 ASM_START
2350 push ds
2351 push ax
2352 mov ax, #EBDA_SEG
2353 mov ds, ax
2354 mov al, [EBDA_CMOS_SHUTDOWN_STATUS_OFFSET]
2355 mov .s3_resume.cmos_shutdown_status[bp], al
2356 pop ax
2357 pop ds
2358 ASM_END
2360 if (cmos_shutdown_status != CMOS_SHUTDOWN_S3)
2361 return;
2363 s3_wakeup_vector = get_s3_waking_vector();
2364 if (!s3_wakeup_vector)
2365 return;
2367 s3_wakeup_ip = s3_wakeup_vector & 0xF;
2368 s3_wakeup_cs = s3_wakeup_vector >> 4;
2370 ASM_START
2371 push .s3_resume.s3_wakeup_cs[bp]
2372 push .s3_resume.s3_wakeup_ip[bp]
2373 retf
2374 ASM_END
2377 #if BX_USE_ATADRV
2379 // ---------------------------------------------------------------------------
2380 // Start of ATA/ATAPI Driver
2381 // ---------------------------------------------------------------------------
2383 // Global defines -- ATA register and register bits.
2384 // command block & control block regs
2385 #define ATA_CB_DATA 0 // data reg in/out pio_base_addr1+0
2386 #define ATA_CB_ERR 1 // error in pio_base_addr1+1
2387 #define ATA_CB_FR 1 // feature reg out pio_base_addr1+1
2388 #define ATA_CB_SC 2 // sector count in/out pio_base_addr1+2
2389 #define ATA_CB_SN 3 // sector number in/out pio_base_addr1+3
2390 #define ATA_CB_CL 4 // cylinder low in/out pio_base_addr1+4
2391 #define ATA_CB_CH 5 // cylinder high in/out pio_base_addr1+5
2392 #define ATA_CB_DH 6 // device head in/out pio_base_addr1+6
2393 #define ATA_CB_STAT 7 // primary status in pio_base_addr1+7
2394 #define ATA_CB_CMD 7 // command out pio_base_addr1+7
2395 #define ATA_CB_ASTAT 6 // alternate status in pio_base_addr2+6
2396 #define ATA_CB_DC 6 // device control out pio_base_addr2+6
2397 #define ATA_CB_DA 7 // device address in pio_base_addr2+7
2399 #define ATA_CB_ER_ICRC 0x80 // ATA Ultra DMA bad CRC
2400 #define ATA_CB_ER_BBK 0x80 // ATA bad block
2401 #define ATA_CB_ER_UNC 0x40 // ATA uncorrected error
2402 #define ATA_CB_ER_MC 0x20 // ATA media change
2403 #define ATA_CB_ER_IDNF 0x10 // ATA id not found
2404 #define ATA_CB_ER_MCR 0x08 // ATA media change request
2405 #define ATA_CB_ER_ABRT 0x04 // ATA command aborted
2406 #define ATA_CB_ER_NTK0 0x02 // ATA track 0 not found
2407 #define ATA_CB_ER_NDAM 0x01 // ATA address mark not found
2409 #define ATA_CB_ER_P_SNSKEY 0xf0 // ATAPI sense key (mask)
2410 #define ATA_CB_ER_P_MCR 0x08 // ATAPI Media Change Request
2411 #define ATA_CB_ER_P_ABRT 0x04 // ATAPI command abort
2412 #define ATA_CB_ER_P_EOM 0x02 // ATAPI End of Media
2413 #define ATA_CB_ER_P_ILI 0x01 // ATAPI Illegal Length Indication
2415 // ATAPI Interrupt Reason bits in the Sector Count reg (CB_SC)
2416 #define ATA_CB_SC_P_TAG 0xf8 // ATAPI tag (mask)
2417 #define ATA_CB_SC_P_REL 0x04 // ATAPI release
2418 #define ATA_CB_SC_P_IO 0x02 // ATAPI I/O
2419 #define ATA_CB_SC_P_CD 0x01 // ATAPI C/D
2421 // bits 7-4 of the device/head (CB_DH) reg
2422 #define ATA_CB_DH_DEV0 0xa0 // select device 0
2423 #define ATA_CB_DH_DEV1 0xb0 // select device 1
2425 // status reg (CB_STAT and CB_ASTAT) bits
2426 #define ATA_CB_STAT_BSY 0x80 // busy
2427 #define ATA_CB_STAT_RDY 0x40 // ready
2428 #define ATA_CB_STAT_DF 0x20 // device fault
2429 #define ATA_CB_STAT_WFT 0x20 // write fault (old name)
2430 #define ATA_CB_STAT_SKC 0x10 // seek complete
2431 #define ATA_CB_STAT_SERV 0x10 // service
2432 #define ATA_CB_STAT_DRQ 0x08 // data request
2433 #define ATA_CB_STAT_CORR 0x04 // corrected
2434 #define ATA_CB_STAT_IDX 0x02 // index
2435 #define ATA_CB_STAT_ERR 0x01 // error (ATA)
2436 #define ATA_CB_STAT_CHK 0x01 // check (ATAPI)
2438 // device control reg (CB_DC) bits
2439 #define ATA_CB_DC_HD15 0x08 // bit should always be set to one
2440 #define ATA_CB_DC_SRST 0x04 // soft reset
2441 #define ATA_CB_DC_NIEN 0x02 // disable interrupts
2443 // Most mandtory and optional ATA commands (from ATA-3),
2444 #define ATA_CMD_CFA_ERASE_SECTORS 0xC0
2445 #define ATA_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03
2446 #define ATA_CMD_CFA_TRANSLATE_SECTOR 0x87
2447 #define ATA_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD
2448 #define ATA_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38
2449 #define ATA_CMD_CHECK_POWER_MODE1 0xE5
2450 #define ATA_CMD_CHECK_POWER_MODE2 0x98
2451 #define ATA_CMD_DEVICE_RESET 0x08
2452 #define ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90
2453 #define ATA_CMD_FLUSH_CACHE 0xE7
2454 #define ATA_CMD_FORMAT_TRACK 0x50
2455 #define ATA_CMD_IDENTIFY_DEVICE 0xEC
2456 #define ATA_CMD_IDENTIFY_DEVICE_PACKET 0xA1
2457 #define ATA_CMD_IDENTIFY_PACKET_DEVICE 0xA1
2458 #define ATA_CMD_IDLE1 0xE3
2459 #define ATA_CMD_IDLE2 0x97
2460 #define ATA_CMD_IDLE_IMMEDIATE1 0xE1
2461 #define ATA_CMD_IDLE_IMMEDIATE2 0x95
2462 #define ATA_CMD_INITIALIZE_DRIVE_PARAMETERS 0x91
2463 #define ATA_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91
2464 #define ATA_CMD_NOP 0x00
2465 #define ATA_CMD_PACKET 0xA0
2466 #define ATA_CMD_READ_BUFFER 0xE4
2467 #define ATA_CMD_READ_DMA 0xC8
2468 #define ATA_CMD_READ_DMA_QUEUED 0xC7
2469 #define ATA_CMD_READ_MULTIPLE 0xC4
2470 #define ATA_CMD_READ_SECTORS 0x20
2471 #define ATA_CMD_READ_VERIFY_SECTORS 0x40
2472 #define ATA_CMD_RECALIBRATE 0x10
2473 #define ATA_CMD_SEEK 0x70
2474 #define ATA_CMD_SET_FEATURES 0xEF
2475 #define ATA_CMD_SET_MULTIPLE_MODE 0xC6
2476 #define ATA_CMD_SLEEP1 0xE6
2477 #define ATA_CMD_SLEEP2 0x99
2478 #define ATA_CMD_STANDBY1 0xE2
2479 #define ATA_CMD_STANDBY2 0x96
2480 #define ATA_CMD_STANDBY_IMMEDIATE1 0xE0
2481 #define ATA_CMD_STANDBY_IMMEDIATE2 0x94
2482 #define ATA_CMD_WRITE_BUFFER 0xE8
2483 #define ATA_CMD_WRITE_DMA 0xCA
2484 #define ATA_CMD_WRITE_DMA_QUEUED 0xCC
2485 #define ATA_CMD_WRITE_MULTIPLE 0xC5
2486 #define ATA_CMD_WRITE_SECTORS 0x30
2487 #define ATA_CMD_WRITE_VERIFY 0x3C
2489 #define ATA_IFACE_NONE 0x00
2490 #define ATA_IFACE_ISA 0x00
2491 #define ATA_IFACE_PCI 0x01
2493 #define ATA_TYPE_NONE 0x00
2494 #define ATA_TYPE_UNKNOWN 0x01
2495 #define ATA_TYPE_ATA 0x02
2496 #define ATA_TYPE_ATAPI 0x03
2498 #define ATA_DEVICE_NONE 0x00
2499 #define ATA_DEVICE_HD 0xFF
2500 #define ATA_DEVICE_CDROM 0x05
2502 #define ATA_MODE_NONE 0x00
2503 #define ATA_MODE_PIO16 0x00
2504 #define ATA_MODE_PIO32 0x01
2505 #define ATA_MODE_ISADMA 0x02
2506 #define ATA_MODE_PCIDMA 0x03
2507 #define ATA_MODE_USEIRQ 0x10
2509 #define ATA_TRANSLATION_NONE 0
2510 #define ATA_TRANSLATION_LBA 1
2511 #define ATA_TRANSLATION_LARGE 2
2512 #define ATA_TRANSLATION_RECHS 3
2514 #define ATA_DATA_NO 0x00
2515 #define ATA_DATA_IN 0x01
2516 #define ATA_DATA_OUT 0x02
2518 // ---------------------------------------------------------------------------
2519 // ATA/ATAPI driver : initialization
2520 // ---------------------------------------------------------------------------
2521 void ata_init( )
2523 Bit16u ebda_seg=read_word(0x0040,0x000E);
2524 Bit8u channel, device;
2526 // Channels info init.
2527 for (channel=0; channel<BX_MAX_ATA_INTERFACES; channel++) {
2528 write_byte(ebda_seg,&EbdaData->ata.channels[channel].iface,ATA_IFACE_NONE);
2529 write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1,0x0);
2530 write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2,0x0);
2531 write_byte(ebda_seg,&EbdaData->ata.channels[channel].irq,0);
2534 // Devices info init.
2535 for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2536 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
2537 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_NONE);
2538 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable,0);
2539 write_byte(ebda_seg,&EbdaData->ata.devices[device].lock,0);
2540 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode,ATA_MODE_NONE);
2541 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize,0);
2542 write_byte(ebda_seg,&EbdaData->ata.devices[device].translation,ATA_TRANSLATION_NONE);
2543 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads,0);
2544 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders,0);
2545 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt,0);
2546 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads,0);
2547 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders,0);
2548 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt,0);
2550 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors,0L);
2553 // hdidmap and cdidmap init.
2554 for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2555 write_byte(ebda_seg,&EbdaData->ata.hdidmap[device],BX_MAX_ATA_DEVICES);
2556 write_byte(ebda_seg,&EbdaData->ata.cdidmap[device],BX_MAX_ATA_DEVICES);
2559 write_byte(ebda_seg,&EbdaData->ata.hdcount,0);
2560 write_byte(ebda_seg,&EbdaData->ata.cdcount,0);
2563 // ---------------------------------------------------------------------------
2564 // ATA/ATAPI driver : device detection
2565 // ---------------------------------------------------------------------------
2567 void ata_detect( )
2569 Bit16u ebda_seg=read_word(0x0040,0x000E);
2570 Bit8u hdcount, cdcount, device, type;
2571 Bit8u buffer[0x0200];
2573 #if BX_MAX_ATA_INTERFACES > 0
2574 write_byte(ebda_seg,&EbdaData->ata.channels[0].iface,ATA_IFACE_ISA);
2575 write_word(ebda_seg,&EbdaData->ata.channels[0].iobase1,0x1f0);
2576 write_word(ebda_seg,&EbdaData->ata.channels[0].iobase2,0x3f0);
2577 write_byte(ebda_seg,&EbdaData->ata.channels[0].irq,14);
2578 #endif
2579 #if BX_MAX_ATA_INTERFACES > 1
2580 write_byte(ebda_seg,&EbdaData->ata.channels[1].iface,ATA_IFACE_ISA);
2581 write_word(ebda_seg,&EbdaData->ata.channels[1].iobase1,0x170);
2582 write_word(ebda_seg,&EbdaData->ata.channels[1].iobase2,0x370);
2583 write_byte(ebda_seg,&EbdaData->ata.channels[1].irq,15);
2584 #endif
2585 #if BX_MAX_ATA_INTERFACES > 2
2586 write_byte(ebda_seg,&EbdaData->ata.channels[2].iface,ATA_IFACE_ISA);
2587 write_word(ebda_seg,&EbdaData->ata.channels[2].iobase1,0x1e8);
2588 write_word(ebda_seg,&EbdaData->ata.channels[2].iobase2,0x3e0);
2589 write_byte(ebda_seg,&EbdaData->ata.channels[2].irq,12);
2590 #endif
2591 #if BX_MAX_ATA_INTERFACES > 3
2592 write_byte(ebda_seg,&EbdaData->ata.channels[3].iface,ATA_IFACE_ISA);
2593 write_word(ebda_seg,&EbdaData->ata.channels[3].iobase1,0x168);
2594 write_word(ebda_seg,&EbdaData->ata.channels[3].iobase2,0x360);
2595 write_byte(ebda_seg,&EbdaData->ata.channels[3].irq,11);
2596 #endif
2597 #if BX_MAX_ATA_INTERFACES > 4
2598 #error Please fill the ATA interface informations
2599 #endif
2601 // Device detection
2602 hdcount=cdcount=0;
2604 for(device=0; device<BX_MAX_ATA_DEVICES; device++) {
2605 Bit16u iobase1, iobase2;
2606 Bit8u channel, slave, shift;
2607 Bit8u sc, sn, cl, ch, st;
2609 channel = device / 2;
2610 slave = device % 2;
2612 iobase1 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1);
2613 iobase2 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2);
2615 // Disable interrupts
2616 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2618 // Look for device
2619 outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2620 outb(iobase1+ATA_CB_SC, 0x55);
2621 outb(iobase1+ATA_CB_SN, 0xaa);
2622 outb(iobase1+ATA_CB_SC, 0xaa);
2623 outb(iobase1+ATA_CB_SN, 0x55);
2624 outb(iobase1+ATA_CB_SC, 0x55);
2625 outb(iobase1+ATA_CB_SN, 0xaa);
2627 // If we found something
2628 sc = inb(iobase1+ATA_CB_SC);
2629 sn = inb(iobase1+ATA_CB_SN);
2631 if ( (sc == 0x55) && (sn == 0xaa) ) {
2632 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_UNKNOWN);
2634 // reset the channel
2635 ata_reset (device);
2637 // check for ATA or ATAPI
2638 outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2639 sc = inb(iobase1+ATA_CB_SC);
2640 sn = inb(iobase1+ATA_CB_SN);
2641 if ( (sc==0x01) && (sn==0x01) ) {
2642 cl = inb(iobase1+ATA_CB_CL);
2643 ch = inb(iobase1+ATA_CB_CH);
2644 st = inb(iobase1+ATA_CB_STAT);
2646 if ( (cl==0x14) && (ch==0xeb) ) {
2647 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATAPI);
2649 else if ( (cl==0x00) && (ch==0x00) && (st!=0x00) ) {
2650 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATA);
2655 type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type);
2657 // Now we send a IDENTIFY command to ATA device
2658 if(type == ATA_TYPE_ATA) {
2659 Bit32u sectors;
2660 Bit16u cylinders, heads, spt, blksize;
2661 Bit8u translation, removable, mode;
2663 // default mode to PIO16
2664 mode = ATA_MODE_PIO16;
2666 //Temporary values to do the transfer
2667 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2668 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2670 if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE, 1, 0, 0, 0, 0L, get_SS(),buffer) !=0 )
2671 BX_PANIC("ata-detect: Failed to detect ATA device\n");
2673 removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2674 #ifndef NO_PIO32
2675 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2676 #endif
2678 blksize = read_word(get_SS(),buffer+10);
2680 cylinders = read_word(get_SS(),buffer+(1*2)); // word 1
2681 heads = read_word(get_SS(),buffer+(3*2)); // word 3
2682 spt = read_word(get_SS(),buffer+(6*2)); // word 6
2684 sectors = read_dword(get_SS(),buffer+(60*2)); // word 60 and word 61
2686 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2687 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2688 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2689 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2690 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads, heads);
2691 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders, cylinders);
2692 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt, spt);
2693 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors, sectors);
2694 BX_INFO("ata%d-%d: PCHS=%u/%d/%d translation=", channel, slave,cylinders, heads, spt);
2696 translation = inb_cmos(0x39 + channel/2);
2697 for (shift=device%4; shift>0; shift--) translation >>= 2;
2698 translation &= 0x03;
2700 write_byte(ebda_seg,&EbdaData->ata.devices[device].translation, translation);
2702 switch (translation) {
2703 case ATA_TRANSLATION_NONE:
2704 BX_INFO("none");
2705 break;
2706 case ATA_TRANSLATION_LBA:
2707 BX_INFO("lba");
2708 break;
2709 case ATA_TRANSLATION_LARGE:
2710 BX_INFO("large");
2711 break;
2712 case ATA_TRANSLATION_RECHS:
2713 BX_INFO("r-echs");
2714 break;
2716 switch (translation) {
2717 case ATA_TRANSLATION_NONE:
2718 break;
2719 case ATA_TRANSLATION_LBA:
2720 spt = 63;
2721 sectors /= 63;
2722 heads = sectors / 1024;
2723 if (heads>128) heads = 255;
2724 else if (heads>64) heads = 128;
2725 else if (heads>32) heads = 64;
2726 else if (heads>16) heads = 32;
2727 else heads=16;
2728 cylinders = sectors / heads;
2729 break;
2730 case ATA_TRANSLATION_RECHS:
2731 // Take care not to overflow
2732 if (heads==16) {
2733 if(cylinders>61439) cylinders=61439;
2734 heads=15;
2735 cylinders = (Bit16u)((Bit32u)(cylinders)*16/15);
2737 // then go through the large bitshift process
2738 case ATA_TRANSLATION_LARGE:
2739 while(cylinders > 1024) {
2740 cylinders >>= 1;
2741 heads <<= 1;
2743 // If we max out the head count
2744 if (heads > 127) break;
2746 break;
2748 // clip to 1024 cylinders in lchs
2749 if (cylinders > 1024) cylinders=1024;
2750 BX_INFO(" LCHS=%d/%d/%d\n", cylinders, heads, spt);
2752 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads, heads);
2753 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders, cylinders);
2754 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt, spt);
2756 // fill hdidmap
2757 write_byte(ebda_seg,&EbdaData->ata.hdidmap[hdcount], device);
2758 hdcount++;
2761 // Now we send a IDENTIFY command to ATAPI device
2762 if(type == ATA_TYPE_ATAPI) {
2764 Bit8u type, removable, mode;
2765 Bit16u blksize;
2767 // default mode to PIO16
2768 mode = ATA_MODE_PIO16;
2770 //Temporary values to do the transfer
2771 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_CDROM);
2772 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2774 if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE_PACKET, 1, 0, 0, 0, 0L, get_SS(),buffer) != 0)
2775 BX_PANIC("ata-detect: Failed to detect ATAPI device\n");
2777 type = read_byte(get_SS(),buffer+1) & 0x1f;
2778 removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2779 #ifndef NO_PIO32
2780 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2781 #endif
2782 blksize = 2048;
2784 write_byte(ebda_seg,&EbdaData->ata.devices[device].device, type);
2785 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2786 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2787 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2789 // fill cdidmap
2790 write_byte(ebda_seg,&EbdaData->ata.cdidmap[cdcount], device);
2791 cdcount++;
2795 Bit32u sizeinmb;
2796 Bit16u ataversion;
2797 Bit8u c, i, version, model[41];
2799 switch (type) {
2800 case ATA_TYPE_ATA:
2801 sizeinmb = read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors);
2802 sizeinmb >>= 11;
2803 case ATA_TYPE_ATAPI:
2804 // Read ATA/ATAPI version
2805 ataversion=((Bit16u)(read_byte(get_SS(),buffer+161))<<8)|read_byte(get_SS(),buffer+160);
2806 for(version=15;version>0;version--) {
2807 if((ataversion&(1<<version))!=0)
2808 break;
2811 // Read model name
2812 for(i=0;i<20;i++){
2813 write_byte(get_SS(),model+(i*2),read_byte(get_SS(),buffer+(i*2)+54+1));
2814 write_byte(get_SS(),model+(i*2)+1,read_byte(get_SS(),buffer+(i*2)+54));
2817 // Reformat
2818 write_byte(get_SS(),model+40,0x00);
2819 for(i=39;i>0;i--){
2820 if(read_byte(get_SS(),model+i)==0x20)
2821 write_byte(get_SS(),model+i,0x00);
2822 else break;
2824 break;
2827 switch (type) {
2828 case ATA_TYPE_ATA:
2829 printf("ata%d %s: ",channel,slave?" slave":"master");
2830 i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
2831 if (sizeinmb < 1UL<<16)
2832 printf(" ATA-%d Hard-Disk (%04u MBytes)\n",version,(Bit16u)sizeinmb);
2833 else
2834 printf(" ATA-%d Hard-Disk (%04u GBytes)\n",version,(Bit16u)(sizeinmb>>10));
2835 break;
2836 case ATA_TYPE_ATAPI:
2837 printf("ata%d %s: ",channel,slave?" slave":"master");
2838 i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
2839 if(read_byte(ebda_seg,&EbdaData->ata.devices[device].device)==ATA_DEVICE_CDROM)
2840 printf(" ATAPI-%d CD-Rom/DVD-Rom\n",version);
2841 else
2842 printf(" ATAPI-%d Device\n",version);
2843 break;
2844 case ATA_TYPE_UNKNOWN:
2845 printf("ata%d %s: Unknown device\n",channel,slave?" slave":"master");
2846 break;
2851 // Store the devices counts
2852 write_byte(ebda_seg,&EbdaData->ata.hdcount, hdcount);
2853 write_byte(ebda_seg,&EbdaData->ata.cdcount, cdcount);
2854 write_byte(0x40,0x75, hdcount);
2856 printf("\n");
2858 // FIXME : should use bios=cmos|auto|disable bits
2859 // FIXME : should know about translation bits
2860 // FIXME : move hard_drive_post here
2864 // ---------------------------------------------------------------------------
2865 // ATA/ATAPI driver : software reset
2866 // ---------------------------------------------------------------------------
2867 // ATA-3
2868 // 8.2.1 Software reset - Device 0
2870 void ata_reset(device)
2871 Bit16u device;
2873 Bit16u ebda_seg=read_word(0x0040,0x000E);
2874 Bit16u iobase1, iobase2;
2875 Bit8u channel, slave, sn, sc;
2876 Bit16u max;
2878 channel = device / 2;
2879 slave = device % 2;
2881 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2882 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2884 // Reset
2886 // 8.2.1 (a) -- set SRST in DC
2887 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST);
2889 // 8.2.1 (b) -- wait for BSY
2890 max=0xff;
2891 while(--max>0) {
2892 Bit8u status = inb(iobase1+ATA_CB_STAT);
2893 if ((status & ATA_CB_STAT_BSY) != 0) break;
2896 // 8.2.1 (f) -- clear SRST
2897 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2899 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_NONE) {
2901 // 8.2.1 (g) -- check for sc==sn==0x01
2902 // select device
2903 outb(iobase1+ATA_CB_DH, slave?ATA_CB_DH_DEV1:ATA_CB_DH_DEV0);
2904 sc = inb(iobase1+ATA_CB_SC);
2905 sn = inb(iobase1+ATA_CB_SN);
2907 if ( (sc==0x01) && (sn==0x01) ) {
2909 // 8.2.1 (h) -- wait for not BSY
2910 max=0xff;
2911 while(--max>0) {
2912 Bit8u status = inb(iobase1+ATA_CB_STAT);
2913 if ((status & ATA_CB_STAT_BSY) == 0) break;
2918 // 8.2.1 (i) -- wait for DRDY
2919 max=0xfff;
2920 while(--max>0) {
2921 Bit8u status = inb(iobase1+ATA_CB_STAT);
2922 if ((status & ATA_CB_STAT_RDY) != 0) break;
2925 // Enable interrupts
2926 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2929 // ---------------------------------------------------------------------------
2930 // ATA/ATAPI driver : execute a non data command
2931 // ---------------------------------------------------------------------------
2933 Bit16u ata_cmd_non_data()
2934 {return 0;}
2936 // ---------------------------------------------------------------------------
2937 // ATA/ATAPI driver : execute a data-in command
2938 // ---------------------------------------------------------------------------
2939 // returns
2940 // 0 : no error
2941 // 1 : BUSY bit set
2942 // 2 : read error
2943 // 3 : expected DRQ=1
2944 // 4 : no sectors left to read/verify
2945 // 5 : more sectors to read/verify
2946 // 6 : no sectors left to write
2947 // 7 : more sectors to write
2948 Bit16u ata_cmd_data_in(device, command, count, cylinder, head, sector, lba, segment, offset)
2949 Bit16u device, command, count, cylinder, head, sector, segment, offset;
2950 Bit32u lba;
2952 Bit16u ebda_seg=read_word(0x0040,0x000E);
2953 Bit16u iobase1, iobase2, blksize;
2954 Bit8u channel, slave;
2955 Bit8u status, current, mode;
2957 channel = device / 2;
2958 slave = device % 2;
2960 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2961 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2962 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2963 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
2964 if (mode == ATA_MODE_PIO32) blksize>>=2;
2965 else blksize>>=1;
2967 // Reset count of transferred data
2968 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2969 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2970 current = 0;
2972 status = inb(iobase1 + ATA_CB_STAT);
2973 if (status & ATA_CB_STAT_BSY) return 1;
2975 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2977 // sector will be 0 only on lba access. Convert to lba-chs
2978 if (sector == 0) {
2979 if ((count >= 1 << 8) || (lba + count >= 1UL << 28)) {
2980 outb(iobase1 + ATA_CB_FR, 0x00);
2981 outb(iobase1 + ATA_CB_SC, (count >> 8) & 0xff);
2982 outb(iobase1 + ATA_CB_SN, lba >> 24);
2983 outb(iobase1 + ATA_CB_CL, 0);
2984 outb(iobase1 + ATA_CB_CH, 0);
2985 command |= 0x04;
2986 count &= (1UL << 8) - 1;
2987 lba &= (1UL << 24) - 1;
2989 sector = (Bit16u) (lba & 0x000000ffL);
2990 lba >>= 8;
2991 cylinder = (Bit16u) (lba & 0x0000ffffL);
2992 lba >>= 16;
2993 head = ((Bit16u) (lba & 0x0000000fL)) | 0x40;
2996 outb(iobase1 + ATA_CB_FR, 0x00);
2997 outb(iobase1 + ATA_CB_SC, count);
2998 outb(iobase1 + ATA_CB_SN, sector);
2999 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
3000 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
3001 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
3002 outb(iobase1 + ATA_CB_CMD, command);
3004 while (1) {
3005 status = inb(iobase1 + ATA_CB_STAT);
3006 if ( !(status & ATA_CB_STAT_BSY) ) break;
3009 if (status & ATA_CB_STAT_ERR) {
3010 BX_DEBUG_ATA("ata_cmd_data_in : read error\n");
3011 return 2;
3012 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
3013 BX_DEBUG_ATA("ata_cmd_data_in : DRQ not set (status %02x)\n", (unsigned) status);
3014 return 3;
3017 // FIXME : move seg/off translation here
3019 ASM_START
3020 sti ;; enable higher priority interrupts
3021 ASM_END
3023 while (1) {
3025 ASM_START
3026 push bp
3027 mov bp, sp
3028 mov di, _ata_cmd_data_in.offset + 2[bp]
3029 mov ax, _ata_cmd_data_in.segment + 2[bp]
3030 mov cx, _ata_cmd_data_in.blksize + 2[bp]
3032 ;; adjust if there will be an overrun. 2K max sector size
3033 cmp di, #0xf800 ;;
3034 jbe ata_in_no_adjust
3036 ata_in_adjust:
3037 sub di, #0x0800 ;; sub 2 kbytes from offset
3038 add ax, #0x0080 ;; add 2 Kbytes to segment
3040 ata_in_no_adjust:
3041 mov es, ax ;; segment in es
3043 mov dx, _ata_cmd_data_in.iobase1 + 2[bp] ;; ATA data read port
3045 mov ah, _ata_cmd_data_in.mode + 2[bp]
3046 cmp ah, #ATA_MODE_PIO32
3047 je ata_in_32
3049 ata_in_16:
3050 rep
3051 insw ;; CX words transfered from port(DX) to ES:[DI]
3052 jmp ata_in_done
3054 ata_in_32:
3055 rep
3056 insd ;; CX dwords transfered from port(DX) to ES:[DI]
3058 ata_in_done:
3059 mov _ata_cmd_data_in.offset + 2[bp], di
3060 mov _ata_cmd_data_in.segment + 2[bp], es
3061 pop bp
3062 ASM_END
3064 current++;
3065 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
3066 count--;
3067 status = inb(iobase1 + ATA_CB_STAT);
3068 if (count == 0) {
3069 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
3070 != ATA_CB_STAT_RDY ) {
3071 BX_DEBUG_ATA("ata_cmd_data_in : no sectors left (status %02x)\n", (unsigned) status);
3072 return 4;
3074 break;
3076 else {
3077 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
3078 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
3079 BX_DEBUG_ATA("ata_cmd_data_in : more sectors left (status %02x)\n", (unsigned) status);
3080 return 5;
3082 continue;
3085 // Enable interrupts
3086 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
3087 return 0;
3090 // ---------------------------------------------------------------------------
3091 // ATA/ATAPI driver : execute a data-out command
3092 // ---------------------------------------------------------------------------
3093 // returns
3094 // 0 : no error
3095 // 1 : BUSY bit set
3096 // 2 : read error
3097 // 3 : expected DRQ=1
3098 // 4 : no sectors left to read/verify
3099 // 5 : more sectors to read/verify
3100 // 6 : no sectors left to write
3101 // 7 : more sectors to write
3102 Bit16u ata_cmd_data_out(device, command, count, cylinder, head, sector, lba, segment, offset)
3103 Bit16u device, command, count, cylinder, head, sector, segment, offset;
3104 Bit32u lba;
3106 Bit16u ebda_seg=read_word(0x0040,0x000E);
3107 Bit16u iobase1, iobase2, blksize;
3108 Bit8u channel, slave;
3109 Bit8u status, current, mode;
3111 channel = device / 2;
3112 slave = device % 2;
3114 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
3115 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
3116 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
3117 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
3118 if (mode == ATA_MODE_PIO32) blksize>>=2;
3119 else blksize>>=1;
3121 // Reset count of transferred data
3122 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
3123 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
3124 current = 0;
3126 status = inb(iobase1 + ATA_CB_STAT);
3127 if (status & ATA_CB_STAT_BSY) return 1;
3129 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
3131 // sector will be 0 only on lba access. Convert to lba-chs
3132 if (sector == 0) {
3133 if ((count >= 1 << 8) || (lba + count >= 1UL << 28)) {
3134 outb(iobase1 + ATA_CB_FR, 0x00);
3135 outb(iobase1 + ATA_CB_SC, (count >> 8) & 0xff);
3136 outb(iobase1 + ATA_CB_SN, lba >> 24);
3137 outb(iobase1 + ATA_CB_CL, 0);
3138 outb(iobase1 + ATA_CB_CH, 0);
3139 command |= 0x04;
3140 count &= (1UL << 8) - 1;
3141 lba &= (1UL << 24) - 1;
3143 sector = (Bit16u) (lba & 0x000000ffL);
3144 lba >>= 8;
3145 cylinder = (Bit16u) (lba & 0x0000ffffL);
3146 lba >>= 16;
3147 head = ((Bit16u) (lba & 0x0000000fL)) | 0x40;
3150 outb(iobase1 + ATA_CB_FR, 0x00);
3151 outb(iobase1 + ATA_CB_SC, count);
3152 outb(iobase1 + ATA_CB_SN, sector);
3153 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
3154 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
3155 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
3156 outb(iobase1 + ATA_CB_CMD, command);
3158 while (1) {
3159 status = inb(iobase1 + ATA_CB_STAT);
3160 if ( !(status & ATA_CB_STAT_BSY) ) break;
3163 if (status & ATA_CB_STAT_ERR) {
3164 BX_DEBUG_ATA("ata_cmd_data_out : read error\n");
3165 return 2;
3166 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
3167 BX_DEBUG_ATA("ata_cmd_data_out : DRQ not set (status %02x)\n", (unsigned) status);
3168 return 3;
3171 // FIXME : move seg/off translation here
3173 ASM_START
3174 sti ;; enable higher priority interrupts
3175 ASM_END
3177 while (1) {
3179 ASM_START
3180 push bp
3181 mov bp, sp
3182 mov si, _ata_cmd_data_out.offset + 2[bp]
3183 mov ax, _ata_cmd_data_out.segment + 2[bp]
3184 mov cx, _ata_cmd_data_out.blksize + 2[bp]
3186 ;; adjust if there will be an overrun. 2K max sector size
3187 cmp si, #0xf800 ;;
3188 jbe ata_out_no_adjust
3190 ata_out_adjust:
3191 sub si, #0x0800 ;; sub 2 kbytes from offset
3192 add ax, #0x0080 ;; add 2 Kbytes to segment
3194 ata_out_no_adjust:
3195 mov es, ax ;; segment in es
3197 mov dx, _ata_cmd_data_out.iobase1 + 2[bp] ;; ATA data write port
3199 mov ah, _ata_cmd_data_out.mode + 2[bp]
3200 cmp ah, #ATA_MODE_PIO32
3201 je ata_out_32
3203 ata_out_16:
3204 seg ES
3205 rep
3206 outsw ;; CX words transfered from port(DX) to ES:[SI]
3207 jmp ata_out_done
3209 ata_out_32:
3210 seg ES
3211 rep
3212 outsd ;; CX dwords transfered from port(DX) to ES:[SI]
3214 ata_out_done:
3215 mov _ata_cmd_data_out.offset + 2[bp], si
3216 mov _ata_cmd_data_out.segment + 2[bp], es
3217 pop bp
3218 ASM_END
3220 current++;
3221 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
3222 count--;
3223 status = inb(iobase1 + ATA_CB_STAT);
3224 if (count == 0) {
3225 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
3226 != ATA_CB_STAT_RDY ) {
3227 BX_DEBUG_ATA("ata_cmd_data_out : no sectors left (status %02x)\n", (unsigned) status);
3228 return 6;
3230 break;
3232 else {
3233 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
3234 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
3235 BX_DEBUG_ATA("ata_cmd_data_out : more sectors left (status %02x)\n", (unsigned) status);
3236 return 7;
3238 continue;
3241 // Enable interrupts
3242 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
3243 return 0;
3246 // ---------------------------------------------------------------------------
3247 // ATA/ATAPI driver : execute a packet command
3248 // ---------------------------------------------------------------------------
3249 // returns
3250 // 0 : no error
3251 // 1 : error in parameters
3252 // 2 : BUSY bit set
3253 // 3 : error
3254 // 4 : not ready
3255 Bit16u ata_cmd_packet(device, cmdlen, cmdseg, cmdoff, header, length, inout, bufseg, bufoff)
3256 Bit8u cmdlen,inout;
3257 Bit16u device,cmdseg, cmdoff, bufseg, bufoff;
3258 Bit16u header;
3259 Bit32u length;
3261 Bit16u ebda_seg=read_word(0x0040,0x000E);
3262 Bit16u iobase1, iobase2;
3263 Bit16u lcount, lbefore, lafter, count;
3264 Bit8u channel, slave;
3265 Bit8u status, mode, lmode;
3266 Bit32u total, transfer;
3268 channel = device / 2;
3269 slave = device % 2;
3271 // Data out is not supported yet
3272 if (inout == ATA_DATA_OUT) {
3273 BX_INFO("ata_cmd_packet: DATA_OUT not supported yet\n");
3274 return 1;
3277 // The header length must be even
3278 if (header & 1) {
3279 BX_DEBUG_ATA("ata_cmd_packet : header must be even (%04x)\n",header);
3280 return 1;
3283 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
3284 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
3285 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
3286 transfer= 0L;
3288 if (cmdlen < 12) cmdlen=12;
3289 if (cmdlen > 12) cmdlen=16;
3290 cmdlen>>=1;
3292 // Reset count of transferred data
3293 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
3294 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
3296 status = inb(iobase1 + ATA_CB_STAT);
3297 if (status & ATA_CB_STAT_BSY) return 2;
3299 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
3300 // outb(iobase1 + ATA_CB_FR, 0x00);
3301 // outb(iobase1 + ATA_CB_SC, 0x00);
3302 // outb(iobase1 + ATA_CB_SN, 0x00);
3303 outb(iobase1 + ATA_CB_CL, 0xfff0 & 0x00ff);
3304 outb(iobase1 + ATA_CB_CH, 0xfff0 >> 8);
3305 outb(iobase1 + ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
3306 outb(iobase1 + ATA_CB_CMD, ATA_CMD_PACKET);
3308 // Device should ok to receive command
3309 while (1) {
3310 status = inb(iobase1 + ATA_CB_STAT);
3311 if ( !(status & ATA_CB_STAT_BSY) ) break;
3314 if (status & ATA_CB_STAT_ERR) {
3315 BX_DEBUG_ATA("ata_cmd_packet : error, status is %02x\n",status);
3316 return 3;
3317 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
3318 BX_DEBUG_ATA("ata_cmd_packet : DRQ not set (status %02x)\n", (unsigned) status);
3319 return 4;
3322 // Normalize address
3323 cmdseg += (cmdoff / 16);
3324 cmdoff %= 16;
3326 // Send command to device
3327 ASM_START
3328 sti ;; enable higher priority interrupts
3330 push bp
3331 mov bp, sp
3333 mov si, _ata_cmd_packet.cmdoff + 2[bp]
3334 mov ax, _ata_cmd_packet.cmdseg + 2[bp]
3335 mov cx, _ata_cmd_packet.cmdlen + 2[bp]
3336 mov es, ax ;; segment in es
3338 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data write port
3340 seg ES
3341 rep
3342 outsw ;; CX words transfered from port(DX) to ES:[SI]
3344 pop bp
3345 ASM_END
3347 if (inout == ATA_DATA_NO) {
3348 status = inb(iobase1 + ATA_CB_STAT);
3350 else {
3351 while (1) {
3353 status = inb(iobase1 + ATA_CB_STAT);
3355 // Check if command completed
3356 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ) ) ==0 ) break;
3358 if (status & ATA_CB_STAT_ERR) {
3359 BX_DEBUG_ATA("ata_cmd_packet : error (status %02x)\n",status);
3360 return 3;
3363 // Device must be ready to send data
3364 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
3365 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
3366 BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", status);
3367 return 4;
3370 // Normalize address
3371 bufseg += (bufoff / 16);
3372 bufoff %= 16;
3374 // Get the byte count
3375 lcount = ((Bit16u)(inb(iobase1 + ATA_CB_CH))<<8)+inb(iobase1 + ATA_CB_CL);
3377 // adjust to read what we want
3378 if(header>lcount) {
3379 lbefore=lcount;
3380 header-=lcount;
3381 lcount=0;
3383 else {
3384 lbefore=header;
3385 header=0;
3386 lcount-=lbefore;
3389 if(lcount>length) {
3390 lafter=lcount-length;
3391 lcount=length;
3392 length=0;
3394 else {
3395 lafter=0;
3396 length-=lcount;
3399 // Save byte count
3400 count = lcount;
3402 BX_DEBUG_ATA("Trying to read %04x bytes (%04x %04x %04x) ",lbefore+lcount+lafter,lbefore,lcount,lafter);
3403 BX_DEBUG_ATA("to 0x%04x:0x%04x\n",bufseg,bufoff);
3405 // If counts not dividable by 4, use 16bits mode
3406 lmode = mode;
3407 if (lbefore & 0x03) lmode=ATA_MODE_PIO16;
3408 if (lcount & 0x03) lmode=ATA_MODE_PIO16;
3409 if (lafter & 0x03) lmode=ATA_MODE_PIO16;
3411 // adds an extra byte if count are odd. before is always even
3412 if (lcount & 0x01) {
3413 lcount+=1;
3414 if ((lafter > 0) && (lafter & 0x01)) {
3415 lafter-=1;
3419 if (lmode == ATA_MODE_PIO32) {
3420 lcount>>=2; lbefore>>=2; lafter>>=2;
3422 else {
3423 lcount>>=1; lbefore>>=1; lafter>>=1;
3426 ; // FIXME bcc bug
3428 ASM_START
3429 push bp
3430 mov bp, sp
3432 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data read port
3434 mov cx, _ata_cmd_packet.lbefore + 2[bp]
3435 jcxz ata_packet_no_before
3437 mov ah, _ata_cmd_packet.lmode + 2[bp]
3438 cmp ah, #ATA_MODE_PIO32
3439 je ata_packet_in_before_32
3441 ata_packet_in_before_16:
3442 in ax, dx
3443 loop ata_packet_in_before_16
3444 jmp ata_packet_no_before
3446 ata_packet_in_before_32:
3447 push eax
3448 ata_packet_in_before_32_loop:
3449 in eax, dx
3450 loop ata_packet_in_before_32_loop
3451 pop eax
3453 ata_packet_no_before:
3454 mov cx, _ata_cmd_packet.lcount + 2[bp]
3455 jcxz ata_packet_after
3457 mov di, _ata_cmd_packet.bufoff + 2[bp]
3458 mov ax, _ata_cmd_packet.bufseg + 2[bp]
3459 mov es, ax
3461 mov ah, _ata_cmd_packet.lmode + 2[bp]
3462 cmp ah, #ATA_MODE_PIO32
3463 je ata_packet_in_32
3465 ata_packet_in_16:
3466 rep
3467 insw ;; CX words transfered tp port(DX) to ES:[DI]
3468 jmp ata_packet_after
3470 ata_packet_in_32:
3471 rep
3472 insd ;; CX dwords transfered to port(DX) to ES:[DI]
3474 ata_packet_after:
3475 mov cx, _ata_cmd_packet.lafter + 2[bp]
3476 jcxz ata_packet_done
3478 mov ah, _ata_cmd_packet.lmode + 2[bp]
3479 cmp ah, #ATA_MODE_PIO32
3480 je ata_packet_in_after_32
3482 ata_packet_in_after_16:
3483 in ax, dx
3484 loop ata_packet_in_after_16
3485 jmp ata_packet_done
3487 ata_packet_in_after_32:
3488 push eax
3489 ata_packet_in_after_32_loop:
3490 in eax, dx
3491 loop ata_packet_in_after_32_loop
3492 pop eax
3494 ata_packet_done:
3495 pop bp
3496 ASM_END
3498 // Compute new buffer address
3499 bufoff += count;
3501 // Save transferred bytes count
3502 transfer += count;
3503 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,transfer);
3507 // Final check, device must be ready
3508 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
3509 != ATA_CB_STAT_RDY ) {
3510 BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", (unsigned) status);
3511 return 4;
3514 // Enable interrupts
3515 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
3516 return 0;
3519 // ---------------------------------------------------------------------------
3520 // End of ATA/ATAPI Driver
3521 // ---------------------------------------------------------------------------
3523 // ---------------------------------------------------------------------------
3524 // Start of ATA/ATAPI generic functions
3525 // ---------------------------------------------------------------------------
3527 Bit16u
3528 atapi_get_sense(device)
3529 Bit16u device;
3531 Bit8u atacmd[12];
3532 Bit8u buffer[16];
3533 Bit8u i;
3535 memsetb(get_SS(),atacmd,0,12);
3537 // Request SENSE
3538 atacmd[0]=0x03;
3539 atacmd[4]=0x20;
3540 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 16L, ATA_DATA_IN, get_SS(), buffer) != 0)
3541 return 0x0002;
3543 if ((buffer[0] & 0x7e) == 0x70) {
3544 return (((Bit16u)buffer[2]&0x0f)*0x100)+buffer[12];
3547 return 0;
3550 Bit16u
3551 atapi_is_ready(device)
3552 Bit16u device;
3554 Bit8u atacmd[12];
3555 Bit8u buffer[];
3557 memsetb(get_SS(),atacmd,0,12);
3559 // Test Unit Ready
3560 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0)
3561 return 0x000f;
3563 if (atapi_get_sense(device) !=0 ) {
3564 memsetb(get_SS(),atacmd,0,12);
3566 // try to send Test Unit Ready again
3567 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0)
3568 return 0x000f;
3570 return atapi_get_sense(device);
3572 return 0;
3575 Bit16u
3576 atapi_is_cdrom(device)
3577 Bit8u device;
3579 Bit16u ebda_seg=read_word(0x0040,0x000E);
3581 if (device >= BX_MAX_ATA_DEVICES)
3582 return 0;
3584 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI)
3585 return 0;
3587 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].device) != ATA_DEVICE_CDROM)
3588 return 0;
3590 return 1;
3593 // ---------------------------------------------------------------------------
3594 // End of ATA/ATAPI generic functions
3595 // ---------------------------------------------------------------------------
3597 #endif // BX_USE_ATADRV
3599 #if BX_ELTORITO_BOOT
3601 // ---------------------------------------------------------------------------
3602 // Start of El-Torito boot functions
3603 // ---------------------------------------------------------------------------
3605 void
3606 cdemu_init()
3608 Bit16u ebda_seg=read_word(0x0040,0x000E);
3610 // the only important data is this one for now
3611 write_byte(ebda_seg,&EbdaData->cdemu.active,0x00);
3614 Bit8u
3615 cdemu_isactive()
3617 Bit16u ebda_seg=read_word(0x0040,0x000E);
3619 return(read_byte(ebda_seg,&EbdaData->cdemu.active));
3622 Bit8u
3623 cdemu_emulated_drive()
3625 Bit16u ebda_seg=read_word(0x0040,0x000E);
3627 return(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
3630 static char isotag[6]="CD001";
3631 static char eltorito[24]="EL TORITO SPECIFICATION";
3632 //
3633 // Returns ah: emulated drive, al: error code
3634 //
3635 Bit16u
3636 cdrom_boot()
3638 Bit16u ebda_seg=read_word(0x0040,0x000E);
3639 Bit8u atacmd[12], buffer[2048];
3640 Bit32u lba;
3641 Bit16u boot_segment, nbsectors, i, error;
3642 Bit8u device;
3644 // Find out the first cdrom
3645 for (device=0; device<BX_MAX_ATA_DEVICES;device++) {
3646 if (atapi_is_cdrom(device)) break;
3649 // if not found
3650 if(device >= BX_MAX_ATA_DEVICES) return 2;
3652 // Read the Boot Record Volume Descriptor
3653 memsetb(get_SS(),atacmd,0,12);
3654 atacmd[0]=0x28; // READ command
3655 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3656 atacmd[8]=(0x01 & 0x00ff); // Sectors
3657 atacmd[2]=(0x11 & 0xff000000) >> 24; // LBA
3658 atacmd[3]=(0x11 & 0x00ff0000) >> 16;
3659 atacmd[4]=(0x11 & 0x0000ff00) >> 8;
3660 atacmd[5]=(0x11 & 0x000000ff);
3661 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3662 return 3;
3664 // Validity checks
3665 if(buffer[0]!=0)return 4;
3666 for(i=0;i<5;i++){
3667 if(buffer[1+i]!=read_byte(0xf000,&isotag[i]))return 5;
3669 for(i=0;i<23;i++)
3670 if(buffer[7+i]!=read_byte(0xf000,&eltorito[i]))return 6;
3672 // ok, now we calculate the Boot catalog address
3673 lba=buffer[0x4A]*0x1000000+buffer[0x49]*0x10000+buffer[0x48]*0x100+buffer[0x47];
3675 // And we read the Boot Catalog
3676 memsetb(get_SS(),atacmd,0,12);
3677 atacmd[0]=0x28; // READ command
3678 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3679 atacmd[8]=(0x01 & 0x00ff); // Sectors
3680 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3681 atacmd[3]=(lba & 0x00ff0000) >> 16;
3682 atacmd[4]=(lba & 0x0000ff00) >> 8;
3683 atacmd[5]=(lba & 0x000000ff);
3684 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3685 return 7;
3687 // Validation entry
3688 if(buffer[0x00]!=0x01)return 8; // Header
3689 if(buffer[0x01]!=0x00)return 9; // Platform
3690 if(buffer[0x1E]!=0x55)return 10; // key 1
3691 if(buffer[0x1F]!=0xAA)return 10; // key 2
3693 // Initial/Default Entry
3694 if(buffer[0x20]!=0x88)return 11; // Bootable
3696 #if BX_TCGBIOS
3697 /* specs: 8.2.3 step 5 and 8.2.5.6, measure El Torito boot catalog */
3698 /* measure 2048 bytes (one sector) */
3699 tcpa_add_bootdevice((Bit32u)1L, (Bit32u)0L); /* bootcd = 1 */
3700 tcpa_ipl((Bit32u)2L,(Bit32u)get_SS(),(Bit32u)buffer,(Bit32u)2048L);
3701 #endif
3703 write_byte(ebda_seg,&EbdaData->cdemu.media,buffer[0x21]);
3704 if(buffer[0x21]==0){
3705 // FIXME ElTorito Hardcoded. cdrom is hardcoded as device 0xE0.
3706 // Win2000 cd boot needs to know it booted from cd
3707 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0xE0);
3709 else if(buffer[0x21]<4)
3710 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x00);
3711 else
3712 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x80);
3714 write_byte(ebda_seg,&EbdaData->cdemu.controller_index,device/2);
3715 write_byte(ebda_seg,&EbdaData->cdemu.device_spec,device%2);
3717 boot_segment=buffer[0x23]*0x100+buffer[0x22];
3718 if(boot_segment==0x0000)boot_segment=0x07C0;
3720 write_word(ebda_seg,&EbdaData->cdemu.load_segment,boot_segment);
3721 write_word(ebda_seg,&EbdaData->cdemu.buffer_segment,0x0000);
3723 nbsectors=buffer[0x27]*0x100+buffer[0x26];
3724 write_word(ebda_seg,&EbdaData->cdemu.sector_count,nbsectors);
3726 lba=buffer[0x2B]*0x1000000+buffer[0x2A]*0x10000+buffer[0x29]*0x100+buffer[0x28];
3727 write_dword(ebda_seg,&EbdaData->cdemu.ilba,lba);
3729 // And we read the image in memory
3730 memsetb(get_SS(),atacmd,0,12);
3731 atacmd[0]=0x28; // READ command
3732 atacmd[7]=((1+(nbsectors-1)/4) & 0xff00) >> 8; // Sectors
3733 atacmd[8]=((1+(nbsectors-1)/4) & 0x00ff); // Sectors
3734 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3735 atacmd[3]=(lba & 0x00ff0000) >> 16;
3736 atacmd[4]=(lba & 0x0000ff00) >> 8;
3737 atacmd[5]=(lba & 0x000000ff);
3738 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, nbsectors*512L, ATA_DATA_IN, boot_segment,0)) != 0)
3739 return 12;
3741 #if BX_TCGBIOS
3742 /* specs: 8.2.3 step 4 and 8.2.5.6, measure El Torito boot image */
3743 /* measure 1st 512 bytes */
3744 tcpa_ipl((Bit32u)1L,(Bit32u)boot_segment,(Bit32u)0L,(Bit32u)512L);
3745 #endif
3748 // Remember the media type
3749 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
3750 case 0x01: // 1.2M floppy
3751 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,15);
3752 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3753 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3754 break;
3755 case 0x02: // 1.44M floppy
3756 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,18);
3757 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3758 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3759 break;
3760 case 0x03: // 2.88M floppy
3761 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,36);
3762 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3763 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3764 break;
3765 case 0x04: // Harddrive
3766 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,read_byte(boot_segment,446+6)&0x3f);
3767 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,
3768 (read_byte(boot_segment,446+6)<<2) + read_byte(boot_segment,446+7) + 1);
3769 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,read_byte(boot_segment,446+5) + 1);
3770 break;
3773 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0) {
3774 // Increase bios installed hardware number of devices
3775 if(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)==0x00)
3776 write_byte(0x40,0x10,read_byte(0x40,0x10)|0x41);
3777 else
3778 write_byte(ebda_seg, &EbdaData->ata.hdcount, read_byte(ebda_seg, &EbdaData->ata.hdcount) + 1);
3782 // everything is ok, so from now on, the emulation is active
3783 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0)
3784 write_byte(ebda_seg,&EbdaData->cdemu.active,0x01);
3786 // return the boot drive + no error
3787 return (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)*0x100)+0;
3790 // ---------------------------------------------------------------------------
3791 // End of El-Torito boot functions
3792 // ---------------------------------------------------------------------------
3793 #endif // BX_ELTORITO_BOOT
3795 void
3796 int14_function(regs, ds, iret_addr)
3797 pusha_regs_t regs; // regs pushed from PUSHA instruction
3798 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
3799 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
3801 Bit16u addr,timer,val16;
3802 Bit8u timeout;
3804 ASM_START
3805 sti
3806 ASM_END
3808 addr = read_word(0x0040, (regs.u.r16.dx << 1));
3809 timeout = read_byte(0x0040, 0x007C + regs.u.r16.dx);
3810 if ((regs.u.r16.dx < 4) && (addr > 0)) {
3811 switch (regs.u.r8.ah) {
3812 case 0:
3813 outb(addr+3, inb(addr+3) | 0x80);
3814 if (regs.u.r8.al & 0xE0 == 0) {
3815 outb(addr, 0x17);
3816 outb(addr+1, 0x04);
3817 } else {
3818 val16 = 0x600 >> ((regs.u.r8.al & 0xE0) >> 5);
3819 outb(addr, val16 & 0xFF);
3820 outb(addr+1, val16 >> 8);
3822 outb(addr+3, regs.u.r8.al & 0x1F);
3823 regs.u.r8.ah = inb(addr+5);
3824 regs.u.r8.al = inb(addr+6);
3825 ClearCF(iret_addr.flags);
3826 break;
3827 case 1:
3828 timer = read_word(0x0040, 0x006C);
3829 while (((inb(addr+5) & 0x60) != 0x60) && (timeout)) {
3830 val16 = read_word(0x0040, 0x006C);
3831 if (val16 != timer) {
3832 timer = val16;
3833 timeout--;
3836 if (timeout) outb(addr, regs.u.r8.al);
3837 regs.u.r8.ah = inb(addr+5);
3838 if (!timeout) regs.u.r8.ah |= 0x80;
3839 ClearCF(iret_addr.flags);
3840 break;
3841 case 2:
3842 timer = read_word(0x0040, 0x006C);
3843 while (((inb(addr+5) & 0x01) == 0) && (timeout)) {
3844 val16 = read_word(0x0040, 0x006C);
3845 if (val16 != timer) {
3846 timer = val16;
3847 timeout--;
3850 if (timeout) {
3851 regs.u.r8.ah = 0;
3852 regs.u.r8.al = inb(addr);
3853 } else {
3854 regs.u.r8.ah = inb(addr+5);
3856 ClearCF(iret_addr.flags);
3857 break;
3858 case 3:
3859 regs.u.r8.ah = inb(addr+5);
3860 regs.u.r8.al = inb(addr+6);
3861 ClearCF(iret_addr.flags);
3862 break;
3863 default:
3864 SetCF(iret_addr.flags); // Unsupported
3866 } else {
3867 SetCF(iret_addr.flags); // Unsupported
3871 void
3872 int15_function(regs, ES, DS, FLAGS)
3873 pusha_regs_t regs; // REGS pushed via pusha
3874 Bit16u ES, DS, FLAGS;
3876 Bit16u ebda_seg=read_word(0x0040,0x000E);
3877 bx_bool prev_a20_enable;
3878 Bit16u base15_00;
3879 Bit8u base23_16;
3880 Bit16u ss;
3881 Bit16u CX,DX;
3883 Bit16u bRegister;
3884 Bit8u irqDisable;
3886 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3888 switch (regs.u.r8.ah) {
3889 case 0x24: /* A20 Control */
3890 switch (regs.u.r8.al) {
3891 case 0x00:
3892 set_enable_a20(0);
3893 CLEAR_CF();
3894 regs.u.r8.ah = 0;
3895 break;
3896 case 0x01:
3897 set_enable_a20(1);
3898 CLEAR_CF();
3899 regs.u.r8.ah = 0;
3900 break;
3901 case 0x02:
3902 regs.u.r8.al = (inb(0x92) >> 1) & 0x01;
3903 CLEAR_CF();
3904 regs.u.r8.ah = 0;
3905 break;
3906 case 0x03:
3907 CLEAR_CF();
3908 regs.u.r8.ah = 0;
3909 regs.u.r16.bx = 3;
3910 break;
3911 default:
3912 BX_INFO("int15: Func 24h, subfunc %02xh, A20 gate control not supported\n", (unsigned) regs.u.r8.al);
3913 SET_CF();
3914 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3916 break;
3918 case 0x41:
3919 SET_CF();
3920 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3921 break;
3923 case 0x4f:
3924 /* keyboard intercept */
3925 #if BX_CPU < 2
3926 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3927 #else
3928 // nop
3929 #endif
3930 SET_CF();
3931 break;
3933 case 0x52: // removable media eject
3934 CLEAR_CF();
3935 regs.u.r8.ah = 0; // "ok ejection may proceed"
3936 break;
3938 case 0x83: {
3939 if( regs.u.r8.al == 0 ) {
3940 // Set Interval requested.
3941 if( ( read_byte( 0x40, 0xA0 ) & 1 ) == 0 ) {
3942 // Interval not already set.
3943 write_byte( 0x40, 0xA0, 1 ); // Set status byte.
3944 write_word( 0x40, 0x98, ES ); // Byte location, segment
3945 write_word( 0x40, 0x9A, regs.u.r16.bx ); // Byte location, offset
3946 write_word( 0x40, 0x9C, regs.u.r16.dx ); // Low word, delay
3947 write_word( 0x40, 0x9E, regs.u.r16.cx ); // High word, delay.
3948 CLEAR_CF( );
3949 irqDisable = inb( 0xA1 );
3950 outb( 0xA1, irqDisable & 0xFE );
3951 bRegister = inb_cmos( 0xB ); // Unmask IRQ8 so INT70 will get through.
3952 outb_cmos( 0xB, bRegister | 0x40 ); // Turn on the Periodic Interrupt timer
3953 } else {
3954 // Interval already set.
3955 BX_DEBUG_INT15("int15: Func 83h, failed, already waiting.\n" );
3956 SET_CF();
3957 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3959 } else if( regs.u.r8.al == 1 ) {
3960 // Clear Interval requested
3961 write_byte( 0x40, 0xA0, 0 ); // Clear status byte
3962 CLEAR_CF( );
3963 bRegister = inb_cmos( 0xB );
3964 outb_cmos( 0xB, bRegister & ~0x40 ); // Turn off the Periodic Interrupt timer
3965 } else {
3966 BX_DEBUG_INT15("int15: Func 83h, failed.\n" );
3967 SET_CF();
3968 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3969 regs.u.r8.al--;
3972 break;
3975 case 0x87:
3976 #if BX_CPU < 3
3977 # error "Int15 function 87h not supported on < 80386"
3978 #endif
3979 // +++ should probably have descriptor checks
3980 // +++ should have exception handlers
3982 // turn off interrupts
3983 ASM_START
3984 cli
3985 ASM_END
3987 prev_a20_enable = set_enable_a20(1); // enable A20 line
3989 // 128K max of transfer on 386+ ???
3990 // source == destination ???
3992 // ES:SI points to descriptor table
3993 // offset use initially comments
3994 // ==============================================
3995 // 00..07 Unused zeros Null descriptor
3996 // 08..0f GDT zeros filled in by BIOS
3997 // 10..17 source ssssssss source of data
3998 // 18..1f dest dddddddd destination of data
3999 // 20..27 CS zeros filled in by BIOS
4000 // 28..2f SS zeros filled in by BIOS
4002 //es:si
4003 //eeee0
4004 //0ssss
4005 //-----
4007 // check for access rights of source & dest here
4009 // Initialize GDT descriptor
4010 base15_00 = (ES << 4) + regs.u.r16.si;
4011 base23_16 = ES >> 12;
4012 if (base15_00 < (ES<<4))
4013 base23_16++;
4014 write_word(ES, regs.u.r16.si+0x08+0, 47); // limit 15:00 = 6 * 8bytes/descriptor
4015 write_word(ES, regs.u.r16.si+0x08+2, base15_00);// base 15:00
4016 write_byte(ES, regs.u.r16.si+0x08+4, base23_16);// base 23:16
4017 write_byte(ES, regs.u.r16.si+0x08+5, 0x93); // access
4018 write_word(ES, regs.u.r16.si+0x08+6, 0x0000); // base 31:24/reserved/limit 19:16
4020 // Initialize CS descriptor
4021 write_word(ES, regs.u.r16.si+0x20+0, 0xffff);// limit 15:00 = normal 64K limit
4022 write_word(ES, regs.u.r16.si+0x20+2, 0x0000);// base 15:00
4023 write_byte(ES, regs.u.r16.si+0x20+4, 0x000f);// base 23:16
4024 write_byte(ES, regs.u.r16.si+0x20+5, 0x9b); // access
4025 write_word(ES, regs.u.r16.si+0x20+6, 0x0000);// base 31:24/reserved/limit 19:16
4027 // Initialize SS descriptor
4028 ss = get_SS();
4029 base15_00 = ss << 4;
4030 base23_16 = ss >> 12;
4031 write_word(ES, regs.u.r16.si+0x28+0, 0xffff); // limit 15:00 = normal 64K limit
4032 write_word(ES, regs.u.r16.si+0x28+2, base15_00);// base 15:00
4033 write_byte(ES, regs.u.r16.si+0x28+4, base23_16);// base 23:16
4034 write_byte(ES, regs.u.r16.si+0x28+5, 0x93); // access
4035 write_word(ES, regs.u.r16.si+0x28+6, 0x0000); // base 31:24/reserved/limit 19:16
4037 CX = regs.u.r16.cx;
4038 ASM_START
4039 // Compile generates locals offset info relative to SP.
4040 // Get CX (word count) from stack.
4041 mov bx, sp
4042 SEG SS
4043 mov cx, _int15_function.CX [bx]
4045 // since we need to set SS:SP, save them to the BDA
4046 // for future restore
4047 push eax
4048 xor eax, eax
4049 mov ds, ax
4050 mov 0x0469, ss
4051 mov 0x0467, sp
4053 SEG ES
4054 lgdt [si + 0x08]
4055 SEG CS
4056 lidt [pmode_IDT_info]
4057 ;; perhaps do something with IDT here
4059 ;; set PE bit in CR0
4060 mov eax, cr0
4061 or al, #0x01
4062 mov cr0, eax
4063 ;; far jump to flush CPU queue after transition to protected mode
4064 JMP_AP(0x0020, protected_mode)
4066 protected_mode:
4067 ;; GDT points to valid descriptor table, now load SS, DS, ES
4068 mov ax, #0x28 ;; 101 000 = 5th descriptor in table, TI=GDT, RPL=00
4069 mov ss, ax
4070 mov ax, #0x10 ;; 010 000 = 2nd descriptor in table, TI=GDT, RPL=00
4071 mov ds, ax
4072 mov ax, #0x18 ;; 011 000 = 3rd descriptor in table, TI=GDT, RPL=00
4073 mov es, ax
4074 xor si, si
4075 xor di, di
4076 cld
4077 rep
4078 movsw ;; move CX words from DS:SI to ES:DI
4080 ;; make sure DS and ES limits are 64KB
4081 mov ax, #0x28
4082 mov ds, ax
4083 mov es, ax
4085 ;; reset PG bit in CR0 ???
4086 mov eax, cr0
4087 and al, #0xFE
4088 mov cr0, eax
4090 ;; far jump to flush CPU queue after transition to real mode
4091 JMP_AP(0xf000, real_mode)
4093 real_mode:
4094 ;; restore IDT to normal real-mode defaults
4095 SEG CS
4096 lidt [rmode_IDT_info]
4098 // restore SS:SP from the BDA
4099 xor ax, ax
4100 mov ds, ax
4101 mov ss, 0x0469
4102 mov sp, 0x0467
4103 pop eax
4104 ASM_END
4106 set_enable_a20(prev_a20_enable);
4108 // turn back on interrupts
4109 ASM_START
4110 sti
4111 ASM_END
4113 regs.u.r8.ah = 0;
4114 CLEAR_CF();
4115 break;
4118 case 0x88:
4119 // Get the amount of extended memory (above 1M)
4120 #if BX_CPU < 2
4121 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4122 SET_CF();
4123 #else
4124 regs.u.r8.al = inb_cmos(0x30);
4125 regs.u.r8.ah = inb_cmos(0x31);
4127 // limit to 15M
4128 if(regs.u.r16.ax > 0x3c00)
4129 regs.u.r16.ax = 0x3c00;
4131 CLEAR_CF();
4132 #endif
4133 break;
4135 case 0x90:
4136 /* Device busy interrupt. Called by Int 16h when no key available */
4137 break;
4139 case 0x91:
4140 /* Interrupt complete. Called by Int 16h when key becomes available */
4141 break;
4143 case 0xbf:
4144 BX_INFO("*** int 15h function AH=bf not yet supported!\n");
4145 SET_CF();
4146 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4147 break;
4149 case 0xC0:
4150 #if 0
4151 SET_CF();
4152 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4153 break;
4154 #endif
4155 CLEAR_CF();
4156 regs.u.r8.ah = 0;
4157 regs.u.r16.bx = BIOS_CONFIG_TABLE;
4158 ES = 0xF000;
4159 break;
4161 case 0xc1:
4162 ES = ebda_seg;
4163 CLEAR_CF();
4164 break;
4166 case 0xd8:
4167 bios_printf(BIOS_PRINTF_DEBUG, "EISA BIOS not present\n");
4168 SET_CF();
4169 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4170 break;
4172 default:
4173 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4174 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4175 SET_CF();
4176 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4177 break;
4181 #if BX_USE_PS2_MOUSE
4182 void
4183 int15_function_mouse(regs, ES, DS, FLAGS)
4184 pusha_regs_t regs; // REGS pushed via pusha
4185 Bit16u ES, DS, FLAGS;
4187 Bit16u ebda_seg=read_word(0x0040,0x000E);
4188 Bit8u mouse_flags_1, mouse_flags_2;
4189 Bit16u mouse_driver_seg;
4190 Bit16u mouse_driver_offset;
4191 Bit8u comm_byte, prev_command_byte;
4192 Bit8u ret, mouse_data1, mouse_data2, mouse_data3;
4194 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
4196 switch (regs.u.r8.ah) {
4197 case 0xC2:
4198 // Return Codes status in AH
4199 // =========================
4200 // 00: success
4201 // 01: invalid subfunction (AL > 7)
4202 // 02: invalid input value (out of allowable range)
4203 // 03: interface error
4204 // 04: resend command received from mouse controller,
4205 // device driver should attempt command again
4206 // 05: cannot enable mouse, since no far call has been installed
4207 // 80/86: mouse service not implemented
4209 switch (regs.u.r8.al) {
4210 case 0: // Disable/Enable Mouse
4211 BX_DEBUG_INT15("case 0:\n");
4212 switch (regs.u.r8.bh) {
4213 case 0: // Disable Mouse
4214 BX_DEBUG_INT15("case 0: disable mouse\n");
4215 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4216 ret = send_to_mouse_ctrl(0xF5); // disable mouse command
4217 if (ret == 0) {
4218 ret = get_mouse_data(&mouse_data1);
4219 if ( (ret == 0) || (mouse_data1 == 0xFA) ) {
4220 CLEAR_CF();
4221 regs.u.r8.ah = 0;
4222 return;
4226 // error
4227 SET_CF();
4228 regs.u.r8.ah = ret;
4229 return;
4230 break;
4232 case 1: // Enable Mouse
4233 BX_DEBUG_INT15("case 1: enable mouse\n");
4234 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4235 if ( (mouse_flags_2 & 0x80) == 0 ) {
4236 BX_DEBUG_INT15("INT 15h C2 Enable Mouse, no far call handler\n");
4237 SET_CF(); // error
4238 regs.u.r8.ah = 5; // no far call installed
4239 return;
4241 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4242 ret = send_to_mouse_ctrl(0xF4); // enable mouse command
4243 if (ret == 0) {
4244 ret = get_mouse_data(&mouse_data1);
4245 if ( (ret == 0) && (mouse_data1 == 0xFA) ) {
4246 enable_mouse_int_and_events(); // turn IRQ12 and packet generation on
4247 CLEAR_CF();
4248 regs.u.r8.ah = 0;
4249 return;
4252 SET_CF();
4253 regs.u.r8.ah = ret;
4254 return;
4256 default: // invalid subfunction
4257 BX_DEBUG_INT15("INT 15h C2 AL=0, BH=%02x\n", (unsigned) regs.u.r8.bh);
4258 SET_CF(); // error
4259 regs.u.r8.ah = 1; // invalid subfunction
4260 return;
4262 break;
4264 case 1: // Reset Mouse
4265 case 5: // Initialize Mouse
4266 BX_DEBUG_INT15("case 1 or 5:\n");
4267 if (regs.u.r8.al == 5) {
4268 if (regs.u.r8.bh != 3) {
4269 SET_CF();
4270 regs.u.r8.ah = 0x02; // invalid input
4271 return;
4273 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4274 mouse_flags_2 = (mouse_flags_2 & 0x00) | regs.u.r8.bh;
4275 mouse_flags_1 = 0x00;
4276 write_byte(ebda_seg, 0x0026, mouse_flags_1);
4277 write_byte(ebda_seg, 0x0027, mouse_flags_2);
4280 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4281 ret = send_to_mouse_ctrl(0xFF); // reset mouse command
4282 if (ret == 0) {
4283 ret = get_mouse_data(&mouse_data3);
4284 // if no mouse attached, it will return RESEND
4285 if (mouse_data3 == 0xfe) {
4286 SET_CF();
4287 return;
4289 if (mouse_data3 != 0xfa)
4290 BX_PANIC("Mouse reset returned %02x (should be ack)\n", (unsigned)mouse_data3);
4291 if ( ret == 0 ) {
4292 ret = get_mouse_data(&mouse_data1);
4293 if ( ret == 0 ) {
4294 ret = get_mouse_data(&mouse_data2);
4295 if ( ret == 0 ) {
4296 // turn IRQ12 and packet generation on
4297 enable_mouse_int_and_events();
4298 CLEAR_CF();
4299 regs.u.r8.ah = 0;
4300 regs.u.r8.bl = mouse_data1;
4301 regs.u.r8.bh = mouse_data2;
4302 return;
4308 // error
4309 SET_CF();
4310 regs.u.r8.ah = ret;
4311 return;
4313 case 2: // Set Sample Rate
4314 BX_DEBUG_INT15("case 2:\n");
4315 switch (regs.u.r8.bh) {
4316 case 0: mouse_data1 = 10; break; // 10 reports/sec
4317 case 1: mouse_data1 = 20; break; // 20 reports/sec
4318 case 2: mouse_data1 = 40; break; // 40 reports/sec
4319 case 3: mouse_data1 = 60; break; // 60 reports/sec
4320 case 4: mouse_data1 = 80; break; // 80 reports/sec
4321 case 5: mouse_data1 = 100; break; // 100 reports/sec (default)
4322 case 6: mouse_data1 = 200; break; // 200 reports/sec
4323 default: mouse_data1 = 0;
4325 if (mouse_data1 > 0) {
4326 ret = send_to_mouse_ctrl(0xF3); // set sample rate command
4327 if (ret == 0) {
4328 ret = get_mouse_data(&mouse_data2);
4329 ret = send_to_mouse_ctrl(mouse_data1);
4330 ret = get_mouse_data(&mouse_data2);
4331 CLEAR_CF();
4332 regs.u.r8.ah = 0;
4333 } else {
4334 // error
4335 SET_CF();
4336 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4338 } else {
4339 // error
4340 SET_CF();
4341 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4343 break;
4345 case 3: // Set Resolution
4346 BX_DEBUG_INT15("case 3:\n");
4347 // BX:
4348 // 0 = 25 dpi, 1 count per millimeter
4349 // 1 = 50 dpi, 2 counts per millimeter
4350 // 2 = 100 dpi, 4 counts per millimeter
4351 // 3 = 200 dpi, 8 counts per millimeter
4352 CLEAR_CF();
4353 regs.u.r8.ah = 0;
4354 break;
4356 case 4: // Get Device ID
4357 BX_DEBUG_INT15("case 4:\n");
4358 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4359 ret = send_to_mouse_ctrl(0xF2); // get mouse ID command
4360 if (ret == 0) {
4361 ret = get_mouse_data(&mouse_data1);
4362 ret = get_mouse_data(&mouse_data2);
4363 CLEAR_CF();
4364 regs.u.r8.ah = 0;
4365 regs.u.r8.bh = mouse_data2;
4366 } else {
4367 // error
4368 SET_CF();
4369 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4371 break;
4373 case 6: // Return Status & Set Scaling Factor...
4374 BX_DEBUG_INT15("case 6:\n");
4375 switch (regs.u.r8.bh) {
4376 case 0: // Return Status
4377 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4378 ret = send_to_mouse_ctrl(0xE9); // get mouse info command
4379 if (ret == 0) {
4380 ret = get_mouse_data(&mouse_data1);
4381 if (mouse_data1 != 0xfa)
4382 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
4383 if (ret == 0) {
4384 ret = get_mouse_data(&mouse_data1);
4385 if ( ret == 0 ) {
4386 ret = get_mouse_data(&mouse_data2);
4387 if ( ret == 0 ) {
4388 ret = get_mouse_data(&mouse_data3);
4389 if ( ret == 0 ) {
4390 CLEAR_CF();
4391 regs.u.r8.ah = 0;
4392 regs.u.r8.bl = mouse_data1;
4393 regs.u.r8.cl = mouse_data2;
4394 regs.u.r8.dl = mouse_data3;
4395 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4396 return;
4403 // error
4404 SET_CF();
4405 regs.u.r8.ah = ret;
4406 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4407 return;
4409 case 1: // Set Scaling Factor to 1:1
4410 case 2: // Set Scaling Factor to 2:1
4411 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4412 if (regs.u.r8.bh == 1) {
4413 ret = send_to_mouse_ctrl(0xE6);
4414 } else {
4415 ret = send_to_mouse_ctrl(0xE7);
4417 if (ret == 0) {
4418 get_mouse_data(&mouse_data1);
4419 ret = (mouse_data1 != 0xFA);
4421 if (ret == 0) {
4422 CLEAR_CF();
4423 regs.u.r8.ah = 0;
4424 } else {
4425 // error
4426 SET_CF();
4427 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4429 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4430 break;
4432 default:
4433 BX_PANIC("INT 15h C2 AL=6, BH=%02x\n", (unsigned) regs.u.r8.bh);
4435 break;
4437 case 7: // Set Mouse Handler Address
4438 BX_DEBUG_INT15("case 7:\n");
4439 mouse_driver_seg = ES;
4440 mouse_driver_offset = regs.u.r16.bx;
4441 write_word(ebda_seg, 0x0022, mouse_driver_offset);
4442 write_word(ebda_seg, 0x0024, mouse_driver_seg);
4443 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4444 if (mouse_driver_offset == 0 && mouse_driver_seg == 0) {
4445 /* remove handler */
4446 if ( (mouse_flags_2 & 0x80) != 0 ) {
4447 mouse_flags_2 &= ~0x80;
4448 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4451 else {
4452 /* install handler */
4453 mouse_flags_2 |= 0x80;
4455 write_byte(ebda_seg, 0x0027, mouse_flags_2);
4456 CLEAR_CF();
4457 regs.u.r8.ah = 0;
4458 break;
4460 default:
4461 BX_DEBUG_INT15("case default:\n");
4462 regs.u.r8.ah = 1; // invalid function
4463 SET_CF();
4465 break;
4467 default:
4468 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4469 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4470 SET_CF();
4471 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4472 break;
4475 #endif
4477 void
4478 int15_function32(regs, ES, DS, FLAGS)
4479 pushad_regs_t regs; // REGS pushed via pushad
4480 Bit16u ES, DS, FLAGS;
4482 Bit32u extended_memory_size=0; // 64bits long
4483 Bit16u CX,DX;
4485 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
4487 switch (regs.u.r8.ah) {
4488 case 0x86:
4489 // Wait for CX:DX microseconds. currently using the
4490 // refresh request port 0x61 bit4, toggling every 15usec
4492 CX = regs.u.r16.cx;
4493 DX = regs.u.r16.dx;
4495 ASM_START
4496 ;; Get the count in eax
4497 mov ax, .int15_function32.CX [bp]
4498 shl eax, #16
4499 mov ax, .int15_function32.DX [bp]
4501 ;; convert to numbers of 15usec ticks
4502 mov ebx, #15
4503 xor edx, edx
4504 div eax, ebx
4505 mov ecx, eax
4507 ;; wait for ecx number of refresh requests
4508 in al, #0x61
4509 and al,#0x10
4510 mov ah, al
4512 or ecx, ecx
4513 je int1586_tick_end
4514 int1586_tick:
4515 in al, #0x61
4516 and al,#0x10
4517 cmp al, ah
4518 je int1586_tick
4519 mov ah, al
4520 dec ecx
4521 jnz int1586_tick
4522 int1586_tick_end:
4523 ASM_END
4525 break;
4527 case 0xe8:
4528 switch(regs.u.r8.al)
4530 case 0x20: {
4531 Bit16u e820_table_size = read_word(0xe000, 0x8) * 0x14;
4533 if (regs.u.r32.edx != 0x534D4150) /* SMAP */
4534 goto int15_unimplemented;
4536 if ((regs.u.r16.bx / 0x14) * 0x14 == regs.u.r16.bx) {
4537 if (regs.u.r16.bx + 0x14 <= e820_table_size)
4538 memcpyb(ES, regs.u.r16.di,
4539 0xe000, 0x10 + regs.u.r16.bx, 0x14);
4540 regs.u.r32.ebx += 0x14;
4541 if ((regs.u.r32.ebx + 0x14 - 1) > e820_table_size)
4542 regs.u.r32.ebx = 0;
4543 } else if (regs.u.r16.bx == 1) {
4544 Bit32u base, type;
4545 Bit16u off;
4546 for (off = 0; off < e820_table_size; off += 0x14) {
4547 base = read_dword(0xe000, 0x10 + off);
4548 type = read_dword(0xe000, 0x20 + off);
4549 if ((base >= 0x100000) && (type == 1))
4550 break;
4552 if (off == e820_table_size) {
4553 SET_CF();
4554 break;
4556 memcpyb(ES, regs.u.r16.di, 0xe000, 0x10 + off, 0x14);
4557 regs.u.r32.ebx = 0;
4558 } else { /* AX=E820, DX=534D4150, BX unrecognized */
4559 goto int15_unimplemented;
4562 regs.u.r32.eax = 0x534D4150;
4563 regs.u.r32.ecx = 0x14;
4564 CLEAR_CF();
4565 break;
4568 case 0x01: {
4569 Bit16u off, e820_table_size = read_word(0xe000, 0x8) * 0x14;
4570 Bit32u base, type, size;
4572 // do we have any reason to fail here ?
4573 CLEAR_CF();
4575 // Get the amount of extended memory (above 1M)
4576 regs.u.r8.cl = inb_cmos(0x30);
4577 regs.u.r8.ch = inb_cmos(0x31);
4579 // limit to 15M
4580 if (regs.u.r16.cx > (15*1024))
4581 regs.u.r16.cx = 15*1024;
4583 // Find first RAM E820 entry >= 1MB.
4584 for (off = 0; off < e820_table_size; off += 0x14) {
4585 base = read_dword(0xe000, 0x10 + off);
4586 type = read_dword(0xe000, 0x20 + off);
4587 if ((base >= 0x100000) && (type == 1))
4588 break;
4591 // If there is RAM above 16MB, return amount in 64kB chunks.
4592 regs.u.r16.dx = 0;
4593 if (off != e820_table_size) {
4594 size = base + read_dword(0xe000, 0x18 + off);
4595 if (size > 0x1000000) {
4596 size -= 0x1000000;
4597 regs.u.r16.dx = (Bit16u)(size >> 16);
4601 // Set configured memory equal to extended memory
4602 regs.u.r16.ax = regs.u.r16.cx;
4603 regs.u.r16.bx = regs.u.r16.dx;
4604 break;
4606 default: /* AH=0xE8?? but not implemented */
4607 goto int15_unimplemented;
4609 break;
4610 int15_unimplemented:
4611 // fall into the default
4612 default:
4613 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4614 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4615 SET_CF();
4616 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4617 break;
4621 void
4622 int16_function(DI, SI, BP, SP, BX, DX, CX, AX, FLAGS)
4623 Bit16u DI, SI, BP, SP, BX, DX, CX, AX, FLAGS;
4625 Bit8u scan_code, ascii_code, shift_flags, count;
4626 Bit16u kbd_code, max;
4628 BX_DEBUG_INT16("int16: AX=%04x BX=%04x CX=%04x DX=%04x \n", AX, BX, CX, DX);
4630 switch (GET_AH()) {
4631 case 0x00: /* read keyboard input */
4633 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4634 BX_PANIC("KBD: int16h: out of keyboard input\n");
4636 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4637 else if (ascii_code == 0xE0) ascii_code = 0;
4638 AX = (scan_code << 8) | ascii_code;
4639 break;
4641 case 0x01: /* check keyboard status */
4642 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4643 SET_ZF();
4644 return;
4646 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4647 else if (ascii_code == 0xE0) ascii_code = 0;
4648 AX = (scan_code << 8) | ascii_code;
4649 CLEAR_ZF();
4650 break;
4652 case 0x02: /* get shift flag status */
4653 shift_flags = read_byte(0x0040, 0x17);
4654 SET_AL(shift_flags);
4655 break;
4657 case 0x05: /* store key-stroke into buffer */
4658 if ( !enqueue_key(GET_CH(), GET_CL()) ) {
4659 SET_AL(1);
4661 else {
4662 SET_AL(0);
4664 break;
4666 case 0x09: /* GET KEYBOARD FUNCTIONALITY */
4667 // bit Bochs Description
4668 // 7 0 reserved
4669 // 6 0 INT 16/AH=20h-22h supported (122-key keyboard support)
4670 // 5 1 INT 16/AH=10h-12h supported (enhanced keyboard support)
4671 // 4 1 INT 16/AH=0Ah supported
4672 // 3 0 INT 16/AX=0306h supported
4673 // 2 0 INT 16/AX=0305h supported
4674 // 1 0 INT 16/AX=0304h supported
4675 // 0 0 INT 16/AX=0300h supported
4676 //
4677 SET_AL(0x30);
4678 break;
4680 case 0x0A: /* GET KEYBOARD ID */
4681 count = 2;
4682 kbd_code = 0x0;
4683 outb(0x60, 0xf2);
4684 /* Wait for data */
4685 max=0xffff;
4686 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4687 if (max>0x0) {
4688 if ((inb(0x60) == 0xfa)) {
4689 do {
4690 max=0xffff;
4691 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4692 if (max>0x0) {
4693 kbd_code >>= 8;
4694 kbd_code |= (inb(0x60) << 8);
4696 } while (--count>0);
4699 BX=kbd_code;
4700 break;
4702 case 0x10: /* read MF-II keyboard input */
4704 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4705 BX_PANIC("KBD: int16h: out of keyboard input\n");
4707 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4708 AX = (scan_code << 8) | ascii_code;
4709 break;
4711 case 0x11: /* check MF-II keyboard status */
4712 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4713 SET_ZF();
4714 return;
4716 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4717 AX = (scan_code << 8) | ascii_code;
4718 CLEAR_ZF();
4719 break;
4721 case 0x12: /* get extended keyboard status */
4722 shift_flags = read_byte(0x0040, 0x17);
4723 SET_AL(shift_flags);
4724 shift_flags = read_byte(0x0040, 0x18);
4725 SET_AH(shift_flags);
4726 BX_DEBUG_INT16("int16: func 12 sending %04x\n",AX);
4727 break;
4729 case 0x92: /* keyboard capability check called by DOS 5.0+ keyb */
4730 SET_AH(0x80); // function int16 ah=0x10-0x12 supported
4731 break;
4733 case 0xA2: /* 122 keys capability check called by DOS 5.0+ keyb */
4734 // don't change AH : function int16 ah=0x20-0x22 NOT supported
4735 break;
4737 case 0x6F:
4738 if (GET_AL() == 0x08)
4739 SET_AH(0x02); // unsupported, aka normal keyboard
4741 default:
4742 BX_INFO("KBD: unsupported int 16h function %02x\n", GET_AH());
4746 unsigned int
4747 dequeue_key(scan_code, ascii_code, incr)
4748 Bit8u *scan_code;
4749 Bit8u *ascii_code;
4750 unsigned int incr;
4752 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail;
4753 Bit16u ss;
4754 Bit8u acode, scode;
4756 #if BX_CPU < 2
4757 buffer_start = 0x001E;
4758 buffer_end = 0x003E;
4759 #else
4760 buffer_start = read_word(0x0040, 0x0080);
4761 buffer_end = read_word(0x0040, 0x0082);
4762 #endif
4764 buffer_head = read_word(0x0040, 0x001a);
4765 buffer_tail = read_word(0x0040, 0x001c);
4767 if (buffer_head != buffer_tail) {
4768 ss = get_SS();
4769 acode = read_byte(0x0040, buffer_head);
4770 scode = read_byte(0x0040, buffer_head+1);
4771 write_byte(ss, ascii_code, acode);
4772 write_byte(ss, scan_code, scode);
4774 if (incr) {
4775 buffer_head += 2;
4776 if (buffer_head >= buffer_end)
4777 buffer_head = buffer_start;
4778 write_word(0x0040, 0x001a, buffer_head);
4780 return(1);
4782 else {
4783 return(0);
4787 static char panic_msg_keyb_buffer_full[] = "%s: keyboard input buffer full\n";
4789 Bit8u
4790 inhibit_mouse_int_and_events()
4792 Bit8u command_byte, prev_command_byte;
4794 // Turn off IRQ generation and aux data line
4795 if ( inb(0x64) & 0x02 )
4796 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4797 outb(0x64, 0x20); // get command byte
4798 while ( (inb(0x64) & 0x01) != 0x01 );
4799 prev_command_byte = inb(0x60);
4800 command_byte = prev_command_byte;
4801 //while ( (inb(0x64) & 0x02) );
4802 if ( inb(0x64) & 0x02 )
4803 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4804 command_byte &= 0xfd; // turn off IRQ 12 generation
4805 command_byte |= 0x20; // disable mouse serial clock line
4806 outb(0x64, 0x60); // write command byte
4807 outb(0x60, command_byte);
4808 return(prev_command_byte);
4811 void
4812 enable_mouse_int_and_events()
4814 Bit8u command_byte;
4816 // Turn on IRQ generation and aux data line
4817 if ( inb(0x64) & 0x02 )
4818 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4819 outb(0x64, 0x20); // get command byte
4820 while ( (inb(0x64) & 0x01) != 0x01 );
4821 command_byte = inb(0x60);
4822 //while ( (inb(0x64) & 0x02) );
4823 if ( inb(0x64) & 0x02 )
4824 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4825 command_byte |= 0x02; // turn on IRQ 12 generation
4826 command_byte &= 0xdf; // enable mouse serial clock line
4827 outb(0x64, 0x60); // write command byte
4828 outb(0x60, command_byte);
4831 Bit8u
4832 send_to_mouse_ctrl(sendbyte)
4833 Bit8u sendbyte;
4835 Bit8u response;
4837 // wait for chance to write to ctrl
4838 if ( inb(0x64) & 0x02 )
4839 BX_PANIC(panic_msg_keyb_buffer_full,"sendmouse");
4840 outb(0x64, 0xD4);
4841 outb(0x60, sendbyte);
4842 return(0);
4846 Bit8u
4847 get_mouse_data(data)
4848 Bit8u *data;
4850 Bit8u response;
4851 Bit16u ss;
4853 while ( (inb(0x64) & 0x21) != 0x21 ) {
4856 response = inb(0x60);
4858 ss = get_SS();
4859 write_byte(ss, data, response);
4860 return(0);
4863 void
4864 set_kbd_command_byte(command_byte)
4865 Bit8u command_byte;
4867 if ( inb(0x64) & 0x02 )
4868 BX_PANIC(panic_msg_keyb_buffer_full,"setkbdcomm");
4869 outb(0x64, 0xD4);
4871 outb(0x64, 0x60); // write command byte
4872 outb(0x60, command_byte);
4875 void
4876 int09_function(DI, SI, BP, SP, BX, DX, CX, AX)
4877 Bit16u DI, SI, BP, SP, BX, DX, CX, AX;
4879 Bit8u scancode, asciicode, shift_flags;
4880 Bit8u mf2_flags, mf2_state, led_flags;
4882 //
4883 // DS has been set to F000 before call
4884 //
4887 scancode = GET_AL();
4889 if (scancode == 0) {
4890 BX_INFO("KBD: int09 handler: AL=0\n");
4891 return;
4895 shift_flags = read_byte(0x0040, 0x17);
4896 mf2_flags = read_byte(0x0040, 0x18);
4897 mf2_state = read_byte(0x0040, 0x96);
4898 led_flags = read_byte(0x0040, 0x97);
4899 asciicode = 0;
4901 switch (scancode) {
4902 case 0x3a: /* Caps Lock press */
4903 shift_flags ^= 0x40;
4904 write_byte(0x0040, 0x17, shift_flags);
4905 mf2_flags |= 0x40;
4906 write_byte(0x0040, 0x18, mf2_flags);
4907 led_flags ^= 0x04;
4908 write_byte(0x0040, 0x97, led_flags);
4909 break;
4910 case 0xba: /* Caps Lock release */
4911 mf2_flags &= ~0x40;
4912 write_byte(0x0040, 0x18, mf2_flags);
4913 break;
4915 case 0x2a: /* L Shift press */
4916 /*shift_flags &= ~0x40;*/
4917 shift_flags |= 0x02;
4918 write_byte(0x0040, 0x17, shift_flags);
4919 led_flags &= ~0x04;
4920 write_byte(0x0040, 0x97, led_flags);
4921 break;
4922 case 0xaa: /* L Shift release */
4923 shift_flags &= ~0x02;
4924 write_byte(0x0040, 0x17, shift_flags);
4925 break;
4927 case 0x36: /* R Shift press */
4928 /*shift_flags &= ~0x40;*/
4929 shift_flags |= 0x01;
4930 write_byte(0x0040, 0x17, shift_flags);
4931 led_flags &= ~0x04;
4932 write_byte(0x0040, 0x97, led_flags);
4933 break;
4934 case 0xb6: /* R Shift release */
4935 shift_flags &= ~0x01;
4936 write_byte(0x0040, 0x17, shift_flags);
4937 break;
4939 case 0x1d: /* Ctrl press */
4940 shift_flags |= 0x04;
4941 write_byte(0x0040, 0x17, shift_flags);
4942 if (mf2_state & 0x01) {
4943 mf2_flags |= 0x04;
4944 } else {
4945 mf2_flags |= 0x01;
4947 write_byte(0x0040, 0x18, mf2_flags);
4948 break;
4949 case 0x9d: /* Ctrl release */
4950 shift_flags &= ~0x04;
4951 write_byte(0x0040, 0x17, shift_flags);
4952 if (mf2_state & 0x01) {
4953 mf2_flags &= ~0x04;
4954 } else {
4955 mf2_flags &= ~0x01;
4957 write_byte(0x0040, 0x18, mf2_flags);
4958 break;
4960 case 0x38: /* Alt press */
4961 shift_flags |= 0x08;
4962 write_byte(0x0040, 0x17, shift_flags);
4963 if (mf2_state & 0x01) {
4964 mf2_flags |= 0x08;
4965 } else {
4966 mf2_flags |= 0x02;
4968 write_byte(0x0040, 0x18, mf2_flags);
4969 break;
4970 case 0xb8: /* Alt release */
4971 shift_flags &= ~0x08;
4972 write_byte(0x0040, 0x17, shift_flags);
4973 if (mf2_state & 0x01) {
4974 mf2_flags &= ~0x08;
4975 } else {
4976 mf2_flags &= ~0x02;
4978 write_byte(0x0040, 0x18, mf2_flags);
4979 break;
4981 case 0x45: /* Num Lock press */
4982 if ((mf2_state & 0x01) == 0) {
4983 mf2_flags |= 0x20;
4984 write_byte(0x0040, 0x18, mf2_flags);
4985 shift_flags ^= 0x20;
4986 led_flags ^= 0x02;
4987 write_byte(0x0040, 0x17, shift_flags);
4988 write_byte(0x0040, 0x97, led_flags);
4990 break;
4991 case 0xc5: /* Num Lock release */
4992 if ((mf2_state & 0x01) == 0) {
4993 mf2_flags &= ~0x20;
4994 write_byte(0x0040, 0x18, mf2_flags);
4996 break;
4998 case 0x46: /* Scroll Lock press */
4999 mf2_flags |= 0x10;
5000 write_byte(0x0040, 0x18, mf2_flags);
5001 shift_flags ^= 0x10;
5002 led_flags ^= 0x01;
5003 write_byte(0x0040, 0x17, shift_flags);
5004 write_byte(0x0040, 0x97, led_flags);
5005 break;
5007 case 0xc6: /* Scroll Lock release */
5008 mf2_flags &= ~0x10;
5009 write_byte(0x0040, 0x18, mf2_flags);
5010 break;
5012 case 0x53: /* Del */
5013 if ((shift_flags & 0x0c) == 0x0c) /* Ctrl + Alt */
5014 machine_reset();
5015 /* Fall through */
5016 default:
5017 if (scancode & 0x80) return; /* toss key releases ... */
5018 if (scancode > MAX_SCAN_CODE) {
5019 BX_INFO("KBD: int09h_handler(): unknown scancode (%x) read!\n", scancode);
5020 return;
5022 if (shift_flags & 0x08) { /* ALT */
5023 asciicode = scan_to_scanascii[scancode].alt;
5024 scancode = scan_to_scanascii[scancode].alt >> 8;
5026 else if (shift_flags & 0x04) { /* CONTROL */
5027 asciicode = scan_to_scanascii[scancode].control;
5028 scancode = scan_to_scanascii[scancode].control >> 8;
5030 else if (shift_flags & 0x03) { /* LSHIFT + RSHIFT */
5031 /* check if lock state should be ignored
5032 * because a SHIFT key are pressed */
5034 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
5035 asciicode = scan_to_scanascii[scancode].normal;
5036 scancode = scan_to_scanascii[scancode].normal >> 8;
5038 else {
5039 asciicode = scan_to_scanascii[scancode].shift;
5040 scancode = scan_to_scanascii[scancode].shift >> 8;
5043 else {
5044 /* check if lock is on */
5045 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
5046 asciicode = scan_to_scanascii[scancode].shift;
5047 scancode = scan_to_scanascii[scancode].shift >> 8;
5049 else {
5050 asciicode = scan_to_scanascii[scancode].normal;
5051 scancode = scan_to_scanascii[scancode].normal >> 8;
5054 if (scancode==0 && asciicode==0) {
5055 BX_INFO("KBD: int09h_handler(): scancode & asciicode are zero?\n");
5057 enqueue_key(scancode, asciicode);
5058 break;
5060 mf2_state &= ~0x01;
5063 unsigned int
5064 enqueue_key(scan_code, ascii_code)
5065 Bit8u scan_code, ascii_code;
5067 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail, temp_tail;
5069 //BX_INFO("KBD: enqueue_key() called scan:%02x, ascii:%02x\n",
5070 // scan_code, ascii_code);
5072 #if BX_CPU < 2
5073 buffer_start = 0x001E;
5074 buffer_end = 0x003E;
5075 #else
5076 buffer_start = read_word(0x0040, 0x0080);
5077 buffer_end = read_word(0x0040, 0x0082);
5078 #endif
5080 buffer_head = read_word(0x0040, 0x001A);
5081 buffer_tail = read_word(0x0040, 0x001C);
5083 temp_tail = buffer_tail;
5084 buffer_tail += 2;
5085 if (buffer_tail >= buffer_end)
5086 buffer_tail = buffer_start;
5088 if (buffer_tail == buffer_head) {
5089 return(0);
5092 write_byte(0x0040, temp_tail, ascii_code);
5093 write_byte(0x0040, temp_tail+1, scan_code);
5094 write_word(0x0040, 0x001C, buffer_tail);
5095 return(1);
5099 void
5100 int74_function(make_farcall, Z, Y, X, status)
5101 Bit16u make_farcall, Z, Y, X, status;
5103 Bit16u ebda_seg=read_word(0x0040,0x000E);
5104 Bit8u in_byte, index, package_count;
5105 Bit8u mouse_flags_1, mouse_flags_2;
5107 BX_DEBUG_INT74("entering int74_function\n");
5108 make_farcall = 0;
5110 in_byte = inb(0x64);
5111 if ( (in_byte & 0x21) != 0x21 ) {
5112 return;
5114 in_byte = inb(0x60);
5115 BX_DEBUG_INT74("int74: read byte %02x\n", in_byte);
5117 mouse_flags_1 = read_byte(ebda_seg, 0x0026);
5118 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
5120 if ( (mouse_flags_2 & 0x80) != 0x80 ) {
5121 // BX_PANIC("int74_function:\n");
5122 return;
5125 package_count = mouse_flags_2 & 0x07;
5126 index = mouse_flags_1 & 0x07;
5127 write_byte(ebda_seg, 0x28 + index, in_byte);
5129 if ( (index+1) >= package_count ) {
5130 BX_DEBUG_INT74("int74_function: make_farcall=1\n");
5131 status = read_byte(ebda_seg, 0x0028 + 0);
5132 X = read_byte(ebda_seg, 0x0028 + 1);
5133 Y = read_byte(ebda_seg, 0x0028 + 2);
5134 Z = 0;
5135 mouse_flags_1 = 0;
5136 // check if far call handler installed
5137 if (mouse_flags_2 & 0x80)
5138 make_farcall = 1;
5140 else {
5141 mouse_flags_1++;
5143 write_byte(ebda_seg, 0x0026, mouse_flags_1);
5146 #define SET_DISK_RET_STATUS(status) write_byte(0x0040, 0x0074, status)
5148 #if BX_USE_ATADRV
5150 void
5151 int13_harddisk(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
5152 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
5154 Bit32u lba;
5155 Bit16u ebda_seg=read_word(0x0040,0x000E);
5156 Bit16u cylinder, head, sector;
5157 Bit16u segment, offset;
5158 Bit16u npc, nph, npspt, nlc, nlh, nlspt;
5159 Bit16u size, count;
5160 Bit8u device, status;
5162 BX_DEBUG_INT13_HD("int13_harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5164 write_byte(0x0040, 0x008e, 0); // clear completion flag
5166 // basic check : device has to be defined
5167 if ( (GET_ELDL() < 0x80) || (GET_ELDL() >= 0x80 + BX_MAX_ATA_DEVICES) ) {
5168 BX_INFO("int13_harddisk: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
5169 goto int13_fail;
5172 // Get the ata channel
5173 device=read_byte(ebda_seg,&EbdaData->ata.hdidmap[GET_ELDL()-0x80]);
5175 // basic check : device has to be valid
5176 if (device >= BX_MAX_ATA_DEVICES) {
5177 BX_INFO("int13_harddisk: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
5178 goto int13_fail;
5181 switch (GET_AH()) {
5183 case 0x00: /* disk controller reset */
5184 ata_reset (device);
5185 goto int13_success;
5186 break;
5188 case 0x01: /* read disk status */
5189 status = read_byte(0x0040, 0x0074);
5190 SET_AH(status);
5191 SET_DISK_RET_STATUS(0);
5192 /* set CF if error status read */
5193 if (status) goto int13_fail_nostatus;
5194 else goto int13_success_noah;
5195 break;
5197 case 0x02: // read disk sectors
5198 case 0x03: // write disk sectors
5199 case 0x04: // verify disk sectors
5201 count = GET_AL();
5202 cylinder = GET_CH();
5203 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
5204 sector = (GET_CL() & 0x3f);
5205 head = GET_DH();
5207 segment = ES;
5208 offset = BX;
5210 if ( (count > 128) || (count == 0) ) {
5211 BX_INFO("int13_harddisk: function %02x, count out of range!\n",GET_AH());
5212 goto int13_fail;
5215 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
5216 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
5217 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
5219 // sanity check on cyl heads, sec
5220 if( (cylinder >= nlc) || (head >= nlh) || (sector > nlspt )) {
5221 BX_INFO("int13_harddisk: function %02x, parameters out of range %04x/%04x/%04x!\n", GET_AH(), cylinder, head, sector);
5222 goto int13_fail;
5225 // FIXME verify
5226 if ( GET_AH() == 0x04 ) goto int13_success;
5228 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
5229 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
5231 // if needed, translate lchs to lba, and execute command
5232 if ( (nph != nlh) || (npspt != nlspt)) {
5233 lba = ((((Bit32u)cylinder * (Bit32u)nlh) + (Bit32u)head) * (Bit32u)nlspt) + (Bit32u)sector - 1;
5234 sector = 0; // this forces the command to be lba
5237 if ( GET_AH() == 0x02 )
5238 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, cylinder, head, sector, lba, segment, offset);
5239 else
5240 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, cylinder, head, sector, lba, segment, offset);
5242 // Set nb of sector transferred
5243 SET_AL(read_word(ebda_seg, &EbdaData->ata.trsfsectors));
5245 if (status != 0) {
5246 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
5247 SET_AH(0x0c);
5248 goto int13_fail_noah;
5251 goto int13_success;
5252 break;
5254 case 0x05: /* format disk track */
5255 BX_INFO("format disk track called\n");
5256 goto int13_success;
5257 return;
5258 break;
5260 case 0x08: /* read disk drive parameters */
5262 // Get logical geometry from table
5263 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
5264 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
5265 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
5266 count = read_byte(ebda_seg, &EbdaData->ata.hdcount);
5268 nlc = nlc - 2; /* 0 based , last sector not used */
5269 SET_AL(0);
5270 SET_CH(nlc & 0xff);
5271 SET_CL(((nlc >> 2) & 0xc0) | (nlspt & 0x3f));
5272 SET_DH(nlh - 1);
5273 SET_DL(count); /* FIXME returns 0, 1, or n hard drives */
5275 // FIXME should set ES & DI
5277 goto int13_success;
5278 break;
5280 case 0x10: /* check drive ready */
5281 // should look at 40:8E also???
5283 // Read the status from controller
5284 status = inb(read_word(ebda_seg, &EbdaData->ata.channels[device/2].iobase1) + ATA_CB_STAT);
5285 if ( (status & ( ATA_CB_STAT_BSY | ATA_CB_STAT_RDY )) == ATA_CB_STAT_RDY ) {
5286 goto int13_success;
5288 else {
5289 SET_AH(0xAA);
5290 goto int13_fail_noah;
5292 break;
5294 case 0x15: /* read disk drive size */
5296 // Get physical geometry from table
5297 npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
5298 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
5299 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
5301 // Compute sector count seen by int13
5302 lba = (Bit32u)(npc - 1) * (Bit32u)nph * (Bit32u)npspt;
5303 CX = lba >> 16;
5304 DX = lba & 0xffff;
5306 SET_AH(3); // hard disk accessible
5307 goto int13_success_noah;
5308 break;
5310 case 0x41: // IBM/MS installation check
5311 BX=0xaa55; // install check
5312 SET_AH(0x30); // EDD 3.0
5313 CX=0x0007; // ext disk access and edd, removable supported
5314 goto int13_success_noah;
5315 break;
5317 case 0x42: // IBM/MS extended read
5318 case 0x43: // IBM/MS extended write
5319 case 0x44: // IBM/MS verify
5320 case 0x47: // IBM/MS extended seek
5322 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
5323 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
5324 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
5326 // Can't use 64 bits lba
5327 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
5328 if (lba != 0L) {
5329 BX_PANIC("int13_harddisk: function %02x. Can't use 64bits lba\n",GET_AH());
5330 goto int13_fail;
5333 // Get 32 bits lba and check
5334 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5335 if (lba >= read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors) ) {
5336 BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH());
5337 goto int13_fail;
5340 // If verify or seek
5341 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
5342 goto int13_success;
5344 // Execute the command
5345 if ( GET_AH() == 0x42 )
5346 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, 0, 0, 0, lba, segment, offset);
5347 else
5348 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, 0, 0, 0, lba, segment, offset);
5350 count=read_word(ebda_seg, &EbdaData->ata.trsfsectors);
5351 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5353 if (status != 0) {
5354 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
5355 SET_AH(0x0c);
5356 goto int13_fail_noah;
5359 goto int13_success;
5360 break;
5362 case 0x45: // IBM/MS lock/unlock drive
5363 case 0x49: // IBM/MS extended media change
5364 goto int13_success; // Always success for HD
5365 break;
5367 case 0x46: // IBM/MS eject media
5368 SET_AH(0xb2); // Volume Not Removable
5369 goto int13_fail_noah; // Always fail for HD
5370 break;
5372 case 0x48: // IBM/MS get drive parameters
5373 size=read_word(DS,SI+(Bit16u)&Int13DPT->size);
5375 // Buffer is too small
5376 if(size < 0x1a)
5377 goto int13_fail;
5379 // EDD 1.x
5380 if(size >= 0x1a) {
5381 Bit16u blksize;
5383 npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
5384 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
5385 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
5386 lba = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors);
5387 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5389 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5390 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x02); // geometry is valid
5391 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, (Bit32u)npc);
5392 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, (Bit32u)nph);
5393 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, (Bit32u)npspt);
5394 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, lba); // FIXME should be Bit64
5395 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0L);
5396 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5399 // EDD 2.x
5400 if(size >= 0x1e) {
5401 Bit8u channel, dev, irq, mode, checksum, i, translation;
5402 Bit16u iobase1, iobase2, options;
5404 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5406 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5407 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5409 // Fill in dpte
5410 channel = device / 2;
5411 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5412 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5413 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5414 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5415 translation = read_byte(ebda_seg, &EbdaData->ata.devices[device].translation);
5417 options = (translation==ATA_TRANSLATION_NONE?0:1<<3); // chs translation
5418 options |= (1<<4); // lba translation
5419 options |= (mode==ATA_MODE_PIO32?1:0<<7);
5420 options |= (translation==ATA_TRANSLATION_LBA?1:0<<9);
5421 options |= (translation==ATA_TRANSLATION_RECHS?3:0<<9);
5423 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5424 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2);
5425 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5426 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5427 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5428 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5429 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5430 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5431 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5432 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5433 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5435 checksum=0;
5436 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, (&EbdaData->ata.dpte) + i);
5437 checksum = ~checksum;
5438 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5441 // EDD 3.x
5442 if(size >= 0x42) {
5443 Bit8u channel, iface, checksum, i;
5444 Bit16u iobase1;
5446 channel = device / 2;
5447 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5448 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5450 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5451 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5452 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5453 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5454 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5456 if (iface==ATA_IFACE_ISA) {
5457 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5458 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5459 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5460 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5462 else {
5463 // FIXME PCI
5465 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5466 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5467 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5468 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5470 if (iface==ATA_IFACE_ISA) {
5471 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5472 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5473 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5475 else {
5476 // FIXME PCI
5478 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5479 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5480 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5481 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5483 checksum=0;
5484 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5485 checksum = ~checksum;
5486 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5489 goto int13_success;
5490 break;
5492 case 0x4e: // // IBM/MS set hardware configuration
5493 // DMA, prefetch, PIO maximum not supported
5494 switch (GET_AL()) {
5495 case 0x01:
5496 case 0x03:
5497 case 0x04:
5498 case 0x06:
5499 goto int13_success;
5500 break;
5501 default :
5502 goto int13_fail;
5504 break;
5506 case 0x09: /* initialize drive parameters */
5507 case 0x0c: /* seek to specified cylinder */
5508 case 0x0d: /* alternate disk reset */
5509 case 0x11: /* recalibrate */
5510 case 0x14: /* controller internal diagnostic */
5511 BX_INFO("int13h_harddisk function %02xh unimplemented, returns success\n", GET_AH());
5512 goto int13_success;
5513 break;
5515 case 0x0a: /* read disk sectors with ECC */
5516 case 0x0b: /* write disk sectors with ECC */
5517 case 0x18: // set media type for format
5518 case 0x50: // IBM/MS send packet command
5519 default:
5520 BX_INFO("int13_harddisk function %02xh unsupported, returns fail\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 int13_fail_noah:
5528 SET_DISK_RET_STATUS(GET_AH());
5529 int13_fail_nostatus:
5530 SET_CF(); // error occurred
5531 return;
5533 int13_success:
5534 SET_AH(0x00); // no error
5535 int13_success_noah:
5536 SET_DISK_RET_STATUS(0x00);
5537 CLEAR_CF(); // no error
5538 return;
5541 // ---------------------------------------------------------------------------
5542 // Start of int13 for cdrom
5543 // ---------------------------------------------------------------------------
5545 void
5546 int13_cdrom(EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
5547 Bit16u EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
5549 Bit16u ebda_seg=read_word(0x0040,0x000E);
5550 Bit8u device, status, locks;
5551 Bit8u atacmd[12];
5552 Bit32u lba;
5553 Bit16u count, segment, offset, i, size;
5555 BX_DEBUG_INT13_CD("int13_cdrom: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5556 // BX_DEBUG_INT13_CD("int13_cdrom: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
5558 SET_DISK_RET_STATUS(0x00);
5560 /* basic check : device should be 0xE0+ */
5561 if( (GET_ELDL() < 0xE0) || (GET_ELDL() >= 0xE0+BX_MAX_ATA_DEVICES) ) {
5562 BX_INFO("int13_cdrom: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
5563 goto int13_fail;
5566 // Get the ata channel
5567 device=read_byte(ebda_seg,&EbdaData->ata.cdidmap[GET_ELDL()-0xE0]);
5569 /* basic check : device has to be valid */
5570 if (device >= BX_MAX_ATA_DEVICES) {
5571 BX_INFO("int13_cdrom: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
5572 goto int13_fail;
5575 switch (GET_AH()) {
5577 // all those functions return SUCCESS
5578 case 0x00: /* disk controller reset */
5579 case 0x09: /* initialize drive parameters */
5580 case 0x0c: /* seek to specified cylinder */
5581 case 0x0d: /* alternate disk reset */
5582 case 0x10: /* check drive ready */
5583 case 0x11: /* recalibrate */
5584 case 0x14: /* controller internal diagnostic */
5585 case 0x16: /* detect disk change */
5586 goto int13_success;
5587 break;
5589 // all those functions return disk write-protected
5590 case 0x03: /* write disk sectors */
5591 case 0x05: /* format disk track */
5592 case 0x43: // IBM/MS extended write
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 0x15: /* read disk drive size */
5608 SET_AH(0x02);
5609 goto int13_fail_noah;
5610 break;
5612 case 0x41: // IBM/MS installation check
5613 BX=0xaa55; // install check
5614 SET_AH(0x30); // EDD 2.1
5615 CX=0x0007; // ext disk access, removable and edd
5616 goto int13_success_noah;
5617 break;
5619 case 0x42: // IBM/MS extended read
5620 case 0x44: // IBM/MS verify sectors
5621 case 0x47: // IBM/MS extended seek
5623 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
5624 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
5625 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
5627 // Can't use 64 bits lba
5628 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
5629 if (lba != 0L) {
5630 BX_PANIC("int13_cdrom: function %02x. Can't use 64bits lba\n",GET_AH());
5631 goto int13_fail;
5634 // Get 32 bits lba
5635 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5637 // If verify or seek
5638 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
5639 goto int13_success;
5641 memsetb(get_SS(),atacmd,0,12);
5642 atacmd[0]=0x28; // READ command
5643 atacmd[7]=(count & 0xff00) >> 8; // Sectors
5644 atacmd[8]=(count & 0x00ff); // Sectors
5645 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
5646 atacmd[3]=(lba & 0x00ff0000) >> 16;
5647 atacmd[4]=(lba & 0x0000ff00) >> 8;
5648 atacmd[5]=(lba & 0x000000ff);
5649 status = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, count*2048L, ATA_DATA_IN, segment,offset);
5651 count = (Bit16u)(read_dword(ebda_seg, &EbdaData->ata.trsfbytes) >> 11);
5652 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5654 if (status != 0) {
5655 BX_INFO("int13_cdrom: function %02x, status %02x !\n",GET_AH(),status);
5656 SET_AH(0x0c);
5657 goto int13_fail_noah;
5660 goto int13_success;
5661 break;
5663 case 0x45: // IBM/MS lock/unlock drive
5664 if (GET_AL() > 2) goto int13_fail;
5666 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5668 switch (GET_AL()) {
5669 case 0 : // lock
5670 if (locks == 0xff) {
5671 SET_AH(0xb4);
5672 SET_AL(1);
5673 goto int13_fail_noah;
5675 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, ++locks);
5676 SET_AL(1);
5677 break;
5678 case 1 : // unlock
5679 if (locks == 0x00) {
5680 SET_AH(0xb0);
5681 SET_AL(0);
5682 goto int13_fail_noah;
5684 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, --locks);
5685 SET_AL(locks==0?0:1);
5686 break;
5687 case 2 : // status
5688 SET_AL(locks==0?0:1);
5689 break;
5691 goto int13_success;
5692 break;
5694 case 0x46: // IBM/MS eject media
5695 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5697 if (locks != 0) {
5698 SET_AH(0xb1); // media locked
5699 goto int13_fail_noah;
5701 // FIXME should handle 0x31 no media in device
5702 // FIXME should handle 0xb5 valid request failed
5704 // Call removable media eject
5705 ASM_START
5706 push bp
5707 mov bp, sp
5709 mov ah, #0x52
5710 int 15
5711 mov _int13_cdrom.status + 2[bp], ah
5712 jnc int13_cdrom_rme_end
5713 mov _int13_cdrom.status, #1
5714 int13_cdrom_rme_end:
5715 pop bp
5716 ASM_END
5718 if (status != 0) {
5719 SET_AH(0xb1); // media locked
5720 goto int13_fail_noah;
5723 goto int13_success;
5724 break;
5726 case 0x48: // IBM/MS get drive parameters
5727 size = read_word(DS,SI+(Bit16u)&Int13Ext->size);
5729 // Buffer is too small
5730 if(size < 0x1a)
5731 goto int13_fail;
5733 // EDD 1.x
5734 if(size >= 0x1a) {
5735 Bit16u cylinders, heads, spt, blksize;
5737 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5739 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5740 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x74); // removable, media change, lockable, max values
5741 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0xffffffff);
5742 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, 0xffffffff);
5743 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, 0xffffffff);
5744 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, 0xffffffff); // FIXME should be Bit64
5745 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0xffffffff);
5746 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5749 // EDD 2.x
5750 if(size >= 0x1e) {
5751 Bit8u channel, dev, irq, mode, checksum, i;
5752 Bit16u iobase1, iobase2, options;
5754 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5756 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5757 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5759 // Fill in dpte
5760 channel = device / 2;
5761 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5762 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5763 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5764 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5766 // FIXME atapi device
5767 options = (1<<4); // lba translation
5768 options |= (1<<5); // removable device
5769 options |= (1<<6); // atapi device
5770 options |= (mode==ATA_MODE_PIO32?1:0<<7);
5772 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5773 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2);
5774 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5775 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5776 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5777 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5778 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5779 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5780 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5781 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5782 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5784 checksum=0;
5785 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, (&EbdaData->ata.dpte) + i);
5786 checksum = ~checksum;
5787 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5790 // EDD 3.x
5791 if(size >= 0x42) {
5792 Bit8u channel, iface, checksum, i;
5793 Bit16u iobase1;
5795 channel = device / 2;
5796 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5797 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5799 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5800 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5801 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5802 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5803 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5805 if (iface==ATA_IFACE_ISA) {
5806 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5807 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5808 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5809 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5811 else {
5812 // FIXME PCI
5814 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5815 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5816 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5817 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5819 if (iface==ATA_IFACE_ISA) {
5820 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5821 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5822 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5824 else {
5825 // FIXME PCI
5827 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5828 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5829 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5830 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5832 checksum=0;
5833 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5834 checksum = ~checksum;
5835 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5838 goto int13_success;
5839 break;
5841 case 0x49: // IBM/MS extended media change
5842 // always send changed ??
5843 SET_AH(06);
5844 goto int13_fail_nostatus;
5845 break;
5847 case 0x4e: // // IBM/MS set hardware configuration
5848 // DMA, prefetch, PIO maximum not supported
5849 switch (GET_AL()) {
5850 case 0x01:
5851 case 0x03:
5852 case 0x04:
5853 case 0x06:
5854 goto int13_success;
5855 break;
5856 default :
5857 goto int13_fail;
5859 break;
5861 // all those functions return unimplemented
5862 case 0x02: /* read sectors */
5863 case 0x04: /* verify sectors */
5864 case 0x08: /* read disk drive parameters */
5865 case 0x0a: /* read disk sectors with ECC */
5866 case 0x0b: /* write disk sectors with ECC */
5867 case 0x18: /* set media type for format */
5868 case 0x50: // ? - send packet command
5869 default:
5870 BX_INFO("int13_cdrom: unsupported AH=%02x\n", GET_AH());
5871 goto int13_fail;
5872 break;
5875 int13_fail:
5876 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5877 int13_fail_noah:
5878 SET_DISK_RET_STATUS(GET_AH());
5879 int13_fail_nostatus:
5880 SET_CF(); // error occurred
5881 return;
5883 int13_success:
5884 SET_AH(0x00); // no error
5885 int13_success_noah:
5886 SET_DISK_RET_STATUS(0x00);
5887 CLEAR_CF(); // no error
5888 return;
5891 // ---------------------------------------------------------------------------
5892 // End of int13 for cdrom
5893 // ---------------------------------------------------------------------------
5895 #if BX_ELTORITO_BOOT
5896 // ---------------------------------------------------------------------------
5897 // Start of int13 for eltorito functions
5898 // ---------------------------------------------------------------------------
5900 void
5901 int13_eltorito(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
5902 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
5904 Bit16u ebda_seg=read_word(0x0040,0x000E);
5906 BX_DEBUG_INT13_ET("int13_eltorito: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5907 // BX_DEBUG_INT13_ET("int13_eltorito: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
5909 switch (GET_AH()) {
5911 // FIXME ElTorito Various. Should be implemented
5912 case 0x4a: // ElTorito - Initiate disk emu
5913 case 0x4c: // ElTorito - Initiate disk emu and boot
5914 case 0x4d: // ElTorito - Return Boot catalog
5915 BX_PANIC("Int13 eltorito call with AX=%04x. Please report\n",AX);
5916 goto int13_fail;
5917 break;
5919 case 0x4b: // ElTorito - Terminate disk emu
5920 // FIXME ElTorito Hardcoded
5921 write_byte(DS,SI+0x00,0x13);
5922 write_byte(DS,SI+0x01,read_byte(ebda_seg,&EbdaData->cdemu.media));
5923 write_byte(DS,SI+0x02,read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
5924 write_byte(DS,SI+0x03,read_byte(ebda_seg,&EbdaData->cdemu.controller_index));
5925 write_dword(DS,SI+0x04,read_dword(ebda_seg,&EbdaData->cdemu.ilba));
5926 write_word(DS,SI+0x08,read_word(ebda_seg,&EbdaData->cdemu.device_spec));
5927 write_word(DS,SI+0x0a,read_word(ebda_seg,&EbdaData->cdemu.buffer_segment));
5928 write_word(DS,SI+0x0c,read_word(ebda_seg,&EbdaData->cdemu.load_segment));
5929 write_word(DS,SI+0x0e,read_word(ebda_seg,&EbdaData->cdemu.sector_count));
5930 write_byte(DS,SI+0x10,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.cylinders));
5931 write_byte(DS,SI+0x11,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.spt));
5932 write_byte(DS,SI+0x12,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.heads));
5934 // If we have to terminate emulation
5935 if(GET_AL() == 0x00) {
5936 // FIXME ElTorito Various. Should be handled accordingly to spec
5937 write_byte(ebda_seg,&EbdaData->cdemu.active, 0x00); // bye bye
5940 goto int13_success;
5941 break;
5943 default:
5944 BX_INFO("int13_eltorito: unsupported AH=%02x\n", GET_AH());
5945 goto int13_fail;
5946 break;
5949 int13_fail:
5950 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5951 SET_DISK_RET_STATUS(GET_AH());
5952 SET_CF(); // error occurred
5953 return;
5955 int13_success:
5956 SET_AH(0x00); // no error
5957 SET_DISK_RET_STATUS(0x00);
5958 CLEAR_CF(); // no error
5959 return;
5962 // ---------------------------------------------------------------------------
5963 // End of int13 for eltorito functions
5964 // ---------------------------------------------------------------------------
5966 // ---------------------------------------------------------------------------
5967 // Start of int13 when emulating a device from the cd
5968 // ---------------------------------------------------------------------------
5970 void
5971 int13_cdemu(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
5972 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
5974 Bit16u ebda_seg=read_word(0x0040,0x000E);
5975 Bit8u device, status;
5976 Bit16u vheads, vspt, vcylinders;
5977 Bit16u head, sector, cylinder, nbsectors;
5978 Bit32u vlba, ilba, slba, elba;
5979 Bit16u before, segment, offset;
5980 Bit8u atacmd[12];
5982 BX_DEBUG_INT13_ET("int13_cdemu: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5983 //BX_DEBUG_INT13_ET("int13_cdemu: SS=%04x ES=%04x DI=%04x SI=%04x\n", get_SS(), ES, DI, SI);
5985 /* at this point, we are emulating a floppy/harddisk */
5987 // Recompute the device number
5988 device = read_byte(ebda_seg,&EbdaData->cdemu.controller_index) * 2;
5989 device += read_byte(ebda_seg,&EbdaData->cdemu.device_spec);
5991 SET_DISK_RET_STATUS(0x00);
5993 /* basic checks : emulation should be active, dl should equal the emulated drive */
5994 if( (read_byte(ebda_seg,&EbdaData->cdemu.active) ==0 )
5995 || (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive ) != GET_DL())) {
5996 BX_INFO("int13_cdemu: function %02x, emulation not active for DL= %02x\n", GET_AH(), GET_DL());
5997 goto int13_fail;
6001 switch (GET_AH()) {
6003 // all those functions return SUCCESS
6004 case 0x00: /* disk controller reset */
6005 case 0x09: /* initialize drive parameters */
6006 case 0x0c: /* seek to specified cylinder */
6007 case 0x0d: /* alternate disk reset */ // FIXME ElTorito Various. should really reset ?
6008 case 0x10: /* check drive ready */ // FIXME ElTorito Various. should check if ready ?
6009 case 0x11: /* recalibrate */
6010 case 0x14: /* controller internal diagnostic */
6011 case 0x16: /* detect disk change */
6012 goto int13_success;
6013 break;
6015 // all those functions return disk write-protected
6016 case 0x03: /* write disk sectors */
6017 case 0x05: /* format disk track */
6018 SET_AH(0x03);
6019 goto int13_fail_noah;
6020 break;
6022 case 0x01: /* read disk status */
6023 status=read_byte(0x0040, 0x0074);
6024 SET_AH(status);
6025 SET_DISK_RET_STATUS(0);
6027 /* set CF if error status read */
6028 if (status) goto int13_fail_nostatus;
6029 else goto int13_success_noah;
6030 break;
6032 case 0x02: // read disk sectors
6033 case 0x04: // verify disk sectors
6034 vspt = read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
6035 vcylinders = read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders);
6036 vheads = read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads);
6038 ilba = read_dword(ebda_seg,&EbdaData->cdemu.ilba);
6040 sector = GET_CL() & 0x003f;
6041 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
6042 head = GET_DH();
6043 nbsectors = GET_AL();
6044 segment = ES;
6045 offset = BX;
6047 // no sector to read ?
6048 if(nbsectors==0) goto int13_success;
6050 // sanity checks sco openserver needs this!
6051 if ((sector > vspt)
6052 || (cylinder >= vcylinders)
6053 || (head >= vheads)) {
6054 goto int13_fail;
6057 // After controls, verify do nothing
6058 if (GET_AH() == 0x04) goto int13_success;
6060 segment = ES+(BX / 16);
6061 offset = BX % 16;
6063 // calculate the virtual lba inside the image
6064 vlba=((((Bit32u)cylinder*(Bit32u)vheads)+(Bit32u)head)*(Bit32u)vspt)+((Bit32u)(sector-1));
6066 // In advance so we don't loose the count
6067 SET_AL(nbsectors);
6069 // start lba on cd
6070 slba = (Bit32u)vlba/4;
6071 before= (Bit16u)vlba%4;
6073 // end lba on cd
6074 elba = (Bit32u)(vlba+nbsectors-1)/4;
6076 memsetb(get_SS(),atacmd,0,12);
6077 atacmd[0]=0x28; // READ command
6078 atacmd[7]=((Bit16u)(elba-slba+1) & 0xff00) >> 8; // Sectors
6079 atacmd[8]=((Bit16u)(elba-slba+1) & 0x00ff); // Sectors
6080 atacmd[2]=(ilba+slba & 0xff000000) >> 24; // LBA
6081 atacmd[3]=(ilba+slba & 0x00ff0000) >> 16;
6082 atacmd[4]=(ilba+slba & 0x0000ff00) >> 8;
6083 atacmd[5]=(ilba+slba & 0x000000ff);
6084 if((status = ata_cmd_packet(device, 12, get_SS(), atacmd, before*512, nbsectors*512L, ATA_DATA_IN, segment,offset)) != 0) {
6085 BX_INFO("int13_cdemu: function %02x, error %02x !\n",GET_AH(),status);
6086 SET_AH(0x02);
6087 SET_AL(0);
6088 goto int13_fail_noah;
6091 goto int13_success;
6092 break;
6094 case 0x08: /* read disk drive parameters */
6095 vspt=read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
6096 vcylinders=read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders) - 1;
6097 vheads=read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads) - 1;
6099 SET_AL( 0x00 );
6100 SET_BL( 0x00 );
6101 SET_CH( vcylinders & 0xff );
6102 SET_CL((( vcylinders >> 2) & 0xc0) | ( vspt & 0x3f ));
6103 SET_DH( vheads );
6104 SET_DL( 0x02 ); // FIXME ElTorito Various. should send the real count of drives 1 or 2
6105 // FIXME ElTorito Harddisk. should send the HD count
6107 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
6108 case 0x01: SET_BL( 0x02 ); break;
6109 case 0x02: SET_BL( 0x04 ); break;
6110 case 0x03: SET_BL( 0x06 ); break;
6113 ASM_START
6114 push bp
6115 mov bp, sp
6116 mov ax, #diskette_param_table2
6117 mov _int13_cdemu.DI+2[bp], ax
6118 mov _int13_cdemu.ES+2[bp], cs
6119 pop bp
6120 ASM_END
6121 goto int13_success;
6122 break;
6124 case 0x15: /* read disk drive size */
6125 // FIXME ElTorito Harddisk. What geometry to send ?
6126 SET_AH(0x03);
6127 goto int13_success_noah;
6128 break;
6130 // all those functions return unimplemented
6131 case 0x0a: /* read disk sectors with ECC */
6132 case 0x0b: /* write disk sectors with ECC */
6133 case 0x18: /* set media type for format */
6134 case 0x41: // IBM/MS installation check
6135 // FIXME ElTorito Harddisk. Darwin would like to use EDD
6136 case 0x42: // IBM/MS extended read
6137 case 0x43: // IBM/MS extended write
6138 case 0x44: // IBM/MS verify sectors
6139 case 0x45: // IBM/MS lock/unlock drive
6140 case 0x46: // IBM/MS eject media
6141 case 0x47: // IBM/MS extended seek
6142 case 0x48: // IBM/MS get drive parameters
6143 case 0x49: // IBM/MS extended media change
6144 case 0x4e: // ? - set hardware configuration
6145 case 0x50: // ? - send packet command
6146 default:
6147 BX_INFO("int13_cdemu function AH=%02x unsupported, returns fail\n", GET_AH());
6148 goto int13_fail;
6149 break;
6152 int13_fail:
6153 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
6154 int13_fail_noah:
6155 SET_DISK_RET_STATUS(GET_AH());
6156 int13_fail_nostatus:
6157 SET_CF(); // error occurred
6158 return;
6160 int13_success:
6161 SET_AH(0x00); // no error
6162 int13_success_noah:
6163 SET_DISK_RET_STATUS(0x00);
6164 CLEAR_CF(); // no error
6165 return;
6168 // ---------------------------------------------------------------------------
6169 // End of int13 when emulating a device from the cd
6170 // ---------------------------------------------------------------------------
6172 #endif // BX_ELTORITO_BOOT
6174 #else //BX_USE_ATADRV
6176 void
6177 outLBA(cylinder,hd_heads,head,hd_sectors,sector,dl)
6178 Bit16u cylinder;
6179 Bit16u hd_heads;
6180 Bit16u head;
6181 Bit16u hd_sectors;
6182 Bit16u sector;
6183 Bit16u dl;
6185 ASM_START
6186 push bp
6187 mov bp, sp
6188 push eax
6189 push ebx
6190 push edx
6191 xor eax,eax
6192 mov ax,4[bp] // cylinder
6193 xor ebx,ebx
6194 mov bl,6[bp] // hd_heads
6195 imul ebx
6197 mov bl,8[bp] // head
6198 add eax,ebx
6199 mov bl,10[bp] // hd_sectors
6200 imul ebx
6201 mov bl,12[bp] // sector
6202 add eax,ebx
6204 dec eax
6205 mov dx,#0x1f3
6206 out dx,al
6207 mov dx,#0x1f4
6208 mov al,ah
6209 out dx,al
6210 shr eax,#16
6211 mov dx,#0x1f5
6212 out dx,al
6213 and ah,#0xf
6214 mov bl,14[bp] // dl
6215 and bl,#1
6216 shl bl,#4
6217 or ah,bl
6218 or ah,#0xe0
6219 mov al,ah
6220 mov dx,#0x01f6
6221 out dx,al
6222 pop edx
6223 pop ebx
6224 pop eax
6225 pop bp
6226 ASM_END
6229 void
6230 int13_harddisk(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
6231 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
6233 Bit8u drive, num_sectors, sector, head, status, mod;
6234 Bit8u drive_map;
6235 Bit8u n_drives;
6236 Bit16u cyl_mod, ax;
6237 Bit16u max_cylinder, cylinder, total_sectors;
6238 Bit16u hd_cylinders;
6239 Bit8u hd_heads, hd_sectors;
6240 Bit16u val16;
6241 Bit8u sector_count;
6242 unsigned int i;
6243 Bit16u tempbx;
6244 Bit16u dpsize;
6246 Bit16u count, segment, offset;
6247 Bit32u lba;
6248 Bit16u error;
6250 BX_DEBUG_INT13_HD("int13 harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
6252 write_byte(0x0040, 0x008e, 0); // clear completion flag
6254 /* at this point, DL is >= 0x80 to be passed from the floppy int13h
6255 handler code */
6256 /* check how many disks first (cmos reg 0x12), return an error if
6257 drive not present */
6258 drive_map = inb_cmos(0x12);
6259 drive_map = (((drive_map & 0xf0)==0) ? 0 : 1) |
6260 (((drive_map & 0x0f)==0) ? 0 : 2);
6261 n_drives = (drive_map==0) ? 0 :
6262 ((drive_map==3) ? 2 : 1);
6264 if (!(drive_map & (1<<(GET_ELDL()&0x7f)))) { /* allow 0, 1, or 2 disks */
6265 SET_AH(0x01);
6266 SET_DISK_RET_STATUS(0x01);
6267 SET_CF(); /* error occurred */
6268 return;
6271 switch (GET_AH()) {
6273 case 0x00: /* disk controller reset */
6274 BX_DEBUG_INT13_HD("int13_f00\n");
6276 SET_AH(0);
6277 SET_DISK_RET_STATUS(0);
6278 set_diskette_ret_status(0);
6279 set_diskette_current_cyl(0, 0); /* current cylinder, diskette 1 */
6280 set_diskette_current_cyl(1, 0); /* current cylinder, diskette 2 */
6281 CLEAR_CF(); /* successful */
6282 return;
6283 break;
6285 case 0x01: /* read disk status */
6286 BX_DEBUG_INT13_HD("int13_f01\n");
6287 status = read_byte(0x0040, 0x0074);
6288 SET_AH(status);
6289 SET_DISK_RET_STATUS(0);
6290 /* set CF if error status read */
6291 if (status) SET_CF();
6292 else CLEAR_CF();
6293 return;
6294 break;
6296 case 0x04: // verify disk sectors
6297 case 0x02: // read disk sectors
6298 drive = GET_ELDL();
6299 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6301 num_sectors = GET_AL();
6302 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
6303 sector = (GET_CL() & 0x3f);
6304 head = GET_DH();
6307 if (hd_cylinders > 1024) {
6308 if (hd_cylinders <= 2048) {
6309 cylinder <<= 1;
6311 else if (hd_cylinders <= 4096) {
6312 cylinder <<= 2;
6314 else if (hd_cylinders <= 8192) {
6315 cylinder <<= 3;
6317 else { // hd_cylinders <= 16384
6318 cylinder <<= 4;
6321 ax = head / hd_heads;
6322 cyl_mod = ax & 0xff;
6323 head = ax >> 8;
6324 cylinder |= cyl_mod;
6327 if ( (cylinder >= hd_cylinders) ||
6328 (sector > hd_sectors) ||
6329 (head >= hd_heads) ) {
6330 SET_AH(1);
6331 SET_DISK_RET_STATUS(1);
6332 SET_CF(); /* error occurred */
6333 return;
6336 if ( (num_sectors > 128) || (num_sectors == 0) )
6337 BX_PANIC("int13_harddisk(): num_sectors out of range!\n");
6339 if (head > 15)
6340 BX_PANIC("hard drive BIOS:(read/verify) head > 15\n");
6342 if ( GET_AH() == 0x04 ) {
6343 SET_AH(0);
6344 SET_DISK_RET_STATUS(0);
6345 CLEAR_CF();
6346 return;
6349 status = inb(0x1f7);
6350 if (status & 0x80) {
6351 BX_PANIC("hard drive BIOS:(read/verify) BUSY bit set\n");
6353 outb(0x01f2, num_sectors);
6354 /* activate LBA? (tomv) */
6355 if (hd_heads > 16) {
6356 BX_DEBUG_INT13_HD("CHS: %x %x %x\n", cylinder, head, sector);
6357 outLBA(cylinder,hd_heads,head,hd_sectors,sector,drive);
6359 else {
6360 outb(0x01f3, sector);
6361 outb(0x01f4, cylinder & 0x00ff);
6362 outb(0x01f5, cylinder >> 8);
6363 outb(0x01f6, 0xa0 | ((drive & 0x01)<<4) | (head & 0x0f));
6365 outb(0x01f7, 0x20);
6367 while (1) {
6368 status = inb(0x1f7);
6369 if ( !(status & 0x80) ) break;
6372 if (status & 0x01) {
6373 BX_PANIC("hard drive BIOS:(read/verify) read error\n");
6374 } else if ( !(status & 0x08) ) {
6375 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
6376 BX_PANIC("hard drive BIOS:(read/verify) expected DRQ=1\n");
6379 sector_count = 0;
6380 tempbx = BX;
6382 ASM_START
6383 sti ;; enable higher priority interrupts
6384 ASM_END
6386 while (1) {
6387 ASM_START
6388 ;; store temp bx in real DI register
6389 push bp
6390 mov bp, sp
6391 mov di, _int13_harddisk.tempbx + 2 [bp]
6392 pop bp
6394 ;; adjust if there will be an overrun
6395 cmp di, #0xfe00
6396 jbe i13_f02_no_adjust
6397 i13_f02_adjust:
6398 sub di, #0x0200 ; sub 512 bytes from offset
6399 mov ax, es
6400 add ax, #0x0020 ; add 512 to segment
6401 mov es, ax
6403 i13_f02_no_adjust:
6404 mov cx, #0x0100 ;; counter (256 words = 512b)
6405 mov dx, #0x01f0 ;; AT data read port
6407 rep
6408 insw ;; CX words transfered from port(DX) to ES:[DI]
6410 i13_f02_done:
6411 ;; store real DI register back to temp bx
6412 push bp
6413 mov bp, sp
6414 mov _int13_harddisk.tempbx + 2 [bp], di
6415 pop bp
6416 ASM_END
6418 sector_count++;
6419 num_sectors--;
6420 if (num_sectors == 0) {
6421 status = inb(0x1f7);
6422 if ( (status & 0xc9) != 0x40 )
6423 BX_PANIC("no sectors left to read/verify, status is %02x\n", (unsigned) status);
6424 break;
6426 else {
6427 status = inb(0x1f7);
6428 if ( (status & 0xc9) != 0x48 )
6429 BX_PANIC("more sectors left to read/verify, status is %02x\n", (unsigned) status);
6430 continue;
6434 SET_AH(0);
6435 SET_DISK_RET_STATUS(0);
6436 SET_AL(sector_count);
6437 CLEAR_CF(); /* successful */
6438 return;
6439 break;
6442 case 0x03: /* write disk sectors */
6443 BX_DEBUG_INT13_HD("int13_f03\n");
6444 drive = GET_ELDL ();
6445 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6447 num_sectors = GET_AL();
6448 cylinder = GET_CH();
6449 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
6450 sector = (GET_CL() & 0x3f);
6451 head = GET_DH();
6453 if (hd_cylinders > 1024) {
6454 if (hd_cylinders <= 2048) {
6455 cylinder <<= 1;
6457 else if (hd_cylinders <= 4096) {
6458 cylinder <<= 2;
6460 else if (hd_cylinders <= 8192) {
6461 cylinder <<= 3;
6463 else { // hd_cylinders <= 16384
6464 cylinder <<= 4;
6467 ax = head / hd_heads;
6468 cyl_mod = ax & 0xff;
6469 head = ax >> 8;
6470 cylinder |= cyl_mod;
6473 if ( (cylinder >= hd_cylinders) ||
6474 (sector > hd_sectors) ||
6475 (head >= hd_heads) ) {
6476 SET_AH( 1);
6477 SET_DISK_RET_STATUS(1);
6478 SET_CF(); /* error occurred */
6479 return;
6482 if ( (num_sectors > 128) || (num_sectors == 0) )
6483 BX_PANIC("int13_harddisk(): num_sectors out of range!\n");
6485 if (head > 15)
6486 BX_PANIC("hard drive BIOS:(read) head > 15\n");
6488 status = inb(0x1f7);
6489 if (status & 0x80) {
6490 BX_PANIC("hard drive BIOS:(read) BUSY bit set\n");
6492 // should check for Drive Ready Bit also in status reg
6493 outb(0x01f2, num_sectors);
6495 /* activate LBA? (tomv) */
6496 if (hd_heads > 16) {
6497 BX_DEBUG_INT13_HD("CHS (write): %x %x %x\n", cylinder, head, sector);
6498 outLBA(cylinder,hd_heads,head,hd_sectors,sector,GET_ELDL());
6500 else {
6501 outb(0x01f3, sector);
6502 outb(0x01f4, cylinder & 0x00ff);
6503 outb(0x01f5, cylinder >> 8);
6504 outb(0x01f6, 0xa0 | ((GET_ELDL() & 0x01)<<4) | (head & 0x0f));
6506 outb(0x01f7, 0x30);
6508 // wait for busy bit to turn off after seeking
6509 while (1) {
6510 status = inb(0x1f7);
6511 if ( !(status & 0x80) ) break;
6514 if ( !(status & 0x08) ) {
6515 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
6516 BX_PANIC("hard drive BIOS:(write) data-request bit not set\n");
6519 sector_count = 0;
6520 tempbx = BX;
6522 ASM_START
6523 sti ;; enable higher priority interrupts
6524 ASM_END
6526 while (1) {
6527 ASM_START
6528 ;; store temp bx in real SI register
6529 push bp
6530 mov bp, sp
6531 mov si, _int13_harddisk.tempbx + 2 [bp]
6532 pop bp
6534 ;; adjust if there will be an overrun
6535 cmp si, #0xfe00
6536 jbe i13_f03_no_adjust
6537 i13_f03_adjust:
6538 sub si, #0x0200 ; sub 512 bytes from offset
6539 mov ax, es
6540 add ax, #0x0020 ; add 512 to segment
6541 mov es, ax
6543 i13_f03_no_adjust:
6544 mov cx, #0x0100 ;; counter (256 words = 512b)
6545 mov dx, #0x01f0 ;; AT data read port
6547 seg ES
6548 rep
6549 outsw ;; CX words tranfered from ES:[SI] to port(DX)
6551 ;; store real SI register back to temp bx
6552 push bp
6553 mov bp, sp
6554 mov _int13_harddisk.tempbx + 2 [bp], si
6555 pop bp
6556 ASM_END
6558 sector_count++;
6559 num_sectors--;
6560 if (num_sectors == 0) {
6561 status = inb(0x1f7);
6562 if ( (status & 0xe9) != 0x40 )
6563 BX_PANIC("no sectors left to write, status is %02x\n", (unsigned) status);
6564 break;
6566 else {
6567 status = inb(0x1f7);
6568 if ( (status & 0xc9) != 0x48 )
6569 BX_PANIC("more sectors left to write, status is %02x\n", (unsigned) status);
6570 continue;
6574 SET_AH(0);
6575 SET_DISK_RET_STATUS(0);
6576 SET_AL(sector_count);
6577 CLEAR_CF(); /* successful */
6578 return;
6579 break;
6581 case 0x05: /* format disk track */
6582 BX_DEBUG_INT13_HD("int13_f05\n");
6583 BX_PANIC("format disk track called\n");
6584 /* nop */
6585 SET_AH(0);
6586 SET_DISK_RET_STATUS(0);
6587 CLEAR_CF(); /* successful */
6588 return;
6589 break;
6591 case 0x08: /* read disk drive parameters */
6592 BX_DEBUG_INT13_HD("int13_f08\n");
6594 drive = GET_ELDL ();
6595 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6597 // translate CHS
6598 //
6599 if (hd_cylinders <= 1024) {
6600 // hd_cylinders >>= 0;
6601 // hd_heads <<= 0;
6603 else if (hd_cylinders <= 2048) {
6604 hd_cylinders >>= 1;
6605 hd_heads <<= 1;
6607 else if (hd_cylinders <= 4096) {
6608 hd_cylinders >>= 2;
6609 hd_heads <<= 2;
6611 else if (hd_cylinders <= 8192) {
6612 hd_cylinders >>= 3;
6613 hd_heads <<= 3;
6615 else { // hd_cylinders <= 16384
6616 hd_cylinders >>= 4;
6617 hd_heads <<= 4;
6620 max_cylinder = hd_cylinders - 2; /* 0 based */
6621 SET_AL(0);
6622 SET_CH(max_cylinder & 0xff);
6623 SET_CL(((max_cylinder >> 2) & 0xc0) | (hd_sectors & 0x3f));
6624 SET_DH(hd_heads - 1);
6625 SET_DL(n_drives); /* returns 0, 1, or 2 hard drives */
6626 SET_AH(0);
6627 SET_DISK_RET_STATUS(0);
6628 CLEAR_CF(); /* successful */
6630 return;
6631 break;
6633 case 0x09: /* initialize drive parameters */
6634 BX_DEBUG_INT13_HD("int13_f09\n");
6635 SET_AH(0);
6636 SET_DISK_RET_STATUS(0);
6637 CLEAR_CF(); /* successful */
6638 return;
6639 break;
6641 case 0x0a: /* read disk sectors with ECC */
6642 BX_DEBUG_INT13_HD("int13_f0a\n");
6643 case 0x0b: /* write disk sectors with ECC */
6644 BX_DEBUG_INT13_HD("int13_f0b\n");
6645 BX_PANIC("int13h Functions 0Ah & 0Bh not implemented!\n");
6646 return;
6647 break;
6649 case 0x0c: /* seek to specified cylinder */
6650 BX_DEBUG_INT13_HD("int13_f0c\n");
6651 BX_INFO("int13h function 0ch (seek) not implemented!\n");
6652 SET_AH(0);
6653 SET_DISK_RET_STATUS(0);
6654 CLEAR_CF(); /* successful */
6655 return;
6656 break;
6658 case 0x0d: /* alternate disk reset */
6659 BX_DEBUG_INT13_HD("int13_f0d\n");
6660 SET_AH(0);
6661 SET_DISK_RET_STATUS(0);
6662 CLEAR_CF(); /* successful */
6663 return;
6664 break;
6666 case 0x10: /* check drive ready */
6667 BX_DEBUG_INT13_HD("int13_f10\n");
6668 //SET_AH(0);
6669 //SET_DISK_RET_STATUS(0);
6670 //CLEAR_CF(); /* successful */
6671 //return;
6672 //break;
6674 // should look at 40:8E also???
6675 status = inb(0x01f7);
6676 if ( (status & 0xc0) == 0x40 ) {
6677 SET_AH(0);
6678 SET_DISK_RET_STATUS(0);
6679 CLEAR_CF(); // drive ready
6680 return;
6682 else {
6683 SET_AH(0xAA);
6684 SET_DISK_RET_STATUS(0xAA);
6685 SET_CF(); // not ready
6686 return;
6688 break;
6690 case 0x11: /* recalibrate */
6691 BX_DEBUG_INT13_HD("int13_f11\n");
6692 SET_AH(0);
6693 SET_DISK_RET_STATUS(0);
6694 CLEAR_CF(); /* successful */
6695 return;
6696 break;
6698 case 0x14: /* controller internal diagnostic */
6699 BX_DEBUG_INT13_HD("int13_f14\n");
6700 SET_AH(0);
6701 SET_DISK_RET_STATUS(0);
6702 CLEAR_CF(); /* successful */
6703 SET_AL(0);
6704 return;
6705 break;
6707 case 0x15: /* read disk drive size */
6708 drive = GET_ELDL();
6709 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6710 ASM_START
6711 push bp
6712 mov bp, sp
6713 mov al, _int13_harddisk.hd_heads + 2 [bp]
6714 mov ah, _int13_harddisk.hd_sectors + 2 [bp]
6715 mul al, ah ;; ax = heads * sectors
6716 mov bx, _int13_harddisk.hd_cylinders + 2 [bp]
6717 dec bx ;; use (cylinders - 1) ???
6718 mul ax, bx ;; dx:ax = (cylinders -1) * (heads * sectors)
6719 ;; now we need to move the 32bit result dx:ax to what the
6720 ;; BIOS wants which is cx:dx.
6721 ;; and then into CX:DX on the stack
6722 mov _int13_harddisk.CX + 2 [bp], dx
6723 mov _int13_harddisk.DX + 2 [bp], ax
6724 pop bp
6725 ASM_END
6726 SET_AH(3); // hard disk accessible
6727 SET_DISK_RET_STATUS(0); // ??? should this be 0
6728 CLEAR_CF(); // successful
6729 return;
6730 break;
6732 case 0x18: // set media type for format
6733 case 0x41: // IBM/MS
6734 case 0x42: // IBM/MS
6735 case 0x43: // IBM/MS
6736 case 0x44: // IBM/MS
6737 case 0x45: // IBM/MS lock/unlock drive
6738 case 0x46: // IBM/MS eject media
6739 case 0x47: // IBM/MS extended seek
6740 case 0x49: // IBM/MS extended media change
6741 case 0x50: // IBM/MS send packet command
6742 default:
6743 BX_INFO("int13_harddisk: unsupported AH=%02x\n", GET_AH());
6745 SET_AH(1); // code=invalid function in AH or invalid parameter
6746 SET_DISK_RET_STATUS(1);
6747 SET_CF(); /* unsuccessful */
6748 return;
6749 break;
6753 static char panic_msg_reg12h[] = "HD%d cmos reg 12h not type F\n";
6754 static char panic_msg_reg19h[] = "HD%d cmos reg %02xh not user definable type 47\n";
6756 void
6757 get_hd_geometry(drive, hd_cylinders, hd_heads, hd_sectors)
6758 Bit8u drive;
6759 Bit16u *hd_cylinders;
6760 Bit8u *hd_heads;
6761 Bit8u *hd_sectors;
6763 Bit8u hd_type;
6764 Bit16u ss;
6765 Bit16u cylinders;
6766 Bit8u iobase;
6768 ss = get_SS();
6769 if (drive == 0x80) {
6770 hd_type = inb_cmos(0x12) & 0xf0;
6771 if (hd_type != 0xf0)
6772 BX_INFO(panic_msg_reg12h,0);
6773 hd_type = inb_cmos(0x19); // HD0: extended type
6774 if (hd_type != 47)
6775 BX_INFO(panic_msg_reg19h,0,0x19);
6776 iobase = 0x1b;
6777 } else {
6778 hd_type = inb_cmos(0x12) & 0x0f;
6779 if (hd_type != 0x0f)
6780 BX_INFO(panic_msg_reg12h,1);
6781 hd_type = inb_cmos(0x1a); // HD0: extended type
6782 if (hd_type != 47)
6783 BX_INFO(panic_msg_reg19h,0,0x1a);
6784 iobase = 0x24;
6787 // cylinders
6788 cylinders = inb_cmos(iobase) | (inb_cmos(iobase+1) << 8);
6789 write_word(ss, hd_cylinders, cylinders);
6791 // heads
6792 write_byte(ss, hd_heads, inb_cmos(iobase+2));
6794 // sectors per track
6795 write_byte(ss, hd_sectors, inb_cmos(iobase+8));
6798 #endif //else BX_USE_ATADRV
6801 //////////////////////
6802 // FLOPPY functions //
6803 //////////////////////
6805 bx_bool
6806 floppy_media_known(drive)
6807 Bit16u drive;
6809 Bit8u val8;
6810 Bit16u media_state_offset;
6812 val8 = read_byte(0x0040, 0x003e); // diskette recal status
6813 if (drive)
6814 val8 >>= 1;
6815 val8 &= 0x01;
6816 if (val8 == 0)
6817 return(0);
6819 media_state_offset = 0x0090;
6820 if (drive)
6821 media_state_offset += 1;
6823 val8 = read_byte(0x0040, media_state_offset);
6824 val8 = (val8 >> 4) & 0x01;
6825 if (val8 == 0)
6826 return(0);
6828 // check pass, return KNOWN
6829 return(1);
6832 bx_bool
6833 floppy_media_sense(drive)
6834 Bit16u drive;
6836 bx_bool retval;
6837 Bit16u media_state_offset;
6838 Bit8u drive_type, config_data, media_state;
6840 if (floppy_drive_recal(drive) == 0) {
6841 return(0);
6844 // for now cheat and get drive type from CMOS,
6845 // assume media is same as drive type
6847 // ** config_data **
6848 // Bitfields for diskette media control:
6849 // Bit(s) Description (Table M0028)
6850 // 7-6 last data rate set by controller
6851 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6852 // 5-4 last diskette drive step rate selected
6853 // 00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
6854 // 3-2 {data rate at start of operation}
6855 // 1-0 reserved
6857 // ** media_state **
6858 // Bitfields for diskette drive media state:
6859 // Bit(s) Description (Table M0030)
6860 // 7-6 data rate
6861 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6862 // 5 double stepping required (e.g. 360kB in 1.2MB)
6863 // 4 media type established
6864 // 3 drive capable of supporting 4MB media
6865 // 2-0 on exit from BIOS, contains
6866 // 000 trying 360kB in 360kB
6867 // 001 trying 360kB in 1.2MB
6868 // 010 trying 1.2MB in 1.2MB
6869 // 011 360kB in 360kB established
6870 // 100 360kB in 1.2MB established
6871 // 101 1.2MB in 1.2MB established
6872 // 110 reserved
6873 // 111 all other formats/drives
6875 drive_type = inb_cmos(0x10);
6876 if (drive == 0)
6877 drive_type >>= 4;
6878 else
6879 drive_type &= 0x0f;
6880 if ( drive_type == 1 ) {
6881 // 360K 5.25" drive
6882 config_data = 0x00; // 0000 0000
6883 media_state = 0x25; // 0010 0101
6884 retval = 1;
6886 else if ( drive_type == 2 ) {
6887 // 1.2 MB 5.25" drive
6888 config_data = 0x00; // 0000 0000
6889 media_state = 0x25; // 0010 0101 // need double stepping??? (bit 5)
6890 retval = 1;
6892 else if ( drive_type == 3 ) {
6893 // 720K 3.5" drive
6894 config_data = 0x00; // 0000 0000 ???
6895 media_state = 0x17; // 0001 0111
6896 retval = 1;
6898 else if ( drive_type == 4 ) {
6899 // 1.44 MB 3.5" drive
6900 config_data = 0x00; // 0000 0000
6901 media_state = 0x17; // 0001 0111
6902 retval = 1;
6904 else if ( drive_type == 5 ) {
6905 // 2.88 MB 3.5" drive
6906 config_data = 0xCC; // 1100 1100
6907 media_state = 0xD7; // 1101 0111
6908 retval = 1;
6910 //
6911 // Extended floppy size uses special cmos setting
6912 else if ( drive_type == 6 ) {
6913 // 160k 5.25" drive
6914 config_data = 0x00; // 0000 0000
6915 media_state = 0x27; // 0010 0111
6916 retval = 1;
6918 else if ( drive_type == 7 ) {
6919 // 180k 5.25" drive
6920 config_data = 0x00; // 0000 0000
6921 media_state = 0x27; // 0010 0111
6922 retval = 1;
6924 else if ( drive_type == 8 ) {
6925 // 320k 5.25" drive
6926 config_data = 0x00; // 0000 0000
6927 media_state = 0x27; // 0010 0111
6928 retval = 1;
6931 else {
6932 // not recognized
6933 config_data = 0x00; // 0000 0000
6934 media_state = 0x00; // 0000 0000
6935 retval = 0;
6938 if (drive == 0)
6939 media_state_offset = 0x90;
6940 else
6941 media_state_offset = 0x91;
6942 write_byte(0x0040, 0x008B, config_data);
6943 write_byte(0x0040, media_state_offset, media_state);
6945 return(retval);
6948 bx_bool
6949 floppy_drive_recal(drive)
6950 Bit16u drive;
6952 Bit8u val8, dor;
6953 Bit16u curr_cyl_offset;
6955 // set 40:3e bit 7 to 0
6956 val8 = read_byte(0x0000, 0x043e);
6957 val8 &= 0x7f;
6958 write_byte(0x0000, 0x043e, val8);
6960 // turn on motor of selected drive, DMA & int enabled, normal operation
6961 if (drive)
6962 dor = 0x20;
6963 else
6964 dor = 0x10;
6965 dor |= 0x0c;
6966 dor |= drive;
6967 outb(0x03f2, dor);
6969 // reset the disk motor timeout value of INT 08
6970 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
6972 // check port 3f4 for drive readiness
6973 val8 = inb(0x3f4);
6974 if ( (val8 & 0xf0) != 0x80 )
6975 BX_PANIC("floppy recal:f07: ctrl not ready\n");
6977 // send Recalibrate command (2 bytes) to controller
6978 outb(0x03f5, 0x07); // 07: Recalibrate
6979 outb(0x03f5, drive); // 0=drive0, 1=drive1
6981 // turn on interrupts
6982 ASM_START
6983 sti
6984 ASM_END
6986 // wait on 40:3e bit 7 to become 1
6987 val8 = (read_byte(0x0000, 0x043e) & 0x80);
6988 while ( val8 == 0 ) {
6989 val8 = (read_byte(0x0000, 0x043e) & 0x80);
6992 val8 = 0; // separate asm from while() loop
6993 // turn off interrupts
6994 ASM_START
6995 cli
6996 ASM_END
6998 // set 40:3e bit 7 to 0, and calibrated bit
6999 val8 = read_byte(0x0000, 0x043e);
7000 val8 &= 0x7f;
7001 if (drive) {
7002 val8 |= 0x02; // Drive 1 calibrated
7003 curr_cyl_offset = 0x0095;
7005 else {
7006 val8 |= 0x01; // Drive 0 calibrated
7007 curr_cyl_offset = 0x0094;
7009 write_byte(0x0040, 0x003e, val8);
7010 write_byte(0x0040, curr_cyl_offset, 0); // current cylinder is 0
7012 return(1);
7017 bx_bool
7018 floppy_drive_exists(drive)
7019 Bit16u drive;
7021 Bit8u drive_type;
7023 // check CMOS to see if drive exists
7024 drive_type = inb_cmos(0x10);
7025 if (drive == 0)
7026 drive_type >>= 4;
7027 else
7028 drive_type &= 0x0f;
7029 if ( drive_type == 0 )
7030 return(0);
7031 else
7032 return(1);
7035 #if BX_SUPPORT_FLOPPY
7036 void
7037 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
7038 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
7040 Bit8u drive, num_sectors, track, sector, head, status;
7041 Bit16u base_address, base_count, base_es;
7042 Bit8u page, mode_register, val8, dor;
7043 Bit8u return_status[7];
7044 Bit8u drive_type, num_floppies, ah;
7045 Bit16u es, last_addr;
7047 BX_DEBUG_INT13_FL("int13_diskette: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
7048 // BX_DEBUG_INT13_FL("int13_diskette: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), get_DS(), ES, DI, SI);
7050 ah = GET_AH();
7052 switch ( ah ) {
7053 case 0x00: // diskette controller reset
7054 BX_DEBUG_INT13_FL("floppy f00\n");
7055 drive = GET_ELDL();
7056 if (drive > 1) {
7057 SET_AH(1); // invalid param
7058 set_diskette_ret_status(1);
7059 SET_CF();
7060 return;
7062 drive_type = inb_cmos(0x10);
7064 if (drive == 0)
7065 drive_type >>= 4;
7066 else
7067 drive_type &= 0x0f;
7068 if (drive_type == 0) {
7069 SET_AH(0x80); // drive not responding
7070 set_diskette_ret_status(0x80);
7071 SET_CF();
7072 return;
7074 SET_AH(0);
7075 set_diskette_ret_status(0);
7076 CLEAR_CF(); // successful
7077 set_diskette_current_cyl(drive, 0); // current cylinder
7078 return;
7080 case 0x01: // Read Diskette Status
7081 CLEAR_CF();
7082 val8 = read_byte(0x0000, 0x0441);
7083 SET_AH(val8);
7084 if (val8) {
7085 SET_CF();
7087 return;
7089 case 0x02: // Read Diskette Sectors
7090 case 0x03: // Write Diskette Sectors
7091 case 0x04: // Verify Diskette Sectors
7092 num_sectors = GET_AL();
7093 track = GET_CH();
7094 sector = GET_CL();
7095 head = GET_DH();
7096 drive = GET_ELDL();
7098 if ( (drive > 1) || (head > 1) ||
7099 (num_sectors == 0) || (num_sectors > 72) ) {
7100 BX_INFO("floppy: drive>1 || head>1 ...\n");
7101 SET_AH(1);
7102 set_diskette_ret_status(1);
7103 SET_AL(0); // no sectors read
7104 SET_CF(); // error occurred
7105 return;
7108 // see if drive exists
7109 if (floppy_drive_exists(drive) == 0) {
7110 SET_AH(0x80); // not responding
7111 set_diskette_ret_status(0x80);
7112 SET_AL(0); // no sectors read
7113 SET_CF(); // error occurred
7114 return;
7117 // see if media in drive, and type is known
7118 if (floppy_media_known(drive) == 0) {
7119 if (floppy_media_sense(drive) == 0) {
7120 SET_AH(0x0C); // Media type not found
7121 set_diskette_ret_status(0x0C);
7122 SET_AL(0); // no sectors read
7123 SET_CF(); // error occurred
7124 return;
7128 if (ah == 0x02) {
7129 // Read Diskette Sectors
7131 //-----------------------------------
7132 // set up DMA controller for transfer
7133 //-----------------------------------
7135 // es:bx = pointer to where to place information from diskette
7136 // port 04: DMA-1 base and current address, channel 2
7137 // port 05: DMA-1 base and current count, channel 2
7138 page = (ES >> 12); // upper 4 bits
7139 base_es = (ES << 4); // lower 16bits contributed by ES
7140 base_address = base_es + BX; // lower 16 bits of address
7141 // contributed by ES:BX
7142 if ( base_address < base_es ) {
7143 // in case of carry, adjust page by 1
7144 page++;
7146 base_count = (num_sectors * 512) - 1;
7148 // check for 64K boundary overrun
7149 last_addr = base_address + base_count;
7150 if (last_addr < base_address) {
7151 SET_AH(0x09);
7152 set_diskette_ret_status(0x09);
7153 SET_AL(0); // no sectors read
7154 SET_CF(); // error occurred
7155 return;
7158 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
7159 outb(0x000a, 0x06);
7161 BX_DEBUG_INT13_FL("clear flip-flop\n");
7162 outb(0x000c, 0x00); // clear flip-flop
7163 outb(0x0004, base_address);
7164 outb(0x0004, base_address>>8);
7165 BX_DEBUG_INT13_FL("clear flip-flop\n");
7166 outb(0x000c, 0x00); // clear flip-flop
7167 outb(0x0005, base_count);
7168 outb(0x0005, base_count>>8);
7170 // port 0b: DMA-1 Mode Register
7171 mode_register = 0x46; // single mode, increment, autoinit disable,
7172 // transfer type=write, channel 2
7173 BX_DEBUG_INT13_FL("setting mode register\n");
7174 outb(0x000b, mode_register);
7176 BX_DEBUG_INT13_FL("setting page register\n");
7177 // port 81: DMA-1 Page Register, channel 2
7178 outb(0x0081, page);
7180 BX_DEBUG_INT13_FL("unmask chan 2\n");
7181 outb(0x000a, 0x02); // unmask channel 2
7183 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
7184 outb(0x000a, 0x02);
7186 //--------------------------------------
7187 // set up floppy controller for transfer
7188 //--------------------------------------
7190 // set 40:3e bit 7 to 0
7191 val8 = read_byte(0x0000, 0x043e);
7192 val8 &= 0x7f;
7193 write_byte(0x0000, 0x043e, val8);
7195 // turn on motor of selected drive, DMA & int enabled, normal operation
7196 if (drive)
7197 dor = 0x20;
7198 else
7199 dor = 0x10;
7200 dor |= 0x0c;
7201 dor |= drive;
7202 outb(0x03f2, dor);
7204 // reset the disk motor timeout value of INT 08
7205 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
7207 // check port 3f4 for drive readiness
7208 val8 = inb(0x3f4);
7209 if ( (val8 & 0xf0) != 0x80 )
7210 BX_PANIC("int13_diskette:f02: ctrl not ready\n");
7212 // send read-normal-data command (9 bytes) to controller
7213 outb(0x03f5, 0xe6); // e6: read normal data
7214 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7215 outb(0x03f5, track);
7216 outb(0x03f5, head);
7217 outb(0x03f5, sector);
7218 outb(0x03f5, 2); // 512 byte sector size
7219 outb(0x03f5, sector + num_sectors - 1); // last sector to read on track
7220 outb(0x03f5, 0); // Gap length
7221 outb(0x03f5, 0xff); // Gap length
7223 // turn on interrupts
7224 ASM_START
7225 sti
7226 ASM_END
7228 // wait on 40:3e bit 7 to become 1
7229 val8 = (read_byte(0x0000, 0x043e) & 0x80);
7230 while ( val8 == 0 ) {
7231 val8 = (read_byte(0x0000, 0x043e) & 0x80);
7234 val8 = 0; // separate asm from while() loop
7235 // turn off interrupts
7236 ASM_START
7237 cli
7238 ASM_END
7240 // set 40:3e bit 7 to 0
7241 val8 = read_byte(0x0000, 0x043e);
7242 val8 &= 0x7f;
7243 write_byte(0x0000, 0x043e, val8);
7245 // check port 3f4 for accessibility to status bytes
7246 val8 = inb(0x3f4);
7247 if ( (val8 & 0xc0) != 0xc0 )
7248 BX_PANIC("int13_diskette: ctrl not ready\n");
7250 // read 7 return status bytes from controller
7251 // using loop index broken, have to unroll...
7252 return_status[0] = inb(0x3f5);
7253 return_status[1] = inb(0x3f5);
7254 return_status[2] = inb(0x3f5);
7255 return_status[3] = inb(0x3f5);
7256 return_status[4] = inb(0x3f5);
7257 return_status[5] = inb(0x3f5);
7258 return_status[6] = inb(0x3f5);
7259 // record in BIOS Data Area
7260 write_byte(0x0040, 0x0042, return_status[0]);
7261 write_byte(0x0040, 0x0043, return_status[1]);
7262 write_byte(0x0040, 0x0044, return_status[2]);
7263 write_byte(0x0040, 0x0045, return_status[3]);
7264 write_byte(0x0040, 0x0046, return_status[4]);
7265 write_byte(0x0040, 0x0047, return_status[5]);
7266 write_byte(0x0040, 0x0048, return_status[6]);
7268 if ( (return_status[0] & 0xc0) != 0 ) {
7269 SET_AH(0x20);
7270 set_diskette_ret_status(0x20);
7271 SET_AL(0); // no sectors read
7272 SET_CF(); // error occurred
7273 return;
7276 // ??? should track be new val from return_status[3] ?
7277 set_diskette_current_cyl(drive, track);
7278 // AL = number of sectors read (same value as passed)
7279 SET_AH(0x00); // success
7280 CLEAR_CF(); // success
7281 return;
7283 else if (ah == 0x03) {
7284 // Write Diskette Sectors
7286 //-----------------------------------
7287 // set up DMA controller for transfer
7288 //-----------------------------------
7290 // es:bx = pointer to where to place information from diskette
7291 // port 04: DMA-1 base and current address, channel 2
7292 // port 05: DMA-1 base and current count, channel 2
7293 page = (ES >> 12); // upper 4 bits
7294 base_es = (ES << 4); // lower 16bits contributed by ES
7295 base_address = base_es + BX; // lower 16 bits of address
7296 // contributed by ES:BX
7297 if ( base_address < base_es ) {
7298 // in case of carry, adjust page by 1
7299 page++;
7301 base_count = (num_sectors * 512) - 1;
7303 // check for 64K boundary overrun
7304 last_addr = base_address + base_count;
7305 if (last_addr < base_address) {
7306 SET_AH(0x09);
7307 set_diskette_ret_status(0x09);
7308 SET_AL(0); // no sectors read
7309 SET_CF(); // error occurred
7310 return;
7313 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
7314 outb(0x000a, 0x06);
7316 outb(0x000c, 0x00); // clear flip-flop
7317 outb(0x0004, base_address);
7318 outb(0x0004, base_address>>8);
7319 outb(0x000c, 0x00); // clear flip-flop
7320 outb(0x0005, base_count);
7321 outb(0x0005, base_count>>8);
7323 // port 0b: DMA-1 Mode Register
7324 mode_register = 0x4a; // single mode, increment, autoinit disable,
7325 // transfer type=read, channel 2
7326 outb(0x000b, mode_register);
7328 // port 81: DMA-1 Page Register, channel 2
7329 outb(0x0081, page);
7331 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
7332 outb(0x000a, 0x02);
7334 //--------------------------------------
7335 // set up floppy controller for transfer
7336 //--------------------------------------
7338 // set 40:3e bit 7 to 0
7339 val8 = read_byte(0x0000, 0x043e);
7340 val8 &= 0x7f;
7341 write_byte(0x0000, 0x043e, val8);
7343 // turn on motor of selected drive, DMA & int enabled, normal operation
7344 if (drive)
7345 dor = 0x20;
7346 else
7347 dor = 0x10;
7348 dor |= 0x0c;
7349 dor |= drive;
7350 outb(0x03f2, dor);
7352 // reset the disk motor timeout value of INT 08
7353 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
7355 // check port 3f4 for drive readiness
7356 val8 = inb(0x3f4);
7357 if ( (val8 & 0xf0) != 0x80 )
7358 BX_PANIC("int13_diskette:f03: ctrl not ready\n");
7360 // send read-normal-data command (9 bytes) to controller
7361 outb(0x03f5, 0xc5); // c5: write normal data
7362 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7363 outb(0x03f5, track);
7364 outb(0x03f5, head);
7365 outb(0x03f5, sector);
7366 outb(0x03f5, 2); // 512 byte sector size
7367 outb(0x03f5, sector + num_sectors - 1); // last sector to write on track
7368 outb(0x03f5, 0); // Gap length
7369 outb(0x03f5, 0xff); // Gap length
7371 // turn on interrupts
7372 ASM_START
7373 sti
7374 ASM_END
7376 // wait on 40:3e bit 7 to become 1
7377 val8 = (read_byte(0x0000, 0x043e) & 0x80);
7378 while ( val8 == 0 ) {
7379 val8 = (read_byte(0x0000, 0x043e) & 0x80);
7382 val8 = 0; // separate asm from while() loop
7383 // turn off interrupts
7384 ASM_START
7385 cli
7386 ASM_END
7388 // set 40:3e bit 7 to 0
7389 val8 = read_byte(0x0000, 0x043e);
7390 val8 &= 0x7f;
7391 write_byte(0x0000, 0x043e, val8);
7393 // check port 3f4 for accessibility to status bytes
7394 val8 = inb(0x3f4);
7395 if ( (val8 & 0xc0) != 0xc0 )
7396 BX_PANIC("int13_diskette: ctrl not ready\n");
7398 // read 7 return status bytes from controller
7399 // using loop index broken, have to unroll...
7400 return_status[0] = inb(0x3f5);
7401 return_status[1] = inb(0x3f5);
7402 return_status[2] = inb(0x3f5);
7403 return_status[3] = inb(0x3f5);
7404 return_status[4] = inb(0x3f5);
7405 return_status[5] = inb(0x3f5);
7406 return_status[6] = inb(0x3f5);
7407 // record in BIOS Data Area
7408 write_byte(0x0040, 0x0042, return_status[0]);
7409 write_byte(0x0040, 0x0043, return_status[1]);
7410 write_byte(0x0040, 0x0044, return_status[2]);
7411 write_byte(0x0040, 0x0045, return_status[3]);
7412 write_byte(0x0040, 0x0046, return_status[4]);
7413 write_byte(0x0040, 0x0047, return_status[5]);
7414 write_byte(0x0040, 0x0048, return_status[6]);
7416 if ( (return_status[0] & 0xc0) != 0 ) {
7417 if ( (return_status[1] & 0x02) != 0 ) {
7418 // diskette not writable.
7419 // AH=status code=0x03 (tried to write on write-protected disk)
7420 // AL=number of sectors written=0
7421 AX = 0x0300;
7422 SET_CF();
7423 return;
7424 } else {
7425 BX_PANIC("int13_diskette_function: read error\n");
7429 // ??? should track be new val from return_status[3] ?
7430 set_diskette_current_cyl(drive, track);
7431 // AL = number of sectors read (same value as passed)
7432 SET_AH(0x00); // success
7433 CLEAR_CF(); // success
7434 return;
7436 else { // if (ah == 0x04)
7437 // Verify Diskette Sectors
7439 // ??? should track be new val from return_status[3] ?
7440 set_diskette_current_cyl(drive, track);
7441 // AL = number of sectors verified (same value as passed)
7442 CLEAR_CF(); // success
7443 SET_AH(0x00); // success
7444 return;
7448 case 0x05: // format diskette track
7449 BX_DEBUG_INT13_FL("floppy f05\n");
7451 num_sectors = GET_AL();
7452 track = GET_CH();
7453 head = GET_DH();
7454 drive = GET_ELDL();
7456 if ((drive > 1) || (head > 1) || (track > 79) ||
7457 (num_sectors == 0) || (num_sectors > 18)) {
7458 SET_AH(1);
7459 set_diskette_ret_status(1);
7460 SET_CF(); // error occurred
7463 // see if drive exists
7464 if (floppy_drive_exists(drive) == 0) {
7465 SET_AH(0x80); // drive not responding
7466 set_diskette_ret_status(0x80);
7467 SET_CF(); // error occurred
7468 return;
7471 // see if media in drive, and type is known
7472 if (floppy_media_known(drive) == 0) {
7473 if (floppy_media_sense(drive) == 0) {
7474 SET_AH(0x0C); // Media type not found
7475 set_diskette_ret_status(0x0C);
7476 SET_AL(0); // no sectors read
7477 SET_CF(); // error occurred
7478 return;
7482 // set up DMA controller for transfer
7483 page = (ES >> 12); // upper 4 bits
7484 base_es = (ES << 4); // lower 16bits contributed by ES
7485 base_address = base_es + BX; // lower 16 bits of address
7486 // contributed by ES:BX
7487 if ( base_address < base_es ) {
7488 // in case of carry, adjust page by 1
7489 page++;
7491 base_count = (num_sectors * 4) - 1;
7493 // check for 64K boundary overrun
7494 last_addr = base_address + base_count;
7495 if (last_addr < base_address) {
7496 SET_AH(0x09);
7497 set_diskette_ret_status(0x09);
7498 SET_AL(0); // no sectors read
7499 SET_CF(); // error occurred
7500 return;
7503 outb(0x000a, 0x06);
7504 outb(0x000c, 0x00); // clear flip-flop
7505 outb(0x0004, base_address);
7506 outb(0x0004, base_address>>8);
7507 outb(0x000c, 0x00); // clear flip-flop
7508 outb(0x0005, base_count);
7509 outb(0x0005, base_count>>8);
7510 mode_register = 0x4a; // single mode, increment, autoinit disable,
7511 // transfer type=read, channel 2
7512 outb(0x000b, mode_register);
7513 // port 81: DMA-1 Page Register, channel 2
7514 outb(0x0081, page);
7515 outb(0x000a, 0x02);
7517 // set up floppy controller for transfer
7518 val8 = read_byte(0x0000, 0x043e);
7519 val8 &= 0x7f;
7520 write_byte(0x0000, 0x043e, val8);
7521 // turn on motor of selected drive, DMA & int enabled, normal operation
7522 if (drive)
7523 dor = 0x20;
7524 else
7525 dor = 0x10;
7526 dor |= 0x0c;
7527 dor |= drive;
7528 outb(0x03f2, dor);
7530 // reset the disk motor timeout value of INT 08
7531 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
7533 // check port 3f4 for drive readiness
7534 val8 = inb(0x3f4);
7535 if ( (val8 & 0xf0) != 0x80 )
7536 BX_PANIC("int13_diskette:f05: ctrl not ready\n");
7538 // send read-normal-data command (6 bytes) to controller
7539 outb(0x03f5, 0x4d); // 4d: format track
7540 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7541 outb(0x03f5, 2); // 512 byte sector size
7542 outb(0x03f5, num_sectors); // number of sectors per track
7543 outb(0x03f5, 0); // Gap length
7544 outb(0x03f5, 0xf6); // Fill byte
7545 // turn on interrupts
7546 ASM_START
7547 sti
7548 ASM_END
7549 // wait on 40:3e bit 7 to become 1
7550 val8 = (read_byte(0x0000, 0x043e) & 0x80);
7551 while ( val8 == 0 ) {
7552 val8 = (read_byte(0x0000, 0x043e) & 0x80);
7554 val8 = 0; // separate asm from while() loop
7555 // turn off interrupts
7556 ASM_START
7557 cli
7558 ASM_END
7559 // set 40:3e bit 7 to 0
7560 val8 = read_byte(0x0000, 0x043e);
7561 val8 &= 0x7f;
7562 write_byte(0x0000, 0x043e, val8);
7563 // check port 3f4 for accessibility to status bytes
7564 val8 = inb(0x3f4);
7565 if ( (val8 & 0xc0) != 0xc0 )
7566 BX_PANIC("int13_diskette: ctrl not ready\n");
7568 // read 7 return status bytes from controller
7569 // using loop index broken, have to unroll...
7570 return_status[0] = inb(0x3f5);
7571 return_status[1] = inb(0x3f5);
7572 return_status[2] = inb(0x3f5);
7573 return_status[3] = inb(0x3f5);
7574 return_status[4] = inb(0x3f5);
7575 return_status[5] = inb(0x3f5);
7576 return_status[6] = inb(0x3f5);
7577 // record in BIOS Data Area
7578 write_byte(0x0040, 0x0042, return_status[0]);
7579 write_byte(0x0040, 0x0043, return_status[1]);
7580 write_byte(0x0040, 0x0044, return_status[2]);
7581 write_byte(0x0040, 0x0045, return_status[3]);
7582 write_byte(0x0040, 0x0046, return_status[4]);
7583 write_byte(0x0040, 0x0047, return_status[5]);
7584 write_byte(0x0040, 0x0048, return_status[6]);
7586 if ( (return_status[0] & 0xc0) != 0 ) {
7587 if ( (return_status[1] & 0x02) != 0 ) {
7588 // diskette not writable.
7589 // AH=status code=0x03 (tried to write on write-protected disk)
7590 // AL=number of sectors written=0
7591 AX = 0x0300;
7592 SET_CF();
7593 return;
7594 } else {
7595 BX_PANIC("int13_diskette_function: write error\n");
7599 SET_AH(0);
7600 set_diskette_ret_status(0);
7601 set_diskette_current_cyl(drive, 0);
7602 CLEAR_CF(); // successful
7603 return;
7606 case 0x08: // read diskette drive parameters
7607 BX_DEBUG_INT13_FL("floppy f08\n");
7608 drive = GET_ELDL();
7610 if (drive > 1) {
7611 AX = 0;
7612 BX = 0;
7613 CX = 0;
7614 DX = 0;
7615 ES = 0;
7616 DI = 0;
7617 SET_DL(num_floppies);
7618 SET_CF();
7619 return;
7622 drive_type = inb_cmos(0x10);
7623 num_floppies = 0;
7624 if (drive_type & 0xf0)
7625 num_floppies++;
7626 if (drive_type & 0x0f)
7627 num_floppies++;
7629 if (drive == 0)
7630 drive_type >>= 4;
7631 else
7632 drive_type &= 0x0f;
7634 SET_BH(0);
7635 SET_BL(drive_type);
7636 SET_AH(0);
7637 SET_AL(0);
7638 SET_DL(num_floppies);
7640 switch (drive_type) {
7641 case 0: // none
7642 CX = 0;
7643 SET_DH(0); // max head #
7644 break;
7646 case 1: // 360KB, 5.25"
7647 CX = 0x2709; // 40 tracks, 9 sectors
7648 SET_DH(1); // max head #
7649 break;
7651 case 2: // 1.2MB, 5.25"
7652 CX = 0x4f0f; // 80 tracks, 15 sectors
7653 SET_DH(1); // max head #
7654 break;
7656 case 3: // 720KB, 3.5"
7657 CX = 0x4f09; // 80 tracks, 9 sectors
7658 SET_DH(1); // max head #
7659 break;
7661 case 4: // 1.44MB, 3.5"
7662 CX = 0x4f12; // 80 tracks, 18 sectors
7663 SET_DH(1); // max head #
7664 break;
7666 case 5: // 2.88MB, 3.5"
7667 CX = 0x4f24; // 80 tracks, 36 sectors
7668 SET_DH(1); // max head #
7669 break;
7671 case 6: // 160k, 5.25"
7672 CX = 0x2708; // 40 tracks, 8 sectors
7673 SET_DH(0); // max head #
7674 break;
7676 case 7: // 180k, 5.25"
7677 CX = 0x2709; // 40 tracks, 9 sectors
7678 SET_DH(0); // max head #
7679 break;
7681 case 8: // 320k, 5.25"
7682 CX = 0x2708; // 40 tracks, 8 sectors
7683 SET_DH(1); // max head #
7684 break;
7686 default: // ?
7687 BX_PANIC("floppy: int13: bad floppy type\n");
7690 /* set es & di to point to 11 byte diskette param table in ROM */
7691 ASM_START
7692 push bp
7693 mov bp, sp
7694 mov ax, #diskette_param_table2
7695 mov _int13_diskette_function.DI+2[bp], ax
7696 mov _int13_diskette_function.ES+2[bp], cs
7697 pop bp
7698 ASM_END
7699 CLEAR_CF(); // success
7700 /* disk status not changed upon success */
7701 return;
7704 case 0x15: // read diskette drive type
7705 BX_DEBUG_INT13_FL("floppy f15\n");
7706 drive = GET_ELDL();
7707 if (drive > 1) {
7708 SET_AH(0); // only 2 drives supported
7709 // set_diskette_ret_status here ???
7710 SET_CF();
7711 return;
7713 drive_type = inb_cmos(0x10);
7715 if (drive == 0)
7716 drive_type >>= 4;
7717 else
7718 drive_type &= 0x0f;
7719 CLEAR_CF(); // successful, not present
7720 if (drive_type==0) {
7721 SET_AH(0); // drive not present
7723 else {
7724 SET_AH(1); // drive present, does not support change line
7727 return;
7729 case 0x16: // get diskette change line status
7730 BX_DEBUG_INT13_FL("floppy f16\n");
7731 drive = GET_ELDL();
7732 if (drive > 1) {
7733 SET_AH(0x01); // invalid drive
7734 set_diskette_ret_status(0x01);
7735 SET_CF();
7736 return;
7739 SET_AH(0x06); // change line not supported
7740 set_diskette_ret_status(0x06);
7741 SET_CF();
7742 return;
7744 case 0x17: // set diskette type for format(old)
7745 BX_DEBUG_INT13_FL("floppy f17\n");
7746 /* not used for 1.44M floppies */
7747 SET_AH(0x01); // not supported
7748 set_diskette_ret_status(1); /* not supported */
7749 SET_CF();
7750 return;
7752 case 0x18: // set diskette type for format(new)
7753 BX_DEBUG_INT13_FL("floppy f18\n");
7754 SET_AH(0x01); // do later
7755 set_diskette_ret_status(1);
7756 SET_CF();
7757 return;
7759 default:
7760 BX_INFO("int13_diskette: unsupported AH=%02x\n", GET_AH());
7762 // if ( (ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e) ) {
7763 SET_AH(0x01); // ???
7764 set_diskette_ret_status(1);
7765 SET_CF();
7766 return;
7767 // }
7770 #else // #if BX_SUPPORT_FLOPPY
7771 void
7772 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
7773 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
7775 Bit8u val8;
7777 switch ( GET_AH() ) {
7779 case 0x01: // Read Diskette Status
7780 CLEAR_CF();
7781 val8 = read_byte(0x0000, 0x0441);
7782 SET_AH(val8);
7783 if (val8) {
7784 SET_CF();
7786 return;
7788 default:
7789 SET_CF();
7790 write_byte(0x0000, 0x0441, 0x01);
7791 SET_AH(0x01);
7794 #endif // #if BX_SUPPORT_FLOPPY
7796 void
7797 set_diskette_ret_status(value)
7798 Bit8u value;
7800 write_byte(0x0040, 0x0041, value);
7803 void
7804 set_diskette_current_cyl(drive, cyl)
7805 Bit8u drive;
7806 Bit8u cyl;
7808 if (drive > 1)
7809 BX_PANIC("set_diskette_current_cyl(): drive > 1\n");
7810 write_byte(0x0040, 0x0094+drive, cyl);
7813 void
7814 determine_floppy_media(drive)
7815 Bit16u drive;
7817 #if 0
7818 Bit8u val8, DOR, ctrl_info;
7820 ctrl_info = read_byte(0x0040, 0x008F);
7821 if (drive==1)
7822 ctrl_info >>= 4;
7823 else
7824 ctrl_info &= 0x0f;
7826 #if 0
7827 if (drive == 0) {
7828 DOR = 0x1c; // DOR: drive0 motor on, DMA&int enabled, normal op, drive select 0
7830 else {
7831 DOR = 0x2d; // DOR: drive1 motor on, DMA&int enabled, normal op, drive select 1
7833 #endif
7835 if ( (ctrl_info & 0x04) != 0x04 ) {
7836 // Drive not determined means no drive exists, done.
7837 return;
7840 #if 0
7841 // check Main Status Register for readiness
7842 val8 = inb(0x03f4) & 0x80; // Main Status Register
7843 if (val8 != 0x80)
7844 BX_PANIC("d_f_m: MRQ bit not set\n");
7846 // change line
7848 // existing BDA values
7850 // turn on drive motor
7851 outb(0x03f2, DOR); // Digital Output Register
7852 //
7853 #endif
7854 BX_PANIC("d_f_m: OK so far\n");
7855 #endif
7858 void
7859 int17_function(regs, ds, iret_addr)
7860 pusha_regs_t regs; // regs pushed from PUSHA instruction
7861 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7862 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7864 Bit16u addr,timeout;
7865 Bit8u val8;
7867 ASM_START
7868 sti
7869 ASM_END
7871 addr = read_word(0x0040, (regs.u.r16.dx << 1) + 8);
7872 if ((regs.u.r8.ah < 3) && (regs.u.r16.dx < 3) && (addr > 0)) {
7873 timeout = read_byte(0x0040, 0x0078 + regs.u.r16.dx) << 8;
7874 if (regs.u.r8.ah == 0) {
7875 outb(addr, regs.u.r8.al);
7876 val8 = inb(addr+2);
7877 outb(addr+2, val8 | 0x01); // send strobe
7878 ASM_START
7879 nop
7880 ASM_END
7881 outb(addr+2, val8 & ~0x01);
7882 while (((inb(addr+1) & 0x40) == 0x40) && (timeout)) {
7883 timeout--;
7886 if (regs.u.r8.ah == 1) {
7887 val8 = inb(addr+2);
7888 outb(addr+2, val8 & ~0x04); // send init
7889 ASM_START
7890 nop
7891 ASM_END
7892 outb(addr+2, val8 | 0x04);
7894 val8 = inb(addr+1);
7895 regs.u.r8.ah = (val8 ^ 0x48);
7896 if (!timeout) regs.u.r8.ah |= 0x01;
7897 ClearCF(iret_addr.flags);
7898 } else {
7899 SetCF(iret_addr.flags); // Unsupported
7903 void
7904 int18_function(seq_nr)
7905 Bit16u seq_nr;
7907 Bit16u ebda_seg=read_word(0x0040,0x000E);
7908 Bit16u bootdev;
7909 Bit8u bootdrv;
7910 Bit8u bootchk;
7911 Bit16u bootseg;
7912 Bit16u bootip;
7913 Bit16u status;
7915 struct ipl_entry e;
7917 // if BX_ELTORITO_BOOT is not defined, old behavior
7918 // check bit 5 in CMOS reg 0x2d. load either 0x00 or 0x80 into DL
7919 // in preparation for the intial INT 13h (0=floppy A:, 0x80=C:)
7920 // 0: system boot sequence, first drive C: then A:
7921 // 1: system boot sequence, first drive A: then C:
7922 // else BX_ELTORITO_BOOT is defined
7923 // CMOS regs 0x3D and 0x38 contain the boot sequence:
7924 // CMOS reg 0x3D & 0x0f : 1st boot device
7925 // CMOS reg 0x3D & 0xf0 : 2nd boot device
7926 // CMOS reg 0x38 & 0xf0 : 3rd boot device
7927 // boot device codes:
7928 // 0x00 : not defined
7929 // 0x01 : first floppy
7930 // 0x02 : first harddrive
7931 // 0x03 : first cdrom
7932 // 0x04 - 0x0f : PnP expansion ROMs (e.g. Etherboot)
7933 // else : boot failure
7935 // Get the boot sequence
7936 #if BX_ELTORITO_BOOT
7937 bootdev = inb_cmos(0x3d);
7938 bootdev |= ((inb_cmos(0x38) & 0xf0) << 4);
7939 bootdev >>= 4 * seq_nr;
7940 bootdev &= 0xf;
7941 if (bootdev == 0) BX_PANIC("No bootable device.\n");
7943 /* Translate from CMOS runes to an IPL table offset by subtracting 1 */
7944 bootdev -= 1;
7945 #else
7946 if (seq_nr ==2) BX_PANIC("No more boot devices.");
7947 if (!!(inb_cmos(0x2d) & 0x20) ^ (seq_nr == 1))
7948 /* Boot from floppy if the bit is set or it's the second boot */
7949 bootdev = 0x00;
7950 else
7951 bootdev = 0x01;
7952 #endif
7954 /* Read the boot device from the IPL table */
7955 if (get_boot_vector(bootdev, &e) == 0) {
7956 BX_INFO("Invalid boot device (0x%x)\n", bootdev);
7957 return;
7960 /* Do the loading, and set up vector as a far pointer to the boot
7961 * address, and bootdrv as the boot drive */
7962 print_boot_device(e.type);
7964 switch(e.type) {
7965 case 0x01: /* FDD */
7966 case 0x02: /* HDD */
7968 bootdrv = (e.type == 0x02) ? 0x80 : 0x00;
7969 bootseg = 0x07c0;
7970 status = 0;
7972 ASM_START
7973 push bp
7974 mov bp, sp
7975 push ax
7976 push bx
7977 push cx
7978 push dx
7980 mov dl, _int18_function.bootdrv + 2[bp]
7981 mov ax, _int18_function.bootseg + 2[bp]
7982 mov es, ax ;; segment
7983 mov bx, #0x0000 ;; offset
7984 mov ah, #0x02 ;; function 2, read diskette sector
7985 mov al, #0x01 ;; read 1 sector
7986 mov ch, #0x00 ;; track 0
7987 mov cl, #0x01 ;; sector 1
7988 mov dh, #0x00 ;; head 0
7989 int #0x13 ;; read sector
7990 jnc int19_load_done
7991 mov ax, #0x0001
7992 mov _int18_function.status + 2[bp], ax
7994 int19_load_done:
7995 pop dx
7996 pop cx
7997 pop bx
7998 pop ax
7999 pop bp
8000 ASM_END
8002 if (status != 0) {
8003 print_boot_failure(e.type, 1);
8004 return;
8007 /* Always check the signature on a HDD boot sector; on FDD, only do
8008 * the check if the CMOS doesn't tell us to skip it */
8009 if (e.type != 0x00 || !((inb_cmos(0x38) & 0x01))) {
8010 if (read_word(bootseg,0x1fe) != 0xaa55) {
8011 print_boot_failure(e.type, 0);
8012 return;
8016 #if BX_TCGBIOS
8017 tcpa_add_bootdevice((Bit32u)0L, (Bit32u)bootdrv);
8018 tcpa_ipl((Bit32u)0L,(Bit32u)bootseg,(Bit32u)0L,(Bit32u)512L); /* specs: 8.2.3 steps 4 and 5 */
8019 #endif
8021 /* Canonicalize bootseg:bootip */
8022 bootip = (bootseg & 0x0fff) << 4;
8023 bootseg &= 0xf000;
8024 break;
8026 #if BX_ELTORITO_BOOT
8027 case 0x03: /* CD-ROM */
8028 status = cdrom_boot();
8030 // If failure
8031 if ( (status & 0x00ff) !=0 ) {
8032 print_cdromboot_failure(status);
8033 print_boot_failure(e.type, 1);
8034 return;
8037 bootdrv = (Bit8u)(status>>8);
8038 bootseg = read_word(ebda_seg,&EbdaData->cdemu.load_segment);
8040 /* Canonicalize bootseg:bootip */
8041 bootip = (bootseg & 0x0fff) << 4;
8042 bootseg &= 0xf000;
8043 break;
8044 #endif
8046 case 0x80: /* Expansion ROM with a Bootstrap Entry Vector (a far pointer) */
8047 bootseg = e.vector >> 16;
8048 bootip = e.vector & 0xffff;
8049 break;
8051 default: return;
8055 /* Jump to the boot vector */
8056 ASM_START
8057 mov bp, sp
8058 ;; Build an iret stack frame that will take us to the boot vector.
8059 ;; iret pops ip, then cs, then flags, so push them in the opposite order.
8060 pushf
8061 mov ax, _int18_function.bootseg + 0[bp]
8062 push ax
8063 mov ax, _int18_function.bootip + 0[bp]
8064 push ax
8065 ;; Set the magic number in ax and the boot drive in dl.
8066 mov ax, #0xaa55
8067 mov dl, _int18_function.bootdrv + 0[bp]
8068 ;; Zero some of the other registers.
8069 xor bx, bx
8070 mov ds, bx
8071 mov es, bx
8072 mov bp, bx
8073 ;; Go!
8074 iret
8075 ASM_END
8078 void
8079 int1a_function(regs, ds, iret_addr)
8080 pusha_regs_t regs; // regs pushed from PUSHA instruction
8081 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
8082 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
8084 Bit8u val8;
8086 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);
8088 ASM_START
8089 sti
8090 ASM_END
8092 switch (regs.u.r8.ah) {
8093 case 0: // get current clock count
8094 ASM_START
8095 cli
8096 ASM_END
8097 regs.u.r16.cx = BiosData->ticks_high;
8098 regs.u.r16.dx = BiosData->ticks_low;
8099 regs.u.r8.al = BiosData->midnight_flag;
8100 BiosData->midnight_flag = 0; // reset flag
8101 ASM_START
8102 sti
8103 ASM_END
8104 // AH already 0
8105 ClearCF(iret_addr.flags); // OK
8106 break;
8108 case 1: // Set Current Clock Count
8109 ASM_START
8110 cli
8111 ASM_END
8112 BiosData->ticks_high = regs.u.r16.cx;
8113 BiosData->ticks_low = regs.u.r16.dx;
8114 BiosData->midnight_flag = 0; // reset flag
8115 ASM_START
8116 sti
8117 ASM_END
8118 regs.u.r8.ah = 0;
8119 ClearCF(iret_addr.flags); // OK
8120 break;
8123 case 2: // Read CMOS Time
8124 if (rtc_updating()) {
8125 SetCF(iret_addr.flags);
8126 break;
8129 regs.u.r8.dh = inb_cmos(0x00); // Seconds
8130 regs.u.r8.cl = inb_cmos(0x02); // Minutes
8131 regs.u.r8.ch = inb_cmos(0x04); // Hours
8132 regs.u.r8.dl = inb_cmos(0x0b) & 0x01; // Stat Reg B
8133 regs.u.r8.ah = 0;
8134 regs.u.r8.al = regs.u.r8.ch;
8135 ClearCF(iret_addr.flags); // OK
8136 break;
8138 case 3: // Set CMOS Time
8139 // Using a debugger, I notice the following masking/setting
8140 // of bits in Status Register B, by setting Reg B to
8141 // a few values and getting its value after INT 1A was called.
8142 //
8143 // try#1 try#2 try#3
8144 // before 1111 1101 0111 1101 0000 0000
8145 // after 0110 0010 0110 0010 0000 0010
8146 //
8147 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8148 // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
8149 if (rtc_updating()) {
8150 init_rtc();
8151 // fall through as if an update were not in progress
8153 outb_cmos(0x00, regs.u.r8.dh); // Seconds
8154 outb_cmos(0x02, regs.u.r8.cl); // Minutes
8155 outb_cmos(0x04, regs.u.r8.ch); // Hours
8156 // Set Daylight Savings time enabled bit to requested value
8157 val8 = (inb_cmos(0x0b) & 0x60) | 0x02 | (regs.u.r8.dl & 0x01);
8158 // (reg B already selected)
8159 outb_cmos(0x0b, val8);
8160 regs.u.r8.ah = 0;
8161 regs.u.r8.al = val8; // val last written to Reg B
8162 ClearCF(iret_addr.flags); // OK
8163 break;
8165 case 4: // Read CMOS Date
8166 regs.u.r8.ah = 0;
8167 if (rtc_updating()) {
8168 SetCF(iret_addr.flags);
8169 break;
8171 regs.u.r8.cl = inb_cmos(0x09); // Year
8172 regs.u.r8.dh = inb_cmos(0x08); // Month
8173 regs.u.r8.dl = inb_cmos(0x07); // Day of Month
8174 regs.u.r8.ch = inb_cmos(0x32); // Century
8175 regs.u.r8.al = regs.u.r8.ch;
8176 ClearCF(iret_addr.flags); // OK
8177 break;
8179 case 5: // Set CMOS Date
8180 // Using a debugger, I notice the following masking/setting
8181 // of bits in Status Register B, by setting Reg B to
8182 // a few values and getting its value after INT 1A was called.
8183 //
8184 // try#1 try#2 try#3 try#4
8185 // before 1111 1101 0111 1101 0000 0010 0000 0000
8186 // after 0110 1101 0111 1101 0000 0010 0000 0000
8187 //
8188 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8189 // My assumption: RegB = (RegB & 01111111b)
8190 if (rtc_updating()) {
8191 init_rtc();
8192 SetCF(iret_addr.flags);
8193 break;
8195 outb_cmos(0x09, regs.u.r8.cl); // Year
8196 outb_cmos(0x08, regs.u.r8.dh); // Month
8197 outb_cmos(0x07, regs.u.r8.dl); // Day of Month
8198 outb_cmos(0x32, regs.u.r8.ch); // Century
8199 val8 = inb_cmos(0x0b) & 0x7f; // clear halt-clock bit
8200 outb_cmos(0x0b, val8);
8201 regs.u.r8.ah = 0;
8202 regs.u.r8.al = val8; // AL = val last written to Reg B
8203 ClearCF(iret_addr.flags); // OK
8204 break;
8206 case 6: // Set Alarm Time in CMOS
8207 // Using a debugger, I notice the following masking/setting
8208 // of bits in Status Register B, by setting Reg B to
8209 // a few values and getting its value after INT 1A was called.
8210 //
8211 // try#1 try#2 try#3
8212 // before 1101 1111 0101 1111 0000 0000
8213 // after 0110 1111 0111 1111 0010 0000
8214 //
8215 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8216 // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
8217 val8 = inb_cmos(0x0b); // Get Status Reg B
8218 regs.u.r16.ax = 0;
8219 if (val8 & 0x20) {
8220 // Alarm interrupt enabled already
8221 SetCF(iret_addr.flags); // Error: alarm in use
8222 break;
8224 if (rtc_updating()) {
8225 init_rtc();
8226 // fall through as if an update were not in progress
8228 outb_cmos(0x01, regs.u.r8.dh); // Seconds alarm
8229 outb_cmos(0x03, regs.u.r8.cl); // Minutes alarm
8230 outb_cmos(0x05, regs.u.r8.ch); // Hours alarm
8231 outb(0xa1, inb(0xa1) & 0xfe); // enable IRQ 8
8232 // enable Status Reg B alarm bit, clear halt clock bit
8233 outb_cmos(0x0b, (val8 & 0x7f) | 0x20);
8234 ClearCF(iret_addr.flags); // OK
8235 break;
8237 case 7: // Turn off Alarm
8238 // Using a debugger, I notice the following masking/setting
8239 // of bits in Status Register B, by setting Reg B to
8240 // a few values and getting its value after INT 1A was called.
8241 //
8242 // try#1 try#2 try#3 try#4
8243 // before 1111 1101 0111 1101 0010 0000 0010 0010
8244 // after 0100 0101 0101 0101 0000 0000 0000 0010
8245 //
8246 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
8247 // My assumption: RegB = (RegB & 01010111b)
8248 val8 = inb_cmos(0x0b); // Get Status Reg B
8249 // clear clock-halt bit, disable alarm bit
8250 outb_cmos(0x0b, val8 & 0x57); // disable alarm bit
8251 regs.u.r8.ah = 0;
8252 regs.u.r8.al = val8; // val last written to Reg B
8253 ClearCF(iret_addr.flags); // OK
8254 break;
8255 #if BX_PCIBIOS
8256 case 0xb1:
8257 // real mode PCI BIOS functions now handled in assembler code
8258 // this C code handles the error code for information only
8259 if (regs.u.r8.bl == 0xff) {
8260 BX_INFO("PCI BIOS: PCI not present\n");
8261 } else if (regs.u.r8.bl == 0x81) {
8262 BX_INFO("unsupported PCI BIOS function 0x%02x\n", regs.u.r8.al);
8263 } else if (regs.u.r8.bl == 0x83) {
8264 BX_INFO("bad PCI vendor ID %04x\n", regs.u.r16.dx);
8265 } else if (regs.u.r8.bl == 0x86) {
8266 BX_INFO("PCI device %04x:%04x not found\n", regs.u.r16.dx, regs.u.r16.cx);
8268 regs.u.r8.ah = regs.u.r8.bl;
8269 SetCF(iret_addr.flags);
8270 break;
8271 #endif
8273 default:
8274 SetCF(iret_addr.flags); // Unsupported
8278 void
8279 int70_function(regs, ds, iret_addr)
8280 pusha_regs_t regs; // regs pushed from PUSHA instruction
8281 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
8282 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
8284 // INT 70h: IRQ 8 - CMOS RTC interrupt from periodic or alarm modes
8285 Bit8u registerB = 0, registerC = 0;
8287 // Check which modes are enabled and have occurred.
8288 registerB = inb_cmos( 0xB );
8289 registerC = inb_cmos( 0xC );
8291 if( ( registerB & 0x60 ) != 0 ) {
8292 if( ( registerC & 0x20 ) != 0 ) {
8293 // Handle Alarm Interrupt.
8294 ASM_START
8295 sti
8296 int #0x4a
8297 cli
8298 ASM_END
8300 if( ( registerC & 0x40 ) != 0 ) {
8301 // Handle Periodic Interrupt.
8303 if( read_byte( 0x40, 0xA0 ) != 0 ) {
8304 // Wait Interval (Int 15, AH=83) active.
8305 Bit32u time, toggle;
8307 time = read_dword( 0x40, 0x9C ); // Time left in microseconds.
8308 if( time < 0x3D1 ) {
8309 // Done waiting.
8310 Bit16u segment, offset;
8312 offset = read_word( 0x40, 0x98 );
8313 segment = read_word( 0x40, 0x9A );
8314 write_byte( 0x40, 0xA0, 0 ); // Turn of status byte.
8315 outb_cmos( 0xB, registerB & 0x37 ); // Clear the Periodic Interrupt.
8316 write_byte( segment, offset, 0x80 ); // Write to specified flag byte.
8317 } else {
8318 // Continue waiting.
8319 time -= 0x3D1;
8320 write_dword( 0x40, 0x9C, time );
8326 ASM_START
8327 call eoi_both_pics
8328 ASM_END
8332 ASM_START
8333 ;------------------------------------------
8334 ;- INT74h : PS/2 mouse hardware interrupt -
8335 ;------------------------------------------
8336 int74_handler:
8337 sti
8338 pusha
8339 push ds ;; save DS
8340 push #0x00 ;; placeholder for status
8341 push #0x00 ;; placeholder for X
8342 push #0x00 ;; placeholder for Y
8343 push #0x00 ;; placeholder for Z
8344 push #0x00 ;; placeholder for make_far_call boolean
8345 call _int74_function
8346 pop cx ;; remove make_far_call from stack
8347 jcxz int74_done
8349 ;; make far call to EBDA:0022
8350 push #0x00
8351 pop ds
8352 push 0x040E ;; push 0000:040E (opcodes 0xff, 0x36, 0x0E, 0x04)
8353 pop ds
8354 //CALL_EP(0x0022) ;; call far routine (call_Ep DS:0022 :opcodes 0xff, 0x1e, 0x22, 0x00)
8355 call far ptr[0x22]
8356 int74_done:
8357 cli
8358 call eoi_both_pics
8359 add sp, #8 ;; pop status, x, y, z
8361 pop ds ;; restore DS
8362 popa
8363 iret
8366 ;; This will perform an IRET, but will retain value of current CF
8367 ;; by altering flags on stack. Better than RETF #02.
8368 iret_modify_cf:
8369 jc carry_set
8370 push bp
8371 mov bp, sp
8372 and BYTE [bp + 0x06], #0xfe
8373 pop bp
8374 iret
8375 carry_set:
8376 push bp
8377 mov bp, sp
8378 or BYTE [bp + 0x06], #0x01
8379 pop bp
8380 iret
8383 ;----------------------
8384 ;- INT13h (relocated) -
8385 ;----------------------
8387 ; int13_relocated is a little bit messed up since I played with it
8388 ; I have to rewrite it:
8389 ; - call a function that detect which function to call
8390 ; - make all called C function get the same parameters list
8392 int13_relocated:
8394 #if BX_ELTORITO_BOOT
8395 ;; check for an eltorito function
8396 cmp ah,#0x4a
8397 jb int13_not_eltorito
8398 cmp ah,#0x4d
8399 ja int13_not_eltorito
8401 pusha
8402 push es
8403 push ds
8404 push ss
8405 pop ds
8407 push #int13_out
8408 jmp _int13_eltorito ;; ELDX not used
8410 int13_not_eltorito:
8411 push ax
8412 push bx
8413 push cx
8414 push dx
8416 ;; check if emulation active
8417 call _cdemu_isactive
8418 cmp al,#0x00
8419 je int13_cdemu_inactive
8421 ;; check if access to the emulated drive
8422 call _cdemu_emulated_drive
8423 pop dx
8424 push dx
8425 cmp al,dl ;; int13 on emulated drive
8426 jne int13_nocdemu
8428 pop dx
8429 pop cx
8430 pop bx
8431 pop ax
8433 pusha
8434 push es
8435 push ds
8436 push ss
8437 pop ds
8439 push #int13_out
8440 jmp _int13_cdemu ;; ELDX not used
8442 int13_nocdemu:
8443 and dl,#0xE0 ;; mask to get device class, including cdroms
8444 cmp al,dl ;; al is 0x00 or 0x80
8445 jne int13_cdemu_inactive ;; inactive for device class
8447 pop dx
8448 pop cx
8449 pop bx
8450 pop ax
8452 push ax
8453 push cx
8454 push dx
8455 push bx
8457 dec dl ;; real drive is dl - 1
8458 jmp int13_legacy
8460 int13_cdemu_inactive:
8461 pop dx
8462 pop cx
8463 pop bx
8464 pop ax
8466 #endif // BX_ELTORITO_BOOT
8468 int13_noeltorito:
8470 push ax
8471 push cx
8472 push dx
8473 push bx
8475 int13_legacy:
8477 push dx ;; push eltorito value of dx instead of sp
8479 push bp
8480 push si
8481 push di
8483 push es
8484 push ds
8485 push ss
8486 pop ds
8488 ;; now the 16-bit registers can be restored with:
8489 ;; pop ds; pop es; popa; iret
8490 ;; arguments passed to functions should be
8491 ;; DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS
8493 test dl, #0x80
8494 jnz int13_notfloppy
8496 push #int13_out
8497 jmp _int13_diskette_function
8499 int13_notfloppy:
8501 #if BX_USE_ATADRV
8503 cmp dl, #0xE0
8504 jb int13_notcdrom
8506 // ebx is modified: BSD 5.2.1 boot loader problem
8507 // someone should figure out which 32 bit register that actually are used
8509 shr ebx, #16
8510 push bx
8512 call _int13_cdrom
8514 pop bx
8515 shl ebx, #16
8517 jmp int13_out
8519 int13_notcdrom:
8521 #endif
8523 int13_disk:
8524 call _int13_harddisk
8526 int13_out:
8527 pop ds
8528 pop es
8529 popa
8530 iret
8532 ;----------
8533 ;- INT18h -
8534 ;----------
8535 int18_handler: ;; Boot Failure recovery: try the next device.
8537 ;; Reset SP and SS
8538 mov ax, #0xfffe
8539 mov sp, ax
8540 xor ax, ax
8541 mov ss, ax
8543 ;; Get the boot sequence number out of the IPL memory
8544 ;; The first time we do this it will have been set to -1 so
8545 ;; we will start from device 0.
8546 mov bx, #IPL_SEG
8547 mov ds, bx ;; Set segment
8548 mov bx, IPL_SEQUENCE_OFFSET ;; BX is now the sequence number
8549 inc bx ;; ++
8550 mov IPL_SEQUENCE_OFFSET, bx ;; Write it back
8551 mov ds, ax ;; and reset the segment to zero.
8553 ;; Call the C code for the next boot device
8554 push bx
8555 call _int18_function
8557 ;; Boot failed: invoke the boot recovery function...
8558 int #0x18
8560 ;----------
8561 ;- INT19h -
8562 ;----------
8563 int19_relocated: ;; Boot function, relocated
8564 ;;
8565 ;; *** Warning: INT 19h resets the whole machine ***
8566 ;;
8567 ;; Because PV drivers in HVM guests detach some of the emulated devices,
8568 ;; it is not safe to do a soft reboot by just dropping to real mode and
8569 ;; invoking INT 19h -- the boot drives might have disappeared!
8570 ;; If the user asks for a soft reboot, the only thing we can do is
8571 ;; reset the whole machine. When it comes back up, the normal BIOS
8572 ;; boot sequence will start, which is more or less the required behaviour.
8573 ;;
8574 ;; Reset SP and SS
8575 mov ax, #0xfffe
8576 mov sp, ax
8577 xor ax, ax
8578 mov ss, ax
8579 call _machine_reset
8581 ;----------
8582 ;- INT1Ch -
8583 ;----------
8584 int1c_handler: ;; User Timer Tick
8585 iret
8588 ;----------------------
8589 ;- POST: Floppy Drive -
8590 ;----------------------
8591 floppy_drive_post:
8592 mov ax, #0x0000
8593 mov ds, ax
8595 mov al, #0x00
8596 mov 0x043e, al ;; drive 0 & 1 uncalibrated, no interrupt has occurred
8598 mov 0x043f, al ;; diskette motor status: read op, drive0, motors off
8600 mov 0x0440, al ;; diskette motor timeout counter: not active
8601 mov 0x0441, al ;; diskette controller status return code
8603 mov 0x0442, al ;; disk & diskette controller status register 0
8604 mov 0x0443, al ;; diskette controller status register 1
8605 mov 0x0444, al ;; diskette controller status register 2
8606 mov 0x0445, al ;; diskette controller cylinder number
8607 mov 0x0446, al ;; diskette controller head number
8608 mov 0x0447, al ;; diskette controller sector number
8609 mov 0x0448, al ;; diskette controller bytes written
8611 mov 0x048b, al ;; diskette configuration data
8613 ;; -----------------------------------------------------------------
8614 ;; (048F) diskette controller information
8615 ;;
8616 mov al, #0x10 ;; get CMOS diskette drive type
8617 out 0x70, AL
8618 in AL, 0x71
8619 mov ah, al ;; save byte to AH
8621 look_drive0:
8622 shr al, #4 ;; look at top 4 bits for drive 0
8623 jz f0_missing ;; jump if no drive0
8624 mov bl, #0x07 ;; drive0 determined, multi-rate, has changed line
8625 jmp look_drive1
8626 f0_missing:
8627 mov bl, #0x00 ;; no drive0
8629 look_drive1:
8630 mov al, ah ;; restore from AH
8631 and al, #0x0f ;; look at bottom 4 bits for drive 1
8632 jz f1_missing ;; jump if no drive1
8633 or bl, #0x70 ;; drive1 determined, multi-rate, has changed line
8634 f1_missing:
8635 ;; leave high bits in BL zerod
8636 mov 0x048f, bl ;; put new val in BDA (diskette controller information)
8637 ;; -----------------------------------------------------------------
8639 mov al, #0x00
8640 mov 0x0490, al ;; diskette 0 media state
8641 mov 0x0491, al ;; diskette 1 media state
8643 ;; diskette 0,1 operational starting state
8644 ;; drive type has not been determined,
8645 ;; has no changed detection line
8646 mov 0x0492, al
8647 mov 0x0493, al
8649 mov 0x0494, al ;; diskette 0 current cylinder
8650 mov 0x0495, al ;; diskette 1 current cylinder
8652 mov al, #0x02
8653 out #0x0a, al ;; clear DMA-1 channel 2 mask bit
8655 SET_INT_VECTOR(0x1E, #0xF000, #diskette_param_table2)
8656 SET_INT_VECTOR(0x40, #0xF000, #int13_diskette)
8657 SET_INT_VECTOR(0x0E, #0xF000, #int0e_handler) ;; IRQ 6
8659 ret
8662 ;--------------------
8663 ;- POST: HARD DRIVE -
8664 ;--------------------
8665 ; relocated here because the primary POST area isnt big enough.
8666 hard_drive_post:
8667 // IRQ 14 = INT 76h
8668 // INT 76h calls INT 15h function ax=9100
8670 mov al, #0x0a ; 0000 1010 = reserved, disable IRQ 14
8671 mov dx, #0x03f6
8672 out dx, al
8674 mov ax, #0x0000
8675 mov ds, ax
8676 mov 0x0474, al /* hard disk status of last operation */
8677 mov 0x0477, al /* hard disk port offset (XT only ???) */
8678 mov 0x048c, al /* hard disk status register */
8679 mov 0x048d, al /* hard disk error register */
8680 mov 0x048e, al /* hard disk task complete flag */
8681 mov al, #0x01
8682 mov 0x0475, al /* hard disk number attached */
8683 mov al, #0xc0
8684 mov 0x0476, al /* hard disk control byte */
8685 SET_INT_VECTOR(0x13, #0xF000, #int13_handler)
8686 SET_INT_VECTOR(0x76, #0xF000, #int76_handler)
8687 ;; INT 41h: hard disk 0 configuration pointer
8688 ;; INT 46h: hard disk 1 configuration pointer
8689 SET_INT_VECTOR(0x41, #EBDA_SEG, #0x003D)
8690 SET_INT_VECTOR(0x46, #EBDA_SEG, #0x004D)
8692 ;; move disk geometry data from CMOS to EBDA disk parameter table(s)
8693 mov al, #0x12
8694 out #0x70, al
8695 in al, #0x71
8696 and al, #0xf0
8697 cmp al, #0xf0
8698 je post_d0_extended
8699 jmp check_for_hd1
8700 post_d0_extended:
8701 mov al, #0x19
8702 out #0x70, al
8703 in al, #0x71
8704 cmp al, #47 ;; decimal 47 - user definable
8705 je post_d0_type47
8706 HALT(__LINE__)
8707 post_d0_type47:
8708 ;; CMOS purpose param table offset
8709 ;; 1b cylinders low 0
8710 ;; 1c cylinders high 1
8711 ;; 1d heads 2
8712 ;; 1e write pre-comp low 5
8713 ;; 1f write pre-comp high 6
8714 ;; 20 retries/bad map/heads>8 8
8715 ;; 21 landing zone low C
8716 ;; 22 landing zone high D
8717 ;; 23 sectors/track E
8719 mov ax, #EBDA_SEG
8720 mov ds, ax
8722 ;;; Filling EBDA table for hard disk 0.
8723 mov al, #0x1f
8724 out #0x70, al
8725 in al, #0x71
8726 mov ah, al
8727 mov al, #0x1e
8728 out #0x70, al
8729 in al, #0x71
8730 mov (0x003d + 0x05), ax ;; write precomp word
8732 mov al, #0x20
8733 out #0x70, al
8734 in al, #0x71
8735 mov (0x003d + 0x08), al ;; drive control byte
8737 mov al, #0x22
8738 out #0x70, al
8739 in al, #0x71
8740 mov ah, al
8741 mov al, #0x21
8742 out #0x70, al
8743 in al, #0x71
8744 mov (0x003d + 0x0C), ax ;; landing zone word
8746 mov al, #0x1c ;; get cylinders word in AX
8747 out #0x70, al
8748 in al, #0x71 ;; high byte
8749 mov ah, al
8750 mov al, #0x1b
8751 out #0x70, al
8752 in al, #0x71 ;; low byte
8753 mov bx, ax ;; BX = cylinders
8755 mov al, #0x1d
8756 out #0x70, al
8757 in al, #0x71
8758 mov cl, al ;; CL = heads
8760 mov al, #0x23
8761 out #0x70, al
8762 in al, #0x71
8763 mov dl, al ;; DL = sectors
8765 cmp bx, #1024
8766 jnbe hd0_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8768 hd0_post_physical_chs:
8769 ;; no logical CHS mapping used, just physical CHS
8770 ;; use Standard Fixed Disk Parameter Table (FDPT)
8771 mov (0x003d + 0x00), bx ;; number of physical cylinders
8772 mov (0x003d + 0x02), cl ;; number of physical heads
8773 mov (0x003d + 0x0E), dl ;; number of physical sectors
8774 jmp check_for_hd1
8776 hd0_post_logical_chs:
8777 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8778 mov (0x003d + 0x09), bx ;; number of physical cylinders
8779 mov (0x003d + 0x0b), cl ;; number of physical heads
8780 mov (0x003d + 0x04), dl ;; number of physical sectors
8781 mov (0x003d + 0x0e), dl ;; number of logical sectors (same)
8782 mov al, #0xa0
8783 mov (0x003d + 0x03), al ;; A0h signature, indicates translated table
8785 cmp bx, #2048
8786 jnbe hd0_post_above_2048
8787 ;; 1024 < c <= 2048 cylinders
8788 shr bx, #0x01
8789 shl cl, #0x01
8790 jmp hd0_post_store_logical
8792 hd0_post_above_2048:
8793 cmp bx, #4096
8794 jnbe hd0_post_above_4096
8795 ;; 2048 < c <= 4096 cylinders
8796 shr bx, #0x02
8797 shl cl, #0x02
8798 jmp hd0_post_store_logical
8800 hd0_post_above_4096:
8801 cmp bx, #8192
8802 jnbe hd0_post_above_8192
8803 ;; 4096 < c <= 8192 cylinders
8804 shr bx, #0x03
8805 shl cl, #0x03
8806 jmp hd0_post_store_logical
8808 hd0_post_above_8192:
8809 ;; 8192 < c <= 16384 cylinders
8810 shr bx, #0x04
8811 shl cl, #0x04
8813 hd0_post_store_logical:
8814 mov (0x003d + 0x00), bx ;; number of physical cylinders
8815 mov (0x003d + 0x02), cl ;; number of physical heads
8816 ;; checksum
8817 mov cl, #0x0f ;; repeat count
8818 mov si, #0x003d ;; offset to disk0 FDPT
8819 mov al, #0x00 ;; sum
8820 hd0_post_checksum_loop:
8821 add al, [si]
8822 inc si
8823 dec cl
8824 jnz hd0_post_checksum_loop
8825 not al ;; now take 2s complement
8826 inc al
8827 mov [si], al
8828 ;;; Done filling EBDA table for hard disk 0.
8831 check_for_hd1:
8832 ;; is there really a second hard disk? if not, return now
8833 mov al, #0x12
8834 out #0x70, al
8835 in al, #0x71
8836 and al, #0x0f
8837 jnz post_d1_exists
8838 ret
8839 post_d1_exists:
8840 ;; check that the hd type is really 0x0f.
8841 cmp al, #0x0f
8842 jz post_d1_extended
8843 HALT(__LINE__)
8844 post_d1_extended:
8845 ;; check that the extended type is 47 - user definable
8846 mov al, #0x1a
8847 out #0x70, al
8848 in al, #0x71
8849 cmp al, #47 ;; decimal 47 - user definable
8850 je post_d1_type47
8851 HALT(__LINE__)
8852 post_d1_type47:
8853 ;; Table for disk1.
8854 ;; CMOS purpose param table offset
8855 ;; 0x24 cylinders low 0
8856 ;; 0x25 cylinders high 1
8857 ;; 0x26 heads 2
8858 ;; 0x27 write pre-comp low 5
8859 ;; 0x28 write pre-comp high 6
8860 ;; 0x29 heads>8 8
8861 ;; 0x2a landing zone low C
8862 ;; 0x2b landing zone high D
8863 ;; 0x2c sectors/track E
8864 ;;; Fill EBDA table for hard disk 1.
8865 mov ax, #EBDA_SEG
8866 mov ds, ax
8867 mov al, #0x28
8868 out #0x70, al
8869 in al, #0x71
8870 mov ah, al
8871 mov al, #0x27
8872 out #0x70, al
8873 in al, #0x71
8874 mov (0x004d + 0x05), ax ;; write precomp word
8876 mov al, #0x29
8877 out #0x70, al
8878 in al, #0x71
8879 mov (0x004d + 0x08), al ;; drive control byte
8881 mov al, #0x2b
8882 out #0x70, al
8883 in al, #0x71
8884 mov ah, al
8885 mov al, #0x2a
8886 out #0x70, al
8887 in al, #0x71
8888 mov (0x004d + 0x0C), ax ;; landing zone word
8890 mov al, #0x25 ;; get cylinders word in AX
8891 out #0x70, al
8892 in al, #0x71 ;; high byte
8893 mov ah, al
8894 mov al, #0x24
8895 out #0x70, al
8896 in al, #0x71 ;; low byte
8897 mov bx, ax ;; BX = cylinders
8899 mov al, #0x26
8900 out #0x70, al
8901 in al, #0x71
8902 mov cl, al ;; CL = heads
8904 mov al, #0x2c
8905 out #0x70, al
8906 in al, #0x71
8907 mov dl, al ;; DL = sectors
8909 cmp bx, #1024
8910 jnbe hd1_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8912 hd1_post_physical_chs:
8913 ;; no logical CHS mapping used, just physical CHS
8914 ;; use Standard Fixed Disk Parameter Table (FDPT)
8915 mov (0x004d + 0x00), bx ;; number of physical cylinders
8916 mov (0x004d + 0x02), cl ;; number of physical heads
8917 mov (0x004d + 0x0E), dl ;; number of physical sectors
8918 ret
8920 hd1_post_logical_chs:
8921 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8922 mov (0x004d + 0x09), bx ;; number of physical cylinders
8923 mov (0x004d + 0x0b), cl ;; number of physical heads
8924 mov (0x004d + 0x04), dl ;; number of physical sectors
8925 mov (0x004d + 0x0e), dl ;; number of logical sectors (same)
8926 mov al, #0xa0
8927 mov (0x004d + 0x03), al ;; A0h signature, indicates translated table
8929 cmp bx, #2048
8930 jnbe hd1_post_above_2048
8931 ;; 1024 < c <= 2048 cylinders
8932 shr bx, #0x01
8933 shl cl, #0x01
8934 jmp hd1_post_store_logical
8936 hd1_post_above_2048:
8937 cmp bx, #4096
8938 jnbe hd1_post_above_4096
8939 ;; 2048 < c <= 4096 cylinders
8940 shr bx, #0x02
8941 shl cl, #0x02
8942 jmp hd1_post_store_logical
8944 hd1_post_above_4096:
8945 cmp bx, #8192
8946 jnbe hd1_post_above_8192
8947 ;; 4096 < c <= 8192 cylinders
8948 shr bx, #0x03
8949 shl cl, #0x03
8950 jmp hd1_post_store_logical
8952 hd1_post_above_8192:
8953 ;; 8192 < c <= 16384 cylinders
8954 shr bx, #0x04
8955 shl cl, #0x04
8957 hd1_post_store_logical:
8958 mov (0x004d + 0x00), bx ;; number of physical cylinders
8959 mov (0x004d + 0x02), cl ;; number of physical heads
8960 ;; checksum
8961 mov cl, #0x0f ;; repeat count
8962 mov si, #0x004d ;; offset to disk0 FDPT
8963 mov al, #0x00 ;; sum
8964 hd1_post_checksum_loop:
8965 add al, [si]
8966 inc si
8967 dec cl
8968 jnz hd1_post_checksum_loop
8969 not al ;; now take 2s complement
8970 inc al
8971 mov [si], al
8972 ;;; Done filling EBDA table for hard disk 1.
8974 ret
8976 ;--------------------
8977 ;- POST: EBDA segment
8978 ;--------------------
8979 ; relocated here because the primary POST area isnt big enough.
8980 ebda_post:
8981 #if BX_USE_EBDA
8982 mov ax, #EBDA_SEG
8983 mov ds, ax
8984 mov byte ptr [0x0], #EBDA_SIZE
8985 #endif
8986 xor ax, ax ; mov EBDA seg into 40E
8987 mov ds, ax
8988 mov word ptr [0x40E], #EBDA_SEG
8989 ret;;
8991 ;--------------------
8992 ;- POST: EOI + jmp via [0x40:67)
8993 ;--------------------
8994 ; relocated here because the primary POST area isnt big enough.
8995 eoi_jmp_post:
8996 call eoi_both_pics
8998 xor ax, ax
8999 mov ds, ax
9001 jmp far ptr [0x467]
9004 ;--------------------
9005 eoi_both_pics:
9006 mov al, #0x20
9007 out #0xA0, al ;; slave PIC EOI
9008 eoi_master_pic:
9009 mov al, #0x20
9010 out #0x20, al ;; master PIC EOI
9011 ret
9013 ;--------------------
9014 BcdToBin:
9015 ;; in: AL in BCD format
9016 ;; out: AL in binary format, AH will always be 0
9017 ;; trashes BX
9018 mov bl, al
9019 and bl, #0x0f ;; bl has low digit
9020 shr al, #4 ;; al has high digit
9021 mov bh, #10
9022 mul al, bh ;; multiply high digit by 10 (result in AX)
9023 add al, bl ;; then add low digit
9024 ret
9026 ;--------------------
9027 timer_tick_post:
9028 ;; Setup the Timer Ticks Count (0x46C:dword) and
9029 ;; Timer Ticks Roller Flag (0x470:byte)
9030 ;; The Timer Ticks Count needs to be set according to
9031 ;; the current CMOS time, as if ticks have been occurring
9032 ;; at 18.2hz since midnight up to this point. Calculating
9033 ;; this is a little complicated. Here are the factors I gather
9034 ;; regarding this. 14,318,180 hz was the original clock speed,
9035 ;; chosen so it could be divided by either 3 to drive the 5Mhz CPU
9036 ;; at the time, or 4 to drive the CGA video adapter. The div3
9037 ;; source was divided again by 4 to feed a 1.193Mhz signal to
9038 ;; the timer. With a maximum 16bit timer count, this is again
9039 ;; divided down by 65536 to 18.2hz.
9040 ;;
9041 ;; 14,318,180 Hz clock
9042 ;; /3 = 4,772,726 Hz fed to orginal 5Mhz CPU
9043 ;; /4 = 1,193,181 Hz fed to timer
9044 ;; /65536 (maximum timer count) = 18.20650736 ticks/second
9045 ;; 1 second = 18.20650736 ticks
9046 ;; 1 minute = 1092.390442 ticks
9047 ;; 1 hour = 65543.42651 ticks
9048 ;;
9049 ;; Given the values in the CMOS clock, one could calculate
9050 ;; the number of ticks by the following:
9051 ;; ticks = (BcdToBin(seconds) * 18.206507) +
9052 ;; (BcdToBin(minutes) * 1092.3904)
9053 ;; (BcdToBin(hours) * 65543.427)
9054 ;; To get a little more accuracy, since Im using integer
9055 ;; arithmatic, I use:
9056 ;; ticks = (BcdToBin(seconds) * 18206507) / 1000000 +
9057 ;; (BcdToBin(minutes) * 10923904) / 10000 +
9058 ;; (BcdToBin(hours) * 65543427) / 1000
9060 ;; assuming DS=0000
9062 ;; get CMOS seconds
9063 xor eax, eax ;; clear EAX
9064 mov al, #0x00
9065 out #0x70, al
9066 in al, #0x71 ;; AL has CMOS seconds in BCD
9067 call BcdToBin ;; EAX now has seconds in binary
9068 mov edx, #18206507
9069 mul eax, edx
9070 mov ebx, #1000000
9071 xor edx, edx
9072 div eax, ebx
9073 mov ecx, eax ;; ECX will accumulate total ticks
9075 ;; get CMOS minutes
9076 xor eax, eax ;; clear EAX
9077 mov al, #0x02
9078 out #0x70, al
9079 in al, #0x71 ;; AL has CMOS minutes in BCD
9080 call BcdToBin ;; EAX now has minutes in binary
9081 mov edx, #10923904
9082 mul eax, edx
9083 mov ebx, #10000
9084 xor edx, edx
9085 div eax, ebx
9086 add ecx, eax ;; add to total ticks
9088 ;; get CMOS hours
9089 xor eax, eax ;; clear EAX
9090 mov al, #0x04
9091 out #0x70, al
9092 in al, #0x71 ;; AL has CMOS hours in BCD
9093 call BcdToBin ;; EAX now has hours in binary
9094 mov edx, #65543427
9095 mul eax, edx
9096 mov ebx, #1000
9097 xor edx, edx
9098 div eax, ebx
9099 add ecx, eax ;; add to total ticks
9101 mov 0x46C, ecx ;; Timer Ticks Count
9102 xor al, al
9103 mov 0x470, al ;; Timer Ticks Rollover Flag
9104 ret
9106 ;--------------------
9107 int76_handler:
9108 ;; record completion in BIOS task complete flag
9109 push ax
9110 push ds
9111 mov ax, #0x0040
9112 mov ds, ax
9113 mov 0x008E, #0xff
9114 call eoi_both_pics
9115 pop ds
9116 pop ax
9117 iret
9120 ;--------------------
9121 #if BX_APM
9123 use32 386
9124 #define APM_PROT32
9125 #include "apmbios.S"
9127 use16 386
9128 #define APM_PROT16
9129 #include "apmbios.S"
9131 #define APM_REAL
9132 #include "apmbios.S"
9134 #endif
9136 ASM_END
9137 #include "32bitgateway.c"
9138 ASM_START
9140 ;--------------------
9141 #if BX_PCIBIOS
9142 use32 386
9143 .align 16
9144 bios32_structure:
9145 db 0x5f, 0x33, 0x32, 0x5f ;; "_32_" signature
9146 dw bios32_entry_point, 0xf ;; 32 bit physical address
9147 db 0 ;; revision level
9148 ;; length in paragraphs and checksum stored in a word to prevent errors
9149 dw (~(((bios32_entry_point >> 8) + (bios32_entry_point & 0xff) + 0x32) \
9150 & 0xff) << 8) + 0x01
9151 db 0,0,0,0,0 ;; reserved
9153 .align 16
9154 bios32_entry_point:
9155 pushf
9156 cmp eax, #0x49435024
9157 jne unknown_service
9158 mov eax, #0x80000000
9159 mov dx, #0x0cf8
9160 out dx, eax
9161 mov dx, #0x0cfc
9162 in eax, dx
9163 cmp eax, #0x12378086
9164 jne unknown_service
9165 mov ebx, #0x000f0000
9166 mov ecx, #0
9167 mov edx, #pcibios_protected
9168 xor al, al
9169 jmp bios32_end
9170 unknown_service:
9171 mov al, #0x80
9172 bios32_end:
9173 popf
9174 retf
9176 .align 16
9177 pcibios_protected:
9178 pushf
9179 cli
9180 push esi
9181 push edi
9182 cmp al, #0x01 ;; installation check
9183 jne pci_pro_f02
9184 mov bx, #0x0210
9185 mov cx, #0
9186 mov edx, #0x20494350
9187 mov al, #0x01
9188 jmp pci_pro_ok
9189 pci_pro_f02: ;; find pci device
9190 cmp al, #0x02
9191 jne pci_pro_f08
9192 shl ecx, #16
9193 mov cx, dx
9194 mov bx, #0x0000
9195 mov di, #0x00
9196 pci_pro_devloop:
9197 call pci_pro_select_reg
9198 mov dx, #0x0cfc
9199 in eax, dx
9200 cmp eax, ecx
9201 jne pci_pro_nextdev
9202 cmp si, #0
9203 je pci_pro_ok
9204 dec si
9205 pci_pro_nextdev:
9206 inc bx
9207 cmp bx, #0x0100
9208 jne pci_pro_devloop
9209 mov ah, #0x86
9210 jmp pci_pro_fail
9211 pci_pro_f08: ;; read configuration byte
9212 cmp al, #0x08
9213 jne pci_pro_f09
9214 call pci_pro_select_reg
9215 push edx
9216 mov dx, di
9217 and dx, #0x03
9218 add dx, #0x0cfc
9219 in al, dx
9220 pop edx
9221 mov cl, al
9222 jmp pci_pro_ok
9223 pci_pro_f09: ;; read configuration word
9224 cmp al, #0x09
9225 jne pci_pro_f0a
9226 call pci_pro_select_reg
9227 push edx
9228 mov dx, di
9229 and dx, #0x02
9230 add dx, #0x0cfc
9231 in ax, dx
9232 pop edx
9233 mov cx, ax
9234 jmp pci_pro_ok
9235 pci_pro_f0a: ;; read configuration dword
9236 cmp al, #0x0a
9237 jne pci_pro_f0b
9238 call pci_pro_select_reg
9239 push edx
9240 mov dx, #0x0cfc
9241 in eax, dx
9242 pop edx
9243 mov ecx, eax
9244 jmp pci_pro_ok
9245 pci_pro_f0b: ;; write configuration byte
9246 cmp al, #0x0b
9247 jne pci_pro_f0c
9248 call pci_pro_select_reg
9249 push edx
9250 mov dx, di
9251 and dx, #0x03
9252 add dx, #0x0cfc
9253 mov al, cl
9254 out dx, al
9255 pop edx
9256 jmp pci_pro_ok
9257 pci_pro_f0c: ;; write configuration word
9258 cmp al, #0x0c
9259 jne pci_pro_f0d
9260 call pci_pro_select_reg
9261 push edx
9262 mov dx, di
9263 and dx, #0x02
9264 add dx, #0x0cfc
9265 mov ax, cx
9266 out dx, ax
9267 pop edx
9268 jmp pci_pro_ok
9269 pci_pro_f0d: ;; write configuration dword
9270 cmp al, #0x0d
9271 jne pci_pro_unknown
9272 call pci_pro_select_reg
9273 push edx
9274 mov dx, #0x0cfc
9275 mov eax, ecx
9276 out dx, eax
9277 pop edx
9278 jmp pci_pro_ok
9279 pci_pro_unknown:
9280 mov ah, #0x81
9281 pci_pro_fail:
9282 pop edi
9283 pop esi
9284 sti
9285 popf
9286 stc
9287 retf
9288 pci_pro_ok:
9289 xor ah, ah
9290 pop edi
9291 pop esi
9292 sti
9293 popf
9294 clc
9295 retf
9297 pci_pro_select_reg:
9298 push edx
9299 mov eax, #0x800000
9300 mov ax, bx
9301 shl eax, #8
9302 and di, #0xff
9303 or ax, di
9304 and al, #0xfc
9305 mov dx, #0x0cf8
9306 out dx, eax
9307 pop edx
9308 ret
9310 use16 386
9312 pcibios_real:
9313 push eax
9314 push dx
9315 mov eax, #0x80000000
9316 mov dx, #0x0cf8
9317 out dx, eax
9318 mov dx, #0x0cfc
9319 in eax, dx
9320 cmp eax, #0x12378086
9321 je pci_present
9322 pop dx
9323 pop eax
9324 mov ah, #0xff
9325 stc
9326 ret
9327 pci_present:
9328 pop dx
9329 pop eax
9330 cmp al, #0x01 ;; installation check
9331 jne pci_real_f02
9332 mov ax, #0x0001
9333 mov bx, #0x0210
9334 mov cx, #0
9335 mov edx, #0x20494350
9336 mov edi, #0xf0000
9337 mov di, #pcibios_protected
9338 clc
9339 ret
9340 pci_real_f02: ;; find pci device
9341 push esi
9342 push edi
9343 cmp al, #0x02
9344 jne pci_real_f08
9345 shl ecx, #16
9346 mov cx, dx
9347 mov bx, #0x0000
9348 mov di, #0x00
9349 pci_real_devloop:
9350 call pci_real_select_reg
9351 mov dx, #0x0cfc
9352 in eax, dx
9353 cmp eax, ecx
9354 jne pci_real_nextdev
9355 cmp si, #0
9356 je pci_real_ok
9357 dec si
9358 pci_real_nextdev:
9359 inc bx
9360 cmp bx, #0x0100
9361 jne pci_real_devloop
9362 mov dx, cx
9363 shr ecx, #16
9364 mov ah, #0x86
9365 jmp pci_real_fail
9366 pci_real_f08: ;; read configuration byte
9367 cmp al, #0x08
9368 jne pci_real_f09
9369 call pci_real_select_reg
9370 push dx
9371 mov dx, di
9372 and dx, #0x03
9373 add dx, #0x0cfc
9374 in al, dx
9375 pop dx
9376 mov cl, al
9377 jmp pci_real_ok
9378 pci_real_f09: ;; read configuration word
9379 cmp al, #0x09
9380 jne pci_real_f0a
9381 call pci_real_select_reg
9382 push dx
9383 mov dx, di
9384 and dx, #0x02
9385 add dx, #0x0cfc
9386 in ax, dx
9387 pop dx
9388 mov cx, ax
9389 jmp pci_real_ok
9390 pci_real_f0a: ;; read configuration dword
9391 cmp al, #0x0a
9392 jne pci_real_f0b
9393 call pci_real_select_reg
9394 push dx
9395 mov dx, #0x0cfc
9396 in eax, dx
9397 pop dx
9398 mov ecx, eax
9399 jmp pci_real_ok
9400 pci_real_f0b: ;; write configuration byte
9401 cmp al, #0x0b
9402 jne pci_real_f0c
9403 call pci_real_select_reg
9404 push dx
9405 mov dx, di
9406 and dx, #0x03
9407 add dx, #0x0cfc
9408 mov al, cl
9409 out dx, al
9410 pop dx
9411 jmp pci_real_ok
9412 pci_real_f0c: ;; write configuration word
9413 cmp al, #0x0c
9414 jne pci_real_f0d
9415 call pci_real_select_reg
9416 push dx
9417 mov dx, di
9418 and dx, #0x02
9419 add dx, #0x0cfc
9420 mov ax, cx
9421 out dx, ax
9422 pop dx
9423 jmp pci_real_ok
9424 pci_real_f0d: ;; write configuration dword
9425 cmp al, #0x0d
9426 jne pci_real_unknown
9427 call pci_real_select_reg
9428 push dx
9429 mov dx, #0x0cfc
9430 mov eax, ecx
9431 out dx, eax
9432 pop dx
9433 jmp pci_real_ok
9434 pci_real_unknown:
9435 mov ah, #0x81
9436 pci_real_fail:
9437 pop edi
9438 pop esi
9439 stc
9440 ret
9441 pci_real_ok:
9442 xor ah, ah
9443 pop edi
9444 pop esi
9445 clc
9446 ret
9448 pci_real_select_reg:
9449 push dx
9450 mov eax, #0x800000
9451 mov ax, bx
9452 shl eax, #8
9453 and di, #0xff
9454 or ax, di
9455 and al, #0xfc
9456 mov dx, #0x0cf8
9457 out dx, eax
9458 pop dx
9459 ret
9461 .align 16
9462 pci_routing_table_structure:
9463 db 0x24, 0x50, 0x49, 0x52 ;; "$PIR" signature
9464 db 0, 1 ;; version
9465 dw 32 + (6 * 16) ;; table size
9466 db 0 ;; PCI interrupt router bus
9467 db 0x08 ;; PCI interrupt router DevFunc
9468 dw 0x0000 ;; PCI exclusive IRQs
9469 dw 0x8086 ;; compatible PCI interrupt router vendor ID
9470 dw 0x7000 ;; compatible PCI interrupt router device ID
9471 dw 0,0 ;; Miniport data
9472 db 0,0,0,0,0,0,0,0,0,0,0 ;; reserved
9473 db 0x07 ;; checksum
9474 ;; first slot entry PCI-to-ISA (embedded)
9475 db 0 ;; pci bus number
9476 db 0x08 ;; pci device number (bit 7-3)
9477 db 0x61 ;; link value INTA#: pointer into PCI2ISA config space
9478 dw 0x0c20 ;; IRQ bitmap INTA#
9479 db 0x62 ;; link value INTB#
9480 dw 0x0c20 ;; IRQ bitmap INTB#
9481 db 0x63 ;; link value INTC#
9482 dw 0x0c20 ;; IRQ bitmap INTC#
9483 db 0x60 ;; link value INTD#
9484 dw 0x0c20 ;; IRQ bitmap INTD#
9485 db 0 ;; physical slot (0 = embedded)
9486 db 0 ;; reserved
9487 ;; second slot entry: 1st PCI slot
9488 db 0 ;; pci bus number
9489 db 0x10 ;; pci device number (bit 7-3)
9490 db 0x62 ;; link value INTA#
9491 dw 0x0c20 ;; IRQ bitmap INTA#
9492 db 0x63 ;; link value INTB#
9493 dw 0x0c20 ;; IRQ bitmap INTB#
9494 db 0x60 ;; link value INTC#
9495 dw 0x0c20 ;; IRQ bitmap INTC#
9496 db 0x61 ;; link value INTD#
9497 dw 0x0c20 ;; IRQ bitmap INTD#
9498 db 1 ;; physical slot (0 = embedded)
9499 db 0 ;; reserved
9500 ;; third slot entry: 2nd PCI slot
9501 db 0 ;; pci bus number
9502 db 0x18 ;; pci device number (bit 7-3)
9503 db 0x63 ;; link value INTA#
9504 dw 0x0c20 ;; IRQ bitmap INTA#
9505 db 0x60 ;; link value INTB#
9506 dw 0x0c20 ;; IRQ bitmap INTB#
9507 db 0x61 ;; link value INTC#
9508 dw 0x0c20 ;; IRQ bitmap INTC#
9509 db 0x62 ;; link value INTD#
9510 dw 0x0c20 ;; IRQ bitmap INTD#
9511 db 2 ;; physical slot (0 = embedded)
9512 db 0 ;; reserved
9513 ;; 4th slot entry: 3rd PCI slot
9514 db 0 ;; pci bus number
9515 db 0x20 ;; pci device number (bit 7-3)
9516 db 0x60 ;; link value INTA#
9517 dw 0x0c20 ;; IRQ bitmap INTA#
9518 db 0x61 ;; link value INTB#
9519 dw 0x0c20 ;; IRQ bitmap INTB#
9520 db 0x62 ;; link value INTC#
9521 dw 0x0c20 ;; IRQ bitmap INTC#
9522 db 0x63 ;; link value INTD#
9523 dw 0x0c20 ;; IRQ bitmap INTD#
9524 db 3 ;; physical slot (0 = embedded)
9525 db 0 ;; reserved
9526 ;; 5th slot entry: 4rd PCI slot
9527 db 0 ;; pci bus number
9528 db 0x28 ;; pci device number (bit 7-3)
9529 db 0x61 ;; link value INTA#
9530 dw 0x0c20 ;; IRQ bitmap INTA#
9531 db 0x62 ;; link value INTB#
9532 dw 0x0c20 ;; IRQ bitmap INTB#
9533 db 0x63 ;; link value INTC#
9534 dw 0x0c20 ;; IRQ bitmap INTC#
9535 db 0x60 ;; link value INTD#
9536 dw 0x0c20 ;; IRQ bitmap INTD#
9537 db 4 ;; physical slot (0 = embedded)
9538 db 0 ;; reserved
9539 ;; 6th slot entry: 5rd PCI slot
9540 db 0 ;; pci bus number
9541 db 0x30 ;; pci device number (bit 7-3)
9542 db 0x62 ;; link value INTA#
9543 dw 0x0c20 ;; IRQ bitmap INTA#
9544 db 0x63 ;; link value INTB#
9545 dw 0x0c20 ;; IRQ bitmap INTB#
9546 db 0x60 ;; link value INTC#
9547 dw 0x0c20 ;; IRQ bitmap INTC#
9548 db 0x61 ;; link value INTD#
9549 dw 0x0c20 ;; IRQ bitmap INTD#
9550 db 5 ;; physical slot (0 = embedded)
9551 db 0 ;; reserved
9552 #endif // BX_PCIBIOS
9554 ; parallel port detection: base address in DX, index in BX, timeout in CL
9555 detect_parport:
9556 push dx
9557 add dx, #2
9558 in al, dx
9559 and al, #0xdf ; clear input mode
9560 out dx, al
9561 pop dx
9562 mov al, #0xaa
9563 out dx, al
9564 in al, dx
9565 cmp al, #0xaa
9566 jne no_parport
9567 push bx
9568 shl bx, #1
9569 mov [bx+0x408], dx ; Parallel I/O address
9570 pop bx
9571 mov [bx+0x478], cl ; Parallel printer timeout
9572 inc bx
9573 no_parport:
9574 ret
9576 ; serial port detection: base address in DX, index in BX, timeout in CL
9577 detect_serial:
9578 push dx
9579 inc dx
9580 mov al, #0x02
9581 out dx, al
9582 in al, dx
9583 cmp al, #0x02
9584 jne no_serial
9585 inc dx
9586 in al, dx
9587 cmp al, #0x02
9588 jne no_serial
9589 dec dx
9590 xor al, al
9591 out dx, al
9592 pop dx
9593 push bx
9594 shl bx, #1
9595 mov [bx+0x400], dx ; Serial I/O address
9596 pop bx
9597 mov [bx+0x47c], cl ; Serial timeout
9598 inc bx
9599 ret
9600 no_serial:
9601 pop dx
9602 ret
9604 rom_checksum:
9605 push ax
9606 push bx
9607 push cx
9608 xor ax, ax
9609 xor bx, bx
9610 xor cx, cx
9611 mov ch, [2]
9612 shl cx, #1
9613 checksum_loop:
9614 add al, [bx]
9615 inc bx
9616 loop checksum_loop
9617 and al, #0xff
9618 pop cx
9619 pop bx
9620 pop ax
9621 ret
9624 ;; We need a copy of this string, but we are not actually a PnP BIOS,
9625 ;; so make sure it is *not* aligned, so OSes will not see it if they scan.
9626 .align 16
9627 db 0
9628 pnp_string:
9629 .ascii "$PnP"
9632 rom_scan:
9633 ;; Scan for existence of valid expansion ROMS.
9634 ;; Video ROM: from 0xC0000..0xC7FFF in 2k increments
9635 ;; General ROM: from 0xC8000..0xDFFFF in 2k increments
9636 ;; System ROM: only 0xE0000
9637 ;;
9638 ;; Header:
9639 ;; Offset Value
9640 ;; 0 0x55
9641 ;; 1 0xAA
9642 ;; 2 ROM length in 512-byte blocks
9643 ;; 3 ROM initialization entry point (FAR CALL)
9645 #if BX_TCGBIOS
9646 call _tcpa_start_option_rom_scan /* specs: 3.2.3.3 + 10.4.3 */
9647 #endif
9648 mov cx, #0xc000
9649 rom_scan_loop:
9650 mov ds, cx
9651 mov ax, #0x0004 ;; start with increment of 4 (512-byte) blocks = 2k
9652 cmp [0], #0xAA55 ;; look for signature
9653 jne rom_scan_increment
9654 call rom_checksum
9655 jnz rom_scan_increment
9656 mov al, [2] ;; change increment to ROM length in 512-byte blocks
9658 ;; We want our increment in 512-byte quantities, rounded to
9659 ;; the nearest 2k quantity, since we only scan at 2k intervals.
9660 test al, #0x03
9661 jz block_count_rounded
9662 and al, #0xfc ;; needs rounding up
9663 add al, #0x04
9664 block_count_rounded:
9666 #if BX_TCGBIOS
9667 push ax
9668 push ds
9669 push ecx
9670 xor ax, ax
9671 mov ds, ax
9672 and ecx, #0xffff
9673 push ecx ;; segment where option rom is located at
9674 call _tcpa_option_rom /* specs: 3.2.3.3 */
9675 add sp, #4 ;; pop segment
9676 pop ecx ;; original ecx
9677 pop ds
9678 pop ax
9679 #endif
9680 push ax ;; Save AX
9681 push di ;; Save DI
9682 ;; Push addr of ROM entry point
9683 push cx ;; Push seg
9684 push #0x0003 ;; Push offset
9686 ;; Get the BDF into ax before invoking the option ROM
9687 mov bl, [2]
9688 mov al, bl
9689 shr al, #7
9690 cmp al, #1
9691 jne fetch_bdf
9692 mov ax, ds ;; Increment the DS since rom size larger than an segment
9693 add ax, #0x1000
9694 mov ds, ax
9695 fetch_bdf:
9696 shl bx, #9
9697 xor ax, ax
9698 mov al, [bx]
9700 ;; Point ES:DI at "$PnP", which tells the ROM that we are a PnP BIOS.
9701 ;; That should stop it grabbing INT 19h; we will use its BEV instead.
9702 mov bx, #0xf000
9703 mov es, bx
9704 lea di, pnp_string
9706 xor bx, bx ;; Restore DS back to 0000:
9707 mov ds, bx
9709 mov bp, sp ;; Call ROM init routine using seg:off on stack
9710 db 0xff ;; call_far ss:[bp+0]
9711 db 0x5e
9712 db 0
9713 cli ;; In case expansion ROM BIOS turns IF on
9714 add sp, #2 ;; Pop offset value
9715 pop cx ;; Pop seg value (restore CX)
9717 ;; Look at the ROM's PnP Expansion header. Properly, we're supposed
9718 ;; to init all the ROMs and then go back and build an IPL table of
9719 ;; all the bootable devices, but we can get away with one pass.
9720 mov ds, cx ;; ROM base
9721 mov bx, 0x001a ;; 0x1A is the offset into ROM header that contains...
9722 mov ax, [bx] ;; the offset of PnP expansion header, where...
9723 cmp ax, #0x5024 ;; we look for signature "$PnP"
9724 jne no_bev
9725 mov ax, 2[bx]
9726 cmp ax, #0x506e
9727 jne no_bev
9729 mov ax, 0x16[bx] ;; 0x16 is the offset of Boot Connection Vector
9730 cmp ax, #0x0000
9731 je no_bcv
9733 ;; Option ROM has BCV. Run it now.
9734 push cx ;; Push seg
9735 push ax ;; Push offset
9737 ;; Point ES:DI at "$PnP", which tells the ROM that we are a PnP BIOS.
9738 mov bx, #0xf000
9739 mov es, bx
9740 lea di, pnp_string
9741 /* jump to BCV function entry pointer */
9742 mov bp, sp ;; Call ROM BCV routine using seg:off on stack
9743 db 0xff ;; call_far ss:[bp+0]
9744 db 0x5e
9745 db 0
9746 cli ;; In case expansion ROM BIOS turns IF on
9747 add sp, #2 ;; Pop offset value
9748 pop cx ;; Pop seg value (restore CX)
9749 jmp no_bev
9751 no_bcv:
9752 mov ax, 0x1a[bx] ;; 0x1A is also the offset into the expansion header of...
9753 cmp ax, #0x0000 ;; the Bootstrap Entry Vector, or zero if there is none.
9754 je no_bev
9756 ;; Found a device that thinks it can boot the system. Record its BEV.
9757 mov bx, #IPL_SEG ;; Go to the segment where the IPL table lives
9758 mov ds, bx
9759 mov bx, IPL_COUNT_OFFSET ;; Read the number of entries so far
9760 cmp bx, #IPL_TABLE_ENTRIES
9761 je no_bev ;; Get out if the table is full
9762 shl bx, #0x4 ;; Turn count into offset (entries are 16 bytes)
9763 mov 0[bx], #0x80 ;; This entry is a BEV device
9764 mov 6[bx], cx ;; Build a far pointer from the segment...
9765 mov 4[bx], ax ;; and the offset
9766 shr bx, #0x4 ;; Turn the offset back into a count
9767 inc bx ;; We have one more entry now
9768 mov IPL_COUNT_OFFSET, bx ;; Remember that.
9770 no_bev:
9771 pop di ;; Restore DI
9772 pop ax ;; Restore AX
9773 rom_scan_increment:
9774 shl ax, #5 ;; convert 512-bytes blocks to 16-byte increments
9775 ;; because the segment selector is shifted left 4 bits.
9776 add cx, ax
9777 cmp cx, #0xe000
9778 jbe rom_scan_loop
9780 xor ax, ax ;; Restore DS back to 0000:
9781 mov ds, ax
9782 ret
9784 #ifdef HVMASSIST
9786 ; Copy the SMBIOS entry point from where hvmloader left it.
9787 ; The entry point must be somewhere in 0xf0000-0xfffff on a 16-byte boundary,
9788 ; but the tables themselves can be elsewhere.
9789 smbios_init:
9790 push ax
9791 push cx
9792 push es
9793 push ds
9794 push di
9795 push si
9797 mov cx, #0x001f ; 0x1f bytes to copy
9798 mov ax, #0xf000
9799 mov es, ax ; destination segment is 0xf0000
9800 mov di, #smbios_entry_point ; destination offset
9801 mov ax, #(SMBIOS_PHYSICAL_ADDRESS>>4)
9802 mov ds, ax
9803 mov si, #(SMBIOS_PHYSICAL_ADDRESS&15)
9804 cld
9805 rep
9806 movsb
9808 pop si
9809 pop di
9810 pop ds
9811 pop es
9812 pop cx
9813 pop ax
9815 ret
9817 #endif
9819 #if BX_TCGBIOS
9820 ; The section between the POST entry and the NMI entry is filling up
9821 ; and causes crashes if this code was directly there
9822 tcpa_post_part1:
9823 call _tcpa_acpi_init
9825 push dword #0
9826 call _tcpa_initialize_tpm
9827 add sp, #4
9829 call _tcpa_do_measure_POSTs
9830 call _tcpa_wake_event /* specs: 3.2.3.7 */
9831 ret
9833 tcpa_post_part2:
9834 call _tcpa_calling_int19h /* specs: 8.2.3 step 1 */
9835 call _tcpa_add_event_separators /* specs: 8.2.3 step 2 */
9836 /* we do not call int 19h handler but keep following eventlog */
9837 call _tcpa_returned_int19h /* specs: 8.2.3 step 3/7 */
9838 ret
9839 #endif
9842 ;; for 'C' strings and other data, insert them here with
9843 ;; a the following hack:
9844 ;; DATA_SEG_DEFS_HERE
9847 ;--------
9848 ;- POST -
9849 ;--------
9850 .org 0xe05b ; POST Entry Point
9851 post:
9853 xor ax, ax
9855 ;; first reset the DMA controllers
9856 out 0x0d,al
9857 out 0xda,al
9859 ;; then initialize the DMA controllers
9860 mov al, #0xC0
9861 out 0xD6, al ; cascade mode of channel 4 enabled
9862 mov al, #0x00
9863 out 0xD4, al ; unmask channel 4
9865 ;; Examine CMOS shutdown status.
9866 mov AL, #0x0f
9867 out 0x70, AL
9868 in AL, 0x71
9870 ;; backup status
9871 mov bl, al
9873 ;; Reset CMOS shutdown status.
9874 mov AL, #0x0f
9875 out 0x70, AL ; select CMOS register Fh
9876 mov AL, #0x00
9877 out 0x71, AL ; set shutdown action to normal
9879 ;; Examine CMOS shutdown status.
9880 mov al, bl
9881 mov dx, #EBDA_SEG
9882 mov ds, dx
9883 mov [EBDA_CMOS_SHUTDOWN_STATUS_OFFSET], AL
9885 cli
9886 mov ax, #0xfffe
9887 mov sp, ax
9888 mov ax, #0x0000
9889 mov ds, ax
9890 mov ss, ax
9892 ;; zero out BIOS data area (40:00..40:ff)
9893 mov es, ax
9894 mov cx, #0x0080 ;; 128 words
9895 mov di, #0x0400
9896 cld
9897 rep
9898 stosw
9900 call _log_bios_start
9902 ;; set all interrupts to default handler
9903 mov bx, #0x0000 ;; offset index
9904 mov cx, #0x0100 ;; counter (256 interrupts)
9905 mov ax, #dummy_iret_handler
9906 mov dx, #0xF000
9908 post_default_ints:
9909 mov [bx], ax
9910 inc bx
9911 inc bx
9912 mov [bx], dx
9913 inc bx
9914 inc bx
9915 loop post_default_ints
9917 ;; set vector 0x79 to zero
9918 ;; this is used by 'gardian angel' protection system
9919 SET_INT_VECTOR(0x79, #0, #0)
9921 ;; base memory in K 40:13 (word)
9922 mov ax, #BASE_MEM_IN_K
9923 mov 0x0413, ax
9926 ;; Manufacturing Test 40:12
9927 ;; zerod out above
9929 ;; Warm Boot Flag 0040:0072
9930 ;; value of 1234h = skip memory checks
9931 ;; zerod out above
9934 ;; Printer Services vector
9935 SET_INT_VECTOR(0x17, #0xF000, #int17_handler)
9937 ;; Bootstrap failure vector
9938 SET_INT_VECTOR(0x18, #0xF000, #int18_handler)
9940 ;; Bootstrap Loader vector
9941 SET_INT_VECTOR(0x19, #0xF000, #int19_handler)
9943 ;; User Timer Tick vector
9944 SET_INT_VECTOR(0x1c, #0xF000, #int1c_handler)
9946 ;; Memory Size Check vector
9947 SET_INT_VECTOR(0x12, #0xF000, #int12_handler)
9949 ;; Equipment Configuration Check vector
9950 SET_INT_VECTOR(0x11, #0xF000, #int11_handler)
9952 ;; System Services
9953 SET_INT_VECTOR(0x15, #0xF000, #int15_handler)
9955 ;; EBDA setup
9956 call ebda_post
9958 ;; PIT setup
9959 SET_INT_VECTOR(0x08, #0xF000, #int08_handler)
9960 ;; int 1C already points at dummy_iret_handler (above)
9961 mov al, #0x34 ; timer0: binary count, 16bit count, mode 2
9962 out 0x43, al
9963 #ifdef HVMASSIST
9964 mov al, #0x0b ; #0xe90b = 20 Hz (temporary, until we fix xen/vmx support)
9965 out 0x40, al ; lsb
9966 mov al, #0xe9
9967 out 0x40, al ; msb
9968 #else
9969 mov al, #0x00 ; maximum count of 0000H = 18.2Hz
9970 out 0x40, al
9971 out 0x40, al
9972 #endif
9974 ;; Keyboard
9975 SET_INT_VECTOR(0x09, #0xF000, #int09_handler)
9976 SET_INT_VECTOR(0x16, #0xF000, #int16_handler)
9978 xor ax, ax
9979 mov ds, ax
9980 mov 0x0417, al /* keyboard shift flags, set 1 */
9981 mov 0x0418, al /* keyboard shift flags, set 2 */
9982 mov 0x0419, al /* keyboard alt-numpad work area */
9983 mov 0x0471, al /* keyboard ctrl-break flag */
9984 mov 0x0497, al /* keyboard status flags 4 */
9985 mov al, #0x10
9986 mov 0x0496, al /* keyboard status flags 3 */
9989 /* keyboard head of buffer pointer */
9990 mov bx, #0x001E
9991 mov 0x041A, bx
9993 /* keyboard end of buffer pointer */
9994 mov 0x041C, bx
9996 /* keyboard pointer to start of buffer */
9997 mov bx, #0x001E
9998 mov 0x0480, bx
10000 /* keyboard pointer to end of buffer */
10001 mov bx, #0x003E
10002 mov 0x0482, bx
10004 /* init the keyboard */
10005 call _keyboard_init
10007 ;; mov CMOS Equipment Byte to BDA Equipment Word
10008 mov ax, 0x0410
10009 mov al, #0x14
10010 out 0x70, al
10011 in al, 0x71
10012 mov 0x0410, ax
10014 #if BX_TCGBIOS
10015 call tcpa_post_part1
10016 #endif
10018 ;; Parallel setup
10019 SET_INT_VECTOR(0x0F, #0xF000, #dummy_iret_handler)
10020 xor ax, ax
10021 mov ds, ax
10022 xor bx, bx
10023 mov cl, #0x14 ; timeout value
10024 mov dx, #0x378 ; Parallel I/O address, port 1
10025 call detect_parport
10026 mov dx, #0x278 ; Parallel I/O address, port 2
10027 call detect_parport
10028 shl bx, #0x0e
10029 mov ax, 0x410 ; Equipment word bits 14..15 determing # parallel ports
10030 and ax, #0x3fff
10031 or ax, bx ; set number of parallel ports
10032 mov 0x410, ax
10034 ;; Serial setup
10035 SET_INT_VECTOR(0x0C, #0xF000, #dummy_iret_handler)
10036 SET_INT_VECTOR(0x14, #0xF000, #int14_handler)
10037 xor bx, bx
10038 mov cl, #0x0a ; timeout value
10039 mov dx, #0x03f8 ; Serial I/O address, port 1
10040 call detect_serial
10041 mov dx, #0x02f8 ; Serial I/O address, port 2
10042 call detect_serial
10043 mov dx, #0x03e8 ; Serial I/O address, port 3
10044 call detect_serial
10045 mov dx, #0x02e8 ; Serial I/O address, port 4
10046 call detect_serial
10047 shl bx, #0x09
10048 mov ax, 0x410 ; Equipment word bits 9..11 determing # serial ports
10049 and ax, #0xf1ff
10050 or ax, bx ; set number of serial port
10051 mov 0x410, ax
10053 ;; CMOS RTC
10054 SET_INT_VECTOR(0x1A, #0xF000, #int1a_handler)
10055 SET_INT_VECTOR(0x4A, #0xF000, #dummy_iret_handler)
10056 SET_INT_VECTOR(0x70, #0xF000, #int70_handler)
10057 ;; BIOS DATA AREA 0x4CE ???
10058 call timer_tick_post
10060 ;; PS/2 mouse setup
10061 SET_INT_VECTOR(0x74, #0xF000, #int74_handler)
10063 ;; IRQ13 (FPU exception) setup
10064 SET_INT_VECTOR(0x75, #0xF000, #int75_handler)
10066 ;; Video setup
10067 SET_INT_VECTOR(0x10, #0xF000, #int10_handler)
10069 ;; PIC
10070 mov al, #0x11 ; send initialisation commands
10071 out 0x20, al
10072 out 0xa0, al
10073 mov al, #0x08
10074 out 0x21, al
10075 mov al, #0x70
10076 out 0xa1, al
10077 mov al, #0x04
10078 out 0x21, al
10079 mov al, #0x02
10080 out 0xa1, al
10081 mov al, #0x01
10082 out 0x21, al
10083 out 0xa1, al
10084 mov al, #0xb8
10085 out 0x21, AL ;master pic: unmask IRQ 0, 1, 2, 6
10086 #if BX_USE_PS2_MOUSE
10087 mov al, #0x8f
10088 #else
10089 mov al, #0x9f
10090 #endif
10091 out 0xa1, AL ;slave pic: unmask IRQ 12, 13, 14
10093 #ifdef HVMASSIST
10094 call _enable_rom_write_access
10095 call _clobber_entry_point
10096 call _copy_e820_table
10097 call smbios_init
10098 call _disable_rom_write_access
10099 #endif
10101 call _init_boot_vectors
10103 call rom_scan
10105 call _print_bios_banner
10107 ;;
10108 ;; Floppy setup
10109 ;;
10110 call floppy_drive_post
10112 #if BX_USE_ATADRV
10114 ;;
10115 ;; Hard Drive setup
10116 ;;
10117 call hard_drive_post
10119 ;;
10120 ;; ATA/ATAPI driver setup
10121 ;;
10122 call _ata_init
10123 call _ata_detect
10124 ;;
10125 #else // BX_USE_ATADRV
10127 ;;
10128 ;; Hard Drive setup
10129 ;;
10130 call hard_drive_post
10132 #endif // BX_USE_ATADRV
10134 #if BX_ELTORITO_BOOT
10135 ;;
10136 ;; eltorito floppy/harddisk emulation from cd
10137 ;;
10138 call _cdemu_init
10139 ;;
10140 #endif // BX_ELTORITO_BOOT
10142 call _s3_resume
10143 call _interactive_bootkey
10145 #if BX_TCGBIOS
10146 call tcpa_post_part2
10147 #endif
10149 ;; Start the boot sequence. See the comments in int19_relocated
10150 ;; for why we use INT 18h instead of INT 19h here.
10151 int #0x18
10153 .org 0xe2c3 ; NMI Handler Entry Point
10154 nmi:
10155 ;; FIXME the NMI handler should not panic
10156 ;; but iret when called from int75 (fpu exception)
10157 call _nmi_handler_msg
10158 iret
10160 int75_handler:
10161 out 0xf0, al // clear irq13
10162 call eoi_both_pics // clear interrupt
10163 int 2 // legacy nmi call
10164 iret
10166 ;-------------------------------------------
10167 ;- INT 13h Fixed Disk Services Entry Point -
10168 ;-------------------------------------------
10169 .org 0xe3fe ; INT 13h Fixed Disk Services Entry Point
10170 int13_handler:
10171 //JMPL(int13_relocated)
10172 jmp int13_relocated
10174 .org 0xe401 ; Fixed Disk Parameter Table
10176 ;----------
10177 ;- INT19h -
10178 ;----------
10179 .org 0xe6f2 ; INT 19h Boot Load Service Entry Point
10180 int19_handler:
10182 jmp int19_relocated
10183 ;-------------------------------------------
10184 ;- System BIOS Configuration Data Table
10185 ;-------------------------------------------
10186 .org BIOS_CONFIG_TABLE
10187 db 0x08 ; Table size (bytes) -Lo
10188 db 0x00 ; Table size (bytes) -Hi
10189 db SYS_MODEL_ID
10190 db SYS_SUBMODEL_ID
10191 db BIOS_REVISION
10192 ; Feature byte 1
10193 ; b7: 1=DMA channel 3 used by hard disk
10194 ; b6: 1=2 interrupt controllers present
10195 ; b5: 1=RTC present
10196 ; b4: 1=BIOS calls int 15h/4Fh every key