- serial functions added, MF-II keyboard functions added, parport updates
[gplbios.git] / rombios.c
blob2f81fe6da0ba1beb6dea40a5d25a9b06de8a697a
1 /////////////////////////////////////////////////////////////////////////
2 // $Id$
3 /////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (C) 2001 MandrakeSoft S.A.
6 //
7 // MandrakeSoft S.A.
8 // 43, rue d'Aboukir
9 // 75002 Paris - France
10 // http://www.linux-mandrake.com/
11 // http://www.mandrakesoft.com/
13 // This library is free software; you can redistribute it and/or
14 // modify it under the terms of the GNU Lesser General Public
15 // License as published by the Free Software Foundation; either
16 // version 2 of the License, or (at your option) any later version.
18 // This library is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 // Lesser General Public License for more details.
23 // You should have received a copy of the GNU Lesser General Public
24 // License along with this library; if not, write to the Free Software
25 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 /* includes a subset of config.h that can be compiled by bcc, and applies
28 to this file */
29 #include "biosconfig.h"
31 // ROM BIOS for use with Bochs x86 emulation environment
34 // ROM BIOS compatability entry points:
35 // ===================================
36 // $e05b ; POST Entry Point
37 // $e2c3 ; NMI Handler Entry Point
38 // $e3fe ; INT 13h Fixed Disk Services Entry Point
39 // $e401 ; Fixed Disk Parameter Table
40 // $e6f2 ; INT 19h Boot Load Service Entry Point
41 // $e6f5 ; Configuration Data Table
42 // $e729 ; Baud Rate Generator Table
43 // $e739 ; INT 14h Serial Communications Service Entry Point
44 // $e82e ; INT 16h Keyboard Service Entry Point
45 // $e987 ; INT 09h Keyboard Service Entry Point
46 // $ec59 ; INT 13h Diskette Service Entry Point
47 // $ef57 ; INT 0Eh Diskette Hardware ISR Entry Point
48 // $efc7 ; Diskette Controller Parameter Table
49 // $efd2 ; INT 17h Printer Service Entry Point
50 // $f045 ; INT 10 Functions 0-Fh Entry Point
51 // $f065 ; INT 10h Video Support Service Entry Point
52 // $f0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
53 // $f841 ; INT 12h Memory Size Service Entry Point
54 // $f84d ; INT 11h Equipment List Service Entry Point
55 // $f859 ; INT 15h System Services Entry Point
56 // $fa6e ; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
57 // $fe6e ; INT 1Ah Time-of-day Service Entry Point
58 // $fea5 ; INT 08h System Timer ISR Entry Point
59 // $fef3 ; Initial Interrupt Vector Offsets Loaded by POST
60 // $ff53 ; IRET Instruction for Dummy Interrupt Handler
61 // $ff54 ; INT 05h Print Screen Service Entry Point
62 // $fff0 ; Power-up Entry Point
63 // $fff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
64 // $fffe ; System Model ID
67 // old NOTES:
68 // int74 needs to be reworked. Uses direct [bp] offsets.
69 // take out int13 printf()s, or conditionally compile them
70 // int13:
71 // f04 (verify sectors) isn't complete
72 // f02/03/04 should set current cyl,etc in BDA
74 // int1a:
75 // f03/f05 are not complete - just CLC for now
77 // NOTES:
78 // 990104:
79 // - NMI access (bit7 of addr written to 70h)
80 // - timer ISR should deal with floppy counter and turn floppy motor off
82 #define BX_CPU 3
83 #define BX_USE_PS2_MOUSE 1
84 #define BX_CALL_INT15_4F 1
85 #define BX_USE_EBDA 1
86 #define BX_SUPPORT_FLOPPY 1
87 #define BX_PCIBIOS 1
89 /* model byte 0xFC = AT */
90 #define SYS_MODEL_ID 0xFC
91 #define SYS_SUBMODEL_ID 0x00
92 #define BIOS_REVISION 1
93 #define BIOS_CONFIG_TABLE 0xe6f5
94 // 1K of base memory used for Extended Bios Data Area (EBDA)
95 // EBDA is used for PS/2 mouse support, and IDE BIOS, etc.
96 #define BASE_MEM_IN_K (640 - 1)
97 #define EBDA_SEG 0x9FC0
99 #define PANIC_PORT 0x400
101 // #20 is dec 20
102 // #$20 is hex 20 = 32
103 // LDA #$20
104 // JSR $E820
105 // LDD .i,S
106 // JSR $C682
107 // mov al, #$20
109 // all hex literals should be prefixed with '0x'
110 // grep "#[0-9a-fA-F][0-9a-fA-F]" rombios.c
111 // no mov SEG-REG, #value, must mov register into seg-reg
112 // grep -i "mov[ ]*.s" rombios.c
116 #asm
117 .rom
118 .org 0x0000
120 #if BX_CPU >= 3
121 use16 386
122 #else
123 use16 286
124 #endif
126 MACRO HALT
127 ;; the HALT macro is called with the line number of the HALT call.
128 ;; The line number is then sent to the PANIC_PORT, causing Bochs to
129 ;; print a BX_PANIC message. This will normally halt the simulation
130 ;; with a message such as "BIOS panic at rombios.c, line 4091".
131 ;; However, users can choose to make panics non-fatal and continue.
132 mov dx,#PANIC_PORT
133 mov ax,#?1
134 out dx,ax
135 MEND
137 MACRO HALT2
138 ;; the HALT macro is called with the line number of the HALT call.
139 ;; The line number is then sent to the PANIC_PORT, causing Bochs to
140 ;; print a BX_PANIC message. This will normally halt the simulation
141 ;; with a message such as "BIOS panic at rombios.c, line 4091".
142 ;; However, users can choose to make panics non-fatal and continue.
143 mov dx,#0x401
144 mov ax,#?1
145 out dx,ax
146 MEND
148 MACRO JMP_AP
149 db 0xea
150 dw ?2
151 dw ?1
152 MEND
154 MACRO SET_INT_VECTOR
155 mov ax, ?3
156 mov ?1*4, ax
157 mov ax, ?2
158 mov ?1*4+2, ax
159 MEND
161 #endasm
164 typedef unsigned char Bit8u;
165 typedef unsigned short Bit16u;
166 typedef unsigned short Boolean;
168 // for access to RAM area which is used by interrupt vectors
169 // and BIOS Data Area
171 typedef struct {
172 unsigned char filler1[0x400];
173 unsigned char filler2[0x6c];
174 Bit16u ticks_low;
175 Bit16u ticks_high;
176 Bit8u midnight_flag;
177 } bios_data_t;
179 #define BiosData ((bios_data_t *) 0)
181 typedef struct {
182 union {
183 struct {
184 Bit16u di, si, bp, sp;
185 Bit16u bx, dx, cx, ax;
186 } r16;
187 struct {
188 Bit16u filler[4];
189 Bit8u bl, bh, dl, dh, cl, ch, al, ah;
190 } r8;
191 } u;
192 } pusha_regs_t;
194 typedef struct {
195 union {
196 struct {
197 Bit16u flags;
198 } r16;
199 struct {
200 Bit8u flagsl;
201 Bit8u flagsh;
202 } r8;
203 } u;
204 } flags_t;
206 #define SetCF(x) x.u.r8.flagsl |= 0x01
207 #define SetZF(x) x.u.r8.flagsl |= 0x40
208 #define ClearCF(x) x.u.r8.flagsl &= 0xfe
209 #define ClearZF(x) x.u.r8.flagsl &= 0xbf
210 #define GetCF(x) (x.u.r8.flagsl & 0x01)
212 typedef struct {
213 Bit16u ip;
214 Bit16u cs;
215 flags_t flags;
216 } iret_addr_t;
220 static Bit8u inb();
221 static Bit8u inb_cmos();
222 static void outb();
223 static void outb_cmos();
224 static Bit16u inw();
225 static void outw();
226 static void init_rtc();
227 static Boolean rtc_updating();
229 static Bit8u read_byte();
230 static Bit16u read_word();
231 static void write_byte();
232 static void write_word();
233 static void bios_printf();
235 static Bit16u UDIV();
237 static Bit8u inhibit_mouse_int_and_events();
238 static void enable_mouse_int_and_events();
239 static Bit8u send_to_mouse_ctrl();
240 static Bit8u get_mouse_data();
241 static void set_kbd_command_byte();
243 static void int09_function();
244 static void int13_function();
245 static void int13_diskette_function();
246 static void int14_function();
247 static void int15_function();
248 static void int16_function();
249 static void int17_function();
250 static void int1a_function();
251 static void int70_function();
252 static void int74_function();
253 //static Bit16u get_DS();
254 //static void set_DS();
255 static Bit16u get_SS();
256 static void enqueue_key();
257 static unsigned int dequeue_key();
258 static void set_disk_ret_status();
259 static void get_hd_geometry();
260 static void set_diskette_ret_status();
261 static void set_diskette_current_cyl();
262 static void determine_floppy_media();
263 static Boolean floppy_drive_exists();
264 static Boolean floppy_drive_recal();
265 static Boolean floppy_media_known();
266 static Boolean floppy_media_sense();
267 static void cli();
268 static Boolean set_enable_a20();
269 static void debugger_on();
270 static void debugger_off();
271 static void keyboard_panic();
272 static void boot_failure_msg();
273 static void nmi_handler_msg();
274 static void print_bios_banner();
276 static char bios_cvs_version_string[] = "$Revision$";
277 static char bios_date_string[] = "$Date$";
279 static char CVSID[] = "$Id$";
280 /* Offset to skip the CVS $Id: prefix */
281 #define bios_version_string (CVSID + 4)
283 #define BIOS_PRINTF_HALT 1
284 #define BIOS_PRINTF_SCREEN 2
285 #define BIOS_PRINTF_DEBUG 4
286 #define BIOS_PRINTF_ALL (BIOS_PRINTF_SCREEN | BIOS_PRINTF_DEBUG)
287 #define BIOS_PRINTF_DEBHALT (BIOS_PRINTF_SCREEN | BIOS_PRINTF_DEBUG | BIOS_PRINTF_HALT)
289 #define DEBUG_ROMBIOS 0
291 #if DEBUG_ROMBIOS
292 # define printf(format, p...) bios_printf(BIOS_PRINTF_DEBUG, format, ##p)
293 # define panic(format, p...) bios_printf(BIOS_PRINTF_DEBHALT, format, ##p)
294 #else
295 # define printf(format, p...)
296 # define panic(format, p...) bios_printf(BIOS_PRINTF_DEBHALT, format, ##p)
297 #endif
300 #define SET_AL(val8) AX = ((AX & 0xff00) | (val8))
301 #define SET_BL(val8) BX = ((BX & 0xff00) | (val8))
302 #define SET_CL(val8) CX = ((CX & 0xff00) | (val8))
303 #define SET_DL(val8) DX = ((DX & 0xff00) | (val8))
304 #define SET_AH(val8) AX = ((AX & 0x00ff) | ((val8) << 8))
305 #define SET_BH(val8) BX = ((BX & 0x00ff) | ((val8) << 8))
306 #define SET_CH(val8) CX = ((CX & 0x00ff) | ((val8) << 8))
307 #define SET_DH(val8) DX = ((DX & 0x00ff) | ((val8) << 8))
309 #define GET_AL() ( AX & 0x00ff )
310 #define GET_BL() ( BX & 0x00ff )
311 #define GET_CL() ( CX & 0x00ff )
312 #define GET_DL() ( DX & 0x00ff )
313 #define GET_AH() ( AX >> 8 )
314 #define GET_BH() ( BX >> 8 )
315 #define GET_CH() ( CX >> 8 )
316 #define GET_DH() ( DX >> 8 )
319 #define SET_CF() FLAGS |= 0x0001
320 #define CLEAR_CF() FLAGS &= 0xfffe
321 #define GET_CF() (FLAGS & 0x0001)
323 #define SET_ZF() FLAGS |= 0x0040
324 #define CLEAR_ZF() FLAGS &= 0xffbf
325 #define GET_ZF() (FLAGS & 0x0040)
327 #define UNSUPPORTED_FUNCTION 0x86
329 #define none 0
330 #define MAX_SCAN_CODE 0x53
332 static struct {
333 Bit16u normal;
334 Bit16u shift;
335 Bit16u control;
336 Bit16u alt;
337 } scan_to_scanascii[MAX_SCAN_CODE + 1] = {
338 { none, none, none, none },
339 { 0x011b, 0x011b, 0x011b, 0x0100 }, /* escape */
340 { 0x0231, 0x0221, none, 0x7800 }, /* 1! */
341 { 0x0332, 0x0340, 0x0300, 0x7900 }, /* 2@ */
342 { 0x0433, 0x0423, none, 0x7a00 }, /* 3# */
343 { 0x0534, 0x0524, none, 0x7b00 }, /* 4$ */
344 { 0x0635, 0x0625, none, 0x7c00 }, /* 5% */
345 { 0x0736, 0x075e, 0x071e, 0x7d00 }, /* 6^ */
346 { 0x0837, 0x0826, none, 0x7e00 }, /* 7& */
347 { 0x0938, 0x092a, none, 0x7f00 }, /* 8* */
348 { 0x0a39, 0x0a28, none, 0x8000 }, /* 9( */
349 { 0x0b30, 0x0b29, none, 0x8100 }, /* 0) */
350 { 0x0c2d, 0x0c5f, 0x0c1f, 0x8200 }, /* -_ */
351 { 0x0d3d, 0x0d2b, none, 0x8300 }, /* =+ */
352 { 0x0e08, 0x0e08, 0x0e7f, none }, /* backspace */
353 { 0x0f09, 0x0f00, none, none }, /* tab */
354 { 0x1071, 0x1051, 0x1011, 0x1000 }, /* Q */
355 { 0x1177, 0x1157, 0x1117, 0x1100 }, /* W */
356 { 0x1265, 0x1245, 0x1205, 0x1200 }, /* E */
357 { 0x1372, 0x1352, 0x1312, 0x1300 }, /* R */
358 { 0x1474, 0x1454, 0x1414, 0x1400 }, /* T */
359 { 0x1579, 0x1559, 0x1519, 0x1500 }, /* Y */
360 { 0x1675, 0x1655, 0x1615, 0x1600 }, /* U */
361 { 0x1769, 0x1749, 0x1709, 0x1700 }, /* I */
362 { 0x186f, 0x184f, 0x180f, 0x1800 }, /* O */
363 { 0x1970, 0x1950, 0x1910, 0x1900 }, /* P */
364 { 0x1a5b, 0x1a7b, 0x1a1b, none }, /* [{ */
365 { 0x1b5d, 0x1b7d, 0x1b1d, none }, /* ]} */
366 { 0x1c0d, 0x1c0d, 0x1c0a, none }, /* Enter */
367 { none, none, none, none }, /* L Ctrl */
368 { 0x1e61, 0x1e41, 0x1e01, 0x1e00 }, /* A */
369 { 0x1f73, 0x1f53, 0x1f13, 0x1f00 }, /* S */
370 { 0x2064, 0x2044, 0x2004, 0x2000 }, /* D */
371 { 0x2166, 0x2146, 0x2106, 0x2100 }, /* F */
372 { 0x2267, 0x2247, 0x2207, 0x2200 }, /* G */
373 { 0x2368, 0x2348, 0x2308, 0x2300 }, /* H */
374 { 0x246a, 0x244a, 0x240a, 0x2400 }, /* J */
375 { 0x256b, 0x254b, 0x250b, 0x2500 }, /* K */
376 { 0x266c, 0x264c, 0x260c, 0x2600 }, /* L */
377 { 0x273b, 0x273a, none, none }, /* ;: */
378 { 0x2827, 0x2822, none, none }, /* '" */
379 { 0x2960, 0x297e, none, none }, /* `~ */
380 { none, none, none, none }, /* L shift */
381 { 0x2b5c, 0x2b7c, 0x2b1c, none }, /* |\ */
382 { 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00 }, /* Z */
383 { 0x2d78, 0x2d58, 0x2d18, 0x2d00 }, /* X */
384 { 0x2e63, 0x2e43, 0x2e03, 0x2e00 }, /* C */
385 { 0x2f76, 0x2f56, 0x2f16, 0x2f00 }, /* V */
386 { 0x3062, 0x3042, 0x3002, 0x3000 }, /* B */
387 { 0x316e, 0x314e, 0x310e, 0x3100 }, /* N */
388 { 0x326d, 0x324d, 0x320d, 0x3200 }, /* M */
389 { 0x332c, 0x333c, none, none }, /* ,< */
390 { 0x342e, 0x343e, none, none }, /* .> */
391 { 0x352f, 0x353f, none, none }, /* /? */
392 { none, none, none, none }, /* R Shift */
393 { 0x372a, 0x372a, none, none }, /* * */
394 { none, none, none, none }, /* L Alt */
395 { 0x3920, 0x3920, 0x3920, 0x3920 }, /* space */
396 { none, none, none, none }, /* caps lock */
397 { 0x3b00, 0x5400, 0x5e00, 0x6800 }, /* F1 */
398 { 0x3c00, 0x5500, 0x5f00, 0x6900 }, /* F2 */
399 { 0x3d00, 0x5600, 0x6000, 0x6a00 }, /* F3 */
400 { 0x3e00, 0x5700, 0x6100, 0x6b00 }, /* F4 */
401 { 0x3f00, 0x5800, 0x6200, 0x6c00 }, /* F5 */
402 { 0x4000, 0x5900, 0x6300, 0x6d00 }, /* F6 */
403 { 0x4100, 0x5a00, 0x6400, 0x6e00 }, /* F7 */
404 { 0x4200, 0x5b00, 0x6500, 0x6f00 }, /* F8 */
405 { 0x4300, 0x5c00, 0x6600, 0x7000 }, /* F9 */
406 { 0x4400, 0x5d00, 0x6700, 0x7100 }, /* F10 */
407 { none, none, none, none }, /* Num Lock */
408 { none, none, none, none }, /* Scroll Lock */
409 { 0x4700, 0x4737, 0x7700, none }, /* 7 Home */
410 { 0x4800, 0x4838, none, none }, /* 8 UP */
411 { 0x4900, 0x4939, 0x8400, none }, /* 9 PgUp */
412 { 0x4a2d, 0x4a2d, none, none }, /* - */
413 { 0x4b00, 0x4b34, 0x7300, none }, /* 4 Left */
414 { 0x4c00, 0x4c35, none, none }, /* 5 */
415 { 0x4d00, 0x4d36, 0x7400, none }, /* 6 Right */
416 { 0x4e2b, 0x4e2b, none, none }, /* + */
417 { 0x4f00, 0x4f31, 0x7500, none }, /* 1 End */
418 { 0x5000, 0x5032, none, none }, /* 2 Down */
419 { 0x5100, 0x5133, 0x7600, none }, /* 3 PgDn */
420 { 0x5200, 0x5230, none, none }, /* 0 Ins */
421 { 0x5300, 0x532e, none, none } /* Del */
424 Bit8u
425 inb(port)
426 Bit16u port;
428 #asm
429 push bp
430 mov bp, sp
432 push dx
433 mov dx, 4[bp]
434 in al, dx
435 pop dx
437 pop bp
438 #endasm
441 #if BX_PCIBIOS
442 Bit16u
443 inw(port)
444 Bit16u port;
446 #asm
447 push bp
448 mov bp, sp
450 push dx
451 mov dx, 4[bp]
452 in ax, dx
453 pop dx
455 pop bp
456 #endasm
458 #endif
461 void
462 outb(port, val)
463 Bit16u port;
464 Bit8u val;
466 #asm
467 push bp
468 mov bp, sp
470 push ax
471 push dx
472 mov dx, 4[bp]
473 mov al, 6[bp]
474 out dx, al
475 pop dx
476 pop ax
478 pop bp
479 #endasm
482 #if BX_PCIBIOS
483 void
484 outw(port, val)
485 Bit16u port;
486 Bit16u val;
488 #asm
489 push bp
490 mov bp, sp
492 push ax
493 push dx
494 mov dx, 4[bp]
495 mov ax, 6[bp]
496 out dx, ax
497 pop dx
498 pop ax
500 pop bp
501 #endasm
503 #endif
505 void
506 outb_cmos(cmos_reg, val)
507 Bit8u cmos_reg;
508 Bit8u val;
510 #asm
511 push bp
512 mov bp, sp
514 mov al, 4[bp] ;; cmos_reg
515 out 0x70, al
516 mov al, 6[bp] ;; val
517 out 0x71, al
519 pop bp
520 #endasm
523 Bit8u
524 inb_cmos(cmos_reg)
525 Bit8u cmos_reg;
527 #asm
528 push bp
529 mov bp, sp
531 mov al, 4[bp] ;; cmos_reg
532 out 0x70, al
533 in al, 0x71
535 pop bp
536 #endasm
539 void
540 init_rtc()
542 outb_cmos(0x0a, 0x26);
543 outb_cmos(0x0b, 0x02);
544 inb_cmos(0x0c);
545 inb_cmos(0x0d);
548 Boolean
549 rtc_updating()
551 // This function checks to see if the update-in-progress bit
552 // is set in CMOS Status Register A. If not, it returns 0.
553 // If it is set, it tries to wait until there is a transition
554 // to 0, and will return 0 if such a transition occurs. A 1
555 // is returned only after timing out. The maximum period
556 // that this bit should be set is constrained to 244useconds.
557 // The count I use below guarantees coverage or more than
558 // this time, with any reasonable IPS setting.
560 Bit16u count;
562 count = 25000;
563 while (--count != 0) {
564 if ( (inb_cmos(0x0a) & 0x80) == 0 )
565 return(0);
567 return(1); // update-in-progress never transitioned to 0
571 Bit8u
572 read_byte(seg, offset)
573 Bit16u seg;
574 Bit16u offset;
576 #asm
577 push bp
578 mov bp, sp
580 push bx
581 push ds
582 mov ax, 4[bp] ; segment
583 mov ds, ax
584 mov bx, 6[bp] ; offset
585 mov al, [bx]
586 ;; al = return value (byte)
587 pop ds
588 pop bx
590 pop bp
591 #endasm
594 Bit16u
595 read_word(seg, offset)
596 Bit16u seg;
597 Bit16u offset;
599 #asm
600 push bp
601 mov bp, sp
603 push bx
604 push ds
605 mov ax, 4[bp] ; segment
606 mov ds, ax
607 mov bx, 6[bp] ; offset
608 mov ax, [bx]
609 ;; ax = return value (word)
610 pop ds
611 pop bx
613 pop bp
614 #endasm
617 void
618 write_byte(seg, offset, data)
619 Bit16u seg;
620 Bit16u offset;
621 Bit8u data;
623 #asm
624 push bp
625 mov bp, sp
627 push ax
628 push bx
629 push ds
630 mov ax, 4[bp] ; segment
631 mov ds, ax
632 mov bx, 6[bp] ; offset
633 mov al, 8[bp] ; data byte
634 mov [bx], al ; write data byte
635 pop ds
636 pop bx
637 pop ax
639 pop bp
640 #endasm
643 void
644 write_word(seg, offset, data)
645 Bit16u seg;
646 Bit16u offset;
648 #asm
649 push bp
650 mov bp, sp
652 push ax
653 push bx
654 push ds
655 mov ax, 4[bp] ; segment
656 mov ds, ax
657 mov bx, 6[bp] ; offset
658 mov ax, 8[bp] ; data word
659 mov [bx], ax ; write data word
660 pop ds
661 pop bx
662 pop ax
664 pop bp
665 #endasm
668 #if BX_PCIBIOS
669 void
670 setPCIaddr(bus, devfunc, regnum)
671 Bit8u bus;
672 Bit8u devfunc;
673 Bit8u regnum;
675 #asm
676 push bp
677 mov bp, sp
678 push dx
679 push eax
681 mov eax, #0x800000
682 mov ah, 4[bp] ;; bus
683 mov al, 6[bp] ;; devfunc
684 shl eax, 8
685 mov al, 8[bp] ;; regnum
686 mov dx, #0x0cf8
687 out dx, eax
689 pop eax
690 pop dx
691 pop bp
692 #endasm
694 #endif
696 Bit16u
697 UDIV(a, b)
698 Bit16u a, b;
700 // divide a by b
701 // return value in AX is: AL=quotient, AH=remainder
702 #asm
703 push bp
704 mov bp, sp
706 push bx
707 mov ax, 4[bp] ;; a
708 mov bx, 6[bp] ;; b: only low eight bits used
709 div bl ;; AX / BL --> quotient=AL, remainder=AH
710 pop bx
712 pop bp
713 #endasm
716 Bit16u
717 UDIV16(a, b)
718 Bit16u a, b;
720 // divide a by b, discarding remainder
721 #asm
722 push bp
723 mov bp, sp
725 push dx
726 push bx
727 xor dx,dx
728 mov ax, 4[bp] ;; a
729 mov bx, 6[bp] ;; b
730 div bx ;; DX:AX / BX -> AX, DX = remainder
731 pop bx
732 pop dx
733 pop bp
734 #endasm
737 // Bit16u
738 //get_DS()
740 //#asm
741 // mov ax, ds
742 //#endasm
745 // void
746 //set_DS(ds_selector)
747 // Bit16u ds_selector;
749 //#asm
750 // push bp
751 // mov bp, sp
753 // push ax
754 // mov ax, 4[bp] ; ds_selector
755 // mov ds, ax
756 // pop ax
758 // pop bp
759 //#endasm
762 Bit16u
763 get_SS()
765 #asm
766 mov ax, ss
767 #endasm
770 void
771 wrch(c)
772 Bit8u c;
774 #asm
775 push bp
776 mov bp, sp
778 push bx
779 mov ah, #$0e
780 mov al, 4[bp]
781 xor bx,bx
782 int #$10
783 pop bx
785 pop bp
786 #endasm
789 void
790 send(action, c)
791 Bit16u action;
792 Bit8u c;
794 if (action & BIOS_PRINTF_DEBUG) outb(0xfff0, c);
795 if (action & BIOS_PRINTF_SCREEN) {
796 if (c == '\n') wrch('\r');
797 wrch(c);
801 void
802 put_int(action, val, width, neg)
803 Bit16u action;
804 short val, width;
805 Boolean neg;
807 short nval = UDIV16(val, 10);
808 if (nval)
809 put_int(action, nval, width - 1, neg);
810 else {
811 while (--width > 0) send(action, ' ');
812 if (neg) send(action, '-');
814 send(action, val - (nval * 10) + '0');
817 //--------------------------------------------------------------------------
818 // bios_printf()
819 // A compact variable argument printf function which prints its output via
820 // an I/O port so that it can be logged by Bochs. Currently, only %x is
821 // supported (or %02x, %04x, etc).
823 // Supports %[format_width][format]
824 // where format can be d,x,c,s
825 //--------------------------------------------------------------------------
826 void
827 bios_printf(action, s)
828 Bit16u action;
829 Bit8u *s;
831 Bit8u c, format_char;
832 Boolean in_format;
833 short i;
834 Bit16u *arg_ptr;
835 Bit16u arg_seg, arg, nibble, shift_count, format_width;
837 arg_ptr = &s;
838 arg_seg = get_SS();
840 in_format = 0;
841 format_width = 0;
843 if ((action & BIOS_PRINTF_DEBHALT) == BIOS_PRINTF_DEBHALT) {
844 bios_printf (BIOS_PRINTF_ALL, "FATAL: ");
847 while (c = read_byte(0xf000, s)) {
848 if ( c == '%' ) {
849 in_format = 1;
850 format_width = 0;
852 else if (in_format) {
853 if ( (c>='0') && (c<='9') ) {
854 format_width = (format_width * 10) + (c - '0');
856 else {
857 arg_ptr++; // increment to next arg
858 arg = read_word(arg_seg, arg_ptr);
859 if (c == 'x') {
860 if (format_width == 0)
861 format_width = 4;
862 for (i=format_width-1; i>=0; i--) {
863 nibble = (arg >> (4 * i)) & 0x000f;
864 send (action, (nibble<=9)? (nibble+'0') : (nibble-10+'A'));
867 else if (c == 'd') {
868 if (arg & 0x8000)
869 put_int(action, -arg, format_width - 1, 1);
870 else
871 put_int(action, arg, format_width, 0);
873 else if (c == 's') {
874 bios_printf(action & (~BIOS_PRINTF_HALT), arg);
876 else if (c == 'c') {
877 send(action, arg);
879 else
880 panic("bios_printf: unknown format");
881 in_format = 0;
884 else {
885 send(action, c);
887 s ++;
890 if (action & BIOS_PRINTF_HALT) {
891 // freeze in a busy loop. If I do a HLT instruction, then in versions
892 // 1.3.pre1 and earlier, it will panic without ever updating the VGA
893 // display, so the panic message will not be visible. By waiting
894 // forever, you are certain to see the panic message on screen.
895 // After a few more versions have passed, we can turn this back into
896 // a halt or something.
897 do {} while (1);
898 #asm
899 HALT2(__LINE__)
900 #endasm
904 void
905 cli()
907 #asm
909 #endasm
912 void
913 keyboard_panic()
915 panic("Keyboard RESET error");
919 This is called when the boot fails to print an appropriate message.
920 If bit 16 is 0, the disk was not readable.
921 If bit 16 is 1, the disk was readable but didn't have the boot signature.
922 If bit 15 is 0, it tried to boot a floppy disk.
923 If bit 15 is 1, it tried to boot a hard disk.
924 Bits 14-0 are the drive number, usually just a 0 or 1.
926 void
927 boot_failure_msg(drive)
928 Bit16u drive;
930 Bit16u drivenum = drive&0x7f;
931 //bios_printf(BIOS_PRINTF_SCREEN, "boot_failure_msg(%x)\n", drive);
932 if (drive & 0x80)
933 bios_printf(BIOS_PRINTF_DEBUG | BIOS_PRINTF_SCREEN, "Boot from hard disk %d failed\n", drivenum);
934 else
935 bios_printf(BIOS_PRINTF_DEBUG | BIOS_PRINTF_SCREEN, "Boot from floppy disk %d failed\n", drivenum);
936 if (drive & 0x100)
937 panic("Not a bootable disk\n");
938 else
939 panic("Could not read the boot disk\n");
942 void
943 nmi_handler_msg()
945 bios_printf(BIOS_PRINTF_DEBUG, "NMI Handler called\n");
948 void
949 log_bios_start()
951 bios_printf(BIOS_PRINTF_DEBUG, "%s\n", bios_version_string);
954 void
955 print_bios_banner()
957 bios_printf(BIOS_PRINTF_SCREEN, "Bochs BIOS, %s %s\n\n",
958 bios_cvs_version_string, bios_date_string);
960 bios_printf(BIOS_PRINTF_SCREEN, "Test: x234=%3x, d-123=%d, c=%c, s=%s\n",
961 0x1234, -123, '!', "ok");
966 Boolean
967 set_enable_a20(val)
968 Boolean val;
970 Bit8u oldval;
972 // Use PS2 System Control port A to set A20 enable
974 // get current setting first
975 oldval = inb(0x92);
977 // change A20 status
978 if (val)
979 outb(0x92, oldval | 0x02);
980 else
981 outb(0x92, oldval & 0xfd);
983 return((oldval & 0x02) != 0);
986 void
987 debugger_on()
989 outb(0xfedc, 0x01);
992 void
993 debugger_off()
995 outb(0xfedc, 0x00);
1000 void
1001 int14_function(regs, ds, iret_addr)
1002 pusha_regs_t regs; // regs pushed from PUSHA instruction
1003 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
1004 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
1006 Bit16u addr,timer,val16;
1007 Bit8u timeout;
1009 #asm
1011 #endasm
1013 addr = read_word(0x0040, 2 * regs.u.r16.dx);
1014 timeout = read_byte(0x0040, 0x007C + regs.u.r16.dx);
1015 if ((regs.u.r16.dx < 4) && (addr > 0)) {
1016 switch (regs.u.r8.ah) {
1017 case 0:
1018 outb(addr+3, inb(addr+3) | 0x80);
1019 if (regs.u.r8.al & 0xE0 == 0) {
1020 outb(addr, 0x17);
1021 outb(addr+1, 0x04);
1022 } else {
1023 val16 = 0x600 >> ((regs.u.r8.al & 0xE0) >> 5);
1024 outb(addr, val16 & 0xFF);
1025 outb(addr+1, val16 >> 8);
1027 outb(addr+3, regs.u.r8.al & 0x1F);
1028 regs.u.r8.ah = inb(addr+5);
1029 regs.u.r8.al = inb(addr+6);
1030 ClearCF(iret_addr.flags);
1031 break;
1032 case 1:
1033 timer = read_word(0x0040, 0x006C);
1034 while (((inb(addr+5) & 0x60) != 0x60) && (timeout)) {
1035 val16 = read_word(0x0040, 0x006C);
1036 if (val16 != timer) {
1037 timer = val16;
1038 timeout--;
1041 if (timeout) outb(addr, regs.u.r8.al);
1042 regs.u.r8.ah = inb(addr+5);
1043 if (!timeout) regs.u.r8.ah |= 0x80;
1044 ClearCF(iret_addr.flags);
1045 break;
1046 case 2:
1047 timer = read_word(0x0040, 0x006C);
1048 while (((inb(addr+5) & 0x01) == 0) && (timeout)) {
1049 val16 = read_word(0x0040, 0x006C);
1050 if (val16 != timer) {
1051 timer = val16;
1052 timeout--;
1055 if (timeout) {
1056 regs.u.r8.ah = 0;
1057 regs.u.r8.al = inb(addr);
1058 } else {
1059 regs.u.r8.ah = inb(addr+5);
1061 ClearCF(iret_addr.flags);
1062 break;
1063 case 3:
1064 regs.u.r8.ah = inb(addr+5);
1065 regs.u.r8.al = inb(addr+6);
1066 ClearCF(iret_addr.flags);
1067 break;
1068 default:
1069 SetCF(iret_addr.flags); // Unsupported
1071 } else {
1072 SetCF(iret_addr.flags); // Unsupported
1076 void
1077 int15_function(DI, SI, BP, SP, BX, DX, CX, AX, ES, DS, FLAGS)
1078 Bit16u DI, SI, BP, SP, BX, DX, CX, AX, ES, DS, FLAGS;
1080 Bit16u ebda_seg;
1081 Bit8u mouse_flags_1, mouse_flags_2;
1082 Bit16u mouse_driver_seg;
1083 Bit16u mouse_driver_offset;
1084 Bit8u in_byte;
1085 Bit8u response, prev_command_byte;
1086 Boolean prev_a20_enable;
1087 Bit16u base15_00;
1088 Bit8u base23_16;
1089 Bit16u ss;
1090 Bit8u ret, mouse_data1, mouse_data2, mouse_data3;
1091 Bit8u comm_byte, mf2_state;
1093 switch (GET_AH()) {
1094 case 0x24: /* A20 Control */
1095 printf("BIOS: int15: Func 24h, subfunc %02xh, A20 gate control not supported\n", (unsigned) GET_AL());
1096 SET_CF();
1097 SET_AH(UNSUPPORTED_FUNCTION);
1098 break;
1100 case 0x41:
1101 SET_CF();
1102 SET_AH(UNSUPPORTED_FUNCTION);
1103 break;
1105 case 0x4f:
1106 /* keyboard intercept */
1107 #if BX_CPU < 2
1108 SET_AH(UNSUPPORTED_FUNCTION);
1109 #else
1110 if (GET_AL() == 0xE0) {
1111 mf2_state = read_byte(0x0040, 0x96);
1112 write_byte(0x0040, 0x96, mf2_state | 0x01);
1113 SET_AL(inb(0x60));
1115 #endif
1116 SET_CF();
1117 break;
1119 case 0x87:
1120 #if BX_CPU < 3
1121 # error "Int15 function 87h not supported on < 80386"
1122 #endif
1123 // +++ should probably have descriptor checks
1124 // +++ should have exception handlers
1126 cli();
1128 prev_a20_enable = set_enable_a20(1); // enable A20 line
1130 // 128K max of transfer on 386+ ???
1131 // source == destination ???
1133 // ES:SI points to descriptor table
1134 // offset use initially comments
1135 // ==============================================
1136 // 00..07 Unused zeros Null descriptor
1137 // 08..0f GDT zeros filled in by BIOS
1138 // 10..17 source ssssssss source of data
1139 // 18..1f dest dddddddd destination of data
1140 // 20..27 CS zeros filled in by BIOS
1141 // 28..2f SS zeros filled in by BIOS
1143 //es:si
1144 //eeee0
1145 //0ssss
1146 //-----
1148 // check for access rights of source & dest here
1150 // Initialize GDT descriptor
1151 base15_00 = (ES << 4) + SI;
1152 base23_16 = ES >> 12;
1153 if (base15_00 < (ES<<4))
1154 base23_16++;
1155 write_word(ES, SI+0x08+0, 47); // limit 15:00 = 6 * 8bytes/descriptor
1156 write_word(ES, SI+0x08+2, base15_00);// base 15:00
1157 write_byte(ES, SI+0x08+4, base23_16);// base 23:16
1158 write_byte(ES, SI+0x08+5, 0x93); // access
1159 write_word(ES, SI+0x08+6, 0x0000); // base 31:24/reserved/limit 19:16
1161 // Initialize CS descriptor
1162 write_word(ES, SI+0x20+0, 0xffff);// limit 15:00 = normal 64K limit
1163 write_word(ES, SI+0x20+2, 0x0000);// base 15:00
1164 write_byte(ES, SI+0x20+4, 0x000f);// base 23:16
1165 write_byte(ES, SI+0x20+5, 0x9b); // access
1166 write_word(ES, SI+0x20+6, 0x0000);// base 31:24/reserved/limit 19:16
1168 // Initialize SS descriptor
1169 ss = get_SS();
1170 base15_00 = ss << 4;
1171 base23_16 = ss >> 12;
1172 write_word(ES, SI+0x28+0, 0xffff); // limit 15:00 = normal 64K limit
1173 write_word(ES, SI+0x28+2, base15_00);// base 15:00
1174 write_byte(ES, SI+0x28+4, base23_16);// base 23:16
1175 write_byte(ES, SI+0x28+5, 0x93); // access
1176 write_word(ES, SI+0x28+6, 0x0000); // base 31:24/reserved/limit 19:16
1178 #asm
1179 // Compile generates locals offset info relative to SP.
1180 // Get CX (word count) from stack.
1181 mov bx, sp
1182 SEG SS
1183 mov cx, _int15_function.CX [bx]
1185 // since we need to set SS:SP, save them to the BDA
1186 // for future restore
1187 mov ax, #0x00
1188 mov ds, ax
1189 mov 0x0469, ss
1190 mov 0x0467, sp
1192 SEG ES
1193 lgdt [si + 0x08]
1194 SEG CS
1195 lidt [pmode_IDT_info]
1196 ;; perhaps do something with IDT here
1198 ;; set PE bit in CR0
1199 xor eax, eax
1200 mov al, #0x01
1201 mov cr0, eax
1202 ;; far jump to flush CPU queue after transition to protected mode
1203 JMP_AP(0x0020, protected_mode)
1205 protected_mode:
1206 ;; GDT points to valid descriptor table, now load SS, DS, ES
1207 mov ax, #0x28 ;; 101 000 = 5th descriptor in table, TI=GDT, RPL=00
1208 mov ss, ax
1209 mov ax, #0x10 ;; 010 000 = 2nd descriptor in table, TI=GDT, RPL=00
1210 mov ds, ax
1211 mov ax, #0x18 ;; 011 000 = 3rd descriptor in table, TI=GDT, RPL=00
1212 mov es, ax
1213 xor si, si
1214 xor di, di
1217 movsw ;; move CX words from DS:SI to ES:DI
1219 ;; clear CR3 and reset PG bit in CR0 ???
1220 xor eax, eax
1221 mov cr0, eax
1223 ;; far jump to flush CPU queue after transition to real mode
1224 JMP_AP(0xf000, real_mode)
1226 real_mode:
1227 ;; restore IDT to normal real-mode defaults
1228 SEG CS
1229 lidt [rmode_IDT_info]
1231 // restore SS:SP from the BDA
1232 mov ax, #0x00
1233 mov ds, ax
1234 mov ss, 0x0469
1235 mov sp, 0x0467
1236 #endasm
1238 set_enable_a20(prev_a20_enable);
1239 SET_AH(0);
1240 CLEAR_CF();
1241 break;
1244 case 0x88: /* extended memory size */
1245 #if BX_CPU < 2
1246 SET_AH(UNSUPPORTED_FUNCTION);
1247 SET_CF();
1248 #else
1249 /* ??? change this back later... */
1250 /* number of 1K blocks of extended memory, subtract off 1st 1Meg */
1251 // AX = bx_mem.get_memory_in_k() - 1024;
1252 in_byte = inb_cmos(0x30);
1253 SET_AL(in_byte);
1254 in_byte = inb_cmos(0x31);
1255 SET_AH(in_byte);
1256 CLEAR_CF();
1257 #endif
1258 break;
1260 case 0x90:
1261 /* Device busy interrupt. Called by Int 16h when no key available */
1262 break;
1264 case 0x91:
1265 /* Interrupt complete. Called by Int 16h when key becomes available */
1266 break;
1268 case 0xbf:
1269 printf("BIOS: *** int 15h function AH=bf not yet supported!\n");
1270 SET_CF();
1271 SET_AH(UNSUPPORTED_FUNCTION);
1272 break;
1274 case 0xC0:
1275 #if 0
1276 SET_CF();
1277 SET_AH(UNSUPPORTED_FUNCTION);
1278 break;
1279 #endif
1280 CLEAR_CF();
1281 SET_AH(0);
1282 BX = BIOS_CONFIG_TABLE;
1283 ES = 0xF000;
1284 break;
1286 case 0xc1:
1287 #if BX_USE_PS2_MOUSE
1288 ES = read_word(0x0040, 0x000E);
1289 CLEAR_CF();
1290 #else
1291 SET_CF();
1292 SET_AH(UNSUPPORTED_FUNCTION);
1293 #endif
1294 break;
1296 case 0xC2:
1297 // Return Codes status in AH
1298 // =========================
1299 // 00: success
1300 // 01: invalid subfunction (AL > 7)
1301 // 02: invalid input value (out of allowable range)
1302 // 03: interface error
1303 // 04: resend command received from mouse controller,
1304 // device driver should attempt command again
1305 // 05: cannot enable mouse, since no far call has been installed
1306 // 80/86: mouse service not implemented
1308 #if BX_USE_PS2_MOUSE < 1
1309 SET_CF();
1310 SET_AH(UNSUPPORTED_FUNCTION);
1311 #else
1312 ebda_seg = read_word(0x0040, 0x000E);
1313 switch (GET_AL()) {
1314 case 0: // Disable/Enable Mouse
1315 printf("case 0:\n");
1316 switch (GET_BH()) {
1317 case 0: // Disable Mouse
1318 printf("case 0: disable mouse\n");
1319 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
1320 ret = send_to_mouse_ctrl(0xF5); // disable mouse command
1321 if (ret == 0) {
1322 ret = get_mouse_data(&mouse_data1);
1323 if ( (ret == 0) || (mouse_data1 == 0xFA) ) {
1324 CLEAR_CF();
1325 SET_AH(0);
1326 return;
1330 // error
1331 SET_CF();
1332 SET_AH(ret);
1333 return;
1334 break;
1336 case 1: // Enable Mouse
1337 printf("case 1: enable mouse\n");
1338 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
1339 if ( (mouse_flags_2 & 0x80) == 0 ) {
1340 //printf("INT 15h C2 Enable Mouse, no far call handler\n");
1341 SET_CF(); // error
1342 SET_AH(5); // no far call installed
1343 return;
1345 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
1346 ret = send_to_mouse_ctrl(0xF4); // enable mouse command
1347 if (ret == 0) {
1348 ret = get_mouse_data(&mouse_data1);
1349 if ( (ret == 0) && (mouse_data1 == 0xFA) ) {
1350 enable_mouse_int_and_events(); // turn IRQ12 and packet generation on
1351 CLEAR_CF();
1352 SET_AH(0);
1353 return;
1356 SET_CF();
1357 SET_AH(ret);
1358 return;
1360 default: // invalid subfunction
1361 //printf("INT 15h C2 AL=0, BH=%02x\n", (unsigned) GET_BH());
1362 SET_CF(); // error
1363 SET_AH(1); // invalid subfunction
1364 return;
1366 break;
1368 case 1: // Reset Mouse
1369 case 5: // Initialize Mouse
1370 printf("case 1 or 5:\n");
1371 if (GET_AL() == 5) {
1372 if (GET_BH() != 3)
1373 panic("INT 15h C2 AL=5, BH=%02x", (unsigned) GET_BH());
1374 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
1375 mouse_flags_2 = (mouse_flags_2 & 0x00) | GET_BH();
1376 mouse_flags_1 = 0x00;
1377 write_byte(ebda_seg, 0x0026, mouse_flags_1);
1378 write_byte(ebda_seg, 0x0027, mouse_flags_2);
1381 inhibit_mouse_int_and_events(); // disable IRQ12 and packets
1382 ret = send_to_mouse_ctrl(0xFF); // disable mouse command
1383 if (ret == 0) {
1384 ret = get_mouse_data(&mouse_data3);
1385 if (mouse_data3 != 0xfa)
1386 panic("Mouse reset returned %02x (should be ack)", (unsigned)mouse_data3);
1387 if ( ret == 0 ) {
1388 ret = get_mouse_data(&mouse_data1);
1389 if ( ret == 0 ) {
1390 ret = get_mouse_data(&mouse_data2);
1391 if ( ret == 0 ) {
1392 // turn IRQ12 and packet generation on
1393 enable_mouse_int_and_events();
1394 CLEAR_CF();
1395 SET_AH(0);
1396 SET_BL(mouse_data1);
1397 SET_BH(mouse_data2);
1398 return;
1404 // error
1405 SET_CF();
1406 SET_AH(ret);
1407 return;
1409 case 2: // Set Sample Rate
1410 printf("case 2:\n");
1411 switch (GET_BH()) {
1412 case 0: // 10 reports/sec
1413 case 1: // 20 reports/sec
1414 case 2: // 40 reports/sec
1415 case 3: // 60 reports/sec
1416 case 4: // 80 reports/sec
1417 case 5: // 100 reports/sec (default)
1418 case 6: // 200 reports/sec
1419 CLEAR_CF();
1420 SET_AH(0);
1421 break;
1422 default:
1423 panic("INT 15h C2 AL=2, BH=%02x", (unsigned) GET_BH());
1425 break;
1427 case 3: // Set Resolution
1428 printf("case 3:\n");
1429 // BX:
1430 // 0 = 25 dpi, 1 count per millimeter
1431 // 1 = 50 dpi, 2 counts per millimeter
1432 // 2 = 100 dpi, 4 counts per millimeter
1433 // 3 = 200 dpi, 8 counts per millimeter
1434 CLEAR_CF();
1435 SET_AH(0);
1436 break;
1438 case 4: // Get Device ID
1439 printf("case 4:\n");
1440 CLEAR_CF();
1441 SET_AH(0);
1442 SET_BH(0);
1443 break;
1445 case 6: // Return Status & Set Scaling Factor...
1446 printf("case 6:\n");
1447 switch (GET_BH()) {
1448 case 0: // Return Status
1449 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
1450 ret = send_to_mouse_ctrl(0xE9); // get mouse info command
1451 if (ret == 0) {
1452 ret = get_mouse_data(&mouse_data1);
1453 if (mouse_data1 != 0xfa)
1454 panic("Mouse status returned %02x (should be ack)", (unsigned)mouse_data1);
1455 if (ret == 0) {
1456 ret = get_mouse_data(&mouse_data1);
1457 if ( ret == 0 ) {
1458 ret = get_mouse_data(&mouse_data2);
1459 if ( ret == 0 ) {
1460 ret = get_mouse_data(&mouse_data3);
1461 if ( ret == 0 ) {
1462 CLEAR_CF();
1463 SET_AH(0);
1464 SET_BL(mouse_data1);
1465 SET_CL(mouse_data2);
1466 SET_DL(mouse_data3);
1467 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
1468 return;
1475 // error
1476 SET_CF();
1477 SET_AH(ret);
1478 set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
1479 return;
1481 case 1: // Set Scaling Factor to 1:1
1482 CLEAR_CF();
1483 SET_AH(0);
1484 break;
1486 default:
1487 panic("INT 15h C2 AL=6, BH=%02x", (unsigned) GET_BH());
1489 break;
1491 case 7: // Set Mouse Handler Address
1492 printf("case 7:\n");
1493 mouse_driver_seg = ES;
1494 mouse_driver_offset = BX;
1495 write_word(ebda_seg, 0x0022, mouse_driver_offset);
1496 write_word(ebda_seg, 0x0024, mouse_driver_seg);
1497 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
1498 mouse_flags_2 |= 0x80;
1499 write_byte(ebda_seg, 0x0027, mouse_flags_2);
1500 CLEAR_CF();
1501 SET_AH(0);
1502 break;
1504 default:
1505 printf("case default:\n");
1506 SET_AH(1); // invalid function
1507 SET_CF();
1509 #endif
1510 break;
1512 case 0xC4:
1513 printf("BIOS: *** int 15h function AX=%04x, BX=%04x not yet supported!\n",
1514 (unsigned) AX, (unsigned) BX);
1515 SET_CF();
1516 SET_AH(UNSUPPORTED_FUNCTION);
1517 break;
1519 case 0xD8:
1520 printf("BIOS: *** int 15h function AX=D8 not yet supported!\n");
1521 SET_CF();
1522 SET_AH(UNSUPPORTED_FUNCTION);
1523 break;
1525 case 0xe0:
1526 printf("BIOS: *** int 15h function AH=e0 not yet supported!\n");
1527 SET_CF();
1528 SET_AH(UNSUPPORTED_FUNCTION);
1529 break;
1531 default:
1532 printf("BIOS: *** int 15h function AH=%02x not yet supported!\n",
1533 (unsigned) GET_AH());
1534 SET_CF();
1535 SET_AH(UNSUPPORTED_FUNCTION);
1536 break;
1541 void
1542 int16_function(DI, SI, BP, SP, BX, DX, CX, AX, FLAGS)
1543 Bit16u DI, SI, BP, SP, BX, DX, CX, AX, FLAGS;
1545 Bit8u scan_code, ascii_code, shift_flags;
1548 switch (GET_AH()) {
1549 case 0x00: /* read keyboard input */
1551 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
1552 panic("KBD: int16h: out of keyboard input");
1554 AX = (scan_code << 8) | ascii_code;
1555 break;
1557 case 0x01: /* check keyboard status */
1558 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
1559 SET_ZF();
1560 return;
1562 AX = (scan_code << 8) | ascii_code;
1563 CLEAR_ZF();
1564 break;
1566 case 0x02: /* get shift flag status */
1567 shift_flags = read_byte(0x0040, 0x17);
1568 SET_AL(shift_flags);
1569 break;
1571 case 0x10: /* read MF-II keyboard input */
1573 if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
1574 panic("KBD: int16h: out of keyboard input");
1576 if (ascii_code == 0) ascii_code = 0xE0;
1577 AX = (scan_code << 8) | ascii_code;
1578 break;
1580 case 0x11: /* check MF-II keyboard status */
1581 if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
1582 SET_ZF();
1583 return;
1585 if (ascii_code == 0) ascii_code = 0xE0;
1586 AX = (scan_code << 8) | ascii_code;
1587 CLEAR_ZF();
1588 break;
1590 case 0x12: /* get extended keyboard status */
1591 shift_flags = read_byte(0x0040, 0x17);
1592 SET_AL(shift_flags);
1593 shift_flags = read_byte(0x0040, 0x18);
1594 SET_AH(shift_flags);
1595 break;
1597 default:
1598 printf("KBD: unsupported int 16h function %02x\n", GET_AH());
1602 unsigned int
1603 dequeue_key(scan_code, ascii_code, incr)
1604 Bit8u *scan_code;
1605 Bit8u *ascii_code;
1606 unsigned int incr;
1608 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail;
1609 Bit16u ss;
1610 Bit8u acode, scode;
1612 #if BX_CPU < 2
1613 buffer_start = 0x001E;
1614 buffer_end = 0x003E;
1615 #else
1616 buffer_start = read_word(0x0040, 0x0080);
1617 buffer_end = read_word(0x0040, 0x0082);
1618 #endif
1620 buffer_head = read_word(0x0040, 0x001a);
1621 buffer_tail = read_word(0x0040, 0x001c);
1623 if (buffer_head != buffer_tail) {
1624 ss = get_SS();
1625 acode = read_byte(0x0040, buffer_head);
1626 scode = read_byte(0x0040, buffer_head+1);
1627 write_byte(ss, ascii_code, acode);
1628 write_byte(ss, scan_code, scode);
1630 if (incr) {
1631 buffer_head += 2;
1632 if (buffer_head >= buffer_end)
1633 buffer_head = buffer_start;
1634 write_word(0x0040, 0x001a, buffer_head);
1636 return(1);
1638 else {
1639 return(0);
1645 Bit8u
1646 inhibit_mouse_int_and_events()
1648 Bit8u command_byte, prev_command_byte;
1650 // Turn off IRQ generation and aux data line
1651 if ( inb(0x64) & 0x02 )
1652 panic("inhibmouse: keyboard input buffer full");
1653 outb(0x64, 0x20); // get command byte
1654 while ( (inb(0x64) & 0x01) != 0x01 );
1655 prev_command_byte = inb(0x60);
1656 command_byte = prev_command_byte;
1657 //while ( (inb(0x64) & 0x02) );
1658 if ( inb(0x64) & 0x02 )
1659 panic("inhibmouse, keyboard input buffer full");
1660 command_byte &= 0xfd; // turn off IRQ 12 generation
1661 command_byte |= 0x20; // disable mouse serial clock line
1662 outb(0x64, 0x60); // write command byte
1663 outb(0x60, command_byte);
1664 return(prev_command_byte);
1667 void
1668 enable_mouse_int_and_events()
1670 Bit8u command_byte;
1672 // Turn on IRQ generation and aux data line
1673 if ( inb(0x64) & 0x02 )
1674 panic("enabmouse: keyboard input buffer full");
1675 outb(0x64, 0x20); // get command byte
1676 while ( (inb(0x64) & 0x01) != 0x01 );
1677 command_byte = inb(0x60);
1678 //while ( (inb(0x64) & 0x02) );
1679 if ( inb(0x64) & 0x02 )
1680 panic("enabmouse, keyboard input buffer full");
1681 command_byte |= 0x02; // turn on IRQ 12 generation
1682 command_byte &= 0xdf; // enable mouse serial clock line
1683 outb(0x64, 0x60); // write command byte
1684 outb(0x60, command_byte);
1687 Bit8u
1688 send_to_mouse_ctrl(sendbyte)
1689 Bit8u sendbyte;
1691 Bit8u response;
1693 // wait for chance to write to ctrl
1694 if ( inb(0x64) & 0x02 )
1695 panic("sendmouse, keyboard input buffer full");
1696 outb(0x64, 0xD4);
1697 outb(0x60, sendbyte);
1698 return(0);
1702 Bit8u
1703 get_mouse_data(data)
1704 Bit8u *data;
1706 Bit8u response;
1707 Bit16u ss;
1709 while ( (inb(0x64) & 0x21) != 0x21 ) {
1712 response = inb(0x60);
1714 ss = get_SS();
1715 write_byte(ss, data, response);
1716 return(0);
1719 void
1720 set_kbd_command_byte(command_byte)
1721 Bit8u command_byte;
1723 if ( inb(0x64) & 0x02 )
1724 panic("setkbdcomm, input buffer full");
1726 outb(0x64, 0x60); // write command byte
1727 outb(0x60, command_byte);
1730 void
1731 int09_function(DI, SI, BP, SP, BX, DX, CX, AX)
1732 Bit16u DI, SI, BP, SP, BX, DX, CX, AX;
1734 Bit8u scancode, asciicode, shift_flags;
1735 Bit8u mf2_flags, mf2_state, led_flags;
1738 // DS has been set to F000 before call
1742 scancode = GET_AL();
1744 if (scancode == 0) {
1745 printf("KBD: int09 handler: AL=0\n");
1746 return;
1750 shift_flags = read_byte(0x0040, 0x17);
1751 mf2_flags = read_byte(0x0040, 0x18);
1752 mf2_state = read_byte(0x0040, 0x96);
1753 led_flags = read_byte(0x0040, 0x97);
1754 asciicode = 0;
1756 switch (scancode) {
1757 case 0x3a: /* Caps Lock press */
1758 shift_flags |= 0x40;
1759 write_byte(0x0040, 0x17, shift_flags);
1760 mf2_flags |= 0x40;
1761 write_byte(0x0040, 0x18, mf2_flags);
1762 led_flags |= 0x04;
1763 write_byte(0x0040, 0x97, led_flags);
1764 break;
1765 case 0xba: /* Caps Lock release */
1766 mf2_flags &= ~0x40;
1767 write_byte(0x0040, 0x18, mf2_flags);
1768 break;
1770 case 0x2a: /* L Shift press */
1771 shift_flags &= ~0x40;
1772 shift_flags |= 0x02;
1773 write_byte(0x0040, 0x17, shift_flags);
1774 led_flags &= ~0x04;
1775 write_byte(0x0040, 0x97, led_flags);
1776 break;
1777 case 0xaa: /* L Shift release */
1778 shift_flags &= ~0x02;
1779 write_byte(0x0040, 0x17, shift_flags);
1780 break;
1782 case 0x36: /* R Shift press */
1783 shift_flags &= ~0x40;
1784 shift_flags |= 0x01;
1785 write_byte(0x0040, 0x17, shift_flags);
1786 led_flags &= ~0x04;
1787 write_byte(0x0040, 0x97, led_flags);
1788 break;
1789 case 0xb6: /* R Shift release */
1790 shift_flags &= ~0x01;
1791 write_byte(0x0040, 0x17, shift_flags);
1792 break;
1794 case 0x1d: /* Ctrl press */
1795 shift_flags |= 0x04;
1796 write_byte(0x0040, 0x17, shift_flags);
1797 if (mf2_state & 0x01) {
1798 mf2_flags |= 0x04;
1799 } else {
1800 mf2_flags |= 0x01;
1802 write_byte(0x0040, 0x18, mf2_flags);
1803 break;
1804 case 0x9d: /* Ctrl release */
1805 shift_flags &= ~0x04;
1806 write_byte(0x0040, 0x17, shift_flags);
1807 if (mf2_state & 0x01) {
1808 mf2_flags &= ~0x04;
1809 } else {
1810 mf2_flags &= ~0x01;
1812 write_byte(0x0040, 0x18, mf2_flags);
1813 break;
1815 case 0x38: /* Alt press */
1816 shift_flags |= 0x08;
1817 write_byte(0x0040, 0x17, shift_flags);
1818 if (mf2_state & 0x01) {
1819 mf2_flags |= 0x08;
1820 } else {
1821 mf2_flags |= 0x02;
1823 write_byte(0x0040, 0x18, mf2_flags);
1824 break;
1825 case 0xb8: /* Alt release */
1826 shift_flags &= ~0x08;
1827 write_byte(0x0040, 0x17, shift_flags);
1828 if (mf2_state & 0x01) {
1829 mf2_flags &= ~0x08;
1830 } else {
1831 mf2_flags &= ~0x02;
1833 write_byte(0x0040, 0x18, mf2_flags);
1834 break;
1836 case 0x45: /* Num Lock press */
1837 if ((mf2_state & 0x01) == 0) {
1838 mf2_flags |= 0x20;
1839 write_byte(0x0040, 0x18, mf2_flags);
1840 if (shift_flags & 0x20) {
1841 shift_flags &= ~0x20;
1842 led_flags &= ~0x02;
1843 } else {
1844 shift_flags |= 0x20;
1845 led_flags |= 0x02;
1847 write_byte(0x0040, 0x17, shift_flags);
1848 write_byte(0x0040, 0x97, led_flags);
1850 break;
1851 case 0xc5: /* Num Lock release */
1852 if ((mf2_state & 0x01) == 0) {
1853 mf2_flags &= ~0x20;
1854 write_byte(0x0040, 0x18, mf2_flags);
1856 break;
1858 case 0x46: /* Scroll Lock press */
1859 mf2_flags |= 0x10;
1860 write_byte(0x0040, 0x18, mf2_flags);
1861 if (shift_flags & 0x10) {
1862 shift_flags &= ~0x10;
1863 led_flags &= ~0x01;
1864 } else {
1865 shift_flags |= 0x10;
1866 led_flags |= 0x01;
1868 write_byte(0x0040, 0x17, shift_flags);
1869 write_byte(0x0040, 0x97, led_flags);
1870 break;
1872 case 0xc6: /* Scroll Lock release */
1873 mf2_flags &= ~0x10;
1874 write_byte(0x0040, 0x18, mf2_flags);
1875 break;
1877 default:
1878 if (scancode & 0x80) return; /* toss key releases ... */
1879 if (scancode > MAX_SCAN_CODE) {
1880 panic("KBD: int09h_handler(): unknown scancode read!");
1881 return;
1883 if (shift_flags & 0x08) { /* ALT */
1884 asciicode = scan_to_scanascii[scancode].alt;
1885 scancode = scan_to_scanascii[scancode].alt >> 8;
1887 else if (shift_flags & 0x04) { /* CONTROL */
1888 asciicode = scan_to_scanascii[scancode].control;
1889 scancode = scan_to_scanascii[scancode].control >> 8;
1891 else if (shift_flags & 0x43) { /* CAPSLOCK + LSHIFT + RSHIFT */
1892 /* check if both CAPSLOCK and a SHIFT key are pressed */
1893 if ((shift_flags & 0x03) && (shift_flags & 0x40)) {
1894 asciicode = scan_to_scanascii[scancode].normal;
1895 scancode = scan_to_scanascii[scancode].normal >> 8;
1897 else {
1898 asciicode = scan_to_scanascii[scancode].shift;
1899 scancode = scan_to_scanascii[scancode].shift >> 8;
1902 else {
1903 asciicode = scan_to_scanascii[scancode].normal;
1904 scancode = scan_to_scanascii[scancode].normal >> 8;
1906 if (scancode==0 && asciicode==0) {
1907 panic("KBD: int09h_handler(): scancode & asciicode are zero?");
1909 enqueue_key(scancode, asciicode);
1910 break;
1912 mf2_state &= ~0x01;
1915 void
1916 enqueue_key(scan_code, ascii_code)
1917 Bit8u scan_code, ascii_code;
1919 Bit16u buffer_start, buffer_end, buffer_head, buffer_tail, temp_tail;
1921 //printf("KBD: enqueue_key() called scan:%02x, ascii:%02x\n",
1922 // scan_code, ascii_code);
1924 #if BX_CPU < 2
1925 buffer_start = 0x001E;
1926 buffer_end = 0x003E;
1927 #else
1928 buffer_start = read_word(0x0040, 0x0080);
1929 buffer_end = read_word(0x0040, 0x0082);
1930 #endif
1932 buffer_head = read_word(0x0040, 0x001A);
1933 buffer_tail = read_word(0x0040, 0x001C);
1935 temp_tail = buffer_tail;
1936 buffer_tail += 2;
1937 if (buffer_tail >= buffer_end)
1938 buffer_tail = buffer_start;
1940 if (buffer_tail == buffer_head) {
1941 panic("KBD: dropped key scan=%02x, ascii=%02x",
1942 (int) scan_code, (int) ascii_code);
1943 return;
1946 write_byte(0x0040, temp_tail, ascii_code);
1947 write_byte(0x0040, temp_tail+1, scan_code);
1948 write_word(0x0040, 0x001C, buffer_tail);
1952 void
1953 int74_function(make_farcall, Z, Y, X, status)
1954 Bit16u make_farcall, Z, Y, X, status;
1956 Bit8u in_byte, index, package_count;
1957 Bit16u ebda_seg;
1958 Bit8u mouse_flags_1, mouse_flags_2;
1960 printf("entering int74_function\n");
1961 make_farcall = 0;
1963 in_byte = inb(0x64);
1964 if ( (in_byte & 0x21) != 0x21 ) {
1965 return;
1967 in_byte = inb(0x60);
1968 printf("int74: read byte %02x\n", in_byte);
1970 ebda_seg = read_word(0x0040, 0x000E);
1971 mouse_flags_1 = read_byte(ebda_seg, 0x0026);
1972 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
1974 if ( (mouse_flags_2 & 0x80) != 0x80 ) {
1975 panic("int74_function:");
1978 package_count = mouse_flags_2 & 0x07;
1979 index = mouse_flags_1 & 0x07;
1980 write_byte(ebda_seg, 0x28 + index, in_byte);
1982 if ( (index+1) >= package_count ) {
1983 printf("int74_function: make_farcall=1\n");
1984 status = read_byte(ebda_seg, 0x0028 + 0);
1985 X = read_byte(ebda_seg, 0x0028 + 1);
1986 Y = read_byte(ebda_seg, 0x0028 + 2);
1987 Z = 0;
1988 mouse_flags_1 = 0;
1989 // check if far call handler installed
1990 if (mouse_flags_2 & 0x80)
1991 make_farcall = 1;
1993 else {
1994 mouse_flags_1++;
1996 write_byte(ebda_seg, 0x0026, mouse_flags_1);
2001 void
2002 outLBA(cylinder,hd_heads,head,hd_sectors,sector,dl)
2003 Bit16u cylinder;
2004 Bit16u hd_heads;
2005 Bit16u head;
2006 Bit16u hd_sectors;
2007 Bit16u sector;
2008 Bit16u dl;
2010 #asm
2011 push bp
2012 mov bp, sp
2013 push eax
2014 push ebx
2015 push edx
2016 xor eax,eax
2017 mov ax,4[bp]
2018 xor ebx,ebx
2019 mov bl,6[bp]
2020 imul ebx
2021 add al,8[bp]
2022 adc ah,#0
2023 mov bl,10[bp]
2024 imul ebx
2025 add al,12[bp]
2026 adc ah,#0
2027 dec eax
2028 mov dx,#0x1f3
2029 out dx,al
2030 mov dx,#0x1f4
2031 mov al,ah
2032 out dx,al
2033 shr eax,#16
2034 mov dx,#0x1f5
2035 out dx,al
2036 and ah,#0xf
2037 mov bl,14[bp]
2038 and bl,#1
2039 shl bl,#4
2040 or ah,bl
2041 or ah,#0xe0
2042 mov al,ah
2043 mov dx,#0x01f6
2044 out dx,al
2045 pop edx
2046 pop ebx
2047 pop eax
2048 pop bp
2049 #endasm
2053 void
2054 int13_function(DI, SI, BP, SP, BX, DX, CX, AX, ES, FLAGS)
2055 Bit16u DI, SI, BP, SP, BX, DX, CX, AX, ES, FLAGS;
2057 Bit8u drive, num_sectors, sector, head, status, mod;
2058 Bit8u n_drives;
2059 Bit16u cyl_mod, ax;
2060 Bit16u max_cylinder, cylinder, total_sectors;
2061 Bit16u hd_cylinders;
2062 Bit8u hd_heads, hd_sectors;
2063 Bit16u val16;
2064 Bit8u sector_count;
2065 unsigned int i;
2066 Bit16u tempbx;
2067 Bit16u lba;
2069 write_byte(0x0040, 0x008e, 0); // clear completion flag
2071 /* at this point, DL is >= 0x80 to be passed from the floppy int13h
2072 handler code */
2073 /* check how many disks first (cmos reg 0x12), return an error if
2074 DL > n_drives */
2075 n_drives = inb_cmos(0x12);
2076 n_drives = ((n_drives & 0xf0)==0) ? 0 :
2077 ((n_drives & 0x0f) ? 2 : 1);
2079 if (!((GET_DL()&0x7f) < n_drives)) { /* allow 0, 1, or 2 disks */
2080 SET_AH(0x01);
2081 set_disk_ret_status(0x01);
2082 SET_CF(); /* error occurred */
2083 return;
2086 switch (GET_AH()) {
2088 case 0x00: /* disk controller reset */
2089 printf("int13_f00\n");
2091 SET_AH(0);
2092 set_disk_ret_status(0);
2093 set_diskette_ret_status(0);
2094 set_diskette_current_cyl(0, 0); /* current cylinder, diskette 1 */
2095 set_diskette_current_cyl(1, 0); /* current cylinder, diskette 2 */
2096 CLEAR_CF(); /* successful */
2097 return;
2098 break;
2100 case 0x01: /* read disk status */
2101 printf("int13_f01\n");
2102 status = read_byte(0x0040, 0x0074);
2103 SET_AH(status);
2104 set_disk_ret_status(0);
2105 /* set CF if error status read */
2106 if (status) SET_CF();
2107 else CLEAR_CF();
2108 return;
2109 break;
2111 case 0x04: // verify disk sectors
2112 case 0x02: // read disk sectors
2113 drive = GET_DL();
2114 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
2116 num_sectors = GET_AL();
2117 cylinder = GET_CH();
2118 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
2119 sector = (GET_CL() & 0x3f);
2120 head = GET_DH();
2123 if (hd_cylinders > 1024) {
2124 if (hd_cylinders <= 2048) {
2125 cylinder <<= 1;
2127 else if (hd_cylinders <= 4096) {
2128 cylinder <<= 2;
2130 else if (hd_cylinders <= 8192) {
2131 cylinder <<= 3;
2133 else { // hd_cylinders <= 16384
2134 cylinder <<= 4;
2137 ax = UDIV(head, hd_heads);
2138 cyl_mod = ax & 0xff;
2139 head = ax >> 8;
2140 cylinder |= cyl_mod;
2143 if ( (cylinder >= hd_cylinders) ||
2144 (sector > hd_sectors) ||
2145 (head >= hd_heads) ) {
2146 SET_AH(1);
2147 set_disk_ret_status(1);
2148 SET_CF(); /* error occurred */
2149 return;
2152 if ( (num_sectors > 128) || (num_sectors == 0) )
2153 panic("int13_function(): num_sectors out of range!");
2155 if (head > 15)
2156 panic("hard drive BIOS:(read/verify) head > 15\n");
2158 if ( GET_AH() == 0x04 ) {
2159 SET_AH(0);
2160 set_disk_ret_status(0);
2161 CLEAR_CF();
2162 return;
2165 status = inb(0x1f7);
2166 if (status & 0x80) {
2167 panic("hard drive BIOS:(read/verify) BUSY bit set");
2169 outb(0x01f2, num_sectors);
2170 /* activate LBA? (tomv) */
2171 if (hd_heads > 15) {
2172 printf("CHS: %x %x %x\n", cylinder, head, sector);
2173 outLBA(cylinder,hd_heads,head,hd_sectors,sector,drive);
2175 else {
2176 outb(0x01f3, sector);
2177 outb(0x01f4, cylinder & 0x00ff);
2178 outb(0x01f5, cylinder >> 8);
2179 outb(0x01f6, 0xa0 | ((drive & 0x01)<<4) | (head & 0x0f));
2181 outb(0x01f7, 0x20);
2183 while (1) {
2184 status = inb(0x1f7);
2185 if ( !(status & 0x80) ) break;
2188 if (status & 0x01) {
2189 panic("hard drive BIOS:(read/verify) read error");
2190 } else if ( !(status & 0x08) ) {
2191 printf("status was %02x\n", (unsigned) status);
2192 panic("hard drive BIOS:(read/verify) expected DRQ=1");
2195 sector_count = 0;
2196 tempbx = BX;
2198 #asm
2199 sti ;; enable higher priority interrupts
2200 #endasm
2202 while (1) {
2203 #asm
2204 ;; store temp bx in real DI register
2205 push bp
2206 mov bp, sp
2207 mov di, _int13_function.tempbx + 2 [bp]
2208 pop bp
2210 ;; adjust if there will be an overrun
2211 cmp di, #0xfe00
2212 jbe i13_f02_no_adjust
2213 i13_f02_adjust:
2214 sub di, #0x0200 ; sub 512 bytes from offset
2215 mov ax, es
2216 add ax, #0x0020 ; add 512 to segment
2217 mov es, ax
2219 i13_f02_no_adjust:
2220 mov cx, #0x0100 ;; counter (256 words = 512b)
2221 mov dx, #0x01f0 ;; AT data read port
2224 insw ;; CX words transfered from port(DX) to ES:[DI]
2226 i13_f02_done:
2227 ;; store real DI register back to temp bx
2228 push bp
2229 mov bp, sp
2230 mov _int13_function.tempbx + 2 [bp], di
2231 pop bp
2232 #endasm
2234 sector_count++;
2235 num_sectors--;
2236 if (num_sectors == 0) {
2237 status = inb(0x1f7);
2238 if ( (status & 0xc9) != 0x40 )
2239 panic("no sectors left to read/verify, status is %02x", (unsigned) status);
2240 break;
2242 else {
2243 status = inb(0x1f7);
2244 if ( (status & 0xc9) != 0x48 )
2245 panic("more sectors left to read/verify, status is %02x", (unsigned) status);
2246 continue;
2250 SET_AH(0);
2251 set_disk_ret_status(0);
2252 SET_AL(sector_count);
2253 CLEAR_CF(); /* successful */
2254 return;
2255 break;
2258 case 0x03: /* write disk sectors */
2259 printf("int13_f03\n");
2260 drive = GET_DL ();
2261 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
2263 num_sectors = GET_AL();
2264 cylinder = GET_CH();
2265 cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
2266 sector = (GET_CL() & 0x3f);
2267 head = GET_DH();
2269 if (hd_cylinders > 1024) {
2270 if (hd_cylinders <= 2048) {
2271 cylinder <<= 1;
2273 else if (hd_cylinders <= 4096) {
2274 cylinder <<= 2;
2276 else if (hd_cylinders <= 8192) {
2277 cylinder <<= 3;
2279 else { // hd_cylinders <= 16384
2280 cylinder <<= 4;
2283 ax = UDIV(head, hd_heads);
2284 cyl_mod = ax & 0xff;
2285 head = ax >> 8;
2286 cylinder |= cyl_mod;
2289 if ( (cylinder >= hd_cylinders) ||
2290 (sector > hd_sectors) ||
2291 (head >= hd_heads) ) {
2292 SET_AH( 1);
2293 set_disk_ret_status(1);
2294 SET_CF(); /* error occurred */
2295 return;
2298 if ( (num_sectors > 128) || (num_sectors == 0) )
2299 panic("int13_function(): num_sectors out of range!");
2301 if (head > 15)
2302 panic("hard drive BIOS:(read) head > 15\n");
2304 status = inb(0x1f7);
2305 if (status & 0x80) {
2306 panic("hard drive BIOS:(read) BUSY bit set");
2308 // should check for Drive Ready Bit also in status reg
2309 outb(0x01f2, num_sectors);
2311 /* activate LBA? (tomv) */
2312 if (hd_heads > 15) {
2313 printf("CHS (write): %x %x %x\n", cylinder, head, sector);
2314 outLBA(cylinder,hd_heads,head,hd_sectors,sector,GET_DL());
2316 else {
2317 outb(0x01f3, sector);
2318 outb(0x01f4, cylinder & 0x00ff);
2319 outb(0x01f5, cylinder >> 8);
2320 outb(0x01f6, 0xa0 | ((GET_DL() & 0x01)<<4) | (head & 0x0f));
2322 outb(0x01f7, 0x30);
2324 // wait for busy bit to turn off after seeking
2325 while (1) {
2326 status = inb(0x1f7);
2327 if ( !(status & 0x80) ) break;
2330 if ( !(status & 0x08) ) {
2331 printf("status was %02x\n", (unsigned) status);
2332 panic("hard drive BIOS:(write) data-request bit not set");
2335 sector_count = 0;
2336 tempbx = BX;
2338 #asm
2339 sti ;; enable higher priority interrupts
2340 #endasm
2342 while (1) {
2343 #asm
2344 ;; store temp bx in real SI register
2345 push bp
2346 mov bp, sp
2347 mov si, _int13_function.tempbx + 2 [bp]
2348 pop bp
2350 ;; adjust if there will be an overrun
2351 cmp si, #0xfe00
2352 jbe i13_f03_no_adjust
2353 i13_f03_adjust:
2354 sub si, #0x0200 ; sub 512 bytes from offset
2355 mov ax, es
2356 add ax, #0x0020 ; add 512 to segment
2357 mov es, ax
2359 i13_f03_no_adjust:
2360 mov cx, #0x0100 ;; counter (256 words = 512b)
2361 mov dx, #0x01f0 ;; AT data read port
2363 seg ES
2365 outsw ;; CX words tranfered from ES:[SI] to port(DX)
2367 ;; store real SI register back to temp bx
2368 push bp
2369 mov bp, sp
2370 mov _int13_function.tempbx + 2 [bp], si
2371 pop bp
2372 #endasm
2374 sector_count++;
2375 num_sectors--;
2376 if (num_sectors == 0) {
2377 status = inb(0x1f7);
2378 if ( (status & 0xe9) != 0x40 )
2379 panic("no sectors left to write, status is %02x", (unsigned) status);
2380 break;
2382 else {
2383 status = inb(0x1f7);
2384 if ( (status & 0xc9) != 0x48 )
2385 panic("more sectors left to write, status is %02x", (unsigned) status);
2386 continue;
2390 SET_AH(0);
2391 set_disk_ret_status(0);
2392 SET_AL(sector_count);
2393 CLEAR_CF(); /* successful */
2394 return;
2395 break;
2397 case 0x05: /* format disk track */
2398 printf("int13_f05\n");
2399 panic("format disk track called");
2400 /* nop */
2401 SET_AH(0);
2402 set_disk_ret_status(0);
2403 CLEAR_CF(); /* successful */
2404 return;
2405 break;
2407 case 0x08: /* read disk drive parameters */
2408 printf("int13_f08\n");
2409 drive = GET_DL ();
2410 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
2412 // translate CHS
2414 if (hd_cylinders <= 1024) {
2415 // hd_cylinders >>= 0;
2416 // hd_heads <<= 0;
2418 else if (hd_cylinders <= 2048) {
2419 hd_cylinders >>= 1;
2420 hd_heads <<= 1;
2422 else if (hd_cylinders <= 4096) {
2423 hd_cylinders >>= 2;
2424 hd_heads <<= 2;
2426 else if (hd_cylinders <= 8192) {
2427 hd_cylinders >>= 3;
2428 hd_heads <<= 3;
2430 else { // hd_cylinders <= 16384
2431 hd_cylinders >>= 4;
2432 hd_heads <<= 4;
2435 max_cylinder = hd_cylinders - 2; /* 0 based */
2436 SET_AL(0);
2437 SET_CH(max_cylinder & 0xff);
2438 SET_CL(((max_cylinder >> 2) & 0xc0) | (hd_sectors & 0x3f));
2439 SET_DH(hd_heads - 1);
2440 SET_DL(n_drives); /* returns 0, 1, or 2 hard drives */
2441 SET_AH(0);
2442 set_disk_ret_status(0);
2443 CLEAR_CF(); /* successful */
2444 return;
2445 break;
2447 case 0x09: /* initialize drive parameters */
2448 printf("int13_f09\n");
2449 SET_AH(0);
2450 set_disk_ret_status(0);
2451 CLEAR_CF(); /* successful */
2452 return;
2453 break;
2455 case 0x0a: /* read disk sectors with ECC */
2456 printf("int13_f0a\n");
2457 case 0x0b: /* write disk sectors with ECC */
2458 printf("int13_f0b\n");
2459 panic("int13h Functions 0Ah & 0Bh not implemented!");
2460 return;
2461 break;
2463 case 0x0c: /* seek to specified cylinder */
2464 printf("int13_f0c\n");
2465 printf("int13h function 0ch (seek) not implemented!\n");
2466 SET_AH(0);
2467 set_disk_ret_status(0);
2468 CLEAR_CF(); /* successful */
2469 return;
2470 break;
2472 case 0x0d: /* alternate disk reset */
2473 printf("int13_f0d\n");
2474 SET_AH(0);
2475 set_disk_ret_status(0);
2476 CLEAR_CF(); /* successful */
2477 return;
2478 break;
2480 case 0x10: /* check drive ready */
2481 printf("int13_f10\n");
2482 //SET_AH(0);
2483 //set_disk_ret_status(0);
2484 //CLEAR_CF(); /* successful */
2485 //return;
2486 //break;
2488 // should look at 40:8E also???
2489 status = inb(0x01f7);
2490 if ( (status & 0xc0) == 0x40 ) {
2491 SET_AH(0);
2492 set_disk_ret_status(0);
2493 CLEAR_CF(); // drive ready
2494 return;
2496 else {
2497 SET_AH(0xAA);
2498 set_disk_ret_status(0xAA);
2499 SET_CF(); // not ready
2500 return;
2502 break;
2504 case 0x11: /* recalibrate */
2505 printf("int13_f11\n");
2506 SET_AH(0);
2507 set_disk_ret_status(0);
2508 CLEAR_CF(); /* successful */
2509 return;
2510 break;
2512 case 0x14: /* controller internal diagnostic */
2513 printf("int13_f14\n");
2514 SET_AH(0);
2515 set_disk_ret_status(0);
2516 CLEAR_CF(); /* successful */
2517 SET_AL(0);
2518 return;
2519 break;
2521 case 0x15: /* read disk drive size */
2522 drive = GET_DL();
2523 get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
2524 #asm
2525 push bp
2526 mov bp, sp
2527 mov al, _int13_function.hd_heads + 2 [bp]
2528 mov ah, _int13_function.hd_sectors + 2 [bp]
2529 mul al, ah ;; ax = heads * sectors
2530 mov bx, _int13_function.hd_cylinders + 2 [bp]
2531 dec bx ;; use (cylinders - 1) ???
2532 mul ax, bx ;; dx:ax = (cylinders -1) * (heads * sectors)
2533 ;; now we need to move the 32bit result dx:ax to what the
2534 ;; BIOS wants which is cx:dx.
2535 ;; and then into CX:DX on the stack
2536 mov _int13_function.CX + 2 [bp], dx
2537 mov _int13_function.DX + 2 [bp], ax
2538 pop bp
2539 #endasm
2540 SET_AH(3); // hard disk accessible
2541 set_disk_ret_status(0); // ??? should this be 0
2542 CLEAR_CF(); // successful
2543 return;
2544 break;
2546 case 0x18: /* */
2547 case 0x41: // IBM/MS installation check
2548 case 0x42: // IBM/MS extended read
2549 case 0x43: // IBM/MS extended write
2550 case 0x44: // IBM/MS verify sectors
2551 case 0x45: // IBM/MS lock/unlock drive
2552 case 0x46: // IBM/MS eject media
2553 case 0x47: // IBM/MS extended seek
2554 case 0x48: // IBM/MS get drive parameters
2555 case 0x49: // IBM/MS extended media change
2556 printf("int13_f18,41-49\n");
2557 SET_AH(1); // code=invalid function in AH or invalid parameter
2558 set_disk_ret_status(1);
2559 SET_CF(); /* unsuccessful */
2560 return;
2561 break;
2563 default:
2564 panic("case 0x%x found in int13_function()", (unsigned) GET_AH());
2565 break;
2570 //////////////////////
2571 // FLOPPY functions //
2572 //////////////////////
2574 Boolean
2575 floppy_media_known(drive)
2576 Bit16u drive;
2578 Bit8u val8;
2579 Bit16u media_state_offset;
2581 val8 = read_byte(0x0040, 0x003e); // diskette recal status
2582 if (drive)
2583 val8 >>= 1;
2584 val8 &= 0x01;
2585 if (val8 == 0)
2586 return(0);
2588 media_state_offset = 0x0090;
2589 if (drive)
2590 media_state_offset += 1;
2592 val8 = read_byte(0x0040, media_state_offset);
2593 val8 = (val8 >> 4) & 0x01;
2594 if (val8 == 0)
2595 return(0);
2597 // check pass, return KNOWN
2598 return(1);
2601 Boolean
2602 floppy_media_sense(drive)
2603 Bit16u drive;
2605 Boolean retval;
2606 Bit16u media_state_offset;
2607 Bit8u drive_type, config_data, media_state;
2609 if (floppy_drive_recal(drive) == 0) {
2610 return(0);
2613 // for now cheat and get drive type from CMOS,
2614 // assume media is same as drive type
2615 drive_type = inb_cmos(0x10);
2616 if (drive == 0)
2617 drive_type >>= 4;
2618 else
2619 drive_type &= 0x0f;
2620 if ( drive_type == 2 ) {
2621 // 1.2 MB 5.25" drive
2622 config_data = 0x00; // 0000 0000
2623 media_state = 0x25; // 0001 0101
2624 retval = 1;
2626 else if ( drive_type == 3 ) {
2627 // 720K 3.5" drive
2628 config_data = 0x00; // 0000 0000 ???
2629 media_state = 0x17; // 0001 0111
2630 retval = 1;
2632 else if ( drive_type == 4 ) {
2633 // 1.44 MB 3.5" drive
2634 config_data = 0x00; // 0000 0000
2635 media_state = 0x17; // 0001 0111
2636 retval = 1;
2638 else if ( drive_type == 5 ) {
2639 // 2.88 MB 3.5" drive
2640 config_data = 0xCC; // 1100 1100
2641 media_state = 0xD7; // 1101 0111
2642 retval = 1;
2644 else {
2645 // not recognized
2646 config_data = 0x00; // 0000 0000
2647 media_state = 0x00; // 0000 0000
2648 retval = 0;
2651 if (drive == 0)
2652 media_state_offset = 0x90;
2653 else
2654 media_state_offset = 0x91;
2655 write_byte(0x0040, 0x008B, config_data);
2656 write_byte(0x0040, media_state_offset, media_state);
2658 return(retval);
2661 Boolean
2662 floppy_drive_recal(drive)
2663 Bit16u drive;
2665 Bit8u val8, dor;
2666 Bit16u curr_cyl_offset;
2668 // set 40:3e bit 7 to 0
2669 val8 = read_byte(0x0000, 0x043e);
2670 val8 &= 0x7f;
2671 write_byte(0x0000, 0x043e, val8);
2673 // turn on motor of selected drive, DMA & int enabled, normal operation
2674 if (drive)
2675 dor = 0x20;
2676 else
2677 dor = 0x10;
2678 dor |= 0x0c;
2679 dor |= drive;
2680 outb(0x03f2, dor);
2682 // check port 3f4 for drive readiness
2683 val8 = inb(0x3f4);
2684 if ( (val8 & 0xf0) != 0x80 )
2685 panic("floppy recal:f07: ctrl not ready");
2687 // send Recalibrate command (2 bytes) to controller
2688 outb(0x03f5, 0x07); // 07: Recalibrate
2689 outb(0x03f5, drive); // 0=drive0, 1=drive1
2691 // turn on interrupts
2692 #asm
2694 #endasm
2696 // wait on 40:3e bit 7 to become 1
2697 val8 = (read_byte(0x0000, 0x043e) & 0x80);
2698 while ( val8 == 0 ) {
2699 val8 = (read_byte(0x0000, 0x043e) & 0x80);
2702 val8 = 0; // separate asm from while() loop
2703 // turn off interrupts
2704 #asm
2706 #endasm
2708 // set 40:3e bit 7 to 0, and calibrated bit
2709 val8 = read_byte(0x0000, 0x043e);
2710 val8 &= 0x7f;
2711 if (drive) {
2712 val8 |= 0x02; // Drive 1 calibrated
2713 curr_cyl_offset = 0x0095;
2715 else {
2716 val8 |= 0x01; // Drive 0 calibrated
2717 curr_cyl_offset = 0x0094;
2719 write_byte(0x0040, 0x003e, val8);
2720 write_byte(0x0040, curr_cyl_offset, 0); // current cylinder is 0
2722 return(1);
2727 Boolean
2728 floppy_drive_exists(drive)
2729 Bit16u drive;
2731 Bit8u drive_type;
2733 // check CMOS to see if drive exists
2734 drive_type = inb_cmos(0x10);
2735 if (drive == 0)
2736 drive_type >>= 4;
2737 else
2738 drive_type &= 0x0f;
2739 if ( drive_type == 0 )
2740 return(0);
2741 else
2742 return(1);
2746 #if BX_SUPPORT_FLOPPY
2747 void
2748 int13_diskette_function(DI, SI, BP, SP, BX, DX, CX, AX, ES, FLAGS)
2749 Bit16u DI, SI, BP, SP, BX, DX, CX, AX, ES, FLAGS;
2751 Bit8u drive, num_sectors, track, sector, head, status;
2752 Bit16u base_address, base_count, base_es;
2753 Bit8u page, mode_register, val8, dor;
2754 Bit8u return_status[7];
2755 Bit8u drive_type, num_floppies, ah;
2756 Bit16u es, last_addr;
2758 //printf("BIOS: int13: AX=%04x BX=%04x CX=%04x DX=%04x\n", AX, BX, CX, DX);
2760 ah = GET_AH();
2762 switch ( ah ) {
2763 case 0x00: // diskette controller reset
2764 printf("floppy f00\n");
2765 drive = GET_DL();
2766 if (drive > 1) {
2767 SET_AH(1); // invalid param
2768 set_diskette_ret_status(1);
2769 SET_CF();
2770 return;
2772 drive_type = inb_cmos(0x10);
2774 if (drive == 0)
2775 drive_type >>= 4;
2776 else
2777 drive_type &= 0x0f;
2778 if (drive_type == 0) {
2779 SET_AH(0x80); // drive not responding
2780 set_diskette_ret_status(0x80);
2781 SET_CF();
2782 return;
2784 SET_AH(0);
2785 set_diskette_ret_status(0);
2786 CLEAR_CF(); // successful
2787 set_diskette_current_cyl(drive, 0); // current cylinder
2788 return;
2790 case 0x01: // Read Diskette Status
2791 CLEAR_CF();
2792 val8 = read_byte(0x0000, 0x0441);
2793 SET_AH(val8);
2794 if (val8) {
2795 SET_CF();
2797 return;
2799 case 0x02: // Read Diskette Sectors
2800 case 0x03: // Write Diskette Sectors
2801 case 0x04: // Verify Diskette Sectors
2802 num_sectors = GET_AL();
2803 track = GET_CH();
2804 sector = GET_CL();
2805 head = GET_DH();
2806 drive = GET_DL();
2808 if ( (drive > 1) || (head > 1) ||
2809 (num_sectors == 0) || (num_sectors > 72) ) {
2810 printf("floppy: drive>1 || head>1 ...\n");
2811 SET_AH(1);
2812 set_diskette_ret_status(1);
2813 SET_AL(0); // no sectors read
2814 SET_CF(); // error occurred
2815 return;
2818 // see if drive exists
2819 if (floppy_drive_exists(drive) == 0) {
2820 SET_AH(0x80); // not responding
2821 set_diskette_ret_status(0x80);
2822 SET_AL(0); // no sectors read
2823 SET_CF(); // error occurred
2824 return;
2827 // see if media in drive, and type is known
2828 if (floppy_media_known(drive) == 0) {
2829 if (floppy_media_sense(drive) == 0) {
2830 SET_AH(0x0C); // Media type not found
2831 set_diskette_ret_status(0x0C);
2832 SET_AL(0); // no sectors read
2833 SET_CF(); // error occurred
2834 return;
2838 if (ah == 0x02) {
2839 // Read Diskette Sectors
2841 //-----------------------------------
2842 // set up DMA controller for transfer
2843 //-----------------------------------
2845 // es:bx = pointer to where to place information from diskette
2846 // port 04: DMA-1 base and current address, channel 2
2847 // port 05: DMA-1 base and current count, channel 2
2848 page = (ES >> 12); // upper 4 bits
2849 base_es = (ES << 4); // lower 16bits contributed by ES
2850 base_address = base_es + BX; // lower 16 bits of address
2851 // contributed by ES:BX
2852 if ( base_address < base_es ) {
2853 // in case of carry, adjust page by 1
2854 page++;
2856 base_count = (num_sectors * 512) - 1;
2858 // check for 64K boundary overrun
2859 last_addr = base_address + base_count;
2860 if (last_addr < base_address) {
2861 SET_AH(0x09);
2862 set_diskette_ret_status(0x09);
2863 SET_AL(0); // no sectors read
2864 SET_CF(); // error occurred
2865 return;
2868 printf("masking DMA-1 c2\n");
2869 outb(0x000a, 0x06);
2871 printf("clear flip-flop\n");
2872 outb(0x000c, 0x00); // clear flip-flop
2873 outb(0x0004, base_address);
2874 outb(0x0004, base_address>>8);
2875 printf("clear flip-flop\n");
2876 outb(0x000c, 0x00); // clear flip-flop
2877 outb(0x0005, base_count);
2878 outb(0x0005, base_count>>8);
2880 // port 0b: DMA-1 Mode Register
2881 mode_register = 0x46; // single mode, increment, autoinit disable,
2882 // transfer type=write, channel 2
2883 printf("setting mode register\n");
2884 outb(0x000b, mode_register);
2886 printf("setting page register\n");
2887 // port 81: DMA-1 Page Register, channel 2
2888 outb(0x0081, page);
2890 printf("unmask chan 2\n");
2891 outb(0x000a, 0x02); // unmask channel 2
2893 printf("unmasking DMA-1 c2\n");
2894 outb(0x000a, 0x02);
2896 //--------------------------------------
2897 // set up floppy controller for transfer
2898 //--------------------------------------
2900 // set 40:3e bit 7 to 0
2901 val8 = read_byte(0x0000, 0x043e);
2902 val8 &= 0x7f;
2903 write_byte(0x0000, 0x043e, val8);
2905 // turn on motor of selected drive, DMA & int enabled, normal operation
2906 if (drive)
2907 dor = 0x20;
2908 else
2909 dor = 0x10;
2910 dor |= 0x0c;
2911 dor |= drive;
2912 outb(0x03f2, dor);
2914 // check port 3f4 for drive readiness
2915 val8 = inb(0x3f4);
2916 if ( (val8 & 0xf0) != 0x80 )
2917 panic("int13_diskette:f02: ctrl not ready");
2919 // send read-normal-data command (9 bytes) to controller
2920 outb(0x03f5, 0xe6); // e6: read normal data
2921 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
2922 outb(0x03f5, track);
2923 outb(0x03f5, head);
2924 outb(0x03f5, sector);
2925 outb(0x03f5, 2); // 512 byte sector size
2926 outb(0x03f5, 0); // last sector number possible on track
2927 outb(0x03f5, 0); // Gap length
2928 outb(0x03f5, 0xff); // Gap length
2930 // turn on interrupts
2931 #asm
2933 #endasm
2935 // wait on 40:3e bit 7 to become 1
2936 val8 = (read_byte(0x0000, 0x043e) & 0x80);
2937 while ( val8 == 0 ) {
2938 val8 = (read_byte(0x0000, 0x043e) & 0x80);
2941 val8 = 0; // separate asm from while() loop
2942 // turn off interrupts
2943 #asm
2945 #endasm
2947 // set 40:3e bit 7 to 0
2948 val8 = read_byte(0x0000, 0x043e);
2949 val8 &= 0x7f;
2950 write_byte(0x0000, 0x043e, val8);
2952 // check port 3f4 for accessibility to status bytes
2953 val8 = inb(0x3f4);
2954 if ( (val8 & 0xc0) != 0xc0 )
2955 panic("int13_diskette: ctrl not ready");
2957 // read 7 return status bytes from controller
2958 // using loop index broken, have to unroll...
2959 return_status[0] = inb(0x3f5);
2960 return_status[1] = inb(0x3f5);
2961 return_status[2] = inb(0x3f5);
2962 return_status[3] = inb(0x3f5);
2963 return_status[4] = inb(0x3f5);
2964 return_status[5] = inb(0x3f5);
2965 return_status[6] = inb(0x3f5);
2966 // record in BIOS Data Area
2967 write_byte(0x0040, 0x0042, return_status[0]);
2968 write_byte(0x0040, 0x0043, return_status[1]);
2969 write_byte(0x0040, 0x0044, return_status[2]);
2970 write_byte(0x0040, 0x0045, return_status[3]);
2971 write_byte(0x0040, 0x0046, return_status[4]);
2972 write_byte(0x0040, 0x0047, return_status[5]);
2973 write_byte(0x0040, 0x0048, return_status[6]);
2975 if ( (return_status[0] & 0xc0) != 0 ) {
2976 SET_AH(0x20);
2977 set_diskette_ret_status(0x20);
2978 SET_AL(0); // no sectors read
2979 SET_CF(); // error occurred
2980 return;
2983 // ??? should track be new val from return_status[3] ?
2984 set_diskette_current_cyl(drive, track);
2985 // AL = number of sectors read (same value as passed)
2986 SET_AH(0x00); // success
2987 CLEAR_CF(); // success
2988 return;
2990 else if (ah == 0x03) {
2991 // Write Diskette Sectors
2993 //-----------------------------------
2994 // set up DMA controller for transfer
2995 //-----------------------------------
2997 // es:bx = pointer to where to place information from diskette
2998 // port 04: DMA-1 base and current address, channel 2
2999 // port 05: DMA-1 base and current count, channel 2
3000 page = (ES >> 12); // upper 4 bits
3001 base_es = (ES << 4); // lower 16bits contributed by ES
3002 base_address = base_es + BX; // lower 16 bits of address
3003 // contributed by ES:BX
3004 if ( base_address < base_es ) {
3005 // in case of carry, adjust page by 1
3006 page++;
3008 base_count = (num_sectors * 512) - 1;
3010 // check for 64K boundary overrun
3011 last_addr = base_address + base_count;
3012 if (last_addr < base_address) {
3013 SET_AH(0x09);
3014 set_diskette_ret_status(0x09);
3015 SET_AL(0); // no sectors read
3016 SET_CF(); // error occurred
3017 return;
3020 printf("masking DMA-1 c2\n");
3021 outb(0x000a, 0x06);
3023 outb(0x000c, 0x00); // clear flip-flop
3024 outb(0x0004, base_address);
3025 outb(0x0004, base_address>>8);
3026 outb(0x000c, 0x00); // clear flip-flop
3027 outb(0x0005, base_count);
3028 outb(0x0005, base_count>>8);
3030 // port 0b: DMA-1 Mode Register
3031 mode_register = 0x4a; // single mode, increment, autoinit disable,
3032 // transfer type=read, channel 2
3033 outb(0x000b, mode_register);
3035 // port 81: DMA-1 Page Register, channel 2
3036 outb(0x0081, page);
3038 printf("unmasking DMA-1 c2\n");
3039 outb(0x000a, 0x02);
3041 //--------------------------------------
3042 // set up floppy controller for transfer
3043 //--------------------------------------
3045 // set 40:3e bit 7 to 0
3046 val8 = read_byte(0x0000, 0x043e);
3047 val8 &= 0x7f;
3048 write_byte(0x0000, 0x043e, val8);
3050 // turn on motor of selected drive, DMA & int enabled, normal operation
3051 if (drive)
3052 dor = 0x20;
3053 else
3054 dor = 0x10;
3055 dor |= 0x0c;
3056 dor |= drive;
3057 outb(0x03f2, dor);
3059 // check port 3f4 for drive readiness
3060 val8 = inb(0x3f4);
3061 if ( (val8 & 0xf0) != 0x80 )
3062 panic("int13_diskette:f03: ctrl not ready");
3064 // send read-normal-data command (9 bytes) to controller
3065 outb(0x03f5, 0xc5); // c5: write normal data
3066 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
3067 outb(0x03f5, track);
3068 outb(0x03f5, head);
3069 outb(0x03f5, sector);
3070 outb(0x03f5, 2); // 512 byte sector size
3071 outb(0x03f5, 0); // last sector number possible on track
3072 outb(0x03f5, 0); // Gap length
3073 outb(0x03f5, 0xff); // Gap length
3075 // turn on interrupts
3076 #asm
3078 #endasm
3080 // wait on 40:3e bit 7 to become 1
3081 val8 = (read_byte(0x0000, 0x043e) & 0x80);
3082 while ( val8 == 0 ) {
3083 val8 = (read_byte(0x0000, 0x043e) & 0x80);
3086 val8 = 0; // separate asm from while() loop
3087 // turn off interrupts
3088 #asm
3090 #endasm
3092 // set 40:3e bit 7 to 0
3093 val8 = read_byte(0x0000, 0x043e);
3094 val8 &= 0x7f;
3095 write_byte(0x0000, 0x043e, val8);
3097 // check port 3f4 for accessibility to status bytes
3098 val8 = inb(0x3f4);
3099 if ( (val8 & 0xc0) != 0xc0 )
3100 panic("int13_diskette: ctrl not ready");
3102 // read 7 return status bytes from controller
3103 // using loop index broken, have to unroll...
3104 return_status[0] = inb(0x3f5);
3105 return_status[1] = inb(0x3f5);
3106 return_status[2] = inb(0x3f5);
3107 return_status[3] = inb(0x3f5);
3108 return_status[4] = inb(0x3f5);
3109 return_status[5] = inb(0x3f5);
3110 return_status[6] = inb(0x3f5);
3111 // record in BIOS Data Area
3112 write_byte(0x0040, 0x0042, return_status[0]);
3113 write_byte(0x0040, 0x0043, return_status[1]);
3114 write_byte(0x0040, 0x0044, return_status[2]);
3115 write_byte(0x0040, 0x0045, return_status[3]);
3116 write_byte(0x0040, 0x0046, return_status[4]);
3117 write_byte(0x0040, 0x0047, return_status[5]);
3118 write_byte(0x0040, 0x0048, return_status[6]);
3120 if ( (return_status[0] & 0xc0) != 0 ) {
3121 if ( (return_status[1] & 0x02) != 0 ) {
3122 // diskette not writable.
3123 // AH=status code=0x03 (tried to write on write-protected disk)
3124 // AL=number of sectors written=0
3125 AX = 0x0300;
3126 SET_CF();
3127 return;
3128 } else {
3129 panic("int13_diskette_function: read error");
3133 // ??? should track be new val from return_status[3] ?
3134 set_diskette_current_cyl(drive, track);
3135 // AL = number of sectors read (same value as passed)
3136 SET_AH(0x00); // success
3137 CLEAR_CF(); // success
3138 return;
3140 else { // if (ah == 0x04)
3141 // Verify Diskette Sectors
3143 // ??? should track be new val from return_status[3] ?
3144 set_diskette_current_cyl(drive, track);
3145 // AL = number of sectors verified (same value as passed)
3146 CLEAR_CF(); // success
3147 SET_AH(0x00); // success
3148 return;
3152 case 0x05: // format diskette track
3153 printf("floppy f05\n");
3155 num_sectors = GET_AL();
3156 track = GET_CH();
3157 head = GET_DH();
3158 drive = GET_DL();
3160 if (drive > 1) {
3161 SET_AH(1);
3162 set_diskette_ret_status(1);
3163 SET_CF(); // error occurred
3165 drive_type = inb_cmos(0x10);
3166 if (drive == 0)
3167 drive_type >>= 4;
3168 else
3169 drive_type &= 0x0f;
3170 if (drive_type == 0) {
3171 SET_AH(0x80); // drive not responding
3172 set_diskette_ret_status(0x80);
3173 SET_CF(); // error occurred
3174 return;
3177 /* nop */
3178 SET_AH(0);
3179 set_diskette_ret_status(0);
3180 set_diskette_current_cyl(drive, track);
3181 CLEAR_CF(); // successful
3182 return;
3185 case 0x08: // read diskette drive parameters
3186 printf("floppy f08\n");
3187 drive = GET_DL();
3189 if (drive>1) {
3190 AX = 0;
3191 BX = 0;
3192 CX = 0;
3193 DX = 0;
3194 //ES = 0; // ???
3195 SET_DL(num_floppies);
3196 //set_diskette_ret_status(AH=1);
3197 SET_CF();
3198 return;
3201 drive_type = inb_cmos(0x10);
3202 num_floppies = 0;
3203 if (drive_type & 0xf0)
3204 num_floppies++;
3205 if (drive_type & 0x0f)
3206 num_floppies++;
3208 if (drive == 0)
3209 drive_type >>= 4;
3210 else
3211 drive_type &= 0x0f;
3214 SET_BH(0);
3215 SET_BL(drive_type);
3216 SET_AH(0);
3217 SET_AL(0);
3218 SET_DL(num_floppies);
3220 switch (drive_type) {
3221 case 0: // none
3222 CX = 0;
3223 SET_DH(0); // max head #
3224 break;
3226 case 1: // 360KB, 5.25"
3227 CX = 0x2709; // 40 tracks, 9 sectors
3228 SET_DH(1); // max head #
3229 break;
3231 case 2: // 1.2MB, 5.25"
3232 CX = 0x4f0f; // 80 tracks, 15 sectors
3233 SET_DH(1); // max head #
3234 break;
3236 case 3: // 720KB, 3.5"
3237 CX = 0x4f09; // 80 tracks, 9 sectors
3238 SET_DH(1); // max head #
3239 break;
3241 case 4: // 1.44MB, 3.5"
3242 CX = 0x4f12; // 80 tracks, 18 sectors
3243 SET_DH(1); // max head #
3244 break;
3246 case 5: // 2.88MB, 3.5"
3247 CX = 0x4f24; // 80 tracks, 36 sectors
3248 SET_DH(1); // max head #
3249 break;
3251 default: // ?
3252 panic("floppy: int13: bad floppy type");
3255 /* set es & di to point to 11 byte diskette param table */
3256 DI = read_word(0x0000, 0x0078);
3257 ES = read_word(0x0000, 0x007a);
3258 CLEAR_CF(); // success
3259 /* disk status not changed upon success */
3260 return;
3263 case 0x15: // read diskette drive type
3264 printf("floppy f15\n");
3265 drive = GET_DL();
3266 if (drive > 1) {
3267 SET_AH(0); // only 2 drives supported
3268 // set_diskette_ret_status here ???
3269 SET_CF();
3270 return;
3272 drive_type = inb_cmos(0x10);
3274 if (drive == 0)
3275 drive_type >>= 4;
3276 else
3277 drive_type &= 0x0f;
3278 CLEAR_CF(); // successful, not present
3279 if (drive_type==0) {
3280 SET_AH(0); // drive not present
3282 else {
3283 SET_AH(1); // drive present, does not support change line
3285 return;
3287 case 0x16: // get diskette change line status
3288 printf("floppy f16\n");
3289 drive = GET_DL();
3290 if (drive > 1) {
3291 SET_AH(0x01); // invalid drive
3292 set_diskette_ret_status(0x01);
3293 SET_CF();
3294 return;
3297 SET_AH(0x06); // change line not supported
3298 set_diskette_ret_status(0x06);
3299 SET_CF();
3300 return;
3302 case 0x17: // set diskette type for format(old)
3303 printf("floppy f17\n");
3304 /* not used for 1.44M floppies */
3305 SET_AH(0x01); // not supported
3306 set_diskette_ret_status(1); /* not supported */
3307 SET_CF();
3308 return;
3310 case 0x18: // set diskette type for format(new)
3311 printf("floppy f18\n");
3312 SET_AH(0x01); // do later
3313 set_diskette_ret_status(1);
3314 SET_CF();
3315 return;
3317 default:
3318 if ( (ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e) ) {
3319 SET_AH(0x01); // ???
3320 set_diskette_ret_status(1);
3321 SET_CF();
3322 printf("floppy: int13: 0x%02x\n", ah);
3323 return;
3325 panic("int13_diskette: AH=%02x", ah);
3328 #else // #if BX_SUPPORT_FLOPPY
3329 void
3330 int13_diskette_function(DI, SI, BP, SP, BX, DX, CX, AX, ES, FLAGS)
3331 Bit16u DI, SI, BP, SP, BX, DX, CX, AX, ES, FLAGS;
3333 Bit8u val8;
3335 switch ( GET_AH() ) {
3337 case 0x01: // Read Diskette Status
3338 CLEAR_CF();
3339 val8 = read_byte(0x0000, 0x0441);
3340 SET_AH(val8);
3341 if (val8) {
3342 SET_CF();
3344 return;
3346 default:
3347 SET_CF();
3348 write_byte(0x0000, 0x0441, 0x01);
3349 SET_AH(0x01);
3352 #endif // #if BX_SUPPORT_FLOPPY
3354 void
3355 set_disk_ret_status(val)
3356 Bit8u val;
3358 write_byte(0x0040, 0x0074, val);
3361 void
3362 set_diskette_ret_status(value)
3363 Bit8u value;
3365 write_byte(0x0040, 0x0041, value);
3368 void
3369 set_diskette_current_cyl(drive, cyl)
3370 Bit8u drive;
3371 Bit8u cyl;
3373 if (drive > 1)
3374 panic("set_diskette_current_cyl(): drive > 1");
3375 write_byte(0x0040, 0x0094+drive, cyl);
3378 void
3379 determine_floppy_media(drive)
3380 Bit16u drive;
3382 #if 0
3383 Bit8u val8, DOR, ctrl_info;
3385 ctrl_info = read_byte(0x0040, 0x008F);
3386 if (drive==1)
3387 ctrl_info >>= 4;
3388 else
3389 ctrl_info &= 0x0f;
3391 #if 0
3392 if (drive == 0) {
3393 DOR = 0x1c; // DOR: drive0 motor on, DMA&int enabled, normal op, drive select 0
3395 else {
3396 DOR = 0x2d; // DOR: drive1 motor on, DMA&int enabled, normal op, drive select 1
3398 #endif
3400 if ( (ctrl_info & 0x04) != 0x04 ) {
3401 // Drive not determined means no drive exists, done.
3402 return;
3405 #if 0
3406 // check Main Status Register for readiness
3407 val8 = inb(0x03f4) & 0x80; // Main Status Register
3408 if (val8 != 0x80)
3409 panic("d_f_m: MRQ bit not set");
3411 // change line
3413 // existing BDA values
3415 // turn on drive motor
3416 outb(0x03f2, DOR); // Digital Output Register
3418 #endif
3419 panic("d_f_m: OK so far");
3420 #endif
3425 void
3426 get_hd_geometry(drive, hd_cylinders, hd_heads, hd_sectors)
3427 Bit8u drive;
3428 Bit16u *hd_cylinders;
3429 Bit8u *hd_heads;
3430 Bit8u *hd_sectors;
3432 Bit8u hd_type;
3433 Bit16u ss;
3434 Bit16u cylinders;
3435 Bit8u iobase;
3437 ss = get_SS();
3438 if (drive == 0x80) {
3439 hd_type = inb_cmos(0x12) & 0xf0;
3440 if (hd_type != 0xf0)
3441 panic("HD0 cmos reg 12h not type F");
3442 hd_type = inb_cmos(0x19); // HD0: extended type
3443 if (hd_type != 47)
3444 panic("HD0 cmos reg 19h not user definable type 47");
3445 iobase = 0x1b;
3446 } else {
3447 hd_type = inb_cmos(0x12) & 0x0f;
3448 if (hd_type != 0x0f)
3449 panic("HD1 cmos reg 12h not type F");
3450 hd_type = inb_cmos(0x1a); // HD0: extended type
3451 if (hd_type != 47)
3452 panic("HD1 cmos reg 1ah not user definable type 47");
3453 iobase = 0x24;
3456 // cylinders
3457 cylinders = inb_cmos(iobase) | (inb_cmos(iobase+1) << 8);
3458 write_word(ss, hd_cylinders, cylinders);
3460 // heads
3461 write_byte(ss, hd_heads, inb_cmos(iobase+2));
3463 // sectors per track
3464 write_byte(ss, hd_sectors, inb_cmos(iobase+8));
3467 void
3468 int17_function(regs, ds, iret_addr)
3469 pusha_regs_t regs; // regs pushed from PUSHA instruction
3470 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
3471 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
3473 Bit16u addr,timeout;
3474 Bit8u val8;
3476 #asm
3478 #endasm
3480 if ((regs.u.r8.ah < 3) && (regs.u.r16.dx == 0)) {
3481 addr = read_word(0x0040, 0x0008);
3482 timeout = read_byte(0x0040, 0x0078) << 8;
3483 if (regs.u.r8.ah == 0) {
3484 outb(addr, regs.u.r8.al);
3485 val8 = inb(addr+2);
3486 outb(addr+2, val8 | 0x01); // send strobe
3487 #asm
3489 #endasm
3490 outb(addr+2, val8 & ~0x01);
3491 while (((inb(addr+1) & 0x40) == 0x40) && (timeout)) {
3492 timeout--;
3495 if (regs.u.r8.ah == 1) {
3496 val8 = inb(addr+2);
3497 outb(addr+2, val8 & ~0x04); // send init
3498 #asm
3500 #endasm
3501 outb(addr+2, val8 | 0x04);
3503 regs.u.r8.ah = inb(addr+1);
3504 val8 = (~regs.u.r8.ah & 0x48);
3505 regs.u.r8.ah &= 0xB7;
3506 regs.u.r8.ah |= val8;
3507 if (!timeout) regs.u.r8.ah |= 0x01;
3508 ClearCF(iret_addr.flags);
3509 } else {
3510 SetCF(iret_addr.flags); // Unsupported
3514 void
3515 int1a_function(regs, ds, iret_addr)
3516 pusha_regs_t regs; // regs pushed from PUSHA instruction
3517 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
3518 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
3520 Bit8u val8;
3522 #asm
3524 #endasm
3526 switch (regs.u.r8.ah) {
3527 case 0: // get current clock count
3528 #asm
3530 #endasm
3531 regs.u.r16.cx = BiosData->ticks_high;
3532 regs.u.r16.dx = BiosData->ticks_low;
3533 regs.u.r8.al = BiosData->midnight_flag;
3534 BiosData->midnight_flag = 0; // reset flag
3535 #asm
3537 #endasm
3538 // AH already 0
3539 ClearCF(iret_addr.flags); // OK
3540 break;
3542 case 1: // Set Current Clock Count
3543 #asm
3545 #endasm
3546 BiosData->ticks_high = regs.u.r16.cx;
3547 BiosData->ticks_low = regs.u.r16.dx;
3548 BiosData->midnight_flag = 0; // reset flag
3549 #asm
3551 #endasm
3552 regs.u.r8.ah = 0;
3553 ClearCF(iret_addr.flags); // OK
3554 break;
3557 case 2: // Read CMOS Time
3558 if (rtc_updating()) {
3559 SetCF(iret_addr.flags);
3560 break;
3563 regs.u.r8.dh = inb_cmos(0x00); // Seconds
3564 regs.u.r8.cl = inb_cmos(0x02); // Minutes
3565 regs.u.r8.ch = inb_cmos(0x04); // Hours
3566 regs.u.r8.dl = inb_cmos(0x0b) & 0x01; // Stat Reg B
3567 regs.u.r8.ah = 0;
3568 regs.u.r8.al = regs.u.r8.ch;
3569 ClearCF(iret_addr.flags); // OK
3570 break;
3572 case 3: // Set CMOS Time
3573 // Using a debugger, I notice the following masking/setting
3574 // of bits in Status Register B, by setting Reg B to
3575 // a few values and getting its value after INT 1A was called.
3577 // try#1 try#2 try#3
3578 // before 1111 1101 0111 1101 0000 0000
3579 // after 0110 0010 0110 0010 0000 0010
3581 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
3582 // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
3583 if (rtc_updating()) {
3584 init_rtc();
3585 // fall through as if an update were not in progress
3587 outb_cmos(0x00, regs.u.r8.dh); // Seconds
3588 outb_cmos(0x02, regs.u.r8.cl); // Minutes
3589 outb_cmos(0x04, regs.u.r8.ch); // Hours
3590 // Set Daylight Savings time enabled bit to requested value
3591 val8 = (inb_cmos(0x0b) & 0x60) | 0x02 | (regs.u.r8.dl & 0x01);
3592 // (reg B already selected)
3593 outb_cmos(0x0b, val8);
3594 regs.u.r8.ah = 0;
3595 regs.u.r8.al = val8; // val last written to Reg B
3596 ClearCF(iret_addr.flags); // OK
3597 break;
3599 case 4: // Read CMOS Date
3600 regs.u.r8.ah = 0;
3601 if (rtc_updating()) {
3602 SetCF(iret_addr.flags);
3603 break;
3605 regs.u.r8.cl = inb_cmos(0x09); // Year
3606 regs.u.r8.dh = inb_cmos(0x08); // Month
3607 regs.u.r8.dl = inb_cmos(0x07); // Day of Month
3608 regs.u.r8.ch = inb_cmos(0x32); // Century
3609 regs.u.r8.al = regs.u.r8.ch;
3610 ClearCF(iret_addr.flags); // OK
3611 break;
3613 case 5: // Set CMOS Date
3614 // Using a debugger, I notice the following masking/setting
3615 // of bits in Status Register B, by setting Reg B to
3616 // a few values and getting its value after INT 1A was called.
3618 // try#1 try#2 try#3 try#4
3619 // before 1111 1101 0111 1101 0000 0010 0000 0000
3620 // after 0110 1101 0111 1101 0000 0010 0000 0000
3622 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
3623 // My assumption: RegB = (RegB & 01111111b)
3624 if (rtc_updating()) {
3625 init_rtc();
3626 SetCF(iret_addr.flags);
3627 break;
3629 outb_cmos(0x09, regs.u.r8.cl); // Year
3630 outb_cmos(0x08, regs.u.r8.dh); // Month
3631 outb_cmos(0x07, regs.u.r8.dl); // Day of Month
3632 outb_cmos(0x32, regs.u.r8.ch); // Century
3633 val8 = inb_cmos(0x0b) & 0x7f; // clear halt-clock bit
3634 outb_cmos(0x0b, val8);
3635 regs.u.r8.ah = 0;
3636 regs.u.r8.al = val8; // AL = val last written to Reg B
3637 ClearCF(iret_addr.flags); // OK
3638 break;
3640 case 6: // Set Alarm Time in CMOS
3641 // Using a debugger, I notice the following masking/setting
3642 // of bits in Status Register B, by setting Reg B to
3643 // a few values and getting its value after INT 1A was called.
3645 // try#1 try#2 try#3
3646 // before 1101 1111 0101 1111 0000 0000
3647 // after 0110 1111 0111 1111 0010 0000
3649 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
3650 // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
3651 val8 = inb_cmos(0x0b); // Get Status Reg B
3652 regs.u.r16.ax = 0;
3653 if (val8 & 0x20) {
3654 // Alarm interrupt enabled already
3655 SetCF(iret_addr.flags); // Error: alarm in use
3656 break;
3658 if (rtc_updating()) {
3659 init_rtc();
3660 // fall through as if an update were not in progress
3662 outb_cmos(0x01, regs.u.r8.dh); // Seconds alarm
3663 outb_cmos(0x03, regs.u.r8.cl); // Minutes alarm
3664 outb_cmos(0x05, regs.u.r8.ch); // Hours alarm
3665 outb(0xa1, inb(0xa1) & 0xfe); // enable IRQ 8
3666 // enable Status Reg B alarm bit, clear halt clock bit
3667 outb_cmos(0x0b, (val8 & 0x7f) | 0x20);
3668 ClearCF(iret_addr.flags); // OK
3669 break;
3671 case 7: // Turn off Alarm
3672 // Using a debugger, I notice the following masking/setting
3673 // of bits in Status Register B, by setting Reg B to
3674 // a few values and getting its value after INT 1A was called.
3676 // try#1 try#2 try#3 try#4
3677 // before 1111 1101 0111 1101 0010 0000 0010 0010
3678 // after 0100 0101 0101 0101 0000 0000 0000 0010
3680 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
3681 // My assumption: RegB = (RegB & 01010111b)
3682 val8 = inb_cmos(0x0b); // Get Status Reg B
3683 // clear clock-halt bit, disable alarm bit
3684 outb_cmos(0x0b, val8 & 0x57); // disable alarm bit
3685 regs.u.r8.ah = 0;
3686 regs.u.r8.al = val8; // val last written to Reg B
3687 ClearCF(iret_addr.flags); // OK
3688 break;
3689 #if BX_PCIBIOS
3690 case 0xb1:
3691 setPCIaddr(0, 0, 0);
3692 if (inw(0x0cfc) != 0x8086) {
3693 bios_printf(0, "PCI BIOS not present\n");
3694 SetCF(iret_addr.flags);
3695 } else {
3696 switch (regs.u.r8.al) {
3697 case 0x01: // Installation check
3698 regs.u.r8.ah = 0;
3699 regs.u.r8.al = 1;
3700 regs.u.r8.bh = 1;
3701 regs.u.r8.cl = 0;
3702 ClearCF(iret_addr.flags);
3703 break;
3704 case 0x09: // Read configuration word
3705 setPCIaddr(regs.u.r8.bh, regs.u.r8.bl, (Bit8u)(regs.u.r16.di & 0xfc));
3706 regs.u.r16.cx = inw(0x0cfc + (regs.u.r16.di & 0x0002));
3707 regs.u.r8.ah = 0;
3708 ClearCF(iret_addr.flags);
3709 break;
3710 case 0x0c: // Write configuration word
3711 bios_printf(0, "reg: 0x%02x value: 0x%02x\n",(Bit8u)(regs.u.r16.di & 0xff),regs.u.r16.cx);
3712 setPCIaddr(regs.u.r8.bh, regs.u.r8.bl, (Bit8u)(regs.u.r16.di & 0xfc));
3713 outw(0x0cfc + (regs.u.r16.di & 0x0002), regs.u.r16.cx);
3714 regs.u.r8.ah = 0;
3715 ClearCF(iret_addr.flags);
3716 break;
3717 default:
3718 bios_printf(0, "unsupported PCI BIOS function 0x%02x\n", regs.u.r8.al);
3719 SetCF(iret_addr.flags);
3722 break;
3723 #endif
3725 default:
3726 SetCF(iret_addr.flags); // Unsupported
3730 void
3731 int70_function(regs, ds, iret_addr)
3732 pusha_regs_t regs; // regs pushed from PUSHA instruction
3733 Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
3734 iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
3736 // INT 70h: IRQ 8 - CMOS RTC interrupt from periodic or alarm modes
3737 Bit8u val8;
3739 val8 = inb_cmos(0x0c); // Status Reg C
3740 if (val8 == 0) panic("int70: regC 0");
3741 if (val8 & 0x40) panic("int70: periodic request");
3742 if (val8 & 0x20) {
3743 // Alarm Flag indicates alarm time matches current time
3744 // call user INT 4Ah alarm handler
3745 #asm
3747 //pushf
3748 //;; call_ep [ds:loc]
3749 //CALL_EP( 0x4a << 2 )
3750 int #0x4a
3752 #endasm
3755 #asm
3756 ;; send EOI to slave & master PICs
3757 mov al, #0x20
3758 out #0xA0, al ;; slave PIC EOI
3759 out #0x20, al ;; master PIC EOI
3760 #endasm
3765 #asm
3766 ;------------------------------------------
3767 ;- INT74h : PS/2 mouse hardware interrupt -
3768 ;------------------------------------------
3769 int74_handler:
3771 pusha
3772 push ds ;; save DS
3773 push #0x00 ;; placeholder for status
3774 push #0x00 ;; placeholder for X
3775 push #0x00 ;; placeholder for Y
3776 push #0x00 ;; placeholder for Z
3777 push #0x00 ;; placeholder for make_far_call boolean
3778 call _int74_function
3779 pop cx ;; remove make_far_call from stack
3780 jcxz int74_done
3782 ;; make far call to EBDA:0022
3783 push #0x00
3784 pop ds
3785 push 0x040E ;; push 0000:040E (opcodes 0xff, 0x36, 0x0E, 0x04)
3786 pop ds
3787 //CALL_EP(0x0022) ;; call far routine (call_Ep DS:0022 :opcodes 0xff, 0x1e, 0x22, 0x00)
3788 call far ptr[0x22]
3789 int74_done:
3791 mov al, #0x20
3792 ;; send EOI to slave & master PICs
3793 out #0xA0, al ;; slave PIC EOI
3794 out #0x20, al ;; master PIC EOI
3795 add sp, #8 ;; pop status, x, y, z
3797 pop ds ;; restore DS
3798 popa
3799 iret
3802 ;; This will perform an IRET, but will retain value of current CF
3803 ;; by altering flags on stack. Better than RETF #02.
3804 iret_modify_cf:
3805 jc carry_set
3806 push bp
3807 mov bp, sp
3808 and BYTE [bp + 0x06], #0xfe
3809 pop bp
3810 iret
3811 carry_set:
3812 push bp
3813 mov bp, sp
3814 or BYTE [bp + 0x06], #0x01
3815 pop bp
3816 iret
3819 ;----------------------
3820 ;- INT13h (relocated) -
3821 ;----------------------
3822 int13_relocated:
3823 pushf
3824 test dl, #0x80
3825 jz int13_floppy
3827 int13_disk:
3828 ;; pushf already done
3829 push es
3830 pusha
3831 call _int13_function
3832 popa
3833 pop es
3834 popf
3835 // JMPL(iret_modify_cf)
3836 jmp iret_modify_cf
3837 int13_floppy:
3838 popf
3839 // JMPL(int13_diskette)
3840 jmp int13_diskette
3843 ;----------------------
3844 ;- INT19h (relocated) -
3845 ;----------------------
3846 int19_relocated:
3847 ;; check bit 5 in CMOS reg 0x2d. load either 0x00 or 0x80 into DL
3848 ;; in preparation for the intial INT 13h (0=floppy A:, 0x80=C:)
3849 ;; 0: system boot sequence, first drive C: then A:
3850 ;; 1: system boot sequence, first drive A: then C:
3852 mov al, #0x2d
3853 out 0x70, al
3854 in al, 0x71
3855 and al, #0x20
3856 jz int19_usedisk
3858 int19_usefloppy:
3859 mov dl, #0x00
3860 jmp int19_loadsector
3862 int19_usedisk:
3863 mov dl, #0x80
3865 int19_loadsector:
3866 mov ax, #0x0000
3867 mov es, ax ;; seg = 0000
3868 mov bx, #0x7c00 ;; load boot sector into 0000:7c000
3869 mov ah, #0x02 ;; function 2, read diskette sector
3870 mov al, #0x01 ;; read 1 sector
3871 mov ch, #0x00 ;; track 0
3872 mov cl, #0x01 ;; sector 1
3873 mov dh, #0x00 ;; head 0
3874 int #0x13
3875 jc int19_load_failed
3876 ;; read sector ok. now check floppy signature.
3877 mov dh, #0x01
3878 cmp WORD [0x7DFE], #0xAA55
3879 jne bootstrap_problem
3880 JMP_AP(0x0000, 0x7c00) ;; sig ok. Now execute the code.
3881 int19_load_failed:
3882 xor dh,dh
3883 bootstrap_problem:
3884 ;; if dh=0, load failed. if dh=1, signature check failed.
3885 push dx
3886 call _boot_failure_msg
3889 ;----------
3890 ;- INT18h -
3891 ;----------
3892 int18_handler: ;; Boot Failure routing
3893 HALT(__LINE__)
3894 iret
3897 ;----------
3898 ;- INT1Ch -
3899 ;----------
3900 int1c_handler: ;; User Timer Tick
3901 iret
3904 ;----------------------
3905 ;- POST: Floppy Drive -
3906 ;----------------------
3907 floppy_drive_post:
3908 mov ax, #0x0000
3909 mov ds, ax
3911 mov al, #0x00
3912 mov 0x043e, al ;; drive 0 & 1 uncalibrated, no interrupt has occurred
3914 mov 0x043f, al ;; diskette motor status: read op, drive0, motors off
3916 mov 0x0440, al ;; diskette motor timeout counter: not active
3917 mov 0x0441, al ;; diskette controller status return code
3919 mov 0x0442, al ;; disk & diskette controller status register 0
3920 mov 0x0443, al ;; diskette controller status register 1
3921 mov 0x0444, al ;; diskette controller status register 2
3922 mov 0x0445, al ;; diskette controller cylinder number
3923 mov 0x0446, al ;; diskette controller head number
3924 mov 0x0447, al ;; diskette controller sector number
3925 mov 0x0448, al ;; diskette controller bytes written
3927 mov 0x048b, al ;; diskette configuration data
3929 ;; -----------------------------------------------------------------
3930 ;; (048F) diskette controller information
3932 mov al, #0x10 ;; get CMOS diskette drive type
3933 out 0x70, AL
3934 in AL, 0x71
3935 mov ah, al ;; save byte to AH
3937 look_drive0:
3938 shr al, #4 ;; look at top 4 bits for drive 0
3939 jz f0_missing ;; jump if no drive0
3940 mov bl, #0x07 ;; drive0 determined, multi-rate, has changed line
3941 jmp look_drive1
3942 f0_missing:
3943 mov bl, #0x00 ;; no drive0
3945 look_drive1:
3946 mov al, ah ;; restore from AH
3947 and al, #0x0f ;; look at bottom 4 bits for drive 1
3948 jz f1_missing ;; jump if no drive1
3949 or bl, #0x70 ;; drive1 determined, multi-rate, has changed line
3950 f1_missing:
3951 ;; leave high bits in BL zerod
3952 mov 0x048f, bl ;; put new val in BDA (diskette controller information)
3953 ;; -----------------------------------------------------------------
3955 mov al, #0x00
3956 mov 0x0490, al ;; diskette 0 media state
3957 mov 0x0491, al ;; diskette 1 media state
3959 ;; diskette 0,1 operational starting state
3960 ;; drive type has not been determined,
3961 ;; has no changed detection line
3962 mov 0x0492, al
3963 mov 0x0493, al
3965 mov 0x0494, al ;; diskette 0 current cylinder
3966 mov 0x0495, al ;; diskette 1 current cylinder
3968 mov al, #0x02
3969 out #0x0a, al ;; clear DMA-1 channel 2 mask bit
3971 SET_INT_VECTOR(0x1E, #0xF000, #diskette_param_table)
3972 SET_INT_VECTOR(0x40, #0xF000, #int13_diskette)
3973 SET_INT_VECTOR(0x0E, #0xF000, #int0e_handler) ;; IRQ 6
3979 ;--------------------
3980 ;- POST: HARD DRIVE -
3981 ;--------------------
3982 ; relocated here because the primary POST area isnt big enough.
3983 hard_drive_post:
3984 // IRQ 14 = INT 76h
3985 // INT 76h calls INT 15h function ax=9100
3987 mov al, #0x0a ; 0000 1010 = reserved, disable IRQ 14
3988 mov dx, #0x03f6
3989 out dx, al
3991 mov ax, #0x0000
3992 mov ds, ax
3993 mov 0x0474, al /* hard disk status of last operation */
3994 mov 0x0477, al /* hard disk port offset (XT only ???) */
3995 mov 0x048c, al /* hard disk status register */
3996 mov 0x048d, al /* hard disk error register */
3997 mov 0x048e, al /* hard disk task complete flag */
3998 mov al, #0x01
3999 mov 0x0475, al /* hard disk number attached */
4000 mov al, #0xc0
4001 mov 0x0476, al /* hard disk control byte */
4002 SET_INT_VECTOR(0x13, #0xF000, #int13_handler)
4003 SET_INT_VECTOR(0x76, #0xF000, #int76_handler)
4004 ;; INT 41h: hard disk 0 configuration pointer
4005 ;; INT 46h: hard disk 1 configuration pointer
4006 SET_INT_VECTOR(0x41, #EBDA_SEG, #0x003D)
4007 SET_INT_VECTOR(0x46, #EBDA_SEG, #0x004D)
4009 ;; move disk geometry data from CMOS to EBDA disk parameter table(s)
4010 mov al, #0x12
4011 out #0x70, al
4012 in al, #0x71
4013 and al, #0xf0
4014 cmp al, #0xf0
4015 je post_d0_extended
4016 jmp check_for_hd1
4017 post_d0_extended:
4018 mov al, #0x19
4019 out #0x70, al
4020 in al, #0x71
4021 cmp al, #47 ;; decimal 47 - user definable
4022 je post_d0_type47
4023 HALT(__LINE__)
4024 post_d0_type47:
4025 ;; CMOS purpose param table offset
4026 ;; 1b cylinders low 0
4027 ;; 1c cylinders high 1
4028 ;; 1d heads 2
4029 ;; 1e write pre-comp low 5
4030 ;; 1f write pre-comp high 6
4031 ;; 20 retries/bad map/heads>8 8
4032 ;; 21 landing zone low C
4033 ;; 22 landing zone high D
4034 ;; 23 sectors/track E
4036 mov ax, #EBDA_SEG
4037 mov ds, ax
4039 ;;; Filling EBDA table for hard disk 0.
4040 mov al, #0x1f
4041 out #0x70, al
4042 in al, #0x71
4043 mov ah, al
4044 mov al, #0x1e
4045 out #0x70, al
4046 in al, #0x71
4047 mov (0x003d + 0x05), ax ;; write precomp word
4049 mov al, #0x20
4050 out #0x70, al
4051 in al, #0x71
4052 mov (0x003d + 0x08), al ;; drive control byte
4054 mov al, #0x22
4055 out #0x70, al
4056 in al, #0x71
4057 mov ah, al
4058 mov al, #0x21
4059 out #0x70, al
4060 in al, #0x71
4061 mov (0x003d + 0x0C), ax ;; landing zone word
4063 mov al, #0x1c ;; get cylinders word in AX
4064 out #0x70, al
4065 in al, #0x71 ;; high byte
4066 mov ah, al
4067 mov al, #0x1b
4068 out #0x70, al
4069 in al, #0x71 ;; low byte
4070 mov bx, ax ;; BX = cylinders
4072 mov al, #0x1d
4073 out #0x70, al
4074 in al, #0x71
4075 mov cl, al ;; CL = heads
4077 mov al, #0x23
4078 out #0x70, al
4079 in al, #0x71
4080 mov dl, al ;; DL = sectors
4082 cmp bx, #1024
4083 jnbe hd0_post_logical_chs ;; if cylinders > 1024, use translated style CHS
4085 hd0_post_physical_chs:
4086 ;; no logical CHS mapping used, just physical CHS
4087 ;; use Standard Fixed Disk Parameter Table (FDPT)
4088 mov (0x003d + 0x00), bx ;; number of physical cylinders
4089 mov (0x003d + 0x02), cl ;; number of physical heads
4090 mov (0x003d + 0x0E), dl ;; number of physical sectors
4091 jmp check_for_hd1
4093 hd0_post_logical_chs:
4094 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
4095 mov (0x003d + 0x09), bx ;; number of physical cylinders
4096 mov (0x003d + 0x0b), cl ;; number of physical heads
4097 mov (0x003d + 0x04), dl ;; number of physical sectors
4098 mov (0x003d + 0x0e), dl ;; number of logical sectors (same)
4099 mov al, #0xa0
4100 mov (0x003d + 0x03), al ;; A0h signature, indicates translated table
4102 cmp bx, #2048
4103 jnbe hd0_post_above_2048
4104 ;; 1024 < c <= 2048 cylinders
4105 shr bx, #0x01
4106 shl cl, #0x01
4107 jmp hd0_post_store_logical
4109 hd0_post_above_2048:
4110 cmp bx, #4096
4111 jnbe hd0_post_above_4096
4112 ;; 2048 < c <= 4096 cylinders
4113 shr bx, #0x02
4114 shl cl, #0x02
4115 jmp hd0_post_store_logical
4117 hd0_post_above_4096:
4118 cmp bx, #8192
4119 jnbe hd0_post_above_8192
4120 ;; 4096 < c <= 8192 cylinders
4121 shr bx, #0x03
4122 shl cl, #0x03
4123 jmp hd0_post_store_logical
4125 hd0_post_above_8192:
4126 ;; 8192 < c <= 16384 cylinders
4127 shr bx, #0x04
4128 shl cl, #0x04
4130 hd0_post_store_logical:
4131 mov (0x003d + 0x00), bx ;; number of physical cylinders
4132 mov (0x003d + 0x02), cl ;; number of physical heads
4133 ;; checksum
4134 mov cl, #0x0f ;; repeat count
4135 mov si, #0x003d ;; offset to disk0 FDPT
4136 mov al, #0x00 ;; sum
4137 hd0_post_checksum_loop:
4138 add al, [si]
4139 inc si
4140 dec cl
4141 jnz hd0_post_checksum_loop
4142 not al ;; now take 2s complement
4143 inc al
4144 mov [si], al
4145 ;;; Done filling EBDA table for hard disk 0.
4148 check_for_hd1:
4149 ;; is there really a second hard disk? if not, return now
4150 mov al, #0x12
4151 out #0x70, al
4152 in al, #0x71
4153 and al, #0x0f
4154 jnz post_d1_exists
4156 post_d1_exists:
4157 ;; check that the hd type is really 0x0f.
4158 cmp al, #0x0f
4159 jz post_d1_extended
4160 HALT(__LINE__)
4161 post_d1_extended:
4162 ;; check that the extended type is 47 - user definable
4163 mov al, #0x1a
4164 out #0x70, al
4165 in al, #0x71
4166 cmp al, #47 ;; decimal 47 - user definable
4167 je post_d1_type47
4168 HALT(__LINE__)
4169 post_d1_type47:
4170 ;; Table for disk1.
4171 ;; CMOS purpose param table offset
4172 ;; 0x24 cylinders low 0
4173 ;; 0x25 cylinders high 1
4174 ;; 0x26 heads 2
4175 ;; 0x27 write pre-comp low 5
4176 ;; 0x28 write pre-comp high 6
4177 ;; 0x29 heads>8 8
4178 ;; 0x2a landing zone low C
4179 ;; 0x2b landing zone high D
4180 ;; 0x2c sectors/track E
4181 ;;; Fill EBDA table for hard disk 1.
4182 mov al, #0x28
4183 out #0x70, al
4184 in al, #0x71
4185 mov ah, al
4186 mov al, #0x27
4187 out #0x70, al
4188 in al, #0x71
4189 mov (0x004d + 0x05), ax ;; write precomp word
4191 mov al, #0x29
4192 out #0x70, al
4193 in al, #0x71
4194 mov (0x004d + 0x08), al ;; drive control byte
4196 mov al, #0x2b
4197 out #0x70, al
4198 in al, #0x71
4199 mov ah, al
4200 mov al, #0x2a
4201 out #0x70, al
4202 in al, #0x71
4203 mov (0x004d + 0x0C), ax ;; landing zone word
4205 mov al, #0x25 ;; get cylinders word in AX
4206 out #0x70, al
4207 in al, #0x71 ;; high byte
4208 mov ah, al
4209 mov al, #0x24
4210 out #0x70, al
4211 in al, #0x71 ;; low byte
4212 mov bx, ax ;; BX = cylinders
4214 mov al, #0x26
4215 out #0x70, al
4216 in al, #0x71
4217 mov cl, al ;; CL = heads
4219 mov al, #0x2c
4220 out #0x70, al
4221 in al, #0x71
4222 mov dl, al ;; DL = sectors
4224 cmp bx, #1024
4225 jnbe hd1_post_logical_chs ;; if cylinders > 1024, use translated style CHS
4227 hd1_post_physical_chs:
4228 ;; no logical CHS mapping used, just physical CHS
4229 ;; use Standard Fixed Disk Parameter Table (FDPT)
4230 mov (0x004d + 0x00), bx ;; number of physical cylinders
4231 mov (0x004d + 0x02), cl ;; number of physical heads
4232 mov (0x004d + 0x0E), dl ;; number of physical sectors
4235 hd1_post_logical_chs:
4236 ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
4237 mov (0x004d + 0x09), bx ;; number of physical cylinders
4238 mov (0x004d + 0x0b), cl ;; number of physical heads
4239 mov (0x004d + 0x04), dl ;; number of physical sectors
4240 mov (0x004d + 0x0e), dl ;; number of logical sectors (same)
4241 mov al, #0xa0
4242 mov (0x004d + 0x03), al ;; A0h signature, indicates translated table
4244 cmp bx, #2048
4245 jnbe hd1_post_above_2048
4246 ;; 1024 < c <= 2048 cylinders
4247 shr bx, #0x01
4248 shl cl, #0x01
4249 jmp hd1_post_store_logical
4251 hd1_post_above_2048:
4252 cmp bx, #4096
4253 jnbe hd1_post_above_4096
4254 ;; 2048 < c <= 4096 cylinders
4255 shr bx, #0x02
4256 shl cl, #0x02
4257 jmp hd1_post_store_logical
4259 hd1_post_above_4096:
4260 cmp bx, #8192
4261 jnbe hd1_post_above_8192
4262 ;; 4096 < c <= 8192 cylinders
4263 shr bx, #0x03
4264 shl cl, #0x03
4265 jmp hd1_post_store_logical
4267 hd1_post_above_8192:
4268 ;; 8192 < c <= 16384 cylinders
4269 shr bx, #0x04
4270 shl cl, #0x04
4272 hd1_post_store_logical:
4273 mov (0x004d + 0x00), bx ;; number of physical cylinders
4274 mov (0x004d + 0x02), cl ;; number of physical heads
4275 ;; checksum
4276 mov cl, #0x0f ;; repeat count
4277 mov si, #0x004d ;; offset to disk0 FDPT
4278 mov al, #0x00 ;; sum
4279 hd1_post_checksum_loop:
4280 add al, [si]
4281 inc si
4282 dec cl
4283 jnz hd1_post_checksum_loop
4284 not al ;; now take 2s complement
4285 inc al
4286 mov [si], al
4287 ;;; Done filling EBDA table for hard disk 0.
4292 BcdToBin:
4293 ;; in: AL in BCD format
4294 ;; out: AL in binary format, AH will always be 0
4295 ;; trashes BX
4296 mov bl, al
4297 and bl, #0x0f ;; bl has low digit
4298 shr al, #4 ;; al has high digit
4299 mov bh, #10
4300 mul al, bh ;; multiply high digit by 10 (result in AX)
4301 add al, bl ;; then add low digit
4304 timer_tick_post:
4305 ;; Setup the Timer Ticks Count (0x46C:dword) and
4306 ;; Timer Ticks Roller Flag (0x470:byte)
4307 ;; The Timer Ticks Count needs to be set according to
4308 ;; the current CMOS time, as if ticks have been occurring
4309 ;; at 18.2hz since midnight up to this point. Calculating
4310 ;; this is a little complicated. Here are the factors I gather
4311 ;; regarding this. 14,318,180 hz was the original clock speed,
4312 ;; chosen so it could be divided by either 3 to drive the 5Mhz CPU
4313 ;; at the time, or 4 to drive the CGA video adapter. The div3
4314 ;; source was divided again by 4 to feed a 1.193Mhz signal to
4315 ;; the timer. With a maximum 16bit timer count, this is again
4316 ;; divided down by 65536 to 18.2hz.
4318 ;; 14,318,180 Hz clock
4319 ;; /3 = 4,772,726 Hz fed to orginal 5Mhz CPU
4320 ;; /4 = 1,193,181 Hz fed to timer
4321 ;; /65536 (maximum timer count) = 18.20650736 ticks/second
4322 ;; 1 second = 18.20650736 ticks
4323 ;; 1 minute = 1092.390442 ticks
4324 ;; 1 hour = 65543.42651 ticks
4326 ;; Given the values in the CMOS clock, one could calculate
4327 ;; the number of ticks by the following:
4328 ;; ticks = (BcdToBin(seconds) * 18.206507) +
4329 ;; (BcdToBin(minutes) * 1092.3904)
4330 ;; (BcdToBin(hours) * 65543.427)
4331 ;; To get a little more accuracy, since Im using integer
4332 ;; arithmatic, I use:
4333 ;; ticks = (BcdToBin(seconds) * 18206507) / 1000000 +
4334 ;; (BcdToBin(minutes) * 10923904) / 10000 +
4335 ;; (BcdToBin(hours) * 65543427) / 1000
4337 ;; assuming DS=0000
4339 ;; get CMOS seconds
4340 xor eax, eax ;; clear EAX
4341 mov al, #0x00
4342 out #0x70, al
4343 in al, #0x71 ;; AL has CMOS seconds in BCD
4344 call BcdToBin ;; EAX now has seconds in binary
4345 mov edx, #18206507
4346 mul eax, edx
4347 mov ebx, #1000000
4348 xor edx, edx
4349 div eax, ebx
4350 mov ecx, eax ;; ECX will accumulate total ticks
4352 ;; get CMOS minutes
4353 xor eax, eax ;; clear EAX
4354 mov al, #0x02
4355 out #0x70, al
4356 in al, #0x71 ;; AL has CMOS minutes in BCD
4357 call BcdToBin ;; EAX now has minutes in binary
4358 mov edx, #10923904
4359 mul eax, edx
4360 mov ebx, #10000
4361 xor edx, edx
4362 div eax, ebx
4363 add ecx, eax ;; add to total ticks
4365 ;; get CMOS hours
4366 xor eax, eax ;; clear EAX
4367 mov al, #0x04
4368 out #0x70, al
4369 in al, #0x71 ;; AL has CMOS hours in BCD
4370 call BcdToBin ;; EAX now has hours in binary
4371 mov edx, #65543427
4372 mul eax, edx
4373 mov ebx, #1000
4374 xor edx, edx
4375 div eax, ebx
4376 add ecx, eax ;; add to total ticks
4378 mov 0x46C, ecx ;; Timer Ticks Count
4379 xor al, al
4380 mov 0x470, al ;; Timer Ticks Rollover Flag
4384 int76_handler:
4385 ;; record completion in BIOS task complete flag
4386 push ax
4387 push ds
4388 mov ax, #0x0040
4389 mov ds, ax
4390 mov 0x008E, #0xff
4391 mov al, #0x20
4392 out #0xA0, al ;; slave PIC EOI
4393 out #0x20, al ;; master PIC EOI
4394 pop ds
4395 pop ax
4396 iret
4398 ;; for 'C' strings and other data, insert them here with
4399 ;; a the following hack:
4400 ;; DATA_SEG_DEFS_HERE
4403 ;--------
4404 ;- POST -
4405 ;--------
4406 .org 0xe05b ; POST Entry Point
4407 post:
4409 ;; Examine CMOS shutdown status.
4410 ;; 0 = normal startup
4411 mov AL, #0x0f
4412 out 0x70, AL
4413 in AL, 0x71
4414 cmp AL, #0x00
4415 jz normal_post
4416 HALT(__LINE__)
4418 mov AL, #0x0f
4419 out 0x70, AL ; select CMOS register Fh
4420 mov AL, #0x00
4421 out 0x71, AL ; set shutdown action to normal
4423 ;#if 0
4424 ; 0xb0, 0x20, /* mov al, #0x20 */
4425 ; 0xe6, 0x20, /* out 0x20, al ;send EOI to PIC */
4426 ;#endif
4428 pop es
4429 pop ds
4430 popa
4431 iret
4433 normal_post:
4434 ; case 0: normal startup
4437 mov ax, #0xfffe
4438 mov sp, ax
4439 mov ax, #0x0000
4440 mov ds, ax
4441 mov ss, ax
4443 call _log_bios_start
4445 ;; zero out BIOS data area (40:00..40:ff)
4446 mov es, ax
4447 mov cx, #0x0080 ;; 128 words
4448 mov di, #0x0400
4451 stosw
4453 ;; set all interrupts to default handler
4454 mov bx, #0x0000 ;; offset index
4455 mov cx, #0x0100 ;; counter (256 interrupts)
4456 mov ax, #dummy_iret_handler
4457 mov dx, #0xF000
4459 post_default_ints:
4460 mov [bx], ax
4461 inc bx
4462 inc bx
4463 mov [bx], dx
4464 inc bx
4465 inc bx
4466 loop post_default_ints
4468 ;; base memory in K 40:13 (word)
4469 mov ax, #BASE_MEM_IN_K
4470 mov 0x0413, ax
4473 ;; Manufacturing Test 40:12
4474 ;; zerod out above
4476 ;; Warm Boot Flag 0040:0072
4477 ;; value of 1234h = skip memory checks
4478 ;; zerod out above
4481 ;; Printer Services vector
4482 SET_INT_VECTOR(0x17, #0xF000, #int17_handler)
4484 ;; Bootstrap failure vector
4485 SET_INT_VECTOR(0x18, #0xF000, #int18_handler)
4487 ;; Bootstrap Loader vector
4488 SET_INT_VECTOR(0x19, #0xF000, #int19_handler)
4490 ;; User Timer Tick vector
4491 SET_INT_VECTOR(0x1c, #0xF000, #int1c_handler)
4493 ;; Memory Size Check vector
4494 SET_INT_VECTOR(0x12, #0xF000, #int12_handler)
4496 ;; Equipment Configuration Check vector
4497 SET_INT_VECTOR(0x11, #0xF000, #int11_handler)
4499 ;; System Services
4500 SET_INT_VECTOR(0x15, #0xF000, #int15_handler)
4502 mov ax, #0x0000 ; mov EBDA seg into 40E
4503 mov ds, ax
4504 mov 0x40E, #EBDA_SEG
4506 ;; PIT setup
4507 SET_INT_VECTOR(0x08, #0xF000, #int08_handler)
4508 ;; int 1C already points at dummy_iret_handler (above)
4509 mov al, #0x34 ; timer0: binary count, 16bit count, mode 2
4510 out 0x43, al
4511 mov al, #0x00 ; maximum count of 0000H = 18.2Hz
4512 out 0x40, al
4513 out 0x40, al
4515 ;; Keyboard
4516 SET_INT_VECTOR(0x09, #0xF000, #int09_handler)
4517 SET_INT_VECTOR(0x16, #0xF000, #int16_handler)
4519 mov ax, #0x0000
4520 mov ds, ax
4521 mov 0x0417, al /* keyboard shift flags, set 1 */
4522 mov 0x0418, al /* keyboard shift flags, set 2 */
4523 mov 0x0419, al /* keyboard alt-numpad work area */
4524 mov 0x0471, al /* keyboard ctrl-break flag */
4525 mov 0x0497, al /* keyboard status flags 4 */
4526 mov al, #0x10
4527 mov 0x0496, al /* keyboard status flags 3 */
4530 /* keyboard head of buffer pointer */
4531 mov bx, #0x001E
4532 mov 0x041A, bx
4534 /* keyboard end of buffer pointer */
4535 mov 0x041C, bx
4537 /* keyboard buffer */
4538 // for (i=0; i<16; i++)
4539 // bx_mem.access_physical(0x41E + i*2, 2, BX_WRITE, &zero16);
4542 /* keyboard pointer to start of buffer */
4543 mov bx, #0x001E
4544 mov 0x0480, bx
4546 /* keyboard pointer to end of buffer */
4547 mov bx, #0x003E
4548 mov 0x0482, bx
4550 /* (mch) Keyboard self-test */
4551 mov al, #0xaa
4552 out 0x64, al
4553 in al, 0x60
4554 cmp al, #0x55
4555 je keyboard_ok
4556 call _keyboard_panic
4557 keyboard_ok:
4560 #if BX_USE_PS2_MOUSE
4561 in al, 0xa1
4562 and al, #0xef
4563 out 0xa1, al
4565 // hack to tell CMOS & BIOS data area that we have a mouse
4567 mov al, #0x14
4568 out 0x70, al
4569 in al, 0x71
4570 or al, #0x04
4571 out 0x71, al
4572 #endif
4574 ;; mov CMOS Equipment Byte to BDA Equipment Word
4575 mov ax, 0x0410
4576 mov al, #0x14
4577 out 0x70, al
4578 in al, 0x71
4579 mov 0x0410, ax
4582 ;; DMA
4583 ;; nothing for now
4585 ;; Parallel setup
4586 SET_INT_VECTOR(0x0F, #0xF000, #dummy_iret_handler)
4587 mov ax, #0x0000
4588 mov ds, ax
4589 mov 0x408, #0x378 ; Parallel I/O address, port 1
4590 mov 0x478, #0x14 ; Parallel printer 1 timeout
4591 mov AX, 0x410 ; Equipment word bits 14..15 determing # parallel ports
4592 and AX, #0x3fff
4593 or AX, #0x4000 ; one parallel port
4594 mov 0x410, AX
4596 ;; Serial setup
4597 SET_INT_VECTOR(0x0C, #0xF000, #dummy_iret_handler)
4598 SET_INT_VECTOR(0x14, #0xF000, #int14_handler)
4599 mov 0x400, #0x03F8 ; Serial I/O address, port 1
4600 mov 0x47C, #0x0a ; Serial 1 timeout
4601 mov AX, 0x410 ; Equipment word bits 9..11 determing # serial ports
4602 and AX, #0xf1ff
4603 or AX, #0x0200 ; one serial port
4604 mov 0x410, AX
4606 ;; CMOS RTC
4607 SET_INT_VECTOR(0x1A, #0xF000, #int1a_handler)
4608 SET_INT_VECTOR(0x4A, #0xF000, #dummy_iret_handler)
4609 SET_INT_VECTOR(0x70, #0xF000, #int70_handler)
4610 ;; BIOS DATA AREA 0x4CE ???
4611 call timer_tick_post
4613 ;; PS/2 mouse setup
4614 SET_INT_VECTOR(0x74, #0xF000, #int74_handler)
4616 ;; Video setup
4617 SET_INT_VECTOR(0x10, #0xF000, #int10_handler)
4619 ;; Call extension ROMs - scan C0000 to F4000 in 800 steps
4621 mov bx, #0xc000
4622 romscan:
4623 mov ds, bx
4624 mov ax, 0x0000
4625 cmp ax, #0xAA55
4626 jne notrom
4627 xor ax,ax
4628 mov ds,ax
4629 push bx
4630 push #3
4631 mov bp,sp
4632 db 0xff ; call 0[bp]
4633 db 0x5e
4634 db 0
4635 pop ax
4636 pop bx
4637 notrom:
4638 add bx,#0x80
4639 cmp bx,#0xf400
4640 jne romscan
4641 xor ax,ax
4642 mov ds,ax
4644 ;; PIC
4645 mov al, #0x00
4646 out 0x21, AL ;master pic: all IRQs unmasked
4647 out 0xA1, AL ;slave pic: all IRQs unmasked
4649 call _print_bios_banner
4652 ;; Hard Drive setup
4654 call hard_drive_post
4657 ;; Floppy setup
4659 call floppy_drive_post
4661 int #0x19
4662 //JMP_EP(0x0064) ; INT 19h location
4665 .org 0xe2c3 ; NMI Handler Entry Point
4666 call _nmi_handler_msg
4667 HALT(__LINE__)
4668 iret
4670 ;-------------------------------------------
4671 ;- INT 13h Fixed Disk Services Entry Point -
4672 ;-------------------------------------------
4673 .org 0xe3fe ; INT 13h Fixed Disk Services Entry Point
4674 int13_handler:
4675 //JMPL(int13_relocated)
4676 jmp int13_relocated
4677 .org 0xe401 ; Fixed Disk Parameter Table
4680 ;----------
4681 ;- INT19h -
4682 ;----------
4683 .org 0xe6f2 ; INT 19h Boot Load Service Entry Point
4684 int19_handler:
4685 //JMPL(int19_relocated)
4686 jmp int19_relocated
4687 ;-------------------------------------------
4688 ;- System BIOS Configuration Data Table
4689 ;-------------------------------------------
4690 .org BIOS_CONFIG_TABLE
4691 db 0x08 ; Table size (bytes) -Lo
4692 db 0x00 ; Table size (bytes) -Hi
4693 db SYS_MODEL_ID
4694 db SYS_SUBMODEL_ID
4695 db BIOS_REVISION
4696 ; Feature byte 1
4697 ; b7: 1=DMA channel 3 used by hard disk
4698 ; b6: 1=2 interrupt controllers present
4699 ; b5: 1=RTC present
4700 ; b4: 1=BIOS calls int 15h/4Fh every key
4701 ; b3: 1=wait for extern event supported (Int 15h/41h)
4702 ; b2: 1=extended BIOS data area used
4703 ; b1: 0=AT or ESDI bus, 1=MicroChannel
4704 ; b0: 1=Dual bus (MicroChannel + ISA)
4705 db (0 << 7) | \
4706 (1 << 6) | \
4707 (1 << 5) | \
4708 (BX_CALL_INT15_4F << 4) | \
4709 (0 << 3) | \
4710 (BX_USE_EBDA << 2) | \
4711 (0 << 1) | \
4712 (0 << 0)
4713 ; Feature byte 2
4714 ; b7: 1=32-bit DMA supported
4715 ; b6: 1=int16h, function 9 supported
4716 ; b5: 1=int15h/C6h (get POS data) supported
4717 ; b4: 1=int15h/C7h (get mem map info) supported
4718 ; b3: 1=int15h/C8h (en/dis CPU) supported
4719 ; b2: 1=non-8042 kb controller
4720 ; b1: 1=data streaming supported
4721 ; b0: reserved
4722 db 0x00
4723 ; Feature byte 3
4724 ; b7: not used
4725 ; b6: reserved
4726 ; b5: reserved
4727 ; b4: POST supports ROM-to-RAM enable/disable
4728 ; b3: SCSI on system board
4729 ; b2: info panel installed
4730 ; b1: Initial Machine Load (IML) system - BIOS on disk
4731 ; b0: SCSI supported in IML
4732 db 0x00
4733 ; Feature byte 4
4734 ; b7: IBM private
4735 ; b6: EEPROM present
4736 ; b5-3: ABIOS presence (011 = not supported)
4737 ; b2: private
4738 ; b1: memory split above 16Mb supported
4739 ; b0: POSTEXT directly supported by POST
4740 db 0x00
4741 ; Feature byte 5 (IBM)
4742 ; b1: enhanced mouse
4743 ; b0: flash EPROM
4744 db 0x00
4748 .org 0xe729 ; Baud Rate Generator Table
4750 ;----------
4751 ;- INT14h -
4752 ;----------
4753 .org 0xe739 ; INT 14h Serial Communications Service Entry Point
4754 int14_handler:
4755 push ds
4756 pusha
4757 mov ax, #0x0000
4758 mov ds, ax
4759 call _int14_function
4760 popa
4761 pop ds
4762 iret
4765 ;----------------------------------------
4766 ;- INT 16h Keyboard Service Entry Point -
4767 ;----------------------------------------
4768 .org 0xe82e
4769 int16_handler:
4771 push ds
4772 pushf
4773 pusha
4775 cmp ah, #0x00
4776 je int16_F00
4778 mov bx, #0xf000
4779 mov ds, bx
4780 call _int16_function
4781 popa
4782 popf
4783 pop ds
4784 jz int16_zero_set
4786 int16_zero_clear:
4787 push bp
4788 mov bp, sp
4789 //SEG SS
4790 and BYTE [bp + 0x06], #0xbf
4791 pop bp
4792 iret
4794 int16_zero_set:
4795 push bp
4796 mov bp, sp
4797 //SEG SS
4798 or BYTE [bp + 0x06], #0x40
4799 pop bp
4800 iret
4802 int16_F00:
4803 mov bx, #0x0040
4804 mov ds, bx
4806 int16_wait_for_key:
4808 mov bx, 0x001a
4809 cmp bx, 0x001c
4810 jne int16_key_found
4813 #if 0
4814 /* no key yet, call int 15h, function AX=9002 */
4815 0x50, /* push AX */
4816 0xb8, 0x02, 0x90, /* mov AX, #0x9002 */
4817 0xcd, 0x15, /* int 15h */
4818 0x58, /* pop AX */
4819 0xeb, 0xea, /* jmp WAIT_FOR_KEY */
4820 #endif
4821 jmp int16_wait_for_key
4823 int16_key_found:
4824 mov bx, #0xf000
4825 mov ds, bx
4826 call _int16_function
4827 popa
4828 popf
4829 pop ds
4830 #if 0
4831 /* notify int16 complete w/ int 15h, function AX=9102 */
4832 0x50, /* push AX */
4833 0xb8, 0x02, 0x91, /* mov AX, #0x9102 */
4834 0xcd, 0x15, /* int 15h */
4835 0x58, /* pop AX */
4836 #endif
4837 iret
4841 ;-------------------------------------------------
4842 ;- INT09h : Keyboard Hardware Service Entry Point -
4843 ;-------------------------------------------------
4844 .org 0xe987
4845 int09_handler:
4847 push ax
4849 mov al, #0xAD ;;disable keyboard
4850 out #0x64, al
4854 ;; see if there is really a key to read from the controller
4855 in al, #0x64
4856 test al, #0x01
4857 jz int09_done ;; nope, skip processing
4859 in al, #0x60 ;;read key from keyboard controller
4860 //test al, #0x80 ;;look for key release
4861 //jnz int09_process_key ;; dont pass releases to intercept?
4863 #ifdef BX_CALL_INT15_4F
4864 mov ah, #0x4f ;; allow for keyboard intercept
4866 int #0x15
4867 jnc int09_done
4868 #endif
4871 //int09_process_key:
4872 push ds
4873 pusha
4874 mov bx, #0xf000
4875 mov ds, bx
4876 call _int09_function
4877 popa
4878 pop ds
4880 int09_done:
4882 ;; look at PIC in-service-register to see if EOI required
4883 mov al, #0x0B
4884 out #0x20, al
4885 in al, #0x20
4886 and al, #0x02 ;; IRQ 1 in service
4887 jz int09_finish
4888 mov al, #0x20 ;; send EOI to master PIC
4889 out #0x20, al
4891 int09_finish:
4892 mov al, #0xAE ;;enable keyboard
4893 out #0x64, al
4894 pop ax
4895 iret
4900 ;----------------------------------------
4901 ;- INT 13h Diskette Service Entry Point -
4902 ;----------------------------------------
4903 .org 0xec59
4904 int13_diskette:
4905 pushf
4906 push es
4907 pusha
4908 call _int13_diskette_function
4909 popa
4910 pop es
4911 popf
4912 //JMPL(iret_modify_cf)
4913 jmp iret_modify_cf
4915 #if 0
4916 pushf
4917 cmp ah, #0x01
4918 je i13d_f01
4920 ;; pushf already done
4921 push es
4922 pusha
4923 call _int13_diskette_function
4924 popa
4925 pop es
4926 popf
4927 //JMPL(iret_modify_cf)
4928 jmp iret_modify_cf
4929 i13d_f01:
4930 popf
4931 push ds
4932 push bx
4933 mov bx, #0x0000
4934 mov ds, bx
4935 mov ah, 0x0441
4936 pop bx
4937 pop ds
4939 ;; ??? dont know if this service changes the return status
4940 //JMPL(iret_modify_cf)
4941 jmp iret_modify_cf
4942 #endif
4946 ;---------------------------------------------
4947 ;- INT 0Eh Diskette Hardware ISR Entry Point -
4948 ;---------------------------------------------
4949 .org 0xef57 ; INT 0Eh Diskette Hardware ISR Entry Point
4950 int0e_handler:
4951 push ax
4952 push ds
4953 mov ax, #0x0000 ;; segment 0000
4954 mov ds, ax
4955 mov al, #0x20
4956 out 0x20, al ;; send EOI to PIC
4957 mov al, 0x043e
4958 or al, #0x80 ;; diskette interrupt has occurred
4959 mov 0x043e, al
4960 pop ds
4961 pop ax
4962 iret
4965 .org 0xefc7 ; Diskette Controller Parameter Table
4966 diskette_param_table:
4967 ;; Since no provisions are made for multiple drive types, most
4968 ;; values in this table are ignored. I set parameters for 1.44M
4969 ;; floppy here
4970 db 0xAF
4971 db 0x02 ;; head load time 0000001, DMA used
4972 db 0x25
4973 db 0x02
4974 db 18
4975 db 0x1B
4976 db 0xFF
4977 db 0x6C
4978 db 0xF6
4979 db 0x0F
4980 db 0x01 ;; most systems default to 8
4983 ;----------------------------------------
4984 ;- INT17h : Printer Service Entry Point -
4985 ;----------------------------------------
4986 .org 0xefd2
4987 int17_handler:
4988 push ds
4989 pusha
4990 mov ax, #0x0000
4991 mov ds, ax
4992 call _int17_function
4993 popa
4994 pop ds
4995 iret
4997 .org 0xf045 ; INT 10 Functions 0-Fh Entry Point
4998 HALT(__LINE__)
4999 iret
5001 ;----------
5002 ;- INT10h -
5003 ;----------
5004 .org 0xf065 ; INT 10h Video Support Service Entry Point
5005 int10_handler:
5006 ;; dont do anything, since the VGA BIOS handles int10h requests
5007 iret
5009 .org 0xf0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
5011 ;----------
5012 ;- INT12h -
5013 ;----------
5014 .org 0xf841 ; INT 12h Memory Size Service Entry Point
5015 ; ??? different for Pentium (machine check)?
5016 int12_handler:
5017 push ds
5018 mov ax, #0x0040
5019 mov ds, ax
5020 mov ax, 0x0013
5021 pop ds
5022 iret
5024 ;----------
5025 ;- INT11h -
5026 ;----------
5027 .org 0xf84d ; INT 11h Equipment List Service Entry Point
5028 int11_handler:
5029 push ds
5030 mov ax, #0x0040
5031 mov ds, ax
5032 mov ax, 0x0010
5033 pop ds
5034 iret
5036 ;----------
5037 ;- INT15h -
5038 ;----------
5039 .org 0xf859 ; INT 15h System Services Entry Point
5040 int15_handler:
5041 pushf
5042 push ds
5043 push es
5044 pusha
5045 call _int15_function
5046 popa
5047 pop es
5048 pop ds
5049 popf
5050 //JMPL(iret_modify_cf)
5051 jmp iret_modify_cf
5053 ;; Protected mode IDT descriptor
5055 ;; I just make the limit 0, so the machine will shutdown
5056 ;; if an exception occurs during protected mode memory
5057 ;; transfers.
5059 ;; Set base to f0000 to correspond to beginning of BIOS,
5060 ;; in case I actually define an IDT later
5061 ;; Set limit to 0
5063 pmode_IDT_info:
5064 dw 0x0000 ;; limit 15:00
5065 dw 0x0000 ;; base 15:00
5066 db 0x0f ;; base 23:16
5068 ;; Real mode IDT descriptor
5070 ;; Set to typical real-mode values.
5071 ;; base = 000000
5072 ;; limit = 03ff
5074 rmode_IDT_info:
5075 dw 0x03ff ;; limit 15:00
5076 dw 0x0000 ;; base 15:00
5077 db 0x00 ;; base 23:16
5079 .org 0xfa6e ; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
5082 ;----------
5083 ;- INT1Ah -
5084 ;----------
5085 .org 0xfe6e ; INT 1Ah Time-of-day Service Entry Point
5086 int1a_handler:
5087 push ds
5088 pusha
5089 mov ax, #0x0000
5090 mov ds, ax
5091 call _int1a_function
5092 popa
5093 pop ds
5094 iret
5097 ;; int70h: IRQ8 - CMOS RTC
5099 int70_handler:
5100 push ds
5101 pusha
5102 mov ax, #0x0000
5103 mov ds, ax
5104 call _int70_function
5105 popa
5106 pop ds
5107 iret
5109 ;---------
5110 ;- INT08 -
5111 ;---------
5112 .org 0xfea5 ; INT 08h System Timer ISR Entry Point
5113 int08_handler:
5115 push eax
5116 push ds
5117 mov ax, #0x0000
5118 mov ds, ax
5119 mov eax, 0x046c ;; get ticks dword
5120 inc eax
5122 ;; compare eax to one days worth of timer ticks at 18.2 hz
5123 cmp eax, #0x001800B0
5124 jb int08_store_ticks
5125 ;; there has been a midnight rollover at this point
5126 xor eax, eax ;; zero out counter
5127 inc BYTE 0x0470 ;; increment rollover flag
5129 int08_store_ticks:
5130 mov 0x046c, eax ;; store new ticks dword
5131 ;; chain to user timer tick INT #0x1c
5132 //pushf
5133 //;; call_ep [ds:loc]
5134 //CALL_EP( 0x1c << 2 )
5135 int #0x1c
5137 mov al, #0x20
5138 out 0x20, al ; send EOI to PIC
5139 pop ds
5140 pop eax
5141 iret
5143 .org 0xfef3 ; Initial Interrupt Vector Offsets Loaded by POST
5145 ;------------------------------------------------
5146 ;- IRET Instruction for Dummy Interrupt Handler -
5147 ;------------------------------------------------
5148 .org 0xff53 ; IRET Instruction for Dummy Interrupt Handler
5149 dummy_iret_handler:
5150 iret
5152 .org 0xff54 ; INT 05h Print Screen Service Entry Point
5153 HALT(__LINE__)
5154 iret
5156 ; .org 0xff00
5157 ; .ascii "(c) 1994-2000 Kevin P. Lawton"
5159 .org 0xfff0 ; Power-up Entry Point
5160 //JMPL(post)
5161 jmp post
5163 .org 0xfff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
5164 .ascii "06/23/99"
5166 .org 0xfffe ; System Model ID
5167 db SYS_MODEL_ID
5168 db 0x00 ; filler
5170 .org 0xd000
5171 // bcc-generated data will be placed here
5173 // For documentation of this config structure, look on developer.intel.com and
5174 // search for multiprocessor specification. Note that when you change anything
5175 // you must update the checksum (a pain!). It would be better to construct this
5176 // with C structures, or at least fill in the checksum automatically.
5178 #if (BX_SMP_PROCESSORS==1)
5179 // no structure necessary.
5180 #elif (BX_SMP_PROCESSORS==2)
5181 // define the Intel MP Configuration Structure for 2 processors at
5182 // APIC ID 0,1. I/O APIC at ID=2.
5183 .align 16
5184 mp_config_table:
5185 db 0x50, 0x43, 0x4d, 0x50 ;; "PCMP" signature
5186 dw (mp_config_end-mp_config_table) ;; table length
5187 db 4 ;; spec rev
5188 db 0x65 ;; checksum
5189 .ascii "BOCHSCPU" ;; OEM id = "BOCHSCPU"
5190 db 0x30, 0x2e, 0x31, 0x20 ;; vendor id = "0.1 "
5191 db 0x20, 0x20, 0x20, 0x20
5192 db 0x20, 0x20, 0x20, 0x20
5193 dw 0,0 ;; oem table ptr
5194 dw 0 ;; oem table size
5195 dw 20 ;; entry count
5196 dw 0x0000, 0xfee0 ;; memory mapped address of local APIC
5197 dw 0 ;; extended table length
5198 db 0 ;; extended table checksum
5199 db 0 ;; reserved
5200 mp_config_proc0:
5201 db 0 ;; entry type=processor
5202 db 0 ;; local APIC id
5203 db 0x11 ;; local APIC version number
5204 db 3 ;; cpu flags: enabled
5205 db 0,6,0,0 ;; cpu signature
5206 dw 0x201,0 ;; feature flags
5207 dw 0,0 ;; reserved
5208 dw 0,0 ;; reserved
5209 mp_config_proc1:
5210 db 0 ;; entry type=processor
5211 db 1 ;; local APIC id
5212 db 0x11 ;; local APIC version number
5213 db 1 ;; cpu flags: enabled
5214 db 0,6,0,0 ;; cpu signature
5215 dw 0x201,0 ;; feature flags
5216 dw 0,0 ;; reserved
5217 dw 0,0 ;; reserved
5218 mp_config_isa_bus:
5219 db 1 ;; entry type=bus
5220 db 0 ;; bus ID
5221 db 0x49, 0x53, 0x41, 0x20, 0x20, 0x20 ;; bus type="ISA "
5222 mp_config_ioapic:
5223 db 2 ;; entry type=I/O APIC
5224 db 2 ;; apic id=2. linux will set.
5225 db 0x11 ;; I/O APIC version number
5226 db 1 ;; flags=1=enabled
5227 dw 0x0000, 0xfec0 ;; memory mapped address of I/O APIC
5228 mp_config_irqs:
5229 db 3 ;; entry type=I/O interrupt
5230 db 0 ;; interrupt type=vectored interrupt
5231 db 0,0 ;; flags po=0, el=0 (linux uses as default)
5232 db 0 ;; source bus ID is ISA
5233 db 0 ;; source bus IRQ
5234 db 2 ;; destination I/O APIC ID
5235 db 0 ;; destination I/O APIC interrrupt in
5236 ;; repeat pattern for interrupts 0-15
5237 db 3,0,0,0,0,1,2,1
5238 db 3,0,0,0,0,2,2,2
5239 db 3,0,0,0,0,3,2,3
5240 db 3,0,0,0,0,4,2,4
5241 db 3,0,0,0,0,5,2,5
5242 db 3,0,0,0,0,6,2,6
5243 db 3,0,0,0,0,7,2,7
5244 db 3,0,0,0,0,8,2,8
5245 db 3,0,0,0,0,9,2,9
5246 db 3,0,0,0,0,10,2,10
5247 db 3,0,0,0,0,11,2,11
5248 db 3,0,0,0,0,12,2,12
5249 db 3,0,0,0,0,13,2,13
5250 db 3,0,0,0,0,14,2,14
5251 db 3,0,0,0,0,15,2,15
5252 #elif (BX_SMP_PROCESSORS==4)
5253 // define the Intel MP Configuration Structure for 4 processors at
5254 // APIC ID 0,1,2,3. I/O APIC at ID=4.
5255 .align 16
5256 mp_config_table:
5257 db 0x50, 0x43, 0x4d, 0x50 ;; "PCMP" signature
5258 dw (mp_config_end-mp_config_table) ;; table length
5259 db 4 ;; spec rev
5260 db 0xdd ;; checksum
5261 .ascii "BOCHSCPU" ;; OEM id = "BOCHSCPU"
5262 db 0x30, 0x2e, 0x31, 0x20 ;; vendor id = "0.1 "
5263 db 0x20, 0x20, 0x20, 0x20
5264 db 0x20, 0x20, 0x20, 0x20
5265 dw 0,0 ;; oem table ptr
5266 dw 0 ;; oem table size
5267 dw 22 ;; entry count
5268 dw 0x0000, 0xfee0 ;; memory mapped address of local APIC
5269 dw 0 ;; extended table length
5270 db 0 ;; extended table checksum
5271 db 0 ;; reserved
5272 mp_config_proc0:
5273 db 0 ;; entry type=processor
5274 db 0 ;; local APIC id
5275 db 0x11 ;; local APIC version number
5276 db 1 ;; cpu flags: enabled
5277 db 0,6,0,0 ;; cpu signature
5278 dw 0x201,0 ;; feature flags
5279 dw 0,0 ;; reserved
5280 dw 0,0 ;; reserved
5281 mp_config_proc1:
5282 db 0 ;; entry type=processor
5283 db 1 ;; local APIC id
5284 db 0x11 ;; local APIC version number
5285 db 1 ;; cpu flags: enabled
5286 db 0,6,0,0 ;; cpu signature
5287 dw 0x201,0 ;; feature flags
5288 dw 0,0 ;; reserved
5289 dw 0,0 ;; reserved
5290 mp_config_proc2:
5291 db 0 ;; entry type=processor
5292 db 2 ;; local APIC id
5293 db 0x11 ;; local APIC version number
5294 db 3 ;; cpu flags: enabled, bootstrap processor
5295 db 0,6,0,0 ;; cpu signature
5296 dw 0x201,0 ;; feature flags
5297 dw 0,0 ;; reserved
5298 dw 0,0 ;; reserved
5299 mp_config_proc3:
5300 db 0 ;; entry type=processor
5301 db 3 ;; local APIC id
5302 db 0x11 ;; local APIC version number
5303 db 1 ;; cpu flags: enabled
5304 db 0,6,0,0 ;; cpu signature
5305 dw 0x201,0 ;; feature flags
5306 dw 0,0 ;; reserved
5307 dw 0,0 ;; reserved
5308 mp_config_isa_bus:
5309 db 1 ;; entry type=bus
5310 db 0 ;; bus ID
5311 db 0x49, 0x53, 0x41, 0x20, 0x20, 0x20 ;; bus type="ISA "
5312 mp_config_ioapic:
5313 db 2 ;; entry type=I/O APIC
5314 db 4 ;; apic id=2. linux will set.
5315 db 0x11 ;; I/O APIC version number
5316 db 1 ;; flags=1=enabled
5317 dw 0x0000, 0xfec0 ;; memory mapped address of I/O APIC
5318 mp_config_irqs:
5319 db 3 ;; entry type=I/O interrupt
5320 db 0 ;; interrupt type=vectored interrupt
5321 db 0,0 ;; flags po=0, el=0 (linux uses as default)
5322 db 0 ;; source bus ID is ISA
5323 db 0 ;; source bus IRQ
5324 db 4 ;; destination I/O APIC ID
5325 db 0 ;; destination I/O APIC interrrupt in
5326 ;; repeat pattern for interrupts 0-15
5327 db 3,0,0,0,0,1,4,1
5328 db 3,0,0,0,0,2,4,2
5329 db 3,0,0,0,0,3,4,3
5330 db 3,0,0,0,0,4,4,4
5331 db 3,0,0,0,0,5,4,5
5332 db 3,0,0,0,0,6,4,6
5333 db 3,0,0,0,0,7,4,7
5334 db 3,0,0,0,0,8,4,8
5335 db 3,0,0,0,0,9,4,9
5336 db 3,0,0,0,0,10,4,10
5337 db 3,0,0,0,0,11,4,11
5338 db 3,0,0,0,0,12,4,12
5339 db 3,0,0,0,0,13,4,13
5340 db 3,0,0,0,0,14,4,14
5341 db 3,0,0,0,0,15,4,15
5342 #else
5343 # error Sorry, rombios only has configurations for 1, 2, or 4 processors.
5344 #endif // if (BX_SMP_PROCESSORS==...)
5346 mp_config_end: // this label used to find length of mp structure
5347 db 0
5349 #if (BX_SMP_PROCESSORS>1)
5350 .align 16
5351 mp_floating_pointer_structure:
5352 db 0x5f, 0x4d, 0x50, 0x5f ; "_MP_" signature
5353 dw mp_config_table, 0xf ;; pointer to MP configuration table
5354 db 1 ;; length of this struct in 16-bit byte chunks
5355 db 4 ;; MP spec revision
5356 db 0xc1 ;; checksum
5357 db 0 ;; MP feature byte 1. value 0 means look at the config table
5358 db 0,0,0,0 ;; MP feature bytes 2-5.
5359 #endif
5361 #endasm