diff options
Diffstat (limited to 'clients/net-snk/app/biosemu/interrupt.c')
-rw-r--r-- | clients/net-snk/app/biosemu/interrupt.c | 375 |
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__); +} |