1 /******************************************************************************
2 * Copyright (c) 2004, 2008 IBM Corporation
4 * This program and the accompanying materials
5 * are made available under the terms of the BSD License
6 * which accompanies this distribution, and is available at
7 * http://www.opensource.org/licenses/bsd-license.php
10 * IBM Corporation - initial implementation
11 *****************************************************************************/
21 #include "interrupt.h"
23 #include <x86emu/x86emu.h>
24 #include <x86emu/prim_ops.h>
28 //setup to run the code at the address, that the Interrupt Vector points to...
32 DEBUG_PRINTF_INTR("%s(%x): executing interrupt handler @%08x\n",
33 __FUNCTION__, intNum, my_rdl(intNum * 4));
34 // push current R_FLG... will be popped by IRET
35 push_word((u16) M.x86.R_FLG);
38 // push current CS:IP to the stack, will be popped by IRET
39 push_word(M.x86.R_CS);
40 push_word(M.x86.R_IP);
41 // set CS:IP to the interrupt handler address... so the next executed instruction will
42 // be the interrupt handler
43 M.x86.R_CS = my_rdw(intNum * 4 + 2);
44 M.x86.R_IP = my_rdw(intNum * 4);
47 // handle int10 (VGA BIOS Interrupt)
51 // the data for INT10 is stored in BDA (0000:0400h) offset 49h-66h
52 // function number in AH
53 //DEBUG_PRINTF_CS_IP("%s:\n", __FUNCTION__);
54 //x86emu_dump_xregs();
55 //if ((M.x86.R_IP == 0x32c2) && (M.x86.R_SI == 0x1ce2)){
57 //M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F;
62 // BDA offset 49h is current video mode
63 my_wrb(0x449, M.x86.R_AL);
66 else if (M.x86.R_AL == 6)
76 // set cursor position
77 // BH: pagenumber, DX: cursor_pos (DH:row, DL:col)
78 // BDA offset 50h-60h are 8 cursor position words for
79 // eight possible video pages
80 my_wrw(0x450 + (M.x86.R_BH * 2), M.x86.R_DX);
85 // BDA offset 50h-60h are 8 cursor position words for
86 // eight possible video pages
88 M.x86.R_CH = 0; // start scan line ???
89 M.x86.R_CL = 0; // end scan line ???
90 M.x86.R_DX = my_rdw(0x450 + (M.x86.R_BH * 2));
94 // BDA offset 62h is current page number
95 my_wrb(0x462, M.x86.R_AL);
101 //scroll down windows
104 //read character and attribute at position
105 M.x86.R_AH = 0x07; // white-on-black
106 M.x86.R_AL = 0x20; // a space...
109 // write character and attribute
110 //AL: char, BH: page number, BL: attribute, CX: number of times to write
111 //BDA offset 62h is current page number
112 CHECK_DBG(DEBUG_PRINT_INT10) {
114 if (M.x86.R_BH == my_rdb(0x462)) {
115 for (i = 0; i < M.x86.R_CX; i++)
116 printf("%c", M.x86.R_AL);
122 //AL: char, BH: page number, BL: attribute, CX: number of times to write
123 //BDA offset 62h is current page number
124 CHECK_DBG(DEBUG_PRINT_INT10) {
126 if (M.x86.R_BH == my_rdb(0x462)) {
127 for (i = 0; i < M.x86.R_CX; i++)
128 printf("%c", M.x86.R_AL);
133 // teletype output: write character and advance cursor...
134 //AL: char, BH: page number, BL: attribute
135 //BDA offset 62h is current page number
136 CHECK_DBG(DEBUG_PRINT_INT10) {
137 // we ignore the pagenumber on this call...
138 //if (M.x86.R_BH == my_rdb(0x462))
140 printf("%c", M.x86.R_AL);
141 // for debugging, to read all lines
142 //if (M.x86.R_AL == 0xd) // carriage return
149 // BDA offset 49h is current video mode
150 // BDA offset 62h is current page number
151 // BDA offset 4ah is columns on screen
152 M.x86.R_AH = 80; //number of character columns... we hardcode it to 80
153 M.x86.R_AL = my_rdb(0x449);
154 M.x86.R_BH = my_rdb(0x462);
157 printf("%s(): unknown function (%x) for int10 handler.\n",
158 __FUNCTION__, M.x86.R_AH);
159 DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n",
160 M.x86.R_AX, M.x86.R_BX, M.x86.R_CX,
167 // this table translates ASCII chars into their XT scan codes:
168 static uint8_t keycode_table[256] = {
169 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0 - 7
170 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8 - 15
171 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 16 - 23
172 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 24 - 31
173 0x39, 0x02, 0x28, 0x04, 0x05, 0x06, 0x08, 0x28, // 32 - 39
174 0x0a, 0x0b, 0x09, 0x2b, 0x33, 0x0d, 0x34, 0x35, // 40 - 47
175 0x0b, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // 48 - 55
176 0x09, 0x0a, 0x27, 0x27, 0x33, 0x2b, 0x34, 0x35, // 56 - 63
177 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 64 - 71
178 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 72 - 79
179 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 80 - 87
180 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 88 - 95
181 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 96 - 103
182 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 104 - 111
183 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 112 - 119
184 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 120 - 127
185 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ...
186 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
187 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
188 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
189 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
190 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
191 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
192 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
193 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
194 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
195 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
196 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
197 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
198 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
199 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
200 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
204 translate_keycode(uint64_t * keycode)
206 uint8_t scan_code = 0;
207 uint8_t char_code = 0;
208 if (*keycode < 256) {
209 scan_code = keycode_table[*keycode];
210 char_code = (uint8_t) * keycode & 0xff;
219 printf("%s(): unknown multibyte keycode: %llx\n",
220 __FUNCTION__, *keycode);
224 //assemble scan/char code in keycode
225 *keycode = (uint64_t) ((((uint16_t) scan_code) << 8) | char_code);
228 // handle int16 (Keyboard BIOS Interrupt)
232 // keyboard buffer is in BIOS Memory Area:
233 // offset 0x1a (WORD) pointer to next char in keybuffer
234 // offset 0x1c (WORD) pointer to next insert slot in keybuffer
235 // offset 0x1e-0x3e: 16 WORD Ring Buffer
236 // since we currently always read the char from the FW buffer,
237 // we misuse the ring buffer, we use it as pointer to a uint64_t that stores
238 // multi-byte keys (e.g. special keys in VT100 terminal)
239 // and as long as a key is available (not 0) we dont read further keys
240 uint64_t *keycode = (uint64_t *) (M.mem_base + 0x41e);
242 // function number in AH
243 DEBUG_PRINTF_INTR("%s(): Keyboard Interrupt: function: %x.\n",
244 __FUNCTION__, M.x86.R_AH);
245 DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n", M.x86.R_AX,
246 M.x86.R_BX, M.x86.R_CX, M.x86.R_DX);
247 switch (M.x86.R_AH) {
251 M.x86.R_AX = (uint16_t) * keycode;
255 M.x86.R_AH = 0x61; // scancode for space key
256 M.x86.R_AL = 0x20; // a space
261 // ZF set = no keystroke
262 // read first byte of key code
264 // already read, but not yet taken
266 M.x86.R_AX = (uint16_t) * keycode;
275 // since after an ESC it may take a while to receive the next char,
276 // we send something that is not shown on the screen, and then try to get
278 // TODO: only after ESC?? what about other multibyte keys
279 printf("tt%c%c", 0x08, 0x08); // 0x08 == Backspace
281 while ((c = getchar()) != -1) {
282 *keycode = (*keycode << 8) | c;
283 DEBUG_PRINTF(" key read: %0llx\n",
286 translate_keycode(keycode);
287 DEBUG_PRINTF(" translated key: %0llx\n",
294 M.x86.R_AX = (uint16_t) * keycode;
296 //M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F;
302 printf("%s(): unknown function (%x) for int16 handler.\n",
303 __FUNCTION__, M.x86.R_AH);
304 DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n",
305 M.x86.R_AX, M.x86.R_BX, M.x86.R_CX,
312 // handle int1a (PCI BIOS Interrupt)
316 // function number in AX
317 uint8_t bus, devfn, offs;
318 switch (M.x86.R_AX) {
320 // Installation check
321 CLEAR_FLAG(F_CF); // clear CF
322 M.x86.R_EDX = 0x20494350; // " ICP" endian swapped "PCI "
323 M.x86.R_AL = 0x1; // Config Space Mechanism 1 supported
324 M.x86.R_BX = 0x0210; // PCI Interface Level Version 2.10
325 M.x86.R_CL = 0xff; // number of last PCI Bus in system TODO: check!
329 // NOTE: we currently only allow the device to find itself...
330 // it SHOULD be all we ever need...
331 // device_id in CX, vendor_id in DX
332 // device index in SI (i.e. if multiple devices with same vendor/device id
333 // are connected). We currently only support device index 0
334 DEBUG_PRINTF_INTR("%s(): function: %x: PCI Find Device\n",
335 __FUNCTION__, M.x86.R_AX);
336 if ((M.x86.R_CX == bios_device.pci_device_id)
337 && (M.x86.R_DX == bios_device.pci_vendor_id)
338 // device index must be 0
339 && (M.x86.R_SI == 0)) {
341 M.x86.R_AH = 0x00; // return code: success
342 M.x86.R_BH = bios_device.bus;
343 M.x86.R_BL = bios_device.devfn;
345 ("%s(): function %x: PCI Find Device --> 0x%04x\n",
346 __FUNCTION__, M.x86.R_AX, M.x86.R_BX);
349 ("%s(): function %x: invalid device/vendor/device index! (%04x/%04x/%02x expected: %04x/%04x/0) \n",
350 __FUNCTION__, M.x86.R_AX, M.x86.R_CX, M.x86.R_DX,
351 M.x86.R_SI, bios_device.pci_device_id,
352 bios_device.pci_vendor_id);
354 M.x86.R_AH = 0x86; // return code: device not found
357 case 0xb108: //read configuration byte
358 case 0xb109: //read configuration word
359 case 0xb10a: //read configuration dword
363 if ((bus != bios_device.bus)
364 || (devfn != bios_device.devfn)) {
365 // fail accesses to any device but ours...
367 ("%s(): Config read access invalid! bus: %x (%x), devfn: %x (%x), offs: %x\n",
368 __FUNCTION__, bus, bios_device.bus, devfn,
369 bios_device.devfn, offs);
371 M.x86.R_AH = 0x87; //return code: bad pci register
375 switch (M.x86.R_AX) {
378 (uint8_t) rtas_pci_config_read(bios_device.
383 ("%s(): function %x: PCI Config Read @%02x --> 0x%02x\n",
384 __FUNCTION__, M.x86.R_AX, offs,
389 (uint16_t) rtas_pci_config_read(bios_device.
394 ("%s(): function %x: PCI Config Read @%02x --> 0x%04x\n",
395 __FUNCTION__, M.x86.R_AX, offs,
400 (uint32_t) rtas_pci_config_read(bios_device.
405 ("%s(): function %x: PCI Config Read @%02x --> 0x%08x\n",
406 __FUNCTION__, M.x86.R_AX, offs,
411 M.x86.R_AH = 0x0; // return code: success
414 case 0xb10b: //write configuration byte
415 case 0xb10c: //write configuration word
416 case 0xb10d: //write configuration dword
420 if ((bus != bios_device.bus)
421 || (devfn != bios_device.devfn)) {
422 // fail accesses to any device but ours...
424 ("%s(): Config read access invalid! bus: %x (%x), devfn: %x (%x), offs: %x\n",
425 __FUNCTION__, bus, bios_device.bus, devfn,
426 bios_device.devfn, offs);
428 M.x86.R_AH = 0x87; //return code: bad pci register
432 switch (M.x86.R_AX) {
434 rtas_pci_config_write(bios_device.puid, 1, bus,
435 devfn, offs, M.x86.R_CL);
437 ("%s(): function %x: PCI Config Write @%02x <-- 0x%02x\n",
438 __FUNCTION__, M.x86.R_AX, offs,
442 rtas_pci_config_write(bios_device.puid, 2, bus,
443 devfn, offs, M.x86.R_CX);
445 ("%s(): function %x: PCI Config Write @%02x <-- 0x%04x\n",
446 __FUNCTION__, M.x86.R_AX, offs,
450 rtas_pci_config_write(bios_device.puid, 4, bus,
451 devfn, offs, M.x86.R_ECX);
453 ("%s(): function %x: PCI Config Write @%02x <-- 0x%08x\n",
454 __FUNCTION__, M.x86.R_AX, offs,
459 M.x86.R_AH = 0x0; // return code: success
463 printf("%s(): unknown function (%x) for int1a handler.\n",
464 __FUNCTION__, M.x86.R_AX);
465 DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n",
466 M.x86.R_AX, M.x86.R_BX, M.x86.R_CX,
473 // main Interrupt Handler routine, should be registered as x86emu interrupt handler
475 handleInterrupt(int intNum)
477 uint8_t int_handled = 0;
478 #ifndef DEBUG_PRINT_INT10
479 // this printf makes output by int 10 unreadable...
480 // so we only enable it, if int10 print is disabled
481 DEBUG_PRINTF_INTR("%s(%x)\n", __FUNCTION__, intNum);
484 case 0x10: //BIOS video interrupt
485 case 0x42: // INT 10h relocated by EGA/VGA BIOS
486 case 0x6d: // INT 10h relocated by VGA BIOS
487 // get interrupt vector from IDT (4 bytes per Interrupt starting at address 0
488 if ((my_rdl(intNum * 4) == 0xF000F065) || //F000:F065 is default BIOS interrupt handler address
489 (my_rdl(intNum * 4) == 0xF4F4F4F4)) //invalid
492 // ignore interrupt...
494 ("%s(%x): invalid interrupt Vector (%08x) found, interrupt ignored...\n",
495 __FUNCTION__, intNum, my_rdl(intNum * 4));
496 DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n",
497 M.x86.R_AX, M.x86.R_BX, M.x86.R_CX,
506 // Keyboard BIOS Interrupt
511 // PCI BIOS Interrupt
516 printf("Interrupt %#x (Vector: %x) not implemented\n", intNum,
518 DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n",
519 M.x86.R_AX, M.x86.R_BX, M.x86.R_CX,
525 // if we did not handle the interrupt, jump to the interrupt vector...
531 // prepare and execute Interrupt 10 (VGA Interrupt)
535 // Initialize stack and data segment
536 M.x86.R_SS = STACK_SEGMENT;
537 M.x86.R_DS = DATA_SEGMENT;
538 M.x86.R_SP = STACK_START_OFFSET;
540 // push a HLT instruction and a pointer to it onto the stack
541 // any return will pop the pointer and jump to the HLT, thus
542 // exiting (more or less) cleanly
543 push_word(0xf4f4); //F4=HLT
544 //push_word(M.x86.R_SS);
545 //push_word(M.x86.R_SP + 2);
547 // setupInt will push the current CS and IP to the stack to return to it,
548 // but we want to halt, so set CS:IP to the HLT instruction we just pushed
550 M.x86.R_CS = M.x86.R_SS;
551 M.x86.R_IP = M.x86.R_SP; // + 4;
553 CHECK_DBG(DEBUG_TRACE_X86EMU) {
556 CHECK_DBG(DEBUG_JMP) {
557 M.x86.debug |= DEBUG_TRACEJMP_REGS_F;
558 M.x86.debug |= DEBUG_TRACEJMP_REGS_F;
559 M.x86.debug |= DEBUG_TRACECALL_F;
560 M.x86.debug |= DEBUG_TRACECALL_REGS_F;
563 DEBUG_PRINTF_INTR("%s(): starting execution of INT10...\n",
566 DEBUG_PRINTF_INTR("%s(): execution finished\n", __FUNCTION__);
569 // prepare and execute Interrupt 13 (Disk Interrupt)
573 // Initialize stack and data segment
574 M.x86.R_SS = STACK_SEGMENT;
575 M.x86.R_DS = DATA_SEGMENT;
576 M.x86.R_SP = STACK_START_OFFSET;
578 // push a HLT instruction and a pointer to it onto the stack
579 // any return will pop the pointer and jump to the HLT, thus
580 // exiting (more or less) cleanly
581 push_word(0xf4f4); //F4=HLT
582 //push_word(M.x86.R_SS);
583 //push_word(M.x86.R_SP + 2);
585 // setupInt will push the current CS and IP to the stack to return to it,
586 // but we want to halt, so set CS:IP to the HLT instruction we just pushed
588 M.x86.R_CS = M.x86.R_SS;
589 M.x86.R_IP = M.x86.R_SP;
591 CHECK_DBG(DEBUG_TRACE_X86EMU) {
594 CHECK_DBG(DEBUG_JMP) {
595 M.x86.debug |= DEBUG_TRACEJMP_REGS_F;
596 M.x86.debug |= DEBUG_TRACEJMP_REGS_F;
597 M.x86.debug |= DEBUG_TRACECALL_F;
598 M.x86.debug |= DEBUG_TRACECALL_REGS_F;
602 DEBUG_PRINTF_INTR("%s(): starting execution of INT13...\n",
605 DEBUG_PRINTF_INTR("%s(): execution finished\n", __FUNCTION__);