direct-io.hg

view tools/firmware/rombios/rombios.c @ 15470:b01225c94f83

[HVM] ROMBIOS: reboot when we see Ctrl+Alt+Del
Signed-off-by: Tim Deegan <Tim.Deegan@xensource.com>
author Tim Deegan <Tim.Deegan@xensource.com>
date Fri Jul 06 11:17:01 2007 +0100 (2007-07-06)
parents 5754173c3d81
children
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 #include "../hvmloader/config.h"
31 #define HVMASSIST
32 #undef HVMTEST
34 // Xen full virtualization does not handle unaligned IO with page crossing.
35 // Disable 32-bit PIO as a workaround.
36 #undef NO_PIO32
39 // ROM BIOS compatability entry points:
40 // ===================================
41 // $e05b ; POST Entry Point
42 // $e2c3 ; NMI Handler Entry Point
43 // $e3fe ; INT 13h Fixed Disk Services Entry Point
44 // $e401 ; Fixed Disk Parameter Table
45 // $e6f2 ; INT 19h Boot Load Service Entry Point
46 // $e6f5 ; Configuration Data Table
47 // $e729 ; Baud Rate Generator Table
48 // $e739 ; INT 14h Serial Communications Service Entry Point
49 // $e82e ; INT 16h Keyboard Service Entry Point
50 // $e987 ; INT 09h Keyboard Service Entry Point
51 // $ec59 ; INT 13h Diskette Service Entry Point
52 // $ef57 ; INT 0Eh Diskette Hardware ISR Entry Point
53 // $efc7 ; Diskette Controller Parameter Table
54 // $efd2 ; INT 17h Printer Service Entry Point
55 // $f045 ; INT 10 Functions 0-Fh Entry Point
56 // $f065 ; INT 10h Video Support Service Entry Point
57 // $f0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
58 // $f841 ; INT 12h Memory Size Service Entry Point
59 // $f84d ; INT 11h Equipment List Service Entry Point
60 // $f859 ; INT 15h System Services Entry Point
61 // $fa6e ; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
62 // $fe6e ; INT 1Ah Time-of-day Service Entry Point
63 // $fea5 ; INT 08h System Timer ISR Entry Point
64 // $fef3 ; Initial Interrupt Vector Offsets Loaded by POST
65 // $ff53 ; IRET Instruction for Dummy Interrupt Handler
66 // $ff54 ; INT 05h Print Screen Service Entry Point
67 // $fff0 ; Power-up Entry Point
68 // $fff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
69 // $fffe ; System Model ID
71 // NOTES for ATA/ATAPI driver (cbbochs@free.fr)
72 // Features
73 // - supports up to 4 ATA interfaces
74 // - device/geometry detection
75 // - 16bits/32bits device access
76 // - pchs/lba access
77 // - datain/dataout/packet command support
78 //
79 // NOTES for El-Torito Boot (cbbochs@free.fr)
80 // - CD-ROM booting is only available if ATA/ATAPI Driver is available
81 // - Current code is only able to boot mono-session cds
82 // - Current code can not boot and emulate a hard-disk
83 // the bios will panic otherwise
84 // - Current code also use memory in EBDA segement.
85 // - I used cmos byte 0x3D to store extended information on boot-device
86 // - Code has to be modified modified to handle multiple cdrom drives
87 // - Here are the cdrom boot failure codes:
88 // 1 : no atapi device found
89 // 2 : no atapi cdrom found
90 // 3 : can not read cd - BRVD
91 // 4 : cd is not eltorito (BRVD)
92 // 5 : cd is not eltorito (ISO TAG)
93 // 6 : cd is not eltorito (ELTORITO TAG)
94 // 7 : can not read cd - boot catalog
95 // 8 : boot catalog : bad header
96 // 9 : boot catalog : bad platform
97 // 10 : boot catalog : bad signature
98 // 11 : boot catalog : bootable flag not set
99 // 12 : can not read cd - boot image
100 //
101 // ATA driver
102 // - EBDA segment.
103 // I used memory starting at 0x121 in the segment
104 // - the translation policy is defined in cmos regs 0x39 & 0x3a
105 //
106 // TODO :
107 //
108 // int74
109 // - needs to be reworked. Uses direct [bp] offsets. (?)
110 //
111 // int13:
112 // - f04 (verify sectors) isn't complete (?)
113 // - f02/03/04 should set current cyl,etc in BDA (?)
114 // - rewrite int13_relocated & clean up int13 entry code
115 //
116 // NOTES:
117 // - NMI access (bit7 of addr written to 70h)
118 //
119 // ATA driver
120 // - should handle the "don't detect" bit (cmos regs 0x3b & 0x3c)
121 // - could send the multiple-sector read/write commands
122 //
123 // El-Torito
124 // - Emulate a Hard-disk (currently only diskette can be emulated) see "FIXME ElTorito Harddisk"
125 // - Implement remaining int13_cdemu functions (as defined by El-Torito specs)
126 // - cdrom drive is hardcoded to ide 0 device 1 in several places. see "FIXME ElTorito Hardcoded"
127 // - int13 Fix DL when emulating a cd. In that case DL is decremented before calling real int13.
128 // This is ok. But DL should be reincremented afterwards.
129 // - Fix all "FIXME ElTorito Various"
130 // - should be able to boot any cdrom instead of the first one
131 //
132 // BCC Bug: find a generic way to handle the bug of #asm after an "if" (fixed in 0.16.7)
134 #define DEBUG_ROMBIOS 0
136 #define DEBUG_ATA 0
137 #define DEBUG_INT13_HD 0
138 #define DEBUG_INT13_CD 0
139 #define DEBUG_INT13_ET 0
140 #define DEBUG_INT13_FL 0
141 #define DEBUG_INT15 0
142 #define DEBUG_INT16 0
143 #define DEBUG_INT1A 0
144 #define DEBUG_INT74 0
145 #define DEBUG_APM 0
147 #define BX_CPU 3
148 #define BX_USE_PS2_MOUSE 1
149 #define BX_CALL_INT15_4F 1
150 #define BX_USE_EBDA 1
151 #define BX_SUPPORT_FLOPPY 1
152 #define BX_FLOPPY_ON_CNT 37 /* 2 seconds */
153 #define BX_PCIBIOS 1
154 #define BX_APM 1
156 #define BX_USE_ATADRV 1
157 #define BX_ELTORITO_BOOT 1
159 #define BX_TCGBIOS 0 /* main switch for TCG BIOS ext. */
161 #define BX_MAX_ATA_INTERFACES 4
162 #define BX_MAX_ATA_DEVICES (BX_MAX_ATA_INTERFACES*2)
164 #define BX_VIRTUAL_PORTS 1 /* normal output to Bochs ports */
165 #define BX_DEBUG_SERIAL 0 /* output to COM1 */
167 /* model byte 0xFC = AT */
168 #define SYS_MODEL_ID 0xFC
169 #define SYS_SUBMODEL_ID 0x00
170 #define BIOS_REVISION 1
171 #define BIOS_CONFIG_TABLE 0xe6f5
173 #ifndef BIOS_BUILD_DATE
174 # define BIOS_BUILD_DATE "06/23/99"
175 #endif
177 // 1K of base memory used for Extended Bios Data Area (EBDA)
178 // EBDA is used for PS/2 mouse support, and IDE BIOS, etc.
179 #define EBDA_SEG 0x9FC0
180 #define EBDA_SIZE 1 // In KiB
181 #define BASE_MEM_IN_K (640 - EBDA_SIZE)
183 // Define the application NAME
184 #ifdef HVMASSIST
185 # define BX_APPNAME "HVMAssist"
186 #elif PLEX86
187 # define BX_APPNAME "Plex86"
188 #else
189 # define BX_APPNAME "Bochs"
190 #endif
192 // Sanity Checks
193 #if BX_USE_ATADRV && BX_CPU<3
194 # error The ATA/ATAPI Driver can only to be used with a 386+ cpu
195 #endif
196 #if BX_USE_ATADRV && !BX_USE_EBDA
197 # error ATA/ATAPI Driver can only be used if EBDA is available
198 #endif
199 #if BX_ELTORITO_BOOT && !BX_USE_ATADRV
200 # error El-Torito Boot can only be use if ATA/ATAPI Driver is available
201 #endif
202 #if BX_PCIBIOS && BX_CPU<3
203 # error PCI BIOS can only be used with 386+ cpu
204 #endif
205 #if BX_APM && BX_CPU<3
206 # error APM BIOS can only be used with 386+ cpu
207 #endif
209 #ifndef BX_SMP_PROCESSORS
210 #define BX_SMP_PROCESSORS 1
211 # warning BX_SMP_PROCESSORS not defined, defaulting to 1
212 #endif
214 #define PANIC_PORT 0x400
215 #define PANIC_PORT2 0x401
216 #define INFO_PORT 0x402
217 #define DEBUG_PORT 0x403
219 // #20 is dec 20
220 // #$20 is hex 20 = 32
221 // #0x20 is hex 20 = 32
222 // LDA #$20
223 // JSR $E820
224 // LDD .i,S
225 // JSR $C682
226 // mov al, #$20
228 // all hex literals should be prefixed with '0x'
229 // grep "#[0-9a-fA-F][0-9a-fA-F]" rombios.c
230 // no mov SEG-REG, #value, must mov register into seg-reg
231 // grep -i "mov[ ]*.s" rombios.c
233 // This is for compiling with gcc2 and gcc3
234 #define ASM_START #asm
235 #define ASM_END #endasm
237 ASM_START
238 .rom
240 .org 0x0000
242 #if BX_CPU >= 3
243 use16 386
244 #else
245 use16 286
246 #endif
248 MACRO HALT
249 ;; the HALT macro is called with the line number of the HALT call.
250 ;; The line number is then sent to the PANIC_PORT, causing Bochs/Plex
251 ;; to print a BX_PANIC message. This will normally halt the simulation
252 ;; with a message such as "BIOS panic at rombios.c, line 4091".
253 ;; However, users can choose to make panics non-fatal and continue.
254 #if BX_VIRTUAL_PORTS
255 mov dx,#PANIC_PORT
256 mov ax,#?1
257 out dx,ax
258 #else
259 mov dx,#0x80
260 mov ax,#?1
261 out dx,al
262 #endif
263 MEND
265 MACRO JMP_AP
266 db 0xea
267 dw ?2
268 dw ?1
269 MEND
271 MACRO SET_INT_VECTOR
272 mov ax, ?3
273 mov ?1*4, ax
274 mov ax, ?2
275 mov ?1*4+2, ax
276 MEND
278 ASM_END
280 typedef unsigned char Bit8u;
281 typedef unsigned short Bit16u;
282 typedef unsigned short bx_bool;
283 typedef unsigned long Bit32u;
286 void memsetb(seg,offset,value,count);
287 void memcpyb(dseg,doffset,sseg,soffset,count);
288 void memcpyd(dseg,doffset,sseg,soffset,count);
290 // memset of count bytes
291 void
292 memsetb(seg,offset,value,count)
293 Bit16u seg;
294 Bit16u offset;
295 Bit16u value;
296 Bit16u count;
297 {
298 ASM_START
299 push bp
300 mov bp, sp
302 push ax
303 push cx
304 push es
305 push di
307 mov cx, 10[bp] ; count
308 cmp cx, #0x00
309 je memsetb_end
310 mov ax, 4[bp] ; segment
311 mov es, ax
312 mov ax, 6[bp] ; offset
313 mov di, ax
314 mov al, 8[bp] ; value
315 cld
316 rep
317 stosb
319 memsetb_end:
320 pop di
321 pop es
322 pop cx
323 pop ax
325 pop bp
326 ASM_END
327 }
329 // memcpy of count bytes
330 void
331 memcpyb(dseg,doffset,sseg,soffset,count)
332 Bit16u dseg;
333 Bit16u doffset;
334 Bit16u sseg;
335 Bit16u soffset;
336 Bit16u count;
337 {
338 ASM_START
339 push bp
340 mov bp, sp
342 push ax
343 push cx
344 push es
345 push di
346 push ds
347 push si
349 mov cx, 12[bp] ; count
350 cmp cx, #0x0000
351 je memcpyb_end
352 mov ax, 4[bp] ; dsegment
353 mov es, ax
354 mov ax, 6[bp] ; doffset
355 mov di, ax
356 mov ax, 8[bp] ; ssegment
357 mov ds, ax
358 mov ax, 10[bp] ; soffset
359 mov si, ax
360 cld
361 rep
362 movsb
364 memcpyb_end:
365 pop si
366 pop ds
367 pop di
368 pop es
369 pop cx
370 pop ax
372 pop bp
373 ASM_END
374 }
376 #if 0
377 // memcpy of count dword
378 void
379 memcpyd(dseg,doffset,sseg,soffset,count)
380 Bit16u dseg;
381 Bit16u doffset;
382 Bit16u sseg;
383 Bit16u soffset;
384 Bit16u count;
385 {
386 ASM_START
387 push bp
388 mov bp, sp
390 push ax
391 push cx
392 push es
393 push di
394 push ds
395 push si
397 mov cx, 12[bp] ; count
398 cmp cx, #0x0000
399 je memcpyd_end
400 mov ax, 4[bp] ; dsegment
401 mov es, ax
402 mov ax, 6[bp] ; doffset
403 mov di, ax
404 mov ax, 8[bp] ; ssegment
405 mov ds, ax
406 mov ax, 10[bp] ; soffset
407 mov si, ax
408 cld
409 rep
410 movsd
412 memcpyd_end:
413 pop si
414 pop ds
415 pop di
416 pop es
417 pop cx
418 pop ax
420 pop bp
421 ASM_END
422 }
423 #endif
425 // read_dword and write_dword functions
426 static Bit32u read_dword();
427 static void write_dword();
429 Bit32u
430 read_dword(seg, offset)
431 Bit16u seg;
432 Bit16u offset;
433 {
434 ASM_START
435 push bp
436 mov bp, sp
438 push bx
439 push ds
440 mov ax, 4[bp] ; segment
441 mov ds, ax
442 mov bx, 6[bp] ; offset
443 mov ax, [bx]
444 inc bx
445 inc bx
446 mov dx, [bx]
447 ;; ax = return value (word)
448 ;; dx = return value (word)
449 pop ds
450 pop bx
452 pop bp
453 ASM_END
454 }
456 void
457 write_dword(seg, offset, data)
458 Bit16u seg;
459 Bit16u offset;
460 Bit32u data;
461 {
462 ASM_START
463 push bp
464 mov bp, sp
466 push ax
467 push bx
468 push ds
469 mov ax, 4[bp] ; segment
470 mov ds, ax
471 mov bx, 6[bp] ; offset
472 mov ax, 8[bp] ; data word
473 mov [bx], ax ; write data word
474 inc bx
475 inc bx
476 mov ax, 10[bp] ; data word
477 mov [bx], ax ; write data word
478 pop ds
479 pop bx
480 pop ax
482 pop bp
483 ASM_END
484 }
486 // Bit32u (unsigned long) and long helper functions
487 ASM_START
489 ;; and function
490 landl:
491 landul:
492 SEG SS
493 and ax,[di]
494 SEG SS
495 and bx,2[di]
496 ret
498 ;; add function
499 laddl:
500 laddul:
501 SEG SS
502 add ax,[di]
503 SEG SS
504 adc bx,2[di]
505 ret
507 ;; cmp function
508 lcmpl:
509 lcmpul:
510 and eax, #0x0000FFFF
511 shl ebx, #16
512 add eax, ebx
513 shr ebx, #16
514 SEG SS
515 cmp eax, dword ptr [di]
516 ret
518 ;; sub function
519 lsubl:
520 lsubul:
521 SEG SS
522 sub ax,[di]
523 SEG SS
524 sbb bx,2[di]
525 ret
527 ;; mul function
528 lmull:
529 lmulul:
530 and eax, #0x0000FFFF
531 shl ebx, #16
532 add eax, ebx
533 SEG SS
534 mul eax, dword ptr [di]
535 mov ebx, eax
536 shr ebx, #16
537 ret
539 ;; dec function
540 ldecl:
541 ldecul:
542 SEG SS
543 dec dword ptr [bx]
544 ret
546 ;; or function
547 lorl:
548 lorul:
549 SEG SS
550 or ax,[di]
551 SEG SS
552 or bx,2[di]
553 ret
555 ;; inc function
556 lincl:
557 lincul:
558 SEG SS
559 inc dword ptr [bx]
560 ret
562 ;; tst function
563 ltstl:
564 ltstul:
565 and eax, #0x0000FFFF
566 shl ebx, #16
567 add eax, ebx
568 shr ebx, #16
569 test eax, eax
570 ret
572 ;; sr function
573 lsrul:
574 mov cx,di
575 jcxz lsr_exit
576 and eax, #0x0000FFFF
577 shl ebx, #16
578 add eax, ebx
579 lsr_loop:
580 shr eax, #1
581 loop lsr_loop
582 mov ebx, eax
583 shr ebx, #16
584 lsr_exit:
585 ret
587 ;; sl function
588 lsll:
589 lslul:
590 mov cx,di
591 jcxz lsl_exit
592 and eax, #0x0000FFFF
593 shl ebx, #16
594 add eax, ebx
595 lsl_loop:
596 shl eax, #1
597 loop lsl_loop
598 mov ebx, eax
599 shr ebx, #16
600 lsl_exit:
601 ret
603 idiv_:
604 cwd
605 idiv bx
606 ret
608 idiv_u:
609 xor dx,dx
610 div bx
611 ret
613 ldivul:
614 and eax, #0x0000FFFF
615 shl ebx, #16
616 add eax, ebx
617 xor edx, edx
618 SEG SS
619 mov bx, 2[di]
620 shl ebx, #16
621 SEG SS
622 mov bx, [di]
623 div ebx
624 mov ebx, eax
625 shr ebx, #16
626 ret
628 ASM_END
630 // for access to RAM area which is used by interrupt vectors
631 // and BIOS Data Area
633 typedef struct {
634 unsigned char filler1[0x400];
635 unsigned char filler2[0x6c];
636 Bit16u ticks_low;
637 Bit16u ticks_high;
638 Bit8u midnight_flag;
639 } bios_data_t;
641 #define BiosData ((bios_data_t *) 0)
643 #if BX_USE_ATADRV
644 typedef struct {
645 Bit16u heads; // # heads
646 Bit16u cylinders; // # cylinders
647 Bit16u spt; // # sectors / track
648 } chs_t;
650 // DPTE definition
651 typedef struct {
652 Bit16u iobase1;
653 Bit16u iobase2;
654 Bit8u prefix;
655 Bit8u unused;
656 Bit8u irq;
657 Bit8u blkcount;
658 Bit8u dma;
659 Bit8u pio;
660 Bit16u options;
661 Bit16u reserved;
662 Bit8u revision;
663 Bit8u checksum;
664 } dpte_t;
666 typedef struct {
667 Bit8u iface; // ISA or PCI
668 Bit16u iobase1; // IO Base 1
669 Bit16u iobase2; // IO Base 2
670 Bit8u irq; // IRQ
671 } ata_channel_t;
673 typedef struct {
674 Bit8u type; // Detected type of ata (ata/atapi/none/unknown)
675 Bit8u device; // Detected type of attached devices (hd/cd/none)
676 Bit8u removable; // Removable device flag
677 Bit8u lock; // Locks for removable devices
678 // Bit8u lba_capable; // LBA capable flag - always yes for bochs devices
679 Bit8u mode; // transfert mode : PIO 16/32 bits - IRQ - ISADMA - PCIDMA
680 Bit16u blksize; // block size
682 Bit8u translation; // type of translation
683 chs_t lchs; // Logical CHS
684 chs_t pchs; // Physical CHS
686 Bit32u sectors; // Total sectors count
687 } ata_device_t;
689 typedef struct {
690 // ATA channels info
691 ata_channel_t channels[BX_MAX_ATA_INTERFACES];
693 // ATA devices info
694 ata_device_t devices[BX_MAX_ATA_DEVICES];
695 //
696 // map between (bios hd id - 0x80) and ata channels
697 Bit8u hdcount, hdidmap[BX_MAX_ATA_DEVICES];
699 // map between (bios cd id - 0xE0) and ata channels
700 Bit8u cdcount, cdidmap[BX_MAX_ATA_DEVICES];
702 // Buffer for DPTE table
703 dpte_t dpte;
705 // Count of transferred sectors and bytes
706 Bit16u trsfsectors;
707 Bit32u trsfbytes;
709 } ata_t;
711 #if BX_ELTORITO_BOOT
712 // ElTorito Device Emulation data
713 typedef struct {
714 Bit8u active;
715 Bit8u media;
716 Bit8u emulated_drive;
717 Bit8u controller_index;
718 Bit16u device_spec;
719 Bit32u ilba;
720 Bit16u buffer_segment;
721 Bit16u load_segment;
722 Bit16u sector_count;
724 // Virtual device
725 chs_t vdevice;
726 } cdemu_t;
727 #endif // BX_ELTORITO_BOOT
729 #include "32bitgateway.h"
731 // for access to EBDA area
732 // The EBDA structure should conform to
733 // http://www.cybertrails.com/~fys/rombios.htm document
734 // I made the ata and cdemu structs begin at 0x121 in the EBDA seg
735 // EBDA must be at most 768 bytes; it lives at 0x9fc00, and the boot
736 // device tables are at 0x9ff00 -- 0x9ffff
737 typedef struct {
738 unsigned char filler1[0x3D];
740 // FDPT - Can be splitted in data members if needed
741 unsigned char fdpt0[0x10];
742 unsigned char fdpt1[0x10];
744 unsigned char filler2[0xC4];
746 // ATA Driver data
747 ata_t ata;
749 #if BX_ELTORITO_BOOT
750 // El Torito Emulation data
751 cdemu_t cdemu;
752 #endif // BX_ELTORITO_BOOT
754 upcall_t upcall;
755 } ebda_data_t;
757 #define EbdaData ((ebda_data_t *) 0)
759 // for access to the int13ext structure
760 typedef struct {
761 Bit8u size;
762 Bit8u reserved;
763 Bit16u count;
764 Bit16u offset;
765 Bit16u segment;
766 Bit32u lba1;
767 Bit32u lba2;
768 } int13ext_t;
770 #define Int13Ext ((int13ext_t *) 0)
772 // Disk Physical Table definition
773 typedef struct {
774 Bit16u size;
775 Bit16u infos;
776 Bit32u cylinders;
777 Bit32u heads;
778 Bit32u spt;
779 Bit32u sector_count1;
780 Bit32u sector_count2;
781 Bit16u blksize;
782 Bit16u dpte_segment;
783 Bit16u dpte_offset;
784 Bit16u key;
785 Bit8u dpi_length;
786 Bit8u reserved1;
787 Bit16u reserved2;
788 Bit8u host_bus[4];
789 Bit8u iface_type[8];
790 Bit8u iface_path[8];
791 Bit8u device_path[8];
792 Bit8u reserved3;
793 Bit8u checksum;
794 } dpt_t;
796 #define Int13DPT ((dpt_t *) 0)
798 #endif // BX_USE_ATADRV
800 typedef struct {
801 union {
802 struct {
803 Bit16u di, si, bp, sp;
804 Bit16u bx, dx, cx, ax;
805 } r16;
806 struct {
807 Bit16u filler[4];
808 Bit8u bl, bh, dl, dh, cl, ch, al, ah;
809 } r8;
810 } u;
811 } pusha_regs_t;
813 typedef struct {
814 union {
815 struct {
816 Bit32u edi, esi, ebp, esp;
817 Bit32u ebx, edx, ecx, eax;
818 } r32;
819 struct {
820 Bit16u di, filler1, si, filler2, bp, filler3, sp, filler4;
821 Bit16u bx, filler5, dx, filler6, cx, filler7, ax, filler8;
822 } r16;
823 struct {
824 Bit32u filler[4];
825 Bit8u bl, bh;
826 Bit16u filler1;
827 Bit8u dl, dh;
828 Bit16u filler2;
829 Bit8u cl, ch;
830 Bit16u filler3;
831 Bit8u al, ah;
832 Bit16u filler4;
833 } r8;
834 } u;
835 } pushad_regs_t;
837 typedef struct {
838 union {
839 struct {
840 Bit16u flags;
841 } r16;
842 struct {
843 Bit8u flagsl;
844 Bit8u flagsh;
845 } r8;
846 } u;
847 } flags_t;
849 #define SetCF(x) x.u.r8.flagsl |= 0x01
850 #define SetZF(x) x.u.r8.flagsl |= 0x40
851 #define ClearCF(x) x.u.r8.flagsl &= 0xfe
852 #define ClearZF(x) x.u.r8.flagsl &= 0xbf
853 #define GetCF(x) (x.u.r8.flagsl & 0x01)
855 typedef struct {
856 Bit16u ip;
857 Bit16u cs;
858 flags_t flags;
859 } iret_addr_t;
863 static Bit8u inb();
864 static Bit8u inb_cmos();
865 static void outb();
866 static void outb_cmos();
867 static Bit16u inw();
868 static void outw();
869 static void init_rtc();
870 static bx_bool rtc_updating();
872 static Bit8u read_byte();
873 static Bit16u read_word();
874 static void write_byte();
875 static void write_word();
876 static void bios_printf();
877 static void copy_e820_table();
879 static Bit8u inhibit_mouse_int_and_events();
880 static void enable_mouse_int_and_events();
881 static Bit8u send_to_mouse_ctrl();
882 static Bit8u get_mouse_data();
883 static void set_kbd_command_byte();
885 static void int09_function();
886 static void int13_harddisk();
887 static void int13_cdrom();
888 static void int13_cdemu();
889 static void int13_eltorito();
890 static void int13_diskette_function();
891 static void int14_function();
892 static void int15_function();
893 static void int16_function();
894 static void int17_function();
895 static void int18_function();
896 static void int1a_function();
897 static void int70_function();
898 static void int74_function();
899 static Bit16u get_CS();
900 //static Bit16u get_DS();
901 //static void set_DS();
902 static Bit16u get_SS();
903 static unsigned int enqueue_key();
904 static unsigned int dequeue_key();
905 static void get_hd_geometry();
906 static void set_diskette_ret_status();
907 static void set_diskette_current_cyl();
908 static void determine_floppy_media();
909 static bx_bool floppy_drive_exists();
910 static bx_bool floppy_drive_recal();
911 static bx_bool floppy_media_known();
912 static bx_bool floppy_media_sense();
913 static bx_bool set_enable_a20();
914 static void debugger_on();
915 static void debugger_off();
916 static void keyboard_init();
917 static void keyboard_panic();
918 static void shutdown_status_panic();
919 static void nmi_handler_msg();
921 static void print_bios_banner();
922 static void print_boot_device();
923 static void print_boot_failure();
924 static void print_cdromboot_failure();
926 # if BX_USE_ATADRV
928 // ATA / ATAPI driver
929 void ata_init();
930 void ata_detect();
931 void ata_reset();
933 Bit16u ata_cmd_non_data();
934 Bit16u ata_cmd_data_in();
935 Bit16u ata_cmd_data_out();
936 Bit16u ata_cmd_packet();
938 Bit16u atapi_get_sense();
939 Bit16u atapi_is_ready();
940 Bit16u atapi_is_cdrom();
942 #endif // BX_USE_ATADRV
944 #if BX_ELTORITO_BOOT
946 void cdemu_init();
947 Bit8u cdemu_isactive();
948 Bit8u cdemu_emulated_drive();
950 Bit16u cdrom_boot();
952 #endif // BX_ELTORITO_BOOT
954 static char bios_cvs_version_string[] = "$Revision: 1.138 $";
955 static char bios_date_string[] = "$Date: 2005/05/07 15:55:26 $";
957 static char CVSID[] = "$Id: rombios.c,v 1.138 2005/05/07 15:55:26 vruppert Exp $";
959 /* Offset to skip the CVS $Id: prefix */
960 #define bios_version_string (CVSID + 4)
962 #define BIOS_PRINTF_HALT 1
963 #define BIOS_PRINTF_SCREEN 2
964 #define BIOS_PRINTF_INFO 4
965 #define BIOS_PRINTF_DEBUG 8
966 #define BIOS_PRINTF_ALL (BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO)
967 #define BIOS_PRINTF_DEBHALT (BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO | BIOS_PRINTF_HALT)
969 #define printf(format, p...) bios_printf(BIOS_PRINTF_SCREEN, format, ##p)
971 // Defines the output macros.
972 // BX_DEBUG goes to INFO port until we can easily choose debug info on a
973 // per-device basis. Debug info are sent only in debug mode
974 #if DEBUG_ROMBIOS
975 # define BX_DEBUG(format, p...) bios_printf(BIOS_PRINTF_INFO, format, ##p)
976 #else
977 # define BX_DEBUG(format, p...)
978 #endif
979 #define BX_INFO(format, p...) bios_printf(BIOS_PRINTF_INFO, format, ##p)
980 #define BX_PANIC(format, p...) bios_printf(BIOS_PRINTF_DEBHALT, format, ##p)
982 #if DEBUG_ATA
983 # define BX_DEBUG_ATA(a...) BX_DEBUG(a)
984 #else
985 # define BX_DEBUG_ATA(a...)
986 #endif
987 #if DEBUG_INT13_HD
988 # define BX_DEBUG_INT13_HD(a...) BX_DEBUG(a)
989 #else
990 # define BX_DEBUG_INT13_HD(a...)
991 #endif
992 #if DEBUG_INT13_CD
993 # define BX_DEBUG_INT13_CD(a...) BX_DEBUG(a)
994 #else
995 # define BX_DEBUG_INT13_CD(a...)
996 #endif
997 #if DEBUG_INT13_ET
998 # define BX_DEBUG_INT13_ET(a...) BX_DEBUG(a)
999 #else
1000 # define BX_DEBUG_INT13_ET(a...)
1001 #endif
1002 #if DEBUG_INT13_FL
1003 # define BX_DEBUG_INT13_FL(a...) BX_DEBUG(a)
1004 #else
1005 # define BX_DEBUG_INT13_FL(a...)
1006 #endif
1007 #if DEBUG_INT15
1008 # define BX_DEBUG_INT15(a...) BX_DEBUG(a)
1009 #else
1010 # define BX_DEBUG_INT15(a...)
1011 #endif
1012 #if DEBUG_INT16
1013 # define BX_DEBUG_INT16(a...) BX_DEBUG(a)
1014 #else
1015 # define BX_DEBUG_INT16(a...)
1016 #endif
1017 #if DEBUG_INT1A
1018 # define BX_DEBUG_INT1A(a...) BX_DEBUG(a)
1019 #else
1020 # define BX_DEBUG_INT1A(a...)
1021 #endif
1022 #if DEBUG_INT74
1023 # define BX_DEBUG_INT74(a...) BX_DEBUG(a)
1024 #else
1025 # define BX_DEBUG_INT74(a...)
1026 #endif
1028 #define SET_AL(val8) AX = ((AX & 0xff00) | (val8))
1029 #define SET_BL(val8) BX = ((BX & 0xff00) | (val8))
1030 #define SET_CL(val8) CX = ((CX & 0xff00) | (val8))
1031 #define SET_DL(val8) DX = ((DX & 0xff00) | (val8))
1032 #define SET_AH(val8) AX = ((AX & 0x00ff) | ((val8) << 8))
1033 #define SET_BH(val8) BX = ((BX & 0x00ff) | ((val8) << 8))
1034 #define SET_CH(val8) CX = ((CX & 0x00ff) | ((val8) << 8))
1035 #define SET_DH(val8) DX = ((DX & 0x00ff) | ((val8) << 8))
1037 #define GET_AL() ( AX & 0x00ff )
1038 #define GET_BL() ( BX & 0x00ff )
1039 #define GET_CL() ( CX & 0x00ff )
1040 #define GET_DL() ( DX & 0x00ff )
1041 #define GET_AH() ( AX >> 8 )
1042 #define GET_BH() ( BX >> 8 )
1043 #define GET_CH() ( CX >> 8 )
1044 #define GET_DH() ( DX >> 8 )
1046 #define GET_ELDL() ( ELDX & 0x00ff )
1047 #define GET_ELDH() ( ELDX >> 8 )
1049 #define SET_CF() FLAGS |= 0x0001
1050 #define CLEAR_CF() FLAGS &= 0xfffe
1051 #define GET_CF() (FLAGS & 0x0001)
1053 #define SET_ZF() FLAGS |= 0x0040
1054 #define CLEAR_ZF() FLAGS &= 0xffbf
1055 #define GET_ZF() (FLAGS & 0x0040)
1057 #define UNSUPPORTED_FUNCTION 0x86
1059 #define none 0
1060 #define MAX_SCAN_CODE 0x53
1062 static struct {
1063 Bit16u normal;
1064 Bit16u shift;
1065 Bit16u control;
1066 Bit16u alt;
1067 Bit8u lock_flags;
1068 } scan_to_scanascii[MAX_SCAN_CODE + 1] = {
1069 { none, none, none, none, none },
1070 { 0x011b, 0x011b, 0x011b, 0x0100, none }, /* escape */
1071 { 0x0231, 0x0221, none, 0x7800, none }, /* 1! */
1072 { 0x0332, 0x0340, 0x0300, 0x7900, none }, /* 2@ */
1073 { 0x0433, 0x0423, none, 0x7a00, none }, /* 3# */
1074 { 0x0534, 0x0524, none, 0x7b00, none }, /* 4$ */
1075 { 0x0635, 0x0625, none, 0x7c00, none }, /* 5% */
1076 { 0x0736, 0x075e, 0x071e, 0x7d00, none }, /* 6^ */
1077 { 0x0837, 0x0826, none, 0x7e00, none }, /* 7& */
1078 { 0x0938, 0x092a, none, 0x7f00, none }, /* 8* */
1079 { 0x0a39, 0x0a28, none, 0x8000, none }, /* 9( */
1080 { 0x0b30, 0x0b29, none, 0x8100, none }, /* 0) */
1081 { 0x0c2d, 0x0c5f, 0x0c1f, 0x8200, none }, /* -_ */
1082 { 0x0d3d, 0x0d2b, none, 0x8300, none }, /* =+ */
1083 { 0x0e08, 0x0e08, 0x0e7f, none, none }, /* backspace */
1084 { 0x0f09, 0x0f00, none, none, none }, /* tab */
1085 { 0x1071, 0x1051, 0x1011, 0x1000, 0x40 }, /* Q */
1086 { 0x1177, 0x1157, 0x1117, 0x1100, 0x40 }, /* W */
1087 { 0x1265, 0x1245, 0x1205, 0x1200, 0x40 }, /* E */
1088 { 0x1372, 0x1352, 0x1312, 0x1300, 0x40 }, /* R */
1089 { 0x1474, 0x1454, 0x1414, 0x1400, 0x40 }, /* T */
1090 { 0x1579, 0x1559, 0x1519, 0x1500, 0x40 }, /* Y */
1091 { 0x1675, 0x1655, 0x1615, 0x1600, 0x40 }, /* U */
1092 { 0x1769, 0x1749, 0x1709, 0x1700, 0x40 }, /* I */
1093 { 0x186f, 0x184f, 0x180f, 0x1800, 0x40 }, /* O */
1094 { 0x1970, 0x1950, 0x1910, 0x1900, 0x40 }, /* P */
1095 { 0x1a5b, 0x1a7b, 0x1a1b, none, none }, /* [{ */
1096 { 0x1b5d, 0x1b7d, 0x1b1d, none, none }, /* ]} */
1097 { 0x1c0d, 0x1c0d, 0x1c0a, none, none }, /* Enter */
1098 { none, none, none, none, none }, /* L Ctrl */
1099 { 0x1e61, 0x1e41, 0x1e01, 0x1e00, 0x40 }, /* A */
1100 { 0x1f73, 0x1f53, 0x1f13, 0x1f00, 0x40 }, /* S */
1101 { 0x2064, 0x2044, 0x2004, 0x2000, 0x40 }, /* D */
1102 { 0x2166, 0x2146, 0x2106, 0x2100, 0x40 }, /* F */
1103 { 0x2267, 0x2247, 0x2207, 0x2200, 0x40 }, /* G */
1104 { 0x2368, 0x2348, 0x2308, 0x2300, 0x40 }, /* H */
1105 { 0x246a, 0x244a, 0x240a, 0x2400, 0x40 }, /* J */
1106 { 0x256b, 0x254b, 0x250b, 0x2500, 0x40 }, /* K */
1107 { 0x266c, 0x264c, 0x260c, 0x2600, 0x40 }, /* L */
1108 { 0x273b, 0x273a, none, none, none }, /* ;: */
1109 { 0x2827, 0x2822, none, none, none }, /* '" */
1110 { 0x2960, 0x297e, none, none, none }, /* `~ */
1111 { none, none, none, none, none }, /* L shift */
1112 { 0x2b5c, 0x2b7c, 0x2b1c, none, none }, /* |\ */
1113 { 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00, 0x40 }, /* Z */
1114 { 0x2d78, 0x2d58, 0x2d18, 0x2d00, 0x40 }, /* X */
1115 { 0x2e63, 0x2e43, 0x2e03, 0x2e00, 0x40 }, /* C */
1116 { 0x2f76, 0x2f56, 0x2f16, 0x2f00, 0x40 }, /* V */
1117 { 0x3062, 0x3042, 0x3002, 0x3000, 0x40 }, /* B */
1118 { 0x316e, 0x314e, 0x310e, 0x3100, 0x40 }, /* N */
1119 { 0x326d, 0x324d, 0x320d, 0x3200, 0x40 }, /* M */
1120 { 0x332c, 0x333c, none, none, none }, /* ,< */
1121 { 0x342e, 0x343e, none, none, none }, /* .> */
1122 { 0x352f, 0x353f, none, none, none }, /* /? */
1123 { none, none, none, none, none }, /* R Shift */
1124 { 0x372a, 0x372a, none, none, none }, /* * */
1125 { none, none, none, none, none }, /* L Alt */
1126 { 0x3920, 0x3920, 0x3920, 0x3920, none }, /* space */
1127 { none, none, none, none, none }, /* caps lock */
1128 { 0x3b00, 0x5400, 0x5e00, 0x6800, none }, /* F1 */
1129 { 0x3c00, 0x5500, 0x5f00, 0x6900, none }, /* F2 */
1130 { 0x3d00, 0x5600, 0x6000, 0x6a00, none }, /* F3 */
1131 { 0x3e00, 0x5700, 0x6100, 0x6b00, none }, /* F4 */
1132 { 0x3f00, 0x5800, 0x6200, 0x6c00, none }, /* F5 */
1133 { 0x4000, 0x5900, 0x6300, 0x6d00, none }, /* F6 */
1134 { 0x4100, 0x5a00, 0x6400, 0x6e00, none }, /* F7 */
1135 { 0x4200, 0x5b00, 0x6500, 0x6f00, none }, /* F8 */
1136 { 0x4300, 0x5c00, 0x6600, 0x7000, none }, /* F9 */
1137 { 0x4400, 0x5d00, 0x6700, 0x7100, none }, /* F10 */
1138 { none, none, none, none, none }, /* Num Lock */
1139 { none, none, none, none, none }, /* Scroll Lock */
1140 { 0x4700, 0x4737, 0x7700, none, 0x20 }, /* 7 Home */
1141 { 0x4800, 0x4838, none, none, 0x20 }, /* 8 UP */
1142 { 0x4900, 0x4939, 0x8400, none, 0x20 }, /* 9 PgUp */
1143 { 0x4a2d, 0x4a2d, none, none, none }, /* - */
1144 { 0x4b00, 0x4b34, 0x7300, none, 0x20 }, /* 4 Left */
1145 { 0x4c00, 0x4c35, none, none, 0x20 }, /* 5 */
1146 { 0x4d00, 0x4d36, 0x7400, none, 0x20 }, /* 6 Right */
1147 { 0x4e2b, 0x4e2b, none, none, none }, /* + */
1148 { 0x4f00, 0x4f31, 0x7500, none, 0x20 }, /* 1 End */
1149 { 0x5000, 0x5032, none, none, 0x20 }, /* 2 Down */
1150 { 0x5100, 0x5133, 0x7600, none, 0x20 }, /* 3 PgDn */
1151 { 0x5200, 0x5230, none, none, 0x20 }, /* 0 Ins */
1152 { 0x5300, 0x532e, none, none, 0x20 } /* Del */
1153 };
1155 Bit8u
1156 inb(port)
1157 Bit16u port;
1159 ASM_START
1160 push bp
1161 mov bp, sp
1163 push dx
1164 mov dx, 4[bp]
1165 in al, dx
1166 pop dx
1168 pop bp
1169 ASM_END
1172 #if BX_USE_ATADRV
1173 Bit16u
1174 inw(port)
1175 Bit16u port;
1177 ASM_START
1178 push bp
1179 mov bp, sp
1181 push dx
1182 mov dx, 4[bp]
1183 in ax, dx
1184 pop dx
1186 pop bp
1187 ASM_END
1189 #endif
1191 void
1192 outb(port, val)
1193 Bit16u port;
1194 Bit8u val;
1196 ASM_START
1197 push bp
1198 mov bp, sp
1200 push ax
1201 push dx
1202 mov dx, 4[bp]
1203 mov al, 6[bp]
1204 out dx, al
1205 pop dx
1206 pop ax
1208 pop bp
1209 ASM_END
1212 #if BX_USE_ATADRV
1213 void
1214 outw(port, val)
1215 Bit16u port;
1216 Bit16u val;
1218 ASM_START
1219 push bp
1220 mov bp, sp
1222 push ax
1223 push dx
1224 mov dx, 4[bp]
1225 mov ax, 6[bp]
1226 out dx, ax
1227 pop dx
1228 pop ax
1230 pop bp
1231 ASM_END
1233 #endif
1235 void
1236 outb_cmos(cmos_reg, val)
1237 Bit8u cmos_reg;
1238 Bit8u val;
1240 ASM_START
1241 push bp
1242 mov bp, sp
1244 mov al, 4[bp] ;; cmos_reg
1245 out 0x70, al
1246 mov al, 6[bp] ;; val
1247 out 0x71, al
1249 pop bp
1250 ASM_END
1253 Bit8u
1254 inb_cmos(cmos_reg)
1255 Bit8u cmos_reg;
1257 ASM_START
1258 push bp
1259 mov bp, sp
1261 mov al, 4[bp] ;; cmos_reg
1262 out 0x70, al
1263 in al, 0x71
1265 pop bp
1266 ASM_END
1269 void
1270 init_rtc()
1272 outb_cmos(0x0a, 0x26);
1273 outb_cmos(0x0b, 0x02);
1274 inb_cmos(0x0c);
1275 inb_cmos(0x0d);
1278 bx_bool
1279 rtc_updating()
1281 // This function checks to see if the update-in-progress bit
1282 // is set in CMOS Status Register A. If not, it returns 0.
1283 // If it is set, it tries to wait until there is a transition
1284 // to 0, and will return 0 if such a transition occurs. A 1
1285 // is returned only after timing out. The maximum period
1286 // that this bit should be set is constrained to 244useconds.
1287 // The count I use below guarantees coverage or more than
1288 // this time, with any reasonable IPS setting.
1290 Bit16u count;
1292 count = 25000;
1293 while (--count != 0) {
1294 if ( (inb_cmos(0x0a) & 0x80) == 0 )
1295 return(0);
1297 return(1); // update-in-progress never transitioned to 0
1301 Bit8u
1302 read_byte(seg, offset)
1303 Bit16u seg;
1304 Bit16u offset;
1306 ASM_START
1307 push bp
1308 mov bp, sp
1310 push bx
1311 push ds
1312 mov ax, 4[bp] ; segment
1313 mov ds, ax
1314 mov bx, 6[bp] ; offset
1315 mov al, [bx]
1316 ;; al = return value (byte)
1317 pop ds
1318 pop bx
1320 pop bp
1321 ASM_END
1324 Bit16u
1325 read_word(seg, offset)
1326 Bit16u seg;
1327 Bit16u offset;
1329 ASM_START
1330 push bp
1331 mov bp, sp
1333 push bx
1334 push ds
1335 mov ax, 4[bp] ; segment
1336 mov ds, ax
1337 mov bx, 6[bp] ; offset
1338 mov ax, [bx]
1339 ;; ax = return value (word)
1340 pop ds
1341 pop bx
1343 pop bp
1344 ASM_END
1347 void
1348 write_byte(seg, offset, data)
1349 Bit16u seg;
1350 Bit16u offset;
1351 Bit8u data;
1353 ASM_START
1354 push bp
1355 mov bp, sp
1357 push ax
1358 push bx
1359 push ds
1360 mov ax, 4[bp] ; segment
1361 mov ds, ax
1362 mov bx, 6[bp] ; offset
1363 mov al, 8[bp] ; data byte
1364 mov [bx], al ; write data byte
1365 pop ds
1366 pop bx
1367 pop ax
1369 pop bp
1370 ASM_END
1373 void
1374 write_word(seg, offset, data)
1375 Bit16u seg;
1376 Bit16u offset;
1377 Bit16u data;
1379 ASM_START
1380 push bp
1381 mov bp, sp
1383 push ax
1384 push bx
1385 push ds
1386 mov ax, 4[bp] ; segment
1387 mov ds, ax
1388 mov bx, 6[bp] ; offset
1389 mov ax, 8[bp] ; data word
1390 mov [bx], ax ; write data word
1391 pop ds
1392 pop bx
1393 pop ax
1395 pop bp
1396 ASM_END
1399 Bit16u
1400 get_CS()
1402 ASM_START
1403 mov ax, cs
1404 ASM_END
1407 // Bit16u
1408 //get_DS()
1409 //{
1410 //ASM_START
1411 // mov ax, ds
1412 //ASM_END
1413 //}
1414 //
1415 // void
1416 //set_DS(ds_selector)
1417 // Bit16u ds_selector;
1418 //{
1419 //ASM_START
1420 // push bp
1421 // mov bp, sp
1422 //
1423 // push ax
1424 // mov ax, 4[bp] ; ds_selector
1425 // mov ds, ax
1426 // pop ax
1427 //
1428 // pop bp
1429 //ASM_END
1430 //}
1432 Bit16u
1433 get_SS()
1435 ASM_START
1436 mov ax, ss
1437 ASM_END
1440 #ifdef HVMASSIST
1441 void
1442 copy_e820_table()
1444 Bit8u nr_entries = read_byte(0x9000, 0x1e8);
1445 Bit32u base_mem;
1446 if (nr_entries > 32)
1447 nr_entries = 32;
1448 write_word(0xe000, 0x8, nr_entries);
1449 memcpyb(0xe000, 0x10, 0x9000, 0x2d0, nr_entries * 0x14);
1450 /* Report the proper base memory size at address 0x0413: otherwise
1451 * non-e820 code will clobber things if BASE_MEM_IN_K is bigger than
1452 * the first e820 entry. Get the size by reading the second 64bit
1453 * field of the first e820 slot. */
1454 base_mem = read_dword(0x9000, 0x2d0 + 8);
1455 write_word(0x40, 0x13, base_mem >> 10);
1457 #endif /* HVMASSIST */
1459 #if BX_DEBUG_SERIAL
1460 /* serial debug port*/
1461 #define BX_DEBUG_PORT 0x03f8
1463 /* data */
1464 #define UART_RBR 0x00
1465 #define UART_THR 0x00
1467 /* control */
1468 #define UART_IER 0x01
1469 #define UART_IIR 0x02
1470 #define UART_FCR 0x02
1471 #define UART_LCR 0x03
1472 #define UART_MCR 0x04
1473 #define UART_DLL 0x00
1474 #define UART_DLM 0x01
1476 /* status */
1477 #define UART_LSR 0x05
1478 #define UART_MSR 0x06
1479 #define UART_SCR 0x07
1481 int uart_can_tx_byte(base_port)
1482 Bit16u base_port;
1484 return inb(base_port + UART_LSR) & 0x20;
1487 void uart_wait_to_tx_byte(base_port)
1488 Bit16u base_port;
1490 while (!uart_can_tx_byte(base_port));
1493 void uart_wait_until_sent(base_port)
1494 Bit16u base_port;
1496 while (!(inb(base_port + UART_LSR) & 0x40));
1499 void uart_tx_byte(base_port, data)
1500 Bit16u base_port;
1501 Bit8u data;
1503 uart_wait_to_tx_byte(base_port);
1504 outb(base_port + UART_THR, data);
1505 uart_wait_until_sent(base_port);
1507 #endif
1509 void
1510 wrch(c)
1511 Bit8u c;
1513 ASM_START
1514 push bp
1515 mov bp, sp
1517 push bx
1518 mov ah, #0x0e
1519 mov al, 4[bp]
1520 xor bx,bx
1521 int #0x10
1522 pop bx
1524 pop bp
1525 ASM_END
1528 void
1529 send(action, c)
1530 Bit16u action;
1531 Bit8u c;
1533 #if BX_DEBUG_SERIAL
1534 if (c == '\n') uart_tx_byte(BX_DEBUG_PORT, '\r');
1535 uart_tx_byte(BX_DEBUG_PORT, c);
1536 #endif
1537 #ifdef HVMASSIST
1538 outb(0xE9, c);
1539 #endif
1540 #if BX_VIRTUAL_PORTS
1541 if (action & BIOS_PRINTF_DEBUG) outb(DEBUG_PORT, c);
1542 if (action & BIOS_PRINTF_INFO) outb(INFO_PORT, c);
1543 #endif
1544 if (action & BIOS_PRINTF_SCREEN) {
1545 if (c == '\n') wrch('\r');
1546 wrch(c);
1550 void
1551 put_int(action, val, width, neg)
1552 Bit16u action;
1553 short val, width;
1554 bx_bool neg;
1556 short nval = val / 10;
1557 if (nval)
1558 put_int(action, nval, width - 1, neg);
1559 else {
1560 while (--width > 0) send(action, ' ');
1561 if (neg) send(action, '-');
1563 send(action, val - (nval * 10) + '0');
1566 void
1567 put_uint(action, val, width, neg)
1568 Bit16u action;
1569 unsigned short val;
1570 short width;
1571 bx_bool neg;
1573 unsigned short nval = val / 10;
1574 if (nval)
1575 put_uint(action, nval, width - 1, neg);
1576 else {
1577 while (--width > 0) send(action, ' ');
1578 if (neg) send(action, '-');
1580 send(action, val - (nval * 10) + '0');
1583 //--------------------------------------------------------------------------
1584 // bios_printf()
1585 // A compact variable argument printf function which prints its output via
1586 // an I/O port so that it can be logged by Bochs/Plex.
1587 // Currently, only %x is supported (or %02x, %04x, etc).
1588 //
1589 // Supports %[format_width][format]
1590 // where format can be d,x,c,s
1591 //--------------------------------------------------------------------------
1592 void
1593 bios_printf(action, s)
1594 Bit16u action;
1595 Bit8u *s;
1597 Bit8u c, format_char;
1598 bx_bool in_format;
1599 short i;
1600 Bit16u *arg_ptr;
1601 Bit16u arg_seg, arg, nibble, shift_count, format_width;
1603 arg_ptr = &s;
1604 arg_seg = get_SS();
1606 in_format = 0;
1607 format_width = 0;
1609 if ((action & BIOS_PRINTF_DEBHALT) == BIOS_PRINTF_DEBHALT) {
1610 #if BX_VIRTUAL_PORTS
1611 outb(PANIC_PORT2, 0x00);
1612 #endif
1613 bios_printf (BIOS_PRINTF_SCREEN, "FATAL: ");
1616 while (c = read_byte(get_CS(), s)) {
1617 if ( c == '%' ) {
1618 in_format = 1;
1619 format_width = 0;
1621 else if (in_format) {
1622 if ( (c>='0') && (c<='9') ) {
1623 format_width = (format_width * 10) + (c - '0');
1625 else {
1626 arg_ptr++; // increment to next arg
1627 arg = read_word(arg_seg, arg_ptr);
1628 if (c == 'x') {
1629 if (format_width == 0)
1630 format_width = 4;
1631 for (i=format_width-1; i>=0; i--) {
1632 nibble = (arg >> (4 * i)) & 0x000f;
1633 send (action, (nibble<=9)? (nibble+'0') : (nibble-10+'A'));
1636 else if (c == 'u') {
1637 put_uint(action, arg, format_width, 0);
1639 else if (c == 'd') {
1640 if (arg & 0x8000)
1641 put_int(action, -arg, format_width - 1, 1);
1642 else
1643 put_int(action, arg, format_width, 0);
1645 else if (c == 's') {
1646 bios_printf(action & (~BIOS_PRINTF_HALT), arg);
1648 else if (c == 'c') {
1649 send(action, arg);
1651 else
1652 BX_PANIC("bios_printf: unknown format\n");
1653 in_format = 0;
1656 else {
1657 send(action, c);
1659 s ++;
1662 if (action & BIOS_PRINTF_HALT) {
1663 // freeze in a busy loop.
1664 ASM_START
1665 cli
1666 halt2_loop:
1667 hlt
1668 jmp halt2_loop
1669 ASM_END
1673 //--------------------------------------------------------------------------
1674 // keyboard_init
1675 //--------------------------------------------------------------------------
1676 // this file is based on LinuxBIOS implementation of keyboard.c
1677 // could convert to #asm to gain space
1678 void
1679 keyboard_init()
1681 Bit16u max;
1683 /* ------------------- Flush buffers ------------------------*/
1684 /* Wait until buffer is empty */
1685 max=0xffff;
1686 while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
1688 /* flush incoming keys */
1689 max=0x2000;
1690 while (--max > 0) {
1691 outb(0x80, 0x00);
1692 if (inb(0x64) & 0x01) {
1693 inb(0x60);
1694 max = 0x2000;
1698 // Due to timer issues, and if the IPS setting is > 15000000,
1699 // the incoming keys might not be flushed here. That will
1700 // cause a panic a few lines below. See sourceforge bug report :
1701 // [ 642031 ] FATAL: Keyboard RESET error:993
1703 /* ------------------- controller side ----------------------*/
1704 /* send cmd = 0xAA, self test 8042 */
1705 outb(0x64, 0xaa);
1707 /* Wait until buffer is empty */
1708 max=0xffff;
1709 while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
1710 if (max==0x0) keyboard_panic(00);
1712 /* Wait for data */
1713 max=0xffff;
1714 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x01);
1715 if (max==0x0) keyboard_panic(01);
1717 /* read self-test result, 0x55 should be returned from 0x60 */
1718 if ((inb(0x60) != 0x55)){
1719 keyboard_panic(991);
1722 /* send cmd = 0xAB, keyboard interface test */
1723 outb(0x64,0xab);
1725 /* Wait until buffer is empty */
1726 max=0xffff;
1727 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x10);
1728 if (max==0x0) keyboard_panic(10);
1730 /* Wait for data */
1731 max=0xffff;
1732 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x11);
1733 if (max==0x0) keyboard_panic(11);
1735 /* read keyboard interface test result, */
1736 /* 0x00 should be returned form 0x60 */
1737 if ((inb(0x60) != 0x00)) {
1738 keyboard_panic(992);
1741 /* Enable Keyboard clock */
1742 outb(0x64,0xae);
1743 outb(0x64,0xa8);
1745 /* ------------------- keyboard side ------------------------*/
1746 /* reset kerboard and self test (keyboard side) */
1747 outb(0x60, 0xff);
1749 /* Wait until buffer is empty */
1750 max=0xffff;
1751 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x20);
1752 if (max==0x0) keyboard_panic(20);
1754 /* Wait for data */
1755 max=0xffff;
1756 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x21);
1757 if (max==0x0) keyboard_panic(21);
1759 /* keyboard should return ACK */
1760 if ((inb(0x60) != 0xfa)) {
1761 keyboard_panic(993);
1764 /* Wait for data */
1765 max=0xffff;
1766 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x31);
1767 if (max==0x0) keyboard_panic(31);
1769 if ((inb(0x60) != 0xaa)) {
1770 keyboard_panic(994);
1773 /* Disable keyboard */
1774 outb(0x60, 0xf5);
1776 /* Wait until buffer is empty */
1777 max=0xffff;
1778 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x40);
1779 if (max==0x0) keyboard_panic(40);
1781 /* Wait for data */
1782 max=0xffff;
1783 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x41);
1784 if (max==0x0) keyboard_panic(41);
1786 /* keyboard should return ACK */
1787 if ((inb(0x60) != 0xfa)) {
1788 keyboard_panic(995);
1791 /* Write Keyboard Mode */
1792 outb(0x64, 0x60);
1794 /* Wait until buffer is empty */
1795 max=0xffff;
1796 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x50);
1797 if (max==0x0) keyboard_panic(50);
1799 /* send cmd: scan code convert, disable mouse, enable IRQ 1 */
1800 outb(0x60, 0x61);
1802 /* Wait until buffer is empty */
1803 max=0xffff;
1804 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x60);
1805 if (max==0x0) keyboard_panic(60);
1807 /* Enable keyboard */
1808 outb(0x60, 0xf4);
1810 /* Wait until buffer is empty */
1811 max=0xffff;
1812 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x70);
1813 if (max==0x0) keyboard_panic(70);
1815 /* Wait for data */
1816 max=0xffff;
1817 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x71);
1818 if (max==0x0) keyboard_panic(70);
1820 /* keyboard should return ACK */
1821 if ((inb(0x60) != 0xfa)) {
1822 keyboard_panic(996);
1825 outb(0x80, 0x77);
1828 //--------------------------------------------------------------------------
1829 // keyboard_panic
1830 //--------------------------------------------------------------------------
1831 void
1832 keyboard_panic(status)
1833 Bit16u status;
1835 // If you're getting a 993 keyboard panic here,
1836 // please see the comment in keyboard_init
1838 BX_PANIC("Keyboard error:%u\n",status);
1841 //--------------------------------------------------------------------------
1842 // machine_reset
1843 //--------------------------------------------------------------------------
1844 void
1845 machine_reset()
1847 /* Frob the keyboard reset line to reset the processor */
1848 outb(0x64, 0x60); /* Map the flags register at data port (0x60) */
1849 outb(0x60, 0x14); /* Set the flags to system|disable */
1850 outb(0x64, 0xfe); /* Pulse output 0 (system reset) low */
1851 BX_PANIC("Couldn't reset the machine\n");
1854 //--------------------------------------------------------------------------
1855 // clobber_entry_point
1856 // Because PV drivers in HVM guests detach some of the emulated devices,
1857 // it is not safe to do a soft reboot by just dropping to real mode and
1858 // jumping at ffff:0000. -- the boot drives might have disappeared!
1859 // This rather foul function overwrites(!) the BIOS entry point
1860 // to point at machine-reset, which will cause the Xen tools to
1861 // rebuild the whole machine from scratch.
1862 //--------------------------------------------------------------------------
1863 void
1864 clobber_entry_point()
1866 /* The instruction at the entry point is one byte (0xea) for the
1867 * jump opcode, then two bytes of address, then two of segment.
1868 * Overwrite the address bytes.*/
1869 write_word(0xffff, 0x0001, machine_reset);
1873 //--------------------------------------------------------------------------
1874 // shutdown_status_panic
1875 // called when the shutdown statsu is not implemented, displays the status
1876 //--------------------------------------------------------------------------
1877 void
1878 shutdown_status_panic(status)
1879 Bit16u status;
1881 BX_PANIC("Unimplemented shutdown status: %02x\n",(Bit8u)status);
1884 //--------------------------------------------------------------------------
1885 // print_bios_banner
1886 // displays a the bios version
1887 //--------------------------------------------------------------------------
1888 void
1889 print_bios_banner()
1891 printf(BX_APPNAME" BIOS, %d cpu%s, ", BX_SMP_PROCESSORS, BX_SMP_PROCESSORS>1?"s":"");
1892 printf("%s %s\n", bios_cvs_version_string, bios_date_string);
1893 #if BX_TCGBIOS
1894 printf("TCG-enabled BIOS.\n");
1895 #endif
1896 printf("\n");
1900 //--------------------------------------------------------------------------
1901 // BIOS Boot Specification 1.0.1 compatibility
1902 //
1903 // Very basic support for the BIOS Boot Specification, which allows expansion
1904 // ROMs to register themselves as boot devices, instead of just stealing the
1905 // INT 19h boot vector.
1906 //
1907 // This is a hack: to do it properly requires a proper PnP BIOS and we aren't
1908 // one; we just lie to the option ROMs to make them behave correctly.
1909 // We also don't support letting option ROMs register as bootable disk
1910 // drives (BCVs), only as bootable devices (BEVs).
1911 //
1912 // http://www.phoenix.com/en/Customer+Services/White+Papers-Specs/pc+industry+specifications.htm
1913 //--------------------------------------------------------------------------
1915 /* 256 bytes at 0x9ff00 -- 0x9ffff is used for the IPL boot table. */
1916 #define IPL_SEG 0x9ff0
1917 #define IPL_TABLE_OFFSET 0x0000
1918 #define IPL_TABLE_ENTRIES 8
1919 #define IPL_COUNT_OFFSET 0x0080 /* u16: number of valid table entries */
1920 #define IPL_SEQUENCE_OFFSET 0x0082 /* u16: next boot device */
1922 struct ipl_entry {
1923 Bit16u type;
1924 Bit16u flags;
1925 Bit32u vector;
1926 Bit32u description;
1927 Bit32u reserved;
1928 };
1930 static void
1931 init_boot_vectors()
1933 struct ipl_entry e;
1934 Bit16u count = 0;
1935 Bit16u ss = get_SS();
1937 /* Clear out the IPL table. */
1938 memsetb(IPL_SEG, IPL_TABLE_OFFSET, 0, 0xff);
1940 /* Floppy drive */
1941 e.type = 1; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
1942 memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
1943 count++;
1945 /* First HDD */
1946 e.type = 2; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
1947 memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
1948 count++;
1950 #if BX_ELTORITO_BOOT
1951 /* CDROM */
1952 e.type = 3; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
1953 memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
1954 count++;
1955 #endif
1957 /* Remember how many devices we have */
1958 write_word(IPL_SEG, IPL_COUNT_OFFSET, count);
1959 /* Not tried booting anything yet */
1960 write_word(IPL_SEG, IPL_SEQUENCE_OFFSET, 0xffff);
1963 static Bit8u
1964 get_boot_vector(i, e)
1965 Bit16u i; struct ipl_entry *e;
1967 Bit16u count;
1968 Bit16u ss = get_SS();
1969 /* Get the count of boot devices, and refuse to overrun the array */
1970 count = read_word(IPL_SEG, IPL_COUNT_OFFSET);
1971 if (i >= count) return 0;
1972 /* OK to read this device */
1973 memcpyb(ss, e, IPL_SEG, IPL_TABLE_OFFSET + i * sizeof (*e), sizeof (*e));
1974 return 1;
1978 //--------------------------------------------------------------------------
1979 // print_boot_device
1980 // displays the boot device
1981 //--------------------------------------------------------------------------
1983 static char drivetypes[][10]={"", "Floppy","Hard Disk","CD-Rom", "Network"};
1985 void
1986 print_boot_device(type)
1987 Bit16u type;
1989 /* NIC appears as type 0x80 */
1990 if (type == 0x80 ) type = 0x4;
1991 if (type == 0 || type > 0x4) BX_PANIC("Bad drive type\n");
1992 printf("Booting from %s...\n", drivetypes[type]);
1995 //--------------------------------------------------------------------------
1996 // print_boot_failure
1997 // displays the reason why boot failed
1998 //--------------------------------------------------------------------------
1999 void
2000 print_boot_failure(type, reason)
2001 Bit16u type; Bit8u reason;
2003 if (type == 0 || type > 0x3) BX_PANIC("Bad drive type\n");
2005 printf("Boot from %s failed", drivetypes[type]);
2006 if (type < 4) {
2007 /* Report the reason too */
2008 if (reason==0)
2009 printf(": not a bootable disk");
2010 else
2011 printf(": could not read the boot disk");
2013 printf("\n");
2016 //--------------------------------------------------------------------------
2017 // print_cdromboot_failure
2018 // displays the reason why boot failed
2019 //--------------------------------------------------------------------------
2020 void
2021 print_cdromboot_failure( code )
2022 Bit16u code;
2024 bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, "CDROM boot failure code : %04x\n",code);
2026 return;
2029 void
2030 nmi_handler_msg()
2032 BX_PANIC("NMI Handler called\n");
2035 void
2036 int18_panic_msg()
2038 BX_PANIC("INT18: BOOT FAILURE\n");
2041 void
2042 log_bios_start()
2044 #if BX_DEBUG_SERIAL
2045 outb(BX_DEBUG_PORT+UART_LCR, 0x03); /* setup for serial logging: 8N1 */
2046 #endif
2047 BX_INFO("%s\n", bios_version_string);
2050 bx_bool
2051 set_enable_a20(val)
2052 bx_bool val;
2054 Bit8u oldval;
2056 // Use PS2 System Control port A to set A20 enable
2058 // get current setting first
2059 oldval = inb(0x92);
2061 // change A20 status
2062 if (val)
2063 outb(0x92, oldval | 0x02);
2064 else
2065 outb(0x92, oldval & 0xfd);
2067 return((oldval & 0x02) != 0);
2070 void
2071 debugger_on()
2073 outb(0xfedc, 0x01);
2076 void
2077 debugger_off()
2079 outb(0xfedc, 0x00);
2082 #if BX_USE_ATADRV
2084 // ---------------------------------------------------------------------------
2085 // Start of ATA/ATAPI Driver
2086 // ---------------------------------------------------------------------------
2088 // Global defines -- ATA register and register bits.
2089 // command block & control block regs
2090 #define ATA_CB_DATA 0 // data reg in/out pio_base_addr1+0
2091 #define ATA_CB_ERR 1 // error in pio_base_addr1+1
2092 #define ATA_CB_FR 1 // feature reg out pio_base_addr1+1
2093 #define ATA_CB_SC 2 // sector count in/out pio_base_addr1+2
2094 #define ATA_CB_SN 3 // sector number in/out pio_base_addr1+3
2095 #define ATA_CB_CL 4 // cylinder low in/out pio_base_addr1+4
2096 #define ATA_CB_CH 5 // cylinder high in/out pio_base_addr1+5
2097 #define ATA_CB_DH 6 // device head in/out pio_base_addr1+6
2098 #define ATA_CB_STAT 7 // primary status in pio_base_addr1+7
2099 #define ATA_CB_CMD 7 // command out pio_base_addr1+7
2100 #define ATA_CB_ASTAT 6 // alternate status in pio_base_addr2+6
2101 #define ATA_CB_DC 6 // device control out pio_base_addr2+6
2102 #define ATA_CB_DA 7 // device address in pio_base_addr2+7
2104 #define ATA_CB_ER_ICRC 0x80 // ATA Ultra DMA bad CRC
2105 #define ATA_CB_ER_BBK 0x80 // ATA bad block
2106 #define ATA_CB_ER_UNC 0x40 // ATA uncorrected error
2107 #define ATA_CB_ER_MC 0x20 // ATA media change
2108 #define ATA_CB_ER_IDNF 0x10 // ATA id not found
2109 #define ATA_CB_ER_MCR 0x08 // ATA media change request
2110 #define ATA_CB_ER_ABRT 0x04 // ATA command aborted
2111 #define ATA_CB_ER_NTK0 0x02 // ATA track 0 not found
2112 #define ATA_CB_ER_NDAM 0x01 // ATA address mark not found
2114 #define ATA_CB_ER_P_SNSKEY 0xf0 // ATAPI sense key (mask)
2115 #define ATA_CB_ER_P_MCR 0x08 // ATAPI Media Change Request
2116 #define ATA_CB_ER_P_ABRT 0x04 // ATAPI command abort
2117 #define ATA_CB_ER_P_EOM 0x02 // ATAPI End of Media
2118 #define ATA_CB_ER_P_ILI 0x01 // ATAPI Illegal Length Indication
2120 // ATAPI Interrupt Reason bits in the Sector Count reg (CB_SC)
2121 #define ATA_CB_SC_P_TAG 0xf8 // ATAPI tag (mask)
2122 #define ATA_CB_SC_P_REL 0x04 // ATAPI release
2123 #define ATA_CB_SC_P_IO 0x02 // ATAPI I/O
2124 #define ATA_CB_SC_P_CD 0x01 // ATAPI C/D
2126 // bits 7-4 of the device/head (CB_DH) reg
2127 #define ATA_CB_DH_DEV0 0xa0 // select device 0
2128 #define ATA_CB_DH_DEV1 0xb0 // select device 1
2130 // status reg (CB_STAT and CB_ASTAT) bits
2131 #define ATA_CB_STAT_BSY 0x80 // busy
2132 #define ATA_CB_STAT_RDY 0x40 // ready
2133 #define ATA_CB_STAT_DF 0x20 // device fault
2134 #define ATA_CB_STAT_WFT 0x20 // write fault (old name)
2135 #define ATA_CB_STAT_SKC 0x10 // seek complete
2136 #define ATA_CB_STAT_SERV 0x10 // service
2137 #define ATA_CB_STAT_DRQ 0x08 // data request
2138 #define ATA_CB_STAT_CORR 0x04 // corrected
2139 #define ATA_CB_STAT_IDX 0x02 // index
2140 #define ATA_CB_STAT_ERR 0x01 // error (ATA)
2141 #define ATA_CB_STAT_CHK 0x01 // check (ATAPI)
2143 // device control reg (CB_DC) bits
2144 #define ATA_CB_DC_HD15 0x08 // bit should always be set to one
2145 #define ATA_CB_DC_SRST 0x04 // soft reset
2146 #define ATA_CB_DC_NIEN 0x02 // disable interrupts
2148 // Most mandtory and optional ATA commands (from ATA-3),
2149 #define ATA_CMD_CFA_ERASE_SECTORS 0xC0
2150 #define ATA_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03
2151 #define ATA_CMD_CFA_TRANSLATE_SECTOR 0x87
2152 #define ATA_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD
2153 #define ATA_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38
2154 #define ATA_CMD_CHECK_POWER_MODE1 0xE5
2155 #define ATA_CMD_CHECK_POWER_MODE2 0x98
2156 #define ATA_CMD_DEVICE_RESET 0x08
2157 #define ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90
2158 #define ATA_CMD_FLUSH_CACHE 0xE7
2159 #define ATA_CMD_FORMAT_TRACK 0x50
2160 #define ATA_CMD_IDENTIFY_DEVICE 0xEC
2161 #define ATA_CMD_IDENTIFY_DEVICE_PACKET 0xA1
2162 #define ATA_CMD_IDENTIFY_PACKET_DEVICE 0xA1
2163 #define ATA_CMD_IDLE1 0xE3
2164 #define ATA_CMD_IDLE2 0x97
2165 #define ATA_CMD_IDLE_IMMEDIATE1 0xE1
2166 #define ATA_CMD_IDLE_IMMEDIATE2 0x95
2167 #define ATA_CMD_INITIALIZE_DRIVE_PARAMETERS 0x91
2168 #define ATA_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91
2169 #define ATA_CMD_NOP 0x00
2170 #define ATA_CMD_PACKET 0xA0
2171 #define ATA_CMD_READ_BUFFER 0xE4
2172 #define ATA_CMD_READ_DMA 0xC8
2173 #define ATA_CMD_READ_DMA_QUEUED 0xC7
2174 #define ATA_CMD_READ_MULTIPLE 0xC4
2175 #define ATA_CMD_READ_SECTORS 0x20
2176 #define ATA_CMD_READ_VERIFY_SECTORS 0x40
2177 #define ATA_CMD_RECALIBRATE 0x10
2178 #define ATA_CMD_SEEK 0x70
2179 #define ATA_CMD_SET_FEATURES 0xEF
2180 #define ATA_CMD_SET_MULTIPLE_MODE 0xC6
2181 #define ATA_CMD_SLEEP1 0xE6
2182 #define ATA_CMD_SLEEP2 0x99
2183 #define ATA_CMD_STANDBY1 0xE2
2184 #define ATA_CMD_STANDBY2 0x96
2185 #define ATA_CMD_STANDBY_IMMEDIATE1 0xE0
2186 #define ATA_CMD_STANDBY_IMMEDIATE2 0x94
2187 #define ATA_CMD_WRITE_BUFFER 0xE8
2188 #define ATA_CMD_WRITE_DMA 0xCA
2189 #define ATA_CMD_WRITE_DMA_QUEUED 0xCC
2190 #define ATA_CMD_WRITE_MULTIPLE 0xC5
2191 #define ATA_CMD_WRITE_SECTORS 0x30
2192 #define ATA_CMD_WRITE_VERIFY 0x3C
2194 #define ATA_IFACE_NONE 0x00
2195 #define ATA_IFACE_ISA 0x00
2196 #define ATA_IFACE_PCI 0x01
2198 #define ATA_TYPE_NONE 0x00
2199 #define ATA_TYPE_UNKNOWN 0x01
2200 #define ATA_TYPE_ATA 0x02
2201 #define ATA_TYPE_ATAPI 0x03
2203 #define ATA_DEVICE_NONE 0x00
2204 #define ATA_DEVICE_HD 0xFF
2205 #define ATA_DEVICE_CDROM 0x05
2207 #define ATA_MODE_NONE 0x00
2208 #define ATA_MODE_PIO16 0x00
2209 #define ATA_MODE_PIO32 0x01
2210 #define ATA_MODE_ISADMA 0x02
2211 #define ATA_MODE_PCIDMA 0x03
2212 #define ATA_MODE_USEIRQ 0x10
2214 #define ATA_TRANSLATION_NONE 0
2215 #define ATA_TRANSLATION_LBA 1
2216 #define ATA_TRANSLATION_LARGE 2
2217 #define ATA_TRANSLATION_RECHS 3
2219 #define ATA_DATA_NO 0x00
2220 #define ATA_DATA_IN 0x01
2221 #define ATA_DATA_OUT 0x02
2223 // ---------------------------------------------------------------------------
2224 // ATA/ATAPI driver : initialization
2225 // ---------------------------------------------------------------------------
2226 void ata_init( )
2228 Bit16u ebda_seg=read_word(0x0040,0x000E);
2229 Bit8u channel, device;
2231 // Channels info init.
2232 for (channel=0; channel<BX_MAX_ATA_INTERFACES; channel++) {
2233 write_byte(ebda_seg,&EbdaData->ata.channels[channel].iface,ATA_IFACE_NONE);
2234 write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1,0x0);
2235 write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2,0x0);
2236 write_byte(ebda_seg,&EbdaData->ata.channels[channel].irq,0);
2239 // Devices info init.
2240 for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2241 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
2242 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_NONE);
2243 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable,0);
2244 write_byte(ebda_seg,&EbdaData->ata.devices[device].lock,0);
2245 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode,ATA_MODE_NONE);
2246 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize,0);
2247 write_byte(ebda_seg,&EbdaData->ata.devices[device].translation,ATA_TRANSLATION_NONE);
2248 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads,0);
2249 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders,0);
2250 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt,0);
2251 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads,0);
2252 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders,0);
2253 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt,0);
2255 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors,0L);
2258 // hdidmap and cdidmap init.
2259 for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2260 write_byte(ebda_seg,&EbdaData->ata.hdidmap[device],BX_MAX_ATA_DEVICES);
2261 write_byte(ebda_seg,&EbdaData->ata.cdidmap[device],BX_MAX_ATA_DEVICES);
2264 write_byte(ebda_seg,&EbdaData->ata.hdcount,0);
2265 write_byte(ebda_seg,&EbdaData->ata.cdcount,0);
2268 // ---------------------------------------------------------------------------
2269 // ATA/ATAPI driver : device detection
2270 // ---------------------------------------------------------------------------
2272 void ata_detect( )
2274 Bit16u ebda_seg=read_word(0x0040,0x000E);
2275 Bit8u hdcount, cdcount, device, type;
2276 Bit8u buffer[0x0200];
2278 #if BX_MAX_ATA_INTERFACES > 0
2279 write_byte(ebda_seg,&EbdaData->ata.channels[0].iface,ATA_IFACE_ISA);
2280 write_word(ebda_seg,&EbdaData->ata.channels[0].iobase1,0x1f0);
2281 write_word(ebda_seg,&EbdaData->ata.channels[0].iobase2,0x3f0);
2282 write_byte(ebda_seg,&EbdaData->ata.channels[0].irq,14);
2283 #endif
2284 #if BX_MAX_ATA_INTERFACES > 1
2285 write_byte(ebda_seg,&EbdaData->ata.channels[1].iface,ATA_IFACE_ISA);
2286 write_word(ebda_seg,&EbdaData->ata.channels[1].iobase1,0x170);
2287 write_word(ebda_seg,&EbdaData->ata.channels[1].iobase2,0x370);
2288 write_byte(ebda_seg,&EbdaData->ata.channels[1].irq,15);
2289 #endif
2290 #if BX_MAX_ATA_INTERFACES > 2
2291 write_byte(ebda_seg,&EbdaData->ata.channels[2].iface,ATA_IFACE_ISA);
2292 write_word(ebda_seg,&EbdaData->ata.channels[2].iobase1,0x1e8);
2293 write_word(ebda_seg,&EbdaData->ata.channels[2].iobase2,0x3e0);
2294 write_byte(ebda_seg,&EbdaData->ata.channels[2].irq,12);
2295 #endif
2296 #if BX_MAX_ATA_INTERFACES > 3
2297 write_byte(ebda_seg,&EbdaData->ata.channels[3].iface,ATA_IFACE_ISA);
2298 write_word(ebda_seg,&EbdaData->ata.channels[3].iobase1,0x168);
2299 write_word(ebda_seg,&EbdaData->ata.channels[3].iobase2,0x360);
2300 write_byte(ebda_seg,&EbdaData->ata.channels[3].irq,11);
2301 #endif
2302 #if BX_MAX_ATA_INTERFACES > 4
2303 #error Please fill the ATA interface informations
2304 #endif
2306 // Device detection
2307 hdcount=cdcount=0;
2309 for(device=0; device<BX_MAX_ATA_DEVICES; device++) {
2310 Bit16u iobase1, iobase2;
2311 Bit8u channel, slave, shift;
2312 Bit8u sc, sn, cl, ch, st;
2314 channel = device / 2;
2315 slave = device % 2;
2317 iobase1 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1);
2318 iobase2 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2);
2320 // Disable interrupts
2321 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2323 // Look for device
2324 outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2325 outb(iobase1+ATA_CB_SC, 0x55);
2326 outb(iobase1+ATA_CB_SN, 0xaa);
2327 outb(iobase1+ATA_CB_SC, 0xaa);
2328 outb(iobase1+ATA_CB_SN, 0x55);
2329 outb(iobase1+ATA_CB_SC, 0x55);
2330 outb(iobase1+ATA_CB_SN, 0xaa);
2332 // If we found something
2333 sc = inb(iobase1+ATA_CB_SC);
2334 sn = inb(iobase1+ATA_CB_SN);
2336 if ( (sc == 0x55) && (sn == 0xaa) ) {
2337 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_UNKNOWN);
2339 // reset the channel
2340 ata_reset (device);
2342 // check for ATA or ATAPI
2343 outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2344 sc = inb(iobase1+ATA_CB_SC);
2345 sn = inb(iobase1+ATA_CB_SN);
2346 if ( (sc==0x01) && (sn==0x01) ) {
2347 cl = inb(iobase1+ATA_CB_CL);
2348 ch = inb(iobase1+ATA_CB_CH);
2349 st = inb(iobase1+ATA_CB_STAT);
2351 if ( (cl==0x14) && (ch==0xeb) ) {
2352 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATAPI);
2354 else if ( (cl==0x00) && (ch==0x00) && (st!=0x00) ) {
2355 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATA);
2360 type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type);
2362 // Now we send a IDENTIFY command to ATA device
2363 if(type == ATA_TYPE_ATA) {
2364 Bit32u sectors;
2365 Bit16u cylinders, heads, spt, blksize;
2366 Bit8u translation, removable, mode;
2368 // default mode to PIO16
2369 mode = ATA_MODE_PIO16;
2371 //Temporary values to do the transfer
2372 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2373 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2375 if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE, 1, 0, 0, 0, 0L, get_SS(),buffer) !=0 )
2376 BX_PANIC("ata-detect: Failed to detect ATA device\n");
2378 removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2379 #ifndef NO_PIO32
2380 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2381 #endif
2383 blksize = read_word(get_SS(),buffer+10);
2385 cylinders = read_word(get_SS(),buffer+(1*2)); // word 1
2386 heads = read_word(get_SS(),buffer+(3*2)); // word 3
2387 spt = read_word(get_SS(),buffer+(6*2)); // word 6
2389 sectors = read_dword(get_SS(),buffer+(60*2)); // word 60 and word 61
2391 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2392 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2393 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2394 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2395 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads, heads);
2396 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders, cylinders);
2397 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt, spt);
2398 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors, sectors);
2399 BX_INFO("ata%d-%d: PCHS=%u/%d/%d translation=", channel, slave,cylinders, heads, spt);
2401 translation = inb_cmos(0x39 + channel/2);
2402 for (shift=device%4; shift>0; shift--) translation >>= 2;
2403 translation &= 0x03;
2405 write_byte(ebda_seg,&EbdaData->ata.devices[device].translation, translation);
2407 switch (translation) {
2408 case ATA_TRANSLATION_NONE:
2409 BX_INFO("none");
2410 break;
2411 case ATA_TRANSLATION_LBA:
2412 BX_INFO("lba");
2413 break;
2414 case ATA_TRANSLATION_LARGE:
2415 BX_INFO("large");
2416 break;
2417 case ATA_TRANSLATION_RECHS:
2418 BX_INFO("r-echs");
2419 break;
2421 switch (translation) {
2422 case ATA_TRANSLATION_NONE:
2423 break;
2424 case ATA_TRANSLATION_LBA:
2425 spt = 63;
2426 sectors /= 63;
2427 heads = sectors / 1024;
2428 if (heads>128) heads = 255;
2429 else if (heads>64) heads = 128;
2430 else if (heads>32) heads = 64;
2431 else if (heads>16) heads = 32;
2432 else heads=16;
2433 cylinders = sectors / heads;
2434 break;
2435 case ATA_TRANSLATION_RECHS:
2436 // Take care not to overflow
2437 if (heads==16) {
2438 if(cylinders>61439) cylinders=61439;
2439 heads=15;
2440 cylinders = (Bit16u)((Bit32u)(cylinders)*16/15);
2442 // then go through the large bitshift process
2443 case ATA_TRANSLATION_LARGE:
2444 while(cylinders > 1024) {
2445 cylinders >>= 1;
2446 heads <<= 1;
2448 // If we max out the head count
2449 if (heads > 127) break;
2451 break;
2453 // clip to 1024 cylinders in lchs
2454 if (cylinders > 1024) cylinders=1024;
2455 BX_INFO(" LCHS=%d/%d/%d\n", cylinders, heads, spt);
2457 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads, heads);
2458 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders, cylinders);
2459 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt, spt);
2461 // fill hdidmap
2462 write_byte(ebda_seg,&EbdaData->ata.hdidmap[hdcount], device);
2463 hdcount++;
2466 // Now we send a IDENTIFY command to ATAPI device
2467 if(type == ATA_TYPE_ATAPI) {
2469 Bit8u type, removable, mode;
2470 Bit16u blksize;
2472 // default mode to PIO16
2473 mode = ATA_MODE_PIO16;
2475 //Temporary values to do the transfer
2476 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_CDROM);
2477 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2479 if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE_PACKET, 1, 0, 0, 0, 0L, get_SS(),buffer) != 0)
2480 BX_PANIC("ata-detect: Failed to detect ATAPI device\n");
2482 type = read_byte(get_SS(),buffer+1) & 0x1f;
2483 removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2484 #ifndef NO_PIO32
2485 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2486 #endif
2487 blksize = 2048;
2489 write_byte(ebda_seg,&EbdaData->ata.devices[device].device, type);
2490 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2491 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2492 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2494 // fill cdidmap
2495 write_byte(ebda_seg,&EbdaData->ata.cdidmap[cdcount], device);
2496 cdcount++;
2500 Bit32u sizeinmb;
2501 Bit16u ataversion;
2502 Bit8u c, i, version, model[41];
2504 switch (type) {
2505 case ATA_TYPE_ATA:
2506 sizeinmb = read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors);
2507 sizeinmb >>= 11;
2508 case ATA_TYPE_ATAPI:
2509 // Read ATA/ATAPI version
2510 ataversion=((Bit16u)(read_byte(get_SS(),buffer+161))<<8)|read_byte(get_SS(),buffer+160);
2511 for(version=15;version>0;version--) {
2512 if((ataversion&(1<<version))!=0)
2513 break;
2516 // Read model name
2517 for(i=0;i<20;i++){
2518 write_byte(get_SS(),model+(i*2),read_byte(get_SS(),buffer+(i*2)+54+1));
2519 write_byte(get_SS(),model+(i*2)+1,read_byte(get_SS(),buffer+(i*2)+54));
2522 // Reformat
2523 write_byte(get_SS(),model+40,0x00);
2524 for(i=39;i>0;i--){
2525 if(read_byte(get_SS(),model+i)==0x20)
2526 write_byte(get_SS(),model+i,0x00);
2527 else break;
2529 break;
2532 switch (type) {
2533 case ATA_TYPE_ATA:
2534 printf("ata%d %s: ",channel,slave?" slave":"master");
2535 i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
2536 printf(" ATA-%d Hard-Disk (%d MBytes)\n",version,(Bit16u)sizeinmb);
2537 break;
2538 case ATA_TYPE_ATAPI:
2539 printf("ata%d %s: ",channel,slave?" slave":"master");
2540 i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
2541 if(read_byte(ebda_seg,&EbdaData->ata.devices[device].device)==ATA_DEVICE_CDROM)
2542 printf(" ATAPI-%d CD-Rom/DVD-Rom\n",version);
2543 else
2544 printf(" ATAPI-%d Device\n",version);
2545 break;
2546 case ATA_TYPE_UNKNOWN:
2547 printf("ata%d %s: Unknown device\n",channel,slave?" slave":"master");
2548 break;
2553 // Store the devices counts
2554 write_byte(ebda_seg,&EbdaData->ata.hdcount, hdcount);
2555 write_byte(ebda_seg,&EbdaData->ata.cdcount, cdcount);
2556 write_byte(0x40,0x75, hdcount);
2558 printf("\n");
2560 // FIXME : should use bios=cmos|auto|disable bits
2561 // FIXME : should know about translation bits
2562 // FIXME : move hard_drive_post here
2566 // ---------------------------------------------------------------------------
2567 // ATA/ATAPI driver : software reset
2568 // ---------------------------------------------------------------------------
2569 // ATA-3
2570 // 8.2.1 Software reset - Device 0
2572 void ata_reset(device)
2573 Bit16u device;
2575 Bit16u ebda_seg=read_word(0x0040,0x000E);
2576 Bit16u iobase1, iobase2;
2577 Bit8u channel, slave, sn, sc;
2578 Bit16u max;
2580 channel = device / 2;
2581 slave = device % 2;
2583 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2584 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2586 // Reset
2588 // 8.2.1 (a) -- set SRST in DC
2589 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST);
2591 // 8.2.1 (b) -- wait for BSY
2592 max=0xff;
2593 while(--max>0) {
2594 Bit8u status = inb(iobase1+ATA_CB_STAT);
2595 if ((status & ATA_CB_STAT_BSY) != 0) break;
2598 // 8.2.1 (f) -- clear SRST
2599 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2601 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_NONE) {
2603 // 8.2.1 (g) -- check for sc==sn==0x01
2604 // select device
2605 outb(iobase1+ATA_CB_DH, slave?ATA_CB_DH_DEV1:ATA_CB_DH_DEV0);
2606 sc = inb(iobase1+ATA_CB_SC);
2607 sn = inb(iobase1+ATA_CB_SN);
2609 if ( (sc==0x01) && (sn==0x01) ) {
2611 // 8.2.1 (h) -- wait for not BSY
2612 max=0xff;
2613 while(--max>0) {
2614 Bit8u status = inb(iobase1+ATA_CB_STAT);
2615 if ((status & ATA_CB_STAT_BSY) == 0) break;
2620 // 8.2.1 (i) -- wait for DRDY
2621 max=0xfff;
2622 while(--max>0) {
2623 Bit8u status = inb(iobase1+ATA_CB_STAT);
2624 if ((status & ATA_CB_STAT_RDY) != 0) break;
2627 // Enable interrupts
2628 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2631 // ---------------------------------------------------------------------------
2632 // ATA/ATAPI driver : execute a non data command
2633 // ---------------------------------------------------------------------------
2635 Bit16u ata_cmd_non_data()
2636 {return 0;}
2638 // ---------------------------------------------------------------------------
2639 // ATA/ATAPI driver : execute a data-in command
2640 // ---------------------------------------------------------------------------
2641 // returns
2642 // 0 : no error
2643 // 1 : BUSY bit set
2644 // 2 : read error
2645 // 3 : expected DRQ=1
2646 // 4 : no sectors left to read/verify
2647 // 5 : more sectors to read/verify
2648 // 6 : no sectors left to write
2649 // 7 : more sectors to write
2650 Bit16u ata_cmd_data_in(device, command, count, cylinder, head, sector, lba, segment, offset)
2651 Bit16u device, command, count, cylinder, head, sector, segment, offset;
2652 Bit32u lba;
2654 Bit16u ebda_seg=read_word(0x0040,0x000E);
2655 Bit16u iobase1, iobase2, blksize;
2656 Bit8u channel, slave;
2657 Bit8u status, current, mode;
2659 channel = device / 2;
2660 slave = device % 2;
2662 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2663 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2664 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2665 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
2666 if (mode == ATA_MODE_PIO32) blksize>>=2;
2667 else blksize>>=1;
2669 // sector will be 0 only on lba access. Convert to lba-chs
2670 if (sector == 0) {
2671 sector = (Bit16u) (lba & 0x000000ffL);
2672 lba >>= 8;
2673 cylinder = (Bit16u) (lba & 0x0000ffffL);
2674 lba >>= 16;
2675 head = ((Bit16u) (lba & 0x0000000fL)) | 0x40;
2678 // Reset count of transferred data
2679 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2680 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2681 current = 0;
2683 status = inb(iobase1 + ATA_CB_STAT);
2684 if (status & ATA_CB_STAT_BSY) return 1;
2686 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2687 outb(iobase1 + ATA_CB_FR, 0x00);
2688 outb(iobase1 + ATA_CB_SC, count);
2689 outb(iobase1 + ATA_CB_SN, sector);
2690 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
2691 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
2692 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
2693 outb(iobase1 + ATA_CB_CMD, command);
2695 while (1) {
2696 status = inb(iobase1 + ATA_CB_STAT);
2697 if ( !(status & ATA_CB_STAT_BSY) ) break;
2700 if (status & ATA_CB_STAT_ERR) {
2701 BX_DEBUG_ATA("ata_cmd_data_in : read error\n");
2702 return 2;
2703 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2704 BX_DEBUG_ATA("ata_cmd_data_in : DRQ not set (status %02x)\n", (unsigned) status);
2705 return 3;
2708 // FIXME : move seg/off translation here
2710 ASM_START
2711 sti ;; enable higher priority interrupts
2712 ASM_END
2714 while (1) {
2716 ASM_START
2717 push bp
2718 mov bp, sp
2719 mov di, _ata_cmd_data_in.offset + 2[bp]
2720 mov ax, _ata_cmd_data_in.segment + 2[bp]
2721 mov cx, _ata_cmd_data_in.blksize + 2[bp]
2723 ;; adjust if there will be an overrun. 2K max sector size
2724 cmp di, #0xf800 ;;
2725 jbe ata_in_no_adjust
2727 ata_in_adjust:
2728 sub di, #0x0800 ;; sub 2 kbytes from offset
2729 add ax, #0x0080 ;; add 2 Kbytes to segment
2731 ata_in_no_adjust:
2732 mov es, ax ;; segment in es
2734 mov dx, _ata_cmd_data_in.iobase1 + 2[bp] ;; ATA data read port
2736 mov ah, _ata_cmd_data_in.mode + 2[bp]
2737 cmp ah, #ATA_MODE_PIO32
2738 je ata_in_32
2740 ata_in_16:
2741 rep
2742 insw ;; CX words transfered from port(DX) to ES:[DI]
2743 jmp ata_in_done
2745 ata_in_32:
2746 rep
2747 insd ;; CX dwords transfered from port(DX) to ES:[DI]
2749 ata_in_done:
2750 mov _ata_cmd_data_in.offset + 2[bp], di
2751 mov _ata_cmd_data_in.segment + 2[bp], es
2752 pop bp
2753 ASM_END
2755 current++;
2756 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
2757 count--;
2758 status = inb(iobase1 + ATA_CB_STAT);
2759 if (count == 0) {
2760 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2761 != ATA_CB_STAT_RDY ) {
2762 BX_DEBUG_ATA("ata_cmd_data_in : no sectors left (status %02x)\n", (unsigned) status);
2763 return 4;
2765 break;
2767 else {
2768 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2769 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2770 BX_DEBUG_ATA("ata_cmd_data_in : more sectors left (status %02x)\n", (unsigned) status);
2771 return 5;
2773 continue;
2776 // Enable interrupts
2777 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2778 return 0;
2781 // ---------------------------------------------------------------------------
2782 // ATA/ATAPI driver : execute a data-out command
2783 // ---------------------------------------------------------------------------
2784 // returns
2785 // 0 : no error
2786 // 1 : BUSY bit set
2787 // 2 : read error
2788 // 3 : expected DRQ=1
2789 // 4 : no sectors left to read/verify
2790 // 5 : more sectors to read/verify
2791 // 6 : no sectors left to write
2792 // 7 : more sectors to write
2793 Bit16u ata_cmd_data_out(device, command, count, cylinder, head, sector, lba, segment, offset)
2794 Bit16u device, command, count, cylinder, head, sector, segment, offset;
2795 Bit32u lba;
2797 Bit16u ebda_seg=read_word(0x0040,0x000E);
2798 Bit16u iobase1, iobase2, blksize;
2799 Bit8u channel, slave;
2800 Bit8u status, current, mode;
2802 channel = device / 2;
2803 slave = device % 2;
2805 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2806 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2807 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2808 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
2809 if (mode == ATA_MODE_PIO32) blksize>>=2;
2810 else blksize>>=1;
2812 // sector will be 0 only on lba access. Convert to lba-chs
2813 if (sector == 0) {
2814 sector = (Bit16u) (lba & 0x000000ffL);
2815 lba >>= 8;
2816 cylinder = (Bit16u) (lba & 0x0000ffffL);
2817 lba >>= 16;
2818 head = ((Bit16u) (lba & 0x0000000fL)) | 0x40;
2821 // Reset count of transferred data
2822 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2823 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2824 current = 0;
2826 status = inb(iobase1 + ATA_CB_STAT);
2827 if (status & ATA_CB_STAT_BSY) return 1;
2829 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2830 outb(iobase1 + ATA_CB_FR, 0x00);
2831 outb(iobase1 + ATA_CB_SC, count);
2832 outb(iobase1 + ATA_CB_SN, sector);
2833 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
2834 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
2835 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
2836 outb(iobase1 + ATA_CB_CMD, command);
2838 while (1) {
2839 status = inb(iobase1 + ATA_CB_STAT);
2840 if ( !(status & ATA_CB_STAT_BSY) ) break;
2843 if (status & ATA_CB_STAT_ERR) {
2844 BX_DEBUG_ATA("ata_cmd_data_out : read error\n");
2845 return 2;
2846 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2847 BX_DEBUG_ATA("ata_cmd_data_out : DRQ not set (status %02x)\n", (unsigned) status);
2848 return 3;
2851 // FIXME : move seg/off translation here
2853 ASM_START
2854 sti ;; enable higher priority interrupts
2855 ASM_END
2857 while (1) {
2859 ASM_START
2860 push bp
2861 mov bp, sp
2862 mov si, _ata_cmd_data_out.offset + 2[bp]
2863 mov ax, _ata_cmd_data_out.segment + 2[bp]
2864 mov cx, _ata_cmd_data_out.blksize + 2[bp]
2866 ;; adjust if there will be an overrun. 2K max sector size
2867 cmp si, #0xf800 ;;
2868 jbe ata_out_no_adjust
2870 ata_out_adjust:
2871 sub si, #0x0800 ;; sub 2 kbytes from offset
2872 add ax, #0x0080 ;; add 2 Kbytes to segment
2874 ata_out_no_adjust:
2875 mov es, ax ;; segment in es
2877 mov dx, _ata_cmd_data_out.iobase1 + 2[bp] ;; ATA data write port
2879 mov ah, _ata_cmd_data_out.mode + 2[bp]
2880 cmp ah, #ATA_MODE_PIO32
2881 je ata_out_32
2883 ata_out_16:
2884 seg ES
2885 rep
2886 outsw ;; CX words transfered from port(DX) to ES:[SI]
2887 jmp ata_out_done
2889 ata_out_32:
2890 seg ES
2891 rep
2892 outsd ;; CX dwords transfered from port(DX) to ES:[SI]
2894 ata_out_done:
2895 mov _ata_cmd_data_out.offset + 2[bp], si
2896 mov _ata_cmd_data_out.segment + 2[bp], es
2897 pop bp
2898 ASM_END
2900 current++;
2901 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
2902 count--;
2903 status = inb(iobase1 + ATA_CB_STAT);
2904 if (count == 0) {
2905 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2906 != ATA_CB_STAT_RDY ) {
2907 BX_DEBUG_ATA("ata_cmd_data_out : no sectors left (status %02x)\n", (unsigned) status);
2908 return 6;
2910 break;
2912 else {
2913 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2914 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2915 BX_DEBUG_ATA("ata_cmd_data_out : more sectors left (status %02x)\n", (unsigned) status);
2916 return 7;
2918 continue;
2921 // Enable interrupts
2922 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2923 return 0;
2926 // ---------------------------------------------------------------------------
2927 // ATA/ATAPI driver : execute a packet command
2928 // ---------------------------------------------------------------------------
2929 // returns
2930 // 0 : no error
2931 // 1 : error in parameters
2932 // 2 : BUSY bit set
2933 // 3 : error
2934 // 4 : not ready
2935 Bit16u ata_cmd_packet(device, cmdlen, cmdseg, cmdoff, header, length, inout, bufseg, bufoff)
2936 Bit8u cmdlen,inout;
2937 Bit16u device,cmdseg, cmdoff, bufseg, bufoff;
2938 Bit16u header;
2939 Bit32u length;
2941 Bit16u ebda_seg=read_word(0x0040,0x000E);
2942 Bit16u iobase1, iobase2;
2943 Bit16u lcount, lbefore, lafter, count;
2944 Bit8u channel, slave;
2945 Bit8u status, mode, lmode;
2946 Bit32u total, transfer;
2948 channel = device / 2;
2949 slave = device % 2;
2951 // Data out is not supported yet
2952 if (inout == ATA_DATA_OUT) {
2953 BX_INFO("ata_cmd_packet: DATA_OUT not supported yet\n");
2954 return 1;
2957 // The header length must be even
2958 if (header & 1) {
2959 BX_DEBUG_ATA("ata_cmd_packet : header must be even (%04x)\n",header);
2960 return 1;
2963 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2964 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2965 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2966 transfer= 0L;
2968 if (cmdlen < 12) cmdlen=12;
2969 if (cmdlen > 12) cmdlen=16;
2970 cmdlen>>=1;
2972 // Reset count of transferred data
2973 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2974 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2976 status = inb(iobase1 + ATA_CB_STAT);
2977 if (status & ATA_CB_STAT_BSY) return 2;
2979 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2980 // outb(iobase1 + ATA_CB_FR, 0x00);
2981 // outb(iobase1 + ATA_CB_SC, 0x00);
2982 // outb(iobase1 + ATA_CB_SN, 0x00);
2983 outb(iobase1 + ATA_CB_CL, 0xfff0 & 0x00ff);
2984 outb(iobase1 + ATA_CB_CH, 0xfff0 >> 8);
2985 outb(iobase1 + ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2986 outb(iobase1 + ATA_CB_CMD, ATA_CMD_PACKET);
2988 // Device should ok to receive command
2989 while (1) {
2990 status = inb(iobase1 + ATA_CB_STAT);
2991 if ( !(status & ATA_CB_STAT_BSY) ) break;
2994 if (status & ATA_CB_STAT_ERR) {
2995 BX_DEBUG_ATA("ata_cmd_packet : error, status is %02x\n",status);
2996 return 3;
2997 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2998 BX_DEBUG_ATA("ata_cmd_packet : DRQ not set (status %02x)\n", (unsigned) status);
2999 return 4;
3002 // Normalize address
3003 cmdseg += (cmdoff / 16);
3004 cmdoff %= 16;
3006 // Send command to device
3007 ASM_START
3008 sti ;; enable higher priority interrupts
3010 push bp
3011 mov bp, sp
3013 mov si, _ata_cmd_packet.cmdoff + 2[bp]
3014 mov ax, _ata_cmd_packet.cmdseg + 2[bp]
3015 mov cx, _ata_cmd_packet.cmdlen + 2[bp]
3016 mov es, ax ;; segment in es
3018 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data write port
3020 seg ES
3021 rep
3022 outsw ;; CX words transfered from port(DX) to ES:[SI]
3024 pop bp
3025 ASM_END
3027 if (inout == ATA_DATA_NO) {
3028 status = inb(iobase1 + ATA_CB_STAT);
3030 else {
3031 while (1) {
3033 status = inb(iobase1 + ATA_CB_STAT);
3035 // Check if command completed
3036 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ) ) ==0 ) break;
3038 if (status & ATA_CB_STAT_ERR) {
3039 BX_DEBUG_ATA("ata_cmd_packet : error (status %02x)\n",status);
3040 return 3;
3043 // Device must be ready to send data
3044 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
3045 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
3046 BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", status);
3047 return 4;
3050 // Normalize address
3051 bufseg += (bufoff / 16);
3052 bufoff %= 16;
3054 // Get the byte count
3055 lcount = ((Bit16u)(inb(iobase1 + ATA_CB_CH))<<8)+inb(iobase1 + ATA_CB_CL);
3057 // adjust to read what we want
3058 if(header>lcount) {
3059 lbefore=lcount;
3060 header-=lcount;
3061 lcount=0;
3063 else {
3064 lbefore=header;
3065 header=0;
3066 lcount-=lbefore;
3069 if(lcount>length) {
3070 lafter=lcount-length;
3071 lcount=length;
3072 length=0;
3074 else {
3075 lafter=0;
3076 length-=lcount;
3079 // Save byte count
3080 count = lcount;
3082 BX_DEBUG_ATA("Trying to read %04x bytes (%04x %04x %04x) ",lbefore+lcount+lafter,lbefore,lcount,lafter);
3083 BX_DEBUG_ATA("to 0x%04x:0x%04x\n",bufseg,bufoff);
3085 // If counts not dividable by 4, use 16bits mode
3086 lmode = mode;
3087 if (lbefore & 0x03) lmode=ATA_MODE_PIO16;
3088 if (lcount & 0x03) lmode=ATA_MODE_PIO16;
3089 if (lafter & 0x03) lmode=ATA_MODE_PIO16;
3091 // adds an extra byte if count are odd. before is always even
3092 if (lcount & 0x01) {
3093 lcount+=1;
3094 if ((lafter > 0) && (lafter & 0x01)) {
3095 lafter-=1;
3099 if (lmode == ATA_MODE_PIO32) {
3100 lcount>>=2; lbefore>>=2; lafter>>=2;
3102 else {
3103 lcount>>=1; lbefore>>=1; lafter>>=1;
3106 ; // FIXME bcc bug
3108 ASM_START
3109 push bp
3110 mov bp, sp
3112 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data read port
3114 mov cx, _ata_cmd_packet.lbefore + 2[bp]
3115 jcxz ata_packet_no_before
3117 mov ah, _ata_cmd_packet.lmode + 2[bp]
3118 cmp ah, #ATA_MODE_PIO32
3119 je ata_packet_in_before_32
3121 ata_packet_in_before_16:
3122 in ax, dx
3123 loop ata_packet_in_before_16
3124 jmp ata_packet_no_before
3126 ata_packet_in_before_32:
3127 push eax
3128 ata_packet_in_before_32_loop:
3129 in eax, dx
3130 loop ata_packet_in_before_32_loop
3131 pop eax
3133 ata_packet_no_before:
3134 mov cx, _ata_cmd_packet.lcount + 2[bp]
3135 jcxz ata_packet_after
3137 mov di, _ata_cmd_packet.bufoff + 2[bp]
3138 mov ax, _ata_cmd_packet.bufseg + 2[bp]
3139 mov es, ax
3141 mov ah, _ata_cmd_packet.lmode + 2[bp]
3142 cmp ah, #ATA_MODE_PIO32
3143 je ata_packet_in_32
3145 ata_packet_in_16:
3146 rep
3147 insw ;; CX words transfered tp port(DX) to ES:[DI]
3148 jmp ata_packet_after
3150 ata_packet_in_32:
3151 rep
3152 insd ;; CX dwords transfered to port(DX) to ES:[DI]
3154 ata_packet_after:
3155 mov cx, _ata_cmd_packet.lafter + 2[bp]
3156 jcxz ata_packet_done
3158 mov ah, _ata_cmd_packet.lmode + 2[bp]
3159 cmp ah, #ATA_MODE_PIO32
3160 je ata_packet_in_after_32
3162 ata_packet_in_after_16:
3163 in ax, dx
3164 loop ata_packet_in_after_16
3165 jmp ata_packet_done
3167 ata_packet_in_after_32:
3168 push eax
3169 ata_packet_in_after_32_loop:
3170 in eax, dx
3171 loop ata_packet_in_after_32_loop
3172 pop eax
3174 ata_packet_done:
3175 pop bp
3176 ASM_END
3178 // Compute new buffer address
3179 bufoff += count;
3181 // Save transferred bytes count
3182 transfer += count;
3183 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,transfer);
3187 // Final check, device must be ready
3188 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
3189 != ATA_CB_STAT_RDY ) {
3190 BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", (unsigned) status);
3191 return 4;
3194 // Enable interrupts
3195 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
3196 return 0;
3199 // ---------------------------------------------------------------------------
3200 // End of ATA/ATAPI Driver
3201 // ---------------------------------------------------------------------------
3203 // ---------------------------------------------------------------------------
3204 // Start of ATA/ATAPI generic functions
3205 // ---------------------------------------------------------------------------
3207 Bit16u
3208 atapi_get_sense(device)
3209 Bit16u device;
3211 Bit8u atacmd[12];
3212 Bit8u buffer[16];
3213 Bit8u i;
3215 memsetb(get_SS(),atacmd,0,12);
3217 // Request SENSE
3218 atacmd[0]=0x03;
3219 atacmd[4]=0x20;
3220 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 16L, ATA_DATA_IN, get_SS(), buffer) != 0)
3221 return 0x0002;
3223 if ((buffer[0] & 0x7e) == 0x70) {
3224 return (((Bit16u)buffer[2]&0x0f)*0x100)+buffer[12];
3227 return 0;
3230 Bit16u
3231 atapi_is_ready(device)
3232 Bit16u device;
3234 Bit8u atacmd[12];
3235 Bit8u buffer[];
3237 memsetb(get_SS(),atacmd,0,12);
3239 // Test Unit Ready
3240 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0)
3241 return 0x000f;
3243 if (atapi_get_sense(device) !=0 ) {
3244 memsetb(get_SS(),atacmd,0,12);
3246 // try to send Test Unit Ready again
3247 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0)
3248 return 0x000f;
3250 return atapi_get_sense(device);
3252 return 0;
3255 Bit16u
3256 atapi_is_cdrom(device)
3257 Bit8u device;
3259 Bit16u ebda_seg=read_word(0x0040,0x000E);
3261 if (device >= BX_MAX_ATA_DEVICES)
3262 return 0;
3264 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI)
3265 return 0;
3267 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].device) != ATA_DEVICE_CDROM)
3268 return 0;
3270 return 1;
3273 // ---------------------------------------------------------------------------
3274 // End of ATA/ATAPI generic functions
3275 // ---------------------------------------------------------------------------
3277 #endif // BX_USE_ATADRV
3279 #if BX_ELTORITO_BOOT
3281 // ---------------------------------------------------------------------------
3282 // Start of El-Torito boot functions
3283 // ---------------------------------------------------------------------------
3285 void
3286 cdemu_init()
3288 Bit16u ebda_seg=read_word(0x0040,0x000E);
3290 // the only important data is this one for now
3291 write_byte(ebda_seg,&EbdaData->cdemu.active,0x00);
3294 Bit8u
3295 cdemu_isactive()
3297 Bit16u ebda_seg=read_word(0x0040,0x000E);
3299 return(read_byte(ebda_seg,&EbdaData->cdemu.active));
3302 Bit8u
3303 cdemu_emulated_drive()
3305 Bit16u ebda_seg=read_word(0x0040,0x000E);
3307 return(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
3310 static char isotag[6]="CD001";
3311 static char eltorito[24]="EL TORITO SPECIFICATION";
3312 //
3313 // Returns ah: emulated drive, al: error code
3314 //
3315 Bit16u
3316 cdrom_boot()
3318 Bit16u ebda_seg=read_word(0x0040,0x000E);
3319 Bit8u atacmd[12], buffer[2048];
3320 Bit32u lba;
3321 Bit16u boot_segment, nbsectors, i, error;
3322 Bit8u device;
3324 // Find out the first cdrom
3325 for (device=0; device<BX_MAX_ATA_DEVICES;device++) {
3326 if (atapi_is_cdrom(device)) break;
3329 // if not found
3330 if(device >= BX_MAX_ATA_DEVICES) return 2;
3332 // Read the Boot Record Volume Descriptor
3333 memsetb(get_SS(),atacmd,0,12);
3334 atacmd[0]=0x28; // READ command
3335 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3336 atacmd[8]=(0x01 & 0x00ff); // Sectors
3337 atacmd[2]=(0x11 & 0xff000000) >> 24; // LBA
3338 atacmd[3]=(0x11 & 0x00ff0000) >> 16;
3339 atacmd[4]=(0x11 & 0x0000ff00) >> 8;
3340 atacmd[5]=(0x11 & 0x000000ff);
3341 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3342 return 3;
3344 // Validity checks
3345 if(buffer[0]!=0)return 4;
3346 for(i=0;i<5;i++){
3347 if(buffer[1+i]!=read_byte(0xf000,&isotag[i]))return 5;
3349 for(i=0;i<23;i++)
3350 if(buffer[7+i]!=read_byte(0xf000,&eltorito[i]))return 6;
3352 // ok, now we calculate the Boot catalog address
3353 lba=buffer[0x4A]*0x1000000+buffer[0x49]*0x10000+buffer[0x48]*0x100+buffer[0x47];
3355 // And we read the Boot Catalog
3356 memsetb(get_SS(),atacmd,0,12);
3357 atacmd[0]=0x28; // READ command
3358 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3359 atacmd[8]=(0x01 & 0x00ff); // Sectors
3360 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3361 atacmd[3]=(lba & 0x00ff0000) >> 16;
3362 atacmd[4]=(lba & 0x0000ff00) >> 8;
3363 atacmd[5]=(lba & 0x000000ff);
3364 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3365 return 7;
3367 // Validation entry
3368 if(buffer[0x00]!=0x01)return 8; // Header
3369 if(buffer[0x01]!=0x00)return 9; // Platform
3370 if(buffer[0x1E]!=0x55)return 10; // key 1
3371 if(buffer[0x1F]!=0xAA)return 10; // key 2
3373 // Initial/Default Entry
3374 if(buffer[0x20]!=0x88)return 11; // Bootable
3376 write_byte(ebda_seg,&EbdaData->cdemu.media,buffer[0x21]);
3377 if(buffer[0x21]==0){
3378 // FIXME ElTorito Hardcoded. cdrom is hardcoded as device 0xE0.
3379 // Win2000 cd boot needs to know it booted from cd
3380 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0xE0);
3382 else if(buffer[0x21]<4)
3383 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x00);
3384 else
3385 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x80);
3387 write_byte(ebda_seg,&EbdaData->cdemu.controller_index,device/2);
3388 write_byte(ebda_seg,&EbdaData->cdemu.device_spec,device%2);
3390 boot_segment=buffer[0x23]*0x100+buffer[0x22];
3391 if(boot_segment==0x0000)boot_segment=0x07C0;
3393 write_word(ebda_seg,&EbdaData->cdemu.load_segment,boot_segment);
3394 write_word(ebda_seg,&EbdaData->cdemu.buffer_segment,0x0000);
3396 nbsectors=buffer[0x27]*0x100+buffer[0x26];
3397 write_word(ebda_seg,&EbdaData->cdemu.sector_count,nbsectors);
3399 lba=buffer[0x2B]*0x1000000+buffer[0x2A]*0x10000+buffer[0x29]*0x100+buffer[0x28];
3400 write_dword(ebda_seg,&EbdaData->cdemu.ilba,lba);
3402 // And we read the image in memory
3403 memsetb(get_SS(),atacmd,0,12);
3404 atacmd[0]=0x28; // READ command
3405 atacmd[7]=((1+(nbsectors-1)/4) & 0xff00) >> 8; // Sectors
3406 atacmd[8]=((1+(nbsectors-1)/4) & 0x00ff); // Sectors
3407 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3408 atacmd[3]=(lba & 0x00ff0000) >> 16;
3409 atacmd[4]=(lba & 0x0000ff00) >> 8;
3410 atacmd[5]=(lba & 0x000000ff);
3411 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, nbsectors*512L, ATA_DATA_IN, boot_segment,0)) != 0)
3412 return 12;
3414 // Remember the media type
3415 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
3416 case 0x01: // 1.2M floppy
3417 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,15);
3418 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3419 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3420 break;
3421 case 0x02: // 1.44M floppy
3422 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,18);
3423 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3424 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3425 break;
3426 case 0x03: // 2.88M floppy
3427 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,36);
3428 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3429 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3430 break;
3431 case 0x04: // Harddrive
3432 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,read_byte(boot_segment,446+6)&0x3f);
3433 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,
3434 (read_byte(boot_segment,446+6)<<2) + read_byte(boot_segment,446+7) + 1);
3435 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,read_byte(boot_segment,446+5) + 1);
3436 break;
3439 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0) {
3440 // Increase bios installed hardware number of devices
3441 if(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)==0x00)
3442 write_byte(0x40,0x10,read_byte(0x40,0x10)|0x41);
3443 else
3444 write_byte(ebda_seg, &EbdaData->ata.hdcount, read_byte(ebda_seg, &EbdaData->ata.hdcount) + 1);
3448 // everything is ok, so from now on, the emulation is active
3449 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0)
3450 write_byte(ebda_seg,&EbdaData->cdemu.active,0x01);
3452 // return the boot drive + no error
3453 return (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)*0x100)+0;
3456 // ---------------------------------------------------------------------------
3457 // End of El-Torito boot functions
3458 // ---------------------------------------------------------------------------
3459 #endif // BX_ELTORITO_BOOT
3461 void
3462 int14_function(regs, ds, iret_addr)
3463 pusha_regs_t regs; // regs pushed from PUSHA instruction
3464 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
3465 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
3467 Bit16u addr,timer,val16;
3468 Bit8u timeout;
3470 ASM_START
3471 sti
3472 ASM_END
3474 addr = read_word(0x0040, (regs.u.r16.dx << 1));
3475 timeout = read_byte(0x0040, 0x007C + regs.u.r16.dx);
3476 if ((regs.u.r16.dx < 4) && (addr > 0)) {
3477 switch (regs.u.r8.ah) {
3478 case 0:
3479 outb(addr+3, inb(addr+3) | 0x80);
3480 if (regs.u.r8.al & 0xE0 == 0) {
3481 outb(addr, 0x17);
3482 outb(addr+1, 0x04);
3483 } else {
3484 val16 = 0x600 >> ((regs.u.r8.al & 0xE0) >> 5);
3485 outb(addr, val16 & 0xFF);
3486 outb(addr+1, val16 >> 8);
3488 outb(addr+3, regs.u.r8.al & 0x1F);
3489 regs.u.r8.ah = inb(addr+5);
3490 regs.u.r8.al = inb(addr+6);
3491 ClearCF(iret_addr.flags);
3492 break;
3493 case 1:
3494 timer = read_word(0x0040, 0x006C);
3495 while (((inb(addr+5) & 0x60) != 0x60) && (timeout)) {
3496 val16 = read_word(0x0040, 0x006C);
3497 if (val16 != timer) {
3498 timer = val16;
3499 timeout--;
3502 if (timeout) outb(addr, regs.u.r8.al);
3503 regs.u.r8.ah = inb(addr+5);
3504 if (!timeout) regs.u.r8.ah |= 0x80;
3505 ClearCF(iret_addr.flags);
3506 break;
3507 case 2:
3508 timer = read_word(0x0040, 0x006C);
3509 while (((inb(addr+5) & 0x01) == 0) && (timeout)) {
3510 val16 = read_word(0x0040, 0x006C);
3511 if (val16 != timer) {
3512 timer = val16;
3513 timeout--;
3516 if (timeout) {
3517 regs.u.r8.ah = 0;
3518 regs.u.r8.al = inb(addr);
3519 } else {
3520 regs.u.r8.ah = inb(addr+5);
3522 ClearCF(iret_addr.flags);
3523 break;
3524 case 3:
3525 regs.u.r8.ah = inb(addr+5);
3526 regs.u.r8.al = inb(addr+6);
3527 ClearCF(iret_addr.flags);
3528 break;
3529 default:
3530 SetCF(iret_addr.flags); // Unsupported
3532 } else {
3533 SetCF(iret_addr.flags); // Unsupported
3537 void
3538 int15_function(regs, ES, DS, FLAGS)
3539 pusha_regs_t regs; // REGS pushed via pusha
3540 Bit16u ES, DS, FLAGS;
3542 Bit16u ebda_seg=read_word(0x0040,0x000E);
3543 bx_bool prev_a20_enable;
3544 Bit16u base15_00;
3545 Bit8u base23_16;
3546 Bit16u ss;
3547 Bit16u CX,DX;
3549 Bit16u bRegister;
3550 Bit8u irqDisable;
3552 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3554 switch (regs.u.r8.ah) {
3555 case 0x24: /* A20 Control */
3556 switch (regs.u.r8.al) {
3557 case 0x00:
3558 set_enable_a20(0);
3559 CLEAR_CF();
3560 regs.u.r8.ah = 0;
3561 break;
3562 case 0x01:
3563 set_enable_a20(1);
3564 CLEAR_CF();
3565 regs.u.r8.ah = 0;
3566 break;
3567 case 0x02:
3568 regs.u.r8.al = (inb(0x92) >> 1) & 0x01;
3569 CLEAR_CF();
3570 regs.u.r8.ah = 0;
3571 break;
3572 case 0x03:
3573 CLEAR_CF();
3574 regs.u.r8.ah = 0;
3575 regs.u.r16.bx = 3;
3576 break;
3577 default:
3578 BX_INFO("int15: Func 24h, subfunc %02xh, A20 gate control not supported\n", (unsigned) regs.u.r8.al);
3579 SET_CF();
3580 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3582 break;
3584 case 0x41:
3585 SET_CF();
3586 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3587 break;
3589 case 0x4f:
3590 /* keyboard intercept */
3591 #if BX_CPU < 2
3592 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3593 #else
3594 // nop
3595 #endif
3596 SET_CF();
3597 break;
3599 case 0x52: // removable media eject
3600 CLEAR_CF();
3601 regs.u.r8.ah = 0; // "ok ejection may proceed"
3602 break;
3604 case 0x83: {
3605 if( regs.u.r8.al == 0 ) {
3606 // Set Interval requested.
3607 if( ( read_byte( 0x40, 0xA0 ) & 1 ) == 0 ) {
3608 // Interval not already set.
3609 write_byte( 0x40, 0xA0, 1 ); // Set status byte.
3610 write_word( 0x40, 0x98, ES ); // Byte location, segment
3611 write_word( 0x40, 0x9A, regs.u.r16.bx ); // Byte location, offset
3612 write_word( 0x40, 0x9C, regs.u.r16.dx ); // Low word, delay
3613 write_word( 0x40, 0x9E, regs.u.r16.cx ); // High word, delay.
3614 CLEAR_CF( );
3615 irqDisable = inb( 0xA1 );
3616 outb( 0xA1, irqDisable & 0xFE );
3617 bRegister = inb_cmos( 0xB ); // Unmask IRQ8 so INT70 will get through.
3618 outb_cmos( 0xB, bRegister | 0x40 ); // Turn on the Periodic Interrupt timer
3619 } else {
3620 // Interval already set.
3621 BX_DEBUG_INT15("int15: Func 83h, failed, already waiting.\n" );
3622 SET_CF();
3623 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3625 } else if( regs.u.r8.al == 1 ) {
3626 // Clear Interval requested
3627 write_byte( 0x40, 0xA0, 0 ); // Clear status byte
3628 CLEAR_CF( );
3629 bRegister = inb_cmos( 0xB );
3630 outb_cmos( 0xB, bRegister & ~0x40 ); // Turn off the Periodic Interrupt timer
3631 } else {
3632 BX_DEBUG_INT15("int15: Func 83h, failed.\n" );
3633 SET_CF();
3634 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3635 regs.u.r8.al--;
3638 break;
3641 case 0x87:
3642 #if BX_CPU < 3
3643 # error "Int15 function 87h not supported on < 80386"
3644 #endif
3645 // +++ should probably have descriptor checks
3646 // +++ should have exception handlers
3648 // turn off interrupts
3649 ASM_START
3650 cli
3651 ASM_END
3653 prev_a20_enable = set_enable_a20(1); // enable A20 line
3655 // 128K max of transfer on 386+ ???
3656 // source == destination ???
3658 // ES:SI points to descriptor table
3659 // offset use initially comments
3660 // ==============================================
3661 // 00..07 Unused zeros Null descriptor
3662 // 08..0f GDT zeros filled in by BIOS
3663 // 10..17 source ssssssss source of data
3664 // 18..1f dest dddddddd destination of data
3665 // 20..27 CS zeros filled in by BIOS
3666 // 28..2f SS zeros filled in by BIOS
3668 //es:si
3669 //eeee0
3670 //0ssss
3671 //-----
3673 // check for access rights of source & dest here
3675 // Initialize GDT descriptor
3676 base15_00 = (ES << 4) + regs.u.r16.si;
3677 base23_16 = ES >> 12;
3678 if (base15_00 < (ES<<4))
3679 base23_16++;
3680 write_word(ES, regs.u.r16.si+0x08+0, 47); // limit 15:00 = 6 * 8bytes/descriptor
3681 write_word(ES, regs.u.r16.si+0x08+2, base15_00);// base 15:00
3682 write_byte(ES, regs.u.r16.si+0x08+4, base23_16);// base 23:16
3683 write_byte(ES, regs.u.r16.si+0x08+5, 0x93); // access
3684 write_word(ES, regs.u.r16.si+0x08+6, 0x0000); // base 31:24/reserved/limit 19:16
3686 // Initialize CS descriptor
3687 write_word(ES, regs.u.r16.si+0x20+0, 0xffff);// limit 15:00 = normal 64K limit
3688 write_word(ES, regs.u.r16.si+0x20+2, 0x0000);// base 15:00
3689 write_byte(ES, regs.u.r16.si+0x20+4, 0x000f);// base 23:16
3690 write_byte(ES, regs.u.r16.si+0x20+5, 0x9b); // access
3691 write_word(ES, regs.u.r16.si+0x20+6, 0x0000);// base 31:24/reserved/limit 19:16
3693 // Initialize SS descriptor
3694 ss = get_SS();
3695 base15_00 = ss << 4;
3696 base23_16 = ss >> 12;
3697 write_word(ES, regs.u.r16.si+0x28+0, 0xffff); // limit 15:00 = normal 64K limit
3698 write_word(ES, regs.u.r16.si+0x28+2, base15_00);// base 15:00
3699 write_byte(ES, regs.u.r16.si+0x28+4, base23_16);// base 23:16
3700 write_byte(ES, regs.u.r16.si+0x28+5, 0x93); // access
3701 write_word(ES, regs.u.r16.si+0x28+6, 0x0000); // base 31:24/reserved/limit 19:16
3703 CX = regs.u.r16.cx;
3704 ASM_START
3705 // Compile generates locals offset info relative to SP.
3706 // Get CX (word count) from stack.
3707 mov bx, sp
3708 SEG SS
3709 mov cx, _int15_function.CX [bx]
3711 // since we need to set SS:SP, save them to the BDA
3712 // for future restore
3713 push eax
3714 xor eax, eax
3715 mov ds, ax
3716 mov 0x0469, ss
3717 mov 0x0467, sp
3719 SEG ES
3720 lgdt [si + 0x08]
3721 SEG CS
3722 lidt [pmode_IDT_info]
3723 ;; perhaps do something with IDT here
3725 ;; set PE bit in CR0
3726 mov eax, cr0
3727 or al, #0x01
3728 mov cr0, eax
3729 ;; far jump to flush CPU queue after transition to protected mode
3730 JMP_AP(0x0020, protected_mode)
3732 protected_mode:
3733 ;; GDT points to valid descriptor table, now load SS, DS, ES
3734 mov ax, #0x28 ;; 101 000 = 5th descriptor in table, TI=GDT, RPL=00
3735 mov ss, ax
3736 mov ax, #0x10 ;; 010 000 = 2nd descriptor in table, TI=GDT, RPL=00
3737 mov ds, ax
3738 mov ax, #0x18 ;; 011 000 = 3rd descriptor in table, TI=GDT, RPL=00
3739 mov es, ax
3740 xor si, si
3741 xor di, di
3742 cld
3743 rep
3744 movsw ;; move CX words from DS:SI to ES:DI
3746 ;; make sure DS and ES limits are 64KB
3747 mov ax, #0x28
3748 mov ds, ax
3749 mov es, ax
3751 ;; reset PG bit in CR0 ???
3752 mov eax, cr0
3753 and al, #0xFE
3754 mov cr0, eax
3756 ;; far jump to flush CPU queue after transition to real mode
3757 JMP_AP(0xf000, real_mode)
3759 real_mode:
3760 ;; restore IDT to normal real-mode defaults
3761 SEG CS
3762 lidt [rmode_IDT_info]
3764 // restore SS:SP from the BDA
3765 xor ax, ax
3766 mov ds, ax
3767 mov ss, 0x0469
3768 mov sp, 0x0467
3769 pop eax
3770 ASM_END
3772 set_enable_a20(prev_a20_enable);
3774 // turn back on interrupts
3775 ASM_START
3776 sti
3777 ASM_END
3779 regs.u.r8.ah = 0;
3780 CLEAR_CF();
3781 break;
3784 case 0x88:
3785 // Get the amount of extended memory (above 1M)
3786 #if BX_CPU < 2
3787 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3788 SET_CF();
3789 #else
3790 regs.u.r8.al = inb_cmos(0x30);
3791 regs.u.r8.ah = inb_cmos(0x31);
3793 // limit to 15M
3794 if(regs.u.r16.ax > 0x3c00)
3795 regs.u.r16.ax = 0x3c00;
3797 CLEAR_CF();
3798 #endif
3799 break;
3801 case 0x90:
3802 /* Device busy interrupt. Called by Int 16h when no key available */
3803 break;
3805 case 0x91:
3806 /* Interrupt complete. Called by Int 16h when key becomes available */
3807 break;
3809 case 0xbf:
3810 BX_INFO("*** int 15h function AH=bf not yet supported!\n");
3811 SET_CF();
3812 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3813 break;
3815 case 0xC0:
3816 #if 0
3817 SET_CF();
3818 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3819 break;
3820 #endif
3821 CLEAR_CF();
3822 regs.u.r8.ah = 0;
3823 regs.u.r16.bx = BIOS_CONFIG_TABLE;
3824 ES = 0xF000;
3825 break;
3827 case 0xc1:
3828 ES = ebda_seg;
3829 CLEAR_CF();
3830 break;
3832 case 0xd8:
3833 bios_printf(BIOS_PRINTF_DEBUG, "EISA BIOS not present\n");
3834 SET_CF();
3835 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3836 break;
3838 default:
3839 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
3840 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
3841 SET_CF();
3842 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3843 break;
3847 #if BX_USE_PS2_MOUSE
3848 void
3849 int15_function_mouse(regs, ES, DS, FLAGS)
3850 pusha_regs_t regs; // REGS pushed via pusha
3851 Bit16u ES, DS, FLAGS;
3853 Bit16u ebda_seg=read_word(0x0040,0x000E);
3854 Bit8u mouse_flags_1, mouse_flags_2;
3855 Bit16u mouse_driver_seg;
3856 Bit16u mouse_driver_offset;
3857 Bit8u comm_byte, prev_command_byte;
3858 Bit8u ret, mouse_data1, mouse_data2, mouse_data3;
3860 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3862 switch (regs.u.r8.ah) {
3863 case 0xC2:
3864 // Return Codes status in AH
3865 // =========================
3866 // 00: success
3867 // 01: invalid subfunction (AL > 7)
3868 // 02: invalid input value (out of allowable range)
3869 // 03: interface error
3870 // 04: resend command received from mouse controller,
3871 // device driver should attempt command again
3872 // 05: cannot enable mouse, since no far call has been installed
3873 // 80/86: mouse service not implemented
3875 switch (regs.u.r8.al) {
3876 case 0: // Disable/Enable Mouse
3877 BX_DEBUG_INT15("case 0:\n");
3878 switch (regs.u.r8.bh) {
3879 case 0: // Disable Mouse
3880 BX_DEBUG_INT15("case 0: disable mouse\n");
3881 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3882 ret = send_to_mouse_ctrl(0xF5); // disable mouse command
3883 if (ret == 0) {
3884 ret = get_mouse_data(&mouse_data1);
3885 if ( (ret == 0) || (mouse_data1 == 0xFA) ) {
3886 CLEAR_CF();
3887 regs.u.r8.ah = 0;
3888 return;
3892 // error
3893 SET_CF();
3894 regs.u.r8.ah = ret;
3895 return;
3896 break;
3898 case 1: // Enable Mouse
3899 BX_DEBUG_INT15("case 1: enable mouse\n");
3900 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
3901 if ( (mouse_flags_2 & 0x80) == 0 ) {
3902 BX_DEBUG_INT15("INT 15h C2 Enable Mouse, no far call handler\n");
3903 SET_CF(); // error
3904 regs.u.r8.ah = 5; // no far call installed
3905 return;
3907 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3908 ret = send_to_mouse_ctrl(0xF4); // enable mouse command
3909 if (ret == 0) {
3910 ret = get_mouse_data(&mouse_data1);
3911 if ( (ret == 0) && (mouse_data1 == 0xFA) ) {
3912 enable_mouse_int_and_events(); // turn IRQ12 and packet generation on
3913 CLEAR_CF();
3914 regs.u.r8.ah = 0;
3915 return;
3918 SET_CF();
3919 regs.u.r8.ah = ret;
3920 return;
3922 default: // invalid subfunction
3923 BX_DEBUG_INT15("INT 15h C2 AL=0, BH=%02x\n", (unsigned) regs.u.r8.bh);
3924 SET_CF(); // error
3925 regs.u.r8.ah = 1; // invalid subfunction
3926 return;
3928 break;
3930 case 1: // Reset Mouse
3931 case 5: // Initialize Mouse
3932 BX_DEBUG_INT15("case 1 or 5:\n");
3933 if (regs.u.r8.al == 5) {
3934 if (regs.u.r8.bh != 3) {
3935 SET_CF();
3936 regs.u.r8.ah = 0x02; // invalid input
3937 return;
3939 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
3940 mouse_flags_2 = (mouse_flags_2 & 0x00) | regs.u.r8.bh;
3941 mouse_flags_1 = 0x00;
3942 write_byte(ebda_seg, 0x0026, mouse_flags_1);
3943 write_byte(ebda_seg, 0x0027, mouse_flags_2);
3946 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3947 ret = send_to_mouse_ctrl(0xFF); // reset mouse command
3948 if (ret == 0) {
3949 ret = get_mouse_data(&mouse_data3);
3950 // if no mouse attached, it will return RESEND
3951 if (mouse_data3 == 0xfe) {
3952 SET_CF();
3953 return;
3955 if (mouse_data3 != 0xfa)
3956 BX_PANIC("Mouse reset returned %02x (should be ack)\n", (unsigned)mouse_data3);
3957 if ( ret == 0 ) {
3958 ret = get_mouse_data(&mouse_data1);
3959 if ( ret == 0 ) {
3960 ret = get_mouse_data(&mouse_data2);
3961 if ( ret == 0 ) {
3962 // turn IRQ12 and packet generation on
3963 enable_mouse_int_and_events();
3964 CLEAR_CF();
3965 regs.u.r8.ah = 0;
3966 regs.u.r8.bl = mouse_data1;
3967 regs.u.r8.bh = mouse_data2;
3968 return;
3974 // error
3975 SET_CF();
3976 regs.u.r8.ah = ret;
3977 return;
3979 case 2: // Set Sample Rate
3980 BX_DEBUG_INT15("case 2:\n");
3981 switch (regs.u.r8.bh) {
3982 case 0: mouse_data1 = 10; break; // 10 reports/sec
3983 case 1: mouse_data1 = 20; break; // 20 reports/sec
3984 case 2: mouse_data1 = 40; break; // 40 reports/sec
3985 case 3: mouse_data1 = 60; break; // 60 reports/sec
3986 case 4: mouse_data1 = 80; break; // 80 reports/sec
3987 case 5: mouse_data1 = 100; break; // 100 reports/sec (default)
3988 case 6: mouse_data1 = 200; break; // 200 reports/sec
3989 default: mouse_data1 = 0;
3991 if (mouse_data1 > 0) {
3992 ret = send_to_mouse_ctrl(0xF3); // set sample rate command
3993 if (ret == 0) {
3994 ret = get_mouse_data(&mouse_data2);
3995 ret = send_to_mouse_ctrl(mouse_data1);
3996 ret = get_mouse_data(&mouse_data2);
3997 CLEAR_CF();
3998 regs.u.r8.ah = 0;
3999 } else {
4000 // error
4001 SET_CF();
4002 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4004 } else {
4005 // error
4006 SET_CF();
4007 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4009 break;
4011 case 3: // Set Resolution
4012 BX_DEBUG_INT15("case 3:\n");
4013 // BX:
4014 // 0 = 25 dpi, 1 count per millimeter
4015 // 1 = 50 dpi, 2 counts per millimeter
4016 // 2 = 100 dpi, 4 counts per millimeter
4017 // 3 = 200 dpi, 8 counts per millimeter
4018 CLEAR_CF();
4019 regs.u.r8.ah = 0;
4020 break;
4022 case 4: // Get Device ID
4023 BX_DEBUG_INT15("case 4:\n");
4024 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4025 ret = send_to_mouse_ctrl(0xF2); // get mouse ID command
4026 if (ret == 0) {
4027 ret = get_mouse_data(&mouse_data1);
4028 ret = get_mouse_data(&mouse_data2);
4029 CLEAR_CF();
4030 regs.u.r8.ah = 0;
4031 regs.u.r8.bh = mouse_data2;
4032 } else {
4033 // error
4034 SET_CF();
4035 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4037 break;
4039 case 6: // Return Status & Set Scaling Factor...
4040 BX_DEBUG_INT15("case 6:\n");
4041 switch (regs.u.r8.bh) {
4042 case 0: // Return Status
4043 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4044 ret = send_to_mouse_ctrl(0xE9); // get mouse info command
4045 if (ret == 0) {
4046 ret = get_mouse_data(&mouse_data1);
4047 if (mouse_data1 != 0xfa)
4048 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
4049 if (ret == 0) {
4050 ret = get_mouse_data(&mouse_data1);
4051 if ( ret == 0 ) {
4052 ret = get_mouse_data(&mouse_data2);
4053 if ( ret == 0 ) {
4054 ret = get_mouse_data(&mouse_data3);
4055 if ( ret == 0 ) {
4056 CLEAR_CF();
4057 regs.u.r8.ah = 0;
4058 regs.u.r8.bl = mouse_data1;
4059 regs.u.r8.cl = mouse_data2;
4060 regs.u.r8.dl = mouse_data3;
4061 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4062 return;
4069 // error
4070 SET_CF();
4071 regs.u.r8.ah = ret;
4072 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4073 return;
4075 case 1: // Set Scaling Factor to 1:1
4076 case 2: // Set Scaling Factor to 2:1
4077 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4078 if (regs.u.r8.bh == 1) {
4079 ret = send_to_mouse_ctrl(0xE6);
4080 } else {
4081 ret = send_to_mouse_ctrl(0xE7);
4083 if (ret == 0) {
4084 get_mouse_data(&mouse_data1);
4085 ret = (mouse_data1 != 0xFA);
4087 if (ret == 0) {
4088 CLEAR_CF();
4089 regs.u.r8.ah = 0;
4090 } else {
4091 // error
4092 SET_CF();
4093 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4095 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4096 break;
4098 default:
4099 BX_PANIC("INT 15h C2 AL=6, BH=%02x\n", (unsigned) regs.u.r8.bh);
4101 break;
4103 case 7: // Set Mouse Handler Address
4104 BX_DEBUG_INT15("case 7:\n");
4105 mouse_driver_seg = ES;
4106 mouse_driver_offset = regs.u.r16.bx;
4107 write_word(ebda_seg, 0x0022, mouse_driver_offset);
4108 write_word(ebda_seg, 0x0024, mouse_driver_seg);
4109 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4110 if (mouse_driver_offset == 0 && mouse_driver_seg == 0) {
4111 /* remove handler */
4112 if ( (mouse_flags_2 & 0x80) != 0 ) {
4113 mouse_flags_2 &= ~0x80;
4114 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4117 else {
4118 /* install handler */
4119 mouse_flags_2 |= 0x80;
4121 write_byte(ebda_seg, 0x0027, mouse_flags_2);
4122 CLEAR_CF();
4123 regs.u.r8.ah = 0;
4124 break;
4126 default:
4127 BX_DEBUG_INT15("case default:\n");
4128 regs.u.r8.ah = 1; // invalid function
4129 SET_CF();
4131 break;
4133 default:
4134 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4135 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4136 SET_CF();
4137 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4138 break;
4141 #endif
4143 void
4144 int15_function32(regs, ES, DS, FLAGS)
4145 pushad_regs_t regs; // REGS pushed via pushad
4146 Bit16u ES, DS, FLAGS;
4148 Bit32u extended_memory_size=0; // 64bits long
4149 Bit16u CX,DX;
4151 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
4153 switch (regs.u.r8.ah) {
4154 case 0x86:
4155 // Wait for CX:DX microseconds. currently using the
4156 // refresh request port 0x61 bit4, toggling every 15usec
4158 CX = regs.u.r16.cx;
4159 DX = regs.u.r16.dx;
4161 ASM_START
4162 ;; Get the count in eax
4163 mov ax, .int15_function32.CX [bp]
4164 shl eax, #16
4165 mov ax, .int15_function32.DX [bp]
4167 ;; convert to numbers of 15usec ticks
4168 mov ebx, #15
4169 xor edx, edx
4170 div eax, ebx
4171 mov ecx, eax
4173 ;; wait for ecx number of refresh requests
4174 in al, #0x61
4175 and al,#0x10
4176 mov ah, al
4178 or ecx, ecx
4179 je int1586_tick_end
4180 int1586_tick:
4181 in al, #0x61
4182 and al,#0x10
4183 cmp al, ah
4184 je int1586_tick
4185 mov ah, al
4186 dec ecx
4187 jnz int1586_tick
4188 int1586_tick_end:
4189 ASM_END
4191 break;
4193 case 0xe8:
4194 switch(regs.u.r8.al)
4196 case 0x20: {
4197 Bit16u e820_table_size = read_word(0xe000, 0x8) * 0x14;
4199 if (regs.u.r32.edx != 0x534D4150) /* SMAP */
4200 goto int15_unimplemented;
4202 if ((regs.u.r16.bx / 0x14) * 0x14 == regs.u.r16.bx) {
4203 if (regs.u.r16.bx + 0x14 <= e820_table_size)
4204 memcpyb(ES, regs.u.r16.di,
4205 0xe000, 0x10 + regs.u.r16.bx, 0x14);
4206 regs.u.r32.ebx += 0x14;
4207 if ((regs.u.r32.ebx + 0x14 - 1) > e820_table_size)
4208 regs.u.r32.ebx = 0;
4209 } else if (regs.u.r16.bx == 1) {
4210 Bit32u base, type;
4211 Bit16u off;
4212 for (off = 0; off < e820_table_size; off += 0x14) {
4213 base = read_dword(0xe000, 0x10 + off);
4214 type = read_dword(0xe000, 0x20 + off);
4215 if ((base >= 0x100000) && (type == 1))
4216 break;
4218 if (off == e820_table_size) {
4219 SET_CF();
4220 break;
4222 memcpyb(ES, regs.u.r16.di, 0xe000, 0x10 + off, 0x14);
4223 regs.u.r32.ebx = 0;
4224 } else { /* AX=E820, DX=534D4150, BX unrecognized */
4225 goto int15_unimplemented;
4228 regs.u.r32.eax = 0x534D4150;
4229 regs.u.r32.ecx = 0x14;
4230 CLEAR_CF();
4231 break;
4234 case 0x01: {
4235 Bit16u off, e820_table_size = read_word(0xe000, 0x8) * 0x14;
4236 Bit32u base, type, size;
4238 // do we have any reason to fail here ?
4239 CLEAR_CF();
4241 // Get the amount of extended memory (above 1M)
4242 regs.u.r8.cl = inb_cmos(0x30);
4243 regs.u.r8.ch = inb_cmos(0x31);
4245 // limit to 15M
4246 if (regs.u.r16.cx > (15*1024))
4247 regs.u.r16.cx = 15*1024;
4249 // Find first RAM E820 entry >= 1MB.
4250 for (off = 0; off < e820_table_size; off += 0x14) {
4251 base = read_dword(0xe000, 0x10 + off);
4252 type = read_dword(0xe000, 0x20 + off);
4253 if ((base >= 0x100000) && (type == 1))
4254 break;
4257 // If there is RAM above 16MB, return amount in 64kB chunks.
4258 regs.u.r16.dx = 0;
4259 if (off != e820_table_size) {
4260 size = base + read_dword(0xe000, 0x18 + off);
4261 if (size > 0x1000000) {
4262 size -= 0x1000000;
4263 regs.u.r16.dx = (Bit16u)(size >> 16);
4267 // Set configured memory equal to extended memory
4268 regs.u.r16.ax = regs.u.r16.cx;
4269 regs.u.r16.bx = regs.u.r16.dx;
4270 break;
4272 default: /* AH=0xE8?? but not implemented */
4273 goto int15_unimplemented;
4275 break;
4276 int15_unimplemented:
4277 // fall into the default
4278 default:
4279 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4280 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4281 SET_CF();
4282 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4283 break;
4287 void
4288 int16_function(DI, SI, BP, SP, BX, DX, CX, AX, FLAGS)
4289 Bit16u DI, SI, BP, SP, BX, DX, CX, AX, FLAGS;
4291 Bit8u scan_code, ascii_code, shift_flags, count;
4292 Bit16u kbd_code, max;
4294 BX_DEBUG_INT16("int16: AX=%04x BX=%04x CX=%04x DX=%04x \n", AX, BX, CX, DX);
4296 switch (GET_AH()) {
4297 case 0x00: /* read keyboard input */
4299 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4300 BX_PANIC("KBD: int16h: out of keyboard input\n");
4302 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4303 else if (ascii_code == 0xE0) ascii_code = 0;
4304 AX = (scan_code << 8) | ascii_code;
4305 break;
4307 case 0x01: /* check keyboard status */
4308 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4309 SET_ZF();
4310 return;
4312 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4313 else if (ascii_code == 0xE0) ascii_code = 0;
4314 AX = (scan_code << 8) | ascii_code;
4315 CLEAR_ZF();
4316 break;
4318 case 0x02: /* get shift flag status */
4319 shift_flags = read_byte(0x0040, 0x17);
4320 SET_AL(shift_flags);
4321 break;
4323 case 0x05: /* store key-stroke into buffer */
4324 if ( !enqueue_key(GET_CH(), GET_CL()) ) {
4325 SET_AL(1);
4327 else {
4328 SET_AL(0);
4330 break;
4332 case 0x09: /* GET KEYBOARD FUNCTIONALITY */
4333 // bit Bochs Description
4334 // 7 0 reserved
4335 // 6 0 INT 16/AH=20h-22h supported (122-key keyboard support)
4336 // 5 1 INT 16/AH=10h-12h supported (enhanced keyboard support)
4337 // 4 1 INT 16/AH=0Ah supported
4338 // 3 0 INT 16/AX=0306h supported
4339 // 2 0 INT 16/AX=0305h supported
4340 // 1 0 INT 16/AX=0304h supported
4341 // 0 0 INT 16/AX=0300h supported
4342 //
4343 SET_AL(0x30);
4344 break;
4346 case 0x0A: /* GET KEYBOARD ID */
4347 count = 2;
4348 kbd_code = 0x0;
4349 outb(0x60, 0xf2);
4350 /* Wait for data */
4351 max=0xffff;
4352 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4353 if (max>0x0) {
4354 if ((inb(0x60) == 0xfa)) {
4355 do {
4356 max=0xffff;
4357 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4358 if (max>0x0) {
4359 kbd_code >>= 8;
4360 kbd_code |= (inb(0x60) << 8);
4362 } while (--count>0);
4365 BX=kbd_code;
4366 break;
4368 case 0x10: /* read MF-II keyboard input */
4370 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4371 BX_PANIC("KBD: int16h: out of keyboard input\n");
4373 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4374 AX = (scan_code << 8) | ascii_code;
4375 break;
4377 case 0x11: /* check MF-II keyboard status */
4378 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4379 SET_ZF();
4380 return;
4382 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4383 AX = (scan_code << 8) | ascii_code;
4384 CLEAR_ZF();
4385 break;
4387 case 0x12: /* get extended keyboard status */
4388 shift_flags = read_byte(0x0040, 0x17);
4389 SET_AL(shift_flags);
4390 shift_flags = read_byte(0x0040, 0x18);
4391 SET_AH(shift_flags);
4392 BX_DEBUG_INT16("int16: func 12 sending %04x\n",AX);
4393 break;
4395 case 0x92: /* keyboard capability check called by DOS 5.0+ keyb */
4396 SET_AH(0x80); // function int16 ah=0x10-0x12 supported
4397 break;
4399 case 0xA2: /* 122 keys capability check called by DOS 5.0+ keyb */
4400 // don't change AH : function int16 ah=0x20-0x22 NOT supported
4401 break;
4403 case 0x6F:
4404 if (GET_AL() == 0x08)
4405 SET_AH(0x02); // unsupported, aka normal keyboard
4407 default:
4408 BX_INFO("KBD: unsupported int 16h function %02x\n", GET_AH());
4412 unsigned int
4413 dequeue_key(scan_code, ascii_code, incr)
4414 Bit8u *scan_code;
4415 Bit8u *ascii_code;
4416 unsigned int incr;
4418 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail;
4419 Bit16u ss;
4420 Bit8u acode, scode;
4422 #if BX_CPU < 2
4423 buffer_start = 0x001E;
4424 buffer_end = 0x003E;
4425 #else
4426 buffer_start = read_word(0x0040, 0x0080);
4427 buffer_end = read_word(0x0040, 0x0082);
4428 #endif
4430 buffer_head = read_word(0x0040, 0x001a);
4431 buffer_tail = read_word(0x0040, 0x001c);
4433 if (buffer_head != buffer_tail) {
4434 ss = get_SS();
4435 acode = read_byte(0x0040, buffer_head);
4436 scode = read_byte(0x0040, buffer_head+1);
4437 write_byte(ss, ascii_code, acode);
4438 write_byte(ss, scan_code, scode);
4440 if (incr) {
4441 buffer_head += 2;
4442 if (buffer_head >= buffer_end)
4443 buffer_head = buffer_start;
4444 write_word(0x0040, 0x001a, buffer_head);
4446 return(1);
4448 else {
4449 return(0);
4453 static char panic_msg_keyb_buffer_full[] = "%s: keyboard input buffer full\n";
4455 Bit8u
4456 inhibit_mouse_int_and_events()
4458 Bit8u command_byte, prev_command_byte;
4460 // Turn off IRQ generation and aux data line
4461 if ( inb(0x64) & 0x02 )
4462 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4463 outb(0x64, 0x20); // get command byte
4464 while ( (inb(0x64) & 0x01) != 0x01 );
4465 prev_command_byte = inb(0x60);
4466 command_byte = prev_command_byte;
4467 //while ( (inb(0x64) & 0x02) );
4468 if ( inb(0x64) & 0x02 )
4469 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4470 command_byte &= 0xfd; // turn off IRQ 12 generation
4471 command_byte |= 0x20; // disable mouse serial clock line
4472 outb(0x64, 0x60); // write command byte
4473 outb(0x60, command_byte);
4474 return(prev_command_byte);
4477 void
4478 enable_mouse_int_and_events()
4480 Bit8u command_byte;
4482 // Turn on IRQ generation and aux data line
4483 if ( inb(0x64) & 0x02 )
4484 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4485 outb(0x64, 0x20); // get command byte
4486 while ( (inb(0x64) & 0x01) != 0x01 );
4487 command_byte = inb(0x60);
4488 //while ( (inb(0x64) & 0x02) );
4489 if ( inb(0x64) & 0x02 )
4490 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4491 command_byte |= 0x02; // turn on IRQ 12 generation
4492 command_byte &= 0xdf; // enable mouse serial clock line
4493 outb(0x64, 0x60); // write command byte
4494 outb(0x60, command_byte);
4497 Bit8u
4498 send_to_mouse_ctrl(sendbyte)
4499 Bit8u sendbyte;
4501 Bit8u response;
4503 // wait for chance to write to ctrl
4504 if ( inb(0x64) & 0x02 )
4505 BX_PANIC(panic_msg_keyb_buffer_full,"sendmouse");
4506 outb(0x64, 0xD4);
4507 outb(0x60, sendbyte);
4508 return(0);
4512 Bit8u
4513 get_mouse_data(data)
4514 Bit8u *data;
4516 Bit8u response;
4517 Bit16u ss;
4519 while ( (inb(0x64) & 0x21) != 0x21 ) {
4522 response = inb(0x60);
4524 ss = get_SS();
4525 write_byte(ss, data, response);
4526 return(0);
4529 void
4530 set_kbd_command_byte(command_byte)
4531 Bit8u command_byte;
4533 if ( inb(0x64) & 0x02 )
4534 BX_PANIC(panic_msg_keyb_buffer_full,"setkbdcomm");
4535 outb(0x64, 0xD4);
4537 outb(0x64, 0x60); // write command byte
4538 outb(0x60, command_byte);
4541 void
4542 int09_function(DI, SI, BP, SP, BX, DX, CX, AX)
4543 Bit16u DI, SI, BP, SP, BX, DX, CX, AX;
4545 Bit8u scancode, asciicode, shift_flags;
4546 Bit8u mf2_flags, mf2_state, led_flags;
4548 //
4549 // DS has been set to F000 before call
4550 //
4553 scancode = GET_AL();
4555 if (scancode == 0) {
4556 BX_INFO("KBD: int09 handler: AL=0\n");
4557 return;
4561 shift_flags = read_byte(0x0040, 0x17);
4562 mf2_flags = read_byte(0x0040, 0x18);
4563 mf2_state = read_byte(0x0040, 0x96);
4564 led_flags = read_byte(0x0040, 0x97);
4565 asciicode = 0;
4567 switch (scancode) {
4568 case 0x3a: /* Caps Lock press */
4569 shift_flags ^= 0x40;
4570 write_byte(0x0040, 0x17, shift_flags);
4571 mf2_flags |= 0x40;
4572 write_byte(0x0040, 0x18, mf2_flags);
4573 led_flags ^= 0x04;
4574 write_byte(0x0040, 0x97, led_flags);
4575 break;
4576 case 0xba: /* Caps Lock release */
4577 mf2_flags &= ~0x40;
4578 write_byte(0x0040, 0x18, mf2_flags);
4579 break;
4581 case 0x2a: /* L Shift press */
4582 /*shift_flags &= ~0x40;*/
4583 shift_flags |= 0x02;
4584 write_byte(0x0040, 0x17, shift_flags);
4585 led_flags &= ~0x04;
4586 write_byte(0x0040, 0x97, led_flags);
4587 break;
4588 case 0xaa: /* L Shift release */
4589 shift_flags &= ~0x02;
4590 write_byte(0x0040, 0x17, shift_flags);
4591 break;
4593 case 0x36: /* R Shift press */
4594 /*shift_flags &= ~0x40;*/
4595 shift_flags |= 0x01;
4596 write_byte(0x0040, 0x17, shift_flags);
4597 led_flags &= ~0x04;
4598 write_byte(0x0040, 0x97, led_flags);
4599 break;
4600 case 0xb6: /* R Shift release */
4601 shift_flags &= ~0x01;
4602 write_byte(0x0040, 0x17, shift_flags);
4603 break;
4605 case 0x1d: /* Ctrl press */
4606 shift_flags |= 0x04;
4607 write_byte(0x0040, 0x17, shift_flags);
4608 if (mf2_state & 0x01) {
4609 mf2_flags |= 0x04;
4610 } else {
4611 mf2_flags |= 0x01;
4613 write_byte(0x0040, 0x18, mf2_flags);
4614 break;
4615 case 0x9d: /* Ctrl release */
4616 shift_flags &= ~0x04;
4617 write_byte(0x0040, 0x17, shift_flags);
4618 if (mf2_state & 0x01) {
4619 mf2_flags &= ~0x04;
4620 } else {
4621 mf2_flags &= ~0x01;
4623 write_byte(0x0040, 0x18, mf2_flags);
4624 break;
4626 case 0x38: /* Alt press */
4627 shift_flags |= 0x08;
4628 write_byte(0x0040, 0x17, shift_flags);
4629 if (mf2_state & 0x01) {
4630 mf2_flags |= 0x08;
4631 } else {
4632 mf2_flags |= 0x02;
4634 write_byte(0x0040, 0x18, mf2_flags);
4635 break;
4636 case 0xb8: /* Alt release */
4637 shift_flags &= ~0x08;
4638 write_byte(0x0040, 0x17, shift_flags);
4639 if (mf2_state & 0x01) {
4640 mf2_flags &= ~0x08;
4641 } else {
4642 mf2_flags &= ~0x02;
4644 write_byte(0x0040, 0x18, mf2_flags);
4645 break;
4647 case 0x45: /* Num Lock press */
4648 if ((mf2_state & 0x01) == 0) {
4649 mf2_flags |= 0x20;
4650 write_byte(0x0040, 0x18, mf2_flags);
4651 shift_flags ^= 0x20;
4652 led_flags ^= 0x02;
4653 write_byte(0x0040, 0x17, shift_flags);
4654 write_byte(0x0040, 0x97, led_flags);
4656 break;
4657 case 0xc5: /* Num Lock release */
4658 if ((mf2_state & 0x01) == 0) {
4659 mf2_flags &= ~0x20;
4660 write_byte(0x0040, 0x18, mf2_flags);
4662 break;
4664 case 0x46: /* Scroll Lock press */
4665 mf2_flags |= 0x10;
4666 write_byte(0x0040, 0x18, mf2_flags);
4667 shift_flags ^= 0x10;
4668 led_flags ^= 0x01;
4669 write_byte(0x0040, 0x17, shift_flags);
4670 write_byte(0x0040, 0x97, led_flags);
4671 break;
4673 case 0xc6: /* Scroll Lock release */
4674 mf2_flags &= ~0x10;
4675 write_byte(0x0040, 0x18, mf2_flags);
4676 break;
4678 case 0x53: /* Del */
4679 if ((shift_flags & 0x0c) == 0x0c) /* Ctrl + Alt */
4680 machine_reset();
4681 /* Fall through */
4682 default:
4683 if (scancode & 0x80) return; /* toss key releases ... */
4684 if (scancode > MAX_SCAN_CODE) {
4685 BX_INFO("KBD: int09h_handler(): unknown scancode read!\n");
4686 return;
4688 if (shift_flags & 0x08) { /* ALT */
4689 asciicode = scan_to_scanascii[scancode].alt;
4690 scancode = scan_to_scanascii[scancode].alt >> 8;
4692 else if (shift_flags & 0x04) { /* CONTROL */
4693 asciicode = scan_to_scanascii[scancode].control;
4694 scancode = scan_to_scanascii[scancode].control >> 8;
4696 else if (shift_flags & 0x03) { /* LSHIFT + RSHIFT */
4697 /* check if lock state should be ignored
4698 * because a SHIFT key are pressed */
4700 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
4701 asciicode = scan_to_scanascii[scancode].normal;
4702 scancode = scan_to_scanascii[scancode].normal >> 8;
4704 else {
4705 asciicode = scan_to_scanascii[scancode].shift;
4706 scancode = scan_to_scanascii[scancode].shift >> 8;
4709 else {
4710 /* check if lock is on */
4711 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
4712 asciicode = scan_to_scanascii[scancode].shift;
4713 scancode = scan_to_scanascii[scancode].shift >> 8;
4715 else {
4716 asciicode = scan_to_scanascii[scancode].normal;
4717 scancode = scan_to_scanascii[scancode].normal >> 8;
4720 if (scancode==0 && asciicode==0) {
4721 BX_INFO("KBD: int09h_handler(): scancode & asciicode are zero?\n");
4723 enqueue_key(scancode, asciicode);
4724 break;
4726 mf2_state &= ~0x01;
4729 unsigned int
4730 enqueue_key(scan_code, ascii_code)
4731 Bit8u scan_code, ascii_code;
4733 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail, temp_tail;
4735 //BX_INFO("KBD: enqueue_key() called scan:%02x, ascii:%02x\n",
4736 // scan_code, ascii_code);
4738 #if BX_CPU < 2
4739 buffer_start = 0x001E;
4740 buffer_end = 0x003E;
4741 #else
4742 buffer_start = read_word(0x0040, 0x0080);
4743 buffer_end = read_word(0x0040, 0x0082);
4744 #endif
4746 buffer_head = read_word(0x0040, 0x001A);
4747 buffer_tail = read_word(0x0040, 0x001C);
4749 temp_tail = buffer_tail;
4750 buffer_tail += 2;
4751 if (buffer_tail >= buffer_end)
4752 buffer_tail = buffer_start;
4754 if (buffer_tail == buffer_head) {
4755 return(0);
4758 write_byte(0x0040, temp_tail, ascii_code);
4759 write_byte(0x0040, temp_tail+1, scan_code);
4760 write_word(0x0040, 0x001C, buffer_tail);
4761 return(1);
4765 void
4766 int74_function(make_farcall, Z, Y, X, status)
4767 Bit16u make_farcall, Z, Y, X, status;
4769 Bit16u ebda_seg=read_word(0x0040,0x000E);
4770 Bit8u in_byte, index, package_count;
4771 Bit8u mouse_flags_1, mouse_flags_2;
4773 BX_DEBUG_INT74("entering int74_function\n");
4774 make_farcall = 0;
4776 in_byte = inb(0x64);
4777 if ( (in_byte & 0x21) != 0x21 ) {
4778 return;
4780 in_byte = inb(0x60);
4781 BX_DEBUG_INT74("int74: read byte %02x\n", in_byte);
4783 mouse_flags_1 = read_byte(ebda_seg, 0x0026);
4784 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4786 if ( (mouse_flags_2 & 0x80) != 0x80 ) {
4787 // BX_PANIC("int74_function:\n");
4788 return;
4791 package_count = mouse_flags_2 & 0x07;
4792 index = mouse_flags_1 & 0x07;
4793 write_byte(ebda_seg, 0x28 + index, in_byte);
4795 if ( (index+1) >= package_count ) {
4796 BX_DEBUG_INT74("int74_function: make_farcall=1\n");
4797 status = read_byte(ebda_seg, 0x0028 + 0);
4798 X = read_byte(ebda_seg, 0x0028 + 1);
4799 Y = read_byte(ebda_seg, 0x0028 + 2);
4800 Z = 0;
4801 mouse_flags_1 = 0;
4802 // check if far call handler installed
4803 if (mouse_flags_2 & 0x80)
4804 make_farcall = 1;
4806 else {
4807 mouse_flags_1++;
4809 write_byte(ebda_seg, 0x0026, mouse_flags_1);
4812 #define SET_DISK_RET_STATUS(status) write_byte(0x0040, 0x0074, status)
4814 #if BX_USE_ATADRV
4816 void
4817 int13_harddisk(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
4818 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
4820 Bit32u lba;
4821 Bit16u ebda_seg=read_word(0x0040,0x000E);
4822 Bit16u cylinder, head, sector;
4823 Bit16u segment, offset;
4824 Bit16u npc, nph, npspt, nlc, nlh, nlspt;
4825 Bit16u size, count;
4826 Bit8u device, status;
4828 BX_DEBUG_INT13_HD("int13_harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
4830 write_byte(0x0040, 0x008e, 0); // clear completion flag
4832 // basic check : device has to be defined
4833 if ( (GET_ELDL() < 0x80) || (GET_ELDL() >= 0x80 + BX_MAX_ATA_DEVICES) ) {
4834 BX_INFO("int13_harddisk: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
4835 goto int13_fail;
4838 // Get the ata channel
4839 device=read_byte(ebda_seg,&EbdaData->ata.hdidmap[GET_ELDL()-0x80]);
4841 // basic check : device has to be valid
4842 if (device >= BX_MAX_ATA_DEVICES) {
4843 BX_INFO("int13_harddisk: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
4844 goto int13_fail;
4847 switch (GET_AH()) {
4849 case 0x00: /* disk controller reset */
4850 ata_reset (device);
4851 goto int13_success;
4852 break;
4854 case 0x01: /* read disk status */
4855 status = read_byte(0x0040, 0x0074);
4856 SET_AH(status);
4857 SET_DISK_RET_STATUS(0);
4858 /* set CF if error status read */
4859 if (status) goto int13_fail_nostatus;
4860 else goto int13_success_noah;
4861 break;
4863 case 0x02: // read disk sectors
4864 case 0x03: // write disk sectors
4865 case 0x04: // verify disk sectors
4867 count = GET_AL();
4868 cylinder = GET_CH();
4869 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
4870 sector = (GET_CL() & 0x3f);
4871 head = GET_DH();
4873 segment = ES;
4874 offset = BX;
4876 if ( (count > 128) || (count == 0) ) {
4877 BX_INFO("int13_harddisk: function %02x, count out of range!\n",GET_AH());
4878 goto int13_fail;
4881 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
4882 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
4883 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
4885 // sanity check on cyl heads, sec
4886 if( (cylinder >= nlc) || (head >= nlh) || (sector > nlspt )) {
4887 BX_INFO("int13_harddisk: function %02x, parameters out of range %04x/%04x/%04x!\n", GET_AH(), cylinder, head, sector);
4888 goto int13_fail;
4891 // FIXME verify
4892 if ( GET_AH() == 0x04 ) goto int13_success;
4894 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
4895 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
4897 // if needed, translate lchs to lba, and execute command
4898 if ( (nph != nlh) || (npspt != nlspt)) {
4899 lba = ((((Bit32u)cylinder * (Bit32u)nlh) + (Bit32u)head) * (Bit32u)nlspt) + (Bit32u)sector - 1;
4900 sector = 0; // this forces the command to be lba
4903 if ( GET_AH() == 0x02 )
4904 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, cylinder, head, sector, lba, segment, offset);
4905 else
4906 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, cylinder, head, sector, lba, segment, offset);
4908 // Set nb of sector transferred
4909 SET_AL(read_word(ebda_seg, &EbdaData->ata.trsfsectors));
4911 if (status != 0) {
4912 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
4913 SET_AH(0x0c);
4914 goto int13_fail_noah;
4917 goto int13_success;
4918 break;
4920 case 0x05: /* format disk track */
4921 BX_INFO("format disk track called\n");
4922 goto int13_success;
4923 return;
4924 break;
4926 case 0x08: /* read disk drive parameters */
4928 // Get logical geometry from table
4929 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
4930 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
4931 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
4932 count = read_byte(ebda_seg, &EbdaData->ata.hdcount);
4934 nlc = nlc - 2; /* 0 based , last sector not used */
4935 SET_AL(0);
4936 SET_CH(nlc & 0xff);
4937 SET_CL(((nlc >> 2) & 0xc0) | (nlspt & 0x3f));
4938 SET_DH(nlh - 1);
4939 SET_DL(count); /* FIXME returns 0, 1, or n hard drives */
4941 // FIXME should set ES & DI
4943 goto int13_success;
4944 break;
4946 case 0x10: /* check drive ready */
4947 // should look at 40:8E also???
4949 // Read the status from controller
4950 status = inb(read_word(ebda_seg, &EbdaData->ata.channels[device/2].iobase1) + ATA_CB_STAT);
4951 if ( (status & ( ATA_CB_STAT_BSY | ATA_CB_STAT_RDY )) == ATA_CB_STAT_RDY ) {
4952 goto int13_success;
4954 else {
4955 SET_AH(0xAA);
4956 goto int13_fail_noah;
4958 break;
4960 case 0x15: /* read disk drive size */
4962 // Get physical geometry from table
4963 npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
4964 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
4965 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
4967 // Compute sector count seen by int13
4968 lba = (Bit32u)(npc - 1) * (Bit32u)nph * (Bit32u)npspt;
4969 CX = lba >> 16;
4970 DX = lba & 0xffff;
4972 SET_AH(3); // hard disk accessible
4973 goto int13_success_noah;
4974 break;
4976 case 0x41: // IBM/MS installation check
4977 BX=0xaa55; // install check
4978 SET_AH(0x30); // EDD 3.0
4979 CX=0x0007; // ext disk access and edd, removable supported
4980 goto int13_success_noah;
4981 break;
4983 case 0x42: // IBM/MS extended read
4984 case 0x43: // IBM/MS extended write
4985 case 0x44: // IBM/MS verify
4986 case 0x47: // IBM/MS extended seek
4988 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
4989 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
4990 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
4992 // Can't use 64 bits lba
4993 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
4994 if (lba != 0L) {
4995 BX_PANIC("int13_harddisk: function %02x. Can't use 64bits lba\n",GET_AH());
4996 goto int13_fail;
4999 // Get 32 bits lba and check
5000 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5001 if (lba >= read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors) ) {
5002 BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH());
5003 goto int13_fail;
5006 // If verify or seek
5007 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
5008 goto int13_success;
5010 // Execute the command
5011 if ( GET_AH() == 0x42 )
5012 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, 0, 0, 0, lba, segment, offset);
5013 else
5014 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, 0, 0, 0, lba, segment, offset);
5016 count=read_word(ebda_seg, &EbdaData->ata.trsfsectors);
5017 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5019 if (status != 0) {
5020 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
5021 SET_AH(0x0c);
5022 goto int13_fail_noah;
5025 goto int13_success;
5026 break;
5028 case 0x45: // IBM/MS lock/unlock drive
5029 case 0x49: // IBM/MS extended media change
5030 goto int13_success; // Always success for HD
5031 break;
5033 case 0x46: // IBM/MS eject media
5034 SET_AH(0xb2); // Volume Not Removable
5035 goto int13_fail_noah; // Always fail for HD
5036 break;
5038 case 0x48: // IBM/MS get drive parameters
5039 size=read_word(DS,SI+(Bit16u)&Int13DPT->size);
5041 // Buffer is too small
5042 if(size < 0x1a)
5043 goto int13_fail;
5045 // EDD 1.x
5046 if(size >= 0x1a) {
5047 Bit16u blksize;
5049 npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
5050 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
5051 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
5052 lba = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors);
5053 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5055 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5056 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x02); // geometry is valid
5057 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, (Bit32u)npc);
5058 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, (Bit32u)nph);
5059 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, (Bit32u)npspt);
5060 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, lba); // FIXME should be Bit64
5061 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0L);
5062 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5065 // EDD 2.x
5066 if(size >= 0x1e) {
5067 Bit8u channel, dev, irq, mode, checksum, i, translation;
5068 Bit16u iobase1, iobase2, options;
5070 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5072 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5073 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5075 // Fill in dpte
5076 channel = device / 2;
5077 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5078 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5079 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5080 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5081 translation = read_byte(ebda_seg, &EbdaData->ata.devices[device].translation);
5083 options = (translation==ATA_TRANSLATION_NONE?0:1<<3); // chs translation
5084 options |= (1<<4); // lba translation
5085 options |= (mode==ATA_MODE_PIO32?1:0<<7);
5086 options |= (translation==ATA_TRANSLATION_LBA?1:0<<9);
5087 options |= (translation==ATA_TRANSLATION_RECHS?3:0<<9);
5089 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5090 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2);
5091 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5092 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5093 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5094 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5095 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5096 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5097 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5098 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5099 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5101 checksum=0;
5102 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, (&EbdaData->ata.dpte) + i);
5103 checksum = ~checksum;
5104 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5107 // EDD 3.x
5108 if(size >= 0x42) {
5109 Bit8u channel, iface, checksum, i;
5110 Bit16u iobase1;
5112 channel = device / 2;
5113 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5114 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5116 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5117 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5118 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5119 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5120 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5122 if (iface==ATA_IFACE_ISA) {
5123 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5124 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5125 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5126 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5128 else {
5129 // FIXME PCI
5131 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5132 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5133 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5134 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5136 if (iface==ATA_IFACE_ISA) {
5137 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5138 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5139 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5141 else {
5142 // FIXME PCI
5144 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5145 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5146 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5147 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5149 checksum=0;
5150 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5151 checksum = ~checksum;
5152 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5155 goto int13_success;
5156 break;
5158 case 0x4e: // // IBM/MS set hardware configuration
5159 // DMA, prefetch, PIO maximum not supported
5160 switch (GET_AL()) {
5161 case 0x01:
5162 case 0x03:
5163 case 0x04:
5164 case 0x06:
5165 goto int13_success;
5166 break;
5167 default :
5168 goto int13_fail;
5170 break;
5172 case 0x09: /* initialize drive parameters */
5173 case 0x0c: /* seek to specified cylinder */
5174 case 0x0d: /* alternate disk reset */
5175 case 0x11: /* recalibrate */
5176 case 0x14: /* controller internal diagnostic */
5177 BX_INFO("int13h_harddisk function %02xh unimplemented, returns success\n", GET_AH());
5178 goto int13_success;
5179 break;
5181 case 0x0a: /* read disk sectors with ECC */
5182 case 0x0b: /* write disk sectors with ECC */
5183 case 0x18: // set media type for format
5184 case 0x50: // IBM/MS send packet command
5185 default:
5186 BX_INFO("int13_harddisk function %02xh unsupported, returns fail\n", GET_AH());
5187 goto int13_fail;
5188 break;
5191 int13_fail:
5192 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5193 int13_fail_noah:
5194 SET_DISK_RET_STATUS(GET_AH());
5195 int13_fail_nostatus:
5196 SET_CF(); // error occurred
5197 return;
5199 int13_success:
5200 SET_AH(0x00); // no error
5201 int13_success_noah:
5202 SET_DISK_RET_STATUS(0x00);
5203 CLEAR_CF(); // no error
5204 return;
5207 // ---------------------------------------------------------------------------
5208 // Start of int13 for cdrom
5209 // ---------------------------------------------------------------------------
5211 void
5212 int13_cdrom(EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
5213 Bit16u EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
5215 Bit16u ebda_seg=read_word(0x0040,0x000E);
5216 Bit8u device, status, locks;
5217 Bit8u atacmd[12];
5218 Bit32u lba;
5219 Bit16u count, segment, offset, i, size;
5221 BX_DEBUG_INT13_CD("int13_cdrom: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5222 // BX_DEBUG_INT13_CD("int13_cdrom: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
5224 SET_DISK_RET_STATUS(0x00);
5226 /* basic check : device should be 0xE0+ */
5227 if( (GET_ELDL() < 0xE0) || (GET_ELDL() >= 0xE0+BX_MAX_ATA_DEVICES) ) {
5228 BX_INFO("int13_cdrom: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
5229 goto int13_fail;
5232 // Get the ata channel
5233 device=read_byte(ebda_seg,&EbdaData->ata.cdidmap[GET_ELDL()-0xE0]);
5235 /* basic check : device has to be valid */
5236 if (device >= BX_MAX_ATA_DEVICES) {
5237 BX_INFO("int13_cdrom: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
5238 goto int13_fail;
5241 switch (GET_AH()) {
5243 // all those functions return SUCCESS
5244 case 0x00: /* disk controller reset */
5245 case 0x09: /* initialize drive parameters */
5246 case 0x0c: /* seek to specified cylinder */
5247 case 0x0d: /* alternate disk reset */
5248 case 0x10: /* check drive ready */
5249 case 0x11: /* recalibrate */
5250 case 0x14: /* controller internal diagnostic */
5251 case 0x16: /* detect disk change */
5252 goto int13_success;
5253 break;
5255 // all those functions return disk write-protected
5256 case 0x03: /* write disk sectors */
5257 case 0x05: /* format disk track */
5258 case 0x43: // IBM/MS extended write
5259 SET_AH(0x03);
5260 goto int13_fail_noah;
5261 break;
5263 case 0x01: /* read disk status */
5264 status = read_byte(0x0040, 0x0074);
5265 SET_AH(status);
5266 SET_DISK_RET_STATUS(0);
5268 /* set CF if error status read */
5269 if (status) goto int13_fail_nostatus;
5270 else goto int13_success_noah;
5271 break;
5273 case 0x15: /* read disk drive size */
5274 SET_AH(0x02);
5275 goto int13_fail_noah;
5276 break;
5278 case 0x41: // IBM/MS installation check
5279 BX=0xaa55; // install check
5280 SET_AH(0x30); // EDD 2.1
5281 CX=0x0007; // ext disk access, removable and edd
5282 goto int13_success_noah;
5283 break;
5285 case 0x42: // IBM/MS extended read
5286 case 0x44: // IBM/MS verify sectors
5287 case 0x47: // IBM/MS extended seek
5289 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
5290 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
5291 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
5293 // Can't use 64 bits lba
5294 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
5295 if (lba != 0L) {
5296 BX_PANIC("int13_cdrom: function %02x. Can't use 64bits lba\n",GET_AH());
5297 goto int13_fail;
5300 // Get 32 bits lba
5301 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5303 // If verify or seek
5304 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
5305 goto int13_success;
5307 memsetb(get_SS(),atacmd,0,12);
5308 atacmd[0]=0x28; // READ command
5309 atacmd[7]=(count & 0xff00) >> 8; // Sectors
5310 atacmd[8]=(count & 0x00ff); // Sectors
5311 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
5312 atacmd[3]=(lba & 0x00ff0000) >> 16;
5313 atacmd[4]=(lba & 0x0000ff00) >> 8;
5314 atacmd[5]=(lba & 0x000000ff);
5315 status = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, count*2048L, ATA_DATA_IN, segment,offset);
5317 count = (Bit16u)(read_dword(ebda_seg, &EbdaData->ata.trsfbytes) >> 11);
5318 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5320 if (status != 0) {
5321 BX_INFO("int13_cdrom: function %02x, status %02x !\n",GET_AH(),status);
5322 SET_AH(0x0c);
5323 goto int13_fail_noah;
5326 goto int13_success;
5327 break;
5329 case 0x45: // IBM/MS lock/unlock drive
5330 if (GET_AL() > 2) goto int13_fail;
5332 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5334 switch (GET_AL()) {
5335 case 0 : // lock
5336 if (locks == 0xff) {
5337 SET_AH(0xb4);
5338 SET_AL(1);
5339 goto int13_fail_noah;
5341 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, ++locks);
5342 SET_AL(1);
5343 break;
5344 case 1 : // unlock
5345 if (locks == 0x00) {
5346 SET_AH(0xb0);
5347 SET_AL(0);
5348 goto int13_fail_noah;
5350 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, --locks);
5351 SET_AL(locks==0?0:1);
5352 break;
5353 case 2 : // status
5354 SET_AL(locks==0?0:1);
5355 break;
5357 goto int13_success;
5358 break;
5360 case 0x46: // IBM/MS eject media
5361 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5363 if (locks != 0) {
5364 SET_AH(0xb1); // media locked
5365 goto int13_fail_noah;
5367 // FIXME should handle 0x31 no media in device
5368 // FIXME should handle 0xb5 valid request failed
5370 // Call removable media eject
5371 ASM_START
5372 push bp
5373 mov bp, sp
5375 mov ah, #0x52
5376 int 15
5377 mov _int13_cdrom.status + 2[bp], ah
5378 jnc int13_cdrom_rme_end
5379 mov _int13_cdrom.status, #1
5380 int13_cdrom_rme_end:
5381 pop bp
5382 ASM_END
5384 if (status != 0) {
5385 SET_AH(0xb1); // media locked
5386 goto int13_fail_noah;
5389 goto int13_success;
5390 break;
5392 case 0x48: // IBM/MS get drive parameters
5393 size = read_word(DS,SI+(Bit16u)&Int13Ext->size);
5395 // Buffer is too small
5396 if(size < 0x1a)
5397 goto int13_fail;
5399 // EDD 1.x
5400 if(size >= 0x1a) {
5401 Bit16u cylinders, heads, spt, blksize;
5403 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5405 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5406 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x74); // removable, media change, lockable, max values
5407 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0xffffffff);
5408 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, 0xffffffff);
5409 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, 0xffffffff);
5410 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, 0xffffffff); // FIXME should be Bit64
5411 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0xffffffff);
5412 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5415 // EDD 2.x
5416 if(size >= 0x1e) {
5417 Bit8u channel, dev, irq, mode, checksum, i;
5418 Bit16u iobase1, iobase2, options;
5420 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5422 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5423 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5425 // Fill in dpte
5426 channel = device / 2;
5427 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5428 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5429 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5430 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5432 // FIXME atapi device
5433 options = (1<<4); // lba translation
5434 options |= (1<<5); // removable device
5435 options |= (1<<6); // atapi device
5436 options |= (mode==ATA_MODE_PIO32?1:0<<7);
5438 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5439 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2);
5440 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5441 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5442 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5443 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5444 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5445 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5446 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5447 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5448 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5450 checksum=0;
5451 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, (&EbdaData->ata.dpte) + i);
5452 checksum = ~checksum;
5453 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5456 // EDD 3.x
5457 if(size >= 0x42) {
5458 Bit8u channel, iface, checksum, i;
5459 Bit16u iobase1;
5461 channel = device / 2;
5462 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5463 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5465 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5466 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5467 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5468 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5469 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5471 if (iface==ATA_IFACE_ISA) {
5472 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5473 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5474 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5475 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5477 else {
5478 // FIXME PCI
5480 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5481 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5482 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5483 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5485 if (iface==ATA_IFACE_ISA) {
5486 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5487 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5488 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5490 else {
5491 // FIXME PCI
5493 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5494 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5495 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5496 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5498 checksum=0;
5499 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5500 checksum = ~checksum;
5501 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5504 goto int13_success;
5505 break;
5507 case 0x49: // IBM/MS extended media change
5508 // always send changed ??
5509 SET_AH(06);
5510 goto int13_fail_nostatus;
5511 break;
5513 case 0x4e: // // IBM/MS set hardware configuration
5514 // DMA, prefetch, PIO maximum not supported
5515 switch (GET_AL()) {
5516 case 0x01:
5517 case 0x03:
5518 case 0x04:
5519 case 0x06:
5520 goto int13_success;
5521 break;
5522 default :
5523 goto int13_fail;
5525 break;
5527 // all those functions return unimplemented
5528 case 0x02: /* read sectors */
5529 case 0x04: /* verify sectors */
5530 case 0x08: /* read disk drive parameters */
5531 case 0x0a: /* read disk sectors with ECC */
5532 case 0x0b: /* write disk sectors with ECC */
5533 case 0x18: /* set media type for format */
5534 case 0x50: // ? - send packet command
5535 default:
5536 BX_INFO("int13_cdrom: unsupported AH=%02x\n", GET_AH());
5537 goto int13_fail;
5538 break;
5541 int13_fail:
5542 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5543 int13_fail_noah:
5544 SET_DISK_RET_STATUS(GET_AH());
5545 int13_fail_nostatus:
5546 SET_CF(); // error occurred
5547 return;
5549 int13_success:
5550 SET_AH(0x00); // no error
5551 int13_success_noah:
5552 SET_DISK_RET_STATUS(0x00);
5553 CLEAR_CF(); // no error
5554 return;
5557 // ---------------------------------------------------------------------------
5558 // End of int13 for cdrom
5559 // ---------------------------------------------------------------------------
5561 #if BX_ELTORITO_BOOT
5562 // ---------------------------------------------------------------------------
5563 // Start of int13 for eltorito functions
5564 // ---------------------------------------------------------------------------
5566 void
5567 int13_eltorito(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
5568 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
5570 Bit16u ebda_seg=read_word(0x0040,0x000E);
5572 BX_DEBUG_INT13_ET("int13_eltorito: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5573 // BX_DEBUG_INT13_ET("int13_eltorito: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
5575 switch (GET_AH()) {
5577 // FIXME ElTorito Various. Should be implemented
5578 case 0x4a: // ElTorito - Initiate disk emu
5579 case 0x4c: // ElTorito - Initiate disk emu and boot
5580 case 0x4d: // ElTorito - Return Boot catalog
5581 BX_PANIC("Int13 eltorito call with AX=%04x. Please report\n",AX);
5582 goto int13_fail;
5583 break;
5585 case 0x4b: // ElTorito - Terminate disk emu
5586 // FIXME ElTorito Hardcoded
5587 write_byte(DS,SI+0x00,0x13);
5588 write_byte(DS,SI+0x01,read_byte(ebda_seg,&EbdaData->cdemu.media));
5589 write_byte(DS,SI+0x02,read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
5590 write_byte(DS,SI+0x03,read_byte(ebda_seg,&EbdaData->cdemu.controller_index));
5591 write_dword(DS,SI+0x04,read_dword(ebda_seg,&EbdaData->cdemu.ilba));
5592 write_word(DS,SI+0x08,read_word(ebda_seg,&EbdaData->cdemu.device_spec));
5593 write_word(DS,SI+0x0a,read_word(ebda_seg,&EbdaData->cdemu.buffer_segment));
5594 write_word(DS,SI+0x0c,read_word(ebda_seg,&EbdaData->cdemu.load_segment));
5595 write_word(DS,SI+0x0e,read_word(ebda_seg,&EbdaData->cdemu.sector_count));
5596 write_byte(DS,SI+0x10,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.cylinders));
5597 write_byte(DS,SI+0x11,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.spt));
5598 write_byte(DS,SI+0x12,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.heads));
5600 // If we have to terminate emulation
5601 if(GET_AL() == 0x00) {
5602 // FIXME ElTorito Various. Should be handled accordingly to spec
5603 write_byte(ebda_seg,&EbdaData->cdemu.active, 0x00); // bye bye
5606 goto int13_success;
5607 break;
5609 default:
5610 BX_INFO("int13_eltorito: unsupported AH=%02x\n", GET_AH());
5611 goto int13_fail;
5612 break;
5615 int13_fail:
5616 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5617 SET_DISK_RET_STATUS(GET_AH());
5618 SET_CF(); // error occurred
5619 return;
5621 int13_success:
5622 SET_AH(0x00); // no error
5623 SET_DISK_RET_STATUS(0x00);
5624 CLEAR_CF(); // no error
5625 return;
5628 // ---------------------------------------------------------------------------
5629 // End of int13 for eltorito functions
5630 // ---------------------------------------------------------------------------
5632 // ---------------------------------------------------------------------------
5633 // Start of int13 when emulating a device from the cd
5634 // ---------------------------------------------------------------------------
5636 void
5637 int13_cdemu(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
5638 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
5640 Bit16u ebda_seg=read_word(0x0040,0x000E);
5641 Bit8u device, status;
5642 Bit16u vheads, vspt, vcylinders;
5643 Bit16u head, sector, cylinder, nbsectors;
5644 Bit32u vlba, ilba, slba, elba;
5645 Bit16u before, segment, offset;
5646 Bit8u atacmd[12];
5648 BX_DEBUG_INT13_ET("int13_cdemu: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5649 //BX_DEBUG_INT13_ET("int13_cdemu: SS=%04x ES=%04x DI=%04x SI=%04x\n", get_SS(), ES, DI, SI);
5651 /* at this point, we are emulating a floppy/harddisk */
5653 // Recompute the device number
5654 device = read_byte(ebda_seg,&EbdaData->cdemu.controller_index) * 2;
5655 device += read_byte(ebda_seg,&EbdaData->cdemu.device_spec);
5657 SET_DISK_RET_STATUS(0x00);
5659 /* basic checks : emulation should be active, dl should equal the emulated drive */
5660 if( (read_byte(ebda_seg,&EbdaData->cdemu.active) ==0 )
5661 || (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive ) != GET_DL())) {
5662 BX_INFO("int13_cdemu: function %02x, emulation not active for DL= %02x\n", GET_AH(), GET_DL());
5663 goto int13_fail;
5667 switch (GET_AH()) {
5669 // all those functions return SUCCESS
5670 case 0x00: /* disk controller reset */
5671 case 0x09: /* initialize drive parameters */
5672 case 0x0c: /* seek to specified cylinder */
5673 case 0x0d: /* alternate disk reset */ // FIXME ElTorito Various. should really reset ?
5674 case 0x10: /* check drive ready */ // FIXME ElTorito Various. should check if ready ?
5675 case 0x11: /* recalibrate */
5676 case 0x14: /* controller internal diagnostic */
5677 case 0x16: /* detect disk change */
5678 goto int13_success;
5679 break;
5681 // all those functions return disk write-protected
5682 case 0x03: /* write disk sectors */
5683 case 0x05: /* format disk track */
5684 SET_AH(0x03);
5685 goto int13_fail_noah;
5686 break;
5688 case 0x01: /* read disk status */
5689 status=read_byte(0x0040, 0x0074);
5690 SET_AH(status);
5691 SET_DISK_RET_STATUS(0);
5693 /* set CF if error status read */
5694 if (status) goto int13_fail_nostatus;
5695 else goto int13_success_noah;
5696 break;
5698 case 0x02: // read disk sectors
5699 case 0x04: // verify disk sectors
5700 vspt = read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
5701 vcylinders = read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders);
5702 vheads = read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads);
5704 ilba = read_dword(ebda_seg,&EbdaData->cdemu.ilba);
5706 sector = GET_CL() & 0x003f;
5707 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
5708 head = GET_DH();
5709 nbsectors = GET_AL();
5710 segment = ES;
5711 offset = BX;
5713 // no sector to read ?
5714 if(nbsectors==0) goto int13_success;
5716 // sanity checks sco openserver needs this!
5717 if ((sector > vspt)
5718 || (cylinder >= vcylinders)
5719 || (head >= vheads)) {
5720 goto int13_fail;
5723 // After controls, verify do nothing
5724 if (GET_AH() == 0x04) goto int13_success;
5726 segment = ES+(BX / 16);
5727 offset = BX % 16;
5729 // calculate the virtual lba inside the image
5730 vlba=((((Bit32u)cylinder*(Bit32u)vheads)+(Bit32u)head)*(Bit32u)vspt)+((Bit32u)(sector-1));
5732 // In advance so we don't loose the count
5733 SET_AL(nbsectors);
5735 // start lba on cd
5736 slba = (Bit32u)vlba/4;
5737 before= (Bit16u)vlba%4;
5739 // end lba on cd
5740 elba = (Bit32u)(vlba+nbsectors-1)/4;
5742 memsetb(get_SS(),atacmd,0,12);
5743 atacmd[0]=0x28; // READ command
5744 atacmd[7]=((Bit16u)(elba-slba+1) & 0xff00) >> 8; // Sectors
5745 atacmd[8]=((Bit16u)(elba-slba+1) & 0x00ff); // Sectors
5746 atacmd[2]=(ilba+slba & 0xff000000) >> 24; // LBA
5747 atacmd[3]=(ilba+slba & 0x00ff0000) >> 16;
5748 atacmd[4]=(ilba+slba & 0x0000ff00) >> 8;
5749 atacmd[5]=(ilba+slba & 0x000000ff);
5750 if((status = ata_cmd_packet(device, 12, get_SS(), atacmd, before*512, nbsectors*512L, ATA_DATA_IN, segment,offset)) != 0) {
5751 BX_INFO("int13_cdemu: function %02x, error %02x !\n",GET_AH(),status);
5752 SET_AH(0x02);
5753 SET_AL(0);
5754 goto int13_fail_noah;
5757 goto int13_success;
5758 break;
5760 case 0x08: /* read disk drive parameters */
5761 vspt=read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
5762 vcylinders=read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders) - 1;
5763 vheads=read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads) - 1;
5765 SET_AL( 0x00 );
5766 SET_BL( 0x00 );
5767 SET_CH( vcylinders & 0xff );
5768 SET_CL((( vcylinders >> 2) & 0xc0) | ( vspt & 0x3f ));
5769 SET_DH( vheads );
5770 SET_DL( 0x02 ); // FIXME ElTorito Various. should send the real count of drives 1 or 2
5771 // FIXME ElTorito Harddisk. should send the HD count
5773 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
5774 case 0x01: SET_BL( 0x02 ); break;
5775 case 0x02: SET_BL( 0x04 ); break;
5776 case 0x03: SET_BL( 0x06 ); break;
5779 ASM_START
5780 push bp
5781 mov bp, sp
5782 mov ax, #diskette_param_table2
5783 mov _int13_cdemu.DI+2[bp], ax
5784 mov _int13_cdemu.ES+2[bp], cs
5785 pop bp
5786 ASM_END
5787 goto int13_success;
5788 break;
5790 case 0x15: /* read disk drive size */
5791 // FIXME ElTorito Harddisk. What geometry to send ?
5792 SET_AH(0x03);
5793 goto int13_success_noah;
5794 break;
5796 // all those functions return unimplemented
5797 case 0x0a: /* read disk sectors with ECC */
5798 case 0x0b: /* write disk sectors with ECC */
5799 case 0x18: /* set media type for format */
5800 case 0x41: // IBM/MS installation check
5801 // FIXME ElTorito Harddisk. Darwin would like to use EDD
5802 case 0x42: // IBM/MS extended read
5803 case 0x43: // IBM/MS extended write
5804 case 0x44: // IBM/MS verify sectors
5805 case 0x45: // IBM/MS lock/unlock drive
5806 case 0x46: // IBM/MS eject media
5807 case 0x47: // IBM/MS extended seek
5808 case 0x48: // IBM/MS get drive parameters
5809 case 0x49: // IBM/MS extended media change
5810 case 0x4e: // ? - set hardware configuration
5811 case 0x50: // ? - send packet command
5812 default:
5813 BX_INFO("int13_cdemu function AH=%02x unsupported, returns fail\n", GET_AH());
5814 goto int13_fail;
5815 break;
5818 int13_fail:
5819 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5820 int13_fail_noah:
5821 SET_DISK_RET_STATUS(GET_AH());
5822 int13_fail_nostatus:
5823 SET_CF(); // error occurred
5824 return;
5826 int13_success:
5827 SET_AH(0x00); // no error
5828 int13_success_noah:
5829 SET_DISK_RET_STATUS(0x00);
5830 CLEAR_CF(); // no error
5831 return;
5834 // ---------------------------------------------------------------------------
5835 // End of int13 when emulating a device from the cd
5836 // ---------------------------------------------------------------------------
5838 #endif // BX_ELTORITO_BOOT
5840 #else //BX_USE_ATADRV
5842 void
5843 outLBA(cylinder,hd_heads,head,hd_sectors,sector,dl)
5844 Bit16u cylinder;
5845 Bit16u hd_heads;
5846 Bit16u head;
5847 Bit16u hd_sectors;
5848 Bit16u sector;
5849 Bit16u dl;
5851 ASM_START
5852 push bp
5853 mov bp, sp
5854 push eax
5855 push ebx
5856 push edx
5857 xor eax,eax
5858 mov ax,4[bp] // cylinder
5859 xor ebx,ebx
5860 mov bl,6[bp] // hd_heads
5861 imul ebx
5863 mov bl,8[bp] // head
5864 add eax,ebx
5865 mov bl,10[bp] // hd_sectors
5866 imul ebx
5867 mov bl,12[bp] // sector
5868 add eax,ebx
5870 dec eax
5871 mov dx,#0x1f3
5872 out dx,al
5873 mov dx,#0x1f4
5874 mov al,ah
5875 out dx,al
5876 shr eax,#16
5877 mov dx,#0x1f5
5878 out dx,al
5879 and ah,#0xf
5880 mov bl,14[bp] // dl
5881 and bl,#1
5882 shl bl,#4
5883 or ah,bl
5884 or ah,#0xe0
5885 mov al,ah
5886 mov dx,#0x01f6
5887 out dx,al
5888 pop edx
5889 pop ebx
5890 pop eax
5891 pop bp
5892 ASM_END
5895 void
5896 int13_harddisk(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
5897 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
5899 Bit8u drive, num_sectors, sector, head, status, mod;
5900 Bit8u drive_map;
5901 Bit8u n_drives;
5902 Bit16u cyl_mod, ax;
5903 Bit16u max_cylinder, cylinder, total_sectors;
5904 Bit16u hd_cylinders;
5905 Bit8u hd_heads, hd_sectors;
5906 Bit16u val16;
5907 Bit8u sector_count;
5908 unsigned int i;
5909 Bit16u tempbx;
5910 Bit16u dpsize;
5912 Bit16u count, segment, offset;
5913 Bit32u lba;
5914 Bit16u error;
5916 BX_DEBUG_INT13_HD("int13 harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5918 write_byte(0x0040, 0x008e, 0); // clear completion flag
5920 /* at this point, DL is >= 0x80 to be passed from the floppy int13h
5921 handler code */
5922 /* check how many disks first (cmos reg 0x12), return an error if
5923 drive not present */
5924 drive_map = inb_cmos(0x12);
5925 drive_map = (((drive_map & 0xf0)==0) ? 0 : 1) |
5926 (((drive_map & 0x0f)==0) ? 0 : 2);
5927 n_drives = (drive_map==0) ? 0 :
5928 ((drive_map==3) ? 2 : 1);
5930 if (!(drive_map & (1<<(GET_ELDL()&0x7f)))) { /* allow 0, 1, or 2 disks */
5931 SET_AH(0x01);
5932 SET_DISK_RET_STATUS(0x01);
5933 SET_CF(); /* error occurred */
5934 return;
5937 switch (GET_AH()) {
5939 case 0x00: /* disk controller reset */
5940 BX_DEBUG_INT13_HD("int13_f00\n");
5942 SET_AH(0);
5943 SET_DISK_RET_STATUS(0);
5944 set_diskette_ret_status(0);
5945 set_diskette_current_cyl(0, 0); /* current cylinder, diskette 1 */
5946 set_diskette_current_cyl(1, 0); /* current cylinder, diskette 2 */
5947 CLEAR_CF(); /* successful */
5948 return;
5949 break;
5951 case 0x01: /* read disk status */
5952 BX_DEBUG_INT13_HD("int13_f01\n");
5953 status = read_byte(0x0040, 0x0074);
5954 SET_AH(status);
5955 SET_DISK_RET_STATUS(0);
5956 /* set CF if error status read */
5957 if (status) SET_CF();
5958 else CLEAR_CF();
5959 return;
5960 break;
5962 case 0x04: // verify disk sectors
5963 case 0x02: // read disk sectors
5964 drive = GET_ELDL();
5965 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
5967 num_sectors = GET_AL();
5968 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
5969 sector = (GET_CL() & 0x3f);
5970 head = GET_DH();
5973 if (hd_cylinders > 1024) {
5974 if (hd_cylinders <= 2048) {
5975 cylinder <<= 1;
5977 else if (hd_cylinders <= 4096) {
5978 cylinder <<= 2;
5980 else if (hd_cylinders <= 8192) {
5981 cylinder <<= 3;
5983 else { // hd_cylinders <= 16384
5984 cylinder <<= 4;
5987 ax = head / hd_heads;
5988 cyl_mod = ax & 0xff;
5989 head = ax >> 8;
5990 cylinder |= cyl_mod;
5993 if ( (cylinder >= hd_cylinders) ||
5994 (sector > hd_sectors) ||
5995 (head >= hd_heads) ) {
5996 SET_AH(1);
5997 SET_DISK_RET_STATUS(1);
5998 SET_CF(); /* error occurred */
5999 return;
6002 if ( (num_sectors > 128) || (num_sectors == 0) )
6003 BX_PANIC("int13_harddisk(): num_sectors out of range!\n");
6005 if (head > 15)
6006 BX_PANIC("hard drive BIOS:(read/verify) head > 15\n");
6008 if ( GET_AH() == 0x04 ) {
6009 SET_AH(0);
6010 SET_DISK_RET_STATUS(0);
6011 CLEAR_CF();
6012 return;
6015 status = inb(0x1f7);
6016 if (status & 0x80) {
6017 BX_PANIC("hard drive BIOS:(read/verify) BUSY bit set\n");
6019 outb(0x01f2, num_sectors);
6020 /* activate LBA? (tomv) */
6021 if (hd_heads > 16) {
6022 BX_DEBUG_INT13_HD("CHS: %x %x %x\n", cylinder, head, sector);
6023 outLBA(cylinder,hd_heads,head,hd_sectors,sector,drive);
6025 else {
6026 outb(0x01f3, sector);
6027 outb(0x01f4, cylinder & 0x00ff);
6028 outb(0x01f5, cylinder >> 8);
6029 outb(0x01f6, 0xa0 | ((drive & 0x01)<<4) | (head & 0x0f));
6031 outb(0x01f7, 0x20);
6033 while (1) {
6034 status = inb(0x1f7);
6035 if ( !(status & 0x80) ) break;
6038 if (status & 0x01) {
6039 BX_PANIC("hard drive BIOS:(read/verify) read error\n");
6040 } else if ( !(status & 0x08) ) {
6041 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
6042 BX_PANIC("hard drive BIOS:(read/verify) expected DRQ=1\n");
6045 sector_count = 0;
6046 tempbx = BX;
6048 ASM_START
6049 sti ;; enable higher priority interrupts
6050 ASM_END
6052 while (1) {
6053 ASM_START
6054 ;; store temp bx in real DI register
6055 push bp
6056 mov bp, sp
6057 mov di, _int13_harddisk.tempbx + 2 [bp]
6058 pop bp
6060 ;; adjust if there will be an overrun
6061 cmp di, #0xfe00
6062 jbe i13_f02_no_adjust
6063 i13_f02_adjust:
6064 sub di, #0x0200 ; sub 512 bytes from offset
6065 mov ax, es
6066 add ax, #0x0020 ; add 512 to segment
6067 mov es, ax
6069 i13_f02_no_adjust:
6070 mov cx, #0x0100 ;; counter (256 words = 512b)
6071 mov dx, #0x01f0 ;; AT data read port
6073 rep
6074 insw ;; CX words transfered from port(DX) to ES:[DI]
6076 i13_f02_done:
6077 ;; store real DI register back to temp bx
6078 push bp
6079 mov bp, sp
6080 mov _int13_harddisk.tempbx + 2 [bp], di
6081 pop bp
6082 ASM_END
6084 sector_count++;
6085 num_sectors--;
6086 if (num_sectors == 0) {
6087 status = inb(0x1f7);
6088 if ( (status & 0xc9) != 0x40 )
6089 BX_PANIC("no sectors left to read/verify, status is %02x\n", (unsigned) status);
6090 break;
6092 else {
6093 status = inb(0x1f7);
6094 if ( (status & 0xc9) != 0x48 )
6095 BX_PANIC("more sectors left to read/verify, status is %02x\n", (unsigned) status);
6096 continue;
6100 SET_AH(0);
6101 SET_DISK_RET_STATUS(0);
6102 SET_AL(sector_count);
6103 CLEAR_CF(); /* successful */
6104 return;
6105 break;
6108 case 0x03: /* write disk sectors */
6109 BX_DEBUG_INT13_HD("int13_f03\n");
6110 drive = GET_ELDL ();
6111 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6113 num_sectors = GET_AL();
6114 cylinder = GET_CH();
6115 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
6116 sector = (GET_CL() & 0x3f);
6117 head = GET_DH();
6119 if (hd_cylinders > 1024) {
6120 if (hd_cylinders <= 2048) {
6121 cylinder <<= 1;
6123 else if (hd_cylinders <= 4096) {
6124 cylinder <<= 2;
6126 else if (hd_cylinders <= 8192) {
6127 cylinder <<= 3;
6129 else { // hd_cylinders <= 16384
6130 cylinder <<= 4;
6133 ax = head / hd_heads;
6134 cyl_mod = ax & 0xff;
6135 head = ax >> 8;
6136 cylinder |= cyl_mod;
6139 if ( (cylinder >= hd_cylinders) ||
6140 (sector > hd_sectors) ||
6141 (head >= hd_heads) ) {
6142 SET_AH( 1);
6143 SET_DISK_RET_STATUS(1);
6144 SET_CF(); /* error occurred */
6145 return;
6148 if ( (num_sectors > 128) || (num_sectors == 0) )
6149 BX_PANIC("int13_harddisk(): num_sectors out of range!\n");
6151 if (head > 15)
6152 BX_PANIC("hard drive BIOS:(read) head > 15\n");
6154 status = inb(0x1f7);
6155 if (status & 0x80) {
6156 BX_PANIC("hard drive BIOS:(read) BUSY bit set\n");
6158 // should check for Drive Ready Bit also in status reg
6159 outb(0x01f2, num_sectors);
6161 /* activate LBA? (tomv) */
6162 if (hd_heads > 16) {
6163 BX_DEBUG_INT13_HD("CHS (write): %x %x %x\n", cylinder, head, sector);
6164 outLBA(cylinder,hd_heads,head,hd_sectors,sector,GET_ELDL());
6166 else {
6167 outb(0x01f3, sector);
6168 outb(0x01f4, cylinder & 0x00ff);
6169 outb(0x01f5, cylinder >> 8);
6170 outb(0x01f6, 0xa0 | ((GET_ELDL() & 0x01)<<4) | (head & 0x0f));
6172 outb(0x01f7, 0x30);
6174 // wait for busy bit to turn off after seeking
6175 while (1) {
6176 status = inb(0x1f7);
6177 if ( !(status & 0x80) ) break;
6180 if ( !(status & 0x08) ) {
6181 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
6182 BX_PANIC("hard drive BIOS:(write) data-request bit not set\n");
6185 sector_count = 0;
6186 tempbx = BX;
6188 ASM_START
6189 sti ;; enable higher priority interrupts
6190 ASM_END
6192 while (1) {
6193 ASM_START
6194 ;; store temp bx in real SI register
6195 push bp
6196 mov bp, sp
6197 mov si, _int13_harddisk.tempbx + 2 [bp]
6198 pop bp
6200 ;; adjust if there will be an overrun
6201 cmp si, #0xfe00
6202 jbe i13_f03_no_adjust
6203 i13_f03_adjust:
6204 sub si, #0x0200 ; sub 512 bytes from offset
6205 mov ax, es
6206 add ax, #0x0020 ; add 512 to segment
6207 mov es, ax
6209 i13_f03_no_adjust:
6210 mov cx, #0x0100 ;; counter (256 words = 512b)
6211 mov dx, #0x01f0 ;; AT data read port
6213 seg ES
6214 rep
6215 outsw ;; CX words tranfered from ES:[SI] to port(DX)
6217 ;; store real SI register back to temp bx
6218 push bp
6219 mov bp, sp
6220 mov _int13_harddisk.tempbx + 2 [bp], si
6221 pop bp
6222 ASM_END
6224 sector_count++;
6225 num_sectors--;
6226 if (num_sectors == 0) {
6227 status = inb(0x1f7);
6228 if ( (status & 0xe9) != 0x40 )
6229 BX_PANIC("no sectors left to write, status is %02x\n", (unsigned) status);
6230 break;
6232 else {
6233 status = inb(0x1f7);
6234 if ( (status & 0xc9) != 0x48 )
6235 BX_PANIC("more sectors left to write, status is %02x\n", (unsigned) status);
6236 continue;
6240 SET_AH(0);
6241 SET_DISK_RET_STATUS(0);
6242 SET_AL(sector_count);
6243 CLEAR_CF(); /* successful */
6244 return;
6245 break;
6247 case 0x05: /* format disk track */
6248 BX_DEBUG_INT13_HD("int13_f05\n");
6249 BX_PANIC("format disk track called\n");
6250 /* nop */
6251 SET_AH(0);
6252 SET_DISK_RET_STATUS(0);
6253 CLEAR_CF(); /* successful */
6254 return;
6255 break;
6257 case 0x08: /* read disk drive parameters */
6258 BX_DEBUG_INT13_HD("int13_f08\n");
6260 drive = GET_ELDL ();
6261 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6263 // translate CHS
6264 //
6265 if (hd_cylinders <= 1024) {
6266 // hd_cylinders >>= 0;
6267 // hd_heads <<= 0;
6269 else if (hd_cylinders <= 2048) {
6270 hd_cylinders >>= 1;
6271 hd_heads <<= 1;
6273 else if (hd_cylinders <= 4096) {
6274 hd_cylinders >>= 2;
6275 hd_heads <<= 2;
6277 else if (hd_cylinders <= 8192) {
6278 hd_cylinders >>= 3;
6279 hd_heads <<= 3;
6281 else { // hd_cylinders <= 16384
6282 hd_cylinders >>= 4;
6283 hd_heads <<= 4;
6286 max_cylinder = hd_cylinders - 2; /* 0 based */
6287 SET_AL(0);
6288 SET_CH(max_cylinder & 0xff);
6289 SET_CL(((max_cylinder >> 2) & 0xc0) | (hd_sectors & 0x3f));
6290 SET_DH(hd_heads - 1);
6291 SET_DL(n_drives); /* returns 0, 1, or 2 hard drives */
6292 SET_AH(0);
6293 SET_DISK_RET_STATUS(0);
6294 CLEAR_CF(); /* successful */
6296 return;
6297 break;
6299 case 0x09: /* initialize drive parameters */
6300 BX_DEBUG_INT13_HD("int13_f09\n");
6301 SET_AH(0);
6302 SET_DISK_RET_STATUS(0);
6303 CLEAR_CF(); /* successful */
6304 return;
6305 break;
6307 case 0x0a: /* read disk sectors with ECC */
6308 BX_DEBUG_INT13_HD("int13_f0a\n");
6309 case 0x0b: /* write disk sectors with ECC */
6310 BX_DEBUG_INT13_HD("int13_f0b\n");
6311 BX_PANIC("int13h Functions 0Ah & 0Bh not implemented!\n");
6312 return;
6313 break;
6315 case 0x0c: /* seek to specified cylinder */
6316 BX_DEBUG_INT13_HD("int13_f0c\n");
6317 BX_INFO("int13h function 0ch (seek) not implemented!\n");
6318 SET_AH(0);
6319 SET_DISK_RET_STATUS(0);
6320 CLEAR_CF(); /* successful */
6321 return;
6322 break;
6324 case 0x0d: /* alternate disk reset */
6325 BX_DEBUG_INT13_HD("int13_f0d\n");
6326 SET_AH(0);
6327 SET_DISK_RET_STATUS(0);
6328 CLEAR_CF(); /* successful */
6329 return;
6330 break;
6332 case 0x10: /* check drive ready */
6333 BX_DEBUG_INT13_HD("int13_f10\n");
6334 //SET_AH(0);
6335 //SET_DISK_RET_STATUS(0);
6336 //CLEAR_CF(); /* successful */
6337 //return;
6338 //break;
6340 // should look at 40:8E also???
6341 status = inb(0x01f7);
6342 if ( (status & 0xc0) == 0x40 ) {
6343 SET_AH(0);
6344 SET_DISK_RET_STATUS(0);
6345 CLEAR_CF(); // drive ready
6346 return;
6348 else {
6349 SET_AH(0xAA);
6350 SET_DISK_RET_STATUS(0xAA);
6351 SET_CF(); // not ready
6352 return;
6354 break;
6356 case 0x11: /* recalibrate */
6357 BX_DEBUG_INT13_HD("int13_f11\n");
6358 SET_AH(0);
6359 SET_DISK_RET_STATUS(0);
6360 CLEAR_CF(); /* successful */
6361 return;
6362 break;
6364 case 0x14: /* controller internal diagnostic */
6365 BX_DEBUG_INT13_HD("int13_f14\n");
6366 SET_AH(0);
6367 SET_DISK_RET_STATUS(0);
6368 CLEAR_CF(); /* successful */
6369 SET_AL(0);
6370 return;
6371 break;
6373 case 0x15: /* read disk drive size */
6374 drive = GET_ELDL();
6375 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6376 ASM_START
6377 push bp
6378 mov bp, sp
6379 mov al, _int13_harddisk.hd_heads + 2 [bp]
6380 mov ah, _int13_harddisk.hd_sectors + 2 [bp]
6381 mul al, ah ;; ax = heads * sectors
6382 mov bx, _int13_harddisk.hd_cylinders + 2 [bp]
6383 dec bx ;; use (cylinders - 1) ???
6384 mul ax, bx ;; dx:ax = (cylinders -1) * (heads * sectors)
6385 ;; now we need to move the 32bit result dx:ax to what the
6386 ;; BIOS wants which is cx:dx.
6387 ;; and then into CX:DX on the stack
6388 mov _int13_harddisk.CX + 2 [bp], dx
6389 mov _int13_harddisk.DX + 2 [bp], ax
6390 pop bp
6391 ASM_END
6392 SET_AH(3); // hard disk accessible
6393 SET_DISK_RET_STATUS(0); // ??? should this be 0
6394 CLEAR_CF(); // successful
6395 return;
6396 break;
6398 case 0x18: // set media type for format
6399 case 0x41: // IBM/MS
6400 case 0x42: // IBM/MS
6401 case 0x43: // IBM/MS
6402 case 0x44: // IBM/MS
6403 case 0x45: // IBM/MS lock/unlock drive
6404 case 0x46: // IBM/MS eject media
6405 case 0x47: // IBM/MS extended seek
6406 case 0x49: // IBM/MS extended media change
6407 case 0x50: // IBM/MS send packet command
6408 default:
6409 BX_INFO("int13_harddisk: unsupported AH=%02x\n", GET_AH());
6411 SET_AH(1); // code=invalid function in AH or invalid parameter
6412 SET_DISK_RET_STATUS(1);
6413 SET_CF(); /* unsuccessful */
6414 return;
6415 break;
6419 static char panic_msg_reg12h[] = "HD%d cmos reg 12h not type F\n";
6420 static char panic_msg_reg19h[] = "HD%d cmos reg %02xh not user definable type 47\n";
6422 void
6423 get_hd_geometry(drive, hd_cylinders, hd_heads, hd_sectors)
6424 Bit8u drive;
6425 Bit16u *hd_cylinders;
6426 Bit8u *hd_heads;
6427 Bit8u *hd_sectors;
6429 Bit8u hd_type;
6430 Bit16u ss;
6431 Bit16u cylinders;
6432 Bit8u iobase;
6434 ss = get_SS();
6435 if (drive == 0x80) {
6436 hd_type = inb_cmos(0x12) & 0xf0;
6437 if (hd_type != 0xf0)
6438 BX_INFO(panic_msg_reg12h,0);
6439 hd_type = inb_cmos(0x19); // HD0: extended type
6440 if (hd_type != 47)
6441 BX_INFO(panic_msg_reg19h,0,0x19);
6442 iobase = 0x1b;
6443 } else {
6444 hd_type = inb_cmos(0x12) & 0x0f;
6445 if (hd_type != 0x0f)
6446 BX_INFO(panic_msg_reg12h,1);
6447 hd_type = inb_cmos(0x1a); // HD0: extended type
6448 if (hd_type != 47)
6449 BX_INFO(panic_msg_reg19h,0,0x1a);
6450 iobase = 0x24;
6453 // cylinders
6454 cylinders = inb_cmos(iobase) | (inb_cmos(iobase+1) << 8);
6455 write_word(ss, hd_cylinders, cylinders);
6457 // heads
6458 write_byte(ss, hd_heads, inb_cmos(iobase+2));
6460 // sectors per track
6461 write_byte(ss, hd_sectors, inb_cmos(iobase+8));
6464 #endif //else BX_USE_ATADRV
6467 //////////////////////
6468 // FLOPPY functions //
6469 //////////////////////
6471 bx_bool
6472 floppy_media_known(drive)
6473 Bit16u drive;
6475 Bit8u val8;
6476 Bit16u media_state_offset;
6478 val8 = read_byte(0x0040, 0x003e); // diskette recal status
6479 if (drive)
6480 val8 >>= 1;
6481 val8 &= 0x01;
6482 if (val8 == 0)
6483 return(0);
6485 media_state_offset = 0x0090;
6486 if (drive)
6487 media_state_offset += 1;
6489 val8 = read_byte(0x0040, media_state_offset);
6490 val8 = (val8 >> 4) & 0x01;
6491 if (val8 == 0)
6492 return(0);
6494 // check pass, return KNOWN
6495 return(1);
6498 bx_bool
6499 floppy_media_sense(drive)
6500 Bit16u drive;
6502 bx_bool retval;
6503 Bit16u media_state_offset;
6504 Bit8u drive_type, config_data, media_state;
6506 if (floppy_drive_recal(drive) == 0) {
6507 return(0);
6510 // for now cheat and get drive type from CMOS,
6511 // assume media is same as drive type
6513 // ** config_data **
6514 // Bitfields for diskette media control:
6515 // Bit(s) Description (Table M0028)
6516 // 7-6 last data rate set by controller
6517 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6518 // 5-4 last diskette drive step rate selected
6519 // 00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
6520 // 3-2 {data rate at start of operation}
6521 // 1-0 reserved
6523 // ** media_state **
6524 // Bitfields for diskette drive media state:
6525 // Bit(s) Description (Table M0030)
6526 // 7-6 data rate
6527 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6528 // 5 double stepping required (e.g. 360kB in 1.2MB)
6529 // 4 media type established
6530 // 3 drive capable of supporting 4MB media
6531 // 2-0 on exit from BIOS, contains
6532 // 000 trying 360kB in 360kB
6533 // 001 trying 360kB in 1.2MB
6534 // 010 trying 1.2MB in 1.2MB
6535 // 011 360kB in 360kB established
6536 // 100 360kB in 1.2MB established
6537 // 101 1.2MB in 1.2MB established
6538 // 110 reserved
6539 // 111 all other formats/drives
6541 drive_type = inb_cmos(0x10);
6542 if (drive == 0)
6543 drive_type >>= 4;
6544 else
6545 drive_type &= 0x0f;
6546 if ( drive_type == 1 ) {
6547 // 360K 5.25" drive
6548 config_data = 0x00; // 0000 0000
6549 media_state = 0x25; // 0010 0101
6550 retval = 1;
6552 else if ( drive_type == 2 ) {
6553 // 1.2 MB 5.25" drive
6554 config_data = 0x00; // 0000 0000
6555 media_state = 0x25; // 0010 0101 // need double stepping??? (bit 5)
6556 retval = 1;
6558 else if ( drive_type == 3 ) {
6559 // 720K 3.5" drive
6560 config_data = 0x00; // 0000 0000 ???
6561 media_state = 0x17; // 0001 0111
6562 retval = 1;
6564 else if ( drive_type == 4 ) {
6565 // 1.44 MB 3.5" drive
6566 config_data = 0x00; // 0000 0000
6567 media_state = 0x17; // 0001 0111
6568 retval = 1;
6570 else if ( drive_type == 5 ) {
6571 // 2.88 MB 3.5" drive
6572 config_data = 0xCC; // 1100 1100
6573 media_state = 0xD7; // 1101 0111
6574 retval = 1;
6576 //
6577 // Extended floppy size uses special cmos setting
6578 else if ( drive_type == 6 ) {
6579 // 160k 5.25" drive
6580 config_data = 0x00; // 0000 0000
6581 media_state = 0x27; // 0010 0111
6582 retval = 1;
6584 else if ( drive_type == 7 ) {
6585 // 180k 5.25" drive
6586 config_data = 0x00; // 0000 0000
6587 media_state = 0x27; // 0010 0111
6588 retval = 1;
6590 else if ( drive_type == 8 ) {
6591 // 320k 5.25" drive
6592 config_data = 0x00; // 0000 0000
6593 media_state = 0x27; // 0010 0111
6594 retval = 1;
6597 else {
6598 // not recognized
6599 config_data = 0x00; // 0000 0000
6600 media_state = 0x00; // 0000 0000
6601 retval = 0;
6604 if (drive == 0)
6605 media_state_offset = 0x90;
6606 else
6607 media_state_offset = 0x91;
6608 write_byte(0x0040, 0x008B, config_data);
6609 write_byte(0x0040, media_state_offset, media_state);
6611 return(retval);
6614 bx_bool
6615 floppy_drive_recal(drive)
6616 Bit16u drive;
6618 Bit8u val8, dor;
6619 Bit16u curr_cyl_offset;
6621 // set 40:3e bit 7 to 0
6622 val8 = read_byte(0x0000, 0x043e);
6623 val8 &= 0x7f;
6624 write_byte(0x0000, 0x043e, val8);
6626 // turn on motor of selected drive, DMA & int enabled, normal operation
6627 if (drive)
6628 dor = 0x20;
6629 else
6630 dor = 0x10;
6631 dor |= 0x0c;
6632 dor |= drive;
6633 outb(0x03f2, dor);
6635 // reset the disk motor timeout value of INT 08
6636 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
6638 // check port 3f4 for drive readiness
6639 val8 = inb(0x3f4);
6640 if ( (val8 & 0xf0) != 0x80 )
6641 BX_PANIC("floppy recal:f07: ctrl not ready\n");
6643 // send Recalibrate command (2 bytes) to controller
6644 outb(0x03f5, 0x07); // 07: Recalibrate
6645 outb(0x03f5, drive); // 0=drive0, 1=drive1
6647 // turn on interrupts
6648 ASM_START
6649 sti
6650 ASM_END
6652 // wait on 40:3e bit 7 to become 1
6653 val8 = (read_byte(0x0000, 0x043e) & 0x80);
6654 while ( val8 == 0 ) {
6655 val8 = (read_byte(0x0000, 0x043e) & 0x80);
6658 val8 = 0; // separate asm from while() loop
6659 // turn off interrupts
6660 ASM_START
6661 cli
6662 ASM_END
6664 // set 40:3e bit 7 to 0, and calibrated bit
6665 val8 = read_byte(0x0000, 0x043e);
6666 val8 &= 0x7f;
6667 if (drive) {
6668 val8 |= 0x02; // Drive 1 calibrated
6669 curr_cyl_offset = 0x0095;
6671 else {
6672 val8 |= 0x01; // Drive 0 calibrated
6673 curr_cyl_offset = 0x0094;
6675 write_byte(0x0040, 0x003e, val8);
6676 write_byte(0x0040, curr_cyl_offset, 0); // current cylinder is 0
6678 return(1);
6683 bx_bool
6684 floppy_drive_exists(drive)
6685 Bit16u drive;
6687 Bit8u drive_type;
6689 // check CMOS to see if drive exists
6690 drive_type = inb_cmos(0x10);
6691 if (drive == 0)
6692 drive_type >>= 4;
6693 else
6694 drive_type &= 0x0f;
6695 if ( drive_type == 0 )
6696 return(0);
6697 else
6698 return(1);
6701 #if BX_SUPPORT_FLOPPY
6702 void
6703 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
6704 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
6706 Bit8u drive, num_sectors, track, sector, head, status;
6707 Bit16u base_address, base_count, base_es;
6708 Bit8u page, mode_register, val8, dor;
6709 Bit8u return_status[7];
6710 Bit8u drive_type, num_floppies, ah;
6711 Bit16u es, last_addr;
6713 BX_DEBUG_INT13_FL("int13_diskette: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
6714 // BX_DEBUG_INT13_FL("int13_diskette: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), get_DS(), ES, DI, SI);
6716 ah = GET_AH();
6718 switch ( ah ) {
6719 case 0x00: // diskette controller reset
6720 BX_DEBUG_INT13_FL("floppy f00\n");
6721 drive = GET_ELDL();
6722 if (drive > 1) {
6723 SET_AH(1); // invalid param
6724 set_diskette_ret_status(1);
6725 SET_CF();
6726 return;
6728 drive_type = inb_cmos(0x10);
6730 if (drive == 0)
6731 drive_type >>= 4;
6732 else
6733 drive_type &= 0x0f;
6734 if (drive_type == 0) {
6735 SET_AH(0x80); // drive not responding
6736 set_diskette_ret_status(0x80);
6737 SET_CF();
6738 return;
6740 SET_AH(0);
6741 set_diskette_ret_status(0);
6742 CLEAR_CF(); // successful
6743 set_diskette_current_cyl(drive, 0); // current cylinder
6744 return;
6746 case 0x01: // Read Diskette Status
6747 CLEAR_CF();
6748 val8 = read_byte(0x0000, 0x0441);
6749 SET_AH(val8);
6750 if (val8) {
6751 SET_CF();
6753 return;
6755 case 0x02: // Read Diskette Sectors
6756 case 0x03: // Write Diskette Sectors
6757 case 0x04: // Verify Diskette Sectors
6758 num_sectors = GET_AL();
6759 track = GET_CH();
6760 sector = GET_CL();
6761 head = GET_DH();
6762 drive = GET_ELDL();
6764 if ( (drive > 1) || (head > 1) ||
6765 (num_sectors == 0) || (num_sectors > 72) ) {
6766 BX_INFO("floppy: drive>1 || head>1 ...\n");
6767 SET_AH(1);
6768 set_diskette_ret_status(1);
6769 SET_AL(0); // no sectors read
6770 SET_CF(); // error occurred
6771 return;
6774 // see if drive exists
6775 if (floppy_drive_exists(drive) == 0) {
6776 SET_AH(0x80); // not responding
6777 set_diskette_ret_status(0x80);
6778 SET_AL(0); // no sectors read
6779 SET_CF(); // error occurred
6780 return;
6783 // see if media in drive, and type is known
6784 if (floppy_media_known(drive) == 0) {
6785 if (floppy_media_sense(drive) == 0) {
6786 SET_AH(0x0C); // Media type not found
6787 set_diskette_ret_status(0x0C);
6788 SET_AL(0); // no sectors read
6789 SET_CF(); // error occurred
6790 return;
6794 if (ah == 0x02) {
6795 // Read Diskette Sectors
6797 //-----------------------------------
6798 // set up DMA controller for transfer
6799 //-----------------------------------
6801 // es:bx = pointer to where to place information from diskette
6802 // port 04: DMA-1 base and current address, channel 2
6803 // port 05: DMA-1 base and current count, channel 2
6804 page = (ES >> 12); // upper 4 bits
6805 base_es = (ES << 4); // lower 16bits contributed by ES
6806 base_address = base_es + BX; // lower 16 bits of address
6807 // contributed by ES:BX
6808 if ( base_address < base_es ) {
6809 // in case of carry, adjust page by 1
6810 page++;
6812 base_count = (num_sectors * 512) - 1;
6814 // check for 64K boundary overrun
6815 last_addr = base_address + base_count;
6816 if (last_addr < base_address) {
6817 SET_AH(0x09);
6818 set_diskette_ret_status(0x09);
6819 SET_AL(0); // no sectors read
6820 SET_CF(); // error occurred
6821 return;
6824 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
6825 outb(0x000a, 0x06);
6827 BX_DEBUG_INT13_FL("clear flip-flop\n");
6828 outb(0x000c, 0x00); // clear flip-flop
6829 outb(0x0004, base_address);
6830 outb(0x0004, base_address>>8);
6831 BX_DEBUG_INT13_FL("clear flip-flop\n");
6832 outb(0x000c, 0x00); // clear flip-flop
6833 outb(0x0005, base_count);
6834 outb(0x0005, base_count>>8);
6836 // port 0b: DMA-1 Mode Register
6837 mode_register = 0x46; // single mode, increment, autoinit disable,
6838 // transfer type=write, channel 2
6839 BX_DEBUG_INT13_FL("setting mode register\n");
6840 outb(0x000b, mode_register);
6842 BX_DEBUG_INT13_FL("setting page register\n");
6843 // port 81: DMA-1 Page Register, channel 2
6844 outb(0x0081, page);
6846 BX_DEBUG_INT13_FL("unmask chan 2\n");
6847 outb(0x000a, 0x02); // unmask channel 2
6849 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
6850 outb(0x000a, 0x02);
6852 //--------------------------------------
6853 // set up floppy controller for transfer
6854 //--------------------------------------
6856 // set 40:3e bit 7 to 0
6857 val8 = read_byte(0x0000, 0x043e);
6858 val8 &= 0x7f;
6859 write_byte(0x0000, 0x043e, val8);
6861 // turn on motor of selected drive, DMA & int enabled, normal operation
6862 if (drive)
6863 dor = 0x20;
6864 else
6865 dor = 0x10;
6866 dor |= 0x0c;
6867 dor |= drive;
6868 outb(0x03f2, dor);
6870 // reset the disk motor timeout value of INT 08
6871 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
6873 // check port 3f4 for drive readiness
6874 val8 = inb(0x3f4);
6875 if ( (val8 & 0xf0) != 0x80 )
6876 BX_PANIC("int13_diskette:f02: ctrl not ready\n");
6878 // send read-normal-data command (9 bytes) to controller
6879 outb(0x03f5, 0xe6); // e6: read normal data
6880 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
6881 outb(0x03f5, track);
6882 outb(0x03f5, head);
6883 outb(0x03f5, sector);
6884 outb(0x03f5, 2); // 512 byte sector size
6885 outb(0x03f5, 0); // last sector number possible on track
6886 outb(0x03f5, 0); // Gap length
6887 outb(0x03f5, 0xff); // Gap length
6889 // turn on interrupts
6890 ASM_START
6891 sti
6892 ASM_END
6894 // wait on 40:3e bit 7 to become 1
6895 val8 = (read_byte(0x0000, 0x043e) & 0x80);
6896 while ( val8 == 0 ) {
6897 val8 = (read_byte(0x0000, 0x043e) & 0x80);
6900 val8 = 0; // separate asm from while() loop
6901 // turn off interrupts
6902 ASM_START
6903 cli
6904 ASM_END
6906 // set 40:3e bit 7 to 0
6907 val8 = read_byte(0x0000, 0x043e);
6908 val8 &= 0x7f;
6909 write_byte(0x0000, 0x043e, val8);
6911 // check port 3f4 for accessibility to status bytes
6912 val8 = inb(0x3f4);
6913 if ( (val8 & 0xc0) != 0xc0 )
6914 BX_PANIC("int13_diskette: ctrl not ready\n");
6916 // read 7 return status bytes from controller
6917 // using loop index broken, have to unroll...
6918 return_status[0] = inb(0x3f5);
6919 return_status[1] = inb(0x3f5);
6920 return_status[2] = inb(0x3f5);
6921 return_status[3] = inb(0x3f5);
6922 return_status[4] = inb(0x3f5);
6923 return_status[5] = inb(0x3f5);
6924 return_status[6] = inb(0x3f5);
6925 // record in BIOS Data Area
6926 write_byte(0x0040, 0x0042, return_status[0]);
6927 write_byte(0x0040, 0x0043, return_status[1]);
6928 write_byte(0x0040, 0x0044, return_status[2]);
6929 write_byte(0x0040, 0x0045, return_status[3]);
6930 write_byte(0x0040, 0x0046, return_status[4]);
6931 write_byte(0x0040, 0x0047, return_status[5]);
6932 write_byte(0x0040, 0x0048, return_status[6]);
6934 if ( (return_status[0] & 0xc0) != 0 ) {
6935 SET_AH(0x20);
6936 set_diskette_ret_status(0x20);
6937 SET_AL(0); // no sectors read
6938 SET_CF(); // error occurred
6939 return;
6942 // ??? should track be new val from return_status[3] ?
6943 set_diskette_current_cyl(drive, track);
6944 // AL = number of sectors read (same value as passed)
6945 SET_AH(0x00); // success
6946 CLEAR_CF(); // success
6947 return;
6949 else if (ah == 0x03) {
6950 // Write Diskette Sectors
6952 //-----------------------------------
6953 // set up DMA controller for transfer
6954 //-----------------------------------
6956 // es:bx = pointer to where to place information from diskette
6957 // port 04: DMA-1 base and current address, channel 2
6958 // port 05: DMA-1 base and current count, channel 2
6959 page = (ES >> 12); // upper 4 bits
6960 base_es = (ES << 4); // lower 16bits contributed by ES
6961 base_address = base_es + BX; // lower 16 bits of address
6962 // contributed by ES:BX
6963 if ( base_address < base_es ) {
6964 // in case of carry, adjust page by 1
6965 page++;
6967 base_count = (num_sectors * 512) - 1;
6969 // check for 64K boundary overrun
6970 last_addr = base_address + base_count;
6971 if (last_addr < base_address) {
6972 SET_AH(0x09);
6973 set_diskette_ret_status(0x09);
6974 SET_AL(0); // no sectors read
6975 SET_CF(); // error occurred
6976 return;
6979 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
6980 outb(0x000a, 0x06);
6982 outb(0x000c, 0x00); // clear flip-flop
6983 outb(0x0004, base_address);
6984 outb(0x0004, base_address>>8);
6985 outb(0x000c, 0x00); // clear flip-flop
6986 outb(0x0005, base_count);
6987 outb(0x0005, base_count>>8);
6989 // port 0b: DMA-1 Mode Register
6990 mode_register = 0x4a; // single mode, increment, autoinit disable,
6991 // transfer type=read, channel 2
6992 outb(0x000b, mode_register);
6994 // port 81: DMA-1 Page Register, channel 2
6995 outb(0x0081, page);
6997 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
6998 outb(0x000a, 0x02);
7000 //--------------------------------------
7001 // set up floppy controller for transfer
7002 //--------------------------------------
7004 // set 40:3e bit 7 to 0
7005 val8 = read_byte(0x0000, 0x043e);
7006 val8 &= 0x7f;
7007 write_byte(0x0000, 0x043e, val8);
7009 // turn on motor of selected drive, DMA & int enabled, normal operation
7010 if (drive)
7011 dor = 0x20;
7012 else
7013 dor = 0x10;
7014 dor |= 0x0c;
7015 dor |= drive;
7016 outb(0x03f2, dor);
7018 // reset the disk motor timeout value of INT 08
7019 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
7021 // check port 3f4 for drive readiness
7022 val8 = inb(0x3f4);
7023 if ( (val8 & 0xf0) != 0x80 )
7024 BX_PANIC("int13_diskette:f03: ctrl not ready\n");
7026 // send read-normal-data command (9 bytes) to controller
7027 outb(0x03f5, 0xc5); // c5: write normal data
7028 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7029 outb(0x03f5, track);
7030 outb(0x03f5, head);
7031 outb(0x03f5, sector);
7032 outb(0x03f5, 2); // 512 byte sector size
7033 outb(0x03f5, 0); // last sector number possible on track
7034 outb(0x03f5, 0); // Gap length
7035 outb(0x03f5, 0xff); // Gap length
7037 // turn on interrupts
7038 ASM_START
7039 sti
7040 ASM_END
7042 // wait on 40:3e bit 7 to become 1
7043 val8 = (read_byte(0x0000, 0x043e) & 0x80);
7044 while ( val8 == 0 ) {
7045 val8 = (read_byte(0x0000, 0x043e) & 0x80);
7048 val8 = 0; // separate asm from while() loop
7049 // turn off interrupts
7050 ASM_START
7051 cli
7052 ASM_END
7054 // set 40:3e bit 7 to 0
7055 val8 = read_byte(0x0000, 0x043e);
7056 val8 &= 0x7f;
7057 write_byte(0x0000, 0x043e, val8);
7059 // check port 3f4 for accessibility to status bytes
7060 val8 = inb(0x3f4);
7061 if ( (val8 & 0xc0) != 0xc0 )
7062 BX_PANIC("int13_diskette: ctrl not ready\n");
7064 // read 7 return status bytes from controller
7065 // using loop index broken, have to unroll...
7066 return_status[0] = inb(0x3f5);
7067 return_status[1] = inb(0x3f5);
7068 return_status[2] = inb(0x3f5);
7069 return_status[3] = inb(0x3f5);
7070 return_status[4] = inb(0x3f5);
7071 return_status[5] = inb(0x3f5);
7072 return_status[6] = inb(0x3f5);
7073 // record in BIOS Data Area
7074 write_byte(0x0040, 0x0042, return_status[0]);
7075 write_byte(0x0040, 0x0043, return_status[1]);
7076 write_byte(0x0040, 0x0044, return_status[2]);
7077 write_byte(0x0040, 0x0045, return_status[3]);
7078 write_byte(0x0040, 0x0046, return_status[4]);
7079 write_byte(0x0040, 0x0047, return_status[5]);
7080 write_byte(0x0040, 0x0048, return_status[6]);
7082 if ( (return_status[0] & 0xc0) != 0 ) {
7083 if ( (return_status[1] & 0x02) != 0 ) {
7084 // diskette not writable.
7085 // AH=status code=0x03 (tried to write on write-protected disk)
7086 // AL=number of sectors written=0
7087 AX = 0x0300;
7088 SET_CF();
7089 return;
7090 } else {
7091 BX_PANIC("int13_diskette_function: read error\n");
7095 // ??? should track be new val from return_status[3] ?
7096 set_diskette_current_cyl(drive, track);
7097 // AL = number of sectors read (same value as passed)
7098 SET_AH(0x00); // success
7099 CLEAR_CF(); // success
7100 return;
7102 else { // if (ah == 0x04)
7103 // Verify Diskette Sectors
7105 // ??? should track be new val from return_status[3] ?
7106 set_diskette_current_cyl(drive, track);
7107 // AL = number of sectors verified (same value as passed)
7108 CLEAR_CF(); // success
7109 SET_AH(0x00); // success
7110 return;
7114 case 0x05: // format diskette track
7115 BX_DEBUG_INT13_FL("floppy f05\n");
7117 num_sectors = GET_AL();
7118 track = GET_CH();
7119 head = GET_DH();
7120 drive = GET_ELDL();
7122 if ((drive > 1) || (head > 1) || (track > 79) ||
7123 (num_sectors == 0) || (num_sectors > 18)) {
7124 SET_AH(1);
7125 set_diskette_ret_status(1);
7126 SET_CF(); // error occurred
7129 // see if drive exists
7130 if (floppy_drive_exists(drive) == 0) {
7131 SET_AH(0x80); // drive not responding
7132 set_diskette_ret_status(0x80);
7133 SET_CF(); // error occurred
7134 return;
7137 // see if media in drive, and type is known
7138 if (floppy_media_known(drive) == 0) {
7139 if (floppy_media_sense(drive) == 0) {
7140 SET_AH(0x0C); // Media type not found
7141 set_diskette_ret_status(0x0C);
7142 SET_AL(0); // no sectors read
7143 SET_CF(); // error occurred
7144 return;
7148 // set up DMA controller for transfer
7149 page = (ES >> 12); // upper 4 bits
7150 base_es = (ES << 4); // lower 16bits contributed by ES
7151 base_address = base_es + BX; // lower 16 bits of address
7152 // contributed by ES:BX
7153 if ( base_address < base_es ) {
7154 // in case of carry, adjust page by 1
7155 page++;
7157 base_count = (num_sectors * 4) - 1;
7159 // check for 64K boundary overrun
7160 last_addr = base_address + base_count;
7161 if (last_addr < base_address) {
7162 SET_AH(0x09);
7163 set_diskette_ret_status(0x09);
7164 SET_AL(0); // no sectors read
7165 SET_CF(); // error occurred
7166 return;
7169 outb(0x000a, 0x06);
7170 outb(0x000c, 0x00); // clear flip-flop
7171 outb(0x0004, base_address);
7172 outb(0x0004, base_address>>8);
7173 outb(0x000c, 0x00); // clear flip-flop
7174 outb(0x0005, base_count);
7175 outb(0x0005, base_count>>8);
7176 mode_register = 0x4a; // single mode, increment, autoinit disable,
7177 // transfer type=read, channel 2
7178 outb(0x000b, mode_register);
7179 // port 81: DMA-1 Page Register, channel 2
7180 outb(0x0081, page);
7181 outb(0x000a, 0x02);
7183 // set up floppy controller for transfer
7184 val8 = read_byte(0x0000, 0x043e);
7185 val8 &= 0x7f;
7186 write_byte(0x0000, 0x043e, val8);
7187 // turn on motor of selected drive, DMA & int enabled, normal operation
7188 if (drive)
7189 dor = 0x20;
7190 else
7191 dor = 0x10;
7192 dor |= 0x0c;
7193 dor |= drive;
7194 outb(0x03f2, dor);
7196 // reset the disk motor timeout value of INT 08
7197 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
7199 // check port 3f4 for drive readiness
7200 val8 = inb(0x3f4);
7201 if ( (val8 & 0xf0) != 0x80 )
7202 BX_PANIC("int13_diskette:f05: ctrl not ready\n");
7204 // send read-normal-data command (6 bytes) to controller
7205 outb(0x03f5, 0x4d); // 4d: format track
7206 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7207 outb(0x03f5, 2); // 512 byte sector size
7208 outb(0x03f5, num_sectors); // number of sectors per track
7209 outb(0x03f5, 0); // Gap length
7210 outb(0x03f5, 0xf6); // Fill byte
7211 // turn on interrupts
7212 ASM_START
7213 sti
7214 ASM_END
7215 // wait on 40:3e bit 7 to become 1
7216 val8 = (read_byte(0x0000, 0x043e) & 0x80);
7217 while ( val8 == 0 ) {
7218 val8 = (read_byte(0x0000, 0x043e) & 0x80);
7220 val8 = 0; // separate asm from while() loop
7221 // turn off interrupts
7222 ASM_START
7223 cli
7224 ASM_END
7225 // set 40:3e bit 7 to 0
7226 val8 = read_byte(0x0000, 0x043e);
7227 val8 &= 0x7f;
7228 write_byte(0x0000, 0x043e, val8);
7229 // check port 3f4 for accessibility to status bytes
7230 val8 = inb(0x3f4);
7231 if ( (val8 & 0xc0) != 0xc0 )
7232 BX_PANIC("int13_diskette: ctrl not ready\n");
7234 // read 7 return status bytes from controller
7235 // using loop index broken, have to unroll...
7236 return_status[0] = inb(0x3f5);
7237 return_status[1] = inb(0x3f5);
7238 return_status[2] = inb(0x3f5);
7239 return_status[3] = inb(0x3f5);
7240 return_status[4] = inb(0x3f5);
7241 return_status[5] = inb(0x3f5);
7242 return_status[6] = inb(0x3f5);
7243 // record in BIOS Data Area
7244 write_byte(0x0040, 0x0042, return_status[0]);
7245 write_byte(0x0040, 0x0043, return_status[1]);
7246 write_byte(0x0040, 0x0044, return_status[2]);
7247 write_byte(0x0040, 0x0045, return_status[3]);
7248 write_byte(0x0040, 0x0046, return_status[4]);
7249 write_byte(0x0040, 0x0047, return_status[5]);
7250 write_byte(0x0040, 0x0048, return_status[6]);
7252 if ( (return_status[0] & 0xc0) != 0 ) {
7253 if ( (return_status[1] & 0x02) != 0 ) {
7254 // diskette not writable.
7255 // AH=status code=0x03 (tried to write on write-protected disk)
7256 // AL=number of sectors written=0
7257 AX = 0x0300;
7258 SET_CF();
7259 return;
7260 } else {
7261 BX_PANIC("int13_diskette_function: write error\n");
7265 SET_AH(0);
7266 set_diskette_ret_status(0);
7267 set_diskette_current_cyl(drive, 0);
7268 CLEAR_CF(); // successful
7269 return;
7272 case 0x08: // read diskette drive parameters
7273 BX_DEBUG_INT13_FL("floppy f08\n");
7274 drive = GET_ELDL();
7276 if (drive > 1) {
7277 AX = 0;
7278 BX = 0;
7279 CX = 0;
7280 DX = 0;
7281 ES = 0;
7282 DI = 0;
7283 SET_DL(num_floppies);
7284 SET_CF();
7285 return;
7288 drive_type = inb_cmos(0x10);
7289 num_floppies = 0;
7290 if (drive_type & 0xf0)
7291 num_floppies++;
7292 if (drive_type & 0x0f)
7293 num_floppies++;
7295 if (drive == 0)
7296 drive_type >>= 4;
7297 else
7298 drive_type &= 0x0f;
7300 SET_BH(0);
7301 SET_BL(drive_type);
7302 SET_AH(0);
7303 SET_AL(0);
7304 SET_DL(num_floppies);
7306 switch (drive_type) {
7307 case 0: // none
7308 CX = 0;
7309 SET_DH(0); // max head #
7310 break;
7312 case 1: // 360KB, 5.25"
7313 CX = 0x2709; // 40 tracks, 9 sectors
7314 SET_DH(1); // max head #
7315 break;
7317 case 2: // 1.2MB, 5.25"
7318 CX = 0x4f0f; // 80 tracks, 15 sectors
7319 SET_DH(1); // max head #
7320 break;
7322 case 3: // 720KB, 3.5"
7323 CX = 0x4f09; // 80 tracks, 9 sectors
7324 SET_DH(1); // max head #
7325 break;
7327 case 4: // 1.44MB, 3.5"
7328 CX = 0x4f12; // 80 tracks, 18 sectors
7329 SET_DH(1); // max head #
7330 break;
7332 case 5: // 2.88MB, 3.5"
7333 CX = 0x4f24; // 80 tracks, 36 sectors
7334 SET_DH(1); // max head #
7335 break;
7337 case 6: // 160k, 5.25"
7338 CX = 0x2708; // 40 tracks, 8 sectors
7339 SET_DH(0); // max head #
7340 break;
7342 case 7: // 180k, 5.25"
7343 CX = 0x2709; // 40 tracks, 9 sectors
7344 SET_DH(0); // max head #
7345 break;
7347 case 8: // 320k, 5.25"
7348 CX = 0x2708; // 40 tracks, 8 sectors
7349 SET_DH(1); // max head #
7350 break;
7352 default: // ?
7353 BX_PANIC("floppy: int13: bad floppy type\n");
7356 /* set es & di to point to 11 byte diskette param table in ROM */
7357 ASM_START
7358 push bp
7359 mov bp, sp
7360 mov ax, #diskette_param_table2
7361 mov _int13_diskette_function.DI+2[bp], ax
7362 mov _int13_diskette_function.ES+2[bp], cs
7363 pop bp
7364 ASM_END
7365 CLEAR_CF(); // success
7366 /* disk status not changed upon success */
7367 return;
7370 case 0x15: // read diskette drive type
7371 BX_DEBUG_INT13_FL("floppy f15\n");
7372 drive = GET_ELDL();
7373 if (drive > 1) {
7374 SET_AH(0); // only 2 drives supported
7375 // set_diskette_ret_status here ???
7376 SET_CF();
7377 return;
7379 drive_type = inb_cmos(0x10);
7381 if (drive == 0)
7382 drive_type >>= 4;
7383 else
7384 drive_type &= 0x0f;
7385 CLEAR_CF(); // successful, not present
7386 if (drive_type==0) {
7387 SET_AH(0); // drive not present
7389 else {
7390 SET_AH(1); // drive present, does not support change line
7393 return;
7395 case 0x16: // get diskette change line status
7396 BX_DEBUG_INT13_FL("floppy f16\n");
7397 drive = GET_ELDL();
7398 if (drive > 1) {
7399 SET_AH(0x01); // invalid drive
7400 set_diskette_ret_status(0x01);
7401 SET_CF();
7402 return;
7405 SET_AH(0x06); // change line not supported
7406 set_diskette_ret_status(0x06);
7407 SET_CF();
7408 return;
7410 case 0x17: // set diskette type for format(old)
7411 BX_DEBUG_INT13_FL("floppy f17\n");
7412 /* not used for 1.44M floppies */
7413 SET_AH(0x01); // not supported
7414 set_diskette_ret_status(1); /* not supported */
7415 SET_CF();
7416 return;
7418 case 0x18: // set diskette type for format(new)
7419 BX_DEBUG_INT13_FL("floppy f18\n");
7420 SET_AH(0x01); // do later
7421 set_diskette_ret_status(1);
7422 SET_CF();
7423 return;
7425 default:
7426 BX_INFO("int13_diskette: unsupported AH=%02x\n", GET_AH());
7428 // if ( (ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e) ) {
7429 SET_AH(0x01); // ???
7430 set_diskette_ret_status(1);
7431 SET_CF();
7432 return;
7433 // }
7436 #else // #if BX_SUPPORT_FLOPPY
7437 void
7438 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
7439 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
7441 Bit8u val8;
7443 switch ( GET_AH() ) {
7445 case 0x01: // Read Diskette Status
7446 CLEAR_CF();
7447 val8 = read_byte(0x0000, 0x0441);
7448 SET_AH(val8);
7449 if (val8) {
7450 SET_CF();
7452 return;
7454 default:
7455 SET_CF();
7456 write_byte(0x0000, 0x0441, 0x01);
7457 SET_AH(0x01);
7460 #endif // #if BX_SUPPORT_FLOPPY
7462 void
7463 set_diskette_ret_status(value)
7464 Bit8u value;
7466 write_byte(0x0040, 0x0041, value);
7469 void
7470 set_diskette_current_cyl(drive, cyl)
7471 Bit8u drive;
7472 Bit8u cyl;
7474 if (drive > 1)
7475 BX_PANIC("set_diskette_current_cyl(): drive > 1\n");
7476 write_byte(0x0040, 0x0094+drive, cyl);
7479 void
7480 determine_floppy_media(drive)
7481 Bit16u drive;
7483 #if 0
7484 Bit8u val8, DOR, ctrl_info;
7486 ctrl_info = read_byte(0x0040, 0x008F);
7487 if (drive==1)
7488 ctrl_info >>= 4;
7489 else
7490 ctrl_info &= 0x0f;
7492 #if 0
7493 if (drive == 0) {
7494 DOR = 0x1c; // DOR: drive0 motor on, DMA&int enabled, normal op, drive select 0
7496 else {
7497 DOR = 0x2d; // DOR: drive1 motor on, DMA&int enabled, normal op, drive select 1
7499 #endif
7501 if ( (ctrl_info & 0x04) != 0x04 ) {
7502 // Drive not determined means no drive exists, done.
7503 return;
7506 #if 0
7507 // check Main Status Register for readiness
7508 val8 = inb(0x03f4) & 0x80; // Main Status Register
7509 if (val8 != 0x80)
7510 BX_PANIC("d_f_m: MRQ bit not set\n");
7512 // change line
7514 // existing BDA values
7516 // turn on drive motor
7517 outb(0x03f2, DOR); // Digital Output Register
7518 //
7519 #endif
7520 BX_PANIC("d_f_m: OK so far\n");
7521 #endif
7524 void
7525 int17_function(regs, ds, iret_addr)
7526 pusha_regs_t regs; // regs pushed from PUSHA instruction
7527 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7528 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7530 Bit16u addr,timeout;
7531 Bit8u val8;
7533 ASM_START
7534 sti
7535 ASM_END
7537 addr = read_word(0x0040, (regs.u.r16.dx << 1) + 8);
7538 if ((regs.u.r8.ah < 3) && (regs.u.r16.dx < 3) && (addr > 0)) {
7539 timeout = read_byte(0x0040, 0x0078 + regs.u.r16.dx) << 8;
7540 if (regs.u.r8.ah == 0) {
7541 outb(addr, regs.u.r8.al);
7542 val8 = inb(addr+2);
7543 outb(addr+2, val8 | 0x01); // send strobe
7544 ASM_START
7545 nop
7546 ASM_END
7547 outb(addr+2, val8 & ~0x01);
7548 while (((inb(addr+1) & 0x40) == 0x40) && (timeout)) {
7549 timeout--;
7552 if (regs.u.r8.ah == 1) {
7553 val8 = inb(addr+2);
7554 outb(addr+2, val8 & ~0x04); // send init
7555 ASM_START
7556 nop
7557 ASM_END
7558 outb(addr+2, val8 | 0x04);
7560 val8 = inb(addr+1);
7561 regs.u.r8.ah = (val8 ^ 0x48);
7562 if (!timeout) regs.u.r8.ah |= 0x01;
7563 ClearCF(iret_addr.flags);
7564 } else {
7565 SetCF(iret_addr.flags); // Unsupported
7569 void
7570 int18_function(seq_nr)
7571 Bit16u seq_nr;
7573 Bit16u ebda_seg=read_word(0x0040,0x000E);
7574 Bit16u bootdev;
7575 Bit8u bootdrv;
7576 Bit8u bootchk;
7577 Bit16u bootseg;
7578 Bit16u bootip;
7579 Bit16u status;
7581 struct ipl_entry e;
7583 // if BX_ELTORITO_BOOT is not defined, old behavior
7584 // check bit 5 in CMOS reg 0x2d. load either 0x00 or 0x80 into DL
7585 // in preparation for the intial INT 13h (0=floppy A:, 0x80=C:)
7586 // 0: system boot sequence, first drive C: then A:
7587 // 1: system boot sequence, first drive A: then C:
7588 // else BX_ELTORITO_BOOT is defined
7589 // CMOS regs 0x3D and 0x38 contain the boot sequence:
7590 // CMOS reg 0x3D & 0x0f : 1st boot device
7591 // CMOS reg 0x3D & 0xf0 : 2nd boot device
7592 // CMOS reg 0x38 & 0xf0 : 3rd boot device
7593 // boot device codes:
7594 // 0x00 : not defined
7595 // 0x01 : first floppy
7596 // 0x02 : first harddrive
7597 // 0x03 : first cdrom
7598 // 0x04 - 0x0f : PnP expansion ROMs (e.g. Etherboot)
7599 // else : boot failure
7601 // Get the boot sequence
7602 #if BX_ELTORITO_BOOT
7603 bootdev = inb_cmos(0x3d);
7604 bootdev |= ((inb_cmos(0x38) & 0xf0) << 4);
7605 bootdev >>= 4 * seq_nr;
7606 bootdev &= 0xf;
7607 if (bootdev == 0) BX_PANIC("No bootable device.\n");
7609 /* Translate from CMOS runes to an IPL table offset by subtracting 1 */
7610 bootdev -= 1;
7611 #else
7612 if (seq_nr ==2) BX_PANIC("No more boot devices.");
7613 if (!!(inb_cmos(0x2d) & 0x20) ^ (seq_nr == 1))
7614 /* Boot from floppy if the bit is set or it's the second boot */
7615 bootdev = 0x00;
7616 else
7617 bootdev = 0x01;
7618 #endif
7620 /* Read the boot device from the IPL table */
7621 if (get_boot_vector(bootdev, &e) == 0) {
7622 BX_INFO("Invalid boot device (0x%x)\n", bootdev);
7623 return;
7626 /* Do the loading, and set up vector as a far pointer to the boot
7627 * address, and bootdrv as the boot drive */
7628 print_boot_device(e.type);
7630 switch(e.type) {
7631 case 0x01: /* FDD */
7632 case 0x02: /* HDD */
7634 bootdrv = (e.type == 0x02) ? 0x80 : 0x00;
7635 bootseg = 0x07c0;
7636 status = 0;
7638 ASM_START
7639 push bp
7640 mov bp, sp
7641 push ax
7642 push bx
7643 push cx
7644 push dx
7646 mov dl, _int18_function.bootdrv + 2[bp]
7647 mov ax, _int18_function.bootseg + 2[bp]
7648 mov es, ax ;; segment
7649 mov bx, #0x0000 ;; offset
7650 mov ah, #0x02 ;; function 2, read diskette sector
7651 mov al, #0x01 ;; read 1 sector
7652 mov ch, #0x00 ;; track 0
7653 mov cl, #0x01 ;; sector 1
7654 mov dh, #0x00 ;; head 0
7655 int #0x13 ;; read sector
7656 jnc int19_load_done
7657 mov ax, #0x0001
7658 mov _int18_function.status + 2[bp], ax
7660 int19_load_done:
7661 pop dx
7662 pop cx
7663 pop bx
7664 pop ax
7665 pop bp
7666 ASM_END
7668 if (status != 0) {
7669 print_boot_failure(e.type, 1);
7670 return;
7673 /* Always check the signature on a HDD boot sector; on FDD, only do
7674 * the check if the CMOS doesn't tell us to skip it */
7675 if (e.type != 0x00 || !((inb_cmos(0x38) & 0x01))) {
7676 if (read_word(bootseg,0x1fe) != 0xaa55) {
7677 print_boot_failure(e.type, 0);
7678 return;
7682 #if BX_TCGBIOS
7683 tcpa_add_bootdevice((Bit32u)0L, (Bit32u)bootdrv);
7684 #endif
7686 /* Canonicalize bootseg:bootip */
7687 bootip = (bootseg & 0x0fff) << 4;
7688 bootseg &= 0xf000;
7689 break;
7691 #if BX_ELTORITO_BOOT
7692 case 0x03: /* CD-ROM */
7693 status = cdrom_boot();
7695 // If failure
7696 if ( (status & 0x00ff) !=0 ) {
7697 print_cdromboot_failure(status);
7698 print_boot_failure(e.type, 1);
7699 return;
7702 bootdrv = (Bit8u)(status>>8);
7703 bootseg = read_word(ebda_seg,&EbdaData->cdemu.load_segment);
7704 #if BX_TCGBIOS
7705 tcpa_add_bootdevice((Bit32u)1L, (Bit32u)0L);
7706 #endif
7708 /* Canonicalize bootseg:bootip */
7709 bootip = (bootseg & 0x0fff) << 4;
7710 bootseg &= 0xf000;
7711 break;
7712 #endif
7714 case 0x80: /* Expansion ROM with a Bootstrap Entry Vector (a far pointer) */
7715 bootseg = e.vector >> 16;
7716 bootip = e.vector & 0xffff;
7717 break;
7719 default: return;
7722 #if BX_TCGBIOS
7723 tcpa_ipl((Bit32u)bootseg); /* specs: 8.2.3 steps 4 and 5 */
7724 #endif
7726 /* Jump to the boot vector */
7727 ASM_START
7728 mov bp, sp
7729 ;; Build an iret stack frame that will take us to the boot vector.
7730 ;; iret pops ip, then cs, then flags, so push them in the opposite order.
7731 pushf
7732 mov ax, _int18_function.bootseg + 0[bp]
7733 push ax
7734 mov ax, _int18_function.bootip + 0[bp]
7735 push ax
7736 ;; Set the magic number in ax and the boot drive in dl.
7737 mov ax, #0xaa55
7738 mov dl, _int18_function.bootdrv + 0[bp]
7739 ;; Zero some of the other registers.
7740 xor bx, bx
7741 mov ds, bx
7742 mov es, bx
7743 mov bp, bx
7744 ;; Go!
7745 iret
7746 ASM_END
7749 void
7750 int1a_function(regs, ds, iret_addr)
7751 pusha_regs_t regs; // regs pushed from PUSHA instruction
7752 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7753 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7755 Bit8u val8;
7757 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);
7759 ASM_START
7760 sti
7761 ASM_END
7763 switch (regs.u.r8.ah) {
7764 case 0: // get current clock count
7765 ASM_START
7766 cli
7767 ASM_END
7768 regs.u.r16.cx = BiosData->ticks_high;
7769 regs.u.r16.dx = BiosData->ticks_low;
7770 regs.u.r8.al = BiosData->midnight_flag;
7771 BiosData->midnight_flag = 0; // reset flag
7772 ASM_START
7773 sti
7774 ASM_END
7775 // AH already 0
7776 ClearCF(iret_addr.flags); // OK
7777 break;
7779 case 1: // Set Current Clock Count
7780 ASM_START
7781 cli
7782 ASM_END
7783 BiosData->ticks_high = regs.u.r16.cx;
7784 BiosData->ticks_low = regs.u.r16.dx;
7785 BiosData->midnight_flag = 0; // reset flag
7786 ASM_START
7787 sti
7788 ASM_END
7789 regs.u.r8.ah = 0;
7790 ClearCF(iret_addr.flags); // OK
7791 break;
7794 case 2: // Read CMOS Time
7795 if (rtc_updating()) {
7796 SetCF(iret_addr.flags);
7797 break;
7800 regs.u.r8.dh = inb_cmos(0x00); // Seconds
7801 regs.u.r8.cl = inb_cmos(0x02); // Minutes
7802 regs.u.r8.ch = inb_cmos(0x04); // Hours
7803 regs.u.r8.dl = inb_cmos(0x0b) & 0x01; // Stat Reg B
7804 regs.u.r8.ah = 0;
7805 regs.u.r8.al = regs.u.r8.ch;
7806 ClearCF(iret_addr.flags); // OK
7807 break;
7809 case 3: // Set CMOS Time
7810 // Using a debugger, I notice the following masking/setting
7811 // of bits in Status Register B, by setting Reg B to
7812 // a few values and getting its value after INT 1A was called.
7813 //
7814 // try#1 try#2 try#3
7815 // before 1111 1101 0111 1101 0000 0000
7816 // after 0110 0010 0110 0010 0000 0010
7817 //
7818 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7819 // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
7820 if (rtc_updating()) {
7821 init_rtc();
7822 // fall through as if an update were not in progress
7824 outb_cmos(0x00, regs.u.r8.dh); // Seconds
7825 outb_cmos(0x02, regs.u.r8.cl); // Minutes
7826 outb_cmos(0x04, regs.u.r8.ch); // Hours
7827 // Set Daylight Savings time enabled bit to requested value
7828 val8 = (inb_cmos(0x0b) & 0x60) | 0x02 | (regs.u.r8.dl & 0x01);
7829 // (reg B already selected)
7830 outb_cmos(0x0b, val8);
7831 regs.u.r8.ah = 0;
7832 regs.u.r8.al = val8; // val last written to Reg B
7833 ClearCF(iret_addr.flags); // OK
7834 break;
7836 case 4: // Read CMOS Date
7837 regs.u.r8.ah = 0;
7838 if (rtc_updating()) {
7839 SetCF(iret_addr.flags);
7840 break;
7842 regs.u.r8.cl = inb_cmos(0x09); // Year
7843 regs.u.r8.dh = inb_cmos(0x08); // Month
7844 regs.u.r8.dl = inb_cmos(0x07); // Day of Month
7845 regs.u.r8.ch = inb_cmos(0x32); // Century
7846 regs.u.r8.al = regs.u.r8.ch;
7847 ClearCF(iret_addr.flags); // OK
7848 break;
7850 case 5: // Set CMOS Date
7851 // Using a debugger, I notice the following masking/setting
7852 // of bits in Status Register B, by setting Reg B to
7853 // a few values and getting its value after INT 1A was called.
7854 //
7855 // try#1 try#2 try#3 try#4
7856 // before 1111 1101 0111 1101 0000 0010 0000 0000
7857 // after 0110 1101 0111 1101 0000 0010 0000 0000
7858 //
7859 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7860 // My assumption: RegB = (RegB & 01111111b)
7861 if (rtc_updating()) {
7862 init_rtc();
7863 SetCF(iret_addr.flags);
7864 break;
7866 outb_cmos(0x09, regs.u.r8.cl); // Year
7867 outb_cmos(0x08, regs.u.r8.dh); // Month
7868 outb_cmos(0x07, regs.u.r8.dl); // Day of Month
7869 outb_cmos(0x32, regs.u.r8.ch); // Century
7870 val8 = inb_cmos(0x0b) & 0x7f; // clear halt-clock bit
7871 outb_cmos(0x0b, val8);
7872 regs.u.r8.ah = 0;
7873 regs.u.r8.al = val8; // AL = val last written to Reg B
7874 ClearCF(iret_addr.flags); // OK
7875 break;
7877 case 6: // Set Alarm Time in CMOS
7878 // Using a debugger, I notice the following masking/setting
7879 // of bits in Status Register B, by setting Reg B to
7880 // a few values and getting its value after INT 1A was called.
7881 //
7882 // try#1 try#2 try#3
7883 // before 1101 1111 0101 1111 0000 0000
7884 // after 0110 1111 0111 1111 0010 0000
7885 //
7886 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7887 // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
7888 val8 = inb_cmos(0x0b); // Get Status Reg B
7889 regs.u.r16.ax = 0;
7890 if (val8 & 0x20) {
7891 // Alarm interrupt enabled already
7892 SetCF(iret_addr.flags); // Error: alarm in use
7893 break;
7895 if (rtc_updating()) {
7896 init_rtc();
7897 // fall through as if an update were not in progress
7899 outb_cmos(0x01, regs.u.r8.dh); // Seconds alarm
7900 outb_cmos(0x03, regs.u.r8.cl); // Minutes alarm
7901 outb_cmos(0x05, regs.u.r8.ch); // Hours alarm
7902 outb(0xa1, inb(0xa1) & 0xfe); // enable IRQ 8
7903 // enable Status Reg B alarm bit, clear halt clock bit
7904 outb_cmos(0x0b, (val8 & 0x7f) | 0x20);
7905 ClearCF(iret_addr.flags); // OK
7906 break;
7908 case 7: // Turn off Alarm
7909 // Using a debugger, I notice the following masking/setting
7910 // of bits in Status Register B, by setting Reg B to
7911 // a few values and getting its value after INT 1A was called.
7912 //
7913 // try#1 try#2 try#3 try#4
7914 // before 1111 1101 0111 1101 0010 0000 0010 0010
7915 // after 0100 0101 0101 0101 0000 0000 0000 0010
7916 //
7917 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7918 // My assumption: RegB = (RegB & 01010111b)
7919 val8 = inb_cmos(0x0b); // Get Status Reg B
7920 // clear clock-halt bit, disable alarm bit
7921 outb_cmos(0x0b, val8 & 0x57); // disable alarm bit
7922 regs.u.r8.ah = 0;
7923 regs.u.r8.al = val8; // val last written to Reg B
7924 ClearCF(iret_addr.flags); // OK
7925 break;
7926 #if BX_PCIBIOS
7927 case 0xb1:
7928 // real mode PCI BIOS functions now handled in assembler code
7929 // this C code handles the error code for information only
7930 if (regs.u.r8.bl == 0xff) {
7931 BX_INFO("PCI BIOS: PCI not present\n");
7932 } else if (regs.u.r8.bl == 0x81) {
7933 BX_INFO("unsupported PCI BIOS function 0x%02x\n", regs.u.r8.al);
7934 } else if (regs.u.r8.bl == 0x83) {
7935 BX_INFO("bad PCI vendor ID %04x\n", regs.u.r16.dx);
7936 } else if (regs.u.r8.bl == 0x86) {
7937 BX_INFO("PCI device %04x:%04x not found\n", regs.u.r16.dx, regs.u.r16.cx);
7939 regs.u.r8.ah = regs.u.r8.bl;
7940 SetCF(iret_addr.flags);
7941 break;
7942 #endif
7944 default:
7945 SetCF(iret_addr.flags); // Unsupported
7949 void
7950 int70_function(regs, ds, iret_addr)
7951 pusha_regs_t regs; // regs pushed from PUSHA instruction
7952 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7953 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7955 // INT 70h: IRQ 8 - CMOS RTC interrupt from periodic or alarm modes
7956 Bit8u registerB = 0, registerC = 0;
7958 // Check which modes are enabled and have occurred.
7959 registerB = inb_cmos( 0xB );
7960 registerC = inb_cmos( 0xC );
7962 if( ( registerB & 0x60 ) != 0 ) {
7963 if( ( registerC & 0x20 ) != 0 ) {
7964 // Handle Alarm Interrupt.
7965 ASM_START
7966 sti
7967 int #0x4a
7968 cli
7969 ASM_END
7971 if( ( registerC & 0x40 ) != 0 ) {
7972 // Handle Periodic Interrupt.
7974 if( read_byte( 0x40, 0xA0 ) != 0 ) {
7975 // Wait Interval (Int 15, AH=83) active.
7976 Bit32u time, toggle;
7978 time = read_dword( 0x40, 0x9C ); // Time left in microseconds.
7979 if( time < 0x3D1 ) {
7980 // Done waiting.
7981 Bit16u segment, offset;
7983 offset = read_word( 0x40, 0x98 );
7984 segment = read_word( 0x40, 0x9A );
7985 write_byte( 0x40, 0xA0, 0 ); // Turn of status byte.
7986 outb_cmos( 0xB, registerB & 0x37 ); // Clear the Periodic Interrupt.
7987 write_byte( segment, offset, 0x80 ); // Write to specified flag byte.
7988 } else {
7989 // Continue waiting.
7990 time -= 0x3D1;
7991 write_dword( 0x40, 0x9C, time );
7997 ASM_START
7998 call eoi_both_pics
7999 ASM_END
8003 ASM_START
8004 ;------------------------------------------
8005 ;- INT74h : PS/2 mouse hardware interrupt -
8006 ;------------------------------------------
8007 int74_handler:
8008 sti
8009 pusha
8010 push ds ;; save DS
8011 push #0x00 ;; placeholder for status
8012 push #0x00 ;; placeholder for X
8013 push #0x00 ;; placeholder for Y
8014 push #0x00 ;; placeholder for Z
8015 push #0x00 ;; placeholder for make_far_call boolean
8016 call _int74_function
8017 pop cx ;; remove make_far_call from stack
8018 jcxz int74_done
8020 ;; make far call to EBDA:0022
8021 push #0x00
8022 pop ds
8023 push 0x040E ;; push 0000:040E (opcodes 0xff, 0x36, 0x0E, 0x04)
8024 pop ds
8025 //CALL_EP(0x0022) ;; call far routine (call_Ep DS:0022 :opcodes 0xff, 0x1e, 0x22, 0x00)
8026 call far ptr[0x22]
8027 int74_done:
8028 cli
8029 call eoi_both_pics
8030 add sp, #8 ;; pop status, x, y, z
8032 pop ds ;; restore DS
8033 popa
8034 iret
8037 ;; This will perform an IRET, but will retain value of current CF
8038 ;; by altering flags on stack. Better than RETF #02.
8039 iret_modify_cf:
8040 jc carry_set
8041 push bp
8042 mov bp, sp
8043 and BYTE [bp + 0x06], #0xfe
8044 pop bp
8045 iret
8046 carry_set:
8047 push bp
8048 mov bp, sp
8049 or BYTE [bp + 0x06], #0x01
8050 pop bp
8051 iret
8054 ;----------------------
8055 ;- INT13h (relocated) -
8056 ;----------------------
8058 ; int13_relocated is a little bit messed up since I played with it
8059 ; I have to rewrite it:
8060 ; - call a function that detect which function to call
8061 ; - make all called C function get the same parameters list
8063 int13_relocated:
8065 #if BX_ELTORITO_BOOT
8066 ;; check for an eltorito function
8067 cmp ah,#0x4a
8068 jb int13_not_eltorito
8069 cmp ah,#0x4d
8070 ja int13_not_eltorito
8072 pusha
8073 push es
8074 push ds
8075 push ss
8076 pop ds
8078 push #int13_out
8079 jmp _int13_eltorito ;; ELDX not used
8081 int13_not_eltorito:
8082 push ax
8083 push bx
8084 push cx
8085 push dx
8087 ;; check if emulation active
8088 call _cdemu_isactive
8089 cmp al,#0x00
8090 je int13_cdemu_inactive
8092 ;; check if access to the emulated drive
8093 call _cdemu_emulated_drive
8094 pop dx
8095 push dx
8096 cmp al,dl ;; int13 on emulated drive
8097 jne int13_nocdemu
8099 pop dx
8100 pop cx
8101 pop bx
8102 pop ax
8104 pusha
8105 push es
8106 push ds
8107 push ss
8108 pop ds
8110 push #int13_out
8111 jmp _int13_cdemu ;; ELDX not used
8113 int13_nocdemu:
8114 and dl,#0xE0 ;; mask to get device class, including cdroms
8115 cmp al,dl ;; al is 0x00 or 0x80
8116 jne int13_cdemu_inactive ;; inactive for device class
8118 pop dx
8119 pop cx
8120 pop bx
8121 pop ax
8123 push ax
8124 push cx
8125 push dx
8126 push bx
8128 dec dl ;; real drive is dl - 1
8129 jmp int13_legacy
8131 int13_cdemu_inactive:
8132 pop dx
8133 pop cx
8134 pop bx
8135 pop ax
8137 #endif // BX_ELTORITO_BOOT
8139 int13_noeltorito:
8141 push ax
8142 push cx
8143 push dx
8144 push bx
8146 int13_legacy:
8148 push dx ;; push eltorito value of dx instead of sp
8150 push bp
8151 push si
8152 push di
8154 push es
8155 push ds
8156 push ss
8157 pop ds
8159 ;; now the 16-bit registers can be restored with:
8160 ;; pop ds; pop es; popa; iret
8161 ;; arguments passed to functions should be
8162 ;; DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS
8164 test dl, #0x80
8165 jnz int13_notfloppy
8167 push #int13_out
8168 jmp _int13_diskette_function
8170 int13_notfloppy:
8172 #if BX_USE_ATADRV
8174 cmp dl, #0xE0
8175 jb int13_notcdrom
8177 // ebx is modified: BSD 5.2.1 boot loader problem
8178 // someone should figure out which 32 bit register that actually are used
8180 shr ebx, #16
8181 push bx
8183 call _int13_cdrom
8185 pop bx
8186 shl ebx, #16
8188 jmp int13_out
8190 int13_notcdrom:
8192 #endif
8194 int13_disk:
8195 call _int13_harddisk
8197 int13_out:
8198 pop ds
8199 pop es
8200 popa
8201 iret
8203 ;----------
8204 ;- INT18h -
8205 ;----------
8206 int18_handler: ;; Boot Failure recovery: try the next device.
8208 ;; Reset SP and SS
8209 mov ax, #0xfffe
8210 mov sp, ax
8211 xor ax, ax
8212 mov ss, ax
8214 ;; Get the boot sequence number out of the IPL memory
8215 ;; The first time we do this it will have been set to -1 so
8216 ;; we will start from device 0.
8217 mov bx, #IPL_SEG
8218 mov ds, bx ;; Set segment
8219 mov bx, IPL_SEQUENCE_OFFSET ;; BX is now the sequence number
8220 inc bx ;; ++
8221 mov IPL_SEQUENCE_OFFSET, bx ;; Write it back
8222 mov ds, ax ;; and reset the segment to zero.
8224 ;; Call the C code for the next boot device
8225 push bx
8226 call _int18_function
8228 ;; Boot failed: invoke the boot recovery function...
8229 int #0x18
8231 ;----------
8232 ;- INT19h -
8233 ;----------
8234 int19_relocated: ;; Boot function, relocated
8235 ;;
8236 ;; *** Warning: INT 19h resets the whole machine ***
8237 ;;
8238 ;; Because PV drivers in HVM guests detach some of the emulated devices,
8239 ;; it is not safe to do a soft reboot by just dropping to real mode and
8240 ;; invoking INT 19h -- the boot drives might have disappeared!
8241 ;; If the user asks for a soft reboot, the only thing we can do is
8242 ;; reset the whole machine. When it comes back up, the normal BIOS
8243 ;; boot sequence will start, which is more or less the required behaviour.
8244 ;;
8245 ;; Reset SP and SS
8246 mov ax, #0xfffe
8247 mov sp, ax
8248 xor ax, ax
8249 mov ss, ax
8250 call _machine_reset
8252 ;----------
8253 ;- INT1Ch -
8254 ;----------
8255 int1c_handler: ;; User Timer Tick
8256 iret
8259 ;----------------------
8260 ;- POST: Floppy Drive -
8261 ;----------------------
8262 floppy_drive_post:
8263 mov ax, #0x0000
8264 mov ds, ax
8266 mov al, #0x00
8267 mov 0x043e, al ;; drive 0 & 1 uncalibrated, no interrupt has occurred
8269 mov 0x043f, al ;; diskette motor status: read op, drive0, motors off
8271 mov 0x0440, al ;; diskette motor timeout counter: not active
8272 mov 0x0441, al ;; diskette controller status return code
8274 mov 0x0442, al ;; disk & diskette controller status register 0
8275 mov 0x0443, al ;; diskette controller status register 1
8276 mov 0x0444, al ;; diskette controller status register 2
8277 mov 0x0445, al ;; diskette controller cylinder number
8278 mov 0x0446, al ;; diskette controller head number
8279 mov 0x0447, al ;; diskette controller sector number
8280 mov 0x0448, al ;; diskette controller bytes written
8282 mov 0x048b, al ;; diskette configuration data
8284 ;; -----------------------------------------------------------------
8285 ;; (048F) diskette controller information
8286 ;;
8287 mov al, #0x10 ;; get CMOS diskette drive type
8288 out 0x70, AL
8289 in AL, 0x71
8290 mov ah, al ;; save byte to AH
8292 look_drive0:
8293 shr al, #4 ;; look at top 4 bits for drive 0
8294 jz f0_missing ;; jump if no drive0
8295 mov bl, #0x07 ;; drive0 determined, multi-rate, has changed line
8296 jmp look_drive1
8297 f0_missing:
8298 mov bl, #0x00 ;; no drive0
8300 look_drive1:
8301 mov al, ah ;; restore from AH
8302 and al, #0x0f ;; look at bottom 4 bits for drive 1
8303 jz f1_missing ;; jump if no drive1
8304 or bl, #0x70 ;; drive1 determined, multi-rate, has changed line
8305 f1_missing:
8306 ;; leave high bits in BL zerod
8307 mov 0x048f, bl ;; put new val in BDA (diskette controller information)
8308 ;; -----------------------------------------------------------------
8310 mov al, #0x00
8311 mov 0x0490, al ;; diskette 0 media state
8312 mov 0x0491, al ;; diskette 1 media state
8314 ;; diskette 0,1 operational starting state
8315 ;; drive type has not been determined,
8316 ;; has no changed detection line
8317 mov 0x0492, al
8318 mov 0x0493, al
8320 mov 0x0494, al ;; diskette 0 current cylinder
8321 mov 0x0495, al ;; diskette 1 current cylinder
8323 mov al, #0x02
8324 out #0x0a, al ;; clear DMA-1 channel 2 mask bit
8326 SET_INT_VECTOR(0x1E, #0xF000, #diskette_param_table2)
8327 SET_INT_VECTOR(0x40, #0xF000, #int13_diskette)
8328 SET_INT_VECTOR(0x0E, #0xF000, #int0e_handler) ;; IRQ 6
8330 ret
8333 ;--------------------
8334 ;- POST: HARD DRIVE -
8335 ;--------------------
8336 ; relocated here because the primary POST area isnt big enough.
8337 hard_drive_post:
8338 // IRQ 14 = INT 76h
8339 // INT 76h calls INT 15h function ax=9100
8341 mov al, #0x0a ; 0000 1010 = reserved, disable IRQ 14
8342 mov dx, #0x03f6
8343 out dx, al
8345 mov ax, #0x0000
8346 mov ds, ax
8347 mov 0x0474, al /* hard disk status of last operation */
8348 mov 0x0477, al /* hard disk port offset (XT only ???) */
8349 mov 0x048c, al /* hard disk status register */
8350 mov 0x048d, al /* hard disk error register */
8351 mov 0x048e, al /* hard disk task complete flag */
8352 mov al, #0x01
8353 mov 0x0475, al /* hard disk number attached */
8354 mov al, #0xc0
8355 mov 0x0476, al /* hard disk control byte */
8356 SET_INT_VECTOR(0x13, #0xF000, #int13_handler)
8357 SET_INT_VECTOR(0x76, #0xF000, #int76_handler)
8358 ;; INT 41h: hard disk 0 configuration pointer
8359 ;; INT 46h: hard disk 1 configuration pointer
8360 SET_INT_VECTOR(0x41, #EBDA_SEG, #0x003D)
8361 SET_INT_VECTOR(0x46, #EBDA_SEG, #0x004D)
8363 ;; move disk geometry data from CMOS to EBDA disk parameter table(s)
8364 mov al, #0x12
8365 out #0x70, al
8366 in al, #0x71
8367 and al, #0xf0
8368 cmp al, #0xf0
8369 je post_d0_extended
8370 jmp check_for_hd1
8371 post_d0_extended:
8372 mov al, #0x19
8373 out #0x70, al
8374 in al, #0x71
8375 cmp al, #47 ;; decimal 47 - user definable
8376 je post_d0_type47
8377 HALT(__LINE__)
8378 post_d0_type47:
8379 ;; CMOS purpose param table offset
8380 ;; 1b cylinders low 0
8381 ;; 1c cylinders high 1
8382 ;; 1d heads 2
8383 ;; 1e write pre-comp low 5
8384 ;; 1f write pre-comp high 6
8385 ;; 20 retries/bad map/heads>8 8
8386 ;; 21 landing zone low C
8387 ;; 22 landing zone high D
8388 ;; 23 sectors/track E
8390 mov ax, #EBDA_SEG
8391 mov ds, ax
8393 ;;; Filling EBDA table for hard disk 0.
8394 mov al, #0x1f
8395 out #0x70, al
8396 in al, #0x71
8397 mov ah, al
8398 mov al, #0x1e
8399 out #0x70, al
8400 in al, #0x71
8401 mov (0x003d + 0x05), ax ;; write precomp word
8403 mov al, #0x20
8404 out #0x70, al
8405 in al, #0x71
8406 mov (0x003d + 0x08), al ;; drive control byte
8408 mov al, #0x22
8409 out #0x70, al
8410 in al, #0x71
8411 mov ah, al
8412 mov al, #0x21
8413 out #0x70, al
8414 in al, #0x71
8415 mov (0x003d + 0x0C), ax ;; landing zone word
8417 mov al, #0x1c ;; get cylinders word in AX
8418 out #0x70, al
8419 in al, #0x71 ;; high byte
8420 mov ah, al
8421 mov al, #0x1b
8422 out #0x70, al
8423 in al, #0x71 ;; low byte
8424 mov bx, ax ;; BX = cylinders
8426 mov al, #0x1d
8427 out #0x70, al
8428 in al, #0x71
8429 mov cl, al ;; CL = heads
8431 mov al, #0x23
8432 out #0x70, al
8433 in al, #0x71
8434 mov dl, al ;; DL = sectors
8436 cmp bx, #1024
8437 jnbe hd0_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8439 hd0_post_physical_chs:
8440 ;; no logical CHS mapping used, just physical CHS
8441 ;; use Standard Fixed Disk Parameter Table (FDPT)
8442 mov (0x003d + 0x00), bx ;; number of physical cylinders
8443 mov (0x003d + 0x02), cl ;; number of physical heads
8444 mov (0x003d + 0x0E), dl ;; number of physical sectors
8445 jmp check_for_hd1
8447 hd0_post_logical_chs:
8448 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8449 mov (0x003d + 0x09), bx ;; number of physical cylinders
8450 mov (0x003d + 0x0b), cl ;; number of physical heads
8451 mov (0x003d + 0x04), dl ;; number of physical sectors
8452 mov (0x003d + 0x0e), dl ;; number of logical sectors (same)
8453 mov al, #0xa0
8454 mov (0x003d + 0x03), al ;; A0h signature, indicates translated table
8456 cmp bx, #2048
8457 jnbe hd0_post_above_2048
8458 ;; 1024 < c <= 2048 cylinders
8459 shr bx, #0x01
8460 shl cl, #0x01
8461 jmp hd0_post_store_logical
8463 hd0_post_above_2048:
8464 cmp bx, #4096
8465 jnbe hd0_post_above_4096
8466 ;; 2048 < c <= 4096 cylinders
8467 shr bx, #0x02
8468 shl cl, #0x02
8469 jmp hd0_post_store_logical
8471 hd0_post_above_4096:
8472 cmp bx, #8192
8473 jnbe hd0_post_above_8192
8474 ;; 4096 < c <= 8192 cylinders
8475 shr bx, #0x03
8476 shl cl, #0x03
8477 jmp hd0_post_store_logical
8479 hd0_post_above_8192:
8480 ;; 8192 < c <= 16384 cylinders
8481 shr bx, #0x04
8482 shl cl, #0x04
8484 hd0_post_store_logical:
8485 mov (0x003d + 0x00), bx ;; number of physical cylinders
8486 mov (0x003d + 0x02), cl ;; number of physical heads
8487 ;; checksum
8488 mov cl, #0x0f ;; repeat count
8489 mov si, #0x003d ;; offset to disk0 FDPT
8490 mov al, #0x00 ;; sum
8491 hd0_post_checksum_loop:
8492 add al, [si]
8493 inc si
8494 dec cl
8495 jnz hd0_post_checksum_loop
8496 not al ;; now take 2s complement
8497 inc al
8498 mov [si], al
8499 ;;; Done filling EBDA table for hard disk 0.
8502 check_for_hd1:
8503 ;; is there really a second hard disk? if not, return now
8504 mov al, #0x12
8505 out #0x70, al
8506 in al, #0x71
8507 and al, #0x0f
8508 jnz post_d1_exists
8509 ret
8510 post_d1_exists:
8511 ;; check that the hd type is really 0x0f.
8512 cmp al, #0x0f
8513 jz post_d1_extended
8514 HALT(__LINE__)
8515 post_d1_extended:
8516 ;; check that the extended type is 47 - user definable
8517 mov al, #0x1a
8518 out #0x70, al
8519 in al, #0x71
8520 cmp al, #47 ;; decimal 47 - user definable
8521 je post_d1_type47
8522 HALT(__LINE__)
8523 post_d1_type47:
8524 ;; Table for disk1.
8525 ;; CMOS purpose param table offset
8526 ;; 0x24 cylinders low 0
8527 ;; 0x25 cylinders high 1
8528 ;; 0x26 heads 2
8529 ;; 0x27 write pre-comp low 5
8530 ;; 0x28 write pre-comp high 6
8531 ;; 0x29 heads>8 8
8532 ;; 0x2a landing zone low C
8533 ;; 0x2b landing zone high D
8534 ;; 0x2c sectors/track E
8535 ;;; Fill EBDA table for hard disk 1.
8536 mov ax, #EBDA_SEG
8537 mov ds, ax
8538 mov al, #0x28
8539 out #0x70, al
8540 in al, #0x71
8541 mov ah, al
8542 mov al, #0x27
8543 out #0x70, al
8544 in al, #0x71
8545 mov (0x004d + 0x05), ax ;; write precomp word
8547 mov al, #0x29
8548 out #0x70, al
8549 in al, #0x71
8550 mov (0x004d + 0x08), al ;; drive control byte
8552 mov al, #0x2b
8553 out #0x70, al
8554 in al, #0x71
8555 mov ah, al
8556 mov al, #0x2a
8557 out #0x70, al
8558 in al, #0x71
8559 mov (0x004d + 0x0C), ax ;; landing zone word
8561 mov al, #0x25 ;; get cylinders word in AX
8562 out #0x70, al
8563 in al, #0x71 ;; high byte
8564 mov ah, al
8565 mov al, #0x24
8566 out #0x70, al
8567 in al, #0x71 ;; low byte
8568 mov bx, ax ;; BX = cylinders
8570 mov al, #0x26
8571 out #0x70, al
8572 in al, #0x71
8573 mov cl, al ;; CL = heads
8575 mov al, #0x2c
8576 out #0x70, al
8577 in al, #0x71
8578 mov dl, al ;; DL = sectors
8580 cmp bx, #1024
8581 jnbe hd1_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8583 hd1_post_physical_chs:
8584 ;; no logical CHS mapping used, just physical CHS
8585 ;; use Standard Fixed Disk Parameter Table (FDPT)
8586 mov (0x004d + 0x00), bx ;; number of physical cylinders
8587 mov (0x004d + 0x02), cl ;; number of physical heads
8588 mov (0x004d + 0x0E), dl ;; number of physical sectors
8589 ret
8591 hd1_post_logical_chs:
8592 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8593 mov (0x004d + 0x09), bx ;; number of physical cylinders
8594 mov (0x004d + 0x0b), cl ;; number of physical heads
8595 mov (0x004d + 0x04), dl ;; number of physical sectors
8596 mov (0x004d + 0x0e), dl ;; number of logical sectors (same)
8597 mov al, #0xa0
8598 mov (0x004d + 0x03), al ;; A0h signature, indicates translated table
8600 cmp bx, #2048
8601 jnbe hd1_post_above_2048
8602 ;; 1024 < c <= 2048 cylinders
8603 shr bx, #0x01
8604 shl cl, #0x01
8605 jmp hd1_post_store_logical
8607 hd1_post_above_2048:
8608 cmp bx, #4096
8609 jnbe hd1_post_above_4096
8610 ;; 2048 < c <= 4096 cylinders
8611 shr bx, #0x02
8612 shl cl, #0x02
8613 jmp hd1_post_store_logical
8615 hd1_post_above_4096:
8616 cmp bx, #8192
8617 jnbe hd1_post_above_8192
8618 ;; 4096 < c <= 8192 cylinders
8619 shr bx, #0x03
8620 shl cl, #0x03
8621 jmp hd1_post_store_logical
8623 hd1_post_above_8192:
8624 ;; 8192 < c <= 16384 cylinders
8625 shr bx, #0x04
8626 shl cl, #0x04
8628 hd1_post_store_logical:
8629 mov (0x004d + 0x00), bx ;; number of physical cylinders
8630 mov (0x004d + 0x02), cl ;; number of physical heads
8631 ;; checksum
8632 mov cl, #0x0f ;; repeat count
8633 mov si, #0x004d ;; offset to disk0 FDPT
8634 mov al, #0x00 ;; sum
8635 hd1_post_checksum_loop:
8636 add al, [si]
8637 inc si
8638 dec cl
8639 jnz hd1_post_checksum_loop
8640 not al ;; now take 2s complement
8641 inc al
8642 mov [si], al
8643 ;;; Done filling EBDA table for hard disk 1.
8645 ret
8647 ;--------------------
8648 ;- POST: EBDA segment
8649 ;--------------------
8650 ; relocated here because the primary POST area isnt big enough.
8651 ebda_post:
8652 #if BX_USE_EBDA
8653 mov ax, #EBDA_SEG
8654 mov ds, ax
8655 mov byte ptr [0x0], #EBDA_SIZE
8656 #endif
8657 xor ax, ax ; mov EBDA seg into 40E
8658 mov ds, ax
8659 mov word ptr [0x40E], #EBDA_SEG
8660 ret;;
8662 ;--------------------
8663 ;- POST: EOI + jmp via [0x40:67)
8664 ;--------------------
8665 ; relocated here because the primary POST area isnt big enough.
8666 eoi_jmp_post:
8667 call eoi_both_pics
8669 xor ax, ax
8670 mov ds, ax
8672 jmp far ptr [0x467]
8675 ;--------------------
8676 eoi_both_pics:
8677 mov al, #0x20
8678 out #0xA0, al ;; slave PIC EOI
8679 eoi_master_pic:
8680 mov al, #0x20
8681 out #0x20, al ;; master PIC EOI
8682 ret
8684 ;--------------------
8685 BcdToBin:
8686 ;; in: AL in BCD format
8687 ;; out: AL in binary format, AH will always be 0
8688 ;; trashes BX
8689 mov bl, al
8690 and bl, #0x0f ;; bl has low digit
8691 shr al, #4 ;; al has high digit
8692 mov bh, #10
8693 mul al, bh ;; multiply high digit by 10 (result in AX)
8694 add al, bl ;; then add low digit
8695 ret
8697 ;--------------------
8698 timer_tick_post:
8699 ;; Setup the Timer Ticks Count (0x46C:dword) and
8700 ;; Timer Ticks Roller Flag (0x470:byte)
8701 ;; The Timer Ticks Count needs to be set according to
8702 ;; the current CMOS time, as if ticks have been occurring
8703 ;; at 18.2hz since midnight up to this point. Calculating
8704 ;; this is a little complicated. Here are the factors I gather
8705 ;; regarding this. 14,318,180 hz was the original clock speed,
8706 ;; chosen so it could be divided by either 3 to drive the 5Mhz CPU
8707 ;; at the time, or 4 to drive the CGA video adapter. The div3
8708 ;; source was divided again by 4 to feed a 1.193Mhz signal to
8709 ;; the timer. With a maximum 16bit timer count, this is again
8710 ;; divided down by 65536 to 18.2hz.
8711 ;;
8712 ;; 14,318,180 Hz clock
8713 ;; /3 = 4,772,726 Hz fed to orginal 5Mhz CPU
8714 ;; /4 = 1,193,181 Hz fed to timer
8715 ;; /65536 (maximum timer count) = 18.20650736 ticks/second
8716 ;; 1 second = 18.20650736 ticks
8717 ;; 1 minute = 1092.390442 ticks
8718 ;; 1 hour = 65543.42651 ticks
8719 ;;
8720 ;; Given the values in the CMOS clock, one could calculate
8721 ;; the number of ticks by the following:
8722 ;; ticks = (BcdToBin(seconds) * 18.206507) +
8723 ;; (BcdToBin(minutes) * 1092.3904)
8724 ;; (BcdToBin(hours) * 65543.427)
8725 ;; To get a little more accuracy, since Im using integer
8726 ;; arithmatic, I use:
8727 ;; ticks = (BcdToBin(seconds) * 18206507) / 1000000 +
8728 ;; (BcdToBin(minutes) * 10923904) / 10000 +
8729 ;; (BcdToBin(hours) * 65543427) / 1000
8731 ;; assuming DS=0000
8733 ;; get CMOS seconds
8734 xor eax, eax ;; clear EAX
8735 mov al, #0x00
8736 out #0x70, al
8737 in al, #0x71 ;; AL has CMOS seconds in BCD
8738 call BcdToBin ;; EAX now has seconds in binary
8739 mov edx, #18206507
8740 mul eax, edx
8741 mov ebx, #1000000
8742 xor edx, edx
8743 div eax, ebx
8744 mov ecx, eax ;; ECX will accumulate total ticks
8746 ;; get CMOS minutes
8747 xor eax, eax ;; clear EAX
8748 mov al, #0x02
8749 out #0x70, al
8750 in al, #0x71 ;; AL has CMOS minutes in BCD
8751 call BcdToBin ;; EAX now has minutes in binary
8752 mov edx, #10923904
8753 mul eax, edx
8754 mov ebx, #10000
8755 xor edx, edx
8756 div eax, ebx
8757 add ecx, eax ;; add to total ticks
8759 ;; get CMOS hours
8760 xor eax, eax ;; clear EAX
8761 mov al, #0x04
8762 out #0x70, al
8763 in al, #0x71 ;; AL has CMOS hours in BCD
8764 call BcdToBin ;; EAX now has hours in binary
8765 mov edx, #65543427
8766 mul eax, edx
8767 mov ebx, #1000
8768 xor edx, edx
8769 div eax, ebx
8770 add ecx, eax ;; add to total ticks
8772 mov 0x46C, ecx ;; Timer Ticks Count
8773 xor al, al
8774 mov 0x470, al ;; Timer Ticks Rollover Flag
8775 ret
8777 ;--------------------
8778 int76_handler:
8779 ;; record completion in BIOS task complete flag
8780 push ax
8781 push ds
8782 mov ax, #0x0040
8783 mov ds, ax
8784 mov 0x008E, #0xff
8785 call eoi_both_pics
8786 pop ds
8787 pop ax
8788 iret
8791 ;--------------------
8792 #if BX_APM
8794 use32 386
8795 #define APM_PROT32
8796 #include "apmbios.S"
8798 use16 386
8799 #define APM_PROT16
8800 #include "apmbios.S"
8802 #define APM_REAL
8803 #include "apmbios.S"
8805 #endif
8807 ASM_END
8808 #include "32bitgateway.c"
8809 ASM_START
8811 ;--------------------
8812 #if BX_PCIBIOS
8813 use32 386
8814 .align 16
8815 bios32_structure:
8816 db 0x5f, 0x33, 0x32, 0x5f ;; "_32_" signature
8817 dw bios32_entry_point, 0xf ;; 32 bit physical address
8818 db 0 ;; revision level
8819 ;; length in paragraphs and checksum stored in a word to prevent errors
8820 dw (~(((bios32_entry_point >> 8) + (bios32_entry_point & 0xff) + 0x32) \
8821 & 0xff) << 8) + 0x01
8822 db 0,0,0,0,0 ;; reserved
8824 .align 16
8825 bios32_entry_point:
8826 pushf
8827 cmp eax, #0x49435024
8828 jne unknown_service
8829 mov eax, #0x80000000
8830 mov dx, #0x0cf8
8831 out dx, eax
8832 mov dx, #0x0cfc
8833 in eax, dx
8834 cmp eax, #0x12378086
8835 jne unknown_service
8836 mov ebx, #0x000f0000
8837 mov ecx, #0
8838 mov edx, #pcibios_protected
8839 xor al, al
8840 jmp bios32_end
8841 unknown_service:
8842 mov al, #0x80
8843 bios32_end:
8844 popf
8845 retf
8847 .align 16
8848 pcibios_protected:
8849 pushf
8850 cli
8851 push esi
8852 push edi
8853 cmp al, #0x01 ;; installation check
8854 jne pci_pro_f02
8855 mov bx, #0x0210
8856 mov cx, #0
8857 mov edx, #0x20494350
8858 mov al, #0x01
8859 jmp pci_pro_ok
8860 pci_pro_f02: ;; find pci device
8861 cmp al, #0x02
8862 jne pci_pro_f08
8863 shl ecx, #16
8864 mov cx, dx
8865 mov bx, #0x0000
8866 mov di, #0x00
8867 pci_pro_devloop:
8868 call pci_pro_select_reg
8869 mov dx, #0x0cfc
8870 in eax, dx
8871 cmp eax, ecx
8872 jne pci_pro_nextdev
8873 cmp si, #0
8874 je pci_pro_ok
8875 dec si
8876 pci_pro_nextdev:
8877 inc bx
8878 cmp bx, #0x0100
8879 jne pci_pro_devloop
8880 mov ah, #0x86
8881 jmp pci_pro_fail
8882 pci_pro_f08: ;; read configuration byte
8883 cmp al, #0x08
8884 jne pci_pro_f09
8885 call pci_pro_select_reg
8886 push edx
8887 mov dx, di
8888 and dx, #0x03
8889 add dx, #0x0cfc
8890 in al, dx
8891 pop edx
8892 mov cl, al
8893 jmp pci_pro_ok
8894 pci_pro_f09: ;; read configuration word
8895 cmp al, #0x09
8896 jne pci_pro_f0a
8897 call pci_pro_select_reg
8898 push edx
8899 mov dx, di
8900 and dx, #0x02
8901 add dx, #0x0cfc
8902 in ax, dx
8903 pop edx
8904 mov cx, ax
8905 jmp pci_pro_ok
8906 pci_pro_f0a: ;; read configuration dword
8907 cmp al, #0x0a
8908 jne pci_pro_f0b
8909 call pci_pro_select_reg
8910 push edx
8911 mov dx, #0x0cfc
8912 in eax, dx
8913 pop edx
8914 mov ecx, eax
8915 jmp pci_pro_ok
8916 pci_pro_f0b: ;; write configuration byte
8917 cmp al, #0x0b
8918 jne pci_pro_f0c
8919 call pci_pro_select_reg
8920 push edx
8921 mov dx, di
8922 and dx, #0x03
8923 add dx, #0x0cfc
8924 mov al, cl
8925 out dx, al
8926 pop edx
8927 jmp pci_pro_ok
8928 pci_pro_f0c: ;; write configuration word
8929 cmp al, #0x0c
8930 jne pci_pro_f0d
8931 call pci_pro_select_reg
8932 push edx
8933 mov dx, di
8934 and dx, #0x02
8935 add dx, #0x0cfc
8936 mov ax, cx
8937 out dx, ax
8938 pop edx
8939 jmp pci_pro_ok
8940 pci_pro_f0d: ;; write configuration dword
8941 cmp al, #0x0d
8942 jne pci_pro_unknown
8943 call pci_pro_select_reg
8944 push edx
8945 mov dx, #0x0cfc
8946 mov eax, ecx
8947 out dx, eax
8948 pop edx
8949 jmp pci_pro_ok
8950 pci_pro_unknown:
8951 mov ah, #0x81
8952 pci_pro_fail:
8953 pop edi
8954 pop esi
8955 sti
8956 popf
8957 stc
8958 retf
8959 pci_pro_ok:
8960 xor ah, ah
8961 pop edi
8962 pop esi
8963 sti
8964 popf
8965 clc
8966 retf
8968 pci_pro_select_reg:
8969 push edx
8970 mov eax, #0x800000
8971 mov ax, bx
8972 shl eax, #8
8973 and di, #0xff
8974 or ax, di
8975 and al, #0xfc
8976 mov dx, #0x0cf8
8977 out dx, eax
8978 pop edx
8979 ret
8981 use16 386
8983 pcibios_real:
8984 push eax
8985 push dx
8986 mov eax, #0x80000000
8987 mov dx, #0x0cf8
8988 out dx, eax
8989 mov dx, #0x0cfc
8990 in eax, dx
8991 cmp eax, #0x12378086
8992 je pci_present
8993 pop dx
8994 pop eax
8995 mov ah, #0xff
8996 stc
8997 ret
8998 pci_present:
8999 pop dx
9000 pop eax
9001 cmp al, #0x01 ;; installation check
9002 jne pci_real_f02
9003 mov ax, #0x0001
9004 mov bx, #0x0210
9005 mov cx, #0
9006 mov edx, #0x20494350
9007 mov edi, #0xf0000
9008 mov di, #pcibios_protected
9009 clc
9010 ret
9011 pci_real_f02: ;; find pci device
9012 push esi
9013 push edi
9014 cmp al, #0x02
9015 jne pci_real_f08
9016 shl ecx, #16
9017 mov cx, dx
9018 mov bx, #0x0000
9019 mov di, #0x00
9020 pci_real_devloop:
9021 call pci_real_select_reg
9022 mov dx, #0x0cfc
9023 in eax, dx
9024 cmp eax, ecx
9025 jne pci_real_nextdev
9026 cmp si, #0
9027 je pci_real_ok
9028 dec si
9029 pci_real_nextdev:
9030 inc bx
9031 cmp bx, #0x0100
9032 jne pci_real_devloop
9033 mov dx, cx
9034 shr ecx, #16
9035 mov ah, #0x86
9036 jmp pci_real_fail
9037 pci_real_f08: ;; read configuration byte
9038 cmp al, #0x08
9039 jne pci_real_f09
9040 call pci_real_select_reg
9041 push dx
9042 mov dx, di
9043 and dx, #0x03
9044 add dx, #0x0cfc
9045 in al, dx
9046 pop dx
9047 mov cl, al
9048 jmp pci_real_ok
9049 pci_real_f09: ;; read configuration word
9050 cmp al, #0x09
9051 jne pci_real_f0a
9052 call pci_real_select_reg
9053 push dx
9054 mov dx, di
9055 and dx, #0x02
9056 add dx, #0x0cfc
9057 in ax, dx
9058 pop dx
9059 mov cx, ax
9060 jmp pci_real_ok
9061 pci_real_f0a: ;; read configuration dword
9062 cmp al, #0x0a
9063 jne pci_real_f0b
9064 call pci_real_select_reg
9065 push dx
9066 mov dx, #0x0cfc
9067 in eax, dx
9068 pop dx
9069 mov ecx, eax
9070 jmp pci_real_ok
9071 pci_real_f0b: ;; write configuration byte
9072 cmp al, #0x0b
9073 jne pci_real_f0c
9074 call pci_real_select_reg
9075 push dx
9076 mov dx, di
9077 and dx, #0x03
9078 add dx, #0x0cfc
9079 mov al, cl
9080 out dx, al
9081 pop dx
9082 jmp pci_real_ok
9083 pci_real_f0c: ;; write configuration word
9084 cmp al, #0x0c
9085 jne pci_real_f0d
9086 call pci_real_select_reg
9087 push dx
9088 mov dx, di
9089 and dx, #0x02
9090 add dx, #0x0cfc
9091 mov ax, cx
9092 out dx, ax
9093 pop dx
9094 jmp pci_real_ok
9095 pci_real_f0d: ;; write configuration dword
9096 cmp al, #0x0d
9097 jne pci_real_unknown
9098 call pci_real_select_reg
9099 push dx
9100 mov dx, #0x0cfc
9101 mov eax, ecx
9102 out dx, eax
9103 pop dx
9104 jmp pci_real_ok
9105 pci_real_unknown:
9106 mov ah, #0x81
9107 pci_real_fail:
9108 pop edi
9109 pop esi
9110 stc
9111 ret
9112 pci_real_ok:
9113 xor ah, ah
9114 pop edi
9115 pop esi
9116 clc
9117 ret
9119 pci_real_select_reg:
9120 push dx
9121 mov eax, #0x800000
9122 mov ax, bx
9123 shl eax, #8
9124 and di, #0xff
9125 or ax, di
9126 and al, #0xfc
9127 mov dx, #0x0cf8
9128 out dx, eax
9129 pop dx
9130 ret
9132 .align 16
9133 pci_routing_table_structure:
9134 db 0x24, 0x50, 0x49, 0x52 ;; "$PIR" signature
9135 db 0, 1 ;; version
9136 dw 32 + (6 * 16) ;; table size
9137 db 0 ;; PCI interrupt router bus
9138 db 0x08 ;; PCI interrupt router DevFunc
9139 dw 0x0000 ;; PCI exclusive IRQs
9140 dw 0x8086 ;; compatible PCI interrupt router vendor ID
9141 dw 0x7000 ;; compatible PCI interrupt router device ID
9142 dw 0,0 ;; Miniport data
9143 db 0,0,0,0,0,0,0,0,0,0,0 ;; reserved
9144 db 0x07 ;; checksum
9145 ;; first slot entry PCI-to-ISA (embedded)
9146 db 0 ;; pci bus number
9147 db 0x08 ;; pci device number (bit 7-3)
9148 db 0x61 ;; link value INTA#: pointer into PCI2ISA config space
9149 dw 0x0ca0 ;; IRQ bitmap INTA#
9150 db 0x62 ;; link value INTB#
9151 dw 0x0ca0 ;; IRQ bitmap INTB#
9152 db 0x63 ;; link value INTC#
9153 dw 0x0ca0 ;; IRQ bitmap INTC#
9154 db 0x60 ;; link value INTD#
9155 dw 0x0ca0 ;; IRQ bitmap INTD#
9156 db 0 ;; physical slot (0 = embedded)
9157 db 0 ;; reserved
9158 ;; second slot entry: 1st PCI slot
9159 db 0 ;; pci bus number
9160 db 0x10 ;; pci device number (bit 7-3)
9161 db 0x62 ;; link value INTA#
9162 dw 0x0ca0 ;; IRQ bitmap INTA#
9163 db 0x63 ;; link value INTB#
9164 dw 0x0ca0 ;; IRQ bitmap INTB#
9165 db 0x60 ;; link value INTC#
9166 dw 0x0ca0 ;; IRQ bitmap INTC#
9167 db 0x61 ;; link value INTD#
9168 dw 0x0ca0 ;; IRQ bitmap INTD#
9169 db 1 ;; physical slot (0 = embedded)
9170 db 0 ;; reserved
9171 ;; third slot entry: 2nd PCI slot
9172 db 0 ;; pci bus number
9173 db 0x18 ;; pci device number (bit 7-3)
9174 db 0x63 ;; link value INTA#
9175 dw 0x0ca0 ;; IRQ bitmap INTA#
9176 db 0x60 ;; link value INTB#
9177 dw 0x0ca0 ;; IRQ bitmap INTB#
9178 db 0x61 ;; link value INTC#
9179 dw 0x0ca0 ;; IRQ bitmap INTC#
9180 db 0x62 ;; link value INTD#
9181 dw 0x0ca0 ;; IRQ bitmap INTD#
9182 db 2 ;; physical slot (0 = embedded)
9183 db 0 ;; reserved
9184 ;; 4th slot entry: 3rd PCI slot
9185 db 0 ;; pci bus number
9186 db 0x20 ;; pci device number (bit 7-3)
9187 db 0x60 ;; link value INTA#
9188 dw 0x0ca0 ;; IRQ bitmap INTA#
9189 db 0x61 ;; link value INTB#
9190 dw 0x0ca0 ;; IRQ bitmap INTB#
9191 db 0x62 ;; link value INTC#
9192 dw 0x0ca0 ;; IRQ bitmap INTC#
9193 db 0x63 ;; link value INTD#
9194 dw 0x0ca0 ;; IRQ bitmap INTD#
9195 db 3 ;; physical slot (0 = embedded)
9196 db 0 ;; reserved
9197 ;; 5th slot entry: 4rd PCI slot
9198 db 0 ;; pci bus number
9199 db 0x28 ;; pci device number (bit 7-3)
9200 db 0x61 ;; link value INTA#
9201 dw 0x0ca0 ;; IRQ bitmap INTA#
9202 db 0x62 ;; link value INTB#
9203 dw 0x0ca0 ;; IRQ bitmap INTB#
9204 db 0x63 ;; link value INTC#
9205 dw 0x0ca0 ;; IRQ bitmap INTC#
9206 db 0x60 ;; link value INTD#
9207 dw 0x0ca0 ;; IRQ bitmap INTD#
9208 db 4 ;; physical slot (0 = embedded)
9209 db 0 ;; reserved
9210 ;; 6th slot entry: 5rd PCI slot
9211 db 0 ;; pci bus number
9212 db 0x30 ;; pci device number (bit 7-3)
9213 db 0x62 ;; link value INTA#
9214 dw 0x0ca0 ;; IRQ bitmap INTA#
9215 db 0x63 ;; link value INTB#
9216 dw 0x0ca0 ;; IRQ bitmap INTB#
9217 db 0x60 ;; link value INTC#
9218 dw 0x0ca0 ;; IRQ bitmap INTC#
9219 db 0x61 ;; link value INTD#
9220 dw 0x0ca0 ;; IRQ bitmap INTD#
9221 db 5 ;; physical slot (0 = embedded)
9222 db 0 ;; reserved
9223 #endif // BX_PCIBIOS
9225 ; parallel port detection: base address in DX, index in BX, timeout in CL
9226 detect_parport:
9227 push dx
9228 add dx, #2
9229 in al, dx
9230 and al, #0xdf ; clear input mode
9231 out dx, al
9232 pop dx
9233 mov al, #0xaa
9234 out dx, al
9235 in al, dx
9236 cmp al, #0xaa
9237 jne no_parport
9238 push bx
9239 shl bx, #1
9240 mov [bx+0x408], dx ; Parallel I/O address
9241 pop bx
9242 mov [bx+0x478], cl ; Parallel printer timeout
9243 inc bx
9244 no_parport:
9245 ret
9247 ; serial port detection: base address in DX, index in BX, timeout in CL
9248 detect_serial:
9249 push dx
9250 inc dx
9251 mov al, #0x02
9252 out dx, al
9253 in al, dx
9254 cmp al, #0x02
9255 jne no_serial
9256 inc dx
9257 in al, dx
9258 cmp al, #0x02
9259 jne no_serial
9260 dec dx
9261 xor al, al
9262 out dx, al
9263 pop dx
9264 push bx
9265 shl bx, #1
9266 mov [bx+0x400], dx ; Serial I/O address
9267 pop bx
9268 mov [bx+0x47c], cl ; Serial timeout
9269 inc bx
9270 ret
9271 no_serial:
9272 pop dx
9273 ret
9275 rom_checksum:
9276 push ax
9277 push bx
9278 push cx
9279 xor ax, ax
9280 xor bx, bx
9281 xor cx, cx
9282 mov ch, [2]
9283 shl cx, #1
9284 checksum_loop:
9285 add al, [bx]
9286 inc bx
9287 loop checksum_loop
9288 and al, #0xff
9289 pop cx
9290 pop bx
9291 pop ax
9292 ret
9295 ;; We need a copy of this string, but we are not actually a PnP BIOS,
9296 ;; so make sure it is *not* aligned, so OSes will not see it if they scan.
9297 .align 16
9298 db 0
9299 pnp_string:
9300 .ascii "$PnP"
9303 rom_scan:
9304 ;; Scan for existence of valid expansion ROMS.
9305 ;; Video ROM: from 0xC0000..0xC7FFF in 2k increments
9306 ;; General ROM: from 0xC8000..0xDFFFF in 2k increments
9307 ;; System ROM: only 0xE0000
9308 ;;
9309 ;; Header:
9310 ;; Offset Value
9311 ;; 0 0x55
9312 ;; 1 0xAA
9313 ;; 2 ROM length in 512-byte blocks
9314 ;; 3 ROM initialization entry point (FAR CALL)
9316 #if BX_TCGBIOS
9317 call _tcpa_start_option_rom_scan /* specs: 3.2.3.3 + 10.4.3 */
9318 #endif
9319 mov cx, #0xc000
9320 rom_scan_loop:
9321 mov ds, cx
9322 mov ax, #0x0004 ;; start with increment of 4 (512-byte) blocks = 2k
9323 cmp [0], #0xAA55 ;; look for signature
9324 jne rom_scan_increment
9325 call rom_checksum
9326 jnz rom_scan_increment
9327 mov al, [2] ;; change increment to ROM length in 512-byte blocks
9329 ;; We want our increment in 512-byte quantities, rounded to
9330 ;; the nearest 2k quantity, since we only scan at 2k intervals.
9331 test al, #0x03
9332 jz block_count_rounded
9333 and al, #0xfc ;; needs rounding up
9334 add al, #0x04
9335 block_count_rounded:
9337 #if BX_TCGBIOS
9338 push ax
9339 push ds
9340 push ecx
9341 xor ax, ax
9342 mov ds, ax
9343 and ecx, #0xffff
9344 push ecx ;; segment where option rom is located at
9345 call _tcpa_option_rom /* specs: 3.2.3.3 */
9346 add sp, #4 ;; pop segment
9347 pop ecx ;; original ecx
9348 pop ds
9349 pop ax
9350 #endif
9351 xor bx, bx ;; Restore DS back to 0000:
9352 mov ds, bx
9353 push ax ;; Save AX
9354 push di ;; Save DI
9355 ;; Push addr of ROM entry point
9356 push cx ;; Push seg
9357 push #0x0003 ;; Push offset
9359 ;; Point ES:DI at "$PnP", which tells the ROM that we are a PnP BIOS.
9360 ;; That should stop it grabbing INT 19h; we will use its BEV instead.
9361 mov ax, #0xf000
9362 mov es, ax
9363 lea di, pnp_string
9365 mov bp, sp ;; Call ROM init routine using seg:off on stack
9366 db 0xff ;; call_far ss:[bp+0]
9367 db 0x5e
9368 db 0
9369 cli ;; In case expansion ROM BIOS turns IF on
9370 add sp, #2 ;; Pop offset value
9371 pop cx ;; Pop seg value (restore CX)
9373 ;; Look at the ROM's PnP Expansion header. Properly, we're supposed
9374 ;; to init all the ROMs and then go back and build an IPL table of
9375 ;; all the bootable devices, but we can get away with one pass.
9376 mov ds, cx ;; ROM base
9377 mov bx, 0x001a ;; 0x1A is the offset into ROM header that contains...
9378 mov ax, [bx] ;; the offset of PnP expansion header, where...
9379 cmp ax, #0x5024 ;; we look for signature "$PnP"
9380 jne no_bev
9381 mov ax, 2[bx]
9382 cmp ax, #0x506e
9383 jne no_bev
9384 mov ax, 0x1a[bx] ;; 0x1A is also the offset into the expansion header of...
9385 cmp ax, #0x0000 ;; the Bootstrap Entry Vector, or zero if there is none.
9386 je no_bev
9388 ;; Found a device that thinks it can boot the system. Record its BEV.
9389 mov bx, #IPL_SEG ;; Go to the segment where the IPL table lives
9390 mov ds, bx
9391 mov bx, IPL_COUNT_OFFSET ;; Read the number of entries so far
9392 cmp bx, #IPL_TABLE_ENTRIES
9393 je no_bev ;; Get out if the table is full
9394 shl bx, #0x4 ;; Turn count into offset (entries are 16 bytes)
9395 mov 0[bx], #0x80 ;; This entry is a BEV device
9396 mov 6[bx], cx ;; Build a far pointer from the segment...
9397 mov 4[bx], ax ;; and the offset
9398 shr bx, #0x4 ;; Turn the offset back into a count
9399 inc bx ;; We have one more entry now
9400 mov IPL_COUNT_OFFSET, bx ;; Remember that.
9402 no_bev:
9403 pop di ;; Restore DI
9404 pop ax ;; Restore AX
9405 rom_scan_increment:
9406 shl ax, #5 ;; convert 512-bytes blocks to 16-byte increments
9407 ;; because the segment selector is shifted left 4 bits.
9408 add cx, ax
9409 cmp cx, #0xe000
9410 jbe rom_scan_loop
9412 xor ax, ax ;; Restore DS back to 0000:
9413 mov ds, ax
9414 ret
9416 #ifdef HVMASSIST
9418 ; Copy the SMBIOS entry point from where hvmloader left it.
9419 ; The entry point must be somewhere in 0xf0000-0xfffff on a 16-byte boundary,
9420 ; but the tables themselves can be elsewhere.
9421 smbios_init:
9422 push ax
9423 push cx
9424 push es
9425 push ds
9426 push di
9427 push si
9429 mov cx, #0x001f ; 0x1f bytes to copy
9430 mov ax, #0xf000
9431 mov es, ax ; destination segment is 0xf0000
9432 mov di, #smbios_entry_point ; destination offset
9433 mov ax, #(SMBIOS_PHYSICAL_ADDRESS>>4)
9434 mov ds, ax
9435 mov si, #(SMBIOS_PHYSICAL_ADDRESS&15)
9436 cld
9437 rep
9438 movsb
9440 pop si
9441 pop di
9442 pop ds
9443 pop es
9444 pop cx
9445 pop ax
9447 ret
9449 #endif
9453 ;; for 'C' strings and other data, insert them here with
9454 ;; a the following hack:
9455 ;; DATA_SEG_DEFS_HERE
9458 ;--------
9459 ;- POST -
9460 ;--------
9461 .org 0xe05b ; POST Entry Point
9462 post:
9464 xor ax, ax
9466 ;; first reset the DMA controllers
9467 out 0x0d,al
9468 out 0xda,al
9470 ;; then initialize the DMA controllers
9471 mov al, #0xC0
9472 out 0xD6, al ; cascade mode of channel 4 enabled
9473 mov al, #0x00
9474 out 0xD4, al ; unmask channel 4
9476 ;; Examine CMOS shutdown status.
9477 mov AL, #0x0f
9478 out 0x70, AL
9479 in AL, 0x71
9481 ;; backup status
9482 mov bl, al
9484 ;; Reset CMOS shutdown status.
9485 mov AL, #0x0f
9486 out 0x70, AL ; select CMOS register Fh
9487 mov AL, #0x00
9488 out 0x71, AL ; set shutdown action to normal
9490 ;; Examine CMOS shutdown status.
9491 mov al, bl
9493 ;; 0x00, 0x09, 0x0D+ = normal startup
9494 cmp AL, #0x00
9495 jz normal_post
9496 cmp AL, #0x0d
9497 jae normal_post
9498 cmp AL, #0x09
9499 je normal_post
9501 ;; 0x05 = eoi + jmp via [0x40:0x67] jump
9502 cmp al, #0x05
9503 je eoi_jmp_post
9505 ;; Examine CMOS shutdown status.
9506 ;; 0x01,0x02,0x03,0x04,0x06,0x07,0x08, 0x0a, 0x0b, 0x0c = Unimplemented shutdown status.
9507 push bx
9508 call _shutdown_status_panic
9510 #if 0
9511 HALT(__LINE__)
9513 ;#if 0
9514 ; 0xb0, 0x20, /* mov al, #0x20 */
9515 ; 0xe6, 0x20, /* out 0x20, al ;send EOI to PIC */
9516 ;#endif
9518 pop es
9519 pop ds
9520 popa
9521 iret
9522 #endif
9524 normal_post:
9525 ; case 0: normal startup
9527 cli
9528 mov ax, #0xfffe
9529 mov sp, ax
9530 mov ax, #0x0000
9531 mov ds, ax
9532 mov ss, ax
9534 ;; zero out BIOS data area (40:00..40:ff)
9535 mov es, ax
9536 mov cx, #0x0080 ;; 128 words
9537 mov di, #0x0400
9538 cld
9539 rep
9540 stosw
9542 call _log_bios_start
9544 call _clobber_entry_point
9546 ;; set all interrupts to default handler
9547 mov bx, #0x0000 ;; offset index
9548 mov cx, #0x0100 ;; counter (256 interrupts)
9549 mov ax, #dummy_iret_handler
9550 mov dx, #0xF000
9552 post_default_ints:
9553 mov [bx], ax
9554 inc bx
9555 inc bx
9556 mov [bx], dx
9557 inc bx
9558 inc bx
9559 loop post_default_ints
9561 ;; set vector 0x79 to zero
9562 ;; this is used by 'gardian angel' protection system
9563 SET_INT_VECTOR(0x79, #0, #0)
9565 ;; base memory in K 40:13 (word)
9566 mov ax, #BASE_MEM_IN_K
9567 mov 0x0413, ax
9570 ;; Manufacturing Test 40:12
9571 ;; zerod out above
9573 ;; Warm Boot Flag 0040:0072
9574 ;; value of 1234h = skip memory checks
9575 ;; zerod out above
9578 ;; Printer Services vector
9579 SET_INT_VECTOR(0x17, #0xF000, #int17_handler)
9581 ;; Bootstrap failure vector
9582 SET_INT_VECTOR(0x18, #0xF000, #int18_handler)
9584 ;; Bootstrap Loader vector
9585 SET_INT_VECTOR(0x19, #0xF000, #int19_handler)
9587 ;; User Timer Tick vector
9588 SET_INT_VECTOR(0x1c, #0xF000, #int1c_handler)
9590 ;; Memory Size Check vector
9591 SET_INT_VECTOR(0x12, #0xF000, #int12_handler)
9593 ;; Equipment Configuration Check vector
9594 SET_INT_VECTOR(0x11, #0xF000, #int11_handler)
9596 ;; System Services
9597 SET_INT_VECTOR(0x15, #0xF000, #int15_handler)
9599 ;; EBDA setup
9600 call ebda_post
9602 ;; PIT setup
9603 SET_INT_VECTOR(0x08, #0xF000, #int08_handler)
9604 ;; int 1C already points at dummy_iret_handler (above)
9605 mov al, #0x34 ; timer0: binary count, 16bit count, mode 2
9606 out 0x43, al
9607 #ifdef HVMASSIST
9608 mov al, #0x0b ; #0xe90b = 20 Hz (temporary, until we fix xen/vmx support)
9609 out 0x40, al ; lsb
9610 mov al, #0xe9
9611 out 0x40, al ; msb
9612 #else
9613 mov al, #0x00 ; maximum count of 0000H = 18.2Hz
9614 out 0x40, al
9615 out 0x40, al
9616 #endif
9618 ;; Keyboard
9619 SET_INT_VECTOR(0x09, #0xF000, #int09_handler)
9620 SET_INT_VECTOR(0x16, #0xF000, #int16_handler)
9622 xor ax, ax
9623 mov ds, ax
9624 mov 0x0417, al /* keyboard shift flags, set 1 */
9625 mov 0x0418, al /* keyboard shift flags, set 2 */
9626 mov 0x0419, al /* keyboard alt-numpad work area */
9627 mov 0x0471, al /* keyboard ctrl-break flag */
9628 mov 0x0497, al /* keyboard status flags 4 */
9629 mov al, #0x10
9630 mov 0x0496, al /* keyboard status flags 3 */
9633 /* keyboard head of buffer pointer */
9634 mov bx, #0x001E
9635 mov 0x041A, bx
9637 /* keyboard end of buffer pointer */
9638 mov 0x041C, bx
9640 /* keyboard pointer to start of buffer */
9641 mov bx, #0x001E
9642 mov 0x0480, bx
9644 /* keyboard pointer to end of buffer */
9645 mov bx, #0x003E
9646 mov 0x0482, bx
9648 /* init the keyboard */
9649 call _keyboard_init
9651 ;; mov CMOS Equipment Byte to BDA Equipment Word
9652 mov ax, 0x0410
9653 mov al, #0x14
9654 out 0x70, al
9655 in al, 0x71
9656 mov 0x0410, ax
9658 #if BX_TCGBIOS
9659 call _tcpa_acpi_init
9661 push dword #0
9662 call _tcpa_initialize_tpm
9663 add sp, #4
9665 call _tcpa_do_measure_POSTs
9666 call _tcpa_wake_event /* specs: 3.2.3.7 */
9667 #endif
9669 ;; Parallel setup
9670 SET_INT_VECTOR(0x0F, #0xF000, #dummy_iret_handler)
9671 xor ax, ax
9672 mov ds, ax
9673 xor bx, bx
9674 mov cl, #0x14 ; timeout value
9675 mov dx, #0x378 ; Parallel I/O address, port 1
9676 call detect_parport
9677 mov dx, #0x278 ; Parallel I/O address, port 2
9678 call detect_parport
9679 shl bx, #0x0e
9680 mov ax, 0x410 ; Equipment word bits 14..15 determing # parallel ports
9681 and ax, #0x3fff
9682 or ax, bx ; set number of parallel ports
9683 mov 0x410, ax
9685 ;; Serial setup
9686 SET_INT_VECTOR(0x0C, #0xF000, #dummy_iret_handler)
9687 SET_INT_VECTOR(0x14, #0xF000, #int14_handler)
9688 xor bx, bx
9689 mov cl, #0x0a ; timeout value
9690 mov dx, #0x03f8 ; Serial I/O address, port 1
9691 call detect_serial
9692 mov dx, #0x02f8 ; Serial I/O address, port 2
9693 call detect_serial
9694 mov dx, #0x03e8 ; Serial I/O address, port 3
9695 call detect_serial
9696 mov dx, #0x02e8 ; Serial I/O address, port 4
9697 call detect_serial
9698 shl bx, #0x09
9699 mov ax, 0x410 ; Equipment word bits 9..11 determing # serial ports
9700 and ax, #0xf1ff
9701 or ax, bx ; set number of serial port
9702 mov 0x410, ax
9704 ;; CMOS RTC
9705 SET_INT_VECTOR(0x1A, #0xF000, #int1a_handler)
9706 SET_INT_VECTOR(0x4A, #0xF000, #dummy_iret_handler)
9707 SET_INT_VECTOR(0x70, #0xF000, #int70_handler)
9708 ;; BIOS DATA AREA 0x4CE ???
9709 call timer_tick_post
9711 ;; PS/2 mouse setup
9712 SET_INT_VECTOR(0x74, #0xF000, #int74_handler)
9714 ;; IRQ13 (FPU exception) setup
9715 SET_INT_VECTOR(0x75, #0xF000, #int75_handler)
9717 ;; Video setup
9718 SET_INT_VECTOR(0x10, #0xF000, #int10_handler)
9720 ;; PIC
9721 mov al, #0x11 ; send initialisation commands
9722 out 0x20, al
9723 out 0xa0, al
9724 mov al, #0x08
9725 out 0x21, al
9726 mov al, #0x70
9727 out 0xa1, al
9728 mov al, #0x04
9729 out 0x21, al
9730 mov al, #0x02
9731 out 0xa1, al
9732 mov al, #0x01
9733 out 0x21, al
9734 out 0xa1, al
9735 mov al, #0xb8
9736 out 0x21, AL ;master pic: unmask IRQ 0, 1, 2, 6
9737 #if BX_USE_PS2_MOUSE
9738 mov al, #0x8f
9739 #else
9740 mov al, #0x9f
9741 #endif
9742 out 0xa1, AL ;slave pic: unmask IRQ 12, 13, 14
9744 #ifdef HVMASSIST
9745 call _copy_e820_table
9746 call smbios_init
9747 #endif
9749 call _init_boot_vectors
9751 call rom_scan
9753 call _print_bios_banner
9755 ;;
9756 ;; Floppy setup
9757 ;;
9758 call floppy_drive_post
9760 #if BX_USE_ATADRV
9762 ;;
9763 ;; Hard Drive setup
9764 ;;
9765 call hard_drive_post
9767 ;;
9768 ;; ATA/ATAPI driver setup
9769 ;;
9770 call _ata_init
9771 call _ata_detect
9772 ;;
9773 #else // BX_USE_ATADRV
9775 ;;
9776 ;; Hard Drive setup
9777 ;;
9778 call hard_drive_post
9780 #endif // BX_USE_ATADRV
9782 #if BX_ELTORITO_BOOT
9783 ;;
9784 ;; eltorito floppy/harddisk emulation from cd
9785 ;;
9786 call _cdemu_init
9787 ;;
9788 #endif // BX_ELTORITO_BOOT
9790 #if BX_TCGBIOS
9791 call _tcpa_calling_int19h /* specs: 8.2.3 step 1 */
9792 call _tcpa_add_event_separators /* specs: 8.2.3 step 2 */
9793 #endif
9795 ;; Start the boot sequence. See the comments in int19_relocated
9796 ;; for why we use INT 18h instead of INT 19h here.
9797 int #0x18
9799 #if BX_TCGBIOS
9800 call _tcpa_returned_int19h /* specs: 8.2.3 step 3/7 */
9801 #endif
9803 .org 0xe2c3 ; NMI Handler Entry Point
9804 nmi:
9805 ;; FIXME the NMI handler should not panic
9806 ;; but iret when called from int75 (fpu exception)
9807 call _nmi_handler_msg
9808 iret
9810 int75_handler:
9811 out 0xf0, al // clear irq13
9812 call eoi_both_pics // clear interrupt
9813 int 2 // legacy nmi call
9814 iret
9816 ;-------------------------------------------
9817 ;- INT 13h Fixed Disk Services Entry Point -
9818 ;-------------------------------------------
9819 .org 0xe3fe ; INT 13h Fixed Disk Services Entry Point
9820 int13_handler:
9821 //JMPL(int13_relocated)
9822 jmp int13_relocated
9824 .org 0xe401 ; Fixed Disk Parameter Table
9826 ;----------
9827 ;- INT19h -
9828 ;----------
9829 .org 0xe6f2 ; INT 19h Boot Load Service Entry Point
9830 int19_handler:
9832 jmp int19_relocated
9833 ;-------------------------------------------
9834 ;- System BIOS Configuration Data Table
9835 ;-------------------------------------------
9836 .org BIOS_CONFIG_TABLE
9837 db 0x08 ; Table size (bytes) -Lo
9838 db 0x00 ; Table size (bytes) -Hi
9839 db SYS_MODEL_ID
9840 db SYS_SUBMODEL_ID
9841 db BIOS_REVISION
9842 ; Feature byte 1
9843 ; b7: 1=DMA channel 3 used by hard disk
9844 ; b6: 1=2 interrupt controllers present
9845 ; b5: 1=RTC present
9846 ; b4: 1=BIOS calls int 15h/4Fh every key
9847 ; b3: 1=wait for extern event supported (Int 15h/41h)
9848 ; b2: 1=extended BIOS data area used
9849 ; b1: 0=AT or ESDI bus, 1=MicroChannel
9850 ; b0: 1=Dual bus (MicroChannel + ISA)
9851 db (0 << 7) | \
9852 (1 << 6) | \
9853 (1 << 5) | \
9854 (BX_CALL_INT15_4F << 4) | \
9855 (0 << 3) | \
9856 (BX_USE_EBDA << 2) | \
9857 (0 << 1) | \
9858 (0 << 0)
9859 ; Feature byte 2
9860 ; b7: 1=32-bit DMA supported
9861 ; b6: 1=int16h, function 9 supported
9862 ; b5: 1=int15h/C6h (get POS data) supported
9863 ; b4: 1=int15h/C7h (get mem map info) supported
9864 ; b3: 1=int15h/C8h (en/dis CPU) supported
9865 ; b2: 1=non-8042 kb controller
9866 ; b1: 1=data streaming supported
9867 ; b0: reserved
9868 db (0 << 7) | \
9869 (1 << 6) | \
9870 (0 << 5) | \
9871 (0 << 4) | \
9872 (0 << 3) | \
9873 (0 << 2) | \
9874 (0 << 1) | \
9875 (0 << 0)
9876 ; Feature byte 3
9877 ; b7: not used
9878 ; b6: reserved
9879 ; b5: reserved
9880 ; b4: POST supports ROM-to-RAM enable/disable
9881 ; b3: SCSI on system board
9882 ; b2: info panel installed
9883 ; b1: Initial Machine Load (IML) system - BIOS on disk
9884 ; b0: SCSI supported in IML
9885 db 0x00
9886 ; Feature byte 4
9887 ; b7: IBM private
9888 ; b6: EEPROM present
9889 ; b5-3: ABIOS presence (011 = not supported)
9890 ; b2: private
9891 ; b1: memory split above 16Mb supported
9892 ; b0: POSTEXT directly supported by POST
9893 db 0x00
9894 ; Feature byte 5 (IBM)
9895 ; b1: enhanced mouse
9896 ; b0: flash EPROM
9897 db 0x00
9901 .org 0xe729 ; Baud Rate Generator Table
9903 ;----------
9904 ;- INT14h -
9905 ;----------
9906 .org 0xe739 ; INT 14h Serial Communications Service Entry Point
9907 int14_handler:
9908 push ds
9909 pusha
9910 mov ax, #0x0000
9911 mov ds, ax
9912 call _int14_function
9913 popa
9914 pop ds
9915 iret
9918 ;----------------------------------------
9919 ;- INT 16h Keyboard Service Entry Point -
9920 ;----------------------------------------
9921 .org 0xe82e
9922 int16_handler:
9924 sti
9925 push ds
9926 pushf
9927 pusha
9929 cmp ah, #0x00
9930 je int16_F00
9931 cmp ah, #0x10
9932 je int16_F00
9934 mov bx, #0xf000
9935 mov ds, bx
9936 call _int16_function
9937 popa
9938 popf
9939 pop ds
9940 jz int16_zero_set
9942 int16_zero_clear:
9943 push bp
9944 mov bp, sp
9945 //SEG SS
9946 and BYTE [bp + 0x06], #0xbf
9947 pop bp
9948 iret
9950 int16_zero_set:
9951 push bp
9952 mov bp, sp
9953 //SEG SS
9954 or BYTE [bp + 0x06], #0x40
9955 pop bp
9956 iret
9958 int16_F00:
9959 mov bx, #0x0040
9960 mov ds, bx
9962 int16_wait_for_key:
9963 cli
9964 mov bx, 0x001a
9965 cmp bx, 0x001c
9966 jne int16_key_found
9967 sti
9968 nop
9969 #if 0
9970 /* no key yet, call int 15h, function AX=9002 */
9971 0x50, /* push AX */
9972 0xb8, 0x02, 0x90, /* mov AX, #0x9002 */
9973 0xcd, 0x15, /* int 15h */
9974 0x58, /* pop AX */
9975 0xeb, 0xea, /* jmp WAIT_FOR_KEY */
9976 #endif
9977 jmp int16_wait_for_key
9979 int16_key_found:
9980 mov bx, #0xf000
9981 mov ds, bx
9982 call _int16_function
9983 popa
9984 popf
9985 pop ds
9986 #if 0
9987 /* notify int16 complete w/ int 15h, function AX=9102 */
9988 0x50, /* push AX */
9989 0xb8, 0x02, 0x91, /* mov AX, #0x9102 */
9990 0xcd, 0x15, /* int 15h */
9991 0x58, /* pop AX */
9992 #endif
9993 iret
9997 ;-------------------------------------------------
9998 ;- INT09h : Keyboard Hardware Service Entry Point -
9999 ;-------------------------------------------------
10000 .org 0xe987
10001 int09_handler:
10002 cli
10003 push ax
10005 mov al, #0xAD ;;disable keyboard
10006 out #0x64, al
10008 mov al, #0x0B
10009 out #0x20, al
10010 in al, #0x20
10011 and al, #0x02
10012 jz int09_finish
10014 in al, #0x60 ;;read key from keyboard controller
10015 //test al, #0x80 ;;look for key release
10016 //jnz int09_process_key ;; dont pass releases to intercept?
10018 ;; check for extended key
10019 cmp al, #0xe0
10020 jne int09_call_int15_4f
10022 push ds
10023 xor ax, ax
10024 mov ds, ax
10025 mov al, BYTE [0x496] ;; mf2_state |= 0x01
10026 or al, #0x01
10027 mov BYTE [0x496], al
10028 pop ds
10030 in al, #0x60 ;;read another key from keyboard controller
10032 sti
10034 int09_call_int15_4f:
10035 push ds
10036 pusha
10037 #ifdef BX_CALL_INT15_4F
10038 mov ah, #0x4f ;; allow for keyboard intercept
10039 stc
10040 int #0x15
10041 jnc int09_done
10042 #endif
10045 //int09_process_key:
10046 mov bx, #0xf000
10047 mov ds, bx
10048 call _int09_function
10050 int09_done:
10051 popa
10052 pop ds
10053 cli
10054 call eoi_master_pic
10056 int09_finish:
10057 mov al, #0xAE ;;enable keyboard
10058 out #0x64, al
10059 pop ax
10060 iret
10065 ;----------------------------------------
10066 ;- INT 13h Diskette Service Entry Point -
10067 ;----------------------------------------
10068 .org 0xec59
10069 int13_diskette:
10070 jmp int13_noeltorito
10072 ;---------------------------------------------
10073 ;- INT 0Eh Diskette Hardware ISR Entry Point -
10074 ;---------------------------------------------
10075 .org 0xef57 ; INT 0Eh Diskette Hardware ISR Entry Point
10076 int0e_handler:
10077 push ax
10078 push dx
10079 mov dx, #0x03f4
10080 in al, dx
10081 and al, #0xc0
10082 cmp al, #0xc0
10083 je int0e_normal
10084 mov dx, #0x03f5
10085 mov al, #0x08 ; sense interrupt status
10086 out dx, al
10087 int0e_loop1:
10088 mov dx, #0x03f4
10089 in al, dx
10090 and al, #0xc0
10091 cmp al, #0xc0
10092 jne int0e_loop1
10093 int0e_loop2:
10094 mov dx, #0x03f5
10095 in al, dx
10096 mov dx, #0x03f4
10097 in al, dx
10098 and al, #0xc0
10099 cmp al, #0xc0
10100 je int0e_loop2
10101 int0e_normal:
10102 push ds
10103 mov ax, #0x0000 ;; segment 0000
10104 mov ds, ax
10105 call eoi_master_pic
10106 mov al, 0x043e
10107 or al, #0x80 ;; diskette interrupt has occurred
10108 mov 0x043e, al
10109 pop ds
10110 pop dx
10111 pop ax
10112 iret
10115 .org 0xefc7 ; Diskette Controller Parameter Table
10116 diskette_param_table:
10117 ;; Since no provisions are made for multiple drive types, most
10118 ;; values in this table are ignored. I set parameters for 1.44M
10119 ;; floppy here
10120 db 0xAF
10121 db 0x02 ;; head load time 0000001, DMA used
10122 db 0x25
10123 db 0x02
10124 db 18
10125 db 0x1B
10126 db 0xFF
10127 db 0x6C
10128 db 0xF6
10129 db 0x0F
10130 db 0x08
10133 ;----------------------------------------
10134 ;- INT17h : Printer Service Entry Point -
10135 ;----------------------------------------
10136 .org 0xefd2
10137 int17_handler:
10138 push ds
10139 pusha
10140 mov ax, #0x0000
10141 mov ds, ax
10142 call _int17_function
10143 popa
10144 pop ds
10145 iret
10147 diskette_param_table2:
10148 ;; New diskette parameter table adding 3 parameters from IBM
10149 ;; Since no provisions are made for multiple drive types, most
10150 ;; values in this table are ignored. I set parameters for 1.44M
10151 ;; floppy here
10152 db 0xAF
10153 db 0x02 ;; head load time 0000001, DMA used
10154 db 0x25
10155 db 0x02
10156 db 18
10157 db 0x1B
10158 db 0xFF
10159 db 0x6C
10160 db 0xF6
10161 db 0x0F
10162 db 0x08
10163 db 79 ;; maximum track
10164 db 0 ;; data transfer rate
10165 db 4 ;; drive type in cmos
10167 .org 0xf045 ; INT 10 Functions 0-Fh Entry Point
10168 HALT(__LINE__)
10169 iret
10171 ;----------
10172 ;- INT10h -
10173 ;----------
10174 .org 0xf065 ; INT 10h Video Support Service Entry Point
10175 int10_handler:
10176 ;; dont do anything, since the VGA BIOS handles int10h requests
10177 iret
10179 .org 0xf0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
10181 ;----------
10182 ;- INT12h -
10183 ;----------
10184 .org 0xf841 ; INT 12h Memory Size Service Entry Point
10185 ; ??? different for Pentium (machine check)?
10186 int12_handler:
10187 push ds
10188 mov ax, #0x0040
10189 mov ds, ax
10190 mov ax, 0x0013
10191 pop ds
10192 iret
10194 ;----------
10195 ;- INT11h -
10196 ;----------
10197 .org 0xf84d ; INT 11h Equipment List Service Entry Point
10198 int11_handler:
10199 push ds
10200 mov ax, #0x0040
10201 mov ds, ax
10202 mov ax, 0x0010
10203 pop ds
10204 iret
10206 ;----------
10207 ;- INT15h -
10208 ;----------
10209 .org 0xf859 ; INT 15h System Services Entry Point
10210 int15_handler:
10211 pushf
10212 #if BX_APM
10213 cmp ah, #0x53
10214 je apm_call
10215 #endif
10216 push ds
10217 push es
10218 cmp ah, #0x86
10219 je int15_handler32
10220 cmp ah, #0xE8
10221 je int15_handler32
10222 pusha
<