aboutsummaryrefslogtreecommitdiff
path: root/clients/net-snk/app/biosemu/interrupt.c
diff options
context:
space:
mode:
Diffstat (limited to 'clients/net-snk/app/biosemu/interrupt.c')
-rw-r--r--clients/net-snk/app/biosemu/interrupt.c375
1 files changed, 350 insertions, 25 deletions
diff --git a/clients/net-snk/app/biosemu/interrupt.c b/clients/net-snk/app/biosemu/interrupt.c
index b0ad12f..f1137fe 100644
--- a/clients/net-snk/app/biosemu/interrupt.c
+++ b/clients/net-snk/app/biosemu/interrupt.c
@@ -1,5 +1,5 @@
/******************************************************************************
- * Copyright (c) 2004, 2007 IBM Corporation
+ * Copyright (c) 2004, 2008 IBM Corporation
* All rights reserved.
* This program and the accompanying materials
* are made available under the terms of the BSD License
@@ -14,6 +14,7 @@
#include <rtas.h>
+#include "biosemu.h"
#include "mem.h"
#include "device.h"
#include "debug.h"
@@ -42,6 +43,273 @@ setupInt(int intNum)
M.x86.R_IP = my_rdw(intNum * 4);
}
+// handle int10 (VGA BIOS Interrupt)
+void
+handleInt10()
+{
+ // the data for INT10 is stored in BDA (0000:0400h) offset 49h-66h
+ // function number in AH
+ //DEBUG_PRINTF_CS_IP("%s:\n", __FUNCTION__);
+ //x86emu_dump_xregs();
+ //if ((M.x86.R_IP == 0x32c2) && (M.x86.R_SI == 0x1ce2)){
+ //X86EMU_trace_on();
+ //M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F;
+ //}
+ switch (M.x86.R_AH) {
+ case 0x00:
+ // set video mode
+ // BDA offset 49h is current video mode
+ my_wrb(0x449, M.x86.R_AL);
+ if (M.x86.R_AL > 7)
+ M.x86.R_AL = 0x20;
+ else if (M.x86.R_AL == 6)
+ M.x86.R_AL = 0x3f;
+ else
+ M.x86.R_AL = 0x30;
+ break;
+ case 0x01:
+ // set cursor shape
+ // ignore
+ break;
+ case 0x02:
+ // set cursor position
+ // BH: pagenumber, DX: cursor_pos (DH:row, DL:col)
+ // BDA offset 50h-60h are 8 cursor position words for
+ // eight possible video pages
+ my_wrw(0x450 + (M.x86.R_BH * 2), M.x86.R_DX);
+ break;
+ case 0x03:
+ //get cursor position
+ // BH: pagenumber
+ // BDA offset 50h-60h are 8 cursor position words for
+ // eight possible video pages
+ M.x86.R_AX = 0;
+ M.x86.R_CH = 0; // start scan line ???
+ M.x86.R_CL = 0; // end scan line ???
+ M.x86.R_DX = my_rdw(0x450 + (M.x86.R_BH * 2));
+ break;
+ case 0x05:
+ // set active page
+ // BDA offset 62h is current page number
+ my_wrb(0x462, M.x86.R_AL);
+ break;
+ case 0x06:
+ //scroll up windows
+ break;
+ case 0x07:
+ //scroll down windows
+ break;
+ case 0x08:
+ //read character and attribute at position
+ M.x86.R_AH = 0x07; // white-on-black
+ M.x86.R_AL = 0x20; // a space...
+ break;
+ case 0x09:
+ // write character and attribute
+ //AL: char, BH: page number, BL: attribute, CX: number of times to write
+ //BDA offset 62h is current page number
+ CHECK_DBG(DEBUG_PRINT_INT10) {
+ uint32_t i = 0;
+ if (M.x86.R_BH == my_rdb(0x462)) {
+ for (i = 0; i < M.x86.R_CX; i++)
+ printf("%c", M.x86.R_AL);
+ }
+ }
+ break;
+ case 0x0a:
+ // write character
+ //AL: char, BH: page number, BL: attribute, CX: number of times to write
+ //BDA offset 62h is current page number
+ CHECK_DBG(DEBUG_PRINT_INT10) {
+ uint32_t i = 0;
+ if (M.x86.R_BH == my_rdb(0x462)) {
+ for (i = 0; i < M.x86.R_CX; i++)
+ printf("%c", M.x86.R_AL);
+ }
+ }
+ break;
+ case 0x0e:
+ // teletype output: write character and advance cursor...
+ //AL: char, BH: page number, BL: attribute
+ //BDA offset 62h is current page number
+ CHECK_DBG(DEBUG_PRINT_INT10) {
+ // we ignore the pagenumber on this call...
+ //if (M.x86.R_BH == my_rdb(0x462))
+ {
+ printf("%c", M.x86.R_AL);
+ // for debugging, to read all lines
+ //if (M.x86.R_AL == 0xd) // carriage return
+ // printf("\n");
+ }
+ }
+ break;
+ case 0x0f:
+ // get video mode
+ // BDA offset 49h is current video mode
+ // BDA offset 62h is current page number
+ // BDA offset 4ah is columns on screen
+ M.x86.R_AH = 80; //number of character columns... we hardcode it to 80
+ M.x86.R_AL = my_rdb(0x449);
+ M.x86.R_BH = my_rdb(0x462);
+ break;
+ default:
+ printf("%s(): unknown function (%x) for int10 handler.\n",
+ __FUNCTION__, M.x86.R_AH);
+ DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n",
+ M.x86.R_AX, M.x86.R_BX, M.x86.R_CX,
+ M.x86.R_DX);
+ HALT_SYS();
+ break;
+ }
+}
+
+// this table translates ASCII chars into their XT scan codes:
+static uint8_t keycode_table[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0 - 7
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8 - 15
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 16 - 23
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 24 - 31
+ 0x39, 0x02, 0x28, 0x04, 0x05, 0x06, 0x08, 0x28, // 32 - 39
+ 0x0a, 0x0b, 0x09, 0x2b, 0x33, 0x0d, 0x34, 0x35, // 40 - 47
+ 0x0b, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // 48 - 55
+ 0x09, 0x0a, 0x27, 0x27, 0x33, 0x2b, 0x34, 0x35, // 56 - 63
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 64 - 71
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 72 - 79
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 80 - 87
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 88 - 95
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 96 - 103
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 104 - 111
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 112 - 119
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 120 - 127
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ...
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+}
+
+;
+
+void
+translate_keycode(uint64_t * keycode)
+{
+ uint8_t scan_code = 0;
+ uint8_t char_code = 0;
+ if (*keycode < 256) {
+ scan_code = keycode_table[*keycode];
+ char_code = (uint8_t) * keycode & 0xff;
+ } else {
+ switch (*keycode) {
+ case 0x1b50:
+ // F1
+ scan_code = 0x3b;
+ char_code = 0x0;
+ break;
+ default:
+ printf("%s(): unknown multibyte keycode: %llx\n",
+ __FUNCTION__, *keycode);
+ break;
+ }
+ }
+ //assemble scan/char code in keycode
+ *keycode = (uint64_t) ((((uint16_t) scan_code) << 8) | char_code);
+}
+
+// handle int16 (Keyboard BIOS Interrupt)
+void
+handleInt16()
+{
+ // keyboard buffer is in BIOS Memory Area:
+ // offset 0x1a (WORD) pointer to next char in keybuffer
+ // offset 0x1c (WORD) pointer to next insert slot in keybuffer
+ // offset 0x1e-0x3e: 16 WORD Ring Buffer
+ // since we currently always read the char from the FW buffer,
+ // we misuse the ring buffer, we use it as pointer to a uint64_t that stores
+ // multi-byte keys (e.g. special keys in VT100 terminal)
+ // and as long as a key is available (not 0) we dont read further keys
+ uint64_t *keycode = (uint64_t *) (M.mem_base + 0x41e);
+ int8_t c;
+ // function number in AH
+ DEBUG_PRINTF_INTR("%s(): Keyboard Interrupt: function: %x.\n",
+ __FUNCTION__, M.x86.R_AH);
+ DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n", M.x86.R_AX,
+ M.x86.R_BX, M.x86.R_CX, M.x86.R_DX);
+ switch (M.x86.R_AH) {
+ case 0x00:
+ // get keystroke
+ if (*keycode) {
+ M.x86.R_AX = (uint16_t) * keycode;
+ // clear keycode
+ *keycode = 0;
+ } else {
+ M.x86.R_AH = 0x61; // scancode for space key
+ M.x86.R_AL = 0x20; // a space
+ }
+ break;
+ case 0x01:
+ // check keystroke
+ // ZF set = no keystroke
+ // read first byte of key code
+ if (*keycode) {
+ // already read, but not yet taken
+ CLEAR_FLAG(F_ZF);
+ M.x86.R_AX = (uint16_t) * keycode;
+ } else {
+ c = getchar();
+ if (c == -1) {
+ // no key available
+ SET_FLAG(F_ZF);
+ } else {
+ *keycode = c;
+
+ // since after an ESC it may take a while to receive the next char,
+ // we send something that is not shown on the screen, and then try to get
+ // the next char
+ // TODO: only after ESC?? what about other multibyte keys
+ printf("tt%c%c", 0x08, 0x08); // 0x08 == Backspace
+
+ while ((c = getchar()) != -1) {
+ *keycode = (*keycode << 8) | c;
+ DEBUG_PRINTF(" key read: %0llx\n",
+ *keycode);
+ }
+ translate_keycode(keycode);
+ DEBUG_PRINTF(" translated key: %0llx\n",
+ *keycode);
+ if (*keycode == 0) {
+ //not found
+ SET_FLAG(F_ZF);
+ } else {
+ CLEAR_FLAG(F_ZF);
+ M.x86.R_AX = (uint16_t) * keycode;
+ //X86EMU_trace_on();
+ //M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F;
+ }
+ }
+ }
+ break;
+ default:
+ printf("%s(): unknown function (%x) for int16 handler.\n",
+ __FUNCTION__, M.x86.R_AH);
+ DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n",
+ M.x86.R_AX, M.x86.R_BX, M.x86.R_CX,
+ M.x86.R_DX);
+ HALT_SYS();
+ break;
+ }
+}
+
// handle int1a (PCI BIOS Interrupt)
void
handleInt1a()
@@ -62,10 +330,14 @@ handleInt1a()
// NOTE: we currently only allow the device to find itself...
// it SHOULD be all we ever need...
// device_id in CX, vendor_id in DX
+ // device index in SI (i.e. if multiple devices with same vendor/device id
+ // are connected). We currently only support device index 0
DEBUG_PRINTF_INTR("%s(): function: %x: PCI Find Device\n",
__FUNCTION__, M.x86.R_AX);
if ((M.x86.R_CX == bios_device.pci_device_id)
- && (M.x86.R_DX == bios_device.pci_vendor_id)) {
+ && (M.x86.R_DX == bios_device.pci_vendor_id)
+ // device index must be 0
+ && (M.x86.R_SI == 0)) {
CLEAR_FLAG(F_CF);
M.x86.R_AH = 0x00; // return code: success
M.x86.R_BH = bios_device.bus;
@@ -75,9 +347,9 @@ handleInt1a()
__FUNCTION__, M.x86.R_AX, M.x86.R_BX);
} else {
DEBUG_PRINTF_INTR
- ("%s(): function %x: invalid device/vendor! (%04x/%04x expected: %04x/%04x) \n",
+ ("%s(): function %x: invalid device/vendor/device index! (%04x/%04x/%02x expected: %04x/%04x/0) \n",
__FUNCTION__, M.x86.R_AX, M.x86.R_CX, M.x86.R_DX,
- bios_device.pci_device_id,
+ M.x86.R_SI, bios_device.pci_device_id,
bios_device.pci_vendor_id);
SET_FLAG(F_CF);
M.x86.R_AH = 0x86; // return code: device not found
@@ -189,16 +461,14 @@ handleInt1a()
}
break;
default:
- DEBUG_PRINTF_INTR
- ("%s(): unknown function (%x) for int1a handler.\n",
- __FUNCTION__, M.x86.R_AX);
+ printf("%s(): unknown function (%x) for int1a handler.\n",
+ __FUNCTION__, M.x86.R_AX);
DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n",
M.x86.R_AX, M.x86.R_BX, M.x86.R_CX,
M.x86.R_DX);
HALT_SYS();
break;
}
-
}
// main Interrupt Handler routine, should be registered as x86emu interrupt handler
@@ -206,32 +476,49 @@ void
handleInterrupt(int intNum)
{
uint8_t int_handled = 0;
+#ifndef DEBUG_PRINT_INT10
+ // this printf makes output by int 10 unreadable...
+ // so we only enable it, if int10 print is disabled
DEBUG_PRINTF_INTR("%s(%x)\n", __FUNCTION__, intNum);
+#endif
switch (intNum) {
case 0x10: //BIOS video interrupt
case 0x42: // INT 10h relocated by EGA/VGA BIOS
case 0x6d: // INT 10h relocated by VGA BIOS
// get interrupt vector from IDT (4 bytes per Interrupt starting at address 0
- if (my_rdl(intNum * 4) == 0xF000F065) //F000:F065 is default BIOS interrupt handler address
+ if ((my_rdl(intNum * 4) == 0xF000F065) || //F000:F065 is default BIOS interrupt handler address
+ (my_rdl(intNum * 4) == 0xF4F4F4F4)) //invalid
{
- // default handler called, ignore interrupt...
+#if 0
+ // ignore interrupt...
DEBUG_PRINTF_INTR
- ("%s(%x): default interrupt Vector (%08x) found, interrupt ignored...\n",
+ ("%s(%x): invalid interrupt Vector (%08x) found, interrupt ignored...\n",
__FUNCTION__, intNum, my_rdl(intNum * 4));
DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n",
M.x86.R_AX, M.x86.R_BX, M.x86.R_CX,
M.x86.R_DX);
//HALT_SYS();
+#endif
+ handleInt10();
int_handled = 1;
}
break;
+ case 0x16:
+ // Keyboard BIOS Interrupt
+ handleInt16();
+ int_handled = 1;
+ break;
case 0x1a:
// PCI BIOS Interrupt
handleInt1a();
int_handled = 1;
break;
default:
- DEBUG_PRINTF_INTR("Interrupt %#x (Vector: %x) not implemented\n", intNum, my_rdl(intNum * 4)); // 4bytes per interrupt vector...
+ printf("Interrupt %#x (Vector: %x) not implemented\n", intNum,
+ my_rdl(intNum * 4));
+ DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n",
+ M.x86.R_AX, M.x86.R_BX, M.x86.R_CX,
+ M.x86.R_DX);
int_handled = 1;
HALT_SYS();
break;
@@ -247,9 +534,9 @@ void
runInt10()
{
// Initialize stack and data segment
- M.x86.R_SS = 0x0030;
- M.x86.R_DS = 0x0040;
- M.x86.R_SP = 0xfffe;
+ M.x86.R_SS = STACK_SEGMENT;
+ M.x86.R_DS = DATA_SEGMENT;
+ M.x86.R_SP = STACK_START_OFFSET;
// push a HLT instruction and a pointer to it onto the stack
// any return will pop the pointer and jump to the HLT, thus
@@ -264,19 +551,57 @@ runInt10()
M.x86.R_CS = M.x86.R_SS;
M.x86.R_IP = M.x86.R_SP; // + 4;
-#ifdef DEBUG_TRACE_X86EMU
- X86EMU_trace_on();
-#endif
-#ifdef DEBUG_JMP
- M.x86.debug |= DEBUG_TRACEJMP_REGS_F;
- M.x86.debug |= DEBUG_TRACEJMP_REGS_F;
- M.x86.debug |= DEBUG_TRACECALL_F;
- M.x86.debug |= DEBUG_TRACECALL_REGS_F;
-#endif
-
+ CHECK_DBG(DEBUG_TRACE_X86EMU) {
+ X86EMU_trace_on();
+ }
+ CHECK_DBG(DEBUG_JMP) {
+ M.x86.debug |= DEBUG_TRACEJMP_REGS_F;
+ M.x86.debug |= DEBUG_TRACEJMP_REGS_F;
+ M.x86.debug |= DEBUG_TRACECALL_F;
+ M.x86.debug |= DEBUG_TRACECALL_REGS_F;
+ }
setupInt(0x10);
DEBUG_PRINTF_INTR("%s(): starting execution of INT10...\n",
__FUNCTION__);
X86EMU_exec();
DEBUG_PRINTF_INTR("%s(): execution finished\n", __FUNCTION__);
}
+
+// prepare and execute Interrupt 13 (Disk Interrupt)
+void
+runInt13()
+{
+ // Initialize stack and data segment
+ M.x86.R_SS = STACK_SEGMENT;
+ M.x86.R_DS = DATA_SEGMENT;
+ M.x86.R_SP = STACK_START_OFFSET;
+
+ // push a HLT instruction and a pointer to it onto the stack
+ // any return will pop the pointer and jump to the HLT, thus
+ // exiting (more or less) cleanly
+ push_word(0xf4f4); //F4=HLT
+ //push_word(M.x86.R_SS);
+ //push_word(M.x86.R_SP + 2);
+
+ // setupInt will push the current CS and IP to the stack to return to it,
+ // but we want to halt, so set CS:IP to the HLT instruction we just pushed
+ // to the stack
+ M.x86.R_CS = M.x86.R_SS;
+ M.x86.R_IP = M.x86.R_SP;
+
+ CHECK_DBG(DEBUG_TRACE_X86EMU) {
+ X86EMU_trace_on();
+ }
+ CHECK_DBG(DEBUG_JMP) {
+ M.x86.debug |= DEBUG_TRACEJMP_REGS_F;
+ M.x86.debug |= DEBUG_TRACEJMP_REGS_F;
+ M.x86.debug |= DEBUG_TRACECALL_F;
+ M.x86.debug |= DEBUG_TRACECALL_REGS_F;
+ }
+
+ setupInt(0x13);
+ DEBUG_PRINTF_INTR("%s(): starting execution of INT13...\n",
+ __FUNCTION__);
+ X86EMU_exec();
+ DEBUG_PRINTF_INTR("%s(): execution finished\n", __FUNCTION__);
+}