2 * Copyright (c) 2001 The FreeBSD Project, Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY The FreeBSD Project, Inc. AND CONTRIBUTORS
15 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
16 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL The FreeBSD Project, Inc. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 * $FreeBSD: src/usr.bin/doscmd/cpu.c,v 1.2.2.2 2002/05/21 11:49:47 tg Exp $
27 * $DragonFly: src/usr.bin/doscmd/cpu.c,v 1.2 2003/06/17 04:29:25 dillon Exp $
33 static u_int32_t
decode_modrm(u_int8_t
*, u_int16_t
,
34 regcontext_t
*, int *);
35 static u_int8_t
*reg8(u_int8_t c
, regcontext_t
*);
36 static u_int16_t
*reg16(u_int8_t c
, regcontext_t
*);
38 static u_int32_t
*reg32(u_int8_t c
, regcontext_t
*);
40 static u_int8_t
read_byte(u_int32_t
);
41 static void write_byte(u_int32_t
, u_int8_t
);
42 static void write_word(u_int32_t
, u_int16_t
);
45 ** Hardware /0 interrupt
48 int00(regcontext_t
*REGS __unused
)
50 debug(D_ALWAYS
, "Divide by 0 in DOS program!\n");
55 int01(regcontext_t
*REGS __unused
)
57 debug(D_ALWAYS
, "INT 1 with no handler! (single-step/debug)\n");
61 int03(regcontext_t
*REGS __unused
)
63 debug(D_ALWAYS
, "INT 3 with no handler! (breakpoint)\n");
67 int0d(regcontext_t
*REGS __unused
)
69 debug(D_ALWAYS
, "IRQ5 with no handler!\n");
77 vec
= insert_hardint_trampoline();
79 register_callback(vec
, int00
, "int 00");
81 vec
= insert_softint_trampoline();
83 register_callback(vec
, int01
, "int 01");
85 vec
= insert_softint_trampoline();
87 register_callback(vec
, int03
, "int 03");
89 vec
= insert_hardint_trampoline();
91 register_callback(vec
, int0d
, "int 0d");
93 vec
= insert_null_trampoline();
94 ivec
[0x34] = vec
; /* floating point emulator */
95 ivec
[0x35] = vec
; /* floating point emulator */
96 ivec
[0x36] = vec
; /* floating point emulator */
97 ivec
[0x37] = vec
; /* floating point emulator */
98 ivec
[0x38] = vec
; /* floating point emulator */
99 ivec
[0x39] = vec
; /* floating point emulator */
100 ivec
[0x3a] = vec
; /* floating point emulator */
101 ivec
[0x3b] = vec
; /* floating point emulator */
102 ivec
[0x3c] = vec
; /* floating point emulator */
103 ivec
[0x3d] = vec
; /* floating point emulator */
104 ivec
[0x3e] = vec
; /* floating point emulator */
105 ivec
[0x3f] = vec
; /* floating point emulator */
109 * Emulate CPU instructions. We need this for VGA graphics, at least in the 16
112 * The emulator is far from complete. We are adding the instructions as we
113 * encounter them, so this function is likely to change over time. There are
114 * no optimizations and we only emulate a single instruction at a time.
116 * As long as there is no support for DPMI or the Operand Size Override prefix
117 * we won't need the 32-bit registers. This also means that the default
118 * operand size is 16 bit.
121 emu_instr(regcontext_t
*REGS
)
124 u_int8_t
*cs
= (u_int8_t
*)(R_CS
<< 4);
126 int dir
, i
, instrlen
;
130 u_int16_t
*seg
= &R_DS
;
131 u_int32_t addr
, toaddr
;
136 case 0x08: /* or r/m8, r8 */
137 addr
= decode_modrm(cs
+ ip
, *seg
, REGS
, &instrlen
);
138 r8
= reg8(cs
[ip
+ 1], REGS
);
139 val8
= read_byte(addr
) | *r8
;
140 write_byte(addr
, val8
);
141 /* clear carry and overflow; check zero, sign, parity */
142 R_EFLAGS
&= ~PSL_C
| ~PSL_V
;
151 case 0x22: /* and r8, r/m8 */
152 addr
= decode_modrm(cs
+ ip
, *seg
, REGS
, &instrlen
);
153 r8
= reg8(cs
[ip
+ 1], REGS
);
154 *r8
&= read_byte(addr
);
155 /* clear carry and overflow; check zero, sign, parity */
156 R_EFLAGS
&= ~PSL_C
| ~PSL_V
;
165 case 0x26: /* Segment Override ES */
170 case 0x2e: /* Segment Override CS */
175 case 0x36: /* Segment Override SS */
180 case 0x3e: /* Segment Override DS */
185 case 0x64: /* Segment Override FS */
190 case 0x65: /* Segment Override GS */
195 case 0x88: /* mov r/m8, r8 */
196 addr
= decode_modrm(cs
+ ip
, *seg
, REGS
, &instrlen
);
197 write_byte(addr
, *reg8(cs
[ip
+ 1], REGS
));
200 case 0x8a: /* mov r8, r/m8 */
201 addr
= decode_modrm(cs
+ ip
, *seg
, REGS
, &instrlen
);
202 r8
= reg8(cs
[ip
+ 1], REGS
);
203 *r8
= read_byte(addr
);
206 case 0xc6: /* mov r/m8, imm8 */
207 addr
= decode_modrm(cs
+ ip
, *seg
, REGS
, &instrlen
);
208 write_byte(addr
, cs
[ip
+ 2 + instrlen
]);
209 ip
+= 2 + instrlen
+ 1;
211 case 0xc7: /* mov r/m32/16, imm32/16 */
212 addr
= decode_modrm(cs
+ ip
, *seg
, REGS
, &instrlen
);
213 val16
= *(u_int16_t
*)&cs
[ip
+ 2 + instrlen
];
214 write_word(addr
, val16
);
215 ip
+= 2 + instrlen
+ 2;
217 case 0xa4: /* movs m8, m8 */
218 write_byte(MAKEPTR(R_ES
, R_DI
), read_byte(MAKEPTR(*seg
, R_SI
)));
219 dir
= (R_EFLAGS
& PSL_D
) ? -1 : 1;
224 case 0xaa: /* stos m8 */
225 addr
= MAKEPTR(R_ES
, R_DI
);
226 write_byte(addr
, R_AL
);
227 R_DI
+= (R_EFLAGS
& PSL_D
) ? -1 : 1;
230 case 0xab: /* stos m32/16*/
231 addr
= MAKEPTR(R_ES
, R_DI
);
232 write_word(addr
, R_AX
);
233 R_DI
+= (R_EFLAGS
& PSL_D
) ? -2 : 2;
238 case 0xa4: /* movs m8, m8 */
239 /* XXX Possible optimization: if both source and target
240 addresses lie within the video memory and write mode 1 is
241 selected, we can use memcpy(). */
242 dir
= (R_EFLAGS
& PSL_D
) ? -1 : 1;
243 addr
= MAKEPTR(R_ES
, R_DI
);
244 toaddr
= MAKEPTR(*seg
, R_SI
);
245 for (i
= R_CX
; i
> 0; i
--) {
246 write_byte(addr
, read_byte(toaddr
));
250 PUTPTR(R_ES
, R_DI
, addr
);
251 PUTPTR(*seg
, R_SI
, toaddr
);
254 case 0xaa: /* stos m8 */
256 dir
= (R_EFLAGS
& PSL_D
) ? -1 : 1;
257 addr
= MAKEPTR(R_ES
, R_DI
);
258 for (i
= R_CX
; i
> 0; i
--) {
259 write_byte(addr
, R_AL
);
262 PUTPTR(R_ES
, R_DI
, addr
);
265 case 0xab: /* stos m32/16 */
267 dir
= (R_EFLAGS
& PSL_D
) ? -2 : 2;
268 addr
= MAKEPTR(R_ES
, R_DI
);
269 for (i
= R_CX
; i
> 0; i
--) {
270 write_word(addr
, R_AX
);
273 PUTPTR(R_ES
, R_DI
, addr
);
277 R_IP
= --ip
; /* Move IP back to the 'rep' instruction. */
283 /* Unknown instruction, get out of here and let trap.c:sigbus()
293 /* Decode the ModR/M byte. Returns the memory address of the operand. 'c'
294 points to the current instruction, 'seg' contains the value for the current
295 base segment; this is usually 'DS', but may have been changed by a segment
296 override prefix. We return the length of the current instruction in
297 'instrlen' so we can adjust 'IP' on return.
299 XXX We will probably need a second function for 32-bit instructions.
301 XXX We do not check for undefined combinations, like Mod=01, R/M=001. */
303 decode_modrm(u_int8_t
*c
, u_int16_t seg
, regcontext_t
*REGS
, int *instrlen
)
305 u_int32_t addr
= 0; /* absolute address */
306 int16_t dspl
= 0; /* displacement, signed */
309 switch (c
[1] & 0xc0) { /* decode Mod */
310 case 0x00: /* DS:[reg] */
311 /* 'reg' is selected in the R/M bits */
313 case 0x40: /* 8 bit displacement */
314 dspl
= (int16_t)(int8_t)c
[2];
317 case 0x80: /* 16 bit displacement */
318 dspl
= *(int16_t *)&c
[2];
321 case 0xc0: /* reg in R/M */
322 if (c
[0] & 1) /* 16-bit reg */
323 return *reg16(c
[1], REGS
);
325 return *reg8(c
[1], REGS
);
329 switch (c
[1] & 0x07) { /* decode R/M */
331 addr
= MAKEPTR(seg
, R_BX
+ R_SI
);
334 addr
= MAKEPTR(seg
, R_BX
+ R_DI
);
337 addr
= MAKEPTR(seg
, R_BP
+ R_SI
);
340 addr
= MAKEPTR(seg
, R_BP
+ R_DI
);
343 addr
= MAKEPTR(seg
, R_SI
);
346 addr
= MAKEPTR(seg
, R_DI
);
349 if ((c
[1] & 0xc0) >= 0x40)
352 addr
= MAKEPTR(seg
, *(int16_t *)&c
[2]);
357 addr
= MAKEPTR(seg
, R_BX
+ dspl
);
365 reg8(u_int8_t c
, regcontext_t
*REGS
)
367 u_int8_t
*r8
[] = {&R_AL
, &R_CL
, &R_DL
, &R_BL
,
368 &R_AH
, &R_CH
, &R_DH
, &R_BH
};
370 /* select 'rrr' bits in ModR/M */
371 return r8
[(c
& 0x38) >> 3];
375 reg16(u_int8_t c
, regcontext_t
*REGS
)
377 u_int16_t
*r16
[] = {&R_AX
, &R_CX
, &R_DX
, &R_BX
,
378 &R_SP
, &R_BP
, &R_SI
, &R_DI
};
380 return r16
[(c
& 0x38) >> 3];
386 reg32(u_int8_t c
, regcontext_t
*REGS
)
388 u_int32_t
*r32
[] = {&R_EAX
, &R_ECX
, &R_EDX
, &R_EBX
,
389 &R_ESP
, &R_EBP
, &R_ESI
, &R_EDI
};
391 return r32
[(c
& 0x38) >> 3];
395 /* Read an 8-bit value from the location specified by 'addr'. If 'addr' lies
396 within the video memory, we call 'video.c:vga_read()'. */
398 read_byte(u_int32_t addr
)
400 if (addr
>= 0xa0000 && addr
< 0xb0000)
401 return vga_read(addr
);
403 return *(u_int8_t
*)addr
;
406 /* Write an 8-bit value to the location specified by 'addr'. If 'addr' lies
407 within the video memory region, we call 'video.c:vga_write()'. */
409 write_byte(u_int32_t addr
, u_int8_t val
)
411 if (addr
>= 0xa0000 && addr
< 0xb0000)
412 vga_write(addr
, val
);
414 *(u_int8_t
*)addr
= val
;
419 /* Write a 16-bit value to the location specified by 'addr'. If 'addr' lies
420 within the video memory region, we call 'video.c:vga_write()'. */
422 write_word(u_int32_t addr
, u_int16_t val
)
424 if (addr
>= 0xa0000 && addr
< 0xb0000) {
425 vga_write(addr
, (u_int8_t
)(val
& 0xff));
426 vga_write(addr
+ 1, (u_int8_t
)((val
& 0xff00) >> 8));
428 *(u_int16_t
*)addr
= val
;