debuggers.hg

view tools/firmware/rombios/rombios.c @ 16707:7fbc521b07a9

x86, hvm, rombios: INT13 LBA48 support for disks bigger than 128GB.
The new limit should be 2TB.

Signed-off-by: Samuel Thibault <samuel.thibault@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Thu Dec 27 13:00:40 2007 +0000 (2007-12-27)
parents 786a210e7cab
children e6e165f72e57
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 0x58
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 { none, none, none, none, none }, /* ??? */
1154 { none, none, none, none, none }, /* ??? */
1155 { none, none, none, none, none }, /* ??? */
1156 { 0x8500, 0x8700, 0x8900, 0x8b00, none }, /* F11 */
1157 { 0x8600, 0x8800, 0x8a00, 0x8c00, none }, /* F12 */
1158 };
1160 Bit8u
1161 inb(port)
1162 Bit16u port;
1164 ASM_START
1165 push bp
1166 mov bp, sp
1168 push dx
1169 mov dx, 4[bp]
1170 in al, dx
1171 pop dx
1173 pop bp
1174 ASM_END
1177 #if BX_USE_ATADRV
1178 Bit16u
1179 inw(port)
1180 Bit16u port;
1182 ASM_START
1183 push bp
1184 mov bp, sp
1186 push dx
1187 mov dx, 4[bp]
1188 in ax, dx
1189 pop dx
1191 pop bp
1192 ASM_END
1194 #endif
1196 void
1197 outb(port, val)
1198 Bit16u port;
1199 Bit8u val;
1201 ASM_START
1202 push bp
1203 mov bp, sp
1205 push ax
1206 push dx
1207 mov dx, 4[bp]
1208 mov al, 6[bp]
1209 out dx, al
1210 pop dx
1211 pop ax
1213 pop bp
1214 ASM_END
1217 #if BX_USE_ATADRV
1218 void
1219 outw(port, val)
1220 Bit16u port;
1221 Bit16u val;
1223 ASM_START
1224 push bp
1225 mov bp, sp
1227 push ax
1228 push dx
1229 mov dx, 4[bp]
1230 mov ax, 6[bp]
1231 out dx, ax
1232 pop dx
1233 pop ax
1235 pop bp
1236 ASM_END
1238 #endif
1240 void
1241 outb_cmos(cmos_reg, val)
1242 Bit8u cmos_reg;
1243 Bit8u val;
1245 ASM_START
1246 push bp
1247 mov bp, sp
1249 mov al, 4[bp] ;; cmos_reg
1250 out 0x70, al
1251 mov al, 6[bp] ;; val
1252 out 0x71, al
1254 pop bp
1255 ASM_END
1258 Bit8u
1259 inb_cmos(cmos_reg)
1260 Bit8u cmos_reg;
1262 ASM_START
1263 push bp
1264 mov bp, sp
1266 mov al, 4[bp] ;; cmos_reg
1267 out 0x70, al
1268 in al, 0x71
1270 pop bp
1271 ASM_END
1274 void
1275 init_rtc()
1277 outb_cmos(0x0a, 0x26);
1278 outb_cmos(0x0b, 0x02);
1279 inb_cmos(0x0c);
1280 inb_cmos(0x0d);
1283 bx_bool
1284 rtc_updating()
1286 // This function checks to see if the update-in-progress bit
1287 // is set in CMOS Status Register A. If not, it returns 0.
1288 // If it is set, it tries to wait until there is a transition
1289 // to 0, and will return 0 if such a transition occurs. A 1
1290 // is returned only after timing out. The maximum period
1291 // that this bit should be set is constrained to 244useconds.
1292 // The count I use below guarantees coverage or more than
1293 // this time, with any reasonable IPS setting.
1295 Bit16u count;
1297 count = 25000;
1298 while (--count != 0) {
1299 if ( (inb_cmos(0x0a) & 0x80) == 0 )
1300 return(0);
1302 return(1); // update-in-progress never transitioned to 0
1306 Bit8u
1307 read_byte(seg, offset)
1308 Bit16u seg;
1309 Bit16u offset;
1311 ASM_START
1312 push bp
1313 mov bp, sp
1315 push bx
1316 push ds
1317 mov ax, 4[bp] ; segment
1318 mov ds, ax
1319 mov bx, 6[bp] ; offset
1320 mov al, [bx]
1321 ;; al = return value (byte)
1322 pop ds
1323 pop bx
1325 pop bp
1326 ASM_END
1329 Bit16u
1330 read_word(seg, offset)
1331 Bit16u seg;
1332 Bit16u offset;
1334 ASM_START
1335 push bp
1336 mov bp, sp
1338 push bx
1339 push ds
1340 mov ax, 4[bp] ; segment
1341 mov ds, ax
1342 mov bx, 6[bp] ; offset
1343 mov ax, [bx]
1344 ;; ax = return value (word)
1345 pop ds
1346 pop bx
1348 pop bp
1349 ASM_END
1352 void
1353 write_byte(seg, offset, data)
1354 Bit16u seg;
1355 Bit16u offset;
1356 Bit8u data;
1358 ASM_START
1359 push bp
1360 mov bp, sp
1362 push ax
1363 push bx
1364 push ds
1365 mov ax, 4[bp] ; segment
1366 mov ds, ax
1367 mov bx, 6[bp] ; offset
1368 mov al, 8[bp] ; data byte
1369 mov [bx], al ; write data byte
1370 pop ds
1371 pop bx
1372 pop ax
1374 pop bp
1375 ASM_END
1378 void
1379 write_word(seg, offset, data)
1380 Bit16u seg;
1381 Bit16u offset;
1382 Bit16u data;
1384 ASM_START
1385 push bp
1386 mov bp, sp
1388 push ax
1389 push bx
1390 push ds
1391 mov ax, 4[bp] ; segment
1392 mov ds, ax
1393 mov bx, 6[bp] ; offset
1394 mov ax, 8[bp] ; data word
1395 mov [bx], ax ; write data word
1396 pop ds
1397 pop bx
1398 pop ax
1400 pop bp
1401 ASM_END
1404 Bit16u
1405 get_CS()
1407 ASM_START
1408 mov ax, cs
1409 ASM_END
1412 // Bit16u
1413 //get_DS()
1414 //{
1415 //ASM_START
1416 // mov ax, ds
1417 //ASM_END
1418 //}
1419 //
1420 // void
1421 //set_DS(ds_selector)
1422 // Bit16u ds_selector;
1423 //{
1424 //ASM_START
1425 // push bp
1426 // mov bp, sp
1427 //
1428 // push ax
1429 // mov ax, 4[bp] ; ds_selector
1430 // mov ds, ax
1431 // pop ax
1432 //
1433 // pop bp
1434 //ASM_END
1435 //}
1437 Bit16u
1438 get_SS()
1440 ASM_START
1441 mov ax, ss
1442 ASM_END
1445 #ifdef HVMASSIST
1446 void
1447 copy_e820_table()
1449 Bit8u nr_entries = read_byte(0x9000, 0x1e8);
1450 Bit32u base_mem;
1451 if (nr_entries > 32)
1452 nr_entries = 32;
1453 write_word(0xe000, 0x8, nr_entries);
1454 memcpyb(0xe000, 0x10, 0x9000, 0x2d0, nr_entries * 0x14);
1455 /* Report the proper base memory size at address 0x0413: otherwise
1456 * non-e820 code will clobber things if BASE_MEM_IN_K is bigger than
1457 * the first e820 entry. Get the size by reading the second 64bit
1458 * field of the first e820 slot. */
1459 base_mem = read_dword(0x9000, 0x2d0 + 8);
1460 write_word(0x40, 0x13, base_mem >> 10);
1462 #endif /* HVMASSIST */
1464 #if BX_DEBUG_SERIAL
1465 /* serial debug port*/
1466 #define BX_DEBUG_PORT 0x03f8
1468 /* data */
1469 #define UART_RBR 0x00
1470 #define UART_THR 0x00
1472 /* control */
1473 #define UART_IER 0x01
1474 #define UART_IIR 0x02
1475 #define UART_FCR 0x02
1476 #define UART_LCR 0x03
1477 #define UART_MCR 0x04
1478 #define UART_DLL 0x00
1479 #define UART_DLM 0x01
1481 /* status */
1482 #define UART_LSR 0x05
1483 #define UART_MSR 0x06
1484 #define UART_SCR 0x07
1486 int uart_can_tx_byte(base_port)
1487 Bit16u base_port;
1489 return inb(base_port + UART_LSR) & 0x20;
1492 void uart_wait_to_tx_byte(base_port)
1493 Bit16u base_port;
1495 while (!uart_can_tx_byte(base_port));
1498 void uart_wait_until_sent(base_port)
1499 Bit16u base_port;
1501 while (!(inb(base_port + UART_LSR) & 0x40));
1504 void uart_tx_byte(base_port, data)
1505 Bit16u base_port;
1506 Bit8u data;
1508 uart_wait_to_tx_byte(base_port);
1509 outb(base_port + UART_THR, data);
1510 uart_wait_until_sent(base_port);
1512 #endif
1514 void
1515 wrch(c)
1516 Bit8u c;
1518 ASM_START
1519 push bp
1520 mov bp, sp
1522 push bx
1523 mov ah, #0x0e
1524 mov al, 4[bp]
1525 xor bx,bx
1526 int #0x10
1527 pop bx
1529 pop bp
1530 ASM_END
1533 void
1534 send(action, c)
1535 Bit16u action;
1536 Bit8u c;
1538 #if BX_DEBUG_SERIAL
1539 if (c == '\n') uart_tx_byte(BX_DEBUG_PORT, '\r');
1540 uart_tx_byte(BX_DEBUG_PORT, c);
1541 #endif
1542 #ifdef HVMASSIST
1543 outb(0xE9, c);
1544 #endif
1545 #if BX_VIRTUAL_PORTS
1546 if (action & BIOS_PRINTF_DEBUG) outb(DEBUG_PORT, c);
1547 if (action & BIOS_PRINTF_INFO) outb(INFO_PORT, c);
1548 #endif
1549 if (action & BIOS_PRINTF_SCREEN) {
1550 if (c == '\n') wrch('\r');
1551 wrch(c);
1555 void
1556 put_int(action, val, width, neg)
1557 Bit16u action;
1558 short val, width;
1559 bx_bool neg;
1561 short nval = val / 10;
1562 if (nval)
1563 put_int(action, nval, width - 1, neg);
1564 else {
1565 while (--width > 0) send(action, ' ');
1566 if (neg) send(action, '-');
1568 send(action, val - (nval * 10) + '0');
1571 void
1572 put_uint(action, val, width, neg)
1573 Bit16u action;
1574 unsigned short val;
1575 short width;
1576 bx_bool neg;
1578 unsigned short nval = val / 10;
1579 if (nval)
1580 put_uint(action, nval, width - 1, neg);
1581 else {
1582 while (--width > 0) send(action, ' ');
1583 if (neg) send(action, '-');
1585 send(action, val - (nval * 10) + '0');
1588 //--------------------------------------------------------------------------
1589 // bios_printf()
1590 // A compact variable argument printf function which prints its output via
1591 // an I/O port so that it can be logged by Bochs/Plex.
1592 // Currently, only %x is supported (or %02x, %04x, etc).
1593 //
1594 // Supports %[format_width][format]
1595 // where format can be d,x,c,s
1596 //--------------------------------------------------------------------------
1597 void
1598 bios_printf(action, s)
1599 Bit16u action;
1600 Bit8u *s;
1602 Bit8u c, format_char;
1603 bx_bool in_format;
1604 short i;
1605 Bit16u *arg_ptr;
1606 Bit16u arg_seg, arg, nibble, shift_count, format_width;
1608 arg_ptr = &s;
1609 arg_seg = get_SS();
1611 in_format = 0;
1612 format_width = 0;
1614 if ((action & BIOS_PRINTF_DEBHALT) == BIOS_PRINTF_DEBHALT) {
1615 #if BX_VIRTUAL_PORTS
1616 outb(PANIC_PORT2, 0x00);
1617 #endif
1618 bios_printf (BIOS_PRINTF_SCREEN, "FATAL: ");
1621 while (c = read_byte(get_CS(), s)) {
1622 if ( c == '%' ) {
1623 in_format = 1;
1624 format_width = 0;
1626 else if (in_format) {
1627 if ( (c>='0') && (c<='9') ) {
1628 format_width = (format_width * 10) + (c - '0');
1630 else {
1631 arg_ptr++; // increment to next arg
1632 arg = read_word(arg_seg, arg_ptr);
1633 if (c == 'x') {
1634 if (format_width == 0)
1635 format_width = 4;
1636 for (i=format_width-1; i>=0; i--) {
1637 nibble = (arg >> (4 * i)) & 0x000f;
1638 send (action, (nibble<=9)? (nibble+'0') : (nibble-10+'A'));
1641 else if (c == 'u') {
1642 put_uint(action, arg, format_width, 0);
1644 else if (c == 'd') {
1645 if (arg & 0x8000)
1646 put_int(action, -arg, format_width - 1, 1);
1647 else
1648 put_int(action, arg, format_width, 0);
1650 else if (c == 's') {
1651 bios_printf(action & (~BIOS_PRINTF_HALT), arg);
1653 else if (c == 'c') {
1654 send(action, arg);
1656 else
1657 BX_PANIC("bios_printf: unknown format\n");
1658 in_format = 0;
1661 else {
1662 send(action, c);
1664 s ++;
1667 if (action & BIOS_PRINTF_HALT) {
1668 // freeze in a busy loop.
1669 ASM_START
1670 cli
1671 halt2_loop:
1672 hlt
1673 jmp halt2_loop
1674 ASM_END
1678 //--------------------------------------------------------------------------
1679 // keyboard_init
1680 //--------------------------------------------------------------------------
1681 // this file is based on LinuxBIOS implementation of keyboard.c
1682 // could convert to #asm to gain space
1683 void
1684 keyboard_init()
1686 Bit16u max;
1688 /* ------------------- Flush buffers ------------------------*/
1689 /* Wait until buffer is empty */
1690 max=0xffff;
1691 while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
1693 /* flush incoming keys */
1694 max=0x2000;
1695 while (--max > 0) {
1696 outb(0x80, 0x00);
1697 if (inb(0x64) & 0x01) {
1698 inb(0x60);
1699 max = 0x2000;
1703 // Due to timer issues, and if the IPS setting is > 15000000,
1704 // the incoming keys might not be flushed here. That will
1705 // cause a panic a few lines below. See sourceforge bug report :
1706 // [ 642031 ] FATAL: Keyboard RESET error:993
1708 /* ------------------- controller side ----------------------*/
1709 /* send cmd = 0xAA, self test 8042 */
1710 outb(0x64, 0xaa);
1712 /* Wait until buffer is empty */
1713 max=0xffff;
1714 while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
1715 if (max==0x0) keyboard_panic(00);
1717 /* Wait for data */
1718 max=0xffff;
1719 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x01);
1720 if (max==0x0) keyboard_panic(01);
1722 /* read self-test result, 0x55 should be returned from 0x60 */
1723 if ((inb(0x60) != 0x55)){
1724 keyboard_panic(991);
1727 /* send cmd = 0xAB, keyboard interface test */
1728 outb(0x64,0xab);
1730 /* Wait until buffer is empty */
1731 max=0xffff;
1732 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x10);
1733 if (max==0x0) keyboard_panic(10);
1735 /* Wait for data */
1736 max=0xffff;
1737 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x11);
1738 if (max==0x0) keyboard_panic(11);
1740 /* read keyboard interface test result, */
1741 /* 0x00 should be returned form 0x60 */
1742 if ((inb(0x60) != 0x00)) {
1743 keyboard_panic(992);
1746 /* Enable Keyboard clock */
1747 outb(0x64,0xae);
1748 outb(0x64,0xa8);
1750 /* ------------------- keyboard side ------------------------*/
1751 /* reset kerboard and self test (keyboard side) */
1752 outb(0x60, 0xff);
1754 /* Wait until buffer is empty */
1755 max=0xffff;
1756 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x20);
1757 if (max==0x0) keyboard_panic(20);
1759 /* Wait for data */
1760 max=0xffff;
1761 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x21);
1762 if (max==0x0) keyboard_panic(21);
1764 /* keyboard should return ACK */
1765 if ((inb(0x60) != 0xfa)) {
1766 keyboard_panic(993);
1769 /* Wait for data */
1770 max=0xffff;
1771 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x31);
1772 if (max==0x0) keyboard_panic(31);
1774 if ((inb(0x60) != 0xaa)) {
1775 keyboard_panic(994);
1778 /* Disable keyboard */
1779 outb(0x60, 0xf5);
1781 /* Wait until buffer is empty */
1782 max=0xffff;
1783 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x40);
1784 if (max==0x0) keyboard_panic(40);
1786 /* Wait for data */
1787 max=0xffff;
1788 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x41);
1789 if (max==0x0) keyboard_panic(41);
1791 /* keyboard should return ACK */
1792 if ((inb(0x60) != 0xfa)) {
1793 keyboard_panic(995);
1796 /* Write Keyboard Mode */
1797 outb(0x64, 0x60);
1799 /* Wait until buffer is empty */
1800 max=0xffff;
1801 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x50);
1802 if (max==0x0) keyboard_panic(50);
1804 /* send cmd: scan code convert, disable mouse, enable IRQ 1 */
1805 outb(0x60, 0x61);
1807 /* Wait until buffer is empty */
1808 max=0xffff;
1809 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x60);
1810 if (max==0x0) keyboard_panic(60);
1812 /* Enable keyboard */
1813 outb(0x60, 0xf4);
1815 /* Wait until buffer is empty */
1816 max=0xffff;
1817 while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x70);
1818 if (max==0x0) keyboard_panic(70);
1820 /* Wait for data */
1821 max=0xffff;
1822 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x71);
1823 if (max==0x0) keyboard_panic(70);
1825 /* keyboard should return ACK */
1826 if ((inb(0x60) != 0xfa)) {
1827 keyboard_panic(996);
1830 outb(0x80, 0x77);
1833 //--------------------------------------------------------------------------
1834 // keyboard_panic
1835 //--------------------------------------------------------------------------
1836 void
1837 keyboard_panic(status)
1838 Bit16u status;
1840 // If you're getting a 993 keyboard panic here,
1841 // please see the comment in keyboard_init
1843 BX_PANIC("Keyboard error:%u\n",status);
1846 //--------------------------------------------------------------------------
1847 // machine_reset
1848 //--------------------------------------------------------------------------
1849 void
1850 machine_reset()
1852 /* Frob the keyboard reset line to reset the processor */
1853 outb(0x64, 0x60); /* Map the flags register at data port (0x60) */
1854 outb(0x60, 0x14); /* Set the flags to system|disable */
1855 outb(0x64, 0xfe); /* Pulse output 0 (system reset) low */
1856 BX_PANIC("Couldn't reset the machine\n");
1859 //--------------------------------------------------------------------------
1860 // clobber_entry_point
1861 // Because PV drivers in HVM guests detach some of the emulated devices,
1862 // it is not safe to do a soft reboot by just dropping to real mode and
1863 // jumping at ffff:0000. -- the boot drives might have disappeared!
1864 // This rather foul function overwrites(!) the BIOS entry point
1865 // to point at machine-reset, which will cause the Xen tools to
1866 // rebuild the whole machine from scratch.
1867 //--------------------------------------------------------------------------
1868 void
1869 clobber_entry_point()
1871 /* The instruction at the entry point is one byte (0xea) for the
1872 * jump opcode, then two bytes of address, then two of segment.
1873 * Overwrite the address bytes.*/
1874 write_word(0xffff, 0x0001, machine_reset);
1878 //--------------------------------------------------------------------------
1879 // shutdown_status_panic
1880 // called when the shutdown statsu is not implemented, displays the status
1881 //--------------------------------------------------------------------------
1882 void
1883 shutdown_status_panic(status)
1884 Bit16u status;
1886 BX_PANIC("Unimplemented shutdown status: %02x\n",(Bit8u)status);
1889 //--------------------------------------------------------------------------
1890 // print_bios_banner
1891 // displays a the bios version
1892 //--------------------------------------------------------------------------
1893 void
1894 print_bios_banner()
1896 printf(BX_APPNAME" BIOS, %d cpu%s, ", BX_SMP_PROCESSORS, BX_SMP_PROCESSORS>1?"s":"");
1897 printf("%s %s\n", bios_cvs_version_string, bios_date_string);
1898 #if BX_TCGBIOS
1899 printf("TCG-enabled BIOS.\n");
1900 #endif
1901 printf("\n");
1905 //--------------------------------------------------------------------------
1906 // BIOS Boot Specification 1.0.1 compatibility
1907 //
1908 // Very basic support for the BIOS Boot Specification, which allows expansion
1909 // ROMs to register themselves as boot devices, instead of just stealing the
1910 // INT 19h boot vector.
1911 //
1912 // This is a hack: to do it properly requires a proper PnP BIOS and we aren't
1913 // one; we just lie to the option ROMs to make them behave correctly.
1914 // We also don't support letting option ROMs register as bootable disk
1915 // drives (BCVs), only as bootable devices (BEVs).
1916 //
1917 // http://www.phoenix.com/en/Customer+Services/White+Papers-Specs/pc+industry+specifications.htm
1918 //--------------------------------------------------------------------------
1920 /* 256 bytes at 0x9ff00 -- 0x9ffff is used for the IPL boot table. */
1921 #define IPL_SEG 0x9ff0
1922 #define IPL_TABLE_OFFSET 0x0000
1923 #define IPL_TABLE_ENTRIES 8
1924 #define IPL_COUNT_OFFSET 0x0080 /* u16: number of valid table entries */
1925 #define IPL_SEQUENCE_OFFSET 0x0082 /* u16: next boot device */
1927 struct ipl_entry {
1928 Bit16u type;
1929 Bit16u flags;
1930 Bit32u vector;
1931 Bit32u description;
1932 Bit32u reserved;
1933 };
1935 static void
1936 init_boot_vectors()
1938 struct ipl_entry e;
1939 Bit16u count = 0;
1940 Bit16u ss = get_SS();
1942 /* Clear out the IPL table. */
1943 memsetb(IPL_SEG, IPL_TABLE_OFFSET, 0, 0xff);
1945 /* Floppy drive */
1946 e.type = 1; 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 /* First HDD */
1951 e.type = 2; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
1952 memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
1953 count++;
1955 #if BX_ELTORITO_BOOT
1956 /* CDROM */
1957 e.type = 3; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
1958 memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
1959 count++;
1960 #endif
1962 /* Remember how many devices we have */
1963 write_word(IPL_SEG, IPL_COUNT_OFFSET, count);
1964 /* Not tried booting anything yet */
1965 write_word(IPL_SEG, IPL_SEQUENCE_OFFSET, 0xffff);
1968 static Bit8u
1969 get_boot_vector(i, e)
1970 Bit16u i; struct ipl_entry *e;
1972 Bit16u count;
1973 Bit16u ss = get_SS();
1974 /* Get the count of boot devices, and refuse to overrun the array */
1975 count = read_word(IPL_SEG, IPL_COUNT_OFFSET);
1976 if (i >= count) return 0;
1977 /* OK to read this device */
1978 memcpyb(ss, e, IPL_SEG, IPL_TABLE_OFFSET + i * sizeof (*e), sizeof (*e));
1979 return 1;
1983 //--------------------------------------------------------------------------
1984 // print_boot_device
1985 // displays the boot device
1986 //--------------------------------------------------------------------------
1988 static char drivetypes[][10]={"", "Floppy","Hard Disk","CD-Rom", "Network"};
1990 void
1991 print_boot_device(type)
1992 Bit16u type;
1994 /* NIC appears as type 0x80 */
1995 if (type == 0x80 ) type = 0x4;
1996 if (type == 0 || type > 0x4) BX_PANIC("Bad drive type\n");
1997 printf("Booting from %s...\n", drivetypes[type]);
2000 //--------------------------------------------------------------------------
2001 // print_boot_failure
2002 // displays the reason why boot failed
2003 //--------------------------------------------------------------------------
2004 void
2005 print_boot_failure(type, reason)
2006 Bit16u type; Bit8u reason;
2008 if (type == 0 || type > 0x3) BX_PANIC("Bad drive type\n");
2010 printf("Boot from %s failed", drivetypes[type]);
2011 if (type < 4) {
2012 /* Report the reason too */
2013 if (reason==0)
2014 printf(": not a bootable disk");
2015 else
2016 printf(": could not read the boot disk");
2018 printf("\n");
2021 //--------------------------------------------------------------------------
2022 // print_cdromboot_failure
2023 // displays the reason why boot failed
2024 //--------------------------------------------------------------------------
2025 void
2026 print_cdromboot_failure( code )
2027 Bit16u code;
2029 bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, "CDROM boot failure code : %04x\n",code);
2031 return;
2034 void
2035 nmi_handler_msg()
2037 BX_PANIC("NMI Handler called\n");
2040 void
2041 int18_panic_msg()
2043 BX_PANIC("INT18: BOOT FAILURE\n");
2046 void
2047 log_bios_start()
2049 #if BX_DEBUG_SERIAL
2050 outb(BX_DEBUG_PORT+UART_LCR, 0x03); /* setup for serial logging: 8N1 */
2051 #endif
2052 BX_INFO("%s\n", bios_version_string);
2055 bx_bool
2056 set_enable_a20(val)
2057 bx_bool val;
2059 Bit8u oldval;
2061 // Use PS2 System Control port A to set A20 enable
2063 // get current setting first
2064 oldval = inb(0x92);
2066 // change A20 status
2067 if (val)
2068 outb(0x92, oldval | 0x02);
2069 else
2070 outb(0x92, oldval & 0xfd);
2072 return((oldval & 0x02) != 0);
2075 void
2076 debugger_on()
2078 outb(0xfedc, 0x01);
2081 void
2082 debugger_off()
2084 outb(0xfedc, 0x00);
2087 #if BX_USE_ATADRV
2089 // ---------------------------------------------------------------------------
2090 // Start of ATA/ATAPI Driver
2091 // ---------------------------------------------------------------------------
2093 // Global defines -- ATA register and register bits.
2094 // command block & control block regs
2095 #define ATA_CB_DATA 0 // data reg in/out pio_base_addr1+0
2096 #define ATA_CB_ERR 1 // error in pio_base_addr1+1
2097 #define ATA_CB_FR 1 // feature reg out pio_base_addr1+1
2098 #define ATA_CB_SC 2 // sector count in/out pio_base_addr1+2
2099 #define ATA_CB_SN 3 // sector number in/out pio_base_addr1+3
2100 #define ATA_CB_CL 4 // cylinder low in/out pio_base_addr1+4
2101 #define ATA_CB_CH 5 // cylinder high in/out pio_base_addr1+5
2102 #define ATA_CB_DH 6 // device head in/out pio_base_addr1+6
2103 #define ATA_CB_STAT 7 // primary status in pio_base_addr1+7
2104 #define ATA_CB_CMD 7 // command out pio_base_addr1+7
2105 #define ATA_CB_ASTAT 6 // alternate status in pio_base_addr2+6
2106 #define ATA_CB_DC 6 // device control out pio_base_addr2+6
2107 #define ATA_CB_DA 7 // device address in pio_base_addr2+7
2109 #define ATA_CB_ER_ICRC 0x80 // ATA Ultra DMA bad CRC
2110 #define ATA_CB_ER_BBK 0x80 // ATA bad block
2111 #define ATA_CB_ER_UNC 0x40 // ATA uncorrected error
2112 #define ATA_CB_ER_MC 0x20 // ATA media change
2113 #define ATA_CB_ER_IDNF 0x10 // ATA id not found
2114 #define ATA_CB_ER_MCR 0x08 // ATA media change request
2115 #define ATA_CB_ER_ABRT 0x04 // ATA command aborted
2116 #define ATA_CB_ER_NTK0 0x02 // ATA track 0 not found
2117 #define ATA_CB_ER_NDAM 0x01 // ATA address mark not found
2119 #define ATA_CB_ER_P_SNSKEY 0xf0 // ATAPI sense key (mask)
2120 #define ATA_CB_ER_P_MCR 0x08 // ATAPI Media Change Request
2121 #define ATA_CB_ER_P_ABRT 0x04 // ATAPI command abort
2122 #define ATA_CB_ER_P_EOM 0x02 // ATAPI End of Media
2123 #define ATA_CB_ER_P_ILI 0x01 // ATAPI Illegal Length Indication
2125 // ATAPI Interrupt Reason bits in the Sector Count reg (CB_SC)
2126 #define ATA_CB_SC_P_TAG 0xf8 // ATAPI tag (mask)
2127 #define ATA_CB_SC_P_REL 0x04 // ATAPI release
2128 #define ATA_CB_SC_P_IO 0x02 // ATAPI I/O
2129 #define ATA_CB_SC_P_CD 0x01 // ATAPI C/D
2131 // bits 7-4 of the device/head (CB_DH) reg
2132 #define ATA_CB_DH_DEV0 0xa0 // select device 0
2133 #define ATA_CB_DH_DEV1 0xb0 // select device 1
2135 // status reg (CB_STAT and CB_ASTAT) bits
2136 #define ATA_CB_STAT_BSY 0x80 // busy
2137 #define ATA_CB_STAT_RDY 0x40 // ready
2138 #define ATA_CB_STAT_DF 0x20 // device fault
2139 #define ATA_CB_STAT_WFT 0x20 // write fault (old name)
2140 #define ATA_CB_STAT_SKC 0x10 // seek complete
2141 #define ATA_CB_STAT_SERV 0x10 // service
2142 #define ATA_CB_STAT_DRQ 0x08 // data request
2143 #define ATA_CB_STAT_CORR 0x04 // corrected
2144 #define ATA_CB_STAT_IDX 0x02 // index
2145 #define ATA_CB_STAT_ERR 0x01 // error (ATA)
2146 #define ATA_CB_STAT_CHK 0x01 // check (ATAPI)
2148 // device control reg (CB_DC) bits
2149 #define ATA_CB_DC_HD15 0x08 // bit should always be set to one
2150 #define ATA_CB_DC_SRST 0x04 // soft reset
2151 #define ATA_CB_DC_NIEN 0x02 // disable interrupts
2153 // Most mandtory and optional ATA commands (from ATA-3),
2154 #define ATA_CMD_CFA_ERASE_SECTORS 0xC0
2155 #define ATA_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03
2156 #define ATA_CMD_CFA_TRANSLATE_SECTOR 0x87
2157 #define ATA_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD
2158 #define ATA_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38
2159 #define ATA_CMD_CHECK_POWER_MODE1 0xE5
2160 #define ATA_CMD_CHECK_POWER_MODE2 0x98
2161 #define ATA_CMD_DEVICE_RESET 0x08
2162 #define ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90
2163 #define ATA_CMD_FLUSH_CACHE 0xE7
2164 #define ATA_CMD_FORMAT_TRACK 0x50
2165 #define ATA_CMD_IDENTIFY_DEVICE 0xEC
2166 #define ATA_CMD_IDENTIFY_DEVICE_PACKET 0xA1
2167 #define ATA_CMD_IDENTIFY_PACKET_DEVICE 0xA1
2168 #define ATA_CMD_IDLE1 0xE3
2169 #define ATA_CMD_IDLE2 0x97
2170 #define ATA_CMD_IDLE_IMMEDIATE1 0xE1
2171 #define ATA_CMD_IDLE_IMMEDIATE2 0x95
2172 #define ATA_CMD_INITIALIZE_DRIVE_PARAMETERS 0x91
2173 #define ATA_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91
2174 #define ATA_CMD_NOP 0x00
2175 #define ATA_CMD_PACKET 0xA0
2176 #define ATA_CMD_READ_BUFFER 0xE4
2177 #define ATA_CMD_READ_DMA 0xC8
2178 #define ATA_CMD_READ_DMA_QUEUED 0xC7
2179 #define ATA_CMD_READ_MULTIPLE 0xC4
2180 #define ATA_CMD_READ_SECTORS 0x20
2181 #define ATA_CMD_READ_VERIFY_SECTORS 0x40
2182 #define ATA_CMD_RECALIBRATE 0x10
2183 #define ATA_CMD_SEEK 0x70
2184 #define ATA_CMD_SET_FEATURES 0xEF
2185 #define ATA_CMD_SET_MULTIPLE_MODE 0xC6
2186 #define ATA_CMD_SLEEP1 0xE6
2187 #define ATA_CMD_SLEEP2 0x99
2188 #define ATA_CMD_STANDBY1 0xE2
2189 #define ATA_CMD_STANDBY2 0x96
2190 #define ATA_CMD_STANDBY_IMMEDIATE1 0xE0
2191 #define ATA_CMD_STANDBY_IMMEDIATE2 0x94
2192 #define ATA_CMD_WRITE_BUFFER 0xE8
2193 #define ATA_CMD_WRITE_DMA 0xCA
2194 #define ATA_CMD_WRITE_DMA_QUEUED 0xCC
2195 #define ATA_CMD_WRITE_MULTIPLE 0xC5
2196 #define ATA_CMD_WRITE_SECTORS 0x30
2197 #define ATA_CMD_WRITE_VERIFY 0x3C
2199 #define ATA_IFACE_NONE 0x00
2200 #define ATA_IFACE_ISA 0x00
2201 #define ATA_IFACE_PCI 0x01
2203 #define ATA_TYPE_NONE 0x00
2204 #define ATA_TYPE_UNKNOWN 0x01
2205 #define ATA_TYPE_ATA 0x02
2206 #define ATA_TYPE_ATAPI 0x03
2208 #define ATA_DEVICE_NONE 0x00
2209 #define ATA_DEVICE_HD 0xFF
2210 #define ATA_DEVICE_CDROM 0x05
2212 #define ATA_MODE_NONE 0x00
2213 #define ATA_MODE_PIO16 0x00
2214 #define ATA_MODE_PIO32 0x01
2215 #define ATA_MODE_ISADMA 0x02
2216 #define ATA_MODE_PCIDMA 0x03
2217 #define ATA_MODE_USEIRQ 0x10
2219 #define ATA_TRANSLATION_NONE 0
2220 #define ATA_TRANSLATION_LBA 1
2221 #define ATA_TRANSLATION_LARGE 2
2222 #define ATA_TRANSLATION_RECHS 3
2224 #define ATA_DATA_NO 0x00
2225 #define ATA_DATA_IN 0x01
2226 #define ATA_DATA_OUT 0x02
2228 // ---------------------------------------------------------------------------
2229 // ATA/ATAPI driver : initialization
2230 // ---------------------------------------------------------------------------
2231 void ata_init( )
2233 Bit16u ebda_seg=read_word(0x0040,0x000E);
2234 Bit8u channel, device;
2236 // Channels info init.
2237 for (channel=0; channel<BX_MAX_ATA_INTERFACES; channel++) {
2238 write_byte(ebda_seg,&EbdaData->ata.channels[channel].iface,ATA_IFACE_NONE);
2239 write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1,0x0);
2240 write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2,0x0);
2241 write_byte(ebda_seg,&EbdaData->ata.channels[channel].irq,0);
2244 // Devices info init.
2245 for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2246 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
2247 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_NONE);
2248 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable,0);
2249 write_byte(ebda_seg,&EbdaData->ata.devices[device].lock,0);
2250 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode,ATA_MODE_NONE);
2251 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize,0);
2252 write_byte(ebda_seg,&EbdaData->ata.devices[device].translation,ATA_TRANSLATION_NONE);
2253 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads,0);
2254 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders,0);
2255 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt,0);
2256 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads,0);
2257 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders,0);
2258 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt,0);
2260 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors,0L);
2263 // hdidmap and cdidmap init.
2264 for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
2265 write_byte(ebda_seg,&EbdaData->ata.hdidmap[device],BX_MAX_ATA_DEVICES);
2266 write_byte(ebda_seg,&EbdaData->ata.cdidmap[device],BX_MAX_ATA_DEVICES);
2269 write_byte(ebda_seg,&EbdaData->ata.hdcount,0);
2270 write_byte(ebda_seg,&EbdaData->ata.cdcount,0);
2273 // ---------------------------------------------------------------------------
2274 // ATA/ATAPI driver : device detection
2275 // ---------------------------------------------------------------------------
2277 void ata_detect( )
2279 Bit16u ebda_seg=read_word(0x0040,0x000E);
2280 Bit8u hdcount, cdcount, device, type;
2281 Bit8u buffer[0x0200];
2283 #if BX_MAX_ATA_INTERFACES > 0
2284 write_byte(ebda_seg,&EbdaData->ata.channels[0].iface,ATA_IFACE_ISA);
2285 write_word(ebda_seg,&EbdaData->ata.channels[0].iobase1,0x1f0);
2286 write_word(ebda_seg,&EbdaData->ata.channels[0].iobase2,0x3f0);
2287 write_byte(ebda_seg,&EbdaData->ata.channels[0].irq,14);
2288 #endif
2289 #if BX_MAX_ATA_INTERFACES > 1
2290 write_byte(ebda_seg,&EbdaData->ata.channels[1].iface,ATA_IFACE_ISA);
2291 write_word(ebda_seg,&EbdaData->ata.channels[1].iobase1,0x170);
2292 write_word(ebda_seg,&EbdaData->ata.channels[1].iobase2,0x370);
2293 write_byte(ebda_seg,&EbdaData->ata.channels[1].irq,15);
2294 #endif
2295 #if BX_MAX_ATA_INTERFACES > 2
2296 write_byte(ebda_seg,&EbdaData->ata.channels[2].iface,ATA_IFACE_ISA);
2297 write_word(ebda_seg,&EbdaData->ata.channels[2].iobase1,0x1e8);
2298 write_word(ebda_seg,&EbdaData->ata.channels[2].iobase2,0x3e0);
2299 write_byte(ebda_seg,&EbdaData->ata.channels[2].irq,12);
2300 #endif
2301 #if BX_MAX_ATA_INTERFACES > 3
2302 write_byte(ebda_seg,&EbdaData->ata.channels[3].iface,ATA_IFACE_ISA);
2303 write_word(ebda_seg,&EbdaData->ata.channels[3].iobase1,0x168);
2304 write_word(ebda_seg,&EbdaData->ata.channels[3].iobase2,0x360);
2305 write_byte(ebda_seg,&EbdaData->ata.channels[3].irq,11);
2306 #endif
2307 #if BX_MAX_ATA_INTERFACES > 4
2308 #error Please fill the ATA interface informations
2309 #endif
2311 // Device detection
2312 hdcount=cdcount=0;
2314 for(device=0; device<BX_MAX_ATA_DEVICES; device++) {
2315 Bit16u iobase1, iobase2;
2316 Bit8u channel, slave, shift;
2317 Bit8u sc, sn, cl, ch, st;
2319 channel = device / 2;
2320 slave = device % 2;
2322 iobase1 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1);
2323 iobase2 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2);
2325 // Disable interrupts
2326 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2328 // Look for device
2329 outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2330 outb(iobase1+ATA_CB_SC, 0x55);
2331 outb(iobase1+ATA_CB_SN, 0xaa);
2332 outb(iobase1+ATA_CB_SC, 0xaa);
2333 outb(iobase1+ATA_CB_SN, 0x55);
2334 outb(iobase1+ATA_CB_SC, 0x55);
2335 outb(iobase1+ATA_CB_SN, 0xaa);
2337 // If we found something
2338 sc = inb(iobase1+ATA_CB_SC);
2339 sn = inb(iobase1+ATA_CB_SN);
2341 if ( (sc == 0x55) && (sn == 0xaa) ) {
2342 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_UNKNOWN);
2344 // reset the channel
2345 ata_reset (device);
2347 // check for ATA or ATAPI
2348 outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
2349 sc = inb(iobase1+ATA_CB_SC);
2350 sn = inb(iobase1+ATA_CB_SN);
2351 if ( (sc==0x01) && (sn==0x01) ) {
2352 cl = inb(iobase1+ATA_CB_CL);
2353 ch = inb(iobase1+ATA_CB_CH);
2354 st = inb(iobase1+ATA_CB_STAT);
2356 if ( (cl==0x14) && (ch==0xeb) ) {
2357 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATAPI);
2359 else if ( (cl==0x00) && (ch==0x00) && (st!=0x00) ) {
2360 write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATA);
2365 type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type);
2367 // Now we send a IDENTIFY command to ATA device
2368 if(type == ATA_TYPE_ATA) {
2369 Bit32u sectors;
2370 Bit16u cylinders, heads, spt, blksize;
2371 Bit8u translation, removable, mode;
2373 // default mode to PIO16
2374 mode = ATA_MODE_PIO16;
2376 //Temporary values to do the transfer
2377 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2378 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2380 if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE, 1, 0, 0, 0, 0L, get_SS(),buffer) !=0 )
2381 BX_PANIC("ata-detect: Failed to detect ATA device\n");
2383 removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2384 #ifndef NO_PIO32
2385 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2386 #endif
2388 blksize = read_word(get_SS(),buffer+10);
2390 cylinders = read_word(get_SS(),buffer+(1*2)); // word 1
2391 heads = read_word(get_SS(),buffer+(3*2)); // word 3
2392 spt = read_word(get_SS(),buffer+(6*2)); // word 6
2394 sectors = read_dword(get_SS(),buffer+(60*2)); // word 60 and word 61
2396 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
2397 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2398 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2399 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2400 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads, heads);
2401 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders, cylinders);
2402 write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt, spt);
2403 write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors, sectors);
2404 BX_INFO("ata%d-%d: PCHS=%u/%d/%d translation=", channel, slave,cylinders, heads, spt);
2406 translation = inb_cmos(0x39 + channel/2);
2407 for (shift=device%4; shift>0; shift--) translation >>= 2;
2408 translation &= 0x03;
2410 write_byte(ebda_seg,&EbdaData->ata.devices[device].translation, translation);
2412 switch (translation) {
2413 case ATA_TRANSLATION_NONE:
2414 BX_INFO("none");
2415 break;
2416 case ATA_TRANSLATION_LBA:
2417 BX_INFO("lba");
2418 break;
2419 case ATA_TRANSLATION_LARGE:
2420 BX_INFO("large");
2421 break;
2422 case ATA_TRANSLATION_RECHS:
2423 BX_INFO("r-echs");
2424 break;
2426 switch (translation) {
2427 case ATA_TRANSLATION_NONE:
2428 break;
2429 case ATA_TRANSLATION_LBA:
2430 spt = 63;
2431 sectors /= 63;
2432 heads = sectors / 1024;
2433 if (heads>128) heads = 255;
2434 else if (heads>64) heads = 128;
2435 else if (heads>32) heads = 64;
2436 else if (heads>16) heads = 32;
2437 else heads=16;
2438 cylinders = sectors / heads;
2439 break;
2440 case ATA_TRANSLATION_RECHS:
2441 // Take care not to overflow
2442 if (heads==16) {
2443 if(cylinders>61439) cylinders=61439;
2444 heads=15;
2445 cylinders = (Bit16u)((Bit32u)(cylinders)*16/15);
2447 // then go through the large bitshift process
2448 case ATA_TRANSLATION_LARGE:
2449 while(cylinders > 1024) {
2450 cylinders >>= 1;
2451 heads <<= 1;
2453 // If we max out the head count
2454 if (heads > 127) break;
2456 break;
2458 // clip to 1024 cylinders in lchs
2459 if (cylinders > 1024) cylinders=1024;
2460 BX_INFO(" LCHS=%d/%d/%d\n", cylinders, heads, spt);
2462 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads, heads);
2463 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders, cylinders);
2464 write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt, spt);
2466 // fill hdidmap
2467 write_byte(ebda_seg,&EbdaData->ata.hdidmap[hdcount], device);
2468 hdcount++;
2471 // Now we send a IDENTIFY command to ATAPI device
2472 if(type == ATA_TYPE_ATAPI) {
2474 Bit8u type, removable, mode;
2475 Bit16u blksize;
2477 // default mode to PIO16
2478 mode = ATA_MODE_PIO16;
2480 //Temporary values to do the transfer
2481 write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_CDROM);
2482 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
2484 if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE_PACKET, 1, 0, 0, 0, 0L, get_SS(),buffer) != 0)
2485 BX_PANIC("ata-detect: Failed to detect ATAPI device\n");
2487 type = read_byte(get_SS(),buffer+1) & 0x1f;
2488 removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
2489 #ifndef NO_PIO32
2490 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
2491 #endif
2492 blksize = 2048;
2494 write_byte(ebda_seg,&EbdaData->ata.devices[device].device, type);
2495 write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
2496 write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
2497 write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
2499 // fill cdidmap
2500 write_byte(ebda_seg,&EbdaData->ata.cdidmap[cdcount], device);
2501 cdcount++;
2505 Bit32u sizeinmb;
2506 Bit16u ataversion;
2507 Bit8u c, i, version, model[41];
2509 switch (type) {
2510 case ATA_TYPE_ATA:
2511 sizeinmb = read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors);
2512 sizeinmb >>= 11;
2513 case ATA_TYPE_ATAPI:
2514 // Read ATA/ATAPI version
2515 ataversion=((Bit16u)(read_byte(get_SS(),buffer+161))<<8)|read_byte(get_SS(),buffer+160);
2516 for(version=15;version>0;version--) {
2517 if((ataversion&(1<<version))!=0)
2518 break;
2521 // Read model name
2522 for(i=0;i<20;i++){
2523 write_byte(get_SS(),model+(i*2),read_byte(get_SS(),buffer+(i*2)+54+1));
2524 write_byte(get_SS(),model+(i*2)+1,read_byte(get_SS(),buffer+(i*2)+54));
2527 // Reformat
2528 write_byte(get_SS(),model+40,0x00);
2529 for(i=39;i>0;i--){
2530 if(read_byte(get_SS(),model+i)==0x20)
2531 write_byte(get_SS(),model+i,0x00);
2532 else break;
2534 break;
2537 switch (type) {
2538 case ATA_TYPE_ATA:
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 (sizeinmb < 1UL<<16)
2542 printf(" ATA-%d Hard-Disk (%04u MBytes)\n",version,(Bit16u)sizeinmb);
2543 else
2544 printf(" ATA-%d Hard-Disk (%04u GBytes)\n",version,(Bit16u)(sizeinmb>>10));
2545 break;
2546 case ATA_TYPE_ATAPI:
2547 printf("ata%d %s: ",channel,slave?" slave":"master");
2548 i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
2549 if(read_byte(ebda_seg,&EbdaData->ata.devices[device].device)==ATA_DEVICE_CDROM)
2550 printf(" ATAPI-%d CD-Rom/DVD-Rom\n",version);
2551 else
2552 printf(" ATAPI-%d Device\n",version);
2553 break;
2554 case ATA_TYPE_UNKNOWN:
2555 printf("ata%d %s: Unknown device\n",channel,slave?" slave":"master");
2556 break;
2561 // Store the devices counts
2562 write_byte(ebda_seg,&EbdaData->ata.hdcount, hdcount);
2563 write_byte(ebda_seg,&EbdaData->ata.cdcount, cdcount);
2564 write_byte(0x40,0x75, hdcount);
2566 printf("\n");
2568 // FIXME : should use bios=cmos|auto|disable bits
2569 // FIXME : should know about translation bits
2570 // FIXME : move hard_drive_post here
2574 // ---------------------------------------------------------------------------
2575 // ATA/ATAPI driver : software reset
2576 // ---------------------------------------------------------------------------
2577 // ATA-3
2578 // 8.2.1 Software reset - Device 0
2580 void ata_reset(device)
2581 Bit16u device;
2583 Bit16u ebda_seg=read_word(0x0040,0x000E);
2584 Bit16u iobase1, iobase2;
2585 Bit8u channel, slave, sn, sc;
2586 Bit16u max;
2588 channel = device / 2;
2589 slave = device % 2;
2591 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2592 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2594 // Reset
2596 // 8.2.1 (a) -- set SRST in DC
2597 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST);
2599 // 8.2.1 (b) -- wait for BSY
2600 max=0xff;
2601 while(--max>0) {
2602 Bit8u status = inb(iobase1+ATA_CB_STAT);
2603 if ((status & ATA_CB_STAT_BSY) != 0) break;
2606 // 8.2.1 (f) -- clear SRST
2607 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2609 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_NONE) {
2611 // 8.2.1 (g) -- check for sc==sn==0x01
2612 // select device
2613 outb(iobase1+ATA_CB_DH, slave?ATA_CB_DH_DEV1:ATA_CB_DH_DEV0);
2614 sc = inb(iobase1+ATA_CB_SC);
2615 sn = inb(iobase1+ATA_CB_SN);
2617 if ( (sc==0x01) && (sn==0x01) ) {
2619 // 8.2.1 (h) -- wait for not BSY
2620 max=0xff;
2621 while(--max>0) {
2622 Bit8u status = inb(iobase1+ATA_CB_STAT);
2623 if ((status & ATA_CB_STAT_BSY) == 0) break;
2628 // 8.2.1 (i) -- wait for DRDY
2629 max=0xfff;
2630 while(--max>0) {
2631 Bit8u status = inb(iobase1+ATA_CB_STAT);
2632 if ((status & ATA_CB_STAT_RDY) != 0) break;
2635 // Enable interrupts
2636 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2639 // ---------------------------------------------------------------------------
2640 // ATA/ATAPI driver : execute a non data command
2641 // ---------------------------------------------------------------------------
2643 Bit16u ata_cmd_non_data()
2644 {return 0;}
2646 // ---------------------------------------------------------------------------
2647 // ATA/ATAPI driver : execute a data-in command
2648 // ---------------------------------------------------------------------------
2649 // returns
2650 // 0 : no error
2651 // 1 : BUSY bit set
2652 // 2 : read error
2653 // 3 : expected DRQ=1
2654 // 4 : no sectors left to read/verify
2655 // 5 : more sectors to read/verify
2656 // 6 : no sectors left to write
2657 // 7 : more sectors to write
2658 Bit16u ata_cmd_data_in(device, command, count, cylinder, head, sector, lba, segment, offset)
2659 Bit16u device, command, count, cylinder, head, sector, segment, offset;
2660 Bit32u lba;
2662 Bit16u ebda_seg=read_word(0x0040,0x000E);
2663 Bit16u iobase1, iobase2, blksize;
2664 Bit8u channel, slave;
2665 Bit8u status, current, mode;
2667 channel = device / 2;
2668 slave = device % 2;
2670 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2671 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2672 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2673 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
2674 if (mode == ATA_MODE_PIO32) blksize>>=2;
2675 else blksize>>=1;
2677 // Reset count of transferred data
2678 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2679 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2680 current = 0;
2682 status = inb(iobase1 + ATA_CB_STAT);
2683 if (status & ATA_CB_STAT_BSY) return 1;
2685 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2687 // sector will be 0 only on lba access. Convert to lba-chs
2688 if (sector == 0) {
2689 if ((count >= 1 << 8) || (lba + count >= 1UL << 28)) {
2690 outb(iobase1 + ATA_CB_FR, 0x00);
2691 outb(iobase1 + ATA_CB_SC, (count >> 8) & 0xff);
2692 outb(iobase1 + ATA_CB_SN, lba >> 24);
2693 outb(iobase1 + ATA_CB_CL, 0);
2694 outb(iobase1 + ATA_CB_CH, 0);
2695 command |= 0x04;
2696 count &= (1UL << 8) - 1;
2697 lba &= (1UL << 24) - 1;
2699 sector = (Bit16u) (lba & 0x000000ffL);
2700 lba >>= 8;
2701 cylinder = (Bit16u) (lba & 0x0000ffffL);
2702 lba >>= 16;
2703 head = ((Bit16u) (lba & 0x0000000fL)) | 0x40;
2706 outb(iobase1 + ATA_CB_FR, 0x00);
2707 outb(iobase1 + ATA_CB_SC, count);
2708 outb(iobase1 + ATA_CB_SN, sector);
2709 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
2710 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
2711 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
2712 outb(iobase1 + ATA_CB_CMD, command);
2714 while (1) {
2715 status = inb(iobase1 + ATA_CB_STAT);
2716 if ( !(status & ATA_CB_STAT_BSY) ) break;
2719 if (status & ATA_CB_STAT_ERR) {
2720 BX_DEBUG_ATA("ata_cmd_data_in : read error\n");
2721 return 2;
2722 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2723 BX_DEBUG_ATA("ata_cmd_data_in : DRQ not set (status %02x)\n", (unsigned) status);
2724 return 3;
2727 // FIXME : move seg/off translation here
2729 ASM_START
2730 sti ;; enable higher priority interrupts
2731 ASM_END
2733 while (1) {
2735 ASM_START
2736 push bp
2737 mov bp, sp
2738 mov di, _ata_cmd_data_in.offset + 2[bp]
2739 mov ax, _ata_cmd_data_in.segment + 2[bp]
2740 mov cx, _ata_cmd_data_in.blksize + 2[bp]
2742 ;; adjust if there will be an overrun. 2K max sector size
2743 cmp di, #0xf800 ;;
2744 jbe ata_in_no_adjust
2746 ata_in_adjust:
2747 sub di, #0x0800 ;; sub 2 kbytes from offset
2748 add ax, #0x0080 ;; add 2 Kbytes to segment
2750 ata_in_no_adjust:
2751 mov es, ax ;; segment in es
2753 mov dx, _ata_cmd_data_in.iobase1 + 2[bp] ;; ATA data read port
2755 mov ah, _ata_cmd_data_in.mode + 2[bp]
2756 cmp ah, #ATA_MODE_PIO32
2757 je ata_in_32
2759 ata_in_16:
2760 rep
2761 insw ;; CX words transfered from port(DX) to ES:[DI]
2762 jmp ata_in_done
2764 ata_in_32:
2765 rep
2766 insd ;; CX dwords transfered from port(DX) to ES:[DI]
2768 ata_in_done:
2769 mov _ata_cmd_data_in.offset + 2[bp], di
2770 mov _ata_cmd_data_in.segment + 2[bp], es
2771 pop bp
2772 ASM_END
2774 current++;
2775 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
2776 count--;
2777 status = inb(iobase1 + ATA_CB_STAT);
2778 if (count == 0) {
2779 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2780 != ATA_CB_STAT_RDY ) {
2781 BX_DEBUG_ATA("ata_cmd_data_in : no sectors left (status %02x)\n", (unsigned) status);
2782 return 4;
2784 break;
2786 else {
2787 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2788 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2789 BX_DEBUG_ATA("ata_cmd_data_in : more sectors left (status %02x)\n", (unsigned) status);
2790 return 5;
2792 continue;
2795 // Enable interrupts
2796 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2797 return 0;
2800 // ---------------------------------------------------------------------------
2801 // ATA/ATAPI driver : execute a data-out command
2802 // ---------------------------------------------------------------------------
2803 // returns
2804 // 0 : no error
2805 // 1 : BUSY bit set
2806 // 2 : read error
2807 // 3 : expected DRQ=1
2808 // 4 : no sectors left to read/verify
2809 // 5 : more sectors to read/verify
2810 // 6 : no sectors left to write
2811 // 7 : more sectors to write
2812 Bit16u ata_cmd_data_out(device, command, count, cylinder, head, sector, lba, segment, offset)
2813 Bit16u device, command, count, cylinder, head, sector, segment, offset;
2814 Bit32u lba;
2816 Bit16u ebda_seg=read_word(0x0040,0x000E);
2817 Bit16u iobase1, iobase2, blksize;
2818 Bit8u channel, slave;
2819 Bit8u status, current, mode;
2821 channel = device / 2;
2822 slave = device % 2;
2824 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2825 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2826 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2827 blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
2828 if (mode == ATA_MODE_PIO32) blksize>>=2;
2829 else blksize>>=1;
2831 // Reset count of transferred data
2832 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
2833 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
2834 current = 0;
2836 status = inb(iobase1 + ATA_CB_STAT);
2837 if (status & ATA_CB_STAT_BSY) return 1;
2839 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
2841 // sector will be 0 only on lba access. Convert to lba-chs
2842 if (sector == 0) {
2843 if ((count >= 1 << 8) || (lba + count >= 1UL << 28)) {
2844 outb(iobase1 + ATA_CB_FR, 0x00);
2845 outb(iobase1 + ATA_CB_SC, (count >> 8) & 0xff);
2846 outb(iobase1 + ATA_CB_SN, lba >> 24);
2847 outb(iobase1 + ATA_CB_CL, 0);
2848 outb(iobase1 + ATA_CB_CH, 0);
2849 command |= 0x04;
2850 count &= (1UL << 8) - 1;
2851 lba &= (1UL << 24) - 1;
2853 sector = (Bit16u) (lba & 0x000000ffL);
2854 lba >>= 8;
2855 cylinder = (Bit16u) (lba & 0x0000ffffL);
2856 lba >>= 16;
2857 head = ((Bit16u) (lba & 0x0000000fL)) | 0x40;
2860 outb(iobase1 + ATA_CB_FR, 0x00);
2861 outb(iobase1 + ATA_CB_SC, count);
2862 outb(iobase1 + ATA_CB_SN, sector);
2863 outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
2864 outb(iobase1 + ATA_CB_CH, cylinder >> 8);
2865 outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
2866 outb(iobase1 + ATA_CB_CMD, command);
2868 while (1) {
2869 status = inb(iobase1 + ATA_CB_STAT);
2870 if ( !(status & ATA_CB_STAT_BSY) ) break;
2873 if (status & ATA_CB_STAT_ERR) {
2874 BX_DEBUG_ATA("ata_cmd_data_out : read error\n");
2875 return 2;
2876 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
2877 BX_DEBUG_ATA("ata_cmd_data_out : DRQ not set (status %02x)\n", (unsigned) status);
2878 return 3;
2881 // FIXME : move seg/off translation here
2883 ASM_START
2884 sti ;; enable higher priority interrupts
2885 ASM_END
2887 while (1) {
2889 ASM_START
2890 push bp
2891 mov bp, sp
2892 mov si, _ata_cmd_data_out.offset + 2[bp]
2893 mov ax, _ata_cmd_data_out.segment + 2[bp]
2894 mov cx, _ata_cmd_data_out.blksize + 2[bp]
2896 ;; adjust if there will be an overrun. 2K max sector size
2897 cmp si, #0xf800 ;;
2898 jbe ata_out_no_adjust
2900 ata_out_adjust:
2901 sub si, #0x0800 ;; sub 2 kbytes from offset
2902 add ax, #0x0080 ;; add 2 Kbytes to segment
2904 ata_out_no_adjust:
2905 mov es, ax ;; segment in es
2907 mov dx, _ata_cmd_data_out.iobase1 + 2[bp] ;; ATA data write port
2909 mov ah, _ata_cmd_data_out.mode + 2[bp]
2910 cmp ah, #ATA_MODE_PIO32
2911 je ata_out_32
2913 ata_out_16:
2914 seg ES
2915 rep
2916 outsw ;; CX words transfered from port(DX) to ES:[SI]
2917 jmp ata_out_done
2919 ata_out_32:
2920 seg ES
2921 rep
2922 outsd ;; CX dwords transfered from port(DX) to ES:[SI]
2924 ata_out_done:
2925 mov _ata_cmd_data_out.offset + 2[bp], si
2926 mov _ata_cmd_data_out.segment + 2[bp], es
2927 pop bp
2928 ASM_END
2930 current++;
2931 write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
2932 count--;
2933 status = inb(iobase1 + ATA_CB_STAT);
2934 if (count == 0) {
2935 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2936 != ATA_CB_STAT_RDY ) {
2937 BX_DEBUG_ATA("ata_cmd_data_out : no sectors left (status %02x)\n", (unsigned) status);
2938 return 6;
2940 break;
2942 else {
2943 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
2944 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
2945 BX_DEBUG_ATA("ata_cmd_data_out : more sectors left (status %02x)\n", (unsigned) status);
2946 return 7;
2948 continue;
2951 // Enable interrupts
2952 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
2953 return 0;
2956 // ---------------------------------------------------------------------------
2957 // ATA/ATAPI driver : execute a packet command
2958 // ---------------------------------------------------------------------------
2959 // returns
2960 // 0 : no error
2961 // 1 : error in parameters
2962 // 2 : BUSY bit set
2963 // 3 : error
2964 // 4 : not ready
2965 Bit16u ata_cmd_packet(device, cmdlen, cmdseg, cmdoff, header, length, inout, bufseg, bufoff)
2966 Bit8u cmdlen,inout;
2967 Bit16u device,cmdseg, cmdoff, bufseg, bufoff;
2968 Bit16u header;
2969 Bit32u length;
2971 Bit16u ebda_seg=read_word(0x0040,0x000E);
2972 Bit16u iobase1, iobase2;
2973 Bit16u lcount, lbefore, lafter, count;
2974 Bit8u channel, slave;
2975 Bit8u status, mode, lmode;
2976 Bit32u total, transfer;
2978 channel = device / 2;
2979 slave = device % 2;
2981 // Data out is not supported yet
2982 if (inout == ATA_DATA_OUT) {
2983 BX_INFO("ata_cmd_packet: DATA_OUT not supported yet\n");
2984 return 1;
2987 // The header length must be even
2988 if (header & 1) {
2989 BX_DEBUG_ATA("ata_cmd_packet : header must be even (%04x)\n",header);
2990 return 1;
2993 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
2994 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
2995 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
2996 transfer= 0L;
2998 if (cmdlen < 12) cmdlen=12;
2999 if (cmdlen > 12) cmdlen=16;
3000 cmdlen>>=1;
3002 // Reset count of transferred data
3003 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
3004 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
3006 status = inb(iobase1 + ATA_CB_STAT);
3007 if (status & ATA_CB_STAT_BSY) return 2;
3009 outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
3010 // outb(iobase1 + ATA_CB_FR, 0x00);
3011 // outb(iobase1 + ATA_CB_SC, 0x00);
3012 // outb(iobase1 + ATA_CB_SN, 0x00);
3013 outb(iobase1 + ATA_CB_CL, 0xfff0 & 0x00ff);
3014 outb(iobase1 + ATA_CB_CH, 0xfff0 >> 8);
3015 outb(iobase1 + ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
3016 outb(iobase1 + ATA_CB_CMD, ATA_CMD_PACKET);
3018 // Device should ok to receive command
3019 while (1) {
3020 status = inb(iobase1 + ATA_CB_STAT);
3021 if ( !(status & ATA_CB_STAT_BSY) ) break;
3024 if (status & ATA_CB_STAT_ERR) {
3025 BX_DEBUG_ATA("ata_cmd_packet : error, status is %02x\n",status);
3026 return 3;
3027 } else if ( !(status & ATA_CB_STAT_DRQ) ) {
3028 BX_DEBUG_ATA("ata_cmd_packet : DRQ not set (status %02x)\n", (unsigned) status);
3029 return 4;
3032 // Normalize address
3033 cmdseg += (cmdoff / 16);
3034 cmdoff %= 16;
3036 // Send command to device
3037 ASM_START
3038 sti ;; enable higher priority interrupts
3040 push bp
3041 mov bp, sp
3043 mov si, _ata_cmd_packet.cmdoff + 2[bp]
3044 mov ax, _ata_cmd_packet.cmdseg + 2[bp]
3045 mov cx, _ata_cmd_packet.cmdlen + 2[bp]
3046 mov es, ax ;; segment in es
3048 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data write port
3050 seg ES
3051 rep
3052 outsw ;; CX words transfered from port(DX) to ES:[SI]
3054 pop bp
3055 ASM_END
3057 if (inout == ATA_DATA_NO) {
3058 status = inb(iobase1 + ATA_CB_STAT);
3060 else {
3061 while (1) {
3063 status = inb(iobase1 + ATA_CB_STAT);
3065 // Check if command completed
3066 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ) ) ==0 ) break;
3068 if (status & ATA_CB_STAT_ERR) {
3069 BX_DEBUG_ATA("ata_cmd_packet : error (status %02x)\n",status);
3070 return 3;
3073 // Device must be ready to send data
3074 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
3075 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
3076 BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", status);
3077 return 4;
3080 // Normalize address
3081 bufseg += (bufoff / 16);
3082 bufoff %= 16;
3084 // Get the byte count
3085 lcount = ((Bit16u)(inb(iobase1 + ATA_CB_CH))<<8)+inb(iobase1 + ATA_CB_CL);
3087 // adjust to read what we want
3088 if(header>lcount) {
3089 lbefore=lcount;
3090 header-=lcount;
3091 lcount=0;
3093 else {
3094 lbefore=header;
3095 header=0;
3096 lcount-=lbefore;
3099 if(lcount>length) {
3100 lafter=lcount-length;
3101 lcount=length;
3102 length=0;
3104 else {
3105 lafter=0;
3106 length-=lcount;
3109 // Save byte count
3110 count = lcount;
3112 BX_DEBUG_ATA("Trying to read %04x bytes (%04x %04x %04x) ",lbefore+lcount+lafter,lbefore,lcount,lafter);
3113 BX_DEBUG_ATA("to 0x%04x:0x%04x\n",bufseg,bufoff);
3115 // If counts not dividable by 4, use 16bits mode
3116 lmode = mode;
3117 if (lbefore & 0x03) lmode=ATA_MODE_PIO16;
3118 if (lcount & 0x03) lmode=ATA_MODE_PIO16;
3119 if (lafter & 0x03) lmode=ATA_MODE_PIO16;
3121 // adds an extra byte if count are odd. before is always even
3122 if (lcount & 0x01) {
3123 lcount+=1;
3124 if ((lafter > 0) && (lafter & 0x01)) {
3125 lafter-=1;
3129 if (lmode == ATA_MODE_PIO32) {
3130 lcount>>=2; lbefore>>=2; lafter>>=2;
3132 else {
3133 lcount>>=1; lbefore>>=1; lafter>>=1;
3136 ; // FIXME bcc bug
3138 ASM_START
3139 push bp
3140 mov bp, sp
3142 mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data read port
3144 mov cx, _ata_cmd_packet.lbefore + 2[bp]
3145 jcxz ata_packet_no_before
3147 mov ah, _ata_cmd_packet.lmode + 2[bp]
3148 cmp ah, #ATA_MODE_PIO32
3149 je ata_packet_in_before_32
3151 ata_packet_in_before_16:
3152 in ax, dx
3153 loop ata_packet_in_before_16
3154 jmp ata_packet_no_before
3156 ata_packet_in_before_32:
3157 push eax
3158 ata_packet_in_before_32_loop:
3159 in eax, dx
3160 loop ata_packet_in_before_32_loop
3161 pop eax
3163 ata_packet_no_before:
3164 mov cx, _ata_cmd_packet.lcount + 2[bp]
3165 jcxz ata_packet_after
3167 mov di, _ata_cmd_packet.bufoff + 2[bp]
3168 mov ax, _ata_cmd_packet.bufseg + 2[bp]
3169 mov es, ax
3171 mov ah, _ata_cmd_packet.lmode + 2[bp]
3172 cmp ah, #ATA_MODE_PIO32
3173 je ata_packet_in_32
3175 ata_packet_in_16:
3176 rep
3177 insw ;; CX words transfered tp port(DX) to ES:[DI]
3178 jmp ata_packet_after
3180 ata_packet_in_32:
3181 rep
3182 insd ;; CX dwords transfered to port(DX) to ES:[DI]
3184 ata_packet_after:
3185 mov cx, _ata_cmd_packet.lafter + 2[bp]
3186 jcxz ata_packet_done
3188 mov ah, _ata_cmd_packet.lmode + 2[bp]
3189 cmp ah, #ATA_MODE_PIO32
3190 je ata_packet_in_after_32
3192 ata_packet_in_after_16:
3193 in ax, dx
3194 loop ata_packet_in_after_16
3195 jmp ata_packet_done
3197 ata_packet_in_after_32:
3198 push eax
3199 ata_packet_in_after_32_loop:
3200 in eax, dx
3201 loop ata_packet_in_after_32_loop
3202 pop eax
3204 ata_packet_done:
3205 pop bp
3206 ASM_END
3208 // Compute new buffer address
3209 bufoff += count;
3211 // Save transferred bytes count
3212 transfer += count;
3213 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,transfer);
3217 // Final check, device must be ready
3218 if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
3219 != ATA_CB_STAT_RDY ) {
3220 BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", (unsigned) status);
3221 return 4;
3224 // Enable interrupts
3225 outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
3226 return 0;
3229 // ---------------------------------------------------------------------------
3230 // End of ATA/ATAPI Driver
3231 // ---------------------------------------------------------------------------
3233 // ---------------------------------------------------------------------------
3234 // Start of ATA/ATAPI generic functions
3235 // ---------------------------------------------------------------------------
3237 Bit16u
3238 atapi_get_sense(device)
3239 Bit16u device;
3241 Bit8u atacmd[12];
3242 Bit8u buffer[16];
3243 Bit8u i;
3245 memsetb(get_SS(),atacmd,0,12);
3247 // Request SENSE
3248 atacmd[0]=0x03;
3249 atacmd[4]=0x20;
3250 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 16L, ATA_DATA_IN, get_SS(), buffer) != 0)
3251 return 0x0002;
3253 if ((buffer[0] & 0x7e) == 0x70) {
3254 return (((Bit16u)buffer[2]&0x0f)*0x100)+buffer[12];
3257 return 0;
3260 Bit16u
3261 atapi_is_ready(device)
3262 Bit16u device;
3264 Bit8u atacmd[12];
3265 Bit8u buffer[];
3267 memsetb(get_SS(),atacmd,0,12);
3269 // Test Unit Ready
3270 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0)
3271 return 0x000f;
3273 if (atapi_get_sense(device) !=0 ) {
3274 memsetb(get_SS(),atacmd,0,12);
3276 // try to send Test Unit Ready again
3277 if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0)
3278 return 0x000f;
3280 return atapi_get_sense(device);
3282 return 0;
3285 Bit16u
3286 atapi_is_cdrom(device)
3287 Bit8u device;
3289 Bit16u ebda_seg=read_word(0x0040,0x000E);
3291 if (device >= BX_MAX_ATA_DEVICES)
3292 return 0;
3294 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI)
3295 return 0;
3297 if (read_byte(ebda_seg,&EbdaData->ata.devices[device].device) != ATA_DEVICE_CDROM)
3298 return 0;
3300 return 1;
3303 // ---------------------------------------------------------------------------
3304 // End of ATA/ATAPI generic functions
3305 // ---------------------------------------------------------------------------
3307 #endif // BX_USE_ATADRV
3309 #if BX_ELTORITO_BOOT
3311 // ---------------------------------------------------------------------------
3312 // Start of El-Torito boot functions
3313 // ---------------------------------------------------------------------------
3315 void
3316 cdemu_init()
3318 Bit16u ebda_seg=read_word(0x0040,0x000E);
3320 // the only important data is this one for now
3321 write_byte(ebda_seg,&EbdaData->cdemu.active,0x00);
3324 Bit8u
3325 cdemu_isactive()
3327 Bit16u ebda_seg=read_word(0x0040,0x000E);
3329 return(read_byte(ebda_seg,&EbdaData->cdemu.active));
3332 Bit8u
3333 cdemu_emulated_drive()
3335 Bit16u ebda_seg=read_word(0x0040,0x000E);
3337 return(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
3340 static char isotag[6]="CD001";
3341 static char eltorito[24]="EL TORITO SPECIFICATION";
3342 //
3343 // Returns ah: emulated drive, al: error code
3344 //
3345 Bit16u
3346 cdrom_boot()
3348 Bit16u ebda_seg=read_word(0x0040,0x000E);
3349 Bit8u atacmd[12], buffer[2048];
3350 Bit32u lba;
3351 Bit16u boot_segment, nbsectors, i, error;
3352 Bit8u device;
3354 // Find out the first cdrom
3355 for (device=0; device<BX_MAX_ATA_DEVICES;device++) {
3356 if (atapi_is_cdrom(device)) break;
3359 // if not found
3360 if(device >= BX_MAX_ATA_DEVICES) return 2;
3362 // Read the Boot Record Volume Descriptor
3363 memsetb(get_SS(),atacmd,0,12);
3364 atacmd[0]=0x28; // READ command
3365 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3366 atacmd[8]=(0x01 & 0x00ff); // Sectors
3367 atacmd[2]=(0x11 & 0xff000000) >> 24; // LBA
3368 atacmd[3]=(0x11 & 0x00ff0000) >> 16;
3369 atacmd[4]=(0x11 & 0x0000ff00) >> 8;
3370 atacmd[5]=(0x11 & 0x000000ff);
3371 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3372 return 3;
3374 // Validity checks
3375 if(buffer[0]!=0)return 4;
3376 for(i=0;i<5;i++){
3377 if(buffer[1+i]!=read_byte(0xf000,&isotag[i]))return 5;
3379 for(i=0;i<23;i++)
3380 if(buffer[7+i]!=read_byte(0xf000,&eltorito[i]))return 6;
3382 // ok, now we calculate the Boot catalog address
3383 lba=buffer[0x4A]*0x1000000+buffer[0x49]*0x10000+buffer[0x48]*0x100+buffer[0x47];
3385 // And we read the Boot Catalog
3386 memsetb(get_SS(),atacmd,0,12);
3387 atacmd[0]=0x28; // READ command
3388 atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
3389 atacmd[8]=(0x01 & 0x00ff); // Sectors
3390 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3391 atacmd[3]=(lba & 0x00ff0000) >> 16;
3392 atacmd[4]=(lba & 0x0000ff00) >> 8;
3393 atacmd[5]=(lba & 0x000000ff);
3394 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
3395 return 7;
3397 // Validation entry
3398 if(buffer[0x00]!=0x01)return 8; // Header
3399 if(buffer[0x01]!=0x00)return 9; // Platform
3400 if(buffer[0x1E]!=0x55)return 10; // key 1
3401 if(buffer[0x1F]!=0xAA)return 10; // key 2
3403 // Initial/Default Entry
3404 if(buffer[0x20]!=0x88)return 11; // Bootable
3406 #if BX_TCGBIOS
3407 /* specs: 8.2.3 step 5 and 8.2.5.6, measure El Torito boot catalog */
3408 /* measure 2048 bytes (one sector) */
3409 tcpa_add_bootdevice((Bit32u)1L, (Bit32u)0L); /* bootcd = 1 */
3410 tcpa_ipl((Bit32u)2L,(Bit32u)get_SS(),(Bit32u)buffer,(Bit32u)2048L);
3411 #endif
3413 write_byte(ebda_seg,&EbdaData->cdemu.media,buffer[0x21]);
3414 if(buffer[0x21]==0){
3415 // FIXME ElTorito Hardcoded. cdrom is hardcoded as device 0xE0.
3416 // Win2000 cd boot needs to know it booted from cd
3417 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0xE0);
3419 else if(buffer[0x21]<4)
3420 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x00);
3421 else
3422 write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x80);
3424 write_byte(ebda_seg,&EbdaData->cdemu.controller_index,device/2);
3425 write_byte(ebda_seg,&EbdaData->cdemu.device_spec,device%2);
3427 boot_segment=buffer[0x23]*0x100+buffer[0x22];
3428 if(boot_segment==0x0000)boot_segment=0x07C0;
3430 write_word(ebda_seg,&EbdaData->cdemu.load_segment,boot_segment);
3431 write_word(ebda_seg,&EbdaData->cdemu.buffer_segment,0x0000);
3433 nbsectors=buffer[0x27]*0x100+buffer[0x26];
3434 write_word(ebda_seg,&EbdaData->cdemu.sector_count,nbsectors);
3436 lba=buffer[0x2B]*0x1000000+buffer[0x2A]*0x10000+buffer[0x29]*0x100+buffer[0x28];
3437 write_dword(ebda_seg,&EbdaData->cdemu.ilba,lba);
3439 // And we read the image in memory
3440 memsetb(get_SS(),atacmd,0,12);
3441 atacmd[0]=0x28; // READ command
3442 atacmd[7]=((1+(nbsectors-1)/4) & 0xff00) >> 8; // Sectors
3443 atacmd[8]=((1+(nbsectors-1)/4) & 0x00ff); // Sectors
3444 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
3445 atacmd[3]=(lba & 0x00ff0000) >> 16;
3446 atacmd[4]=(lba & 0x0000ff00) >> 8;
3447 atacmd[5]=(lba & 0x000000ff);
3448 if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, nbsectors*512L, ATA_DATA_IN, boot_segment,0)) != 0)
3449 return 12;
3451 #if BX_TCGBIOS
3452 /* specs: 8.2.3 step 4 and 8.2.5.6, measure El Torito boot image */
3453 /* measure 1st 512 bytes */
3454 tcpa_ipl((Bit32u)1L,(Bit32u)boot_segment,(Bit32u)0L,(Bit32u)512L);
3455 #endif
3458 // Remember the media type
3459 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
3460 case 0x01: // 1.2M floppy
3461 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,15);
3462 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3463 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3464 break;
3465 case 0x02: // 1.44M floppy
3466 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,18);
3467 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3468 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3469 break;
3470 case 0x03: // 2.88M floppy
3471 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,36);
3472 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
3473 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
3474 break;
3475 case 0x04: // Harddrive
3476 write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,read_byte(boot_segment,446+6)&0x3f);
3477 write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,
3478 (read_byte(boot_segment,446+6)<<2) + read_byte(boot_segment,446+7) + 1);
3479 write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,read_byte(boot_segment,446+5) + 1);
3480 break;
3483 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0) {
3484 // Increase bios installed hardware number of devices
3485 if(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)==0x00)
3486 write_byte(0x40,0x10,read_byte(0x40,0x10)|0x41);
3487 else
3488 write_byte(ebda_seg, &EbdaData->ata.hdcount, read_byte(ebda_seg, &EbdaData->ata.hdcount) + 1);
3492 // everything is ok, so from now on, the emulation is active
3493 if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0)
3494 write_byte(ebda_seg,&EbdaData->cdemu.active,0x01);
3496 // return the boot drive + no error
3497 return (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)*0x100)+0;
3500 // ---------------------------------------------------------------------------
3501 // End of El-Torito boot functions
3502 // ---------------------------------------------------------------------------
3503 #endif // BX_ELTORITO_BOOT
3505 void
3506 int14_function(regs, ds, iret_addr)
3507 pusha_regs_t regs; // regs pushed from PUSHA instruction
3508 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
3509 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
3511 Bit16u addr,timer,val16;
3512 Bit8u timeout;
3514 ASM_START
3515 sti
3516 ASM_END
3518 addr = read_word(0x0040, (regs.u.r16.dx << 1));
3519 timeout = read_byte(0x0040, 0x007C + regs.u.r16.dx);
3520 if ((regs.u.r16.dx < 4) && (addr > 0)) {
3521 switch (regs.u.r8.ah) {
3522 case 0:
3523 outb(addr+3, inb(addr+3) | 0x80);
3524 if (regs.u.r8.al & 0xE0 == 0) {
3525 outb(addr, 0x17);
3526 outb(addr+1, 0x04);
3527 } else {
3528 val16 = 0x600 >> ((regs.u.r8.al & 0xE0) >> 5);
3529 outb(addr, val16 & 0xFF);
3530 outb(addr+1, val16 >> 8);
3532 outb(addr+3, regs.u.r8.al & 0x1F);
3533 regs.u.r8.ah = inb(addr+5);
3534 regs.u.r8.al = inb(addr+6);
3535 ClearCF(iret_addr.flags);
3536 break;
3537 case 1:
3538 timer = read_word(0x0040, 0x006C);
3539 while (((inb(addr+5) & 0x60) != 0x60) && (timeout)) {
3540 val16 = read_word(0x0040, 0x006C);
3541 if (val16 != timer) {
3542 timer = val16;
3543 timeout--;
3546 if (timeout) outb(addr, regs.u.r8.al);
3547 regs.u.r8.ah = inb(addr+5);
3548 if (!timeout) regs.u.r8.ah |= 0x80;
3549 ClearCF(iret_addr.flags);
3550 break;
3551 case 2:
3552 timer = read_word(0x0040, 0x006C);
3553 while (((inb(addr+5) & 0x01) == 0) && (timeout)) {
3554 val16 = read_word(0x0040, 0x006C);
3555 if (val16 != timer) {
3556 timer = val16;
3557 timeout--;
3560 if (timeout) {
3561 regs.u.r8.ah = 0;
3562 regs.u.r8.al = inb(addr);
3563 } else {
3564 regs.u.r8.ah = inb(addr+5);
3566 ClearCF(iret_addr.flags);
3567 break;
3568 case 3:
3569 regs.u.r8.ah = inb(addr+5);
3570 regs.u.r8.al = inb(addr+6);
3571 ClearCF(iret_addr.flags);
3572 break;
3573 default:
3574 SetCF(iret_addr.flags); // Unsupported
3576 } else {
3577 SetCF(iret_addr.flags); // Unsupported
3581 void
3582 int15_function(regs, ES, DS, FLAGS)
3583 pusha_regs_t regs; // REGS pushed via pusha
3584 Bit16u ES, DS, FLAGS;
3586 Bit16u ebda_seg=read_word(0x0040,0x000E);
3587 bx_bool prev_a20_enable;
3588 Bit16u base15_00;
3589 Bit8u base23_16;
3590 Bit16u ss;
3591 Bit16u CX,DX;
3593 Bit16u bRegister;
3594 Bit8u irqDisable;
3596 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3598 switch (regs.u.r8.ah) {
3599 case 0x24: /* A20 Control */
3600 switch (regs.u.r8.al) {
3601 case 0x00:
3602 set_enable_a20(0);
3603 CLEAR_CF();
3604 regs.u.r8.ah = 0;
3605 break;
3606 case 0x01:
3607 set_enable_a20(1);
3608 CLEAR_CF();
3609 regs.u.r8.ah = 0;
3610 break;
3611 case 0x02:
3612 regs.u.r8.al = (inb(0x92) >> 1) & 0x01;
3613 CLEAR_CF();
3614 regs.u.r8.ah = 0;
3615 break;
3616 case 0x03:
3617 CLEAR_CF();
3618 regs.u.r8.ah = 0;
3619 regs.u.r16.bx = 3;
3620 break;
3621 default:
3622 BX_INFO("int15: Func 24h, subfunc %02xh, A20 gate control not supported\n", (unsigned) regs.u.r8.al);
3623 SET_CF();
3624 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3626 break;
3628 case 0x41:
3629 SET_CF();
3630 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3631 break;
3633 case 0x4f:
3634 /* keyboard intercept */
3635 #if BX_CPU < 2
3636 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3637 #else
3638 // nop
3639 #endif
3640 SET_CF();
3641 break;
3643 case 0x52: // removable media eject
3644 CLEAR_CF();
3645 regs.u.r8.ah = 0; // "ok ejection may proceed"
3646 break;
3648 case 0x83: {
3649 if( regs.u.r8.al == 0 ) {
3650 // Set Interval requested.
3651 if( ( read_byte( 0x40, 0xA0 ) & 1 ) == 0 ) {
3652 // Interval not already set.
3653 write_byte( 0x40, 0xA0, 1 ); // Set status byte.
3654 write_word( 0x40, 0x98, ES ); // Byte location, segment
3655 write_word( 0x40, 0x9A, regs.u.r16.bx ); // Byte location, offset
3656 write_word( 0x40, 0x9C, regs.u.r16.dx ); // Low word, delay
3657 write_word( 0x40, 0x9E, regs.u.r16.cx ); // High word, delay.
3658 CLEAR_CF( );
3659 irqDisable = inb( 0xA1 );
3660 outb( 0xA1, irqDisable & 0xFE );
3661 bRegister = inb_cmos( 0xB ); // Unmask IRQ8 so INT70 will get through.
3662 outb_cmos( 0xB, bRegister | 0x40 ); // Turn on the Periodic Interrupt timer
3663 } else {
3664 // Interval already set.
3665 BX_DEBUG_INT15("int15: Func 83h, failed, already waiting.\n" );
3666 SET_CF();
3667 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3669 } else if( regs.u.r8.al == 1 ) {
3670 // Clear Interval requested
3671 write_byte( 0x40, 0xA0, 0 ); // Clear status byte
3672 CLEAR_CF( );
3673 bRegister = inb_cmos( 0xB );
3674 outb_cmos( 0xB, bRegister & ~0x40 ); // Turn off the Periodic Interrupt timer
3675 } else {
3676 BX_DEBUG_INT15("int15: Func 83h, failed.\n" );
3677 SET_CF();
3678 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3679 regs.u.r8.al--;
3682 break;
3685 case 0x87:
3686 #if BX_CPU < 3
3687 # error "Int15 function 87h not supported on < 80386"
3688 #endif
3689 // +++ should probably have descriptor checks
3690 // +++ should have exception handlers
3692 // turn off interrupts
3693 ASM_START
3694 cli
3695 ASM_END
3697 prev_a20_enable = set_enable_a20(1); // enable A20 line
3699 // 128K max of transfer on 386+ ???
3700 // source == destination ???
3702 // ES:SI points to descriptor table
3703 // offset use initially comments
3704 // ==============================================
3705 // 00..07 Unused zeros Null descriptor
3706 // 08..0f GDT zeros filled in by BIOS
3707 // 10..17 source ssssssss source of data
3708 // 18..1f dest dddddddd destination of data
3709 // 20..27 CS zeros filled in by BIOS
3710 // 28..2f SS zeros filled in by BIOS
3712 //es:si
3713 //eeee0
3714 //0ssss
3715 //-----
3717 // check for access rights of source & dest here
3719 // Initialize GDT descriptor
3720 base15_00 = (ES << 4) + regs.u.r16.si;
3721 base23_16 = ES >> 12;
3722 if (base15_00 < (ES<<4))
3723 base23_16++;
3724 write_word(ES, regs.u.r16.si+0x08+0, 47); // limit 15:00 = 6 * 8bytes/descriptor
3725 write_word(ES, regs.u.r16.si+0x08+2, base15_00);// base 15:00
3726 write_byte(ES, regs.u.r16.si+0x08+4, base23_16);// base 23:16
3727 write_byte(ES, regs.u.r16.si+0x08+5, 0x93); // access
3728 write_word(ES, regs.u.r16.si+0x08+6, 0x0000); // base 31:24/reserved/limit 19:16
3730 // Initialize CS descriptor
3731 write_word(ES, regs.u.r16.si+0x20+0, 0xffff);// limit 15:00 = normal 64K limit
3732 write_word(ES, regs.u.r16.si+0x20+2, 0x0000);// base 15:00
3733 write_byte(ES, regs.u.r16.si+0x20+4, 0x000f);// base 23:16
3734 write_byte(ES, regs.u.r16.si+0x20+5, 0x9b); // access
3735 write_word(ES, regs.u.r16.si+0x20+6, 0x0000);// base 31:24/reserved/limit 19:16
3737 // Initialize SS descriptor
3738 ss = get_SS();
3739 base15_00 = ss << 4;
3740 base23_16 = ss >> 12;
3741 write_word(ES, regs.u.r16.si+0x28+0, 0xffff); // limit 15:00 = normal 64K limit
3742 write_word(ES, regs.u.r16.si+0x28+2, base15_00);// base 15:00
3743 write_byte(ES, regs.u.r16.si+0x28+4, base23_16);// base 23:16
3744 write_byte(ES, regs.u.r16.si+0x28+5, 0x93); // access
3745 write_word(ES, regs.u.r16.si+0x28+6, 0x0000); // base 31:24/reserved/limit 19:16
3747 CX = regs.u.r16.cx;
3748 ASM_START
3749 // Compile generates locals offset info relative to SP.
3750 // Get CX (word count) from stack.
3751 mov bx, sp
3752 SEG SS
3753 mov cx, _int15_function.CX [bx]
3755 // since we need to set SS:SP, save them to the BDA
3756 // for future restore
3757 push eax
3758 xor eax, eax
3759 mov ds, ax
3760 mov 0x0469, ss
3761 mov 0x0467, sp
3763 SEG ES
3764 lgdt [si + 0x08]
3765 SEG CS
3766 lidt [pmode_IDT_info]
3767 ;; perhaps do something with IDT here
3769 ;; set PE bit in CR0
3770 mov eax, cr0
3771 or al, #0x01
3772 mov cr0, eax
3773 ;; far jump to flush CPU queue after transition to protected mode
3774 JMP_AP(0x0020, protected_mode)
3776 protected_mode:
3777 ;; GDT points to valid descriptor table, now load SS, DS, ES
3778 mov ax, #0x28 ;; 101 000 = 5th descriptor in table, TI=GDT, RPL=00
3779 mov ss, ax
3780 mov ax, #0x10 ;; 010 000 = 2nd descriptor in table, TI=GDT, RPL=00
3781 mov ds, ax
3782 mov ax, #0x18 ;; 011 000 = 3rd descriptor in table, TI=GDT, RPL=00
3783 mov es, ax
3784 xor si, si
3785 xor di, di
3786 cld
3787 rep
3788 movsw ;; move CX words from DS:SI to ES:DI
3790 ;; make sure DS and ES limits are 64KB
3791 mov ax, #0x28
3792 mov ds, ax
3793 mov es, ax
3795 ;; reset PG bit in CR0 ???
3796 mov eax, cr0
3797 and al, #0xFE
3798 mov cr0, eax
3800 ;; far jump to flush CPU queue after transition to real mode
3801 JMP_AP(0xf000, real_mode)
3803 real_mode:
3804 ;; restore IDT to normal real-mode defaults
3805 SEG CS
3806 lidt [rmode_IDT_info]
3808 // restore SS:SP from the BDA
3809 xor ax, ax
3810 mov ds, ax
3811 mov ss, 0x0469
3812 mov sp, 0x0467
3813 pop eax
3814 ASM_END
3816 set_enable_a20(prev_a20_enable);
3818 // turn back on interrupts
3819 ASM_START
3820 sti
3821 ASM_END
3823 regs.u.r8.ah = 0;
3824 CLEAR_CF();
3825 break;
3828 case 0x88:
3829 // Get the amount of extended memory (above 1M)
3830 #if BX_CPU < 2
3831 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3832 SET_CF();
3833 #else
3834 regs.u.r8.al = inb_cmos(0x30);
3835 regs.u.r8.ah = inb_cmos(0x31);
3837 // limit to 15M
3838 if(regs.u.r16.ax > 0x3c00)
3839 regs.u.r16.ax = 0x3c00;
3841 CLEAR_CF();
3842 #endif
3843 break;
3845 case 0x90:
3846 /* Device busy interrupt. Called by Int 16h when no key available */
3847 break;
3849 case 0x91:
3850 /* Interrupt complete. Called by Int 16h when key becomes available */
3851 break;
3853 case 0xbf:
3854 BX_INFO("*** int 15h function AH=bf not yet supported!\n");
3855 SET_CF();
3856 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3857 break;
3859 case 0xC0:
3860 #if 0
3861 SET_CF();
3862 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3863 break;
3864 #endif
3865 CLEAR_CF();
3866 regs.u.r8.ah = 0;
3867 regs.u.r16.bx = BIOS_CONFIG_TABLE;
3868 ES = 0xF000;
3869 break;
3871 case 0xc1:
3872 ES = ebda_seg;
3873 CLEAR_CF();
3874 break;
3876 case 0xd8:
3877 bios_printf(BIOS_PRINTF_DEBUG, "EISA BIOS not present\n");
3878 SET_CF();
3879 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3880 break;
3882 default:
3883 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
3884 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
3885 SET_CF();
3886 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
3887 break;
3891 #if BX_USE_PS2_MOUSE
3892 void
3893 int15_function_mouse(regs, ES, DS, FLAGS)
3894 pusha_regs_t regs; // REGS pushed via pusha
3895 Bit16u ES, DS, FLAGS;
3897 Bit16u ebda_seg=read_word(0x0040,0x000E);
3898 Bit8u mouse_flags_1, mouse_flags_2;
3899 Bit16u mouse_driver_seg;
3900 Bit16u mouse_driver_offset;
3901 Bit8u comm_byte, prev_command_byte;
3902 Bit8u ret, mouse_data1, mouse_data2, mouse_data3;
3904 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
3906 switch (regs.u.r8.ah) {
3907 case 0xC2:
3908 // Return Codes status in AH
3909 // =========================
3910 // 00: success
3911 // 01: invalid subfunction (AL > 7)
3912 // 02: invalid input value (out of allowable range)
3913 // 03: interface error
3914 // 04: resend command received from mouse controller,
3915 // device driver should attempt command again
3916 // 05: cannot enable mouse, since no far call has been installed
3917 // 80/86: mouse service not implemented
3919 switch (regs.u.r8.al) {
3920 case 0: // Disable/Enable Mouse
3921 BX_DEBUG_INT15("case 0:\n");
3922 switch (regs.u.r8.bh) {
3923 case 0: // Disable Mouse
3924 BX_DEBUG_INT15("case 0: disable mouse\n");
3925 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3926 ret = send_to_mouse_ctrl(0xF5); // disable mouse command
3927 if (ret == 0) {
3928 ret = get_mouse_data(&mouse_data1);
3929 if ( (ret == 0) || (mouse_data1 == 0xFA) ) {
3930 CLEAR_CF();
3931 regs.u.r8.ah = 0;
3932 return;
3936 // error
3937 SET_CF();
3938 regs.u.r8.ah = ret;
3939 return;
3940 break;
3942 case 1: // Enable Mouse
3943 BX_DEBUG_INT15("case 1: enable mouse\n");
3944 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
3945 if ( (mouse_flags_2 & 0x80) == 0 ) {
3946 BX_DEBUG_INT15("INT 15h C2 Enable Mouse, no far call handler\n");
3947 SET_CF(); // error
3948 regs.u.r8.ah = 5; // no far call installed
3949 return;
3951 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3952 ret = send_to_mouse_ctrl(0xF4); // enable mouse command
3953 if (ret == 0) {
3954 ret = get_mouse_data(&mouse_data1);
3955 if ( (ret == 0) && (mouse_data1 == 0xFA) ) {
3956 enable_mouse_int_and_events(); // turn IRQ12 and packet generation on
3957 CLEAR_CF();
3958 regs.u.r8.ah = 0;
3959 return;
3962 SET_CF();
3963 regs.u.r8.ah = ret;
3964 return;
3966 default: // invalid subfunction
3967 BX_DEBUG_INT15("INT 15h C2 AL=0, BH=%02x\n", (unsigned) regs.u.r8.bh);
3968 SET_CF(); // error
3969 regs.u.r8.ah = 1; // invalid subfunction
3970 return;
3972 break;
3974 case 1: // Reset Mouse
3975 case 5: // Initialize Mouse
3976 BX_DEBUG_INT15("case 1 or 5:\n");
3977 if (regs.u.r8.al == 5) {
3978 if (regs.u.r8.bh != 3) {
3979 SET_CF();
3980 regs.u.r8.ah = 0x02; // invalid input
3981 return;
3983 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
3984 mouse_flags_2 = (mouse_flags_2 & 0x00) | regs.u.r8.bh;
3985 mouse_flags_1 = 0x00;
3986 write_byte(ebda_seg, 0x0026, mouse_flags_1);
3987 write_byte(ebda_seg, 0x0027, mouse_flags_2);
3990 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
3991 ret = send_to_mouse_ctrl(0xFF); // reset mouse command
3992 if (ret == 0) {
3993 ret = get_mouse_data(&mouse_data3);
3994 // if no mouse attached, it will return RESEND
3995 if (mouse_data3 == 0xfe) {
3996 SET_CF();
3997 return;
3999 if (mouse_data3 != 0xfa)
4000 BX_PANIC("Mouse reset returned %02x (should be ack)\n", (unsigned)mouse_data3);
4001 if ( ret == 0 ) {
4002 ret = get_mouse_data(&mouse_data1);
4003 if ( ret == 0 ) {
4004 ret = get_mouse_data(&mouse_data2);
4005 if ( ret == 0 ) {
4006 // turn IRQ12 and packet generation on
4007 enable_mouse_int_and_events();
4008 CLEAR_CF();
4009 regs.u.r8.ah = 0;
4010 regs.u.r8.bl = mouse_data1;
4011 regs.u.r8.bh = mouse_data2;
4012 return;
4018 // error
4019 SET_CF();
4020 regs.u.r8.ah = ret;
4021 return;
4023 case 2: // Set Sample Rate
4024 BX_DEBUG_INT15("case 2:\n");
4025 switch (regs.u.r8.bh) {
4026 case 0: mouse_data1 = 10; break; // 10 reports/sec
4027 case 1: mouse_data1 = 20; break; // 20 reports/sec
4028 case 2: mouse_data1 = 40; break; // 40 reports/sec
4029 case 3: mouse_data1 = 60; break; // 60 reports/sec
4030 case 4: mouse_data1 = 80; break; // 80 reports/sec
4031 case 5: mouse_data1 = 100; break; // 100 reports/sec (default)
4032 case 6: mouse_data1 = 200; break; // 200 reports/sec
4033 default: mouse_data1 = 0;
4035 if (mouse_data1 > 0) {
4036 ret = send_to_mouse_ctrl(0xF3); // set sample rate command
4037 if (ret == 0) {
4038 ret = get_mouse_data(&mouse_data2);
4039 ret = send_to_mouse_ctrl(mouse_data1);
4040 ret = get_mouse_data(&mouse_data2);
4041 CLEAR_CF();
4042 regs.u.r8.ah = 0;
4043 } else {
4044 // error
4045 SET_CF();
4046 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4048 } else {
4049 // error
4050 SET_CF();
4051 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4053 break;
4055 case 3: // Set Resolution
4056 BX_DEBUG_INT15("case 3:\n");
4057 // BX:
4058 // 0 = 25 dpi, 1 count per millimeter
4059 // 1 = 50 dpi, 2 counts per millimeter
4060 // 2 = 100 dpi, 4 counts per millimeter
4061 // 3 = 200 dpi, 8 counts per millimeter
4062 CLEAR_CF();
4063 regs.u.r8.ah = 0;
4064 break;
4066 case 4: // Get Device ID
4067 BX_DEBUG_INT15("case 4:\n");
4068 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4069 ret = send_to_mouse_ctrl(0xF2); // get mouse ID command
4070 if (ret == 0) {
4071 ret = get_mouse_data(&mouse_data1);
4072 ret = get_mouse_data(&mouse_data2);
4073 CLEAR_CF();
4074 regs.u.r8.ah = 0;
4075 regs.u.r8.bh = mouse_data2;
4076 } else {
4077 // error
4078 SET_CF();
4079 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4081 break;
4083 case 6: // Return Status & Set Scaling Factor...
4084 BX_DEBUG_INT15("case 6:\n");
4085 switch (regs.u.r8.bh) {
4086 case 0: // Return Status
4087 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4088 ret = send_to_mouse_ctrl(0xE9); // get mouse info command
4089 if (ret == 0) {
4090 ret = get_mouse_data(&mouse_data1);
4091 if (mouse_data1 != 0xfa)
4092 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
4093 if (ret == 0) {
4094 ret = get_mouse_data(&mouse_data1);
4095 if ( ret == 0 ) {
4096 ret = get_mouse_data(&mouse_data2);
4097 if ( ret == 0 ) {
4098 ret = get_mouse_data(&mouse_data3);
4099 if ( ret == 0 ) {
4100 CLEAR_CF();
4101 regs.u.r8.ah = 0;
4102 regs.u.r8.bl = mouse_data1;
4103 regs.u.r8.cl = mouse_data2;
4104 regs.u.r8.dl = mouse_data3;
4105 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4106 return;
4113 // error
4114 SET_CF();
4115 regs.u.r8.ah = ret;
4116 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4117 return;
4119 case 1: // Set Scaling Factor to 1:1
4120 case 2: // Set Scaling Factor to 2:1
4121 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4122 if (regs.u.r8.bh == 1) {
4123 ret = send_to_mouse_ctrl(0xE6);
4124 } else {
4125 ret = send_to_mouse_ctrl(0xE7);
4127 if (ret == 0) {
4128 get_mouse_data(&mouse_data1);
4129 ret = (mouse_data1 != 0xFA);
4131 if (ret == 0) {
4132 CLEAR_CF();
4133 regs.u.r8.ah = 0;
4134 } else {
4135 // error
4136 SET_CF();
4137 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4139 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
4140 break;
4142 default:
4143 BX_PANIC("INT 15h C2 AL=6, BH=%02x\n", (unsigned) regs.u.r8.bh);
4145 break;
4147 case 7: // Set Mouse Handler Address
4148 BX_DEBUG_INT15("case 7:\n");
4149 mouse_driver_seg = ES;
4150 mouse_driver_offset = regs.u.r16.bx;
4151 write_word(ebda_seg, 0x0022, mouse_driver_offset);
4152 write_word(ebda_seg, 0x0024, mouse_driver_seg);
4153 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4154 if (mouse_driver_offset == 0 && mouse_driver_seg == 0) {
4155 /* remove handler */
4156 if ( (mouse_flags_2 & 0x80) != 0 ) {
4157 mouse_flags_2 &= ~0x80;
4158 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
4161 else {
4162 /* install handler */
4163 mouse_flags_2 |= 0x80;
4165 write_byte(ebda_seg, 0x0027, mouse_flags_2);
4166 CLEAR_CF();
4167 regs.u.r8.ah = 0;
4168 break;
4170 default:
4171 BX_DEBUG_INT15("case default:\n");
4172 regs.u.r8.ah = 1; // invalid function
4173 SET_CF();
4175 break;
4177 default:
4178 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4179 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4180 SET_CF();
4181 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4182 break;
4185 #endif
4187 void
4188 int15_function32(regs, ES, DS, FLAGS)
4189 pushad_regs_t regs; // REGS pushed via pushad
4190 Bit16u ES, DS, FLAGS;
4192 Bit32u extended_memory_size=0; // 64bits long
4193 Bit16u CX,DX;
4195 BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
4197 switch (regs.u.r8.ah) {
4198 case 0x86:
4199 // Wait for CX:DX microseconds. currently using the
4200 // refresh request port 0x61 bit4, toggling every 15usec
4202 CX = regs.u.r16.cx;
4203 DX = regs.u.r16.dx;
4205 ASM_START
4206 ;; Get the count in eax
4207 mov ax, .int15_function32.CX [bp]
4208 shl eax, #16
4209 mov ax, .int15_function32.DX [bp]
4211 ;; convert to numbers of 15usec ticks
4212 mov ebx, #15
4213 xor edx, edx
4214 div eax, ebx
4215 mov ecx, eax
4217 ;; wait for ecx number of refresh requests
4218 in al, #0x61
4219 and al,#0x10
4220 mov ah, al
4222 or ecx, ecx
4223 je int1586_tick_end
4224 int1586_tick:
4225 in al, #0x61
4226 and al,#0x10
4227 cmp al, ah
4228 je int1586_tick
4229 mov ah, al
4230 dec ecx
4231 jnz int1586_tick
4232 int1586_tick_end:
4233 ASM_END
4235 break;
4237 case 0xe8:
4238 switch(regs.u.r8.al)
4240 case 0x20: {
4241 Bit16u e820_table_size = read_word(0xe000, 0x8) * 0x14;
4243 if (regs.u.r32.edx != 0x534D4150) /* SMAP */
4244 goto int15_unimplemented;
4246 if ((regs.u.r16.bx / 0x14) * 0x14 == regs.u.r16.bx) {
4247 if (regs.u.r16.bx + 0x14 <= e820_table_size)
4248 memcpyb(ES, regs.u.r16.di,
4249 0xe000, 0x10 + regs.u.r16.bx, 0x14);
4250 regs.u.r32.ebx += 0x14;
4251 if ((regs.u.r32.ebx + 0x14 - 1) > e820_table_size)
4252 regs.u.r32.ebx = 0;
4253 } else if (regs.u.r16.bx == 1) {
4254 Bit32u base, type;
4255 Bit16u off;
4256 for (off = 0; off < e820_table_size; off += 0x14) {
4257 base = read_dword(0xe000, 0x10 + off);
4258 type = read_dword(0xe000, 0x20 + off);
4259 if ((base >= 0x100000) && (type == 1))
4260 break;
4262 if (off == e820_table_size) {
4263 SET_CF();
4264 break;
4266 memcpyb(ES, regs.u.r16.di, 0xe000, 0x10 + off, 0x14);
4267 regs.u.r32.ebx = 0;
4268 } else { /* AX=E820, DX=534D4150, BX unrecognized */
4269 goto int15_unimplemented;
4272 regs.u.r32.eax = 0x534D4150;
4273 regs.u.r32.ecx = 0x14;
4274 CLEAR_CF();
4275 break;
4278 case 0x01: {
4279 Bit16u off, e820_table_size = read_word(0xe000, 0x8) * 0x14;
4280 Bit32u base, type, size;
4282 // do we have any reason to fail here ?
4283 CLEAR_CF();
4285 // Get the amount of extended memory (above 1M)
4286 regs.u.r8.cl = inb_cmos(0x30);
4287 regs.u.r8.ch = inb_cmos(0x31);
4289 // limit to 15M
4290 if (regs.u.r16.cx > (15*1024))
4291 regs.u.r16.cx = 15*1024;
4293 // Find first RAM E820 entry >= 1MB.
4294 for (off = 0; off < e820_table_size; off += 0x14) {
4295 base = read_dword(0xe000, 0x10 + off);
4296 type = read_dword(0xe000, 0x20 + off);
4297 if ((base >= 0x100000) && (type == 1))
4298 break;
4301 // If there is RAM above 16MB, return amount in 64kB chunks.
4302 regs.u.r16.dx = 0;
4303 if (off != e820_table_size) {
4304 size = base + read_dword(0xe000, 0x18 + off);
4305 if (size > 0x1000000) {
4306 size -= 0x1000000;
4307 regs.u.r16.dx = (Bit16u)(size >> 16);
4311 // Set configured memory equal to extended memory
4312 regs.u.r16.ax = regs.u.r16.cx;
4313 regs.u.r16.bx = regs.u.r16.dx;
4314 break;
4316 default: /* AH=0xE8?? but not implemented */
4317 goto int15_unimplemented;
4319 break;
4320 int15_unimplemented:
4321 // fall into the default
4322 default:
4323 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
4324 (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
4325 SET_CF();
4326 regs.u.r8.ah = UNSUPPORTED_FUNCTION;
4327 break;
4331 void
4332 int16_function(DI, SI, BP, SP, BX, DX, CX, AX, FLAGS)
4333 Bit16u DI, SI, BP, SP, BX, DX, CX, AX, FLAGS;
4335 Bit8u scan_code, ascii_code, shift_flags, count;
4336 Bit16u kbd_code, max;
4338 BX_DEBUG_INT16("int16: AX=%04x BX=%04x CX=%04x DX=%04x \n", AX, BX, CX, DX);
4340 switch (GET_AH()) {
4341 case 0x00: /* read keyboard input */
4343 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4344 BX_PANIC("KBD: int16h: out of keyboard input\n");
4346 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4347 else if (ascii_code == 0xE0) ascii_code = 0;
4348 AX = (scan_code << 8) | ascii_code;
4349 break;
4351 case 0x01: /* check keyboard status */
4352 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4353 SET_ZF();
4354 return;
4356 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4357 else if (ascii_code == 0xE0) ascii_code = 0;
4358 AX = (scan_code << 8) | ascii_code;
4359 CLEAR_ZF();
4360 break;
4362 case 0x02: /* get shift flag status */
4363 shift_flags = read_byte(0x0040, 0x17);
4364 SET_AL(shift_flags);
4365 break;
4367 case 0x05: /* store key-stroke into buffer */
4368 if ( !enqueue_key(GET_CH(), GET_CL()) ) {
4369 SET_AL(1);
4371 else {
4372 SET_AL(0);
4374 break;
4376 case 0x09: /* GET KEYBOARD FUNCTIONALITY */
4377 // bit Bochs Description
4378 // 7 0 reserved
4379 // 6 0 INT 16/AH=20h-22h supported (122-key keyboard support)
4380 // 5 1 INT 16/AH=10h-12h supported (enhanced keyboard support)
4381 // 4 1 INT 16/AH=0Ah supported
4382 // 3 0 INT 16/AX=0306h supported
4383 // 2 0 INT 16/AX=0305h supported
4384 // 1 0 INT 16/AX=0304h supported
4385 // 0 0 INT 16/AX=0300h supported
4386 //
4387 SET_AL(0x30);
4388 break;
4390 case 0x0A: /* GET KEYBOARD ID */
4391 count = 2;
4392 kbd_code = 0x0;
4393 outb(0x60, 0xf2);
4394 /* Wait for data */
4395 max=0xffff;
4396 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4397 if (max>0x0) {
4398 if ((inb(0x60) == 0xfa)) {
4399 do {
4400 max=0xffff;
4401 while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
4402 if (max>0x0) {
4403 kbd_code >>= 8;
4404 kbd_code |= (inb(0x60) << 8);
4406 } while (--count>0);
4409 BX=kbd_code;
4410 break;
4412 case 0x10: /* read MF-II keyboard input */
4414 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
4415 BX_PANIC("KBD: int16h: out of keyboard input\n");
4417 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4418 AX = (scan_code << 8) | ascii_code;
4419 break;
4421 case 0x11: /* check MF-II keyboard status */
4422 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
4423 SET_ZF();
4424 return;
4426 if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
4427 AX = (scan_code << 8) | ascii_code;
4428 CLEAR_ZF();
4429 break;
4431 case 0x12: /* get extended keyboard status */
4432 shift_flags = read_byte(0x0040, 0x17);
4433 SET_AL(shift_flags);
4434 shift_flags = read_byte(0x0040, 0x18);
4435 SET_AH(shift_flags);
4436 BX_DEBUG_INT16("int16: func 12 sending %04x\n",AX);
4437 break;
4439 case 0x92: /* keyboard capability check called by DOS 5.0+ keyb */
4440 SET_AH(0x80); // function int16 ah=0x10-0x12 supported
4441 break;
4443 case 0xA2: /* 122 keys capability check called by DOS 5.0+ keyb */
4444 // don't change AH : function int16 ah=0x20-0x22 NOT supported
4445 break;
4447 case 0x6F:
4448 if (GET_AL() == 0x08)
4449 SET_AH(0x02); // unsupported, aka normal keyboard
4451 default:
4452 BX_INFO("KBD: unsupported int 16h function %02x\n", GET_AH());
4456 unsigned int
4457 dequeue_key(scan_code, ascii_code, incr)
4458 Bit8u *scan_code;
4459 Bit8u *ascii_code;
4460 unsigned int incr;
4462 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail;
4463 Bit16u ss;
4464 Bit8u acode, scode;
4466 #if BX_CPU < 2
4467 buffer_start = 0x001E;
4468 buffer_end = 0x003E;
4469 #else
4470 buffer_start = read_word(0x0040, 0x0080);
4471 buffer_end = read_word(0x0040, 0x0082);
4472 #endif
4474 buffer_head = read_word(0x0040, 0x001a);
4475 buffer_tail = read_word(0x0040, 0x001c);
4477 if (buffer_head != buffer_tail) {
4478 ss = get_SS();
4479 acode = read_byte(0x0040, buffer_head);
4480 scode = read_byte(0x0040, buffer_head+1);
4481 write_byte(ss, ascii_code, acode);
4482 write_byte(ss, scan_code, scode);
4484 if (incr) {
4485 buffer_head += 2;
4486 if (buffer_head >= buffer_end)
4487 buffer_head = buffer_start;
4488 write_word(0x0040, 0x001a, buffer_head);
4490 return(1);
4492 else {
4493 return(0);
4497 static char panic_msg_keyb_buffer_full[] = "%s: keyboard input buffer full\n";
4499 Bit8u
4500 inhibit_mouse_int_and_events()
4502 Bit8u command_byte, prev_command_byte;
4504 // Turn off IRQ generation and aux data line
4505 if ( inb(0x64) & 0x02 )
4506 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4507 outb(0x64, 0x20); // get command byte
4508 while ( (inb(0x64) & 0x01) != 0x01 );
4509 prev_command_byte = inb(0x60);
4510 command_byte = prev_command_byte;
4511 //while ( (inb(0x64) & 0x02) );
4512 if ( inb(0x64) & 0x02 )
4513 BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
4514 command_byte &= 0xfd; // turn off IRQ 12 generation
4515 command_byte |= 0x20; // disable mouse serial clock line
4516 outb(0x64, 0x60); // write command byte
4517 outb(0x60, command_byte);
4518 return(prev_command_byte);
4521 void
4522 enable_mouse_int_and_events()
4524 Bit8u command_byte;
4526 // Turn on IRQ generation and aux data line
4527 if ( inb(0x64) & 0x02 )
4528 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4529 outb(0x64, 0x20); // get command byte
4530 while ( (inb(0x64) & 0x01) != 0x01 );
4531 command_byte = inb(0x60);
4532 //while ( (inb(0x64) & 0x02) );
4533 if ( inb(0x64) & 0x02 )
4534 BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
4535 command_byte |= 0x02; // turn on IRQ 12 generation
4536 command_byte &= 0xdf; // enable mouse serial clock line
4537 outb(0x64, 0x60); // write command byte
4538 outb(0x60, command_byte);
4541 Bit8u
4542 send_to_mouse_ctrl(sendbyte)
4543 Bit8u sendbyte;
4545 Bit8u response;
4547 // wait for chance to write to ctrl
4548 if ( inb(0x64) & 0x02 )
4549 BX_PANIC(panic_msg_keyb_buffer_full,"sendmouse");
4550 outb(0x64, 0xD4);
4551 outb(0x60, sendbyte);
4552 return(0);
4556 Bit8u
4557 get_mouse_data(data)
4558 Bit8u *data;
4560 Bit8u response;
4561 Bit16u ss;
4563 while ( (inb(0x64) & 0x21) != 0x21 ) {
4566 response = inb(0x60);
4568 ss = get_SS();
4569 write_byte(ss, data, response);
4570 return(0);
4573 void
4574 set_kbd_command_byte(command_byte)
4575 Bit8u command_byte;
4577 if ( inb(0x64) & 0x02 )
4578 BX_PANIC(panic_msg_keyb_buffer_full,"setkbdcomm");
4579 outb(0x64, 0xD4);
4581 outb(0x64, 0x60); // write command byte
4582 outb(0x60, command_byte);
4585 void
4586 int09_function(DI, SI, BP, SP, BX, DX, CX, AX)
4587 Bit16u DI, SI, BP, SP, BX, DX, CX, AX;
4589 Bit8u scancode, asciicode, shift_flags;
4590 Bit8u mf2_flags, mf2_state, led_flags;
4592 //
4593 // DS has been set to F000 before call
4594 //
4597 scancode = GET_AL();
4599 if (scancode == 0) {
4600 BX_INFO("KBD: int09 handler: AL=0\n");
4601 return;
4605 shift_flags = read_byte(0x0040, 0x17);
4606 mf2_flags = read_byte(0x0040, 0x18);
4607 mf2_state = read_byte(0x0040, 0x96);
4608 led_flags = read_byte(0x0040, 0x97);
4609 asciicode = 0;
4611 switch (scancode) {
4612 case 0x3a: /* Caps Lock press */
4613 shift_flags ^= 0x40;
4614 write_byte(0x0040, 0x17, shift_flags);
4615 mf2_flags |= 0x40;
4616 write_byte(0x0040, 0x18, mf2_flags);
4617 led_flags ^= 0x04;
4618 write_byte(0x0040, 0x97, led_flags);
4619 break;
4620 case 0xba: /* Caps Lock release */
4621 mf2_flags &= ~0x40;
4622 write_byte(0x0040, 0x18, mf2_flags);
4623 break;
4625 case 0x2a: /* L Shift press */
4626 /*shift_flags &= ~0x40;*/
4627 shift_flags |= 0x02;
4628 write_byte(0x0040, 0x17, shift_flags);
4629 led_flags &= ~0x04;
4630 write_byte(0x0040, 0x97, led_flags);
4631 break;
4632 case 0xaa: /* L Shift release */
4633 shift_flags &= ~0x02;
4634 write_byte(0x0040, 0x17, shift_flags);
4635 break;
4637 case 0x36: /* R Shift press */
4638 /*shift_flags &= ~0x40;*/
4639 shift_flags |= 0x01;
4640 write_byte(0x0040, 0x17, shift_flags);
4641 led_flags &= ~0x04;
4642 write_byte(0x0040, 0x97, led_flags);
4643 break;
4644 case 0xb6: /* R Shift release */
4645 shift_flags &= ~0x01;
4646 write_byte(0x0040, 0x17, shift_flags);
4647 break;
4649 case 0x1d: /* Ctrl press */
4650 shift_flags |= 0x04;
4651 write_byte(0x0040, 0x17, shift_flags);
4652 if (mf2_state & 0x01) {
4653 mf2_flags |= 0x04;
4654 } else {
4655 mf2_flags |= 0x01;
4657 write_byte(0x0040, 0x18, mf2_flags);
4658 break;
4659 case 0x9d: /* Ctrl release */
4660 shift_flags &= ~0x04;
4661 write_byte(0x0040, 0x17, shift_flags);
4662 if (mf2_state & 0x01) {
4663 mf2_flags &= ~0x04;
4664 } else {
4665 mf2_flags &= ~0x01;
4667 write_byte(0x0040, 0x18, mf2_flags);
4668 break;
4670 case 0x38: /* Alt press */
4671 shift_flags |= 0x08;
4672 write_byte(0x0040, 0x17, shift_flags);
4673 if (mf2_state & 0x01) {
4674 mf2_flags |= 0x08;
4675 } else {
4676 mf2_flags |= 0x02;
4678 write_byte(0x0040, 0x18, mf2_flags);
4679 break;
4680 case 0xb8: /* Alt release */
4681 shift_flags &= ~0x08;
4682 write_byte(0x0040, 0x17, shift_flags);
4683 if (mf2_state & 0x01) {
4684 mf2_flags &= ~0x08;
4685 } else {
4686 mf2_flags &= ~0x02;
4688 write_byte(0x0040, 0x18, mf2_flags);
4689 break;
4691 case 0x45: /* Num Lock press */
4692 if ((mf2_state & 0x01) == 0) {
4693 mf2_flags |= 0x20;
4694 write_byte(0x0040, 0x18, mf2_flags);
4695 shift_flags ^= 0x20;
4696 led_flags ^= 0x02;
4697 write_byte(0x0040, 0x17, shift_flags);
4698 write_byte(0x0040, 0x97, led_flags);
4700 break;
4701 case 0xc5: /* Num Lock release */
4702 if ((mf2_state & 0x01) == 0) {
4703 mf2_flags &= ~0x20;
4704 write_byte(0x0040, 0x18, mf2_flags);
4706 break;
4708 case 0x46: /* Scroll Lock press */
4709 mf2_flags |= 0x10;
4710 write_byte(0x0040, 0x18, mf2_flags);
4711 shift_flags ^= 0x10;
4712 led_flags ^= 0x01;
4713 write_byte(0x0040, 0x17, shift_flags);
4714 write_byte(0x0040, 0x97, led_flags);
4715 break;
4717 case 0xc6: /* Scroll Lock release */
4718 mf2_flags &= ~0x10;
4719 write_byte(0x0040, 0x18, mf2_flags);
4720 break;
4722 case 0x53: /* Del */
4723 if ((shift_flags & 0x0c) == 0x0c) /* Ctrl + Alt */
4724 machine_reset();
4725 /* Fall through */
4726 default:
4727 if (scancode & 0x80) return; /* toss key releases ... */
4728 if (scancode > MAX_SCAN_CODE) {
4729 BX_INFO("KBD: int09h_handler(): unknown scancode (%x) read!\n", scancode);
4730 return;
4732 if (shift_flags & 0x08) { /* ALT */
4733 asciicode = scan_to_scanascii[scancode].alt;
4734 scancode = scan_to_scanascii[scancode].alt >> 8;
4736 else if (shift_flags & 0x04) { /* CONTROL */
4737 asciicode = scan_to_scanascii[scancode].control;
4738 scancode = scan_to_scanascii[scancode].control >> 8;
4740 else if (shift_flags & 0x03) { /* LSHIFT + RSHIFT */
4741 /* check if lock state should be ignored
4742 * because a SHIFT key are pressed */
4744 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
4745 asciicode = scan_to_scanascii[scancode].normal;
4746 scancode = scan_to_scanascii[scancode].normal >> 8;
4748 else {
4749 asciicode = scan_to_scanascii[scancode].shift;
4750 scancode = scan_to_scanascii[scancode].shift >> 8;
4753 else {
4754 /* check if lock is on */
4755 if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
4756 asciicode = scan_to_scanascii[scancode].shift;
4757 scancode = scan_to_scanascii[scancode].shift >> 8;
4759 else {
4760 asciicode = scan_to_scanascii[scancode].normal;
4761 scancode = scan_to_scanascii[scancode].normal >> 8;
4764 if (scancode==0 && asciicode==0) {
4765 BX_INFO("KBD: int09h_handler(): scancode & asciicode are zero?\n");
4767 enqueue_key(scancode, asciicode);
4768 break;
4770 mf2_state &= ~0x01;
4773 unsigned int
4774 enqueue_key(scan_code, ascii_code)
4775 Bit8u scan_code, ascii_code;
4777 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail, temp_tail;
4779 //BX_INFO("KBD: enqueue_key() called scan:%02x, ascii:%02x\n",
4780 // scan_code, ascii_code);
4782 #if BX_CPU < 2
4783 buffer_start = 0x001E;
4784 buffer_end = 0x003E;
4785 #else
4786 buffer_start = read_word(0x0040, 0x0080);
4787 buffer_end = read_word(0x0040, 0x0082);
4788 #endif
4790 buffer_head = read_word(0x0040, 0x001A);
4791 buffer_tail = read_word(0x0040, 0x001C);
4793 temp_tail = buffer_tail;
4794 buffer_tail += 2;
4795 if (buffer_tail >= buffer_end)
4796 buffer_tail = buffer_start;
4798 if (buffer_tail == buffer_head) {
4799 return(0);
4802 write_byte(0x0040, temp_tail, ascii_code);
4803 write_byte(0x0040, temp_tail+1, scan_code);
4804 write_word(0x0040, 0x001C, buffer_tail);
4805 return(1);
4809 void
4810 int74_function(make_farcall, Z, Y, X, status)
4811 Bit16u make_farcall, Z, Y, X, status;
4813 Bit16u ebda_seg=read_word(0x0040,0x000E);
4814 Bit8u in_byte, index, package_count;
4815 Bit8u mouse_flags_1, mouse_flags_2;
4817 BX_DEBUG_INT74("entering int74_function\n");
4818 make_farcall = 0;
4820 in_byte = inb(0x64);
4821 if ( (in_byte & 0x21) != 0x21 ) {
4822 return;
4824 in_byte = inb(0x60);
4825 BX_DEBUG_INT74("int74: read byte %02x\n", in_byte);
4827 mouse_flags_1 = read_byte(ebda_seg, 0x0026);
4828 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
4830 if ( (mouse_flags_2 & 0x80) != 0x80 ) {
4831 // BX_PANIC("int74_function:\n");
4832 return;
4835 package_count = mouse_flags_2 & 0x07;
4836 index = mouse_flags_1 & 0x07;
4837 write_byte(ebda_seg, 0x28 + index, in_byte);
4839 if ( (index+1) >= package_count ) {
4840 BX_DEBUG_INT74("int74_function: make_farcall=1\n");
4841 status = read_byte(ebda_seg, 0x0028 + 0);
4842 X = read_byte(ebda_seg, 0x0028 + 1);
4843 Y = read_byte(ebda_seg, 0x0028 + 2);
4844 Z = 0;
4845 mouse_flags_1 = 0;
4846 // check if far call handler installed
4847 if (mouse_flags_2 & 0x80)
4848 make_farcall = 1;
4850 else {
4851 mouse_flags_1++;
4853 write_byte(ebda_seg, 0x0026, mouse_flags_1);
4856 #define SET_DISK_RET_STATUS(status) write_byte(0x0040, 0x0074, status)
4858 #if BX_USE_ATADRV
4860 void
4861 int13_harddisk(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
4862 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
4864 Bit32u lba;
4865 Bit16u ebda_seg=read_word(0x0040,0x000E);
4866 Bit16u cylinder, head, sector;
4867 Bit16u segment, offset;
4868 Bit16u npc, nph, npspt, nlc, nlh, nlspt;
4869 Bit16u size, count;
4870 Bit8u device, status;
4872 BX_DEBUG_INT13_HD("int13_harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
4874 write_byte(0x0040, 0x008e, 0); // clear completion flag
4876 // basic check : device has to be defined
4877 if ( (GET_ELDL() < 0x80) || (GET_ELDL() >= 0x80 + BX_MAX_ATA_DEVICES) ) {
4878 BX_INFO("int13_harddisk: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
4879 goto int13_fail;
4882 // Get the ata channel
4883 device=read_byte(ebda_seg,&EbdaData->ata.hdidmap[GET_ELDL()-0x80]);
4885 // basic check : device has to be valid
4886 if (device >= BX_MAX_ATA_DEVICES) {
4887 BX_INFO("int13_harddisk: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
4888 goto int13_fail;
4891 switch (GET_AH()) {
4893 case 0x00: /* disk controller reset */
4894 ata_reset (device);
4895 goto int13_success;
4896 break;
4898 case 0x01: /* read disk status */
4899 status = read_byte(0x0040, 0x0074);
4900 SET_AH(status);
4901 SET_DISK_RET_STATUS(0);
4902 /* set CF if error status read */
4903 if (status) goto int13_fail_nostatus;
4904 else goto int13_success_noah;
4905 break;
4907 case 0x02: // read disk sectors
4908 case 0x03: // write disk sectors
4909 case 0x04: // verify disk sectors
4911 count = GET_AL();
4912 cylinder = GET_CH();
4913 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
4914 sector = (GET_CL() & 0x3f);
4915 head = GET_DH();
4917 segment = ES;
4918 offset = BX;
4920 if ( (count > 128) || (count == 0) ) {
4921 BX_INFO("int13_harddisk: function %02x, count out of range!\n",GET_AH());
4922 goto int13_fail;
4925 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
4926 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
4927 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
4929 // sanity check on cyl heads, sec
4930 if( (cylinder >= nlc) || (head >= nlh) || (sector > nlspt )) {
4931 BX_INFO("int13_harddisk: function %02x, parameters out of range %04x/%04x/%04x!\n", GET_AH(), cylinder, head, sector);
4932 goto int13_fail;
4935 // FIXME verify
4936 if ( GET_AH() == 0x04 ) goto int13_success;
4938 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
4939 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
4941 // if needed, translate lchs to lba, and execute command
4942 if ( (nph != nlh) || (npspt != nlspt)) {
4943 lba = ((((Bit32u)cylinder * (Bit32u)nlh) + (Bit32u)head) * (Bit32u)nlspt) + (Bit32u)sector - 1;
4944 sector = 0; // this forces the command to be lba
4947 if ( GET_AH() == 0x02 )
4948 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, cylinder, head, sector, lba, segment, offset);
4949 else
4950 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, cylinder, head, sector, lba, segment, offset);
4952 // Set nb of sector transferred
4953 SET_AL(read_word(ebda_seg, &EbdaData->ata.trsfsectors));
4955 if (status != 0) {
4956 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
4957 SET_AH(0x0c);
4958 goto int13_fail_noah;
4961 goto int13_success;
4962 break;
4964 case 0x05: /* format disk track */
4965 BX_INFO("format disk track called\n");
4966 goto int13_success;
4967 return;
4968 break;
4970 case 0x08: /* read disk drive parameters */
4972 // Get logical geometry from table
4973 nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
4974 nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
4975 nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
4976 count = read_byte(ebda_seg, &EbdaData->ata.hdcount);
4978 nlc = nlc - 2; /* 0 based , last sector not used */
4979 SET_AL(0);
4980 SET_CH(nlc & 0xff);
4981 SET_CL(((nlc >> 2) & 0xc0) | (nlspt & 0x3f));
4982 SET_DH(nlh - 1);
4983 SET_DL(count); /* FIXME returns 0, 1, or n hard drives */
4985 // FIXME should set ES & DI
4987 goto int13_success;
4988 break;
4990 case 0x10: /* check drive ready */
4991 // should look at 40:8E also???
4993 // Read the status from controller
4994 status = inb(read_word(ebda_seg, &EbdaData->ata.channels[device/2].iobase1) + ATA_CB_STAT);
4995 if ( (status & ( ATA_CB_STAT_BSY | ATA_CB_STAT_RDY )) == ATA_CB_STAT_RDY ) {
4996 goto int13_success;
4998 else {
4999 SET_AH(0xAA);
5000 goto int13_fail_noah;
5002 break;
5004 case 0x15: /* read disk drive size */
5006 // Get physical geometry from table
5007 npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
5008 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
5009 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
5011 // Compute sector count seen by int13
5012 lba = (Bit32u)(npc - 1) * (Bit32u)nph * (Bit32u)npspt;
5013 CX = lba >> 16;
5014 DX = lba & 0xffff;
5016 SET_AH(3); // hard disk accessible
5017 goto int13_success_noah;
5018 break;
5020 case 0x41: // IBM/MS installation check
5021 BX=0xaa55; // install check
5022 SET_AH(0x30); // EDD 3.0
5023 CX=0x0007; // ext disk access and edd, removable supported
5024 goto int13_success_noah;
5025 break;
5027 case 0x42: // IBM/MS extended read
5028 case 0x43: // IBM/MS extended write
5029 case 0x44: // IBM/MS verify
5030 case 0x47: // IBM/MS extended seek
5032 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
5033 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
5034 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
5036 // Can't use 64 bits lba
5037 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
5038 if (lba != 0L) {
5039 BX_PANIC("int13_harddisk: function %02x. Can't use 64bits lba\n",GET_AH());
5040 goto int13_fail;
5043 // Get 32 bits lba and check
5044 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5045 if (lba >= read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors) ) {
5046 BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH());
5047 goto int13_fail;
5050 // If verify or seek
5051 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
5052 goto int13_success;
5054 // Execute the command
5055 if ( GET_AH() == 0x42 )
5056 status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, 0, 0, 0, lba, segment, offset);
5057 else
5058 status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, 0, 0, 0, lba, segment, offset);
5060 count=read_word(ebda_seg, &EbdaData->ata.trsfsectors);
5061 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5063 if (status != 0) {
5064 BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
5065 SET_AH(0x0c);
5066 goto int13_fail_noah;
5069 goto int13_success;
5070 break;
5072 case 0x45: // IBM/MS lock/unlock drive
5073 case 0x49: // IBM/MS extended media change
5074 goto int13_success; // Always success for HD
5075 break;
5077 case 0x46: // IBM/MS eject media
5078 SET_AH(0xb2); // Volume Not Removable
5079 goto int13_fail_noah; // Always fail for HD
5080 break;
5082 case 0x48: // IBM/MS get drive parameters
5083 size=read_word(DS,SI+(Bit16u)&Int13DPT->size);
5085 // Buffer is too small
5086 if(size < 0x1a)
5087 goto int13_fail;
5089 // EDD 1.x
5090 if(size >= 0x1a) {
5091 Bit16u blksize;
5093 npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
5094 nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
5095 npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
5096 lba = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors);
5097 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5099 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5100 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x02); // geometry is valid
5101 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, (Bit32u)npc);
5102 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, (Bit32u)nph);
5103 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, (Bit32u)npspt);
5104 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, lba); // FIXME should be Bit64
5105 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0L);
5106 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5109 // EDD 2.x
5110 if(size >= 0x1e) {
5111 Bit8u channel, dev, irq, mode, checksum, i, translation;
5112 Bit16u iobase1, iobase2, options;
5114 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5116 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5117 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5119 // Fill in dpte
5120 channel = device / 2;
5121 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5122 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5123 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5124 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5125 translation = read_byte(ebda_seg, &EbdaData->ata.devices[device].translation);
5127 options = (translation==ATA_TRANSLATION_NONE?0:1<<3); // chs translation
5128 options |= (1<<4); // lba translation
5129 options |= (mode==ATA_MODE_PIO32?1:0<<7);
5130 options |= (translation==ATA_TRANSLATION_LBA?1:0<<9);
5131 options |= (translation==ATA_TRANSLATION_RECHS?3:0<<9);
5133 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5134 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2);
5135 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5136 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5137 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5138 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5139 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5140 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5141 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5142 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5143 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5145 checksum=0;
5146 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, (&EbdaData->ata.dpte) + i);
5147 checksum = ~checksum;
5148 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5151 // EDD 3.x
5152 if(size >= 0x42) {
5153 Bit8u channel, iface, checksum, i;
5154 Bit16u iobase1;
5156 channel = device / 2;
5157 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5158 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5160 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5161 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5162 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5163 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5164 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5166 if (iface==ATA_IFACE_ISA) {
5167 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5168 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5169 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5170 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5172 else {
5173 // FIXME PCI
5175 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5176 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5177 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5178 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5180 if (iface==ATA_IFACE_ISA) {
5181 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5182 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5183 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5185 else {
5186 // FIXME PCI
5188 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5189 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5190 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5191 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5193 checksum=0;
5194 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5195 checksum = ~checksum;
5196 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5199 goto int13_success;
5200 break;
5202 case 0x4e: // // IBM/MS set hardware configuration
5203 // DMA, prefetch, PIO maximum not supported
5204 switch (GET_AL()) {
5205 case 0x01:
5206 case 0x03:
5207 case 0x04:
5208 case 0x06:
5209 goto int13_success;
5210 break;
5211 default :
5212 goto int13_fail;
5214 break;
5216 case 0x09: /* initialize drive parameters */
5217 case 0x0c: /* seek to specified cylinder */
5218 case 0x0d: /* alternate disk reset */
5219 case 0x11: /* recalibrate */
5220 case 0x14: /* controller internal diagnostic */
5221 BX_INFO("int13h_harddisk function %02xh unimplemented, returns success\n", GET_AH());
5222 goto int13_success;
5223 break;
5225 case 0x0a: /* read disk sectors with ECC */
5226 case 0x0b: /* write disk sectors with ECC */
5227 case 0x18: // set media type for format
5228 case 0x50: // IBM/MS send packet command
5229 default:
5230 BX_INFO("int13_harddisk function %02xh unsupported, returns fail\n", GET_AH());
5231 goto int13_fail;
5232 break;
5235 int13_fail:
5236 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5237 int13_fail_noah:
5238 SET_DISK_RET_STATUS(GET_AH());
5239 int13_fail_nostatus:
5240 SET_CF(); // error occurred
5241 return;
5243 int13_success:
5244 SET_AH(0x00); // no error
5245 int13_success_noah:
5246 SET_DISK_RET_STATUS(0x00);
5247 CLEAR_CF(); // no error
5248 return;
5251 // ---------------------------------------------------------------------------
5252 // Start of int13 for cdrom
5253 // ---------------------------------------------------------------------------
5255 void
5256 int13_cdrom(EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
5257 Bit16u EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
5259 Bit16u ebda_seg=read_word(0x0040,0x000E);
5260 Bit8u device, status, locks;
5261 Bit8u atacmd[12];
5262 Bit32u lba;
5263 Bit16u count, segment, offset, i, size;
5265 BX_DEBUG_INT13_CD("int13_cdrom: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5266 // BX_DEBUG_INT13_CD("int13_cdrom: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
5268 SET_DISK_RET_STATUS(0x00);
5270 /* basic check : device should be 0xE0+ */
5271 if( (GET_ELDL() < 0xE0) || (GET_ELDL() >= 0xE0+BX_MAX_ATA_DEVICES) ) {
5272 BX_INFO("int13_cdrom: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
5273 goto int13_fail;
5276 // Get the ata channel
5277 device=read_byte(ebda_seg,&EbdaData->ata.cdidmap[GET_ELDL()-0xE0]);
5279 /* basic check : device has to be valid */
5280 if (device >= BX_MAX_ATA_DEVICES) {
5281 BX_INFO("int13_cdrom: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
5282 goto int13_fail;
5285 switch (GET_AH()) {
5287 // all those functions return SUCCESS
5288 case 0x00: /* disk controller reset */
5289 case 0x09: /* initialize drive parameters */
5290 case 0x0c: /* seek to specified cylinder */
5291 case 0x0d: /* alternate disk reset */
5292 case 0x10: /* check drive ready */
5293 case 0x11: /* recalibrate */
5294 case 0x14: /* controller internal diagnostic */
5295 case 0x16: /* detect disk change */
5296 goto int13_success;
5297 break;
5299 // all those functions return disk write-protected
5300 case 0x03: /* write disk sectors */
5301 case 0x05: /* format disk track */
5302 case 0x43: // IBM/MS extended write
5303 SET_AH(0x03);
5304 goto int13_fail_noah;
5305 break;
5307 case 0x01: /* read disk status */
5308 status = read_byte(0x0040, 0x0074);
5309 SET_AH(status);
5310 SET_DISK_RET_STATUS(0);
5312 /* set CF if error status read */
5313 if (status) goto int13_fail_nostatus;
5314 else goto int13_success_noah;
5315 break;
5317 case 0x15: /* read disk drive size */
5318 SET_AH(0x02);
5319 goto int13_fail_noah;
5320 break;
5322 case 0x41: // IBM/MS installation check
5323 BX=0xaa55; // install check
5324 SET_AH(0x30); // EDD 2.1
5325 CX=0x0007; // ext disk access, removable and edd
5326 goto int13_success_noah;
5327 break;
5329 case 0x42: // IBM/MS extended read
5330 case 0x44: // IBM/MS verify sectors
5331 case 0x47: // IBM/MS extended seek
5333 count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
5334 segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
5335 offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
5337 // Can't use 64 bits lba
5338 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
5339 if (lba != 0L) {
5340 BX_PANIC("int13_cdrom: function %02x. Can't use 64bits lba\n",GET_AH());
5341 goto int13_fail;
5344 // Get 32 bits lba
5345 lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
5347 // If verify or seek
5348 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
5349 goto int13_success;
5351 memsetb(get_SS(),atacmd,0,12);
5352 atacmd[0]=0x28; // READ command
5353 atacmd[7]=(count & 0xff00) >> 8; // Sectors
5354 atacmd[8]=(count & 0x00ff); // Sectors
5355 atacmd[2]=(lba & 0xff000000) >> 24; // LBA
5356 atacmd[3]=(lba & 0x00ff0000) >> 16;
5357 atacmd[4]=(lba & 0x0000ff00) >> 8;
5358 atacmd[5]=(lba & 0x000000ff);
5359 status = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, count*2048L, ATA_DATA_IN, segment,offset);
5361 count = (Bit16u)(read_dword(ebda_seg, &EbdaData->ata.trsfbytes) >> 11);
5362 write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
5364 if (status != 0) {
5365 BX_INFO("int13_cdrom: function %02x, status %02x !\n",GET_AH(),status);
5366 SET_AH(0x0c);
5367 goto int13_fail_noah;
5370 goto int13_success;
5371 break;
5373 case 0x45: // IBM/MS lock/unlock drive
5374 if (GET_AL() > 2) goto int13_fail;
5376 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5378 switch (GET_AL()) {
5379 case 0 : // lock
5380 if (locks == 0xff) {
5381 SET_AH(0xb4);
5382 SET_AL(1);
5383 goto int13_fail_noah;
5385 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, ++locks);
5386 SET_AL(1);
5387 break;
5388 case 1 : // unlock
5389 if (locks == 0x00) {
5390 SET_AH(0xb0);
5391 SET_AL(0);
5392 goto int13_fail_noah;
5394 write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, --locks);
5395 SET_AL(locks==0?0:1);
5396 break;
5397 case 2 : // status
5398 SET_AL(locks==0?0:1);
5399 break;
5401 goto int13_success;
5402 break;
5404 case 0x46: // IBM/MS eject media
5405 locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
5407 if (locks != 0) {
5408 SET_AH(0xb1); // media locked
5409 goto int13_fail_noah;
5411 // FIXME should handle 0x31 no media in device
5412 // FIXME should handle 0xb5 valid request failed
5414 // Call removable media eject
5415 ASM_START
5416 push bp
5417 mov bp, sp
5419 mov ah, #0x52
5420 int 15
5421 mov _int13_cdrom.status + 2[bp], ah
5422 jnc int13_cdrom_rme_end
5423 mov _int13_cdrom.status, #1
5424 int13_cdrom_rme_end:
5425 pop bp
5426 ASM_END
5428 if (status != 0) {
5429 SET_AH(0xb1); // media locked
5430 goto int13_fail_noah;
5433 goto int13_success;
5434 break;
5436 case 0x48: // IBM/MS get drive parameters
5437 size = read_word(DS,SI+(Bit16u)&Int13Ext->size);
5439 // Buffer is too small
5440 if(size < 0x1a)
5441 goto int13_fail;
5443 // EDD 1.x
5444 if(size >= 0x1a) {
5445 Bit16u cylinders, heads, spt, blksize;
5447 blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
5449 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
5450 write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x74); // removable, media change, lockable, max values
5451 write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0xffffffff);
5452 write_dword(DS, SI+(Bit16u)&Int13DPT->heads, 0xffffffff);
5453 write_dword(DS, SI+(Bit16u)&Int13DPT->spt, 0xffffffff);
5454 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, 0xffffffff); // FIXME should be Bit64
5455 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0xffffffff);
5456 write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
5459 // EDD 2.x
5460 if(size >= 0x1e) {
5461 Bit8u channel, dev, irq, mode, checksum, i;
5462 Bit16u iobase1, iobase2, options;
5464 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
5466 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
5467 write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
5469 // Fill in dpte
5470 channel = device / 2;
5471 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5472 iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
5473 irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
5474 mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
5476 // FIXME atapi device
5477 options = (1<<4); // lba translation
5478 options |= (1<<5); // removable device
5479 options |= (1<<6); // atapi device
5480 options |= (mode==ATA_MODE_PIO32?1:0<<7);
5482 write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
5483 write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2);
5484 write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
5485 write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
5486 write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
5487 write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
5488 write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
5489 write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
5490 write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
5491 write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
5492 write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
5494 checksum=0;
5495 for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, (&EbdaData->ata.dpte) + i);
5496 checksum = ~checksum;
5497 write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
5500 // EDD 3.x
5501 if(size >= 0x42) {
5502 Bit8u channel, iface, checksum, i;
5503 Bit16u iobase1;
5505 channel = device / 2;
5506 iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
5507 iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
5509 write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
5510 write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
5511 write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
5512 write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
5513 write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
5515 if (iface==ATA_IFACE_ISA) {
5516 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
5517 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
5518 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
5519 write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
5521 else {
5522 // FIXME PCI
5524 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
5525 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
5526 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
5527 write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
5529 if (iface==ATA_IFACE_ISA) {
5530 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
5531 write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
5532 write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
5534 else {
5535 // FIXME PCI
5537 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
5538 write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
5539 write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
5540 write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
5542 checksum=0;
5543 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
5544 checksum = ~checksum;
5545 write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
5548 goto int13_success;
5549 break;
5551 case 0x49: // IBM/MS extended media change
5552 // always send changed ??
5553 SET_AH(06);
5554 goto int13_fail_nostatus;
5555 break;
5557 case 0x4e: // // IBM/MS set hardware configuration
5558 // DMA, prefetch, PIO maximum not supported
5559 switch (GET_AL()) {
5560 case 0x01:
5561 case 0x03:
5562 case 0x04:
5563 case 0x06:
5564 goto int13_success;
5565 break;
5566 default :
5567 goto int13_fail;
5569 break;
5571 // all those functions return unimplemented
5572 case 0x02: /* read sectors */
5573 case 0x04: /* verify sectors */
5574 case 0x08: /* read disk drive parameters */
5575 case 0x0a: /* read disk sectors with ECC */
5576 case 0x0b: /* write disk sectors with ECC */
5577 case 0x18: /* set media type for format */
5578 case 0x50: // ? - send packet command
5579 default:
5580 BX_INFO("int13_cdrom: unsupported AH=%02x\n", GET_AH());
5581 goto int13_fail;
5582 break;
5585 int13_fail:
5586 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5587 int13_fail_noah:
5588 SET_DISK_RET_STATUS(GET_AH());
5589 int13_fail_nostatus:
5590 SET_CF(); // error occurred
5591 return;
5593 int13_success:
5594 SET_AH(0x00); // no error
5595 int13_success_noah:
5596 SET_DISK_RET_STATUS(0x00);
5597 CLEAR_CF(); // no error
5598 return;
5601 // ---------------------------------------------------------------------------
5602 // End of int13 for cdrom
5603 // ---------------------------------------------------------------------------
5605 #if BX_ELTORITO_BOOT
5606 // ---------------------------------------------------------------------------
5607 // Start of int13 for eltorito functions
5608 // ---------------------------------------------------------------------------
5610 void
5611 int13_eltorito(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
5612 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
5614 Bit16u ebda_seg=read_word(0x0040,0x000E);
5616 BX_DEBUG_INT13_ET("int13_eltorito: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5617 // BX_DEBUG_INT13_ET("int13_eltorito: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
5619 switch (GET_AH()) {
5621 // FIXME ElTorito Various. Should be implemented
5622 case 0x4a: // ElTorito - Initiate disk emu
5623 case 0x4c: // ElTorito - Initiate disk emu and boot
5624 case 0x4d: // ElTorito - Return Boot catalog
5625 BX_PANIC("Int13 eltorito call with AX=%04x. Please report\n",AX);
5626 goto int13_fail;
5627 break;
5629 case 0x4b: // ElTorito - Terminate disk emu
5630 // FIXME ElTorito Hardcoded
5631 write_byte(DS,SI+0x00,0x13);
5632 write_byte(DS,SI+0x01,read_byte(ebda_seg,&EbdaData->cdemu.media));
5633 write_byte(DS,SI+0x02,read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
5634 write_byte(DS,SI+0x03,read_byte(ebda_seg,&EbdaData->cdemu.controller_index));
5635 write_dword(DS,SI+0x04,read_dword(ebda_seg,&EbdaData->cdemu.ilba));
5636 write_word(DS,SI+0x08,read_word(ebda_seg,&EbdaData->cdemu.device_spec));
5637 write_word(DS,SI+0x0a,read_word(ebda_seg,&EbdaData->cdemu.buffer_segment));
5638 write_word(DS,SI+0x0c,read_word(ebda_seg,&EbdaData->cdemu.load_segment));
5639 write_word(DS,SI+0x0e,read_word(ebda_seg,&EbdaData->cdemu.sector_count));
5640 write_byte(DS,SI+0x10,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.cylinders));
5641 write_byte(DS,SI+0x11,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.spt));
5642 write_byte(DS,SI+0x12,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.heads));
5644 // If we have to terminate emulation
5645 if(GET_AL() == 0x00) {
5646 // FIXME ElTorito Various. Should be handled accordingly to spec
5647 write_byte(ebda_seg,&EbdaData->cdemu.active, 0x00); // bye bye
5650 goto int13_success;
5651 break;
5653 default:
5654 BX_INFO("int13_eltorito: unsupported AH=%02x\n", GET_AH());
5655 goto int13_fail;
5656 break;
5659 int13_fail:
5660 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5661 SET_DISK_RET_STATUS(GET_AH());
5662 SET_CF(); // error occurred
5663 return;
5665 int13_success:
5666 SET_AH(0x00); // no error
5667 SET_DISK_RET_STATUS(0x00);
5668 CLEAR_CF(); // no error
5669 return;
5672 // ---------------------------------------------------------------------------
5673 // End of int13 for eltorito functions
5674 // ---------------------------------------------------------------------------
5676 // ---------------------------------------------------------------------------
5677 // Start of int13 when emulating a device from the cd
5678 // ---------------------------------------------------------------------------
5680 void
5681 int13_cdemu(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
5682 Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
5684 Bit16u ebda_seg=read_word(0x0040,0x000E);
5685 Bit8u device, status;
5686 Bit16u vheads, vspt, vcylinders;
5687 Bit16u head, sector, cylinder, nbsectors;
5688 Bit32u vlba, ilba, slba, elba;
5689 Bit16u before, segment, offset;
5690 Bit8u atacmd[12];
5692 BX_DEBUG_INT13_ET("int13_cdemu: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5693 //BX_DEBUG_INT13_ET("int13_cdemu: SS=%04x ES=%04x DI=%04x SI=%04x\n", get_SS(), ES, DI, SI);
5695 /* at this point, we are emulating a floppy/harddisk */
5697 // Recompute the device number
5698 device = read_byte(ebda_seg,&EbdaData->cdemu.controller_index) * 2;
5699 device += read_byte(ebda_seg,&EbdaData->cdemu.device_spec);
5701 SET_DISK_RET_STATUS(0x00);
5703 /* basic checks : emulation should be active, dl should equal the emulated drive */
5704 if( (read_byte(ebda_seg,&EbdaData->cdemu.active) ==0 )
5705 || (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive ) != GET_DL())) {
5706 BX_INFO("int13_cdemu: function %02x, emulation not active for DL= %02x\n", GET_AH(), GET_DL());
5707 goto int13_fail;
5711 switch (GET_AH()) {
5713 // all those functions return SUCCESS
5714 case 0x00: /* disk controller reset */
5715 case 0x09: /* initialize drive parameters */
5716 case 0x0c: /* seek to specified cylinder */
5717 case 0x0d: /* alternate disk reset */ // FIXME ElTorito Various. should really reset ?
5718 case 0x10: /* check drive ready */ // FIXME ElTorito Various. should check if ready ?
5719 case 0x11: /* recalibrate */
5720 case 0x14: /* controller internal diagnostic */
5721 case 0x16: /* detect disk change */
5722 goto int13_success;
5723 break;
5725 // all those functions return disk write-protected
5726 case 0x03: /* write disk sectors */
5727 case 0x05: /* format disk track */
5728 SET_AH(0x03);
5729 goto int13_fail_noah;
5730 break;
5732 case 0x01: /* read disk status */
5733 status=read_byte(0x0040, 0x0074);
5734 SET_AH(status);
5735 SET_DISK_RET_STATUS(0);
5737 /* set CF if error status read */
5738 if (status) goto int13_fail_nostatus;
5739 else goto int13_success_noah;
5740 break;
5742 case 0x02: // read disk sectors
5743 case 0x04: // verify disk sectors
5744 vspt = read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
5745 vcylinders = read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders);
5746 vheads = read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads);
5748 ilba = read_dword(ebda_seg,&EbdaData->cdemu.ilba);
5750 sector = GET_CL() & 0x003f;
5751 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
5752 head = GET_DH();
5753 nbsectors = GET_AL();
5754 segment = ES;
5755 offset = BX;
5757 // no sector to read ?
5758 if(nbsectors==0) goto int13_success;
5760 // sanity checks sco openserver needs this!
5761 if ((sector > vspt)
5762 || (cylinder >= vcylinders)
5763 || (head >= vheads)) {
5764 goto int13_fail;
5767 // After controls, verify do nothing
5768 if (GET_AH() == 0x04) goto int13_success;
5770 segment = ES+(BX / 16);
5771 offset = BX % 16;
5773 // calculate the virtual lba inside the image
5774 vlba=((((Bit32u)cylinder*(Bit32u)vheads)+(Bit32u)head)*(Bit32u)vspt)+((Bit32u)(sector-1));
5776 // In advance so we don't loose the count
5777 SET_AL(nbsectors);
5779 // start lba on cd
5780 slba = (Bit32u)vlba/4;
5781 before= (Bit16u)vlba%4;
5783 // end lba on cd
5784 elba = (Bit32u)(vlba+nbsectors-1)/4;
5786 memsetb(get_SS(),atacmd,0,12);
5787 atacmd[0]=0x28; // READ command
5788 atacmd[7]=((Bit16u)(elba-slba+1) & 0xff00) >> 8; // Sectors
5789 atacmd[8]=((Bit16u)(elba-slba+1) & 0x00ff); // Sectors
5790 atacmd[2]=(ilba+slba & 0xff000000) >> 24; // LBA
5791 atacmd[3]=(ilba+slba & 0x00ff0000) >> 16;
5792 atacmd[4]=(ilba+slba & 0x0000ff00) >> 8;
5793 atacmd[5]=(ilba+slba & 0x000000ff);
5794 if((status = ata_cmd_packet(device, 12, get_SS(), atacmd, before*512, nbsectors*512L, ATA_DATA_IN, segment,offset)) != 0) {
5795 BX_INFO("int13_cdemu: function %02x, error %02x !\n",GET_AH(),status);
5796 SET_AH(0x02);
5797 SET_AL(0);
5798 goto int13_fail_noah;
5801 goto int13_success;
5802 break;
5804 case 0x08: /* read disk drive parameters */
5805 vspt=read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
5806 vcylinders=read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders) - 1;
5807 vheads=read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads) - 1;
5809 SET_AL( 0x00 );
5810 SET_BL( 0x00 );
5811 SET_CH( vcylinders & 0xff );
5812 SET_CL((( vcylinders >> 2) & 0xc0) | ( vspt & 0x3f ));
5813 SET_DH( vheads );
5814 SET_DL( 0x02 ); // FIXME ElTorito Various. should send the real count of drives 1 or 2
5815 // FIXME ElTorito Harddisk. should send the HD count
5817 switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
5818 case 0x01: SET_BL( 0x02 ); break;
5819 case 0x02: SET_BL( 0x04 ); break;
5820 case 0x03: SET_BL( 0x06 ); break;
5823 ASM_START
5824 push bp
5825 mov bp, sp
5826 mov ax, #diskette_param_table2
5827 mov _int13_cdemu.DI+2[bp], ax
5828 mov _int13_cdemu.ES+2[bp], cs
5829 pop bp
5830 ASM_END
5831 goto int13_success;
5832 break;
5834 case 0x15: /* read disk drive size */
5835 // FIXME ElTorito Harddisk. What geometry to send ?
5836 SET_AH(0x03);
5837 goto int13_success_noah;
5838 break;
5840 // all those functions return unimplemented
5841 case 0x0a: /* read disk sectors with ECC */
5842 case 0x0b: /* write disk sectors with ECC */
5843 case 0x18: /* set media type for format */
5844 case 0x41: // IBM/MS installation check
5845 // FIXME ElTorito Harddisk. Darwin would like to use EDD
5846 case 0x42: // IBM/MS extended read
5847 case 0x43: // IBM/MS extended write
5848 case 0x44: // IBM/MS verify sectors
5849 case 0x45: // IBM/MS lock/unlock drive
5850 case 0x46: // IBM/MS eject media
5851 case 0x47: // IBM/MS extended seek
5852 case 0x48: // IBM/MS get drive parameters
5853 case 0x49: // IBM/MS extended media change
5854 case 0x4e: // ? - set hardware configuration
5855 case 0x50: // ? - send packet command
5856 default:
5857 BX_INFO("int13_cdemu function AH=%02x unsupported, returns fail\n", GET_AH());
5858 goto int13_fail;
5859 break;
5862 int13_fail:
5863 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
5864 int13_fail_noah:
5865 SET_DISK_RET_STATUS(GET_AH());
5866 int13_fail_nostatus:
5867 SET_CF(); // error occurred
5868 return;
5870 int13_success:
5871 SET_AH(0x00); // no error
5872 int13_success_noah:
5873 SET_DISK_RET_STATUS(0x00);
5874 CLEAR_CF(); // no error
5875 return;
5878 // ---------------------------------------------------------------------------
5879 // End of int13 when emulating a device from the cd
5880 // ---------------------------------------------------------------------------
5882 #endif // BX_ELTORITO_BOOT
5884 #else //BX_USE_ATADRV
5886 void
5887 outLBA(cylinder,hd_heads,head,hd_sectors,sector,dl)
5888 Bit16u cylinder;
5889 Bit16u hd_heads;
5890 Bit16u head;
5891 Bit16u hd_sectors;
5892 Bit16u sector;
5893 Bit16u dl;
5895 ASM_START
5896 push bp
5897 mov bp, sp
5898 push eax
5899 push ebx
5900 push edx
5901 xor eax,eax
5902 mov ax,4[bp] // cylinder
5903 xor ebx,ebx
5904 mov bl,6[bp] // hd_heads
5905 imul ebx
5907 mov bl,8[bp] // head
5908 add eax,ebx
5909 mov bl,10[bp] // hd_sectors
5910 imul ebx
5911 mov bl,12[bp] // sector
5912 add eax,ebx
5914 dec eax
5915 mov dx,#0x1f3
5916 out dx,al
5917 mov dx,#0x1f4
5918 mov al,ah
5919 out dx,al
5920 shr eax,#16
5921 mov dx,#0x1f5
5922 out dx,al
5923 and ah,#0xf
5924 mov bl,14[bp] // dl
5925 and bl,#1
5926 shl bl,#4
5927 or ah,bl
5928 or ah,#0xe0
5929 mov al,ah
5930 mov dx,#0x01f6
5931 out dx,al
5932 pop edx
5933 pop ebx
5934 pop eax
5935 pop bp
5936 ASM_END
5939 void
5940 int13_harddisk(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
5941 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
5943 Bit8u drive, num_sectors, sector, head, status, mod;
5944 Bit8u drive_map;
5945 Bit8u n_drives;
5946 Bit16u cyl_mod, ax;
5947 Bit16u max_cylinder, cylinder, total_sectors;
5948 Bit16u hd_cylinders;
5949 Bit8u hd_heads, hd_sectors;
5950 Bit16u val16;
5951 Bit8u sector_count;
5952 unsigned int i;
5953 Bit16u tempbx;
5954 Bit16u dpsize;
5956 Bit16u count, segment, offset;
5957 Bit32u lba;
5958 Bit16u error;
5960 BX_DEBUG_INT13_HD("int13 harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
5962 write_byte(0x0040, 0x008e, 0); // clear completion flag
5964 /* at this point, DL is >= 0x80 to be passed from the floppy int13h
5965 handler code */
5966 /* check how many disks first (cmos reg 0x12), return an error if
5967 drive not present */
5968 drive_map = inb_cmos(0x12);
5969 drive_map = (((drive_map & 0xf0)==0) ? 0 : 1) |
5970 (((drive_map & 0x0f)==0) ? 0 : 2);
5971 n_drives = (drive_map==0) ? 0 :
5972 ((drive_map==3) ? 2 : 1);
5974 if (!(drive_map & (1<<(GET_ELDL()&0x7f)))) { /* allow 0, 1, or 2 disks */
5975 SET_AH(0x01);
5976 SET_DISK_RET_STATUS(0x01);
5977 SET_CF(); /* error occurred */
5978 return;
5981 switch (GET_AH()) {
5983 case 0x00: /* disk controller reset */
5984 BX_DEBUG_INT13_HD("int13_f00\n");
5986 SET_AH(0);
5987 SET_DISK_RET_STATUS(0);
5988 set_diskette_ret_status(0);
5989 set_diskette_current_cyl(0, 0); /* current cylinder, diskette 1 */
5990 set_diskette_current_cyl(1, 0); /* current cylinder, diskette 2 */
5991 CLEAR_CF(); /* successful */
5992 return;
5993 break;
5995 case 0x01: /* read disk status */
5996 BX_DEBUG_INT13_HD("int13_f01\n");
5997 status = read_byte(0x0040, 0x0074);
5998 SET_AH(status);
5999 SET_DISK_RET_STATUS(0);
6000 /* set CF if error status read */
6001 if (status) SET_CF();
6002 else CLEAR_CF();
6003 return;
6004 break;
6006 case 0x04: // verify disk sectors
6007 case 0x02: // read disk sectors
6008 drive = GET_ELDL();
6009 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6011 num_sectors = GET_AL();
6012 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
6013 sector = (GET_CL() & 0x3f);
6014 head = GET_DH();
6017 if (hd_cylinders > 1024) {
6018 if (hd_cylinders <= 2048) {
6019 cylinder <<= 1;
6021 else if (hd_cylinders <= 4096) {
6022 cylinder <<= 2;
6024 else if (hd_cylinders <= 8192) {
6025 cylinder <<= 3;
6027 else { // hd_cylinders <= 16384
6028 cylinder <<= 4;
6031 ax = head / hd_heads;
6032 cyl_mod = ax & 0xff;
6033 head = ax >> 8;
6034 cylinder |= cyl_mod;
6037 if ( (cylinder >= hd_cylinders) ||
6038 (sector > hd_sectors) ||
6039 (head >= hd_heads) ) {
6040 SET_AH(1);
6041 SET_DISK_RET_STATUS(1);
6042 SET_CF(); /* error occurred */
6043 return;
6046 if ( (num_sectors > 128) || (num_sectors == 0) )
6047 BX_PANIC("int13_harddisk(): num_sectors out of range!\n");
6049 if (head > 15)
6050 BX_PANIC("hard drive BIOS:(read/verify) head > 15\n");
6052 if ( GET_AH() == 0x04 ) {
6053 SET_AH(0);
6054 SET_DISK_RET_STATUS(0);
6055 CLEAR_CF();
6056 return;
6059 status = inb(0x1f7);
6060 if (status & 0x80) {
6061 BX_PANIC("hard drive BIOS:(read/verify) BUSY bit set\n");
6063 outb(0x01f2, num_sectors);
6064 /* activate LBA? (tomv) */
6065 if (hd_heads > 16) {
6066 BX_DEBUG_INT13_HD("CHS: %x %x %x\n", cylinder, head, sector);
6067 outLBA(cylinder,hd_heads,head,hd_sectors,sector,drive);
6069 else {
6070 outb(0x01f3, sector);
6071 outb(0x01f4, cylinder & 0x00ff);
6072 outb(0x01f5, cylinder >> 8);
6073 outb(0x01f6, 0xa0 | ((drive & 0x01)<<4) | (head & 0x0f));
6075 outb(0x01f7, 0x20);
6077 while (1) {
6078 status = inb(0x1f7);
6079 if ( !(status & 0x80) ) break;
6082 if (status & 0x01) {
6083 BX_PANIC("hard drive BIOS:(read/verify) read error\n");
6084 } else if ( !(status & 0x08) ) {
6085 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
6086 BX_PANIC("hard drive BIOS:(read/verify) expected DRQ=1\n");
6089 sector_count = 0;
6090 tempbx = BX;
6092 ASM_START
6093 sti ;; enable higher priority interrupts
6094 ASM_END
6096 while (1) {
6097 ASM_START
6098 ;; store temp bx in real DI register
6099 push bp
6100 mov bp, sp
6101 mov di, _int13_harddisk.tempbx + 2 [bp]
6102 pop bp
6104 ;; adjust if there will be an overrun
6105 cmp di, #0xfe00
6106 jbe i13_f02_no_adjust
6107 i13_f02_adjust:
6108 sub di, #0x0200 ; sub 512 bytes from offset
6109 mov ax, es
6110 add ax, #0x0020 ; add 512 to segment
6111 mov es, ax
6113 i13_f02_no_adjust:
6114 mov cx, #0x0100 ;; counter (256 words = 512b)
6115 mov dx, #0x01f0 ;; AT data read port
6117 rep
6118 insw ;; CX words transfered from port(DX) to ES:[DI]
6120 i13_f02_done:
6121 ;; store real DI register back to temp bx
6122 push bp
6123 mov bp, sp
6124 mov _int13_harddisk.tempbx + 2 [bp], di
6125 pop bp
6126 ASM_END
6128 sector_count++;
6129 num_sectors--;
6130 if (num_sectors == 0) {
6131 status = inb(0x1f7);
6132 if ( (status & 0xc9) != 0x40 )
6133 BX_PANIC("no sectors left to read/verify, status is %02x\n", (unsigned) status);
6134 break;
6136 else {
6137 status = inb(0x1f7);
6138 if ( (status & 0xc9) != 0x48 )
6139 BX_PANIC("more sectors left to read/verify, status is %02x\n", (unsigned) status);
6140 continue;
6144 SET_AH(0);
6145 SET_DISK_RET_STATUS(0);
6146 SET_AL(sector_count);
6147 CLEAR_CF(); /* successful */
6148 return;
6149 break;
6152 case 0x03: /* write disk sectors */
6153 BX_DEBUG_INT13_HD("int13_f03\n");
6154 drive = GET_ELDL ();
6155 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6157 num_sectors = GET_AL();
6158 cylinder = GET_CH();
6159 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
6160 sector = (GET_CL() & 0x3f);
6161 head = GET_DH();
6163 if (hd_cylinders > 1024) {
6164 if (hd_cylinders <= 2048) {
6165 cylinder <<= 1;
6167 else if (hd_cylinders <= 4096) {
6168 cylinder <<= 2;
6170 else if (hd_cylinders <= 8192) {
6171 cylinder <<= 3;
6173 else { // hd_cylinders <= 16384
6174 cylinder <<= 4;
6177 ax = head / hd_heads;
6178 cyl_mod = ax & 0xff;
6179 head = ax >> 8;
6180 cylinder |= cyl_mod;
6183 if ( (cylinder >= hd_cylinders) ||
6184 (sector > hd_sectors) ||
6185 (head >= hd_heads) ) {
6186 SET_AH( 1);
6187 SET_DISK_RET_STATUS(1);
6188 SET_CF(); /* error occurred */
6189 return;
6192 if ( (num_sectors > 128) || (num_sectors == 0) )
6193 BX_PANIC("int13_harddisk(): num_sectors out of range!\n");
6195 if (head > 15)
6196 BX_PANIC("hard drive BIOS:(read) head > 15\n");
6198 status = inb(0x1f7);
6199 if (status & 0x80) {
6200 BX_PANIC("hard drive BIOS:(read) BUSY bit set\n");
6202 // should check for Drive Ready Bit also in status reg
6203 outb(0x01f2, num_sectors);
6205 /* activate LBA? (tomv) */
6206 if (hd_heads > 16) {
6207 BX_DEBUG_INT13_HD("CHS (write): %x %x %x\n", cylinder, head, sector);
6208 outLBA(cylinder,hd_heads,head,hd_sectors,sector,GET_ELDL());
6210 else {
6211 outb(0x01f3, sector);
6212 outb(0x01f4, cylinder & 0x00ff);
6213 outb(0x01f5, cylinder >> 8);
6214 outb(0x01f6, 0xa0 | ((GET_ELDL() & 0x01)<<4) | (head & 0x0f));
6216 outb(0x01f7, 0x30);
6218 // wait for busy bit to turn off after seeking
6219 while (1) {
6220 status = inb(0x1f7);
6221 if ( !(status & 0x80) ) break;
6224 if ( !(status & 0x08) ) {
6225 BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
6226 BX_PANIC("hard drive BIOS:(write) data-request bit not set\n");
6229 sector_count = 0;
6230 tempbx = BX;
6232 ASM_START
6233 sti ;; enable higher priority interrupts
6234 ASM_END
6236 while (1) {
6237 ASM_START
6238 ;; store temp bx in real SI register
6239 push bp
6240 mov bp, sp
6241 mov si, _int13_harddisk.tempbx + 2 [bp]
6242 pop bp
6244 ;; adjust if there will be an overrun
6245 cmp si, #0xfe00
6246 jbe i13_f03_no_adjust
6247 i13_f03_adjust:
6248 sub si, #0x0200 ; sub 512 bytes from offset
6249 mov ax, es
6250 add ax, #0x0020 ; add 512 to segment
6251 mov es, ax
6253 i13_f03_no_adjust:
6254 mov cx, #0x0100 ;; counter (256 words = 512b)
6255 mov dx, #0x01f0 ;; AT data read port
6257 seg ES
6258 rep
6259 outsw ;; CX words tranfered from ES:[SI] to port(DX)
6261 ;; store real SI register back to temp bx
6262 push bp
6263 mov bp, sp
6264 mov _int13_harddisk.tempbx + 2 [bp], si
6265 pop bp
6266 ASM_END
6268 sector_count++;
6269 num_sectors--;
6270 if (num_sectors == 0) {
6271 status = inb(0x1f7);
6272 if ( (status & 0xe9) != 0x40 )
6273 BX_PANIC("no sectors left to write, status is %02x\n", (unsigned) status);
6274 break;
6276 else {
6277 status = inb(0x1f7);
6278 if ( (status & 0xc9) != 0x48 )
6279 BX_PANIC("more sectors left to write, status is %02x\n", (unsigned) status);
6280 continue;
6284 SET_AH(0);
6285 SET_DISK_RET_STATUS(0);
6286 SET_AL(sector_count);
6287 CLEAR_CF(); /* successful */
6288 return;
6289 break;
6291 case 0x05: /* format disk track */
6292 BX_DEBUG_INT13_HD("int13_f05\n");
6293 BX_PANIC("format disk track called\n");
6294 /* nop */
6295 SET_AH(0);
6296 SET_DISK_RET_STATUS(0);
6297 CLEAR_CF(); /* successful */
6298 return;
6299 break;
6301 case 0x08: /* read disk drive parameters */
6302 BX_DEBUG_INT13_HD("int13_f08\n");
6304 drive = GET_ELDL ();
6305 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6307 // translate CHS
6308 //
6309 if (hd_cylinders <= 1024) {
6310 // hd_cylinders >>= 0;
6311 // hd_heads <<= 0;
6313 else if (hd_cylinders <= 2048) {
6314 hd_cylinders >>= 1;
6315 hd_heads <<= 1;
6317 else if (hd_cylinders <= 4096) {
6318 hd_cylinders >>= 2;
6319 hd_heads <<= 2;
6321 else if (hd_cylinders <= 8192) {
6322 hd_cylinders >>= 3;
6323 hd_heads <<= 3;
6325 else { // hd_cylinders <= 16384
6326 hd_cylinders >>= 4;
6327 hd_heads <<= 4;
6330 max_cylinder = hd_cylinders - 2; /* 0 based */
6331 SET_AL(0);
6332 SET_CH(max_cylinder & 0xff);
6333 SET_CL(((max_cylinder >> 2) & 0xc0) | (hd_sectors & 0x3f));
6334 SET_DH(hd_heads - 1);
6335 SET_DL(n_drives); /* returns 0, 1, or 2 hard drives */
6336 SET_AH(0);
6337 SET_DISK_RET_STATUS(0);
6338 CLEAR_CF(); /* successful */
6340 return;
6341 break;
6343 case 0x09: /* initialize drive parameters */
6344 BX_DEBUG_INT13_HD("int13_f09\n");
6345 SET_AH(0);
6346 SET_DISK_RET_STATUS(0);
6347 CLEAR_CF(); /* successful */
6348 return;
6349 break;
6351 case 0x0a: /* read disk sectors with ECC */
6352 BX_DEBUG_INT13_HD("int13_f0a\n");
6353 case 0x0b: /* write disk sectors with ECC */
6354 BX_DEBUG_INT13_HD("int13_f0b\n");
6355 BX_PANIC("int13h Functions 0Ah & 0Bh not implemented!\n");
6356 return;
6357 break;
6359 case 0x0c: /* seek to specified cylinder */
6360 BX_DEBUG_INT13_HD("int13_f0c\n");
6361 BX_INFO("int13h function 0ch (seek) not implemented!\n");
6362 SET_AH(0);
6363 SET_DISK_RET_STATUS(0);
6364 CLEAR_CF(); /* successful */
6365 return;
6366 break;
6368 case 0x0d: /* alternate disk reset */
6369 BX_DEBUG_INT13_HD("int13_f0d\n");
6370 SET_AH(0);
6371 SET_DISK_RET_STATUS(0);
6372 CLEAR_CF(); /* successful */
6373 return;
6374 break;
6376 case 0x10: /* check drive ready */
6377 BX_DEBUG_INT13_HD("int13_f10\n");
6378 //SET_AH(0);
6379 //SET_DISK_RET_STATUS(0);
6380 //CLEAR_CF(); /* successful */
6381 //return;
6382 //break;
6384 // should look at 40:8E also???
6385 status = inb(0x01f7);
6386 if ( (status & 0xc0) == 0x40 ) {
6387 SET_AH(0);
6388 SET_DISK_RET_STATUS(0);
6389 CLEAR_CF(); // drive ready
6390 return;
6392 else {
6393 SET_AH(0xAA);
6394 SET_DISK_RET_STATUS(0xAA);
6395 SET_CF(); // not ready
6396 return;
6398 break;
6400 case 0x11: /* recalibrate */
6401 BX_DEBUG_INT13_HD("int13_f11\n");
6402 SET_AH(0);
6403 SET_DISK_RET_STATUS(0);
6404 CLEAR_CF(); /* successful */
6405 return;
6406 break;
6408 case 0x14: /* controller internal diagnostic */
6409 BX_DEBUG_INT13_HD("int13_f14\n");
6410 SET_AH(0);
6411 SET_DISK_RET_STATUS(0);
6412 CLEAR_CF(); /* successful */
6413 SET_AL(0);
6414 return;
6415 break;
6417 case 0x15: /* read disk drive size */
6418 drive = GET_ELDL();
6419 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
6420 ASM_START
6421 push bp
6422 mov bp, sp
6423 mov al, _int13_harddisk.hd_heads + 2 [bp]
6424 mov ah, _int13_harddisk.hd_sectors + 2 [bp]
6425 mul al, ah ;; ax = heads * sectors
6426 mov bx, _int13_harddisk.hd_cylinders + 2 [bp]
6427 dec bx ;; use (cylinders - 1) ???
6428 mul ax, bx ;; dx:ax = (cylinders -1) * (heads * sectors)
6429 ;; now we need to move the 32bit result dx:ax to what the
6430 ;; BIOS wants which is cx:dx.
6431 ;; and then into CX:DX on the stack
6432 mov _int13_harddisk.CX + 2 [bp], dx
6433 mov _int13_harddisk.DX + 2 [bp], ax
6434 pop bp
6435 ASM_END
6436 SET_AH(3); // hard disk accessible
6437 SET_DISK_RET_STATUS(0); // ??? should this be 0
6438 CLEAR_CF(); // successful
6439 return;
6440 break;
6442 case 0x18: // set media type for format
6443 case 0x41: // IBM/MS
6444 case 0x42: // IBM/MS
6445 case 0x43: // IBM/MS
6446 case 0x44: // IBM/MS
6447 case 0x45: // IBM/MS lock/unlock drive
6448 case 0x46: // IBM/MS eject media
6449 case 0x47: // IBM/MS extended seek
6450 case 0x49: // IBM/MS extended media change
6451 case 0x50: // IBM/MS send packet command
6452 default:
6453 BX_INFO("int13_harddisk: unsupported AH=%02x\n", GET_AH());
6455 SET_AH(1); // code=invalid function in AH or invalid parameter
6456 SET_DISK_RET_STATUS(1);
6457 SET_CF(); /* unsuccessful */
6458 return;
6459 break;
6463 static char panic_msg_reg12h[] = "HD%d cmos reg 12h not type F\n";
6464 static char panic_msg_reg19h[] = "HD%d cmos reg %02xh not user definable type 47\n";
6466 void
6467 get_hd_geometry(drive, hd_cylinders, hd_heads, hd_sectors)
6468 Bit8u drive;
6469 Bit16u *hd_cylinders;
6470 Bit8u *hd_heads;
6471 Bit8u *hd_sectors;
6473 Bit8u hd_type;
6474 Bit16u ss;
6475 Bit16u cylinders;
6476 Bit8u iobase;
6478 ss = get_SS();
6479 if (drive == 0x80) {
6480 hd_type = inb_cmos(0x12) & 0xf0;
6481 if (hd_type != 0xf0)
6482 BX_INFO(panic_msg_reg12h,0);
6483 hd_type = inb_cmos(0x19); // HD0: extended type
6484 if (hd_type != 47)
6485 BX_INFO(panic_msg_reg19h,0,0x19);
6486 iobase = 0x1b;
6487 } else {
6488 hd_type = inb_cmos(0x12) & 0x0f;
6489 if (hd_type != 0x0f)
6490 BX_INFO(panic_msg_reg12h,1);
6491 hd_type = inb_cmos(0x1a); // HD0: extended type
6492 if (hd_type != 47)
6493 BX_INFO(panic_msg_reg19h,0,0x1a);
6494 iobase = 0x24;
6497 // cylinders
6498 cylinders = inb_cmos(iobase) | (inb_cmos(iobase+1) << 8);
6499 write_word(ss, hd_cylinders, cylinders);
6501 // heads
6502 write_byte(ss, hd_heads, inb_cmos(iobase+2));
6504 // sectors per track
6505 write_byte(ss, hd_sectors, inb_cmos(iobase+8));
6508 #endif //else BX_USE_ATADRV
6511 //////////////////////
6512 // FLOPPY functions //
6513 //////////////////////
6515 bx_bool
6516 floppy_media_known(drive)
6517 Bit16u drive;
6519 Bit8u val8;
6520 Bit16u media_state_offset;
6522 val8 = read_byte(0x0040, 0x003e); // diskette recal status
6523 if (drive)
6524 val8 >>= 1;
6525 val8 &= 0x01;
6526 if (val8 == 0)
6527 return(0);
6529 media_state_offset = 0x0090;
6530 if (drive)
6531 media_state_offset += 1;
6533 val8 = read_byte(0x0040, media_state_offset);
6534 val8 = (val8 >> 4) & 0x01;
6535 if (val8 == 0)
6536 return(0);
6538 // check pass, return KNOWN
6539 return(1);
6542 bx_bool
6543 floppy_media_sense(drive)
6544 Bit16u drive;
6546 bx_bool retval;
6547 Bit16u media_state_offset;
6548 Bit8u drive_type, config_data, media_state;
6550 if (floppy_drive_recal(drive) == 0) {
6551 return(0);
6554 // for now cheat and get drive type from CMOS,
6555 // assume media is same as drive type
6557 // ** config_data **
6558 // Bitfields for diskette media control:
6559 // Bit(s) Description (Table M0028)
6560 // 7-6 last data rate set by controller
6561 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6562 // 5-4 last diskette drive step rate selected
6563 // 00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
6564 // 3-2 {data rate at start of operation}
6565 // 1-0 reserved
6567 // ** media_state **
6568 // Bitfields for diskette drive media state:
6569 // Bit(s) Description (Table M0030)
6570 // 7-6 data rate
6571 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
6572 // 5 double stepping required (e.g. 360kB in 1.2MB)
6573 // 4 media type established
6574 // 3 drive capable of supporting 4MB media
6575 // 2-0 on exit from BIOS, contains
6576 // 000 trying 360kB in 360kB
6577 // 001 trying 360kB in 1.2MB
6578 // 010 trying 1.2MB in 1.2MB
6579 // 011 360kB in 360kB established
6580 // 100 360kB in 1.2MB established
6581 // 101 1.2MB in 1.2MB established
6582 // 110 reserved
6583 // 111 all other formats/drives
6585 drive_type = inb_cmos(0x10);
6586 if (drive == 0)
6587 drive_type >>= 4;
6588 else
6589 drive_type &= 0x0f;
6590 if ( drive_type == 1 ) {
6591 // 360K 5.25" drive
6592 config_data = 0x00; // 0000 0000
6593 media_state = 0x25; // 0010 0101
6594 retval = 1;
6596 else if ( drive_type == 2 ) {
6597 // 1.2 MB 5.25" drive
6598 config_data = 0x00; // 0000 0000
6599 media_state = 0x25; // 0010 0101 // need double stepping??? (bit 5)
6600 retval = 1;
6602 else if ( drive_type == 3 ) {
6603 // 720K 3.5" drive
6604 config_data = 0x00; // 0000 0000 ???
6605 media_state = 0x17; // 0001 0111
6606 retval = 1;
6608 else if ( drive_type == 4 ) {
6609 // 1.44 MB 3.5" drive
6610 config_data = 0x00; // 0000 0000
6611 media_state = 0x17; // 0001 0111
6612 retval = 1;
6614 else if ( drive_type == 5 ) {
6615 // 2.88 MB 3.5" drive
6616 config_data = 0xCC; // 1100 1100
6617 media_state = 0xD7; // 1101 0111
6618 retval = 1;
6620 //
6621 // Extended floppy size uses special cmos setting
6622 else if ( drive_type == 6 ) {
6623 // 160k 5.25" drive
6624 config_data = 0x00; // 0000 0000
6625 media_state = 0x27; // 0010 0111
6626 retval = 1;
6628 else if ( drive_type == 7 ) {
6629 // 180k 5.25" drive
6630 config_data = 0x00; // 0000 0000
6631 media_state = 0x27; // 0010 0111
6632 retval = 1;
6634 else if ( drive_type == 8 ) {
6635 // 320k 5.25" drive
6636 config_data = 0x00; // 0000 0000
6637 media_state = 0x27; // 0010 0111
6638 retval = 1;
6641 else {
6642 // not recognized
6643 config_data = 0x00; // 0000 0000
6644 media_state = 0x00; // 0000 0000
6645 retval = 0;
6648 if (drive == 0)
6649 media_state_offset = 0x90;
6650 else
6651 media_state_offset = 0x91;
6652 write_byte(0x0040, 0x008B, config_data);
6653 write_byte(0x0040, media_state_offset, media_state);
6655 return(retval);
6658 bx_bool
6659 floppy_drive_recal(drive)
6660 Bit16u drive;
6662 Bit8u val8, dor;
6663 Bit16u curr_cyl_offset;
6665 // set 40:3e bit 7 to 0
6666 val8 = read_byte(0x0000, 0x043e);
6667 val8 &= 0x7f;
6668 write_byte(0x0000, 0x043e, val8);
6670 // turn on motor of selected drive, DMA & int enabled, normal operation
6671 if (drive)
6672 dor = 0x20;
6673 else
6674 dor = 0x10;
6675 dor |= 0x0c;
6676 dor |= drive;
6677 outb(0x03f2, dor);
6679 // reset the disk motor timeout value of INT 08
6680 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
6682 // check port 3f4 for drive readiness
6683 val8 = inb(0x3f4);
6684 if ( (val8 & 0xf0) != 0x80 )
6685 BX_PANIC("floppy recal:f07: ctrl not ready\n");
6687 // send Recalibrate command (2 bytes) to controller
6688 outb(0x03f5, 0x07); // 07: Recalibrate
6689 outb(0x03f5, drive); // 0=drive0, 1=drive1
6691 // turn on interrupts
6692 ASM_START
6693 sti
6694 ASM_END
6696 // wait on 40:3e bit 7 to become 1
6697 val8 = (read_byte(0x0000, 0x043e) & 0x80);
6698 while ( val8 == 0 ) {
6699 val8 = (read_byte(0x0000, 0x043e) & 0x80);
6702 val8 = 0; // separate asm from while() loop
6703 // turn off interrupts
6704 ASM_START
6705 cli
6706 ASM_END
6708 // set 40:3e bit 7 to 0, and calibrated bit
6709 val8 = read_byte(0x0000, 0x043e);
6710 val8 &= 0x7f;
6711 if (drive) {
6712 val8 |= 0x02; // Drive 1 calibrated
6713 curr_cyl_offset = 0x0095;
6715 else {
6716 val8 |= 0x01; // Drive 0 calibrated
6717 curr_cyl_offset = 0x0094;
6719 write_byte(0x0040, 0x003e, val8);
6720 write_byte(0x0040, curr_cyl_offset, 0); // current cylinder is 0
6722 return(1);
6727 bx_bool
6728 floppy_drive_exists(drive)
6729 Bit16u drive;
6731 Bit8u drive_type;
6733 // check CMOS to see if drive exists
6734 drive_type = inb_cmos(0x10);
6735 if (drive == 0)
6736 drive_type >>= 4;
6737 else
6738 drive_type &= 0x0f;
6739 if ( drive_type == 0 )
6740 return(0);
6741 else
6742 return(1);
6745 #if BX_SUPPORT_FLOPPY
6746 void
6747 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
6748 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
6750 Bit8u drive, num_sectors, track, sector, head, status;
6751 Bit16u base_address, base_count, base_es;
6752 Bit8u page, mode_register, val8, dor;
6753 Bit8u return_status[7];
6754 Bit8u drive_type, num_floppies, ah;
6755 Bit16u es, last_addr;
6757 BX_DEBUG_INT13_FL("int13_diskette: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
6758 // BX_DEBUG_INT13_FL("int13_diskette: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), get_DS(), ES, DI, SI);
6760 ah = GET_AH();
6762 switch ( ah ) {
6763 case 0x00: // diskette controller reset
6764 BX_DEBUG_INT13_FL("floppy f00\n");
6765 drive = GET_ELDL();
6766 if (drive > 1) {
6767 SET_AH(1); // invalid param
6768 set_diskette_ret_status(1);
6769 SET_CF();
6770 return;
6772 drive_type = inb_cmos(0x10);
6774 if (drive == 0)
6775 drive_type >>= 4;
6776 else
6777 drive_type &= 0x0f;
6778 if (drive_type == 0) {
6779 SET_AH(0x80); // drive not responding
6780 set_diskette_ret_status(0x80);
6781 SET_CF();
6782 return;
6784 SET_AH(0);
6785 set_diskette_ret_status(0);
6786 CLEAR_CF(); // successful
6787 set_diskette_current_cyl(drive, 0); // current cylinder
6788 return;
6790 case 0x01: // Read Diskette Status
6791 CLEAR_CF();
6792 val8 = read_byte(0x0000, 0x0441);
6793 SET_AH(val8);
6794 if (val8) {
6795 SET_CF();
6797 return;
6799 case 0x02: // Read Diskette Sectors
6800 case 0x03: // Write Diskette Sectors
6801 case 0x04: // Verify Diskette Sectors
6802 num_sectors = GET_AL();
6803 track = GET_CH();
6804 sector = GET_CL();
6805 head = GET_DH();
6806 drive = GET_ELDL();
6808 if ( (drive > 1) || (head > 1) ||
6809 (num_sectors == 0) || (num_sectors > 72) ) {
6810 BX_INFO("floppy: drive>1 || head>1 ...\n");
6811 SET_AH(1);
6812 set_diskette_ret_status(1);
6813 SET_AL(0); // no sectors read
6814 SET_CF(); // error occurred
6815 return;
6818 // see if drive exists
6819 if (floppy_drive_exists(drive) == 0) {
6820 SET_AH(0x80); // not responding
6821 set_diskette_ret_status(0x80);
6822 SET_AL(0); // no sectors read
6823 SET_CF(); // error occurred
6824 return;
6827 // see if media in drive, and type is known
6828 if (floppy_media_known(drive) == 0) {
6829 if (floppy_media_sense(drive) == 0) {
6830 SET_AH(0x0C); // Media type not found
6831 set_diskette_ret_status(0x0C);
6832 SET_AL(0); // no sectors read
6833 SET_CF(); // error occurred
6834 return;
6838 if (ah == 0x02) {
6839 // Read Diskette Sectors
6841 //-----------------------------------
6842 // set up DMA controller for transfer
6843 //-----------------------------------
6845 // es:bx = pointer to where to place information from diskette
6846 // port 04: DMA-1 base and current address, channel 2
6847 // port 05: DMA-1 base and current count, channel 2
6848 page = (ES >> 12); // upper 4 bits
6849 base_es = (ES << 4); // lower 16bits contributed by ES
6850 base_address = base_es + BX; // lower 16 bits of address
6851 // contributed by ES:BX
6852 if ( base_address < base_es ) {
6853 // in case of carry, adjust page by 1
6854 page++;
6856 base_count = (num_sectors * 512) - 1;
6858 // check for 64K boundary overrun
6859 last_addr = base_address + base_count;
6860 if (last_addr < base_address) {
6861 SET_AH(0x09);
6862 set_diskette_ret_status(0x09);
6863 SET_AL(0); // no sectors read
6864 SET_CF(); // error occurred
6865 return;
6868 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
6869 outb(0x000a, 0x06);
6871 BX_DEBUG_INT13_FL("clear flip-flop\n");
6872 outb(0x000c, 0x00); // clear flip-flop
6873 outb(0x0004, base_address);
6874 outb(0x0004, base_address>>8);
6875 BX_DEBUG_INT13_FL("clear flip-flop\n");
6876 outb(0x000c, 0x00); // clear flip-flop
6877 outb(0x0005, base_count);
6878 outb(0x0005, base_count>>8);
6880 // port 0b: DMA-1 Mode Register
6881 mode_register = 0x46; // single mode, increment, autoinit disable,
6882 // transfer type=write, channel 2
6883 BX_DEBUG_INT13_FL("setting mode register\n");
6884 outb(0x000b, mode_register);
6886 BX_DEBUG_INT13_FL("setting page register\n");
6887 // port 81: DMA-1 Page Register, channel 2
6888 outb(0x0081, page);
6890 BX_DEBUG_INT13_FL("unmask chan 2\n");
6891 outb(0x000a, 0x02); // unmask channel 2
6893 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
6894 outb(0x000a, 0x02);
6896 //--------------------------------------
6897 // set up floppy controller for transfer
6898 //--------------------------------------
6900 // set 40:3e bit 7 to 0
6901 val8 = read_byte(0x0000, 0x043e);
6902 val8 &= 0x7f;
6903 write_byte(0x0000, 0x043e, val8);
6905 // turn on motor of selected drive, DMA & int enabled, normal operation
6906 if (drive)
6907 dor = 0x20;
6908 else
6909 dor = 0x10;
6910 dor |= 0x0c;
6911 dor |= drive;
6912 outb(0x03f2, dor);
6914 // reset the disk motor timeout value of INT 08
6915 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
6917 // check port 3f4 for drive readiness
6918 val8 = inb(0x3f4);
6919 if ( (val8 & 0xf0) != 0x80 )
6920 BX_PANIC("int13_diskette:f02: ctrl not ready\n");
6922 // send read-normal-data command (9 bytes) to controller
6923 outb(0x03f5, 0xe6); // e6: read normal data
6924 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
6925 outb(0x03f5, track);
6926 outb(0x03f5, head);
6927 outb(0x03f5, sector);
6928 outb(0x03f5, 2); // 512 byte sector size
6929 outb(0x03f5, 0); // last sector number possible on track
6930 outb(0x03f5, 0); // Gap length
6931 outb(0x03f5, 0xff); // Gap length
6933 // turn on interrupts
6934 ASM_START
6935 sti
6936 ASM_END
6938 // wait on 40:3e bit 7 to become 1
6939 val8 = (read_byte(0x0000, 0x043e) & 0x80);
6940 while ( val8 == 0 ) {
6941 val8 = (read_byte(0x0000, 0x043e) & 0x80);
6944 val8 = 0; // separate asm from while() loop
6945 // turn off interrupts
6946 ASM_START
6947 cli
6948 ASM_END
6950 // set 40:3e bit 7 to 0
6951 val8 = read_byte(0x0000, 0x043e);
6952 val8 &= 0x7f;
6953 write_byte(0x0000, 0x043e, val8);
6955 // check port 3f4 for accessibility to status bytes
6956 val8 = inb(0x3f4);
6957 if ( (val8 & 0xc0) != 0xc0 )
6958 BX_PANIC("int13_diskette: ctrl not ready\n");
6960 // read 7 return status bytes from controller
6961 // using loop index broken, have to unroll...
6962 return_status[0] = inb(0x3f5);
6963 return_status[1] = inb(0x3f5);
6964 return_status[2] = inb(0x3f5);
6965 return_status[3] = inb(0x3f5);
6966 return_status[4] = inb(0x3f5);
6967 return_status[5] = inb(0x3f5);
6968 return_status[6] = inb(0x3f5);
6969 // record in BIOS Data Area
6970 write_byte(0x0040, 0x0042, return_status[0]);
6971 write_byte(0x0040, 0x0043, return_status[1]);
6972 write_byte(0x0040, 0x0044, return_status[2]);
6973 write_byte(0x0040, 0x0045, return_status[3]);
6974 write_byte(0x0040, 0x0046, return_status[4]);
6975 write_byte(0x0040, 0x0047, return_status[5]);
6976 write_byte(0x0040, 0x0048, return_status[6]);
6978 if ( (return_status[0] & 0xc0) != 0 ) {
6979 SET_AH(0x20);
6980 set_diskette_ret_status(0x20);
6981 SET_AL(0); // no sectors read
6982 SET_CF(); // error occurred
6983 return;
6986 // ??? should track be new val from return_status[3] ?
6987 set_diskette_current_cyl(drive, track);
6988 // AL = number of sectors read (same value as passed)
6989 SET_AH(0x00); // success
6990 CLEAR_CF(); // success
6991 return;
6993 else if (ah == 0x03) {
6994 // Write Diskette Sectors
6996 //-----------------------------------
6997 // set up DMA controller for transfer
6998 //-----------------------------------
7000 // es:bx = pointer to where to place information from diskette
7001 // port 04: DMA-1 base and current address, channel 2
7002 // port 05: DMA-1 base and current count, channel 2
7003 page = (ES >> 12); // upper 4 bits
7004 base_es = (ES << 4); // lower 16bits contributed by ES
7005 base_address = base_es + BX; // lower 16 bits of address
7006 // contributed by ES:BX
7007 if ( base_address < base_es ) {
7008 // in case of carry, adjust page by 1
7009 page++;
7011 base_count = (num_sectors * 512) - 1;
7013 // check for 64K boundary overrun
7014 last_addr = base_address + base_count;
7015 if (last_addr < base_address) {
7016 SET_AH(0x09);
7017 set_diskette_ret_status(0x09);
7018 SET_AL(0); // no sectors read
7019 SET_CF(); // error occurred
7020 return;
7023 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
7024 outb(0x000a, 0x06);
7026 outb(0x000c, 0x00); // clear flip-flop
7027 outb(0x0004, base_address);
7028 outb(0x0004, base_address>>8);
7029 outb(0x000c, 0x00); // clear flip-flop
7030 outb(0x0005, base_count);
7031 outb(0x0005, base_count>>8);
7033 // port 0b: DMA-1 Mode Register
7034 mode_register = 0x4a; // single mode, increment, autoinit disable,
7035 // transfer type=read, channel 2
7036 outb(0x000b, mode_register);
7038 // port 81: DMA-1 Page Register, channel 2
7039 outb(0x0081, page);
7041 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
7042 outb(0x000a, 0x02);
7044 //--------------------------------------
7045 // set up floppy controller for transfer
7046 //--------------------------------------
7048 // set 40:3e bit 7 to 0
7049 val8 = read_byte(0x0000, 0x043e);
7050 val8 &= 0x7f;
7051 write_byte(0x0000, 0x043e, val8);
7053 // turn on motor of selected drive, DMA & int enabled, normal operation
7054 if (drive)
7055 dor = 0x20;
7056 else
7057 dor = 0x10;
7058 dor |= 0x0c;
7059 dor |= drive;
7060 outb(0x03f2, dor);
7062 // reset the disk motor timeout value of INT 08
7063 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
7065 // check port 3f4 for drive readiness
7066 val8 = inb(0x3f4);
7067 if ( (val8 & 0xf0) != 0x80 )
7068 BX_PANIC("int13_diskette:f03: ctrl not ready\n");
7070 // send read-normal-data command (9 bytes) to controller
7071 outb(0x03f5, 0xc5); // c5: write normal data
7072 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7073 outb(0x03f5, track);
7074 outb(0x03f5, head);
7075 outb(0x03f5, sector);
7076 outb(0x03f5, 2); // 512 byte sector size
7077 outb(0x03f5, 0); // last sector number possible on track
7078 outb(0x03f5, 0); // Gap length
7079 outb(0x03f5, 0xff); // Gap length
7081 // turn on interrupts
7082 ASM_START
7083 sti
7084 ASM_END
7086 // wait on 40:3e bit 7 to become 1
7087 val8 = (read_byte(0x0000, 0x043e) & 0x80);
7088 while ( val8 == 0 ) {
7089 val8 = (read_byte(0x0000, 0x043e) & 0x80);
7092 val8 = 0; // separate asm from while() loop
7093 // turn off interrupts
7094 ASM_START
7095 cli
7096 ASM_END
7098 // set 40:3e bit 7 to 0
7099 val8 = read_byte(0x0000, 0x043e);
7100 val8 &= 0x7f;
7101 write_byte(0x0000, 0x043e, val8);
7103 // check port 3f4 for accessibility to status bytes
7104 val8 = inb(0x3f4);
7105 if ( (val8 & 0xc0) != 0xc0 )
7106 BX_PANIC("int13_diskette: ctrl not ready\n");
7108 // read 7 return status bytes from controller
7109 // using loop index broken, have to unroll...
7110 return_status[0] = inb(0x3f5);
7111 return_status[1] = inb(0x3f5);
7112 return_status[2] = inb(0x3f5);
7113 return_status[3] = inb(0x3f5);
7114 return_status[4] = inb(0x3f5);
7115 return_status[5] = inb(0x3f5);
7116 return_status[6] = inb(0x3f5);
7117 // record in BIOS Data Area
7118 write_byte(0x0040, 0x0042, return_status[0]);
7119 write_byte(0x0040, 0x0043, return_status[1]);
7120 write_byte(0x0040, 0x0044, return_status[2]);
7121 write_byte(0x0040, 0x0045, return_status[3]);
7122 write_byte(0x0040, 0x0046, return_status[4]);
7123 write_byte(0x0040, 0x0047, return_status[5]);
7124 write_byte(0x0040, 0x0048, return_status[6]);
7126 if ( (return_status[0] & 0xc0) != 0 ) {
7127 if ( (return_status[1] & 0x02) != 0 ) {
7128 // diskette not writable.
7129 // AH=status code=0x03 (tried to write on write-protected disk)
7130 // AL=number of sectors written=0
7131 AX = 0x0300;
7132 SET_CF();
7133 return;
7134 } else {
7135 BX_PANIC("int13_diskette_function: read error\n");
7139 // ??? should track be new val from return_status[3] ?
7140 set_diskette_current_cyl(drive, track);
7141 // AL = number of sectors read (same value as passed)
7142 SET_AH(0x00); // success
7143 CLEAR_CF(); // success
7144 return;
7146 else { // if (ah == 0x04)
7147 // Verify Diskette Sectors
7149 // ??? should track be new val from return_status[3] ?
7150 set_diskette_current_cyl(drive, track);
7151 // AL = number of sectors verified (same value as passed)
7152 CLEAR_CF(); // success
7153 SET_AH(0x00); // success
7154 return;
7158 case 0x05: // format diskette track
7159 BX_DEBUG_INT13_FL("floppy f05\n");
7161 num_sectors = GET_AL();
7162 track = GET_CH();
7163 head = GET_DH();
7164 drive = GET_ELDL();
7166 if ((drive > 1) || (head > 1) || (track > 79) ||
7167 (num_sectors == 0) || (num_sectors > 18)) {
7168 SET_AH(1);
7169 set_diskette_ret_status(1);
7170 SET_CF(); // error occurred
7173 // see if drive exists
7174 if (floppy_drive_exists(drive) == 0) {
7175 SET_AH(0x80); // drive not responding
7176 set_diskette_ret_status(0x80);
7177 SET_CF(); // error occurred
7178 return;
7181 // see if media in drive, and type is known
7182 if (floppy_media_known(drive) == 0) {
7183 if (floppy_media_sense(drive) == 0) {
7184 SET_AH(0x0C); // Media type not found
7185 set_diskette_ret_status(0x0C);
7186 SET_AL(0); // no sectors read
7187 SET_CF(); // error occurred
7188 return;
7192 // set up DMA controller for transfer
7193 page = (ES >> 12); // upper 4 bits
7194 base_es = (ES << 4); // lower 16bits contributed by ES
7195 base_address = base_es + BX; // lower 16 bits of address
7196 // contributed by ES:BX
7197 if ( base_address < base_es ) {
7198 // in case of carry, adjust page by 1
7199 page++;
7201 base_count = (num_sectors * 4) - 1;
7203 // check for 64K boundary overrun
7204 last_addr = base_address + base_count;
7205 if (last_addr < base_address) {
7206 SET_AH(0x09);
7207 set_diskette_ret_status(0x09);
7208 SET_AL(0); // no sectors read
7209 SET_CF(); // error occurred
7210 return;
7213 outb(0x000a, 0x06);
7214 outb(0x000c, 0x00); // clear flip-flop
7215 outb(0x0004, base_address);
7216 outb(0x0004, base_address>>8);
7217 outb(0x000c, 0x00); // clear flip-flop
7218 outb(0x0005, base_count);
7219 outb(0x0005, base_count>>8);
7220 mode_register = 0x4a; // single mode, increment, autoinit disable,
7221 // transfer type=read, channel 2
7222 outb(0x000b, mode_register);
7223 // port 81: DMA-1 Page Register, channel 2
7224 outb(0x0081, page);
7225 outb(0x000a, 0x02);
7227 // set up floppy controller for transfer
7228 val8 = read_byte(0x0000, 0x043e);
7229 val8 &= 0x7f;
7230 write_byte(0x0000, 0x043e, val8);
7231 // turn on motor of selected drive, DMA & int enabled, normal operation
7232 if (drive)
7233 dor = 0x20;
7234 else
7235 dor = 0x10;
7236 dor |= 0x0c;
7237 dor |= drive;
7238 outb(0x03f2, dor);
7240 // reset the disk motor timeout value of INT 08
7241 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
7243 // check port 3f4 for drive readiness
7244 val8 = inb(0x3f4);
7245 if ( (val8 & 0xf0) != 0x80 )
7246 BX_PANIC("int13_diskette:f05: ctrl not ready\n");
7248 // send read-normal-data command (6 bytes) to controller
7249 outb(0x03f5, 0x4d); // 4d: format track
7250 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
7251 outb(0x03f5, 2); // 512 byte sector size
7252 outb(0x03f5, num_sectors); // number of sectors per track
7253 outb(0x03f5, 0); // Gap length
7254 outb(0x03f5, 0xf6); // Fill byte
7255 // turn on interrupts
7256 ASM_START
7257 sti
7258 ASM_END
7259 // wait on 40:3e bit 7 to become 1
7260 val8 = (read_byte(0x0000, 0x043e) & 0x80);
7261 while ( val8 == 0 ) {
7262 val8 = (read_byte(0x0000, 0x043e) & 0x80);
7264 val8 = 0; // separate asm from while() loop
7265 // turn off interrupts
7266 ASM_START
7267 cli
7268 ASM_END
7269 // set 40:3e bit 7 to 0
7270 val8 = read_byte(0x0000, 0x043e);
7271 val8 &= 0x7f;
7272 write_byte(0x0000, 0x043e, val8);
7273 // check port 3f4 for accessibility to status bytes
7274 val8 = inb(0x3f4);
7275 if ( (val8 & 0xc0) != 0xc0 )
7276 BX_PANIC("int13_diskette: ctrl not ready\n");
7278 // read 7 return status bytes from controller
7279 // using loop index broken, have to unroll...
7280 return_status[0] = inb(0x3f5);
7281 return_status[1] = inb(0x3f5);
7282 return_status[2] = inb(0x3f5);
7283 return_status[3] = inb(0x3f5);
7284 return_status[4] = inb(0x3f5);
7285 return_status[5] = inb(0x3f5);
7286 return_status[6] = inb(0x3f5);
7287 // record in BIOS Data Area
7288 write_byte(0x0040, 0x0042, return_status[0]);
7289 write_byte(0x0040, 0x0043, return_status[1]);
7290 write_byte(0x0040, 0x0044, return_status[2]);
7291 write_byte(0x0040, 0x0045, return_status[3]);
7292 write_byte(0x0040, 0x0046, return_status[4]);
7293 write_byte(0x0040, 0x0047, return_status[5]);
7294 write_byte(0x0040, 0x0048, return_status[6]);
7296 if ( (return_status[0] & 0xc0) != 0 ) {
7297 if ( (return_status[1] & 0x02) != 0 ) {
7298 // diskette not writable.
7299 // AH=status code=0x03 (tried to write on write-protected disk)
7300 // AL=number of sectors written=0
7301 AX = 0x0300;
7302 SET_CF();
7303 return;
7304 } else {
7305 BX_PANIC("int13_diskette_function: write error\n");
7309 SET_AH(0);
7310 set_diskette_ret_status(0);
7311 set_diskette_current_cyl(drive, 0);
7312 CLEAR_CF(); // successful
7313 return;
7316 case 0x08: // read diskette drive parameters
7317 BX_DEBUG_INT13_FL("floppy f08\n");
7318 drive = GET_ELDL();
7320 if (drive > 1) {
7321 AX = 0;
7322 BX = 0;
7323 CX = 0;
7324 DX = 0;
7325 ES = 0;
7326 DI = 0;
7327 SET_DL(num_floppies);
7328 SET_CF();
7329 return;
7332 drive_type = inb_cmos(0x10);
7333 num_floppies = 0;
7334 if (drive_type & 0xf0)
7335 num_floppies++;
7336 if (drive_type & 0x0f)
7337 num_floppies++;
7339 if (drive == 0)
7340 drive_type >>= 4;
7341 else
7342 drive_type &= 0x0f;
7344 SET_BH(0);
7345 SET_BL(drive_type);
7346 SET_AH(0);
7347 SET_AL(0);
7348 SET_DL(num_floppies);
7350 switch (drive_type) {
7351 case 0: // none
7352 CX = 0;
7353 SET_DH(0); // max head #
7354 break;
7356 case 1: // 360KB, 5.25"
7357 CX = 0x2709; // 40 tracks, 9 sectors
7358 SET_DH(1); // max head #
7359 break;
7361 case 2: // 1.2MB, 5.25"
7362 CX = 0x4f0f; // 80 tracks, 15 sectors
7363 SET_DH(1); // max head #
7364 break;
7366 case 3: // 720KB, 3.5"
7367 CX = 0x4f09; // 80 tracks, 9 sectors
7368 SET_DH(1); // max head #
7369 break;
7371 case 4: // 1.44MB, 3.5"
7372 CX = 0x4f12; // 80 tracks, 18 sectors
7373 SET_DH(1); // max head #
7374 break;
7376 case 5: // 2.88MB, 3.5"
7377 CX = 0x4f24; // 80 tracks, 36 sectors
7378 SET_DH(1); // max head #
7379 break;
7381 case 6: // 160k, 5.25"
7382 CX = 0x2708; // 40 tracks, 8 sectors
7383 SET_DH(0); // max head #
7384 break;
7386 case 7: // 180k, 5.25"
7387 CX = 0x2709; // 40 tracks, 9 sectors
7388 SET_DH(0); // max head #
7389 break;
7391 case 8: // 320k, 5.25"
7392 CX = 0x2708; // 40 tracks, 8 sectors
7393 SET_DH(1); // max head #
7394 break;
7396 default: // ?
7397 BX_PANIC("floppy: int13: bad floppy type\n");
7400 /* set es & di to point to 11 byte diskette param table in ROM */
7401 ASM_START
7402 push bp
7403 mov bp, sp
7404 mov ax, #diskette_param_table2
7405 mov _int13_diskette_function.DI+2[bp], ax
7406 mov _int13_diskette_function.ES+2[bp], cs
7407 pop bp
7408 ASM_END
7409 CLEAR_CF(); // success
7410 /* disk status not changed upon success */
7411 return;
7414 case 0x15: // read diskette drive type
7415 BX_DEBUG_INT13_FL("floppy f15\n");
7416 drive = GET_ELDL();
7417 if (drive > 1) {
7418 SET_AH(0); // only 2 drives supported
7419 // set_diskette_ret_status here ???
7420 SET_CF();
7421 return;
7423 drive_type = inb_cmos(0x10);
7425 if (drive == 0)
7426 drive_type >>= 4;
7427 else
7428 drive_type &= 0x0f;
7429 CLEAR_CF(); // successful, not present
7430 if (drive_type==0) {
7431 SET_AH(0); // drive not present
7433 else {
7434 SET_AH(1); // drive present, does not support change line
7437 return;
7439 case 0x16: // get diskette change line status
7440 BX_DEBUG_INT13_FL("floppy f16\n");
7441 drive = GET_ELDL();
7442 if (drive > 1) {
7443 SET_AH(0x01); // invalid drive
7444 set_diskette_ret_status(0x01);
7445 SET_CF();
7446 return;
7449 SET_AH(0x06); // change line not supported
7450 set_diskette_ret_status(0x06);
7451 SET_CF();
7452 return;
7454 case 0x17: // set diskette type for format(old)
7455 BX_DEBUG_INT13_FL("floppy f17\n");
7456 /* not used for 1.44M floppies */
7457 SET_AH(0x01); // not supported
7458 set_diskette_ret_status(1); /* not supported */
7459 SET_CF();
7460 return;
7462 case 0x18: // set diskette type for format(new)
7463 BX_DEBUG_INT13_FL("floppy f18\n");
7464 SET_AH(0x01); // do later
7465 set_diskette_ret_status(1);
7466 SET_CF();
7467 return;
7469 default:
7470 BX_INFO("int13_diskette: unsupported AH=%02x\n", GET_AH());
7472 // if ( (ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e) ) {
7473 SET_AH(0x01); // ???
7474 set_diskette_ret_status(1);
7475 SET_CF();
7476 return;
7477 // }
7480 #else // #if BX_SUPPORT_FLOPPY
7481 void
7482 int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
7483 Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
7485 Bit8u val8;
7487 switch ( GET_AH() ) {
7489 case 0x01: // Read Diskette Status
7490 CLEAR_CF();
7491 val8 = read_byte(0x0000, 0x0441);
7492 SET_AH(val8);
7493 if (val8) {
7494 SET_CF();
7496 return;
7498 default:
7499 SET_CF();
7500 write_byte(0x0000, 0x0441, 0x01);
7501 SET_AH(0x01);
7504 #endif // #if BX_SUPPORT_FLOPPY
7506 void
7507 set_diskette_ret_status(value)
7508 Bit8u value;
7510 write_byte(0x0040, 0x0041, value);
7513 void
7514 set_diskette_current_cyl(drive, cyl)
7515 Bit8u drive;
7516 Bit8u cyl;
7518 if (drive > 1)
7519 BX_PANIC("set_diskette_current_cyl(): drive > 1\n");
7520 write_byte(0x0040, 0x0094+drive, cyl);
7523 void
7524 determine_floppy_media(drive)
7525 Bit16u drive;
7527 #if 0
7528 Bit8u val8, DOR, ctrl_info;
7530 ctrl_info = read_byte(0x0040, 0x008F);
7531 if (drive==1)
7532 ctrl_info >>= 4;
7533 else
7534 ctrl_info &= 0x0f;
7536 #if 0
7537 if (drive == 0) {
7538 DOR = 0x1c; // DOR: drive0 motor on, DMA&int enabled, normal op, drive select 0
7540 else {
7541 DOR = 0x2d; // DOR: drive1 motor on, DMA&int enabled, normal op, drive select 1
7543 #endif
7545 if ( (ctrl_info & 0x04) != 0x04 ) {
7546 // Drive not determined means no drive exists, done.
7547 return;
7550 #if 0
7551 // check Main Status Register for readiness
7552 val8 = inb(0x03f4) & 0x80; // Main Status Register
7553 if (val8 != 0x80)
7554 BX_PANIC("d_f_m: MRQ bit not set\n");
7556 // change line
7558 // existing BDA values
7560 // turn on drive motor
7561 outb(0x03f2, DOR); // Digital Output Register
7562 //
7563 #endif
7564 BX_PANIC("d_f_m: OK so far\n");
7565 #endif
7568 void
7569 int17_function(regs, ds, iret_addr)
7570 pusha_regs_t regs; // regs pushed from PUSHA instruction
7571 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7572 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7574 Bit16u addr,timeout;
7575 Bit8u val8;
7577 ASM_START
7578 sti
7579 ASM_END
7581 addr = read_word(0x0040, (regs.u.r16.dx << 1) + 8);
7582 if ((regs.u.r8.ah < 3) && (regs.u.r16.dx < 3) && (addr > 0)) {
7583 timeout = read_byte(0x0040, 0x0078 + regs.u.r16.dx) << 8;
7584 if (regs.u.r8.ah == 0) {
7585 outb(addr, regs.u.r8.al);
7586 val8 = inb(addr+2);
7587 outb(addr+2, val8 | 0x01); // send strobe
7588 ASM_START
7589 nop
7590 ASM_END
7591 outb(addr+2, val8 & ~0x01);
7592 while (((inb(addr+1) & 0x40) == 0x40) && (timeout)) {
7593 timeout--;
7596 if (regs.u.r8.ah == 1) {
7597 val8 = inb(addr+2);
7598 outb(addr+2, val8 & ~0x04); // send init
7599 ASM_START
7600 nop
7601 ASM_END
7602 outb(addr+2, val8 | 0x04);
7604 val8 = inb(addr+1);
7605 regs.u.r8.ah = (val8 ^ 0x48);
7606 if (!timeout) regs.u.r8.ah |= 0x01;
7607 ClearCF(iret_addr.flags);
7608 } else {
7609 SetCF(iret_addr.flags); // Unsupported
7613 void
7614 int18_function(seq_nr)
7615 Bit16u seq_nr;
7617 Bit16u ebda_seg=read_word(0x0040,0x000E);
7618 Bit16u bootdev;
7619 Bit8u bootdrv;
7620 Bit8u bootchk;
7621 Bit16u bootseg;
7622 Bit16u bootip;
7623 Bit16u status;
7625 struct ipl_entry e;
7627 // if BX_ELTORITO_BOOT is not defined, old behavior
7628 // check bit 5 in CMOS reg 0x2d. load either 0x00 or 0x80 into DL
7629 // in preparation for the intial INT 13h (0=floppy A:, 0x80=C:)
7630 // 0: system boot sequence, first drive C: then A:
7631 // 1: system boot sequence, first drive A: then C:
7632 // else BX_ELTORITO_BOOT is defined
7633 // CMOS regs 0x3D and 0x38 contain the boot sequence:
7634 // CMOS reg 0x3D & 0x0f : 1st boot device
7635 // CMOS reg 0x3D & 0xf0 : 2nd boot device
7636 // CMOS reg 0x38 & 0xf0 : 3rd boot device
7637 // boot device codes:
7638 // 0x00 : not defined
7639 // 0x01 : first floppy
7640 // 0x02 : first harddrive
7641 // 0x03 : first cdrom
7642 // 0x04 - 0x0f : PnP expansion ROMs (e.g. Etherboot)
7643 // else : boot failure
7645 // Get the boot sequence
7646 #if BX_ELTORITO_BOOT
7647 bootdev = inb_cmos(0x3d);
7648 bootdev |= ((inb_cmos(0x38) & 0xf0) << 4);
7649 bootdev >>= 4 * seq_nr;
7650 bootdev &= 0xf;
7651 if (bootdev == 0) BX_PANIC("No bootable device.\n");
7653 /* Translate from CMOS runes to an IPL table offset by subtracting 1 */
7654 bootdev -= 1;
7655 #else
7656 if (seq_nr ==2) BX_PANIC("No more boot devices.");
7657 if (!!(inb_cmos(0x2d) & 0x20) ^ (seq_nr == 1))
7658 /* Boot from floppy if the bit is set or it's the second boot */
7659 bootdev = 0x00;
7660 else
7661 bootdev = 0x01;
7662 #endif
7664 /* Read the boot device from the IPL table */
7665 if (get_boot_vector(bootdev, &e) == 0) {
7666 BX_INFO("Invalid boot device (0x%x)\n", bootdev);
7667 return;
7670 /* Do the loading, and set up vector as a far pointer to the boot
7671 * address, and bootdrv as the boot drive */
7672 print_boot_device(e.type);
7674 switch(e.type) {
7675 case 0x01: /* FDD */
7676 case 0x02: /* HDD */
7678 bootdrv = (e.type == 0x02) ? 0x80 : 0x00;
7679 bootseg = 0x07c0;
7680 status = 0;
7682 ASM_START
7683 push bp
7684 mov bp, sp
7685 push ax
7686 push bx
7687 push cx
7688 push dx
7690 mov dl, _int18_function.bootdrv + 2[bp]
7691 mov ax, _int18_function.bootseg + 2[bp]
7692 mov es, ax ;; segment
7693 mov bx, #0x0000 ;; offset
7694 mov ah, #0x02 ;; function 2, read diskette sector
7695 mov al, #0x01 ;; read 1 sector
7696 mov ch, #0x00 ;; track 0
7697 mov cl, #0x01 ;; sector 1
7698 mov dh, #0x00 ;; head 0
7699 int #0x13 ;; read sector
7700 jnc int19_load_done
7701 mov ax, #0x0001
7702 mov _int18_function.status + 2[bp], ax
7704 int19_load_done:
7705 pop dx
7706 pop cx
7707 pop bx
7708 pop ax
7709 pop bp
7710 ASM_END
7712 if (status != 0) {
7713 print_boot_failure(e.type, 1);
7714 return;
7717 /* Always check the signature on a HDD boot sector; on FDD, only do
7718 * the check if the CMOS doesn't tell us to skip it */
7719 if (e.type != 0x00 || !((inb_cmos(0x38) & 0x01))) {
7720 if (read_word(bootseg,0x1fe) != 0xaa55) {
7721 print_boot_failure(e.type, 0);
7722 return;
7726 #if BX_TCGBIOS
7727 tcpa_add_bootdevice((Bit32u)0L, (Bit32u)bootdrv);
7728 tcpa_ipl((Bit32u)0L,(Bit32u)bootseg,(Bit32u)0L,(Bit32u)512L); /* specs: 8.2.3 steps 4 and 5 */
7729 #endif
7731 /* Canonicalize bootseg:bootip */
7732 bootip = (bootseg & 0x0fff) << 4;
7733 bootseg &= 0xf000;
7734 break;
7736 #if BX_ELTORITO_BOOT
7737 case 0x03: /* CD-ROM */
7738 status = cdrom_boot();
7740 // If failure
7741 if ( (status & 0x00ff) !=0 ) {
7742 print_cdromboot_failure(status);
7743 print_boot_failure(e.type, 1);
7744 return;
7747 bootdrv = (Bit8u)(status>>8);
7748 bootseg = read_word(ebda_seg,&EbdaData->cdemu.load_segment);
7750 /* Canonicalize bootseg:bootip */
7751 bootip = (bootseg & 0x0fff) << 4;
7752 bootseg &= 0xf000;
7753 break;
7754 #endif
7756 case 0x80: /* Expansion ROM with a Bootstrap Entry Vector (a far pointer) */
7757 bootseg = e.vector >> 16;
7758 bootip = e.vector & 0xffff;
7759 break;
7761 default: return;
7765 /* Jump to the boot vector */
7766 ASM_START
7767 mov bp, sp
7768 ;; Build an iret stack frame that will take us to the boot vector.
7769 ;; iret pops ip, then cs, then flags, so push them in the opposite order.
7770 pushf
7771 mov ax, _int18_function.bootseg + 0[bp]
7772 push ax
7773 mov ax, _int18_function.bootip + 0[bp]
7774 push ax
7775 ;; Set the magic number in ax and the boot drive in dl.
7776 mov ax, #0xaa55
7777 mov dl, _int18_function.bootdrv + 0[bp]
7778 ;; Zero some of the other registers.
7779 xor bx, bx
7780 mov ds, bx
7781 mov es, bx
7782 mov bp, bx
7783 ;; Go!
7784 iret
7785 ASM_END
7788 void
7789 int1a_function(regs, ds, iret_addr)
7790 pusha_regs_t regs; // regs pushed from PUSHA instruction
7791 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7792 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7794 Bit8u val8;
7796 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);
7798 ASM_START
7799 sti
7800 ASM_END
7802 switch (regs.u.r8.ah) {
7803 case 0: // get current clock count
7804 ASM_START
7805 cli
7806 ASM_END
7807 regs.u.r16.cx = BiosData->ticks_high;
7808 regs.u.r16.dx = BiosData->ticks_low;
7809 regs.u.r8.al = BiosData->midnight_flag;
7810 BiosData->midnight_flag = 0; // reset flag
7811 ASM_START
7812 sti
7813 ASM_END
7814 // AH already 0
7815 ClearCF(iret_addr.flags); // OK
7816 break;
7818 case 1: // Set Current Clock Count
7819 ASM_START
7820 cli
7821 ASM_END
7822 BiosData->ticks_high = regs.u.r16.cx;
7823 BiosData->ticks_low = regs.u.r16.dx;
7824 BiosData->midnight_flag = 0; // reset flag
7825 ASM_START
7826 sti
7827 ASM_END
7828 regs.u.r8.ah = 0;
7829 ClearCF(iret_addr.flags); // OK
7830 break;
7833 case 2: // Read CMOS Time
7834 if (rtc_updating()) {
7835 SetCF(iret_addr.flags);
7836 break;
7839 regs.u.r8.dh = inb_cmos(0x00); // Seconds
7840 regs.u.r8.cl = inb_cmos(0x02); // Minutes
7841 regs.u.r8.ch = inb_cmos(0x04); // Hours
7842 regs.u.r8.dl = inb_cmos(0x0b) & 0x01; // Stat Reg B
7843 regs.u.r8.ah = 0;
7844 regs.u.r8.al = regs.u.r8.ch;
7845 ClearCF(iret_addr.flags); // OK
7846 break;
7848 case 3: // Set CMOS Time
7849 // Using a debugger, I notice the following masking/setting
7850 // of bits in Status Register B, by setting Reg B to
7851 // a few values and getting its value after INT 1A was called.
7852 //
7853 // try#1 try#2 try#3
7854 // before 1111 1101 0111 1101 0000 0000
7855 // after 0110 0010 0110 0010 0000 0010
7856 //
7857 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7858 // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
7859 if (rtc_updating()) {
7860 init_rtc();
7861 // fall through as if an update were not in progress
7863 outb_cmos(0x00, regs.u.r8.dh); // Seconds
7864 outb_cmos(0x02, regs.u.r8.cl); // Minutes
7865 outb_cmos(0x04, regs.u.r8.ch); // Hours
7866 // Set Daylight Savings time enabled bit to requested value
7867 val8 = (inb_cmos(0x0b) & 0x60) | 0x02 | (regs.u.r8.dl & 0x01);
7868 // (reg B already selected)
7869 outb_cmos(0x0b, val8);
7870 regs.u.r8.ah = 0;
7871 regs.u.r8.al = val8; // val last written to Reg B
7872 ClearCF(iret_addr.flags); // OK
7873 break;
7875 case 4: // Read CMOS Date
7876 regs.u.r8.ah = 0;
7877 if (rtc_updating()) {
7878 SetCF(iret_addr.flags);
7879 break;
7881 regs.u.r8.cl = inb_cmos(0x09); // Year
7882 regs.u.r8.dh = inb_cmos(0x08); // Month
7883 regs.u.r8.dl = inb_cmos(0x07); // Day of Month
7884 regs.u.r8.ch = inb_cmos(0x32); // Century
7885 regs.u.r8.al = regs.u.r8.ch;
7886 ClearCF(iret_addr.flags); // OK
7887 break;
7889 case 5: // Set CMOS Date
7890 // Using a debugger, I notice the following masking/setting
7891 // of bits in Status Register B, by setting Reg B to
7892 // a few values and getting its value after INT 1A was called.
7893 //
7894 // try#1 try#2 try#3 try#4
7895 // before 1111 1101 0111 1101 0000 0010 0000 0000
7896 // after 0110 1101 0111 1101 0000 0010 0000 0000
7897 //
7898 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7899 // My assumption: RegB = (RegB & 01111111b)
7900 if (rtc_updating()) {
7901 init_rtc();
7902 SetCF(iret_addr.flags);
7903 break;
7905 outb_cmos(0x09, regs.u.r8.cl); // Year
7906 outb_cmos(0x08, regs.u.r8.dh); // Month
7907 outb_cmos(0x07, regs.u.r8.dl); // Day of Month
7908 outb_cmos(0x32, regs.u.r8.ch); // Century
7909 val8 = inb_cmos(0x0b) & 0x7f; // clear halt-clock bit
7910 outb_cmos(0x0b, val8);
7911 regs.u.r8.ah = 0;
7912 regs.u.r8.al = val8; // AL = val last written to Reg B
7913 ClearCF(iret_addr.flags); // OK
7914 break;
7916 case 6: // Set Alarm Time in CMOS
7917 // Using a debugger, I notice the following masking/setting
7918 // of bits in Status Register B, by setting Reg B to
7919 // a few values and getting its value after INT 1A was called.
7920 //
7921 // try#1 try#2 try#3
7922 // before 1101 1111 0101 1111 0000 0000
7923 // after 0110 1111 0111 1111 0010 0000
7924 //
7925 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7926 // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
7927 val8 = inb_cmos(0x0b); // Get Status Reg B
7928 regs.u.r16.ax = 0;
7929 if (val8 & 0x20) {
7930 // Alarm interrupt enabled already
7931 SetCF(iret_addr.flags); // Error: alarm in use
7932 break;
7934 if (rtc_updating()) {
7935 init_rtc();
7936 // fall through as if an update were not in progress
7938 outb_cmos(0x01, regs.u.r8.dh); // Seconds alarm
7939 outb_cmos(0x03, regs.u.r8.cl); // Minutes alarm
7940 outb_cmos(0x05, regs.u.r8.ch); // Hours alarm
7941 outb(0xa1, inb(0xa1) & 0xfe); // enable IRQ 8
7942 // enable Status Reg B alarm bit, clear halt clock bit
7943 outb_cmos(0x0b, (val8 & 0x7f) | 0x20);
7944 ClearCF(iret_addr.flags); // OK
7945 break;
7947 case 7: // Turn off Alarm
7948 // Using a debugger, I notice the following masking/setting
7949 // of bits in Status Register B, by setting Reg B to
7950 // a few values and getting its value after INT 1A was called.
7951 //
7952 // try#1 try#2 try#3 try#4
7953 // before 1111 1101 0111 1101 0010 0000 0010 0010
7954 // after 0100 0101 0101 0101 0000 0000 0000 0010
7955 //
7956 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
7957 // My assumption: RegB = (RegB & 01010111b)
7958 val8 = inb_cmos(0x0b); // Get Status Reg B
7959 // clear clock-halt bit, disable alarm bit
7960 outb_cmos(0x0b, val8 & 0x57); // disable alarm bit
7961 regs.u.r8.ah = 0;
7962 regs.u.r8.al = val8; // val last written to Reg B
7963 ClearCF(iret_addr.flags); // OK
7964 break;
7965 #if BX_PCIBIOS
7966 case 0xb1:
7967 // real mode PCI BIOS functions now handled in assembler code
7968 // this C code handles the error code for information only
7969 if (regs.u.r8.bl == 0xff) {
7970 BX_INFO("PCI BIOS: PCI not present\n");
7971 } else if (regs.u.r8.bl == 0x81) {
7972 BX_INFO("unsupported PCI BIOS function 0x%02x\n", regs.u.r8.al);
7973 } else if (regs.u.r8.bl == 0x83) {
7974 BX_INFO("bad PCI vendor ID %04x\n", regs.u.r16.dx);
7975 } else if (regs.u.r8.bl == 0x86) {
7976 BX_INFO("PCI device %04x:%04x not found\n", regs.u.r16.dx, regs.u.r16.cx);
7978 regs.u.r8.ah = regs.u.r8.bl;
7979 SetCF(iret_addr.flags);
7980 break;
7981 #endif
7983 default:
7984 SetCF(iret_addr.flags); // Unsupported
7988 void
7989 int70_function(regs, ds, iret_addr)
7990 pusha_regs_t regs; // regs pushed from PUSHA instruction
7991 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
7992 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
7994 // INT 70h: IRQ 8 - CMOS RTC interrupt from periodic or alarm modes
7995 Bit8u registerB = 0, registerC = 0;
7997 // Check which modes are enabled and have occurred.
7998 registerB = inb_cmos( 0xB );
7999 registerC = inb_cmos( 0xC );
8001 if( ( registerB & 0x60 ) != 0 ) {
8002 if( ( registerC & 0x20 ) != 0 ) {
8003 // Handle Alarm Interrupt.
8004 ASM_START
8005 sti
8006 int #0x4a
8007 cli
8008 ASM_END
8010 if( ( registerC & 0x40 ) != 0 ) {
8011 // Handle Periodic Interrupt.
8013 if( read_byte( 0x40, 0xA0 ) != 0 ) {
8014 // Wait Interval (Int 15, AH=83) active.
8015 Bit32u time, toggle;
8017 time = read_dword( 0x40, 0x9C ); // Time left in microseconds.
8018 if( time < 0x3D1 ) {
8019 // Done waiting.
8020 Bit16u segment, offset;
8022 offset = read_word( 0x40, 0x98 );
8023 segment = read_word( 0x40, 0x9A );
8024 write_byte( 0x40, 0xA0, 0 ); // Turn of status byte.
8025 outb_cmos( 0xB, registerB & 0x37 ); // Clear the Periodic Interrupt.
8026 write_byte( segment, offset, 0x80 ); // Write to specified flag byte.
8027 } else {
8028 // Continue waiting.
8029 time -= 0x3D1;
8030 write_dword( 0x40, 0x9C, time );
8036 ASM_START
8037 call eoi_both_pics
8038 ASM_END
8042 ASM_START
8043 ;------------------------------------------
8044 ;- INT74h : PS/2 mouse hardware interrupt -
8045 ;------------------------------------------
8046 int74_handler:
8047 sti
8048 pusha
8049 push ds ;; save DS
8050 push #0x00 ;; placeholder for status
8051 push #0x00 ;; placeholder for X
8052 push #0x00 ;; placeholder for Y
8053 push #0x00 ;; placeholder for Z
8054 push #0x00 ;; placeholder for make_far_call boolean
8055 call _int74_function
8056 pop cx ;; remove make_far_call from stack
8057 jcxz int74_done
8059 ;; make far call to EBDA:0022
8060 push #0x00
8061 pop ds
8062 push 0x040E ;; push 0000:040E (opcodes 0xff, 0x36, 0x0E, 0x04)
8063 pop ds
8064 //CALL_EP(0x0022) ;; call far routine (call_Ep DS:0022 :opcodes 0xff, 0x1e, 0x22, 0x00)
8065 call far ptr[0x22]
8066 int74_done:
8067 cli
8068 call eoi_both_pics
8069 add sp, #8 ;; pop status, x, y, z
8071 pop ds ;; restore DS
8072 popa
8073 iret
8076 ;; This will perform an IRET, but will retain value of current CF
8077 ;; by altering flags on stack. Better than RETF #02.
8078 iret_modify_cf:
8079 jc carry_set
8080 push bp
8081 mov bp, sp
8082 and BYTE [bp + 0x06], #0xfe
8083 pop bp
8084 iret
8085 carry_set:
8086 push bp
8087 mov bp, sp
8088 or BYTE [bp + 0x06], #0x01
8089 pop bp
8090 iret
8093 ;----------------------
8094 ;- INT13h (relocated) -
8095 ;----------------------
8097 ; int13_relocated is a little bit messed up since I played with it
8098 ; I have to rewrite it:
8099 ; - call a function that detect which function to call
8100 ; - make all called C function get the same parameters list
8102 int13_relocated:
8104 #if BX_ELTORITO_BOOT
8105 ;; check for an eltorito function
8106 cmp ah,#0x4a
8107 jb int13_not_eltorito
8108 cmp ah,#0x4d
8109 ja int13_not_eltorito
8111 pusha
8112 push es
8113 push ds
8114 push ss
8115 pop ds
8117 push #int13_out
8118 jmp _int13_eltorito ;; ELDX not used
8120 int13_not_eltorito:
8121 push ax
8122 push bx
8123 push cx
8124 push dx
8126 ;; check if emulation active
8127 call _cdemu_isactive
8128 cmp al,#0x00
8129 je int13_cdemu_inactive
8131 ;; check if access to the emulated drive
8132 call _cdemu_emulated_drive
8133 pop dx
8134 push dx
8135 cmp al,dl ;; int13 on emulated drive
8136 jne int13_nocdemu
8138 pop dx
8139 pop cx
8140 pop bx
8141 pop ax
8143 pusha
8144 push es
8145 push ds
8146 push ss
8147 pop ds
8149 push #int13_out
8150 jmp _int13_cdemu ;; ELDX not used
8152 int13_nocdemu:
8153 and dl,#0xE0 ;; mask to get device class, including cdroms
8154 cmp al,dl ;; al is 0x00 or 0x80
8155 jne int13_cdemu_inactive ;; inactive for device class
8157 pop dx
8158 pop cx
8159 pop bx
8160 pop ax
8162 push ax
8163 push cx
8164 push dx
8165 push bx
8167 dec dl ;; real drive is dl - 1
8168 jmp int13_legacy
8170 int13_cdemu_inactive:
8171 pop dx
8172 pop cx
8173 pop bx
8174 pop ax
8176 #endif // BX_ELTORITO_BOOT
8178 int13_noeltorito:
8180 push ax
8181 push cx
8182 push dx
8183 push bx
8185 int13_legacy:
8187 push dx ;; push eltorito value of dx instead of sp
8189 push bp
8190 push si
8191 push di
8193 push es
8194 push ds
8195 push ss
8196 pop ds
8198 ;; now the 16-bit registers can be restored with:
8199 ;; pop ds; pop es; popa; iret
8200 ;; arguments passed to functions should be
8201 ;; DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS
8203 test dl, #0x80
8204 jnz int13_notfloppy
8206 push #int13_out
8207 jmp _int13_diskette_function
8209 int13_notfloppy:
8211 #if BX_USE_ATADRV
8213 cmp dl, #0xE0
8214 jb int13_notcdrom
8216 // ebx is modified: BSD 5.2.1 boot loader problem
8217 // someone should figure out which 32 bit register that actually are used
8219 shr ebx, #16
8220 push bx
8222 call _int13_cdrom
8224 pop bx
8225 shl ebx, #16
8227 jmp int13_out
8229 int13_notcdrom:
8231 #endif
8233 int13_disk:
8234 call _int13_harddisk
8236 int13_out:
8237 pop ds
8238 pop es
8239 popa
8240 iret
8242 ;----------
8243 ;- INT18h -
8244 ;----------
8245 int18_handler: ;; Boot Failure recovery: try the next device.
8247 ;; Reset SP and SS
8248 mov ax, #0xfffe
8249 mov sp, ax
8250 xor ax, ax
8251 mov ss, ax
8253 ;; Get the boot sequence number out of the IPL memory
8254 ;; The first time we do this it will have been set to -1 so
8255 ;; we will start from device 0.
8256 mov bx, #IPL_SEG
8257 mov ds, bx ;; Set segment
8258 mov bx, IPL_SEQUENCE_OFFSET ;; BX is now the sequence number
8259 inc bx ;; ++
8260 mov IPL_SEQUENCE_OFFSET, bx ;; Write it back
8261 mov ds, ax ;; and reset the segment to zero.
8263 ;; Call the C code for the next boot device
8264 push bx
8265 call _int18_function
8267 ;; Boot failed: invoke the boot recovery function...
8268 int #0x18
8270 ;----------
8271 ;- INT19h -
8272 ;----------
8273 int19_relocated: ;; Boot function, relocated
8274 ;;
8275 ;; *** Warning: INT 19h resets the whole machine ***
8276 ;;
8277 ;; Because PV drivers in HVM guests detach some of the emulated devices,
8278 ;; it is not safe to do a soft reboot by just dropping to real mode and
8279 ;; invoking INT 19h -- the boot drives might have disappeared!
8280 ;; If the user asks for a soft reboot, the only thing we can do is
8281 ;; reset the whole machine. When it comes back up, the normal BIOS
8282 ;; boot sequence will start, which is more or less the required behaviour.
8283 ;;
8284 ;; Reset SP and SS
8285 mov ax, #0xfffe
8286 mov sp, ax
8287 xor ax, ax
8288 mov ss, ax
8289 call _machine_reset
8291 ;----------
8292 ;- INT1Ch -
8293 ;----------
8294 int1c_handler: ;; User Timer Tick
8295 iret
8298 ;----------------------
8299 ;- POST: Floppy Drive -
8300 ;----------------------
8301 floppy_drive_post:
8302 mov ax, #0x0000
8303 mov ds, ax
8305 mov al, #0x00
8306 mov 0x043e, al ;; drive 0 & 1 uncalibrated, no interrupt has occurred
8308 mov 0x043f, al ;; diskette motor status: read op, drive0, motors off
8310 mov 0x0440, al ;; diskette motor timeout counter: not active
8311 mov 0x0441, al ;; diskette controller status return code
8313 mov 0x0442, al ;; disk & diskette controller status register 0
8314 mov 0x0443, al ;; diskette controller status register 1
8315 mov 0x0444, al ;; diskette controller status register 2
8316 mov 0x0445, al ;; diskette controller cylinder number
8317 mov 0x0446, al ;; diskette controller head number
8318 mov 0x0447, al ;; diskette controller sector number
8319 mov 0x0448, al ;; diskette controller bytes written
8321 mov 0x048b, al ;; diskette configuration data
8323 ;; -----------------------------------------------------------------
8324 ;; (048F) diskette controller information
8325 ;;
8326 mov al, #0x10 ;; get CMOS diskette drive type
8327 out 0x70, AL
8328 in AL, 0x71
8329 mov ah, al ;; save byte to AH
8331 look_drive0:
8332 shr al, #4 ;; look at top 4 bits for drive 0
8333 jz f0_missing ;; jump if no drive0
8334 mov bl, #0x07 ;; drive0 determined, multi-rate, has changed line
8335 jmp look_drive1
8336 f0_missing:
8337 mov bl, #0x00 ;; no drive0
8339 look_drive1:
8340 mov al, ah ;; restore from AH
8341 and al, #0x0f ;; look at bottom 4 bits for drive 1
8342 jz f1_missing ;; jump if no drive1
8343 or bl, #0x70 ;; drive1 determined, multi-rate, has changed line
8344 f1_missing:
8345 ;; leave high bits in BL zerod
8346 mov 0x048f, bl ;; put new val in BDA (diskette controller information)
8347 ;; -----------------------------------------------------------------
8349 mov al, #0x00
8350 mov 0x0490, al ;; diskette 0 media state
8351 mov 0x0491, al ;; diskette 1 media state
8353 ;; diskette 0,1 operational starting state
8354 ;; drive type has not been determined,
8355 ;; has no changed detection line
8356 mov 0x0492, al
8357 mov 0x0493, al
8359 mov 0x0494, al ;; diskette 0 current cylinder
8360 mov 0x0495, al ;; diskette 1 current cylinder
8362 mov al, #0x02
8363 out #0x0a, al ;; clear DMA-1 channel 2 mask bit
8365 SET_INT_VECTOR(0x1E, #0xF000, #diskette_param_table2)
8366 SET_INT_VECTOR(0x40, #0xF000, #int13_diskette)
8367 SET_INT_VECTOR(0x0E, #0xF000, #int0e_handler) ;; IRQ 6
8369 ret
8372 ;--------------------
8373 ;- POST: HARD DRIVE -
8374 ;--------------------
8375 ; relocated here because the primary POST area isnt big enough.
8376 hard_drive_post:
8377 // IRQ 14 = INT 76h
8378 // INT 76h calls INT 15h function ax=9100
8380 mov al, #0x0a ; 0000 1010 = reserved, disable IRQ 14
8381 mov dx, #0x03f6
8382 out dx, al
8384 mov ax, #0x0000
8385 mov ds, ax
8386 mov 0x0474, al /* hard disk status of last operation */
8387 mov 0x0477, al /* hard disk port offset (XT only ???) */
8388 mov 0x048c, al /* hard disk status register */
8389 mov 0x048d, al /* hard disk error register */
8390 mov 0x048e, al /* hard disk task complete flag */
8391 mov al, #0x01
8392 mov 0x0475, al /* hard disk number attached */
8393 mov al, #0xc0
8394 mov 0x0476, al /* hard disk control byte */
8395 SET_INT_VECTOR(0x13, #0xF000, #int13_handler)
8396 SET_INT_VECTOR(0x76, #0xF000, #int76_handler)
8397 ;; INT 41h: hard disk 0 configuration pointer
8398 ;; INT 46h: hard disk 1 configuration pointer
8399 SET_INT_VECTOR(0x41, #EBDA_SEG, #0x003D)
8400 SET_INT_VECTOR(0x46, #EBDA_SEG, #0x004D)
8402 ;; move disk geometry data from CMOS to EBDA disk parameter table(s)
8403 mov al, #0x12
8404 out #0x70, al
8405 in al, #0x71
8406 and al, #0xf0
8407 cmp al, #0xf0
8408 je post_d0_extended
8409 jmp check_for_hd1
8410 post_d0_extended:
8411 mov al, #0x19
8412 out #0x70, al
8413 in al, #0x71
8414 cmp al, #47 ;; decimal 47 - user definable
8415 je post_d0_type47
8416 HALT(__LINE__)
8417 post_d0_type47:
8418 ;; CMOS purpose param table offset
8419 ;; 1b cylinders low 0
8420 ;; 1c cylinders high 1
8421 ;; 1d heads 2
8422 ;; 1e write pre-comp low 5
8423 ;; 1f write pre-comp high 6
8424 ;; 20 retries/bad map/heads>8 8
8425 ;; 21 landing zone low C
8426 ;; 22 landing zone high D
8427 ;; 23 sectors/track E
8429 mov ax, #EBDA_SEG
8430 mov ds, ax
8432 ;;; Filling EBDA table for hard disk 0.
8433 mov al, #0x1f
8434 out #0x70, al
8435 in al, #0x71
8436 mov ah, al
8437 mov al, #0x1e
8438 out #0x70, al
8439 in al, #0x71
8440 mov (0x003d + 0x05), ax ;; write precomp word
8442 mov al, #0x20
8443 out #0x70, al
8444 in al, #0x71
8445 mov (0x003d + 0x08), al ;; drive control byte
8447 mov al, #0x22
8448 out #0x70, al
8449 in al, #0x71
8450 mov ah, al
8451 mov al, #0x21
8452 out #0x70, al
8453 in al, #0x71
8454 mov (0x003d + 0x0C), ax ;; landing zone word
8456 mov al, #0x1c ;; get cylinders word in AX
8457 out #0x70, al
8458 in al, #0x71 ;; high byte
8459 mov ah, al
8460 mov al, #0x1b
8461 out #0x70, al
8462 in al, #0x71 ;; low byte
8463 mov bx, ax ;; BX = cylinders
8465 mov al, #0x1d
8466 out #0x70, al
8467 in al, #0x71
8468 mov cl, al ;; CL = heads
8470 mov al, #0x23
8471 out #0x70, al
8472 in al, #0x71
8473 mov dl, al ;; DL = sectors
8475 cmp bx, #1024
8476 jnbe hd0_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8478 hd0_post_physical_chs:
8479 ;; no logical CHS mapping used, just physical CHS
8480 ;; use Standard Fixed Disk Parameter Table (FDPT)
8481 mov (0x003d + 0x00), bx ;; number of physical cylinders
8482 mov (0x003d + 0x02), cl ;; number of physical heads
8483 mov (0x003d + 0x0E), dl ;; number of physical sectors
8484 jmp check_for_hd1
8486 hd0_post_logical_chs:
8487 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8488 mov (0x003d + 0x09), bx ;; number of physical cylinders
8489 mov (0x003d + 0x0b), cl ;; number of physical heads
8490 mov (0x003d + 0x04), dl ;; number of physical sectors
8491 mov (0x003d + 0x0e), dl ;; number of logical sectors (same)
8492 mov al, #0xa0
8493 mov (0x003d + 0x03), al ;; A0h signature, indicates translated table
8495 cmp bx, #2048
8496 jnbe hd0_post_above_2048
8497 ;; 1024 < c <= 2048 cylinders
8498 shr bx, #0x01
8499 shl cl, #0x01
8500 jmp hd0_post_store_logical
8502 hd0_post_above_2048:
8503 cmp bx, #4096
8504 jnbe hd0_post_above_4096
8505 ;; 2048 < c <= 4096 cylinders
8506 shr bx, #0x02
8507 shl cl, #0x02
8508 jmp hd0_post_store_logical
8510 hd0_post_above_4096:
8511 cmp bx, #8192
8512 jnbe hd0_post_above_8192
8513 ;; 4096 < c <= 8192 cylinders
8514 shr bx, #0x03
8515 shl cl, #0x03
8516 jmp hd0_post_store_logical
8518 hd0_post_above_8192:
8519 ;; 8192 < c <= 16384 cylinders
8520 shr bx, #0x04
8521 shl cl, #0x04
8523 hd0_post_store_logical:
8524 mov (0x003d + 0x00), bx ;; number of physical cylinders
8525 mov (0x003d + 0x02), cl ;; number of physical heads
8526 ;; checksum
8527 mov cl, #0x0f ;; repeat count
8528 mov si, #0x003d ;; offset to disk0 FDPT
8529 mov al, #0x00 ;; sum
8530 hd0_post_checksum_loop:
8531 add al, [si]
8532 inc si
8533 dec cl
8534 jnz hd0_post_checksum_loop
8535 not al ;; now take 2s complement
8536 inc al
8537 mov [si], al
8538 ;;; Done filling EBDA table for hard disk 0.
8541 check_for_hd1:
8542 ;; is there really a second hard disk? if not, return now
8543 mov al, #0x12
8544 out #0x70, al
8545 in al, #0x71
8546 and al, #0x0f
8547 jnz post_d1_exists
8548 ret
8549 post_d1_exists:
8550 ;; check that the hd type is really 0x0f.
8551 cmp al, #0x0f
8552 jz post_d1_extended
8553 HALT(__LINE__)
8554 post_d1_extended:
8555 ;; check that the extended type is 47 - user definable
8556 mov al, #0x1a
8557 out #0x70, al
8558 in al, #0x71
8559 cmp al, #47 ;; decimal 47 - user definable
8560 je post_d1_type47
8561 HALT(__LINE__)
8562 post_d1_type47:
8563 ;; Table for disk1.
8564 ;; CMOS purpose param table offset
8565 ;; 0x24 cylinders low 0
8566 ;; 0x25 cylinders high 1
8567 ;; 0x26 heads 2
8568 ;; 0x27 write pre-comp low 5
8569 ;; 0x28 write pre-comp high 6
8570 ;; 0x29 heads>8 8
8571 ;; 0x2a landing zone low C
8572 ;; 0x2b landing zone high D
8573 ;; 0x2c sectors/track E
8574 ;;; Fill EBDA table for hard disk 1.
8575 mov ax, #EBDA_SEG
8576 mov ds, ax
8577 mov al, #0x28
8578 out #0x70, al
8579 in al, #0x71
8580 mov ah, al
8581 mov al, #0x27
8582 out #0x70, al
8583 in al, #0x71
8584 mov (0x004d + 0x05), ax ;; write precomp word
8586 mov al, #0x29
8587 out #0x70, al
8588 in al, #0x71
8589 mov (0x004d + 0x08), al ;; drive control byte
8591 mov al, #0x2b
8592 out #0x70, al
8593 in al, #0x71
8594 mov ah, al
8595 mov al, #0x2a
8596 out #0x70, al
8597 in al, #0x71
8598 mov (0x004d + 0x0C), ax ;; landing zone word
8600 mov al, #0x25 ;; get cylinders word in AX
8601 out #0x70, al
8602 in al, #0x71 ;; high byte
8603 mov ah, al
8604 mov al, #0x24
8605 out #0x70, al
8606 in al, #0x71 ;; low byte
8607 mov bx, ax ;; BX = cylinders
8609 mov al, #0x26
8610 out #0x70, al
8611 in al, #0x71
8612 mov cl, al ;; CL = heads
8614 mov al, #0x2c
8615 out #0x70, al
8616 in al, #0x71
8617 mov dl, al ;; DL = sectors
8619 cmp bx, #1024
8620 jnbe hd1_post_logical_chs ;; if cylinders > 1024, use translated style CHS
8622 hd1_post_physical_chs:
8623 ;; no logical CHS mapping used, just physical CHS
8624 ;; use Standard Fixed Disk Parameter Table (FDPT)
8625 mov (0x004d + 0x00), bx ;; number of physical cylinders
8626 mov (0x004d + 0x02), cl ;; number of physical heads
8627 mov (0x004d + 0x0E), dl ;; number of physical sectors
8628 ret
8630 hd1_post_logical_chs:
8631 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
8632 mov (0x004d + 0x09), bx ;; number of physical cylinders
8633 mov (0x004d + 0x0b), cl ;; number of physical heads
8634 mov (0x004d + 0x04), dl ;; number of physical sectors
8635 mov (0x004d + 0x0e), dl ;; number of logical sectors (same)
8636 mov al, #0xa0
8637 mov (0x004d + 0x03), al ;; A0h signature, indicates translated table
8639 cmp bx, #2048
8640 jnbe hd1_post_above_2048
8641 ;; 1024 < c <= 2048 cylinders
8642 shr bx, #0x01
8643 shl cl, #0x01
8644 jmp hd1_post_store_logical
8646 hd1_post_above_2048:
8647 cmp bx, #4096
8648 jnbe hd1_post_above_4096
8649 ;; 2048 < c <= 4096 cylinders
8650 shr bx, #0x02
8651 shl cl, #0x02
8652 jmp hd1_post_store_logical
8654 hd1_post_above_4096:
8655 cmp bx, #8192
8656 jnbe hd1_post_above_8192
8657 ;; 4096 < c <= 8192 cylinders
8658 shr bx, #0x03
8659 shl cl, #0x03
8660 jmp hd1_post_store_logical
8662 hd1_post_above_8192:
8663 ;; 8192 < c <= 16384 cylinders
8664 shr bx, #0x04
8665 shl cl, #0x04
8667 hd1_post_store_logical:
8668 mov (0x004d + 0x00), bx ;; number of physical cylinders
8669 mov (0x004d + 0x02), cl ;; number of physical heads
8670 ;; checksum
8671 mov cl, #0x0f ;; repeat count
8672 mov si, #0x004d ;; offset to disk0 FDPT
8673 mov al, #0x00 ;; sum
8674 hd1_post_checksum_loop:
8675 add al, [si]
8676 inc si
8677 dec cl
8678 jnz hd1_post_checksum_loop
8679 not al ;; now take 2s complement
8680 inc al
8681 mov [si], al
8682 ;;; Done filling EBDA table for hard disk 1.
8684 ret
8686 ;--------------------
8687 ;- POST: EBDA segment
8688 ;--------------------
8689 ; relocated here because the primary POST area isnt big enough.
8690 ebda_post:
8691 #if BX_USE_EBDA
8692 mov ax, #EBDA_SEG
8693 mov ds, ax
8694 mov byte ptr [0x0], #EBDA_SIZE
8695 #endif
8696 xor ax, ax ; mov EBDA seg into 40E
8697 mov ds, ax
8698 mov word ptr [0x40E], #EBDA_SEG
8699 ret;;
8701 ;--------------------
8702 ;- POST: EOI + jmp via [0x40:67)
8703 ;--------------------
8704 ; relocated here because the primary POST area isnt big enough.
8705 eoi_jmp_post:
8706 call eoi_both_pics
8708 xor ax, ax
8709 mov ds, ax
8711 jmp far ptr [0x467]
8714 ;--------------------
8715 eoi_both_pics:
8716 mov al, #0x20
8717 out #0xA0, al ;; slave PIC EOI
8718 eoi_master_pic:
8719 mov al, #0x20
8720 out #0x20, al ;; master PIC EOI
8721 ret
8723 ;--------------------
8724 BcdToBin:
8725 ;; in: AL in BCD format
8726 ;; out: AL in binary format, AH will always be 0
8727 ;; trashes BX
8728 mov bl, al
8729 and bl, #0x0f ;; bl has low digit
8730 shr al, #4 ;; al has high digit
8731 mov bh, #10
8732 mul al, bh ;; multiply high digit by 10 (result in AX)
8733 add al, bl ;; then add low digit
8734 ret
8736 ;--------------------
8737 timer_tick_post:
8738 ;; Setup the Timer Ticks Count (0x46C:dword) and
8739 ;; Timer Ticks Roller Flag (0x470:byte)
8740 ;; The Timer Ticks Count needs to be set according to
8741 ;; the current CMOS time, as if ticks have been occurring
8742 ;; at 18.2hz since midnight up to this point. Calculating
8743 ;; this is a little complicated. Here are the factors I gather
8744 ;; regarding this. 14,318,180 hz was the original clock speed,
8745 ;; chosen so it could be divided by either 3 to drive the 5Mhz CPU
8746 ;; at the time, or 4 to drive the CGA video adapter. The div3
8747 ;; source was divided again by 4 to feed a 1.193Mhz signal to
8748 ;; the timer. With a maximum 16bit timer count, this is again
8749 ;; divided down by 65536 to 18.2hz.
8750 ;;
8751 ;; 14,318,180 Hz clock
8752 ;; /3 = 4,772,726 Hz fed to orginal 5Mhz CPU
8753 ;; /4 = 1,193,181 Hz fed to timer
8754 ;; /65536 (maximum timer count) = 18.20650736 ticks/second
8755 ;; 1 second = 18.20650736 ticks
8756 ;; 1 minute = 1092.390442 ticks
8757 ;; 1 hour = 65543.42651 ticks
8758 ;;
8759 ;; Given the values in the CMOS clock, one could calculate
8760 ;; the number of ticks by the following:
8761 ;; ticks = (BcdToBin(seconds) * 18.206507) +
8762 ;; (BcdToBin(minutes) * 1092.3904)
8763 ;; (BcdToBin(hours) * 65543.427)
8764 ;; To get a little more accuracy, since Im using integer
8765 ;; arithmatic, I use:
8766 ;; ticks = (BcdToBin(seconds) * 18206507) / 1000000 +
8767 ;; (BcdToBin(minutes) * 10923904) / 10000 +
8768 ;; (BcdToBin(hours) * 65543427) / 1000
8770 ;; assuming DS=0000
8772 ;; get CMOS seconds
8773 xor eax, eax ;; clear EAX
8774 mov al, #0x00
8775 out #0x70, al
8776 in al, #0x71 ;; AL has CMOS seconds in BCD
8777 call BcdToBin ;; EAX now has seconds in binary
8778 mov edx, #18206507
8779 mul eax, edx
8780 mov ebx, #1000000
8781 xor edx, edx
8782 div eax, ebx
8783 mov ecx, eax ;; ECX will accumulate total ticks
8785 ;; get CMOS minutes
8786 xor eax, eax ;; clear EAX
8787 mov al, #0x02
8788 out #0x70, al
8789 in al, #0x71 ;; AL has CMOS minutes in BCD
8790 call BcdToBin ;; EAX now has minutes in binary
8791 mov edx, #10923904
8792 mul eax, edx
8793 mov ebx, #10000
8794 xor edx, edx
8795 div eax, ebx
8796 add ecx, eax ;; add to total ticks
8798 ;; get CMOS hours
8799 xor eax, eax ;; clear EAX
8800 mov al, #0x04
8801 out #0x70, al
8802 in al, #0x71 ;; AL has CMOS hours in BCD
8803 call BcdToBin ;; EAX now has hours in binary
8804 mov edx, #65543427
8805 mul eax, edx
8806 mov ebx, #1000
8807 xor edx, edx
8808 div eax, ebx
8809 add ecx, eax ;; add to total ticks
8811 mov 0x46C, ecx ;; Timer Ticks Count
8812 xor al, al
8813 mov 0x470, al ;; Timer Ticks Rollover Flag
8814 ret
8816 ;--------------------
8817 int76_handler:
8818 ;; record completion in BIOS task complete flag
8819 push ax
8820 push ds
8821 mov ax, #0x0040
8822 mov ds, ax
8823 mov 0x008E, #0xff
8824 call eoi_both_pics
8825 pop ds
8826 pop ax
8827 iret
8830 ;--------------------
8831 #if BX_APM
8833 use32 386
8834 #define APM_PROT32
8835 #include "apmbios.S"
8837 use16 386
8838 #define APM_PROT16
8839 #include "apmbios.S"
8841 #define APM_REAL
8842 #include "apmbios.S"
8844 #endif
8846 ASM_END
8847 #include "32bitgateway.c"
8848 ASM_START
8850 ;--------------------
8851 #if BX_PCIBIOS
8852 use32 386
8853 .align 16
8854 bios32_structure:
8855 db 0x5f, 0x33, 0x32, 0x5f ;; "_32_" signature
8856 dw bios32_entry_point, 0xf ;; 32 bit physical address
8857 db 0 ;; revision level
8858 ;; length in paragraphs and checksum stored in a word to prevent errors
8859 dw (~(((bios32_entry_point >> 8) + (bios32_entry_point & 0xff) + 0x32) \
8860 & 0xff) << 8) + 0x01
8861 db 0,0,0,0,0 ;; reserved
8863 .align 16
8864 bios32_entry_point:
8865 pushf
8866 cmp eax, #0x49435024
8867 jne unknown_service
8868 mov eax, #0x80000000
8869 mov dx, #0x0cf8
8870 out dx, eax
8871 mov dx, #0x0cfc
8872 in eax, dx
8873 cmp eax, #0x12378086
8874 jne unknown_service
8875 mov ebx, #0x000f0000
8876 mov ecx, #0
8877 mov edx, #pcibios_protected
8878 xor al, al
8879 jmp bios32_end
8880 unknown_service:
8881 mov al, #0x80
8882 bios32_end:
8883 popf
8884 retf
8886 .align 16
8887 pcibios_protected:
8888 pushf
8889 cli
8890 push esi
8891 push edi
8892 cmp al, #0x01 ;; installation check
8893 jne pci_pro_f02
8894 mov bx, #0x0210
8895 mov cx, #0
8896 mov edx, #0x20494350
8897 mov al, #0x01
8898 jmp pci_pro_ok
8899 pci_pro_f02: ;; find pci device
8900 cmp al, #0x02
8901 jne pci_pro_f08
8902 shl ecx, #16
8903 mov cx, dx
8904 mov bx, #0x0000
8905 mov di, #0x00
8906 pci_pro_devloop:
8907 call pci_pro_select_reg
8908 mov dx, #0x0cfc
8909 in eax, dx
8910 cmp eax, ecx
8911 jne pci_pro_nextdev
8912 cmp si, #0
8913 je pci_pro_ok
8914 dec si
8915 pci_pro_nextdev:
8916 inc bx
8917 cmp bx, #0x0100
8918 jne pci_pro_devloop
8919 mov ah, #0x86
8920 jmp pci_pro_fail
8921 pci_pro_f08: ;; read configuration byte
8922 cmp al, #0x08
8923 jne pci_pro_f09
8924 call pci_pro_select_reg
8925 push edx
8926 mov dx, di
8927 and dx, #0x03
8928 add dx, #0x0cfc
8929 in al, dx
8930 pop edx
8931 mov cl, al
8932 jmp pci_pro_ok
8933 pci_pro_f09: ;; read configuration word
8934 cmp al, #0x09
8935 jne pci_pro_f0a
8936 call pci_pro_select_reg
8937 push edx
8938 mov dx, di
8939 and dx, #0x02
8940 add dx, #0x0cfc
8941 in ax, dx
8942 pop edx
8943 mov cx, ax
8944 jmp pci_pro_ok
8945 pci_pro_f0a: ;; read configuration dword
8946 cmp al, #0x0a
8947 jne pci_pro_f0b
8948 call pci_pro_select_reg
8949 push edx
8950 mov dx, #0x0cfc
8951 in eax, dx
8952 pop edx
8953 mov ecx, eax
8954 jmp pci_pro_ok
8955 pci_pro_f0b: ;; write configuration byte
8956 cmp al, #0x0b
8957 jne pci_pro_f0c
8958 call pci_pro_select_reg
8959 push edx
8960 mov dx, di
8961 and dx, #0x03
8962 add dx, #0x0cfc
8963 mov al, cl
8964 out dx, al
8965 pop edx
8966 jmp pci_pro_ok
8967 pci_pro_f0c: ;; write configuration word
8968 cmp al, #0x0c
8969 jne pci_pro_f0d
8970 call pci_pro_select_reg
8971 push edx
8972 mov dx, di
8973 and dx, #0x02
8974 add dx, #0x0cfc
8975 mov ax, cx
8976 out dx, ax
8977 pop edx
8978 jmp pci_pro_ok
8979 pci_pro_f0d: ;; write configuration dword
8980 cmp al, #0x0d
8981 jne pci_pro_unknown
8982 call pci_pro_select_reg
8983 push edx
8984 mov dx, #0x0cfc
8985 mov eax, ecx
8986 out dx, eax
8987 pop edx
8988 jmp pci_pro_ok
8989 pci_pro_unknown:
8990 mov ah, #0x81
8991 pci_pro_fail:
8992 pop edi
8993 pop esi
8994 sti
8995 popf
8996 stc
8997 retf
8998 pci_pro_ok:
8999 xor ah, ah
9000 pop edi
9001 pop esi
9002 sti
9003 popf
9004 clc
9005 retf
9007 pci_pro_select_reg:
9008 push edx
9009 mov eax, #0x800000
9010 mov ax, bx
9011 shl eax, #8
9012 and di, #0xff
9013 or ax, di
9014 and al, #0xfc
9015 mov dx, #0x0cf8
9016 out dx, eax
9017 pop edx
9018 ret
9020 use16 386
9022 pcibios_real:
9023 push eax
9024 push dx
9025 mov eax, #0x80000000
9026 mov dx, #0x0cf8
9027 out dx, eax
9028 mov dx, #0x0cfc
9029 in eax, dx
9030 cmp eax, #0x12378086
9031 je pci_present
9032 pop dx
9033 pop eax
9034 mov ah, #0xff
9035 stc
9036 ret
9037 pci_present:
9038 pop dx
9039 pop eax
9040 cmp al, #0x01 ;; installation check
9041 jne pci_real_f02
9042 mov ax, #0x0001
9043 mov bx, #0x0210
9044 mov cx, #0
9045 mov edx, #0x20494350
9046 mov edi, #0xf0000
9047 mov di, #pcibios_protected
9048 clc
9049 ret
9050 pci_real_f02: ;; find pci device
9051 push esi
9052 push edi
9053 cmp al, #0x02
9054 jne pci_real_f08
9055 shl ecx, #16
9056 mov cx, dx
9057 mov bx, #0x0000
9058 mov di, #0x00
9059 pci_real_devloop:
9060 call pci_real_select_reg
9061 mov dx, #0x0cfc
9062 in eax, dx
9063 cmp eax, ecx
9064 jne pci_real_nextdev
9065 cmp si, #0
9066 je pci_real_ok
9067 dec si
9068 pci_real_nextdev:
9069 inc bx
9070 cmp bx, #0x0100
9071 jne pci_real_devloop
9072 mov dx, cx
9073 shr ecx, #16
9074 mov ah, #0x86
9075 jmp pci_real_fail
9076 pci_real_f08: ;; read configuration byte
9077 cmp al, #0x08
9078 jne pci_real_f09
9079 call pci_real_select_reg
9080 push dx
9081 mov dx, di
9082 and dx, #0x03
9083 add dx, #0x0cfc
9084 in al, dx
9085 pop dx
9086 mov cl, al
9087 jmp pci_real_ok
9088 pci_real_f09: ;; read configuration word
9089 cmp al, #0x09
9090 jne pci_real_f0a
9091 call pci_real_select_reg
9092 push dx
9093 mov dx, di
9094 and dx, #0x02
9095 add dx, #0x0cfc
9096 in ax, dx
9097 pop dx
9098 mov cx, ax
9099 jmp pci_real_ok
9100 pci_real_f0a: ;; read configuration dword
9101 cmp al, #0x0a
9102 jne pci_real_f0b
9103 call pci_real_select_reg
9104 push dx
9105 mov dx, #0x0cfc
9106 in eax, dx
9107 pop dx
9108 mov ecx, eax
9109 jmp pci_real_ok
9110 pci_real_f0b: ;; write configuration byte
9111 cmp al, #0x0b
9112 jne pci_real_f0c
9113 call pci_real_select_reg
9114 push dx
9115 mov dx, di
9116 and dx, #0x03
9117 add dx, #0x0cfc
9118 mov al, cl
9119 out dx, al
9120 pop dx
9121 jmp pci_real_ok
9122 pci_real_f0c: ;; write configuration word
9123 cmp al, #0x0c
9124 jne pci_real_f0d
9125 call pci_real_select_reg
9126 push dx
9127 mov dx, di
9128 and dx, #0x02
9129 add dx, #0x0cfc
9130 mov ax, cx
9131 out dx, ax
9132 pop dx
9133 jmp pci_real_ok
9134 pci_real_f0d: ;; write configuration dword
9135 cmp al, #0x0d
9136 jne pci_real_unknown
9137 call pci_real_select_reg
9138 push dx
9139 mov dx, #0x0cfc
9140 mov eax, ecx
9141 out dx, eax
9142 pop dx
9143 jmp pci_real_ok
9144 pci_real_unknown:
9145 mov ah, #0x81
9146 pci_real_fail:
9147 pop edi
9148 pop esi
9149 stc
9150 ret
9151 pci_real_ok:
9152 xor ah, ah
9153 pop edi
9154 pop esi
9155 clc
9156 ret
9158 pci_real_select_reg:
9159 push dx
9160 mov eax, #0x800000
9161 mov ax, bx
9162 shl eax, #8
9163 and di, #0xff
9164 or ax, di
9165 and al, #0xfc
9166 mov dx, #0x0cf8
9167 out dx, eax
9168 pop dx
9169 ret
9171 .align 16
9172 pci_routing_table_structure:
9173 db 0x24, 0x50, 0x49, 0x52 ;; "$PIR" signature
9174 db 0, 1 ;; version
9175 dw 32 + (6 * 16) ;; table size
9176 db 0 ;; PCI interrupt router bus
9177 db 0x08 ;; PCI interrupt router DevFunc
9178 dw 0x0000 ;; PCI exclusive IRQs
9179 dw 0x8086 ;; compatible PCI interrupt router vendor ID
9180 dw 0x7000 ;; compatible PCI interrupt router device ID
9181 dw 0,0 ;; Miniport data
9182 db 0,0,0,0,0,0,0,0,0,0,0 ;; reserved
9183 db 0x07 ;; checksum
9184 ;; first slot entry PCI-to-ISA (embedded)
9185 db 0 ;; pci bus number
9186 db 0x08 ;; pci device number (bit 7-3)
9187 db 0x61 ;; link value INTA#: pointer into PCI2ISA config space
9188 dw 0x0c20 ;; IRQ bitmap INTA#
9189 db 0x62 ;; link value INTB#
9190 dw 0x0c20 ;; IRQ bitmap INTB#
9191 db 0x63 ;; link value INTC#
9192 dw 0x0c20 ;; IRQ bitmap INTC#
9193 db 0x60 ;; link value INTD#
9194 dw 0x0c20 ;; IRQ bitmap INTD#
9195 db 0 ;; physical slot (0 = embedded)
9196 db 0 ;; reserved
9197 ;; second slot entry: 1st PCI slot
9198 db 0 ;; pci bus number
9199 db 0x10 ;; pci device number (bit 7-3)
9200 db 0x62 ;; link value INTA#
9201 dw 0x0c20 ;; IRQ bitmap INTA#
9202 db 0x63 ;; link value INTB#
9203 dw 0x0c20 ;; IRQ bitmap INTB#
9204 db 0x60 ;; link value INTC#
9205 dw 0x0c20 ;; IRQ bitmap INTC#
9206 db 0x61 ;; link value INTD#
9207 dw 0x0c20 ;; IRQ bitmap INTD#
9208 db 1 ;; physical slot (0 = embedded)
9209 db 0 ;; reserved
9210 ;; third slot entry: 2nd PCI slot
9211 db 0 ;; pci bus number
9212 db 0x18 ;; pci device number (bit 7-3)
9213 db 0x63 ;; link value INTA#
9214 dw 0x0c20 ;; IRQ bitmap INTA#
9215 db 0x60 ;; link value INTB#
9216 dw 0x0c20 ;; IRQ bitmap INTB#
9217 db 0x61 ;; link value INTC#
9218 dw 0x0c20 ;; IRQ bitmap INTC#
9219 db 0x62 ;; link value INTD#
9220 dw 0x0c20 ;; IRQ bitmap INTD#
9221 db 2 ;; physical slot (0 = embedded)
9222 db 0 ;; reserved
9223 ;; 4th slot entry: 3rd PCI slot
9224 db 0 ;; pci bus number
9225 db 0x20 ;; pci device number (bit 7-3)
9226 db 0x60 ;; link value INTA#
9227 dw 0x0c20 ;; IRQ bitmap INTA#
9228 db 0x61 ;; link value INTB#
9229 dw 0x0c20 ;; IRQ bitmap INTB#
9230 db 0x62 ;; link value INTC#
9231 dw 0x0c20 ;; IRQ bitmap INTC#
9232 db 0x63 ;; link value INTD#
9233 dw 0x0c20 ;; IRQ bitmap INTD#
9234 db 3 ;; physical slot (0 = embedded)
9235 db 0 ;; reserved
9236 ;; 5th slot entry: 4rd PCI slot
9237 db 0 ;; pci bus number
9238 db 0x28 ;; pci device number (bit 7-3)
9239 db 0x61 ;; link value INTA#
9240 dw 0x0c20 ;; IRQ bitmap INTA#
9241 db 0x62 ;; link value INTB#
9242 dw 0x0c20 ;; IRQ bitmap INTB#
9243 db 0x63 ;; link value INTC#
9244 dw 0x0c20 ;; IRQ bitmap INTC#
9245 db 0x60 ;; link value INTD#
9246 dw 0x0c20 ;; IRQ bitmap INTD#
9247 db 4 ;; physical slot (0 = embedded)
9248 db 0 ;; reserved
9249 ;; 6th slot entry: 5rd PCI slot
9250 db 0 ;; pci bus number
9251 db 0x30 ;; pci device number (bit 7-3)
9252 db 0x62 ;; link value INTA#
9253 dw 0x0c20 ;; IRQ bitmap INTA#
9254 db 0x63 ;; link value INTB#
9255 dw 0x0c20 ;; IRQ bitmap INTB#
9256 db 0x60 ;; link value INTC#
9257 dw 0x0c20 ;; IRQ bitmap INTC#
9258 db 0x61 ;; link value INTD#
9259 dw 0x0c20 ;; IRQ bitmap INTD#
9260 db 5 ;; physical slot (0 = embedded)
9261 db 0 ;; reserved
9262 #endif // BX_PCIBIOS
9264 ; parallel port detection: base address in DX, index in BX, timeout in CL
9265 detect_parport:
9266 push dx
9267 add dx, #2
9268 in al, dx
9269 and al, #0xdf ; clear input mode
9270 out dx, al
9271 pop dx
9272 mov al, #0xaa
9273 out dx, al
9274 in al, dx
9275 cmp al, #0xaa
9276 jne no_parport
9277 push bx
9278 shl bx, #1
9279 mov [bx+0x408], dx ; Parallel I/O address
9280 pop bx
9281 mov [bx+0x478], cl ; Parallel printer timeout
9282 inc bx
9283 no_parport:
9284 ret
9286 ; serial port detection: base address in DX, index in BX, timeout in CL
9287 detect_serial:
9288 push dx
9289 inc dx
9290 mov al, #0x02
9291 out dx, al
9292 in al, dx
9293 cmp al, #0x02
9294 jne no_serial
9295 inc dx
9296 in al, dx
9297 cmp al, #0x02
9298 jne no_serial
9299 dec dx
9300 xor al, al
9301 out dx, al
9302 pop dx
9303 push bx
9304 shl bx, #1
9305 mov [bx+0x400], dx ; Serial I/O address
9306 pop bx
9307 mov [bx+0x47c], cl ; Serial timeout
9308 inc bx
9309 ret
9310 no_serial:
9311 pop dx
9312 ret
9314 rom_checksum:
9315 push ax
9316 push bx
9317 push cx
9318 xor ax, ax
9319 xor bx, bx
9320 xor cx, cx
9321 mov ch, [2]
9322 shl cx, #1
9323 checksum_loop:
9324 add al, [bx]
9325 inc bx
9326 loop checksum_loop
9327 and al, #0xff
9328 pop cx
9329 pop bx
9330 pop ax
9331 ret
9334 ;; We need a copy of this string, but we are not actually a PnP BIOS,
9335 ;; so make sure it is *not* aligned, so OSes will not see it if they scan.
9336 .align 16
9337 db 0
9338 pnp_string:
9339 .ascii "$PnP"
9342 rom_scan:
9343 ;; Scan for existence of valid expansion ROMS.
9344 ;; Video ROM: from 0xC0000..0xC7FFF in 2k increments
9345 ;; General ROM: from 0xC8000..0xDFFFF in 2k increments
9346 ;; System ROM: only 0xE0000
9347 ;;
9348 ;; Header:
9349 ;; Offset Value
9350 ;; 0 0x55
9351 ;; 1 0xAA
9352 ;; 2 ROM length in 512-byte blocks
9353 ;; 3 ROM initialization entry point (FAR CALL)
9355 #if BX_TCGBIOS
9356 call _tcpa_start_option_rom_scan /* specs: 3.2.3.3 + 10.4.3 */
9357 #endif
9358 mov cx, #0xc000
9359 rom_scan_loop:
9360 mov ds, cx
9361 mov ax, #0x0004 ;; start with increment of 4 (512-byte) blocks = 2k
9362 cmp [0], #0xAA55 ;; look for signature
9363 jne rom_scan_increment
9364 call rom_checksum
9365 jnz rom_scan_increment
9366 mov al, [2] ;; change increment to ROM length in 512-byte blocks
9368 ;; We want our increment in 512-byte quantities, rounded to
9369 ;; the nearest 2k quantity, since we only scan at 2k intervals.
9370 test al, #0x03
9371 jz block_count_rounded
9372 and al, #0xfc ;; needs rounding up
9373 add al, #0x04
9374 block_count_rounded:
9376 #if BX_TCGBIOS
9377 push ax
9378 push ds
9379 push ecx
9380 xor ax, ax
9381 mov ds, ax
9382 and ecx, #0xffff
9383 push ecx ;; segment where option rom is located at
9384 call _tcpa_option_rom /* specs: 3.2.3.3 */
9385 add sp, #4 ;; pop segment
9386 pop ecx ;; original ecx
9387 pop ds
9388 pop ax
9389 #endif
9390 xor bx, bx ;; Restore DS back to 0000:
9391 mov ds, bx
9392 push ax ;; Save AX
9393 push di ;; Save DI
9394 ;; Push addr of ROM entry point
9395 push cx ;; Push seg
9396 push #0x0003 ;; Push offset
9398 ;; Point ES:DI at "$PnP", which tells the ROM that we are a PnP BIOS.
9399 ;; That should stop it grabbing INT 19h; we will use its BEV instead.
9400 mov ax, #0xf000
9401 mov es, ax
9402 lea di, pnp_string
9404 mov bp, sp ;; Call ROM init routine using seg:off on stack
9405 db 0xff ;; call_far ss:[bp+0]
9406 db 0x5e
9407 db 0
9408 cli ;; In case expansion ROM BIOS turns IF on
9409 add sp, #2 ;; Pop offset value
9410 pop cx ;; Pop seg value (restore CX)
9412 ;; Look at the ROM's PnP Expansion header. Properly, we're supposed
9413 ;; to init all the ROMs and then go back and build an IPL table of
9414 ;; all the bootable devices, but we can get away with one pass.
9415 mov ds, cx ;; ROM base
9416 mov bx, 0x001a ;; 0x1A is the offset into ROM header that contains...
9417 mov ax, [bx] ;; the offset of PnP expansion header, where...
9418 cmp ax, #0x5024 ;; we look for signature "$PnP"
9419 jne no_bev
9420 mov ax, 2[bx]
9421 cmp ax, #0x506e
9422 jne no_bev
9423 mov ax, 0x1a[bx] ;; 0x1A is also the offset into the expansion header of...
9424 cmp ax, #0x0000 ;; the Bootstrap Entry Vector, or zero if there is none.
9425 je no_bev
9427 ;; Found a device that thinks it can boot the system. Record its BEV.
9428 mov bx, #IPL_SEG ;; Go to the segment where the IPL table lives
9429 mov ds, bx
9430 mov bx, IPL_COUNT_OFFSET ;; Read the number of entries so far
9431 cmp bx, #IPL_TABLE_ENTRIES
9432 je no_bev ;; Get out if the table is full
9433 shl bx, #0x4 ;; Turn count into offset (entries are 16 bytes)
9434 mov 0[bx], #0x80 ;; This entry is a BEV device
9435 mov 6[bx], cx ;; Build a far pointer from the segment...
9436 mov 4[bx], ax ;; and the offset
9437 shr bx, #0x4 ;; Turn the offset back into a count
9438 inc bx ;; We have one more entry now
9439 mov IPL_COUNT_OFFSET, bx ;; Remember that.
9441 no_bev:
9442 pop di ;; Restore DI
9443 pop ax ;; Restore AX
9444 rom_scan_increment:
9445 shl ax, #5 ;; convert 512-bytes blocks to 16-byte increments
9446 ;; because the segment selector is shifted left 4 bits.
9447 add cx, ax
9448 cmp cx, #0xe000
9449 jbe rom_scan_loop
9451 xor ax, ax ;; Restore DS back to 0000:
9452 mov ds, ax
9453 ret
9455 #ifdef HVMASSIST
9457 ; Copy the SMBIOS entry point from where hvmloader left it.
9458 ; The entry point must be somewhere in 0xf0000-0xfffff on a 16-byte boundary,
9459 ; but the tables themselves can be elsewhere.
9460 smbios_init:
9461 push ax
9462 push cx
9463 push es
9464 push ds
9465 push di
9466 push si
9468 mov cx, #0x001f ; 0x1f bytes to copy
9469 mov ax, #0xf000
9470 mov es, ax ; destination segment is 0xf0000
9471 mov di, #smbios_entry_point ; destination offset
9472 mov ax, #(SMBIOS_PHYSICAL_ADDRESS>>4)
9473 mov ds, ax
9474 mov si, #(SMBIOS_PHYSICAL_ADDRESS&15)
9475 cld
9476 rep
9477 movsb
9479 pop si
9480 pop di
9481 pop ds
9482 pop es
9483 pop cx
9484 pop ax
9486 ret
9488 #endif
9492 ;; for 'C' strings and other data, insert them here with
9493 ;; a the following hack:
9494 ;; DATA_SEG_DEFS_HERE
9497 ;--------
9498 ;- POST -
9499 ;--------
9500 .org 0xe05b ; POST Entry Point
9501 post:
9503 xor ax, ax
9505 ;; first reset the DMA controllers
9506 out 0x0d,al
9507 out 0xda,al
9509 ;; then initialize the DMA controllers
9510 mov al, #0xC0
9511 out 0xD6, al ; cascade mode of channel 4 enabled
9512 mov al, #0x00
9513 out 0xD4, al ; unmask channel 4
9515 ;; Examine CMOS shutdown status.
9516 mov AL, #0x0f
9517 out 0x70, AL
9518 in AL, 0x71
9520 ;; backup status
9521 mov bl, al
9523 ;; Reset CMOS shutdown status.
9524 mov AL, #0x0f
9525 out 0x70, AL ; select CMOS register Fh
9526 mov AL, #0x00
9527 out 0x71, AL ; set shutdown action to normal
9529 ;; Examine CMOS shutdown status.
9530 mov al, bl
9532 ;; 0x00, 0x09, 0x0D+ = normal startup
9533 cmp AL, #0x00
9534 jz normal_post
9535 cmp AL, #0x0d
9536 jae normal_post
9537 cmp AL, #0x09
9538 je normal_post
9540 ;; 0x05 = eoi + jmp via [0x40:0x67] jump
9541 cmp al, #0x05
9542 je eoi_jmp_post
9544 ;; Examine CMOS shutdown status.
9545 ;; 0x01,0x02,0x03,0x04,0x06,0x07,0x08, 0x0a, 0x0b, 0x0c = Unimplemented shutdown status.
9546 push bx
9547 call _shutdown_status_panic
9549 #if 0
9550 HALT(__LINE__)
9552 ;#if 0
9553 ; 0xb0, 0x20, /* mov al, #0x20 */
9554 ; 0xe6, 0x20, /* out 0x20, al ;send EOI to PIC */
9555 ;#endif
9557 pop es
9558 pop ds
9559 popa
9560 iret
9561 #endif
9563 normal_post:
9564 ; case 0: normal startup
9566 cli
9567 mov ax, #0xfffe
9568 mov sp, ax
9569 mov ax, #0x0000
9570 mov ds, ax
9571 mov ss, ax
9573 ;; zero out BIOS data area (40:00..40:ff)
9574 mov es, ax
9575 mov cx, #0x0080 ;; 128 words
9576 mov di, #0x0400
9577 cld
9578 rep
9579 stosw
9581 call _log_bios_start
9583 call _clobber_entry_point
9585 ;; set all interrupts to default handler
9586 mov bx, #0x0000 ;; offset index
9587 mov cx, #0x0100 ;; counter (256 interrupts)
9588 mov ax, #dummy_iret_handler
9589 mov dx, #0xF000
9591 post_default_ints:
9592 mov [bx], ax
9593 inc bx
9594 inc bx
9595 mov [bx], dx
9596 inc bx
9597 inc bx
9598 loop post_default_ints
9600 ;; set vector 0x79 to zero
9601 ;; this is used by 'gardian angel' protection system
9602 SET_INT_VECTOR(0x79, #0, #0)
9604 ;; base memory in K 40:13 (word)
9605 mov ax, #BASE_MEM_IN_K
9606 mov 0x0413, ax
9609 ;; Manufacturing Test 40:12
9610 ;; zerod out above
9612 ;; Warm Boot Flag 0040:0072
9613 ;; value of 1234h = skip memory checks
9614 ;; zerod out above
9617 ;; Printer Services vector
9618 SET_INT_VECTOR(0x17, #0xF000, #int17_handler)
9620 ;; Bootstrap failure vector
9621 SET_INT_VECTOR(0x18, #0xF000, #int18_handler)
9623 ;; Bootstrap Loader vector
9624 SET_INT_VECTOR(0x19, #0xF000, #int19_handler)
9626 ;; User Timer Tick vector
9627 SET_INT_VECTOR(0x1c, #0xF000, #int1c_handler)
9629 ;; Memory Size Check vector
9630 SET_INT_VECTOR(0x12, #0xF000, #int12_handler)
9632 ;; Equipment Configuration Check vector
9633 SET_INT_VECTOR(0x11, #0xF000, #int11_handler)
9635 ;; System Services
9636 SET_INT_VECTOR(0x15, #0xF000, #int15_handler)
9638 ;; EBDA setup
9639 call ebda_post
9641 ;; PIT setup
9642 SET_INT_VECTOR(0x08, #0xF000, #int08_handler)
9643 ;; int 1C already points at dummy_iret_handler (above)
9644 mov al, #0x34 ; timer0: binary count, 16bit count, mode 2
9645 out 0x43, al
9646 #ifdef HVMASSIST
9647 mov al, #0x0b ; #0xe90b = 20 Hz (temporary, until we fix xen/vmx support)
9648 out 0x40, al ; lsb
9649 mov al, #0xe9
9650 out 0x40, al ; msb
9651 #else
9652 mov al, #0x00 ; maximum count of 0000H = 18.2Hz
9653 out 0x40, al
9654 out 0x40, al
9655 #endif
9657 ;; Keyboard
9658 SET_INT_VECTOR(0x09, #0xF000, #int09_handler)
9659 SET_INT_VECTOR(0x16, #0xF000, #int16_handler)
9661 xor ax, ax
9662 mov ds, ax
9663 mov 0x0417, al /* keyboard shift flags, set 1 */
9664 mov 0x0418, al /* keyboard shift flags, set 2 */
9665 mov 0x0419, al /* keyboard alt-numpad work area */
9666 mov 0x0471, al /* keyboard ctrl-break flag */
9667 mov 0x0497, al /* keyboard status flags 4 */
9668 mov al, #0x10
9669 mov 0x0496, al /* keyboard status flags 3 */
9672 /* keyboard head of buffer pointer */
9673 mov bx, #0x001E
9674 mov 0x041A, bx
9676 /* keyboard end of buffer pointer */
9677 mov 0x041C, bx
9679 /* keyboard pointer to start of buffer */
9680 mov bx, #0x001E
9681 mov 0x0480, bx
9683 /* keyboard pointer to end of buffer */
9684 mov bx, #0x003E
9685 mov 0x0482, bx
9687 /* init the keyboard */
9688 call _keyboard_init
9690 ;; mov CMOS Equipment Byte to BDA Equipment Word
9691 mov ax, 0x0410
9692 mov al, #0x14
9693 out 0x70, al
9694 in al, 0x71
9695 mov 0x0410, ax
9697 #if BX_TCGBIOS
9698 call _tcpa_acpi_init
9700 push dword #0
9701 call _tcpa_initialize_tpm
9702 add sp, #4
9704 call _tcpa_do_measure_POSTs
9705 call _tcpa_wake_event /* specs: 3.2.3.7 */
9706 #endif
9708 ;; Parallel setup
9709 SET_INT_VECTOR(0x0F, #0xF000, #dummy_iret_handler)
9710 xor ax, ax
9711 mov ds, ax
9712 xor bx, bx
9713 mov cl, #0x14 ; timeout value
9714 mov dx, #0x378 ; Parallel I/O address, port 1
9715 call detect_parport
9716 mov dx, #0x278 ; Parallel I/O address, port 2
9717 call detect_parport
9718 shl bx, #0x0e
9719 mov ax, 0x410 ; Equipment word bits 14..15 determing # parallel ports
9720 and ax, #0x3fff
9721 or ax, bx ; set number of parallel ports
9722 mov 0x410, ax
9724 ;; Serial setup
9725 SET_INT_VECTOR(0x0C, #0xF000, #dummy_iret_handler)
9726 SET_INT_VECTOR(0x14, #0xF000, #int14_handler)
9727 xor bx, bx
9728 mov cl, #0x0a ; timeout value
9729 mov dx, #0x03f8 ; Serial I/O address, port 1
9730 call detect_serial
9731 mov dx, #0x02f8 ; Serial I/O address, port 2
9732 call detect_serial
9733 mov dx, #0x03e8 ; Serial I/O address, port 3
9734 call detect_serial
9735 mov dx, #0x02e8 ; Serial I/O address, port 4
9736 call detect_serial
9737 shl bx, #0x09
9738 mov ax, 0x410 ; Equipment word bits 9..11 determing # serial ports
9739 and ax, #0xf1ff
9740 or ax, bx ; set number of serial port
9741 mov 0x410, ax
9743 ;; CMOS RTC
9744 SET_INT_VECTOR(0x1A, #0xF000, #int1a_handler)
9745 SET_INT_VECTOR(0x4A, #0xF000, #dummy_iret_handler)
9746 SET_INT_VECTOR(0x70, #0xF000, #int70_handler)
9747 ;; BIOS DATA AREA 0x4CE ???
9748 call timer_tick_post
9750 ;; PS/2 mouse setup
9751 SET_INT_VECTOR(0x74, #0xF000, #int74_handler)
9753 ;; IRQ13 (FPU exception) setup
9754 SET_INT_VECTOR(0x75, #0xF000, #int75_handler)
9756 ;; Video setup
9757 SET_INT_VECTOR(0x10, #0xF000, #int10_handler)
9759 ;; PIC
9760 mov al, #0x11 ; send initialisation commands
9761 out 0x20, al
9762 out 0xa0, al
9763 mov al, #0x08
9764 out 0x21, al
9765 mov al, #0x70
9766 out 0xa1, al
9767 mov al, #0x04
9768 out 0x21, al
9769 mov al, #0x02
9770 out 0xa1, al
9771 mov al, #0x01
9772 out 0x21, al
9773 out 0xa1, al
9774 mov al, #0xb8
9775 out 0x21, AL ;master pic: unmask IRQ 0, 1, 2, 6
9776 #if BX_USE_PS2_MOUSE
9777 mov al, #0x8f
9778 #else
9779 mov al, #0x9f
9780 #endif
9781 out 0xa1, AL ;slave pic: unmask IRQ 12, 13, 14
9783 #ifdef HVMASSIST
9784 call _copy_e820_table
9785 call smbios_init
9786 #endif
9788 call _init_boot_vectors
9790 call rom_scan
9792 call _print_bios_banner
9794 ;;
9795 ;; Floppy setup
9796 ;;
9797 call floppy_drive_post
9799 #if BX_USE_ATADRV
9801 ;;
9802 ;; Hard Drive setup
9803 ;;
9804 call hard_drive_post
9806 ;;
9807 ;; ATA/ATAPI driver setup
9808 ;;
9809 call _ata_init
9810 call _ata_detect
9811 ;;
9812 #else // BX_USE_ATADRV
9814 ;;
9815 ;; Hard Drive setup
9816 ;;
9817 call hard_drive_post
9819 #endif // BX_USE_ATADRV
9821 #if BX_ELTORITO_BOOT
9822 ;;
9823 ;; eltorito floppy/harddisk emulation from cd
9824 ;;
9825 call _cdemu_init
9826 ;;
9827 #endif // BX_ELTORITO_BOOT
9829 #if BX_TCGBIOS
9830 call _tcpa_calling_int19h /* specs: 8.2.3 step 1 */
9831 call _tcpa_add_event_separators /* specs: 8.2.3 step 2 */
9832 /* we do not call int 19h handler but keep following eventlog */
9833 call _tcpa_returned_int19h /* specs: 8.2.3 step 3/7 */
9834 #endif
9836 ;; Start the boot sequence. See the comments in int19_relocated
9837 ;; for why we use INT 18h instead of INT 19h here.
9838 int #0x18
9840 .org 0xe2c3 ; NMI Handler Entry Point
9841 nmi:
9842 ;; FIXME the NMI handler should not panic
9843 ;; but iret when called from int75 (fpu exception)
9844 call _nmi_handler_msg
9845 iret
9847 int75_handler:
9848 out 0xf0, al // clear irq13
9849 call eoi_both_pics // clear interrupt
9850 int 2 // legacy nmi call
9851 iret
9853 ;-------------------------------------------
9854 ;- INT 13h Fixed Disk Services Entry Point -
9855 ;-------------------------------------------
9856 .org 0xe3fe ; INT 13h Fixed Disk Services Entry Point
9857 int13_handler:
9858 //JMPL(int13_relocated)
9859 jmp int13_relocated
9861 .org 0xe401 ; Fixed Disk Parameter Table
9863 ;----------
9864 ;- INT19h -
9865 ;----------
9866 .org 0xe6f2 ; INT 19h Boot Load Service Entry Point
9867 int19_handler:
9869 jmp int19_relocated
9870 ;-------------------------------------------
9871 ;- System BIOS Configuration Data Table
9872 ;-------------------------------------------
9873 .org BIOS_CONFIG_TABLE
9874 db 0x08 ; Table size (bytes) -Lo
9875 db 0x00 ; Table size (bytes) -Hi
9876 db SYS_MODEL_ID
9877 db SYS_SUBMODEL_ID
9878 db BIOS_REVISION
9879 ; Feature byte 1
9880 ; b7: 1=DMA channel 3 used by hard disk
9881 ; b6: 1=2 interrupt controllers present
9882 ; b5: 1=RTC present
9883 ; b4: 1=BIOS calls int 15h/4Fh every key
9884 ; b3: 1=wait for extern event supported (Int 15h/41h)
9885 ; b2: 1=extended BIOS data area used
9886 ; b1: 0=AT or ESDI bus, 1=MicroChannel
9887 ; b0: 1=Dual bus (MicroChannel + ISA)
9888 db (0 << 7) | \
9889 (1 << 6) | \
9890 (1 << 5) | \
9891 (BX_CALL_INT15_4F << 4) | \
9892 (0 << 3) | \
9893 (BX_USE_EBDA << 2) | \
9894 (0 << 1) | \
9895 (0 << 0)
9896 ; Feature byte 2
9897 ; b7: 1=32-bit DMA supported
9898 ; b6: 1=int16h, function 9 supported
9899 ; b5: 1=int15h/C6h (get POS data) supported
9900 ; b4: 1=int15h/C7h (get mem map info) supported
9901 ; b3: 1=int15h/C8h (en/dis CPU) supported
9902 ; b2: 1=non-8042 kb controller
9903 ; b1: 1=data streaming supported
9904 ; b0: reserved
9905 db (0 << 7) | \
9906 (1 << 6) | \
9907 (0 << 5) | \
9908 (0 << 4) | \
9909 (0 << 3) | \
9910 (0 << 2) | \
9911 (0 << 1) | \
9912 (0 << 0)
9913 ; Feature byte 3
9914 ; b7: not used
9915 ; b6: reserved
9916 ; b5: reserved
9917 ; b4: POST supports ROM-to-RAM enable/disable
9918 ; b3: SCSI on system board
9919 ; b2: info panel installed
9920 ; b1: Initial Machine Load (IML) system - BIOS on disk
9921 ; b0: SCSI supported in IML
9922 db 0x00
9923 ; Feature byte 4
9924 ; b7: IBM private
9925 ; b6: EEPROM present
9926 ; b5-3: ABIOS presence (011 = not supported)
9927 ; b2: private
9928 ; b1: memory split above 16Mb supported
9929 ; b0: POSTEXT directly supported by POST
9930 db 0x00
9931 ; Feature byte 5 (IBM)
9932 ; b1: enhanced mouse
9933 ; b0: flash EPROM
9934 db 0x00
9938 .org 0xe729 ; Baud Rate Generator Table
9940 ;----------
9941 ;- INT14h -
9942 ;----------
9943 .org 0xe739 ; INT 14h Serial Communications Service Entry Point
9944 int14_handler:
9945 push ds
9946 pusha
9947 mov ax, #0x0000
9948 mov ds, ax
9949 call _int14_function
9950 popa
9951 pop ds
9952 iret
9955 ;----------------------------------------
9956 ;- INT 16h Keyboard Service Entry Point -
9957 ;----------------------------------------
9958 .org 0xe82e
9959 int16_handler:
9961 sti
9962 push ds
9963 pushf
9964 pusha
9966 cmp ah, #0x00
9967 je int16_F00
9968 cmp ah, #0x10
9969 je int16_F00
9971 mov bx, #0xf000
9972 mov ds, bx
9973 call _int16_function
9974 popa
9975 popf
9976 pop ds
9977 jz int16_zero_set
9979 int16_zero_clear:
9980 push bp
9981 mov bp, sp
9982 //SEG SS
9983 and BYTE [bp + 0x06], #0xbf
9984 pop bp
9985 iret
9987 int16_zero_set:
9988 push bp
9989 mov bp, sp
9990 //SEG SS
9991 or BYTE [bp + 0x06], #0x40
9992 pop bp
9993 iret
9995 int16_F00:
9996 mov bx, #0x0040
9997 mov ds, bx
9999 int16_wait_for_key:
10000 cli
10001 mov bx, 0x001a
10002 cmp bx, 0x001c
10003 jne int16_key_found
10004 sti
10005 nop
10006 #if 0
10007 /* no key yet, call int 15h, function AX=9002 */
10008 0x50, /* push AX */
10009 0xb8, 0x02, 0x90, /* mov AX, #0x9002 */
10010 0xcd, 0x15, /* int 15h */
10011 0x58, /* pop AX */
10012 0xeb, 0xea, /* jmp WAIT_FOR_KEY */
10013 #endif
10014 jmp int16_wait_for_key
10016 int16_key_found:
10017 mov bx, #0xf000
10018 mov ds, bx
10019 call _int16_function
10020 popa
10021 popf
10022 pop ds
10023 #if 0
10024 /* notify int16 complete w/ int 15h, function AX=9102 */
10025 0x50, /* push AX */
10026 0xb8, 0x02, 0x91, /* mov AX, #0x9102 */
10027 0xcd, 0x15, /* int 15h */
10028 0x58, /* pop AX */
10029 #endif
10030 iret
10034 ;-------------------------------------------------
10035 ;- INT09h : Keyboard Hardware Service Entry Point -
10036 ;-------------------------------------------------
10037 .org 0xe987
10038 int09_handler:
10039 cli
10040 push ax
10042 mov al, #0xAD ;;disable keyboard
10043 out #0x64, al
10045 mov al, #0x0B
10046 out #0x20, al
10047 in al, #0x20
10048 and al, #0x02
10049 jz int09_finish
10051 in al, #0x60 ;;read key from keyboard controller
10052 //test al, #0x80 ;;look for key release
10053 //jnz int09_process_key ;; dont pass releases to intercept?
10055 ;; check for extended key
10056 cmp al, #0xe0
10057 jne int09_call_int15_4f
10059 push ds
10060 xor ax, ax
10061 mov ds, ax
10062 mov al, BYTE [0x496] ;; mf2_state |= 0x01
10063 or al, #0x01
10064 mov BYTE [0x496], al
10065 pop ds
10067 in al, #0x60 ;;read another key from keyboard controller
10069 sti
10071 int09_call_int15_4f:
10072 push ds
10073 pusha
10074 #ifdef BX_CALL_INT15_4F
10075 mov ah, #0x4f ;; allow for keyboard intercept
10076 stc
10077 int #0x15
10078 jnc int09_done
10079 #endif
10082 //int09_process_key:
10083 mov bx, #0xf000
10084 mov ds, bx
10085 call _int09_function
10087 int09_done:
10088 popa
10089 pop ds
10090 cli
10091 call eoi_master_pic
10093 int09_finish:
10094 mov al, #0xAE ;;enable keyboard
10095 out #0x64, al
10096 pop ax
10097 iret
10102 ;----------------------------------------
10103 ;- INT 13h Diskette Service Entry Point -
10104 ;----------------------------------------
10105 .org 0xec59
10106 int13_diskette:
10107 jmp int13_noeltorito
10109 ;---------------------------------------------
10110 ;- INT 0Eh Diskette Hardware ISR Entry Point -
10111 ;---------------------------------------------
10112 .org 0xef57 ; INT 0Eh Diskette Hardware ISR Entry Point
10113 int0e_handler:
10114 push ax
10115 push dx
10116 mov dx, #0x03f4
10117 in al, dx
10118 and al, #0xc0
10119 cmp al, #0xc0
10120 je int0e_normal
10121 mov dx, #0x03f5
10122 mov al, #0x08 ; sense interrupt status
10123 out dx, al
10124 int0e_loop1:
10125 mov dx, #0x03f4
10126 in al, dx
10127 and al, #0xc0
10128 cmp al, #0xc0
10129 jne int0e_loop1
10130 int0e_loop2:
10131 mov dx, #0x03f5
10132 in al, dx
10133 mov dx, #0x03f4
10134 in al, dx
10135 and al, #0xc0
10136 cmp al, #0xc0
10137 je int0e_loop2
10138 int0e_normal:
10139 push ds
10140 mov ax, #0x0000 ;; segment 0000
10141 mov ds, ax
10142 call eoi_master_pic
10143 mov al, 0x043e
10144 or al, #0x80 ;; diskette interrupt has occurred
10145 mov 0x043e, al
10146 pop ds
10147 pop dx
10148 pop ax
10149 iret
10152 .org 0xefc7 ; Diskette Controller Parameter Table
10153 diskette_param_table:
10154 ;; Since no provisions are made for multiple drive types, most
10155 ;; values in this table are ignored. I set parameters for 1.44M
10156 ;; floppy here
10157 db 0xAF
10158 db 0x02 ;; head load time 0000001, DMA used
10159 db 0x25
10160 db 0x02
10161 db 18
10162 db 0x1B
10163 db 0xFF
10164 db 0x6C
10165 db 0xF6
10166 db 0x0F
10167 db 0x08
10170 ;----------------------------------------
10171 ;- INT17h : Printer Service Entry Point -
10172 ;----------------------------------------
10173 .org 0xefd2
10174 int17_handler:
10175 push ds
10176 pusha
10177 mov ax, #0x0000
10178 mov ds, ax
10179 call _int17_function
10180 popa
10181 pop ds
10182 iret
10184 diskette_param_table2:
10185 ;; New diskette parameter table adding 3 parameters from IBM
10186 ;; Since no provisions are made for multiple drive types, most
10187 ;; values in this table are ignored. I set parameters for 1.44M
10188 ;; floppy here
10189 db 0xAF
10190 db 0x02 ;; head load time 0000001, DMA used
10191 db 0x25
10192 db 0x02
10193 db 18
10194 db 0x1B
10195 db 0xFF
10196 db 0x6C
10197 db 0xF6
10198 db 0x0F
10199 db 0x08
10200 db 79 ;; maximum track
10201 db 0 ;; data transfer rate
10202 db 4 ;; drive type in cmos
10204 .org 0xf045 ; INT 10 Functions 0-Fh Entry Point
10205 HALT(__LINE__)
10206 iret
10208 ;----------