- Merge following link state detection fixes from FreeBSD:
[dragonfly/port-amd64.git] / usr.bin / doscmd / cpu.c
blobf0bfa675af1fc14d4ec9760a147ca73464d58cc6
1 /*
2 * Copyright (c) 2001 The FreeBSD Project, Inc.
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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 $
30 #include "doscmd.h"
31 #include "video.h"
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 *);
37 #if 0
38 static u_int32_t *reg32(u_int8_t c, regcontext_t *);
39 #endif
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
47 void
48 int00(regcontext_t *REGS __unused)
50 debug(D_ALWAYS, "Divide by 0 in DOS program!\n");
51 exit(1);
54 void
55 int01(regcontext_t *REGS __unused)
57 debug(D_ALWAYS, "INT 1 with no handler! (single-step/debug)\n");
60 void
61 int03(regcontext_t *REGS __unused)
63 debug(D_ALWAYS, "INT 3 with no handler! (breakpoint)\n");
66 void
67 int0d(regcontext_t *REGS __unused)
69 debug(D_ALWAYS, "IRQ5 with no handler!\n");
72 void
73 cpu_init(void)
75 u_long vec;
77 vec = insert_hardint_trampoline();
78 ivec[0x00] = vec;
79 register_callback(vec, int00, "int 00");
81 vec = insert_softint_trampoline();
82 ivec[0x01] = vec;
83 register_callback(vec, int01, "int 01");
85 vec = insert_softint_trampoline();
86 ivec[0x03] = vec;
87 register_callback(vec, int03, "int 03");
89 vec = insert_hardint_trampoline();
90 ivec[0x0d] = vec;
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
110 * color modes.
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)
123 int prefix = 1;
124 u_int8_t *cs = (u_int8_t *)(R_CS << 4);
125 int ip = R_IP;
126 int dir, i, instrlen;
127 u_int8_t *r8;
128 u_int8_t val8;
129 u_int16_t val16;
130 u_int16_t *seg = &R_DS;
131 u_int32_t addr, toaddr;
133 while (prefix) {
134 prefix = 0;
135 switch (cs[ip]) {
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;
143 if (val8 == 0)
144 R_EFLAGS |= PSL_Z;
145 if (val8 % 2 != 0)
146 R_EFLAGS |= PSL_PF;
147 if (val8 & 0x80)
148 R_EFLAGS |= PSL_N;
149 ip += 2 + instrlen;
150 break;
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;
157 if (*r8 == 0)
158 R_EFLAGS |= PSL_Z;
159 if (*r8 % 2 != 0)
160 R_EFLAGS |= PSL_PF;
161 if (*r8 & 0x80)
162 R_EFLAGS |= PSL_N;
163 ip += 2 + instrlen;
164 break;
165 case 0x26: /* Segment Override ES */
166 seg = &R_ES;
167 prefix = 1;
168 ip++;
169 break;
170 case 0x2e: /* Segment Override CS */
171 seg = &R_CS;
172 prefix = 1;
173 ip++;
174 break;
175 case 0x36: /* Segment Override SS */
176 seg = &R_SS;
177 prefix = 1;
178 ip++;
179 break;
180 case 0x3e: /* Segment Override DS */
181 seg = &R_DS;
182 prefix = 1;
183 ip++;
184 break;
185 case 0x64: /* Segment Override FS */
186 seg = &R_FS;
187 prefix = 1;
188 ip++;
189 break;
190 case 0x65: /* Segment Override GS */
191 seg = &R_GS;
192 prefix = 1;
193 ip++;
194 break;
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));
198 ip += 2 + instrlen;
199 break;
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);
204 ip += 2 + instrlen;
205 break;
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;
210 break;
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;
216 break;
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;
220 R_DI += dir;
221 R_SI += dir;
222 ip++;
223 break;
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;
228 ip++;
229 break;
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;
234 ip++;
235 break;
236 case 0xf3: /* rep */
237 switch (cs[++ip]) {
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));
247 addr += dir;
248 toaddr += dir;
250 PUTPTR(R_ES, R_DI, addr);
251 PUTPTR(*seg, R_SI, toaddr);
252 ip++;
253 break;
254 case 0xaa: /* stos m8 */
255 /* direction */
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);
260 addr += dir;
262 PUTPTR(R_ES, R_DI, addr);
263 ip++;
264 break;
265 case 0xab: /* stos m32/16 */
266 /* direction */
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);
271 addr += dir;
273 PUTPTR(R_ES, R_DI, addr);
274 ip++;
275 break;
276 default:
277 R_IP = --ip; /* Move IP back to the 'rep' instruction. */
278 return -1;
280 R_CX = 0;
281 break;
282 default:
283 /* Unknown instruction, get out of here and let trap.c:sigbus()
284 catch it. */
285 return -1;
287 R_IP = ip;
290 return 0;
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. */
302 static u_int32_t
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 */
307 *instrlen = 0;
309 switch (c[1] & 0xc0) { /* decode Mod */
310 case 0x00: /* DS:[reg] */
311 /* 'reg' is selected in the R/M bits */
312 break;
313 case 0x40: /* 8 bit displacement */
314 dspl = (int16_t)(int8_t)c[2];
315 *instrlen = 1;
316 break;
317 case 0x80: /* 16 bit displacement */
318 dspl = *(int16_t *)&c[2];
319 *instrlen = 2;
320 break;
321 case 0xc0: /* reg in R/M */
322 if (c[0] & 1) /* 16-bit reg */
323 return *reg16(c[1], REGS);
324 else /* 8-bit reg */
325 return *reg8(c[1], REGS);
326 break;
329 switch (c[1] & 0x07) { /* decode R/M */
330 case 0x00:
331 addr = MAKEPTR(seg, R_BX + R_SI);
332 break;
333 case 0x01:
334 addr = MAKEPTR(seg, R_BX + R_DI);
335 break;
336 case 0x02:
337 addr = MAKEPTR(seg, R_BP + R_SI);
338 break;
339 case 0x03:
340 addr = MAKEPTR(seg, R_BP + R_DI);
341 break;
342 case 0x04:
343 addr = MAKEPTR(seg, R_SI);
344 break;
345 case 0x05:
346 addr = MAKEPTR(seg, R_DI);
347 break;
348 case 0x06:
349 if ((c[1] & 0xc0) >= 0x40)
350 addr += R_BP;
351 else {
352 addr = MAKEPTR(seg, *(int16_t *)&c[2]);
353 *instrlen = 2;
355 break;
356 case 0x07:
357 addr = MAKEPTR(seg, R_BX + dspl);
358 break;
361 return addr;
364 static u_int8_t *
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];
374 static u_int16_t *
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];
383 #if 0
384 /* not yet */
385 static u_int32_t *
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];
393 #endif
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()'. */
397 static u_int8_t
398 read_byte(u_int32_t addr)
400 if (addr >= 0xa0000 && addr < 0xb0000)
401 return vga_read(addr);
402 else
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()'. */
408 static void
409 write_byte(u_int32_t addr, u_int8_t val)
411 if (addr >= 0xa0000 && addr < 0xb0000)
412 vga_write(addr, val);
413 else
414 *(u_int8_t *)addr = val;
416 return;
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()'. */
421 static void
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));
427 } else
428 *(u_int16_t *)addr = val;
430 return;