summaryrefslogtreecommitdiffstats
path: root/usr.bin/doscmd/cpu.c
diff options
context:
space:
mode:
authortg <tg@FreeBSD.org>2001-07-24 11:44:20 +0000
committertg <tg@FreeBSD.org>2001-07-24 11:44:20 +0000
commit97507091ff20d3dc72113dfbd0c9532423d3acb4 (patch)
treef37909a1b76ac49ce96e941e94a62649b7210a4e /usr.bin/doscmd/cpu.c
parentdfb5f5d5892585bd43bea7a627c21456e0ff9687 (diff)
downloadFreeBSD-src-97507091ff20d3dc72113dfbd0c9532423d3acb4.zip
FreeBSD-src-97507091ff20d3dc72113dfbd0c9532423d3acb4.tar.gz
Rewrite video emulation. Features:
- slightly more accurate VGA hardware emulation; - more int 10 functions, especially wrt to palette handling; - first shot at graphics support; - mode switching. Bugs: - graphics too slow; - only 16 color modes work for now; - works only under X, and only with 16 bit TrueColor visuals; - far from being genuinely useful (I can play an old EGA game now, though (mahjongg.exe)). Also, the code has been cleaned up a bit (more to come in a separate commit).
Diffstat (limited to 'usr.bin/doscmd/cpu.c')
-rw-r--r--usr.bin/doscmd/cpu.c279
1 files changed, 275 insertions, 4 deletions
diff --git a/usr.bin/doscmd/cpu.c b/usr.bin/doscmd/cpu.c
index c273084..e0d58a7 100644
--- a/usr.bin/doscmd/cpu.c
+++ b/usr.bin/doscmd/cpu.c
@@ -1,9 +1,42 @@
/*
-** No copyright ?!
-**
-** $FreeBSD$
-*/
+ * Copyright (c) 2001 The FreeBSD Project, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY The FreeBSD Project, Inc. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL The FreeBSD Project, Inc. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
#include "doscmd.h"
+#include "video.h"
+
+static u_int32_t decode_modrm(u_int8_t *, u_int16_t, regcontext_t *, int *);
+static u_int8_t reg8(u_int8_t c, regcontext_t *);
+static u_int16_t reg16(u_int8_t c, regcontext_t *);
+#if 0
+static u_int32_t reg32(u_int8_t c, regcontext_t *);
+#endif
+static void write_byte(u_int32_t, u_int8_t);
+static void write_word(u_int32_t, u_int16_t);
/*
** Hardware /0 interrupt
@@ -68,3 +101,241 @@ cpu_init(void)
ivec[0x3e] = vec; /* floating point emulator */
ivec[0x3f] = vec; /* floating point emulator */
}
+
+/*
+ * Emulate CPU instructions. We need this for VGA graphics, at least in the 16
+ * color modes.
+ *
+ * The emulator is far from complete. We are adding the instructions as we
+ * encounter them, so this function is likely to change over time. There are
+ * no optimizations and we only emulate a single instruction at a time.
+ *
+ * As long as there is no support for DPMI or the Operand Size Override prefix
+ * we won't need the 32-bit registers. This also means that the default
+ * operand size is 16 bit.
+ */
+int
+emu_instr(regcontext_t *REGS)
+{
+ int prefix = 1;
+ u_int8_t *cs = (u_int8_t *)(R_CS << 4);
+ int ip = R_IP;
+ int instrlen;
+ int dir;
+ u_int16_t value;
+ u_int16_t seg = R_DS;
+ u_int32_t addr, endaddr;
+
+ while (prefix) {
+ prefix = 0;
+ switch (cs[ip]) {
+ case 0x26: /* Segment Override ES */
+ seg = R_ES;
+ prefix = 1;
+ ip++;
+ break;
+ case 0x2e: /* Segment Override CS */
+ seg = R_CS;
+ prefix = 1;
+ ip++;
+ break;
+ case 0x36: /* Segment Override SS */
+ seg = R_SS;
+ prefix = 1;
+ ip++;
+ break;
+ case 0x3e: /* Segment Override DS */
+ seg = R_DS;
+ prefix = 1;
+ ip++;
+ break;
+ case 0x64: /* Segment Override FS */
+ seg = R_FS;
+ prefix = 1;
+ ip++;
+ break;
+ case 0x65: /* Segment Override GS */
+ seg = R_GS;
+ prefix = 1;
+ ip++;
+ break;
+ case 0x88: /* mov r/m8, r8 */
+ addr = decode_modrm(cs + ip, seg, REGS, &instrlen);
+ write_byte(addr, reg8(cs[ip + 1], REGS));
+ ip += 2 + instrlen;
+ break;
+ case 0xc6: /* mov r/m8, imm8 */
+ addr = decode_modrm(cs + ip, seg, REGS, &instrlen);
+ write_byte(addr, cs[ip + 2 + instrlen]);
+ ip += 2 + instrlen + 1;
+ break;
+ case 0xc7: /* mov r/m32/16, imm32/16 */
+ addr = decode_modrm(cs + ip, seg, REGS, &instrlen);
+ value = *(u_int16_t *)&cs[ip + 2 + instrlen];
+ write_word(addr, value);
+ ip += 2 + instrlen + 2;
+ break;
+ case 0xab: /* stos m32/16*/
+ break;
+ case 0xf3: /* rep */
+ switch (cs[++ip]) {
+ case 0xab: /* stos m32/16 */
+ value = R_AX;
+ /* direction */
+ dir = (R_EFLAGS & PSL_D) ? -1 : 1;
+ addr = MAKEPTR(R_ES, R_DI);
+ endaddr = MAKEPTR(R_ES, R_DI) + dir * R_CX;
+ if (addr <= endaddr)
+ while (addr <= endaddr) {
+ write_word(addr, value);
+ addr += 2;
+ }
+ else
+ while (addr >= endaddr) {
+ write_word(addr, value);
+ addr -= 2;
+ }
+ ip += 2;
+ break;
+ default:
+ R_IP = ip--; /* Move IP back to the 'rep' instruction */
+ return -1;
+ }
+ break;
+ default:
+ /* unknown instruction, get out of here and let trap.c:sigbus()
+ catch it. */
+ return -1;
+ }
+ R_IP = ip;
+ }
+
+ return 0;
+}
+
+/* Decode the ModR/M byte. Returns the memory address of the operand. 'c'
+ points to the current instruction, 'seg' contains the value for the current
+ base segment; this is usually 'DS', but may have been changed by a segment
+ override prefix. We return the length of the current instruction in
+ 'instrlen' so we can adjust 'IP' on return.
+
+ XXX We will probably need a second function for 32-bit instructions.
+
+ XXX We do not check for undefined combinations, like Mod=01, R/M=001. */
+static u_int32_t
+decode_modrm(u_int8_t *c, u_int16_t seg, regcontext_t *REGS, int *instrlen)
+{
+ u_int32_t addr = 0; /* absolute address */
+ int16_t dspl = 0; /* displacement, signed */
+ *instrlen = 0;
+
+ switch (c[1] & 0xc0) { /* decode Mod */
+ case 0x00: /* DS:[reg] */
+ /* 'reg' is selected in the R/M bits */
+ break;
+ case 0x40: /* 8 bit displacement */
+ dspl = (int16_t)(int8_t)c[2];
+ *instrlen = 1;
+ break;
+ case 0x80: /* 16 bit displacement */
+ dspl = *(int16_t *)&c[2];
+ *instrlen = 2;
+ break;
+ case 0xc0: /* reg in R/M */
+ if (c[0] & 1) /* 16-bit reg */
+ return reg16(c[1], REGS);
+ else /* 8-bit reg */
+ return reg8(c[1], REGS);
+ break;
+ }
+
+ switch (c[1] & 0x07) { /* decode R/M */
+ case 0x00:
+ addr = MAKEPTR(seg, R_BX + R_SI);
+ break;
+ case 0x01:
+ addr = MAKEPTR(seg, R_BX + R_DI);
+ break;
+ case 0x02:
+ addr = MAKEPTR(seg, R_BP + R_SI);
+ break;
+ case 0x03:
+ addr = MAKEPTR(seg, R_BP + R_DI);
+ break;
+ case 0x04:
+ addr = MAKEPTR(seg, R_SI);
+ break;
+ case 0x05:
+ addr = MAKEPTR(seg, R_DI);
+ break;
+ case 0x06:
+ if ((c[1] & 0xc0) >= 0x40)
+ addr += R_BP;
+ else {
+ addr = MAKEPTR(seg, *(int16_t *)&c[2]);
+ *instrlen = 2;
+ }
+ break;
+ case 0x07:
+ addr = MAKEPTR(seg, R_BX + dspl);
+ break;
+ }
+
+ return addr;
+}
+
+static u_int8_t
+reg8(u_int8_t c, regcontext_t *REGS)
+{
+ u_int8_t r8[] = {R_AL, R_CL, R_DL, R_BL, R_AH, R_CH, R_DH, R_BH};
+
+ /* select 'rrr' bits in ModR/M */
+ return r8[(c & 0x34) >> 3];
+}
+
+static u_int16_t
+reg16(u_int8_t c, regcontext_t *REGS)
+{
+ u_int16_t r16[] = {R_AX, R_CX, R_DX, R_BX, R_SP, R_BP, R_SI, R_DI};
+
+ return r16[(c & 0x34) >> 3];
+}
+
+#if 0
+/* not yet */
+static u_int32_t
+reg32(u_int8_t c, regcontext_t *REGS)
+{
+ u_int32_t r32[] = {R_EAX, R_ECX, R_EDX, R_EBX,
+ R_ESP, R_EBP, R_ESI, R_EDI};
+
+ return r32[(c & 0x34) >> 3];
+}
+#endif
+
+/* Write an 8-bit value to the location specified by 'addr'. If 'addr' lies
+ within the video memory region, we call video.c:vga_write(). */
+static void
+write_byte(u_int32_t addr, u_int8_t val)
+{
+ if (addr >= 0xa0000 && addr < 0xb0000) {
+ vga_write(addr, val);
+ } else
+ *(u_int8_t *)addr = val;
+
+ return;
+}
+
+/* Write a 16-bit value to the location specified by 'addr'. If 'addr' lies
+ within the video memory region, we call video.c:vga_write(). */
+static void
+write_word(u_int32_t addr, u_int16_t val)
+{
+ if (addr >= 0xa0000 && addr < 0xb0000) {
+ vga_write(addr, (u_int8_t)(val & 0xff));
+ vga_write(addr + 1, (u_int8_t)((val & 0xff00) >> 8));
+ } else
+ *(u_int16_t *)addr = val;
+
+ return;
+}
OpenPOWER on IntegriCloud