diff options
author | tg <tg@FreeBSD.org> | 2001-07-24 11:44:20 +0000 |
---|---|---|
committer | tg <tg@FreeBSD.org> | 2001-07-24 11:44:20 +0000 |
commit | 97507091ff20d3dc72113dfbd0c9532423d3acb4 (patch) | |
tree | f37909a1b76ac49ce96e941e94a62649b7210a4e /usr.bin/doscmd/cpu.c | |
parent | dfb5f5d5892585bd43bea7a627c21456e0ff9687 (diff) | |
download | FreeBSD-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.c | 279 |
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; +} |