kernel: Use hashdestroy() to free hash tables allocated with hashinit().
[dragonfly.git] / usr.bin / doscmd / cpu.c
1 /*
2  * Copyright (c) 2001 The FreeBSD Project, Inc.
3  * All rights reserved.
4  *
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.
13  *
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.
25  *
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 $
28  */
29
30 #include "doscmd.h"
31 #include "video.h"
32
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);
43
44 /*
45 ** Hardware /0 interrupt
46 */
47 void
48 int00(regcontext_t *REGS __unused)
49 {
50     debug(D_ALWAYS, "Divide by 0 in DOS program!\n");
51     exit(1);
52 }
53
54 void
55 int01(regcontext_t *REGS __unused)
56 {
57     debug(D_ALWAYS, "INT 1 with no handler! (single-step/debug)\n");
58 }
59
60 void
61 int03(regcontext_t *REGS __unused)
62 {
63     debug(D_ALWAYS, "INT 3 with no handler! (breakpoint)\n");
64 }
65
66 void
67 int0d(regcontext_t *REGS __unused)
68 {
69     debug(D_ALWAYS, "IRQ5 with no handler!\n");
70 }
71
72 void
73 cpu_init(void)
74 {
75     u_long vec;
76
77     vec = insert_hardint_trampoline();
78     ivec[0x00] = vec;
79     register_callback(vec, int00, "int 00");
80
81     vec = insert_softint_trampoline();
82     ivec[0x01] = vec;
83     register_callback(vec, int01, "int 01");
84
85     vec = insert_softint_trampoline();
86     ivec[0x03] = vec;
87     register_callback(vec, int03, "int 03");
88
89     vec = insert_hardint_trampoline();
90     ivec[0x0d] = vec;
91     register_callback(vec, int0d, "int 0d");
92
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 */
106 }
107
108 /*
109  * Emulate CPU instructions. We need this for VGA graphics, at least in the 16
110  * color modes.
111  *
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.
115  *
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.
119  */
120 int
121 emu_instr(regcontext_t *REGS)
122 {
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;
132     
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;
249                 }
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;
261                 }
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;
272                 }
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;
279             }
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;
286         }
287         R_IP = ip;
288     }
289
290     return 0;
291 }
292
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.
298
299    XXX We will probably need a second function for 32-bit instructions.
300
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)
304 {
305     u_int32_t   addr = 0;               /* absolute address */
306     int16_t     dspl = 0;               /* displacement, signed */
307     *instrlen = 0;
308     
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;
327     }
328     
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;
354         }
355         break;
356     case 0x07:
357         addr = MAKEPTR(seg, R_BX + dspl);
358         break;
359     }
360     
361     return addr;
362 }
363
364 static u_int8_t *
365 reg8(u_int8_t c, regcontext_t *REGS)
366 {
367     u_int8_t *r8[] = {&R_AL, &R_CL, &R_DL, &R_BL,
368                       &R_AH, &R_CH, &R_DH, &R_BH};
369
370     /* select 'rrr' bits in ModR/M */
371     return r8[(c & 0x38) >> 3];
372 }
373
374 static u_int16_t *
375 reg16(u_int8_t c, regcontext_t *REGS)
376 {
377     u_int16_t *r16[] = {&R_AX, &R_CX, &R_DX, &R_BX,
378                         &R_SP, &R_BP, &R_SI, &R_DI};
379
380     return r16[(c & 0x38) >> 3];
381 }
382
383 #if 0
384 /* not yet */
385 static u_int32_t *
386 reg32(u_int8_t c, regcontext_t *REGS)
387 {
388     u_int32_t *r32[] = {&R_EAX, &R_ECX, &R_EDX, &R_EBX,
389                         &R_ESP, &R_EBP, &R_ESI, &R_EDI};
390
391     return r32[(c & 0x38) >> 3];
392 }
393 #endif
394
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)
399 {
400     if (addr >= 0xa0000 && addr < 0xb0000)
401         return vga_read(addr);
402     else
403         return *(u_int8_t *)addr;
404 }
405
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)
410 {
411     if (addr >= 0xa0000 && addr < 0xb0000)
412         vga_write(addr, val);
413     else
414         *(u_int8_t *)addr = val;
415
416     return;
417 }
418
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)
423 {
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;
429
430     return;
431 }