├── 8086.c ├── 8086.h ├── Makefile ├── README.md ├── dis86.c ├── disasm.c ├── disasm.h ├── discolor.c ├── discolor.h ├── dissim.c ├── exe.h ├── loader-bin.c ├── loader-dos.c ├── loader-elks.c ├── opcodes.S ├── opcodes.txt ├── sim86.c ├── syms.c ├── syms.h ├── syscall-dos.c └── syscall-elks.c /8086.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 8086 emulator 3 | * 4 | * Emulator orginally from Andrew Jenner's reenigne project 5 | * DOS enhancements by TK Chia 6 | * ELKS executable support by Greg Haerr 7 | * Heavily rewritten and disassembler added by Greg Haerr 8 | */ 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "8086.h" 15 | #include "disasm.h" 16 | #include "exe.h" /* required for handleInterrupt/checkStack */ 17 | 18 | #if BLINK16 19 | #include "blink/machine.h" 20 | #else 21 | typedef int bool; 22 | #endif 23 | 24 | /* emulator globals */ 25 | Word registers[12]; 26 | Byte* byteRegisters[8]; 27 | Byte ram[RAMSIZE]; 28 | int f_verbose; 29 | 30 | static Byte shadowRam[RAMSIZE]; 31 | static bool useMemory; 32 | static Word address; 33 | static Word ip; 34 | static Byte opcode; 35 | static Word flags; 36 | static Byte modRM; 37 | static int segment; 38 | static int segmentOverride; 39 | static bool wordSize; 40 | static bool sourceIsRM; 41 | static DWord data; 42 | static DWord destination; 43 | static DWord source; 44 | static Word residue; 45 | static int aluOperation; 46 | static Word savedIP; 47 | static Word savedCS; 48 | static bool running; 49 | static bool prefix; 50 | static bool repeating; 51 | static int rep; 52 | static int ios; 53 | static struct exe *ep; 54 | 55 | static inline Word rw(void) { return registers[opcode & 7]; } 56 | static inline void setRW(Word value) { registers[opcode & 7] = value; } 57 | static inline void setRB(Byte value) { *byteRegisters[opcode & 7] = value; } 58 | 59 | int initMachine(struct exe *e) 60 | { 61 | memset(ram, 0, sizeof(ram)); 62 | memset(shadowRam, 0, sizeof(shadowRam)); 63 | ep = e; /* saved passed struct exe * for handleInterrupt() */ 64 | 65 | segment = 0; 66 | segmentOverride = -1; 67 | prefix = false; 68 | repeating = false; 69 | running = false; 70 | 71 | setCX(0x00FF); /* must be 0x00FF as for big endian test below */ 72 | Byte* byteData = (Byte*)®isters[0]; 73 | int bigEndian = (byteData[2] == 0 ? 1 : 0); 74 | int byteNumbers[8] = {0, 2, 4, 6, 1, 3, 5, 7}; 75 | for (int i = 0 ; i < 8; ++i) 76 | byteRegisters[i] = &byteData[byteNumbers[i] ^ bigEndian]; 77 | 78 | return 0; 79 | } 80 | 81 | void initExecute(void) 82 | { 83 | running = true; 84 | } 85 | 86 | static void divideOverflow(void) 87 | { 88 | handleInterrupt(ep, INT0_DIV_ERROR); 89 | data = source = 1; 90 | } 91 | 92 | void setShadowFlags(Word offset, int seg, int len, int flags) 93 | { 94 | DWord a = ((DWord)registers[8 + seg] << 4) + offset; 95 | int i; 96 | 97 | if (f_verbose) 98 | printf("setShadow %04x:%04x len %05x to %x\n", 99 | registers[8+seg], offset, len, flags); 100 | for (i=0; i= RAMSIZE) 122 | runtimeError("Accessing address outside RAM %s %04x:%04x\n", 123 | segname[seg], segmentAddress, offset); 124 | flags = shadowRam[a]; 125 | if (write && running && !(flags & fWrite)) 126 | runtimeError("Writing disallowed address %s %04x:%04x\n", 127 | segname[seg], segmentAddress, offset); 128 | if (!write && !(flags & fRead)) 129 | runtimeError("Reading uninitialized address %s %04x:%04x\n", 130 | segname[seg], segmentAddress, offset); 131 | if (running) 132 | shadowRam[a] |= fRead; 133 | return a; 134 | } 135 | 136 | Byte readByte(Word offset, int seg) 137 | { 138 | DWord a = physicalAddress(offset, seg, false); 139 | #if BLINK16 140 | if (seg != CS) SetReadAddr(g_machine, a, 1); 141 | #endif 142 | return ram[a]; 143 | } 144 | 145 | Word readWordSeg(Word offset, int seg) 146 | { 147 | DWord a = physicalAddress(offset, seg, false); 148 | Word r = ram[a]; 149 | #if BLINK16 150 | if (seg != CS) SetReadAddr(g_machine, a, 2); 151 | #endif 152 | return r | (ram[physicalAddress(offset + 1, seg, false)] << 8); 153 | } 154 | 155 | void writeByte(Byte value, Word offset, int seg) 156 | { 157 | DWord a = physicalAddress(offset, seg, true); 158 | ram[a] = value; 159 | #if BLINK16 160 | if (seg != CS) SetWriteAddr(g_machine, a, 1); 161 | #endif 162 | } 163 | 164 | void writeWord(Word value, Word offset, int seg) 165 | { 166 | DWord a = physicalAddress(offset, seg, true); 167 | ram[a] = value; 168 | ram[physicalAddress(offset + 1, seg, true)] = value >> 8; 169 | #if BLINK16 170 | if (seg != CS) SetWriteAddr(g_machine, a, 2); 171 | #endif 172 | } 173 | 174 | static Word readwb(Word offset, int seg) 175 | { 176 | return wordSize ? readWordSeg(offset, seg) : readByte(offset, seg); 177 | } 178 | 179 | static void writewb(Word value, Word offset, int seg) 180 | { 181 | if (wordSize) 182 | writeWord(value, offset, seg); 183 | else 184 | writeByte((Byte)value, offset, seg); 185 | } 186 | static Byte fetchByte() { Byte b = readByte(ip, CS); ++ip; return b; } 187 | static Word fetchWord() { Word w = fetchByte(); w += fetchByte() << 8; return w; } 188 | static Word fetch(bool wordSize) 189 | { 190 | if (wordSize) 191 | return fetchWord(); 192 | return fetchByte(); 193 | } 194 | static Word signExtend(Byte data) { return data + (data < 0x80 ? 0 : 0xff00); } 195 | static int modRMReg() { return (modRM >> 3) & 7; } 196 | static void doJump(Word newIP) 197 | { 198 | /*printf("\n");*/ 199 | ip = newIP; 200 | } 201 | static void jumpShort(Byte data, bool jump) 202 | { 203 | if (jump) 204 | doJump(ip + signExtend(data)); 205 | } 206 | int isRepeating(void) { return repeating; } 207 | Word getIP(void) { return ip; } 208 | Word getFlags(void) { return flags; } 209 | void setIP(Word w) { ip = w; } 210 | void setFlags(Word w) { flags = w; } 211 | void setCF(int cf) { flags = (flags & ~1) | (cf ? 1 : 0); } 212 | static void setAF(bool af) { flags = (flags & ~0x10) | (af ? 0x10 : 0); } 213 | static void clearCA() { setCF(false); setAF(false); } 214 | static void setOF(bool of) { flags = (flags & ~0x800) | (of ? 0x800 : 0); } 215 | static void clearCAO() { clearCA(); setOF(false); } 216 | static void setPF() 217 | { 218 | static Byte table[0x100] = { 219 | 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, 220 | 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, 221 | 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, 222 | 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, 223 | 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, 224 | 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, 225 | 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, 226 | 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, 227 | 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, 228 | 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, 229 | 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, 230 | 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, 231 | 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, 232 | 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, 233 | 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, 234 | 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4}; 235 | flags = (flags & ~4) | table[data & 0xff]; 236 | } 237 | static void setZF() 238 | { 239 | flags = (flags & ~0x40) | 240 | ((data & (!wordSize ? 0xff : 0xffff)) == 0 ? 0x40 : 0); 241 | } 242 | static void setSF() 243 | { 244 | flags = (flags & ~0x80) | 245 | ((data & (!wordSize ? 0x80 : 0x8000)) != 0 ? 0x80 : 0); 246 | } 247 | static void setPZS() { setPF(); setZF(); setSF(); } 248 | static void bitwise(Word value) { data = value; clearCAO(); setPZS(); } 249 | static void test(Word d, Word s) 250 | { 251 | destination = d; 252 | source = s; 253 | bitwise(destination & source); 254 | } 255 | static bool cf() { return (flags & 1) != 0; } 256 | static bool pf() { return (flags & 4) != 0; } 257 | static bool af() { return (flags & 0x10) != 0; } 258 | static bool zf() { return (flags & 0x40) != 0; } 259 | static bool sf() { return (flags & 0x80) != 0; } 260 | static void setIF(bool intf) { flags = (flags & ~0x200) | (intf ? 0x200 : 0); } 261 | static void setDF(bool df) { flags = (flags & ~0x400) | (df ? 0x400 : 0); } 262 | static bool df() { return (flags & 0x400) != 0; } 263 | static bool of() { return (flags & 0x800) != 0; } 264 | static int stringIncrement() 265 | { 266 | int r = (wordSize ? 2 : 1); 267 | return !df() ? r : -r; 268 | } 269 | static Word lodS() 270 | { 271 | address = si(); 272 | setSI(si() + stringIncrement()); 273 | segment = DS; 274 | return readwb(address, -1); 275 | } 276 | static void doRep(bool compare) 277 | { 278 | if (rep == 1 && !compare) 279 | runtimeError("REPNE prefix with non-compare string instruction"); 280 | if (rep == 0 || cx() == 0) 281 | return; 282 | setCX(cx() - 1); 283 | repeating = cx() != 0 && (!compare || zf() != (rep == 1)); 284 | } 285 | static Word lodDIS() 286 | { 287 | address = di(); 288 | setDI(di() + stringIncrement()); 289 | return readwb(address, ES); 290 | } 291 | static void stoS(Word data) 292 | { 293 | address = di(); 294 | setDI(di() + stringIncrement()); 295 | writewb(data, address, ES); 296 | } 297 | #define o(c) 298 | /***void o(char c) 299 | { 300 | while (oCycle < ios) { 301 | ++oCycle; 302 | printf(" "); 303 | } 304 | ++oCycle; 305 | printf("%c", c); 306 | }***/ 307 | static void push(Word value) 308 | { 309 | o('{'); 310 | setSP(sp() - 2); 311 | if (ep->checkStack(ep)) 312 | runtimeError("Stack overflow SS:SP = %04x:%04x\n", ss(), sp()); 313 | writeWord(value, sp(), SS); 314 | } 315 | static Word pop() { 316 | Word r = readWordSeg(sp(), SS); 317 | setSP(sp() + 2); 318 | o('}'); 319 | return r; 320 | } 321 | void setCA() { setCF(true); setAF(true); } 322 | static void doAF() { setAF(((data ^ source ^ destination) & 0x10) != 0); } 323 | static void doCF() { setCF((data & (!wordSize ? 0x100 : 0x10000)) != 0); } 324 | static void setCAPZS() { setPZS(); doAF(); doCF(); } 325 | static void setOFAdd() 326 | { 327 | Word t = (data ^ source) & (data ^ destination); 328 | setOF((t & (!wordSize ? 0x80 : 0x8000)) != 0); 329 | } 330 | static void add() { data = destination + source; setCAPZS(); setOFAdd(); } 331 | static void setOFSub() 332 | { 333 | Word t = (destination ^ source) & (data ^ destination); 334 | setOF((t & (!wordSize ? 0x80 : 0x8000)) != 0); 335 | } 336 | static void sub() { data = destination - source; setCAPZS(); setOFSub(); } 337 | static void setOFRotate() 338 | { 339 | setOF(((data ^ destination) & (!wordSize ? 0x80 : 0x8000)) != 0); 340 | } 341 | static void doALUOperation() 342 | { 343 | switch (aluOperation) { 344 | case 0: add(); o('+'); break; 345 | case 1: bitwise(destination | source); o('|'); break; 346 | case 2: source += cf() ? 1 : 0; add(); o('a'); break; 347 | case 3: source += cf() ? 1 : 0; sub(); o('B'); break; 348 | case 4: test(destination, source); o('&'); break; 349 | case 5: sub(); o('-'); break; 350 | case 7: sub(); o('?'); break; 351 | case 6: bitwise(destination ^ source); o('^'); break; 352 | } 353 | } 354 | static void divide() 355 | { 356 | bool negative = false; 357 | bool dividendNegative = false; 358 | if (modRMReg() == 7) { 359 | if ((destination & 0x80000000) != 0) { 360 | destination = (unsigned)-(signed)destination; 361 | negative = !negative; 362 | dividendNegative = true; 363 | } 364 | if ((source & 0x8000) != 0) { 365 | source = (unsigned)-(signed)source & 0xffff; 366 | negative = !negative; 367 | } 368 | } 369 | data = destination / source; 370 | DWord product = data * source; 371 | // ISO C++ 2003 does not specify a rounding mode, but the x86 always 372 | // rounds towards zero. 373 | if (product > destination) { 374 | --data; 375 | product -= source; 376 | } 377 | residue = destination - product; 378 | if (negative) 379 | data = (unsigned)-(signed)data; 380 | if (dividendNegative) 381 | residue = (unsigned)-(signed)residue; 382 | } 383 | static Word* modRMRW() { return ®isters[modRMReg()]; } 384 | static Byte* modRMRB() { return byteRegisters[modRMReg()]; } 385 | static Word getReg() 386 | { 387 | if (!wordSize) 388 | return *modRMRB(); 389 | return *modRMRW(); 390 | } 391 | static Word getAccum() { return !wordSize ? al() : ax(); } 392 | static void setAccum() { if (!wordSize) setAL(data); else setAX(data); } 393 | static void setReg(Word value) 394 | { 395 | if (!wordSize) 396 | *modRMRB() = (Byte)value; 397 | else 398 | *modRMRW() = value; 399 | } 400 | static Word ea() 401 | { 402 | modRM = fetchByte(); 403 | useMemory = true; 404 | switch (modRM & 7) { 405 | case 0: segment = DS; address = bx() + si(); break; 406 | case 1: segment = DS; address = bx() + di(); break; 407 | case 2: segment = SS; address = bp() + si(); break; 408 | case 3: segment = SS; address = bp() + di(); break; 409 | case 4: segment = DS; address = si(); break; 410 | case 5: segment = DS; address = di(); break; 411 | case 6: segment = SS; address = bp(); break; 412 | case 7: segment = DS; address = bx(); break; 413 | } 414 | switch (modRM & 0xc0) { 415 | case 0x00: 416 | if ((modRM & 0xc7) == 6) { 417 | segment = 3; 418 | address = fetchWord(); 419 | } 420 | break; 421 | case 0x40: address += signExtend(fetchByte()); break; 422 | case 0x80: address += fetchWord(); break; 423 | case 0xc0: 424 | useMemory = false; 425 | address = modRM & 7; 426 | } 427 | return address; 428 | } 429 | static Word readEA2() 430 | { 431 | if (!useMemory) { 432 | if (wordSize) 433 | return registers[address]; 434 | return *byteRegisters[address]; 435 | } 436 | return readwb(address, -1); 437 | } 438 | static Word readEA() { address = ea(); return readEA2(); } 439 | static void finishWriteEA(Word data) 440 | { 441 | if (!useMemory) { 442 | if (wordSize) 443 | registers[address] = data; 444 | else 445 | *byteRegisters[address] = (Byte)data; 446 | } 447 | else 448 | writewb(data, address, -1); 449 | } 450 | static void writeEA(Word data) { ea(); finishWriteEA(data); } 451 | static void farLoad() 452 | { 453 | if (!useMemory) 454 | runtimeError("This instruction needs a memory address"); 455 | savedIP = readWordSeg(address, -1); 456 | savedCS = readWordSeg(address + 2, -1); 457 | } 458 | static void farJump() { setCS(savedCS); doJump(savedIP); } 459 | static void farCall() { push(cs()); push(ip); farJump(); } 460 | static void call(Word address) { push(ip); doJump(address); } 461 | static Word incdec(bool decrement) 462 | { 463 | source = 1; 464 | if (!decrement) { 465 | data = destination + source; 466 | setOFAdd(); 467 | } 468 | else { 469 | data = destination - source; 470 | setOFSub(); 471 | } 472 | doAF(); 473 | setPZS(); 474 | return data; 475 | } 476 | 477 | /* execute a single repetition of instruction */ 478 | void executeInstruction(void) 479 | { 480 | if (!repeating) { 481 | if (!prefix) { 482 | segmentOverride = -1; 483 | rep = 0; 484 | } 485 | prefix = false; 486 | opcode = fetchByte(); 487 | } 488 | if (rep != 0 && (opcode < 0xa4 || opcode >= 0xb0 || opcode == 0xa8 || opcode == 0xa9)) 489 | runtimeError("REP prefix with non-string instruction"); 490 | wordSize = ((opcode & 1) != 0); 491 | sourceIsRM = ((opcode & 2) != 0); 492 | int operation = (opcode >> 3) & 7; 493 | bool jump; 494 | 495 | switch (opcode) { 496 | case 0x00: case 0x01: case 0x02: case 0x03: 497 | case 0x08: case 0x09: case 0x0a: case 0x0b: 498 | case 0x10: case 0x11: case 0x12: case 0x13: 499 | case 0x18: case 0x19: case 0x1a: case 0x1b: 500 | case 0x20: case 0x21: case 0x22: case 0x23: 501 | case 0x28: case 0x29: case 0x2a: case 0x2b: 502 | case 0x30: case 0x31: case 0x32: case 0x33: 503 | case 0x38: case 0x39: case 0x3a: case 0x3b: // alu rmv,rmv 504 | data = readEA(); 505 | if (!sourceIsRM) { 506 | destination = data; 507 | source = getReg(); 508 | } 509 | else { 510 | destination = getReg(); 511 | source = data; 512 | } 513 | aluOperation = operation; 514 | doALUOperation(); 515 | if (aluOperation != 7) { 516 | if (!sourceIsRM) 517 | finishWriteEA(data); 518 | else 519 | setReg(data); 520 | } 521 | break; 522 | case 0x04: case 0x05: case 0x0c: case 0x0d: 523 | case 0x14: case 0x15: case 0x1c: case 0x1d: 524 | case 0x24: case 0x25: case 0x2c: case 0x2d: 525 | case 0x34: case 0x35: case 0x3c: case 0x3d: // alu accum,i 526 | destination = getAccum(); 527 | source = !wordSize ? fetchByte() : fetchWord(); 528 | aluOperation = operation; 529 | doALUOperation(); 530 | if (aluOperation != 7) 531 | setAccum(); 532 | break; 533 | case 0x06: case 0x0e: case 0x16: case 0x1e: // PUSH segreg 534 | push(registers[operation + 8]); 535 | break; 536 | case 0x07: case 0x17: case 0x1f: // POP segreg 537 | registers[operation + 8] = pop(); 538 | break; 539 | case 0x26: case 0x2e: case 0x36: case 0x3e: // segment override 540 | segmentOverride = operation - 4; 541 | o("e%ZE"[segmentOverride]); 542 | prefix = true; 543 | break; 544 | case 0x27: // DAA 545 | case 0x2f: // DAS 546 | if (af() || (al() & 0x0f) > 9) { 547 | data = al() + (opcode == 0x27 ? 6 : -6); 548 | setAL(data); 549 | setAF(true); 550 | if ((data & 0x100) != 0) 551 | setCF(true); 552 | } 553 | setCF(cf() || al() > 0x9f); 554 | if (cf()) 555 | setAL(al() + (opcode == 0x27 ? 0x60 : -0x60)); 556 | wordSize = false; 557 | data = al(); 558 | setPZS(); 559 | o(opcode == 0x27 ? 'y' : 'Y'); 560 | break; 561 | case 0x37: // AAA 562 | case 0x3f: // AAS 563 | if (af() || (al() & 0xf) > 9) { 564 | setAL(al() + (opcode == 0x37 ? 6 : -6)); 565 | setAH(ah() + (opcode == 0x37 ? 1 : -1)); 566 | setCA(); 567 | } 568 | else 569 | clearCA(); 570 | setAL(al() & 0x0f); 571 | o(opcode == 0x37 ? 'A' : 'u'); 572 | break; 573 | case 0x40: case 0x41: case 0x42: case 0x43: 574 | case 0x44: case 0x45: case 0x46: case 0x47: 575 | case 0x48: case 0x49: case 0x4a: case 0x4b: 576 | case 0x4c: case 0x4d: case 0x4e: case 0x4f: // incdec rw 577 | destination = rw(); 578 | wordSize = true; 579 | setRW(incdec((opcode & 8) != 0)); 580 | o((opcode & 8) != 0 ? 'i' : 'd'); 581 | break; 582 | case 0x50: case 0x51: case 0x52: case 0x53: 583 | case 0x54: case 0x55: case 0x56: case 0x57: // PUSH rw 584 | push(rw()); 585 | break; 586 | case 0x58: case 0x59: case 0x5a: case 0x5b: 587 | case 0x5c: case 0x5d: case 0x5e: case 0x5f: // POP rw 588 | setRW(pop()); 589 | break; 590 | case 0x60: case 0x61: case 0x62: case 0x63: 591 | case 0x64: case 0x65: case 0x66: case 0x67: 592 | case 0x68: case 0x69: case 0x6a: case 0x6b: 593 | case 0x6c: case 0x6d: case 0x6e: case 0x6f: 594 | case 0xc0: case 0xc1: case 0xc8: case 0xc9: // invalid 595 | case 0xf1: 596 | case 0xd8: case 0xd9: case 0xda: case 0xdb: 597 | case 0xdc: case 0xdd: case 0xde: case 0xdf: // escape 598 | case 0x0f: // POP CS 599 | case 0x9b: // WAIT 600 | case 0xf0: // LOCK 601 | case 0xf4: // HLT 602 | runtimeError("Invalid opcode %02x", opcode); 603 | break; 604 | case 0xe4: case 0xe5: // IN ib 605 | (void)fetchByte(); 606 | //FIXME implement, returns -1 for now 607 | data = -1; setAccum(); 608 | break; 609 | case 0xe6: case 0xe7: // OUT ib 610 | (void)fetchByte(); 611 | //FIXME implement 612 | break; 613 | case 0xec: case 0xed: // IN dx 614 | //FIXME implement, returns -1 for now 615 | data = -1; setAccum(); 616 | break; 617 | case 0xee: case 0xef: // OUT dx 618 | //FIXME implement 619 | break; 620 | case 0x70: case 0x71: case 0x72: case 0x73: 621 | case 0x74: case 0x75: case 0x76: case 0x77: 622 | case 0x78: case 0x79: case 0x7a: case 0x7b: 623 | case 0x7c: case 0x7d: case 0x7e: case 0x7f: // Jcond cb 624 | switch (opcode & 0x0e) { 625 | case 0x00: jump = of(); break; 626 | case 0x02: jump = cf(); break; 627 | case 0x04: jump = zf(); break; 628 | case 0x06: jump = cf() || zf(); break; 629 | case 0x08: jump = sf(); break; 630 | case 0x0a: jump = pf(); break; 631 | case 0x0c: jump = sf() != of(); break; 632 | default: jump = sf() != of() || zf(); break; 633 | } 634 | jumpShort(fetchByte(), jump == ((opcode & 1) == 0)); 635 | o("MK[)=J(]GgpP<.,>"[opcode & 0xf]); 636 | break; 637 | case 0x80: case 0x81: case 0x82: case 0x83: // alu rmv,iv 638 | destination = readEA(); 639 | data = fetch(opcode == 0x81); 640 | if (opcode != 0x83) 641 | source = data; 642 | else 643 | source = signExtend(data); 644 | aluOperation = modRMReg(); 645 | doALUOperation(); 646 | if (aluOperation != 7) 647 | finishWriteEA(data); 648 | break; 649 | case 0x84: case 0x85: // TEST rmv,rv 650 | data = readEA(); 651 | test(data, getReg()); 652 | o('t'); 653 | break; 654 | case 0x86: case 0x87: // XCHG rmv,rv 655 | data = readEA(); 656 | finishWriteEA(getReg()); 657 | setReg(data); 658 | o('x'); 659 | break; 660 | case 0x88: case 0x89: // MOV rmv,rv 661 | ea(); 662 | finishWriteEA(getReg()); 663 | o('m'); 664 | break; 665 | case 0x8a: case 0x8b: // MOV rv,rmv 666 | setReg(readEA()); 667 | o('m'); 668 | break; 669 | case 0x8c: // MOV rmw,segreg 670 | ea(); 671 | wordSize = 1; 672 | finishWriteEA(registers[modRMReg() + 8]); 673 | o('m'); 674 | break; 675 | case 0x8d: // LEA 676 | address = ea(); 677 | if (!useMemory) 678 | runtimeError("LEA needs a memory address"); 679 | setReg(address); 680 | o('l'); 681 | break; 682 | case 0x8e: // MOV segreg,rmw 683 | wordSize = 1; 684 | data = readEA(); 685 | registers[modRMReg() + 8] = data; 686 | o('m'); 687 | break; 688 | case 0x8f: // POP rmw 689 | writeEA(pop()); 690 | break; 691 | case 0x90: case 0x91: case 0x92: case 0x93: 692 | case 0x94: case 0x95: case 0x96: case 0x97: // XCHG AX,rw 693 | data = ax(); 694 | setAX(rw()); 695 | setRW(data); 696 | o(";xxxxxxx"[opcode & 7]); 697 | break; 698 | case 0x98: // CBW 699 | setAX(signExtend(al())); 700 | o('b'); 701 | break; 702 | case 0x99: // CWD 703 | setDX((ax() & 0x8000) == 0 ? 0x0000 : 0xffff); 704 | o('w'); 705 | break; 706 | case 0x9a: // CALL cp 707 | savedIP = fetchWord(); 708 | savedCS = fetchWord(); 709 | o('c'); 710 | farCall(); 711 | break; 712 | case 0x9c: // PUSHF 713 | o('U'); 714 | push((flags & 0x0fd7) | 0xf000); 715 | break; 716 | case 0x9d: // POPF 717 | o('O'); 718 | flags = pop() | 2; 719 | break; 720 | case 0x9e: // SAHF 721 | flags = (flags & 0xff02) | ah(); 722 | o('s'); 723 | break; 724 | case 0x9f: // LAHF 725 | setAH(flags & 0xd7); 726 | o('L'); 727 | break; 728 | case 0xa0: case 0xa1: // MOV accum,xv 729 | segment = DS; 730 | data = readwb(fetchWord(), -1); 731 | setAccum(); 732 | o('m'); 733 | break; 734 | case 0xa2: case 0xa3: // MOV xv,accum 735 | segment = DS; 736 | writewb(getAccum(), fetchWord(), -1); 737 | o('m'); 738 | break; 739 | case 0xa4: case 0xa5: // MOVSv 740 | if (rep == 0 || cx() != 0) 741 | stoS(lodS()); 742 | doRep(false); 743 | o('4' + (opcode & 1)); 744 | break; 745 | case 0xa6: case 0xa7: // CMPSv 746 | if (rep == 0 || cx() != 0) { 747 | destination = lodS(); 748 | source = lodDIS(); 749 | sub(); 750 | } 751 | doRep(true); 752 | o('0' + (opcode & 1)); 753 | break; 754 | case 0xa8: case 0xa9: // TEST accum,iv 755 | data = fetch(wordSize); 756 | test(getAccum(), data); 757 | o('t'); 758 | break; 759 | case 0xaa: case 0xab: // STOSv 760 | if (rep == 0 || cx() != 0) 761 | stoS(getAccum()); 762 | doRep(false); 763 | o('8' + (opcode & 1)); 764 | break; 765 | case 0xac: case 0xad: // LODSv 766 | if (rep == 0 || cx() != 0) { 767 | data = lodS(); 768 | setAccum(); 769 | } 770 | doRep(false); 771 | o('2' + (opcode & 1)); 772 | break; 773 | case 0xae: case 0xaf: // SCASv 774 | if (rep == 0 || cx() != 0) { 775 | destination = getAccum(); 776 | source = lodDIS(); 777 | sub(); 778 | } 779 | doRep(true); 780 | o('6' + (opcode & 1)); 781 | break; 782 | case 0xb0: case 0xb1: case 0xb2: case 0xb3: 783 | case 0xb4: case 0xb5: case 0xb6: case 0xb7: 784 | setRB(fetchByte()); 785 | o('m'); 786 | break; 787 | case 0xb8: case 0xb9: case 0xba: case 0xbb: 788 | case 0xbc: case 0xbd: case 0xbe: case 0xbf: // MOV rv,iv 789 | setRW(fetchWord()); 790 | o('m'); 791 | break; 792 | case 0xc2: case 0xc3: case 0xca: case 0xcb: // RET 793 | savedIP = pop(); 794 | savedCS = (opcode & 8) == 0 ? cs() : pop(); 795 | if (!wordSize) 796 | setSP(sp() + fetchWord()); 797 | o('R'); 798 | farJump(); 799 | break; 800 | case 0xc4: case 0xc5: // LES/LDS 801 | ea(); 802 | farLoad(); 803 | *modRMRW() = savedIP; 804 | registers[8 + (!wordSize ? 0 : 3)] = savedCS; 805 | o("NT"[opcode & 1]); 806 | break; 807 | case 0xc6: case 0xc7: // MOV rmv,iv 808 | ea(); 809 | finishWriteEA(fetch(wordSize)); 810 | o('m'); 811 | break; 812 | case 0xcc: // INT 3 813 | handleInterrupt(ep, INT3_BREAKPOINT); 814 | break; 815 | case 0xcd: 816 | handleInterrupt(ep, fetchByte()); 817 | o('$'); 818 | break; 819 | case 0xce: // INTO 820 | handleInterrupt(ep, INT4_OVERFLOW); 821 | break; 822 | case 0xcf: // IRET 823 | o('I'); 824 | doJump(pop()); 825 | setCS(pop()); 826 | flags = pop() | 2; 827 | break; 828 | case 0xd0: case 0xd1: case 0xd2: case 0xd3: // rot rmv,n 829 | data = readEA(); 830 | if ((opcode & 2) == 0) 831 | source = 1; 832 | else 833 | source = cl(); 834 | while (source != 0) { 835 | destination = data; 836 | switch (modRMReg()) { 837 | case 0: // ROL 838 | data <<= 1; 839 | doCF(); 840 | data |= (cf() ? 1 : 0); 841 | setOFRotate(); 842 | break; 843 | case 1: // ROR 844 | setCF((data & 1) != 0); 845 | data >>= 1; 846 | if (cf()) 847 | data |= (!wordSize ? 0x80 : 0x8000); 848 | setOFRotate(); 849 | break; 850 | case 2: // RCL 851 | data = (data << 1) | (cf() ? 1 : 0); 852 | doCF(); 853 | setOFRotate(); 854 | break; 855 | case 3: // RCR 856 | data >>= 1; 857 | if (cf()) 858 | data |= (!wordSize ? 0x80 : 0x8000); 859 | setCF((destination & 1) != 0); 860 | setOFRotate(); 861 | break; 862 | case 4: // SHL 863 | case 6: 864 | data <<= 1; 865 | doCF(); 866 | setOFRotate(); 867 | setPZS(); 868 | break; 869 | case 5: // SHR 870 | setCF((data & 1) != 0); 871 | data >>= 1; 872 | setOFRotate(); 873 | setAF(true); 874 | setPZS(); 875 | break; 876 | case 7: // SAR 877 | setCF((data & 1) != 0); 878 | data >>= 1; 879 | if (!wordSize) 880 | data |= (destination & 0x80); 881 | else 882 | data |= (destination & 0x8000); 883 | setOFRotate(); 884 | setAF(true); 885 | setPZS(); 886 | break; 887 | } 888 | --source; 889 | } 890 | finishWriteEA(data); 891 | o("hHfFvVvW"[modRMReg()]); 892 | break; 893 | case 0xd4: // AAM 894 | data = fetchByte(); 895 | if (data == 0) 896 | divideOverflow(); 897 | setAH(al() / data); 898 | setAL(al() % data); 899 | wordSize = true; 900 | setPZS(); 901 | o('n'); 902 | break; 903 | case 0xd5: // AAD 904 | data = fetchByte(); 905 | setAL(al() + ah()*data); 906 | setAH(0); 907 | setPZS(); 908 | o('k'); 909 | break; 910 | case 0xd6: // SALC 911 | setAL(cf() ? 0xff : 0x00); 912 | o('S'); 913 | break; 914 | case 0xd7: // XLATB 915 | setAL(readByte(bx() + al(), -1)); 916 | o('@'); 917 | break; 918 | case 0xe0: case 0xe1: case 0xe2: // LOOPc cb 919 | setCX(cx() - 1); 920 | jump = (cx() != 0); 921 | switch (opcode) { 922 | case 0xe0: if (zf()) jump = false; break; 923 | case 0xe1: if (!zf()) jump = false; break; 924 | } 925 | o("Qqo"[opcode & 3]); 926 | jumpShort(fetchByte(), jump); 927 | break; 928 | case 0xe3: // JCXZ cb 929 | o('z'); 930 | jumpShort(fetchByte(), cx() == 0); 931 | break; 932 | case 0xe8: // CALL cw 933 | data = fetchWord(); 934 | o('c'); 935 | call(ip + data); 936 | break; 937 | case 0xe9: // JMP cw 938 | o('j'); 939 | data = fetchWord(); 940 | doJump(ip + data); 941 | break; 942 | case 0xea: // JMP cp 943 | o('j'); 944 | savedIP = fetchWord(); 945 | savedCS = fetchWord(); 946 | farJump(); 947 | break; 948 | case 0xeb: // JMP cb 949 | o('j'); 950 | jumpShort(fetchByte(), true); 951 | break; 952 | case 0xf2: // REPNZ 953 | case 0xf3: // REPZ 954 | o('r'); 955 | rep = opcode == 0xf2 ? 1 : 2; 956 | prefix = true; 957 | break; 958 | case 0xf5: // CMC 959 | o('\"'); 960 | flags ^= 1; 961 | break; 962 | case 0xf6: case 0xf7: // math rmv 963 | data = readEA(); 964 | switch (modRMReg()) { 965 | case 0: case 1: // TEST rmv,iv 966 | test(data, fetch(wordSize)); 967 | o('t'); 968 | break; 969 | case 2: // NOT iv 970 | finishWriteEA(~data); 971 | o('~'); 972 | break; 973 | case 3: // NEG iv 974 | source = data; 975 | destination = 0; 976 | sub(); 977 | finishWriteEA(data); 978 | o('_'); 979 | break; 980 | case 4: case 5: // MUL rmv, IMUL rmv 981 | source = data; 982 | destination = getAccum(); 983 | data = destination; 984 | setSF(); 985 | setPF(); 986 | data *= source; 987 | setAX(data); 988 | if (!wordSize) { 989 | if (modRMReg() == 4) 990 | setCF(ah() != 0); 991 | else { 992 | if ((source & 0x80) != 0) 993 | setAH(ah() - destination); 994 | if ((destination & 0x80) != 0) 995 | setAH(ah() - source); 996 | setCF(ah() == 997 | ((al() & 0x80) == 0 ? 0 : 0xff)); 998 | } 999 | } 1000 | else { 1001 | setDX(data >> 16); 1002 | if (modRMReg() == 4) { 1003 | data |= dx(); 1004 | setCF(dx() != 0); 1005 | } 1006 | else { 1007 | if ((source & 0x8000) != 0) 1008 | setDX(dx() - destination); 1009 | if ((destination & 0x8000) != 0) 1010 | setDX(dx() - source); 1011 | data |= dx(); 1012 | setCF(dx() == 1013 | ((ax() & 0x8000) == 0 ? 0 : 0xffff)); 1014 | } 1015 | } 1016 | setZF(); 1017 | setOF(cf()); 1018 | o("*#"[opcode & 1]); 1019 | break; 1020 | case 6: case 7: // DIV rmv, IDIV rmv 1021 | source = data; 1022 | if (source == 0) 1023 | divideOverflow(); 1024 | if (!wordSize) { 1025 | destination = ax(); 1026 | if (modRMReg() == 6) { 1027 | divide(); 1028 | if (data > 0xff) 1029 | divideOverflow(); 1030 | } 1031 | else { 1032 | destination = ax(); 1033 | if ((destination & 0x8000) != 0) 1034 | destination |= 0xffff0000; 1035 | source = signExtend(source); 1036 | divide(); 1037 | if (data > 0x7f && data < 0xffffff80) 1038 | divideOverflow(); 1039 | } 1040 | setAH((Byte)residue); 1041 | setAL(data); 1042 | } 1043 | else { 1044 | destination = (dx() << 16) + ax(); 1045 | divide(); 1046 | if (modRMReg() == 6) { 1047 | if (data > 0xffff) 1048 | divideOverflow(); 1049 | } 1050 | else { 1051 | if (data > 0x7fff && data < 0xffff8000) 1052 | divideOverflow(); 1053 | } 1054 | setDX(residue); 1055 | setAX(data); 1056 | } 1057 | o("/\\"[opcode & 1]); 1058 | break; 1059 | } 1060 | break; 1061 | case 0xf8: case 0xf9: // STC/CLC 1062 | setCF(wordSize); 1063 | o("\'`"[opcode & 1]); 1064 | break; 1065 | case 0xfa: case 0xfb: // STI/CLI 1066 | setIF(wordSize); 1067 | o("!:"[opcode & 1]); 1068 | break; 1069 | case 0xfc: case 0xfd: // STD/CLD 1070 | setDF(wordSize); 1071 | o("CD"[opcode & 1]); 1072 | break; 1073 | case 0xfe: case 0xff: // misc 1074 | ea(); 1075 | if ((!wordSize && modRMReg() >= 2 && modRMReg() <= 6) || 1076 | modRMReg() == 7) { 1077 | runtimeError("Invalid instruction %02x %02x", opcode, modRM); 1078 | } 1079 | switch (modRMReg()) { 1080 | case 0: case 1: // incdec rmv 1081 | destination = readEA2(); 1082 | finishWriteEA(incdec(modRMReg() != 0)); 1083 | o("id"[modRMReg() & 1]); 1084 | break; 1085 | case 2: // CALL rmv 1086 | o('c'); 1087 | call(readEA2()); 1088 | break; 1089 | case 3: // CALL mp 1090 | o('c'); 1091 | farLoad(); 1092 | farCall(); 1093 | break; 1094 | case 4: // JMP rmw 1095 | o('j'); 1096 | doJump(readEA2()); 1097 | break; 1098 | case 5: // JMP mp 1099 | o('j'); 1100 | farLoad(); 1101 | farJump(); 1102 | break; 1103 | case 6: // PUSH rmw 1104 | push(readEA2()); 1105 | break; 1106 | } 1107 | break; 1108 | } 1109 | } 1110 | -------------------------------------------------------------------------------- /8086.h: -------------------------------------------------------------------------------- 1 | /* 8086 emulator header file */ 2 | #include 3 | 4 | typedef uint8_t Byte; 5 | typedef uint16_t Word; 6 | typedef uint32_t DWord; 7 | #ifndef false 8 | enum { false = 0, true }; 9 | #endif 10 | 11 | /* segment registers after 8 general registers */ 12 | enum { ES = 0, CS, SS, DS }; 13 | 14 | /* emulator globals */ 15 | #define RAMSIZE 0x100000 /* 1M RAM */ 16 | extern Word registers[12]; 17 | extern Byte* byteRegisters[8]; 18 | extern Byte ram[RAMSIZE]; 19 | 20 | /* emulator operation */ 21 | struct exe; /* defined in exe.h */ 22 | int initMachine(struct exe *e); 23 | void initExecute(void); 24 | void executeInstruction(void); 25 | int isRepeating(void); 26 | 27 | /* emulator callouts */ 28 | void runtimeError(const char *msg, ...); 29 | void handleInterrupt(struct exe *e, int intno); 30 | int checkStackElks(struct exe *e); 31 | int checkStackDOS(struct exe *e); 32 | int handleSyscallElks(struct exe *e, int intno); 33 | int handleSyscallDOS(struct exe *e, int intno); 34 | 35 | /* memory access functions */ 36 | Byte readByte(Word offset, int seg); 37 | Word readWordSeg(Word offset, int seg); 38 | void writeByte(Byte value, Word offset, int seg); 39 | void writeWord(Word value, Word offset, int seg); 40 | DWord physicalAddress(Word offset, int seg, int write); 41 | #define fRead 0x01 42 | #define fWrite 0x02 43 | void setShadowFlags(Word offset, int seg, int len, int flags); 44 | 45 | #define INT0_DIV_ERROR 0 46 | #define INT3_BREAKPOINT 3 47 | #define INT4_OVERFLOW 4 48 | 49 | /* register access functions */ 50 | static inline Word ax() { return registers[0]; } 51 | static inline Word cx() { return registers[1]; } 52 | static inline Word dx() { return registers[2]; } 53 | static inline Word bx() { return registers[3]; } 54 | static inline Word sp() { return registers[4]; } 55 | static inline Word bp() { return registers[5]; } 56 | static inline Word si() { return registers[6]; } 57 | static inline Word di() { return registers[7]; } 58 | static inline Word es() { return registers[8]; } 59 | static inline Word cs() { return registers[9]; } 60 | static inline Word ss() { return registers[10]; } 61 | static inline Word ds() { return registers[11]; } 62 | static inline Byte al() { return *byteRegisters[0]; } 63 | static inline Byte cl() { return *byteRegisters[1]; } 64 | static inline Byte dl() { return *byteRegisters[2]; } 65 | static inline Byte bl() { return *byteRegisters[3]; } 66 | static inline Byte ah() { return *byteRegisters[4]; } 67 | static inline Byte ch() { return *byteRegisters[5]; } 68 | static inline Byte dh() { return *byteRegisters[6]; } 69 | static inline Byte bh() { return *byteRegisters[7]; } 70 | static inline void setAX(Word value) { registers[0] = value; } 71 | static inline void setCX(Word value) { registers[1] = value; } 72 | static inline void setDX(Word value) { registers[2] = value; } 73 | static inline void setBX(Word value) { registers[3] = value; } 74 | static inline void setSP(Word value) { registers[4] = value; } 75 | static inline void setBP(Word value) { registers[5] = value; } 76 | static inline void setSI(Word value) { registers[6] = value; } 77 | static inline void setDI(Word value) { registers[7] = value; } 78 | static inline void setES(Word value) { registers[8] = value; } 79 | static inline void setCS(Word value) { registers[9] = value; } 80 | static inline void setSS(Word value) { registers[10] = value; } 81 | static inline void setDS(Word value) { registers[11] = value; } 82 | static inline void setAL(Byte value) { *byteRegisters[0] = value; } 83 | static inline void setCL(Byte value) { *byteRegisters[1] = value; } 84 | static inline void setDL(Byte value) { *byteRegisters[2] = value; } 85 | static inline void setBL(Byte value) { *byteRegisters[3] = value; } 86 | static inline void setAH(Byte value) { *byteRegisters[4] = value; } 87 | static inline void setCH(Byte value) { *byteRegisters[5] = value; } 88 | static inline void setDH(Byte value) { *byteRegisters[6] = value; } 89 | static inline void setBH(Byte value) { *byteRegisters[7] = value; } 90 | Word getIP(void); 91 | void setIP(Word w); 92 | void setFlags(Word w); 93 | Word getFlags(void); 94 | void setCF(int cf); 95 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # sim86 8086 emulator Makefile 2 | # 3 | ELKS=../elks-gh 4 | CFLAGS=-Wall -O3 5 | 6 | all: sim86 dis86 nm86 test testdos 7 | 8 | clean: 9 | -rm -f sim86 dis86 opcodes 10 | 11 | test2: sim86 12 | sim86 -a echo1 13 | 14 | test: sim86 15 | ./sim86 -v $(ELKS)/target/bin/banner ELKS Emulator 16 | @#./sim86 $(ELKS)/target/bin/hd sim86 17 | @#./sim86 $(ELKS)/target/bin/cat sim86.c 18 | @#./sim86 $(ELKS)/target/bin/echo This is a test 19 | @#./sim86 $(ELKS)/target/bin/printenv 20 | @#./sim86 $(ELKS)/target/bin/env 21 | @#./sim86 $(ELKS)/target/bin/hd 0:0#256 22 | @#./sim86 $(ELKS)/target/bin/login 23 | @#./sim86 $(ELKS)/target/bin/chmem $(ELKS)/target/bin/login 24 | 25 | testdos: sim86 26 | ./sim86 -va test.exe 27 | @#./sim-dos hexdump.exe 28 | 29 | testbin: sim86 30 | ./sim86 -va basic.bin 31 | 32 | sim86: sim86.c 8086.c disasm.c dissim.c discolor.c syms.c loader-elks.c syscall-elks.c loader-dos.c syscall-dos.c loader-bin.c 33 | $(CC) $(CFLAGS) -o $@ $^ 34 | 35 | dis86: dis86.c disasm.c dissim.c discolor.c syms.c 36 | $(CC) $(CFLAGS) -D__far= -Wno-format -o $@ $^ 37 | 38 | nm86: nm86.c syms.c 39 | $(CC) $(CFLAGS) -o $@ $^ 40 | 41 | opcodes: opcodes.S 42 | ia16-elf-gcc -melks -ffreestanding -nostdlib -o $@ $^ 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 86sim 2 | 3 | A lightweight 8086 CPU emulator and disassembler that runs ELKS (a.out) 4 | and MSDOS (MZ header) executables outside their respective operating systems. 5 | 6 | Developed on macOS, should run on Linux. 7 | 8 | Original 8086 emulator from [Andrew Jenner's Reenigne Project](https://github.com/reenigne/reenigne/tree/master/8088/86sim). 9 | 10 | Enhancements from TK Chia's [fork](https://github.com/tkchia/reenigne/blob/master/8088/86sim/86sim.cpp) of that project. 11 | 12 | ELKS and disassembly support is a work in progress. 13 | -------------------------------------------------------------------------------- /dis86.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ELKS 8086 Disassembler 3 | * 4 | * Aug 2022 Greg Haerr 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #if __ia16__ 15 | #include 16 | #include "instrument.h" 17 | #endif 18 | #include "exe.h" 19 | #include "syms.h" 20 | #include "disasm.h" 21 | #include "discolor.h" 22 | 23 | #define KSYMTAB "/lib/system.sym" 24 | #define MAGIC 0x0301 /* magic number for executable progs */ 25 | 26 | static int f_asmout; 27 | static int f_octal; 28 | static int f_ksyms; 29 | static struct exe e; 30 | static struct dis d; 31 | 32 | #define peekb(cs,ip) (*(unsigned char __far *)(((unsigned long)(cs) << 16) | (int)(ip))) 33 | 34 | int nextbyte_mem(int cs, int ip) 35 | { 36 | return peekb(cs, ip); 37 | } 38 | 39 | void disasm_mem(int cs, int ip, int opcount) 40 | { 41 | int n; 42 | int nextip; 43 | 44 | int flags = f_asmout? fDisInst | fDisAsmSource : fDisCS | fDisIP | fDisBytes | fDisInst; 45 | if (f_octal) flags |= fDisOctal; 46 | if (!opcount) opcount = 32767; 47 | if (!f_asmout) printf("Disassembly of %s:\n", getsymbol(&d, cs, (int)ip)); 48 | e.textseg = cs; 49 | for (n=0; n 0xffff || off > 0xffff) { 161 | fprintf(stderr, "Error: segment or offset larger than 0xffff\n"); 162 | return 1; 163 | } 164 | disasm_mem((int)seg, (int)off, (int)count); 165 | } else return disasm_file(*av); 166 | return 0; 167 | } 168 | -------------------------------------------------------------------------------- /disasm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Tiny 8086 disassembler 3 | * 4 | * Written Jan 2022 Greg Haerr 5 | * Inspired by Andrew Jenner's 8086 simulator 86sim.cpp 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include "disasm.h" 11 | 12 | typedef uint8_t Byte; 13 | typedef uint16_t Word; 14 | typedef int bool; 15 | enum { false = 0, true }; 16 | 17 | static bool wordSize; 18 | static bool sourceIsRM; 19 | static Byte opcode; 20 | static Byte d_modRM; 21 | //static int segOver; 22 | 23 | static void decode(struct dis *d); 24 | 25 | int disasm(struct dis *d, int cs, int ip, int (*nextbyte)(int, int), int ds, int flags) 26 | { 27 | d->cs = cs; 28 | d->ip = ip; 29 | d->ds = ds; 30 | d->getbyte = nextbyte; 31 | d->flags = flags; 32 | d->col = 0; 33 | d->s = d->buf; 34 | if (d->flags & fDisAddr) 35 | d->s += sprintf(d->s, "%05lx ", (unsigned long)(cs << 4) | (unsigned short)ip); 36 | if (d->flags & fDisCS) 37 | d->s += sprintf(d->s, "%04hx:", (unsigned short)cs); 38 | if (d->flags & fDisIP) 39 | d->s += sprintf(d->s, "%04hx ", (unsigned short)ip); 40 | decode(d); 41 | d->s[0] = '\0'; 42 | d->oplen = d->ip - ip; 43 | return d->ip; 44 | } 45 | 46 | static const char *wordregs[] = { 47 | "%ax", "%cx", "%dx", "%bx", "%sp", "%bp", "%si", "%di"}; 48 | static const char *byteregs[] = { 49 | "%al", "%cl", "%dl", "%bl", "%ah", "%ch", "%dh", "%bh"}; 50 | static const char *segregs[] = { "%es", "%cs", "%ss", "%ds"}; 51 | 52 | static Word d_fetchByte(struct dis *d) 53 | { 54 | Byte b = d->getbyte(d->cs, d->ip++); 55 | if (d->flags & fDisBytes) { 56 | d->s += sprintf(d->s, (d->flags & fDisOctal)? "%03o ": "%02x ", b); 57 | d->col++; 58 | } 59 | return b; 60 | } 61 | 62 | static Word d_fetchWord(struct dis *d) 63 | { 64 | Word w = d_fetchByte(d); 65 | w += d_fetchByte(d) << 8; 66 | return w; 67 | } 68 | 69 | static int d_modRMReg() 70 | { 71 | return (d_modRM >> 3) & 7; 72 | } 73 | 74 | static void outREG(struct dis *d) 75 | { 76 | if (wordSize || opcode == 0xee || opcode == 0xec) // OUT dx 77 | d->s += sprintf(d->s, "%s", wordregs[(d_modRM >> 3) & 7]); 78 | else d->s += sprintf(d->s, "%s", byteregs[(d_modRM >> 3) & 7]); 79 | } 80 | 81 | static void outSREG(struct dis *d) 82 | { 83 | d->s += sprintf(d->s, "%s", segregs[(d_modRM >> 3) & 3]); 84 | } 85 | 86 | static void outRM(struct dis *d, Word w) 87 | { 88 | signed char b; 89 | static const char *basemodes[] = { 90 | "(%bx,%si)", "(%bx,%di)", "(%bp,%si)", "(%bp,%di)", 91 | "(%si)", "(%di)", "(%bp)", "(%bx)" }; 92 | 93 | switch (d_modRM & 0xc0) { 94 | case 0x00: 95 | if ((d_modRM & 0xc7) == 6) 96 | d->s += sprintf(d->s, "(%s)", getsymbol(d, d->ds, w)); 97 | else d->s += sprintf(d->s, "%s", basemodes[d_modRM & 7]); 98 | break; 99 | case 0x40: 100 | b = (signed char) w; /* signed */ 101 | if (b < 0) d->s += sprintf(d->s, "-0x%02x", -b); 102 | else d->s += sprintf(d->s, "0x%02x", b); 103 | d->s += sprintf(d->s, "%s", basemodes[d_modRM & 7]); 104 | break; 105 | case 0x80: 106 | d->s += sprintf(d->s, "0x%04x%s", w, basemodes[d_modRM & 7]); 107 | break; 108 | case 0xc0: 109 | if (wordSize) d->s += sprintf(d->s, "%s", wordregs[d_modRM & 7]); 110 | else d->s += sprintf(d->s, "%s", byteregs[d_modRM & 7]); 111 | break; 112 | } 113 | } 114 | 115 | #define BW 0x0001 /* display byte/word instruction */ 116 | #define RDMOD 0x0002 /* read modRMReg byte */ 117 | #define OPS2 0x0004 /* display both REG & R/M operands */ 118 | #define RM 0x0008 /* display R/M operand */ 119 | #define SREG 0x0010 /* display SREG operand */ 120 | #define REGOP 0x0020 /* display REG operand from opcode */ 121 | #define IMM 0x0040 /* display immediate operand */ 122 | #define BYTE 0x0080 /* fetch unsigned byte immediate operand */ 123 | #define SBYTE 0x0100 /* fetch signed byte */ 124 | #define WORD 0x0200 /* fetch word, and/or display word not immediate operand */ 125 | #define DWORD 0x0400 /* fetch second word */ 126 | #define MEMWORD 0x0800 /* display word memory address */ 127 | #define ACC 0x1000 /* display accumulator */ 128 | #define JMP 0x2000 /* display jmp w/byte, word or dword operand */ 129 | #define SHIFTBY1 0x4000 /* display shift 1 operand */ 130 | #define SHIFTBYCL 0x8000 /* display shift CL operand */ 131 | 132 | static void out_bw(struct dis *d, int flags) 133 | { 134 | int bw, special; 135 | 136 | if ((flags & (BW|OPS2|RM)) == 0) 137 | return; 138 | 139 | /* handle test/not/neg/mul/imul/div/idiv/inc/dec specially */ 140 | special = (opcode == 0xfe || opcode == 0xff || opcode == 0xf6 || opcode == 0xf7); 141 | 142 | /* discard non-(BW, IMM, shift) and non-direct addressing */ 143 | bw = (flags == BW || (flags & (IMM|SHIFTBY1|SHIFTBYCL)) || special); 144 | if (!bw && ((flags & (OPS2|RM)) && (d_modRM & 0xc7) != 6)) 145 | return; 146 | /* discard register operands on special */ 147 | if (special && (d_modRM & 0xc0) == 0xc0) 148 | return; 149 | /* discard immediate to register */ 150 | if ((flags & IMM) && ((flags & (OPS2|RM)) && ((d_modRM & 0xc0) == 0xc0))) 151 | return; 152 | if ((flags & (SHIFTBY1|SHIFTBYCL)) && ((d_modRM & 0xc0) == 0xc0)) 153 | return; 154 | /* discard register operands */ 155 | if (flags & REGOP) 156 | return; 157 | /* discard register operands on alu ops */ 158 | if ((opcode & 0x3d) == opcode) 159 | return; 160 | /* discard mov accumulator and test accumulator opcodes */ 161 | if ((opcode & 0xa3) == opcode || opcode == 0xa8 || opcode == 0xa9) { 162 | /* but not alu opcodes */ 163 | if (opcode < 0x80 || opcode > 0x83) 164 | return; 165 | } 166 | d->col++; 167 | *d->s++ = wordSize? 'w': 'b'; 168 | } 169 | 170 | static void outs(struct dis *d, const char *str, int flags) 171 | { 172 | Word w = 0; 173 | Word w2 = 0; 174 | signed char c = 0; 175 | 176 | if (flags & RDMOD) 177 | d_modRM = d_fetchByte(d); 178 | if (flags & (OPS2|RM)) { 179 | if (((d_modRM & 0xc7) == 6) || ((d_modRM & 0xc0) == 0x80)) 180 | w = d_fetchWord(d); 181 | if ((d_modRM & 0xc0) == 0x40) 182 | w = d_fetchByte(d); 183 | } 184 | if ((flags & (IMM|BYTE|SBYTE|WORD)) == IMM) 185 | w2 = !wordSize ? d_fetchByte(d) : d_fetchWord(d); 186 | 187 | if (flags & (WORD|DWORD|MEMWORD)) 188 | w2 = d_fetchWord(d); 189 | if (flags & BYTE) 190 | w2 = d_fetchByte(d); 191 | if (flags & SBYTE) 192 | w2 = c = d_fetchByte(d); 193 | if (flags & DWORD) 194 | w = d_fetchWord(d); 195 | 196 | if (!(d->flags & fDisInst)) 197 | return; 198 | if (d->flags & fDisBytes) { 199 | while (d->col++ < 6) { 200 | d->s += sprintf(d->s, (d->flags & fDisOctal)? " ": " "); 201 | } 202 | } 203 | if ((d->flags & fDisAsmSource) && !strcmp(str, "???")) { 204 | d->s += sprintf(d->s, ".byte 0x%02x", opcode); 205 | return; 206 | } 207 | d->col = strlen(str); 208 | d->s += sprintf(d->s, "%s", str); 209 | if (flags & BW) out_bw(d, flags); 210 | if (flags != 0) { 211 | while (d->col++ & 7) 212 | *d->s++ = ' '; 213 | } 214 | #if 0 215 | if (segOver != -1) { 216 | d->s += sprintf(d->s, "%s", segregs[segOver]); 217 | *d->s++ = ':'; 218 | } 219 | #endif 220 | if ((flags & (OPS2|SREG)) == OPS2) { 221 | if (sourceIsRM) outRM(d, w); else outREG(d); 222 | *d->s++ = ','; 223 | if (sourceIsRM) outREG(d); else outRM(d, w); 224 | } 225 | if ((flags & (OPS2|SREG)) == (OPS2|SREG)) { 226 | wordSize = 1; 227 | if (sourceIsRM) outRM(d, w); else outSREG(d); 228 | *d->s++ = ','; 229 | if (sourceIsRM) outSREG(d); else outRM(d, w); 230 | } 231 | if ((flags & (IMM|BYTE|ACC)) == (IMM|BYTE|ACC)) { // IN, OUT imm 232 | if (!sourceIsRM) d->s += sprintf(d->s, "$0x%x,%s", w2, wordSize? "%ax": "%al"); 233 | else d->s += sprintf(d->s, "%s,$0x%x", wordSize? "%ax": "%al", w2); 234 | flags = 0; 235 | } 236 | else if (flags & IMM) { 237 | d->s += sprintf(d->s, "$0x%x", w2); 238 | if (flags != (flags & IMM)) 239 | d->s += sprintf(d->s, ","); 240 | } 241 | if (flags & SHIFTBY1) d->s += sprintf(d->s, "$1,"); 242 | if (flags & SHIFTBYCL) d->s += sprintf(d->s, "%%cl,"); 243 | if (flags & RM) outRM(d, w); 244 | if ((flags & (OPS2|SREG)) == SREG) outSREG(d); 245 | if ((flags & ACC) && sourceIsRM == 0) 246 | d->s += sprintf(d->s, "%s,", wordSize? wordregs[0]: byteregs[0]); 247 | if ((flags & MEMWORD) && sourceIsRM) 248 | d->s += sprintf(d->s, "(%s),", getsymbol(d, d->ds, w2)); 249 | if ((flags & ACC) && sourceIsRM) 250 | d->s += sprintf(d->s, "%s", wordSize? wordregs[0]: byteregs[0]); 251 | if ((flags & MEMWORD) && sourceIsRM == 0) 252 | d->s += sprintf(d->s, "(%s)", getsymbol(d, d->ds, w2)); 253 | if (flags & REGOP) 254 | d->s += sprintf(d->s, "%s", wordSize? wordregs[opcode & 7]: byteregs[opcode & 7]); 255 | if ((flags & (JMP|IMM|WORD)) == WORD) 256 | d->s += sprintf(d->s, "%u", w2); 257 | if (flags & JMP) { 258 | if (flags & SBYTE) { 259 | if (d->flags & fDisAsmSource) 260 | d->s += sprintf(d->s, ".%s%d // %04x", c>=0? "+": "", c+2, d->ip+c); 261 | else d->s += sprintf(d->s, "%04x", d->ip + c); 262 | } 263 | if (flags & WORD) { 264 | int waddr = (d->ip + w2) & 0xffff; 265 | if (opcode == 0xfe || opcode == 0xff) { 266 | d->s += sprintf(d->s, "*%s", getsymbol(d, d->cs, w2)); 267 | } else d->s += sprintf(d->s, "%s // %04x", getsymbol(d, d->cs, waddr), waddr); 268 | } 269 | if (flags & DWORD) { 270 | d->s += sprintf(d->s, "$%s,$%s", getsegsymbol(d, w), getsymbol(d, w, w2)); 271 | } 272 | } 273 | } 274 | 275 | static void decode(struct dis *d) 276 | { 277 | static const char *alunames[8] = { 278 | "add", "or", "adc", "sbb", "and", "sub", "xor", "cmp" 279 | }; 280 | 281 | opcode = d_fetchByte(d); 282 | wordSize = ((opcode & 1) != 0); 283 | sourceIsRM = ((opcode & 2) != 0); 284 | int operation = (opcode >> 3) & 7; 285 | int flags; 286 | switch (opcode) { 287 | case 0x00: case 0x01: case 0x02: case 0x03: 288 | case 0x08: case 0x09: case 0x0a: case 0x0b: 289 | case 0x10: case 0x11: case 0x12: case 0x13: 290 | case 0x18: case 0x19: case 0x1a: case 0x1b: 291 | case 0x20: case 0x21: case 0x22: case 0x23: 292 | case 0x28: case 0x29: case 0x2a: case 0x2b: 293 | case 0x30: case 0x31: case 0x32: case 0x33: 294 | case 0x38: case 0x39: case 0x3a: case 0x3b: // alu rmv,rmv 295 | outs(d,alunames[(opcode >> 3) & 7], BW|RDMOD|OPS2); 296 | break; 297 | case 0x04: case 0x05: case 0x0c: case 0x0d: 298 | case 0x14: case 0x15: case 0x1c: case 0x1d: 299 | case 0x24: case 0x25: case 0x2c: case 0x2d: 300 | case 0x34: case 0x35: case 0x3c: case 0x3d: // alu accum,i 301 | sourceIsRM = 1; // acc dest 302 | outs(d, alunames[(opcode >> 3) & 7], BW|IMM|ACC); 303 | break; 304 | case 0x06: case 0x0e: case 0x16: case 0x1e: // PUSH segreg 305 | d_modRM = opcode; 306 | outs(d, "push", SREG); 307 | break; 308 | case 0x07: case 0x17: case 0x1f: // POP segreg 309 | d_modRM = opcode; 310 | outs(d, "pop", SREG); 311 | break; 312 | case 0x26: case 0x2e: case 0x36: case 0x3e: // segment override 313 | { 314 | #if 1 315 | static const char *segprefix[] = { "es", "cs", "ss", "ds"}; 316 | outs(d, segprefix[operation - 4], 0); 317 | break; 318 | #else 319 | segOver = operation - 4; 320 | prefix = true; 321 | goto nextopcode; 322 | #endif 323 | } 324 | case 0x27: // DAA 325 | case 0x2f: // DAS 326 | outs(d, opcode == 0x27? "daa": "das", 0); 327 | break; 328 | case 0x37: // AAA 329 | case 0x3f: // AAS 330 | outs(d, opcode == 0x37? "aaa": "aas", 0); 331 | break; 332 | case 0x40: case 0x41: case 0x42: case 0x43: 333 | case 0x44: case 0x45: case 0x46: case 0x47: 334 | case 0x48: case 0x49: case 0x4a: case 0x4b: 335 | case 0x4c: case 0x4d: case 0x4e: case 0x4f: // incdec rw 336 | wordSize = 1; 337 | outs(d, (opcode & 8)? "dec": "inc", REGOP); 338 | break; 339 | case 0x50: case 0x51: case 0x52: case 0x53: 340 | case 0x54: case 0x55: case 0x56: case 0x57: // PUSH rw 341 | wordSize = 1; 342 | outs(d, "push", REGOP); 343 | break; 344 | case 0x58: case 0x59: case 0x5a: case 0x5b: 345 | case 0x5c: case 0x5d: case 0x5e: case 0x5f: // POP rw 346 | wordSize = 1; 347 | outs(d, "pop", REGOP); 348 | break; 349 | case 0x60: case 0x61: case 0x62: case 0x63: 350 | case 0x64: case 0x65: case 0x66: case 0x67: 351 | case 0x68: case 0x69: case 0x6a: case 0x6b: 352 | case 0x6c: case 0x6d: case 0x6e: case 0x6f: 353 | case 0xc0: case 0xc1: case 0xc8: case 0xc9: // invalid 354 | case 0xf1: 355 | case 0xd8: case 0xd9: case 0xda: case 0xdb: 356 | case 0xdc: case 0xdd: case 0xde: case 0xdf: // escape 357 | case 0x0f: // POP CS 358 | outs(d, "???", 0); 359 | break; 360 | case 0x70: case 0x71: case 0x72: case 0x73: 361 | case 0x74: case 0x75: case 0x76: case 0x77: 362 | case 0x78: case 0x79: case 0x7a: case 0x7b: 363 | case 0x7c: case 0x7d: case 0x7e: case 0x7f: // Jcond cb 364 | { 365 | static const char *jumpnames[] = { 366 | "jo ", "jno", "jb ", "jae", "je ", "jne", "jbe", "ja ", 367 | "js ", "jns", "jp", "jnp","jl ", "jge", "jle", "jg " }; 368 | outs(d, jumpnames[opcode & 0x0f], JMP|SBYTE); 369 | } 370 | break; 371 | case 0x80: case 0x81: case 0x82: case 0x83: // alu rmv,iv 372 | d_modRM = d_fetchByte(d); 373 | flags = BW|IMM|RM; 374 | if (opcode == 0x81) flags |= WORD; 375 | else if (opcode == 0x83) flags |= SBYTE; 376 | else flags |= BYTE; 377 | outs(d, alunames[d_modRMReg()], flags); 378 | break; 379 | case 0x84: case 0x85: // TEST rmv,rv 380 | sourceIsRM = 0; 381 | outs(d, "test", BW|RDMOD|OPS2); 382 | break; 383 | case 0x86: case 0x87: // XCHG rmv,rv 384 | sourceIsRM = 0; 385 | outs(d, "xchg", BW|RDMOD|OPS2); 386 | break; 387 | case 0x88: case 0x89: // MOV rmv,rv 388 | sourceIsRM = 0; 389 | outs(d, "mov", BW|RDMOD|OPS2); 390 | break; 391 | case 0x8a: case 0x8b: // MOV rv,rmv 392 | sourceIsRM = 1; 393 | outs(d, "mov", BW|RDMOD|OPS2); 394 | break; 395 | case 0x8c: // MOV rmw,segreg 396 | sourceIsRM = 0; 397 | outs(d, "mov", RDMOD|OPS2|SREG); 398 | break; 399 | case 0x8d: // LEA 400 | sourceIsRM = 1; 401 | outs(d, "lea", RDMOD|OPS2); 402 | //if (!useMemory) runtimeError("LEA needs a memory address"); 403 | break; 404 | case 0x8e: // MOV segreg,rmw 405 | sourceIsRM = 1; 406 | outs(d, "mov", RDMOD|OPS2|SREG); 407 | break; 408 | case 0x8f: // POP rmw 409 | outs(d, "pop", RDMOD|RM); 410 | break; 411 | case 0x90: 412 | outs(d, "nop", 0); 413 | break; 414 | case 0x91: case 0x92: case 0x93: 415 | case 0x94: case 0x95: case 0x96: case 0x97: // XCHG AX,rw 416 | wordSize = 1; 417 | sourceIsRM = 0; // acc src 418 | outs(d, "xchg", ACC|REGOP); 419 | break; 420 | case 0x98: // CBTW 421 | outs(d, "cbtw", 0); 422 | break; 423 | case 0x99: // CWTD 424 | outs(d, "cwtd", 0); 425 | break; 426 | case 0x9a: // CALL cp 427 | outs(d, "lcall", JMP|DWORD); 428 | break; 429 | case 0x9b: // WAIT 430 | outs(d, "fwait", 0); 431 | break; 432 | case 0x9c: // PUSHF 433 | outs(d, "pushf", 0); 434 | break; 435 | case 0x9d: // POPF 436 | outs(d, "popf", 0); 437 | break; 438 | case 0x9e: // SAHF 439 | outs(d, "sahf", 0); 440 | break; 441 | case 0x9f: // LAHF 442 | outs(d, "lahf", 0); 443 | break; 444 | case 0xa0: // MOVB accum,xv 445 | wordSize = 0; 446 | sourceIsRM = 1; // acc dest 447 | outs(d, "mov", BW|MEMWORD|ACC); 448 | break; 449 | case 0xa1: // MOVW accum,xv 450 | wordSize = 1; 451 | sourceIsRM = 1; // acc dest 452 | outs(d, "mov", BW|MEMWORD|ACC); 453 | break; 454 | case 0xa2: // MOVB xv,accum 455 | wordSize = 0; 456 | sourceIsRM = 0; // acc src 457 | outs(d, "mov", BW|ACC|MEMWORD); 458 | break; 459 | case 0xa3: // MOVW xv,accum 460 | wordSize = 1; 461 | sourceIsRM = 0; // acc src 462 | outs(d, "mov", BW|ACC|MEMWORD); 463 | break; 464 | case 0xa4: case 0xa5: // MOVSv 465 | outs(d, "movs", BW); 466 | break; 467 | case 0xa6: case 0xa7: // CMPSv 468 | outs(d, "cmps", BW); 469 | break; 470 | case 0xa8: case 0xa9: // TEST accum,iv 471 | sourceIsRM = 1; // acc dest 472 | outs(d, "test", BW|IMM|ACC); 473 | break; 474 | case 0xaa: case 0xab: // STOSv 475 | outs(d, "stos", BW); 476 | break; 477 | case 0xac: case 0xad: // LODSv 478 | outs(d, "lods", BW); 479 | break; 480 | case 0xae: case 0xaf: // SCASv 481 | outs(d, "scas", BW); 482 | break; 483 | case 0xb0: case 0xb1: case 0xb2: case 0xb3: 484 | case 0xb4: case 0xb5: case 0xb6: case 0xb7: 485 | wordSize = 0; 486 | outs(d, "mov", BW|IMM|REGOP); 487 | break; 488 | case 0xb8: case 0xb9: case 0xba: case 0xbb: 489 | case 0xbc: case 0xbd: case 0xbe: case 0xbf: // MOV rv,iv 490 | wordSize = 1; 491 | outs(d, "mov", BW|IMM|REGOP); 492 | break; 493 | case 0xc2: case 0xc3: // RET 494 | outs(d, "ret", !wordSize? WORD: 0); //FIXME should display $WORD 495 | break; 496 | case 0xca: case 0xcb: // RETF 497 | outs(d, "lret", !wordSize? WORD: 0); //FIXME should display $WORD 498 | break; 499 | case 0xc4: case 0xc5: // LES/LDS 500 | //if (!useMemory) runtimeError("This instruction needs a memory address"); 501 | wordSize = 1; 502 | sourceIsRM = 1; 503 | outs(d, (opcode & 1)? "lds": "les", RDMOD|OPS2); 504 | break; 505 | case 0xc6: case 0xc7: // MOV rmv,iv 506 | outs(d, "mov", BW|RDMOD|IMM|RM); 507 | break; 508 | case 0xcc: // INT 3 509 | outs(d, "int3", 0); 510 | break; 511 | case 0xcd: 512 | wordSize = 0; 513 | outs(d, "int", IMM); 514 | break; 515 | case 0xce: // INTO 516 | outs(d, "into", 0); 517 | break; 518 | case 0xcf: // IRET 519 | outs(d, "iret", 0); 520 | break; 521 | case 0xd0: case 0xd1: case 0xd2: case 0xd3: // rot rmv,n 522 | { 523 | static const char *rotates[] = { 524 | "rol", "ror", "rcl", "rcr", "shl", "shr", "shl", "sar" }; 525 | d_modRM = d_fetchByte(d); 526 | flags = BW|RM; 527 | if (opcode & 2) flags |= SHIFTBYCL; 528 | else flags |= SHIFTBY1; 529 | outs(d, rotates[d_modRMReg()], flags); 530 | } 531 | break; 532 | case 0xd4: // AAM 533 | outs(d, "aam", 0); //FIXME should display $BYTE 534 | break; 535 | case 0xd5: // AAD 536 | outs(d, "aad", 0); //FIXME should display $BYTE 537 | break; 538 | case 0xd6: // SALC (undocumented) 539 | outs(d, "salc", 0); 540 | break; 541 | case 0xd7: // XLATB 542 | outs(d, "xlatb", 0); //FIXME xlat %ds:(%bx)? 543 | break; 544 | case 0xe0: case 0xe1: case 0xe2: // LOOPc cb 545 | outs(d, opcode == 0xe0? "loopne": 546 | opcode == 0xe1? "loope": "loop", JMP|SBYTE); 547 | break; 548 | case 0xe3: // JCXZ cb 549 | outs(d, "jcxz", JMP|SBYTE); 550 | break; 551 | case 0xe8: // CALL cw 552 | outs(d, "call", JMP|WORD); 553 | break; 554 | case 0xe9: // JMP cw 555 | outs(d, "jmp", JMP|WORD); 556 | break; 557 | case 0xea: // JMP cp 558 | outs(d, "ljmp", JMP|DWORD); 559 | break; 560 | case 0xeb: // JMP cb 561 | outs(d, "jmp", JMP|SBYTE); 562 | break; 563 | case 0xe4: case 0xe5: // IN ib 564 | case 0xe6: case 0xe7: // OUT ib 565 | outs(d, (opcode & 2)? "out": "in", IMM|BYTE|ACC); 566 | break; 567 | case 0xec: case 0xed: // IN dx 568 | case 0xee: case 0xef: // OUT dx 569 | d_modRM = 0xd0; 570 | outs(d, (opcode & 2)? "out": "in", OPS2); 571 | break; 572 | case 0xf0: // LOCK 573 | outs(d, "lock", 0); 574 | break; 575 | case 0xf2: // REPNZ 576 | //prefix = true; 577 | outs(d, "repnz ", 0); 578 | break; 579 | case 0xf3: // REPZ 580 | //prefix = true; 581 | outs(d, "repz ", 0); 582 | break; 583 | case 0xf4: // HLT 584 | outs(d, "hlt", 0); 585 | break; 586 | case 0xf5: // CMC 587 | outs(d, "cmc", 0); 588 | break; 589 | case 0xf6: case 0xf7: // math rmv 590 | d_modRM = d_fetchByte(d); 591 | switch (d_modRMReg()) { 592 | case 0: case 1: // TEST rmv,iv 593 | outs(d, "test", BW|IMM|RM); 594 | break; 595 | case 2: // NOT iv 596 | outs(d, "not", BW|RM); 597 | break; 598 | case 3: // NEG iv 599 | outs(d, "neg", BW|RM); 600 | break; 601 | case 4: // MUL rmv 602 | outs(d, "mul", BW|RM); 603 | break; 604 | case 5: // IMUL rmv 605 | outs(d, "imul", BW|RM); 606 | break; 607 | case 6: // DIV rmv 608 | outs(d, "div", BW|RM); 609 | break; 610 | case 7: // IDIV rmv 611 | outs(d, "idiv", BW|RM); 612 | break; 613 | } 614 | break; 615 | case 0xf8: case 0xf9: // STC/CLC 616 | outs(d, wordSize? "stc": "clc", 0); 617 | break; 618 | case 0xfa: case 0xfb: // STI/CLI 619 | outs(d, wordSize? "sti": "cli", 0); 620 | break; 621 | case 0xfc: case 0xfd: // STD/CLD 622 | outs(d, wordSize? "std": "cld", 0); 623 | break; 624 | case 0xfe: case 0xff: // misc 625 | d_modRM = d_fetchByte(d); 626 | if ((!wordSize && d_modRMReg() >= 2 && d_modRMReg() <= 6) || 627 | d_modRMReg() == 7) { 628 | outs(d, "???", 0); 629 | } else switch (d_modRMReg()) { 630 | case 0: // inc rmv 631 | outs(d, "inc", BW|RM); 632 | break; 633 | case 1: // dec rmv 634 | outs(d, "dec", BW|RM); 635 | break; 636 | case 2: // CALL rmv 637 | outs(d, "call", RM); 638 | break; 639 | case 3: // CALL mp 640 | outs(d, "lcallw", JMP|WORD); 641 | break; 642 | case 4: // JMP rmw 643 | outs(d, "jmp", RM); 644 | break; 645 | case 5: // JMP mp 646 | outs(d, "ljmpw", JMP|WORD); 647 | break; 648 | case 6: // PUSH rmw 649 | outs(d, "push", RM); 650 | break; 651 | } 652 | break; 653 | } 654 | //} while (prefix == true); 655 | } 656 | -------------------------------------------------------------------------------- /disasm.h: -------------------------------------------------------------------------------- 1 | #ifndef DISASM_H_ 2 | #define DISASM_H_ 3 | /* ELKS disassembler header file */ 4 | 5 | #ifndef noinstrument 6 | #define noinstrument __attribute__((no_instrument_function)) 7 | #endif 8 | 9 | struct dis { 10 | unsigned int cs; 11 | unsigned int ip; 12 | unsigned int ds; 13 | unsigned int flags; 14 | unsigned int oplen; 15 | unsigned int col; 16 | int (*getbyte)(int, int); 17 | char *s; 18 | struct exe *e; 19 | char buf[128]; 20 | }; 21 | 22 | /* disassembler flags */ 23 | #define fDisCS 0x0001 /* show CS: value */ 24 | #define fDisIP 0x0002 /* show IP address */ 25 | #define fDisAddr 0x0004 /* show linear address */ 26 | #define fDisBytes 0x0008 /* show byte codes */ 27 | #define fDisOctal 0x0010 /* use octal for byte codes */ 28 | #define fDisInst 0x0020 /* show instruction */ 29 | #define fDisAsmSource 0x0040 /* output gnu compatible 'as' input */ 30 | 31 | /* disasm.c */ 32 | // use unsigned! 33 | int disasm(struct dis *d, int cs, int ip, int (*nextbyte)(int, int), int ds, int flags); 34 | 35 | /* to be defined by caller of disasm() */ 36 | char * noinstrument getsymbol(struct dis *d, int seg, int offset); 37 | char * noinstrument getsegsymbol(struct dis *d, int seg); 38 | 39 | #endif /* DISASM_H */ 40 | -------------------------------------------------------------------------------- /discolor.c: -------------------------------------------------------------------------------- 1 | /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ 2 | │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ 3 | ╞══════════════════════════════════════════════════════════════════════════════╡ 4 | │ Copyright 2022 Justine Alexandra Roberts Tunney │ 5 | │ │ 6 | │ Permission to use, copy, modify, and/or distribute this software for │ 7 | │ any purpose with or without fee is hereby granted, provided that the │ 8 | │ above copyright notice and this permission notice appear in all copies. │ 9 | │ │ 10 | │ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ 11 | │ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ 12 | │ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ 13 | │ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ 14 | │ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ 15 | │ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ 16 | │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ 17 | │ PERFORMANCE OF THIS SOFTWARE. │ 18 | ╚─────────────────────────────────────────────────────────────────────────────*/ 19 | #include 20 | #include 21 | 22 | #include "discolor.h" 23 | 24 | #if BLINK16 25 | struct highlight g_high = { 26 | .enabled = 1, 27 | .active = 0, 28 | //.keyword = 155, 29 | .keyword = 40, 30 | .reg = 215, 31 | .literal = 182, 32 | //.label = 221, 33 | .label = 1, 34 | .comment = 112, 35 | .quote = 215, 36 | .grey = 241, 37 | .symbol = 1 38 | }; 39 | 40 | #else 41 | struct highlight g_high = { 42 | .enabled = 1, 43 | .active = 0, 44 | //.keyword = 40, // instruction 45 | //.reg = 215, // register 46 | //.literal = 182, // literal 47 | //.label = 221 48 | .label = 1, // label: 49 | .comment = 112, 50 | .quote = 215, 51 | .grey = 241, // op bytes 52 | .symbol = 1, // symbol 53 | }; 54 | #endif 55 | 56 | char *highStart(char *p, int h) { 57 | if (g_high.enabled) { 58 | if (h == 1) { /* special case for bold */ 59 | p = stpcpy(p, "\033[1m"); 60 | g_high.active = 2; 61 | } else if (h) { 62 | p = stpcpy(p, "\033[38;5;"); 63 | p += snprintf(p, 12, "%u", h); 64 | p = stpcpy(p, "m"); 65 | g_high.active = 1; 66 | } 67 | } 68 | return p; 69 | } 70 | 71 | char *highEnd(char *p) { 72 | if (g_high.enabled) { 73 | if (g_high.active == 2) { 74 | p = stpcpy(p, "\033[m"); 75 | } else if (g_high.active) { 76 | p = stpcpy(p, "\033[39m"); 77 | } 78 | g_high.active = 0; 79 | } 80 | return p; 81 | } 82 | 83 | char *colorInst(struct dis *d, char *str) 84 | { 85 | static char buf[256]; 86 | int i, startbytes = 0, startinst = 0; 87 | char *p = buf; 88 | 89 | if (d->flags & fDisAddr) 90 | startbytes += 6; 91 | if (d->flags & fDisCS) 92 | startbytes += 5; 93 | if (d->flags & fDisIP) 94 | startbytes += 5; 95 | startinst = startbytes; 96 | if (d->flags & fDisBytes) 97 | startinst += 6 * ((d->flags & fDisOctal)? 4: 3) - 1; 98 | 99 | for (i=0; iflags & fDisBytes) { 102 | p = highStart(p, g_high.grey); 103 | do { 104 | *p++ = *str++; 105 | } while (i++ < startinst); 106 | p = highEnd(p); 107 | } 108 | p = highStart(p, g_high.keyword); 109 | do { 110 | *p++ = *str++; 111 | } while (*str && *str != ' '); 112 | p = highEnd(p); 113 | 114 | while (*str) { 115 | switch (*str) { 116 | case '%': 117 | p = highStart(p, g_high.reg); 118 | *p++ = *str++; 119 | *p++ = *str++; 120 | *p++ = *str++; 121 | p = highEnd(p); 122 | break; 123 | case '$': 124 | *p++ = *str++; 125 | p = highStart(p, g_high.literal); 126 | do { 127 | *p++ = *str++; 128 | } while (*str && *str != ','); 129 | p = highEnd(p); 130 | break; 131 | default: 132 | *p++ = *str++; 133 | break; 134 | } 135 | } 136 | *p = '\0'; 137 | return buf; 138 | } 139 | -------------------------------------------------------------------------------- /discolor.h: -------------------------------------------------------------------------------- 1 | #ifndef COLORINST_H_ 2 | #define COLORINST_H_ 3 | #include "disasm.h" 4 | 5 | struct highlight { 6 | int enabled; 7 | int active; 8 | unsigned char keyword; 9 | unsigned char reg; 10 | unsigned char literal; 11 | unsigned char label; 12 | unsigned char comment; 13 | unsigned char quote; 14 | unsigned char grey; 15 | unsigned char symbol; 16 | }; 17 | 18 | extern struct highlight g_high; 19 | 20 | char *highStart(char *, int); 21 | char *highEnd(char *); 22 | char *colorInst(struct dis *d, char *str); 23 | 24 | #endif /* COLORINST_H_ */ 25 | -------------------------------------------------------------------------------- /dissim.c: -------------------------------------------------------------------------------- 1 | /* symbol string functions for disasm */ 2 | 3 | #include 4 | #include 5 | #include "disasm.h" 6 | #include "discolor.h" 7 | #include "syms.h" 8 | #include "exe.h" 9 | 10 | char * getsymbol(struct dis *d, int seg, int offset) 11 | { 12 | char *p; 13 | struct exe *e = d->e; 14 | static char buf[64]; 15 | 16 | if (e && e->syms) { 17 | if (seg != 0 && seg == e->dataseg) { 18 | p = highStart(buf, g_high.symbol); 19 | p = stpcpy(p, sym_data_symbol(e, offset, 1)); 20 | p = highEnd(p); 21 | *p = '\0'; 22 | return buf; 23 | } 24 | if (seg != 0 && seg == e->ftextseg) { 25 | p = highStart(buf, g_high.symbol); 26 | p = stpcpy(p, sym_ftext_symbol(e, offset, 1)); 27 | p = highEnd(p); 28 | *p = '\0'; 29 | return buf; 30 | } 31 | //if (seg == e->textseg) { 32 | p = highStart(buf, g_high.symbol); 33 | p = stpcpy(p, sym_text_symbol(e, offset, 1)); 34 | p = highEnd(p); 35 | *p = '\0'; 36 | return buf; 37 | //} 38 | } 39 | 40 | sprintf(buf, "0x%04x", offset); 41 | return buf; 42 | } 43 | 44 | char * getsegsymbol(struct dis *d, int seg) 45 | { 46 | struct exe *e = d->e; 47 | static char buf[8]; 48 | 49 | if (e && e->syms) { 50 | if (seg == e->textseg) 51 | return ".text"; 52 | if (seg != 0 && seg == e->ftextseg) 53 | return ".fartext"; 54 | if (seg != 0 && seg == e->dataseg) 55 | return ".data"; 56 | } 57 | 58 | sprintf(buf, "0x%04x", seg); 59 | return buf; 60 | } 61 | -------------------------------------------------------------------------------- /exe.h: -------------------------------------------------------------------------------- 1 | #ifndef EXE_H_ 2 | #define EXE_H_ 3 | /* ELKS a.out and DOS MZ headers */ 4 | 5 | #include 6 | 7 | /* minimal ELKS header */ 8 | struct minix_exec_hdr { 9 | uint32_t type; 10 | uint8_t hlen; // 0x04 11 | uint8_t reserved1; 12 | uint16_t version; 13 | uint32_t tseg; // 0x08 14 | uint32_t dseg; // 0x0c 15 | uint32_t bseg; // 0x10 16 | uint32_t entry; 17 | uint16_t chmem; 18 | uint16_t minstack; 19 | uint32_t syms; 20 | }; 21 | 22 | /* ELKS optional fields */ 23 | struct elks_supl_hdr { 24 | uint32_t msh_trsize; /* text relocation size */ // 0x20 25 | uint32_t msh_drsize; /* data relocation size */ // 0x24 26 | uint32_t msh_tbase; /* text relocation base */ 27 | uint32_t msh_dbase; /* data relocation base */ 28 | uint32_t esh_ftseg; /* far text size */ // 0x30 29 | uint32_t esh_ftrsize; /* far text relocation size */ // 0x34 30 | uint16_t esh_compr_tseg; /* compressed tseg size */ 31 | uint16_t esh_compr_dseg; /* compressed dseg size* */ 32 | uint16_t esh_compr_ftseg; /* compressed ftseg size*/ 33 | uint16_t esh_reserved; 34 | }; 35 | 36 | struct minix_reloc { 37 | uint32_t r_vaddr; /* address of place within section */ 38 | uint16_t r_symndx; /* index into symbol table */ // 0x04 39 | uint16_t r_type; /* relocation type */ // 0x06 40 | }; 41 | 42 | struct image_dos_header { // DOS .EXE header 43 | uint16_t e_magic; // Magic number // 0x00 44 | uint16_t e_cblp; // Bytes on last page of file // 0x02 45 | uint16_t e_cp; // Pages in file // 0x04 46 | uint16_t e_crlc; // Relocations // 0x06 47 | uint16_t e_cparhdr; // Size of header in paragraphs // 0x08 48 | uint16_t e_minalloc; // Minimum extra paragraphs needed 49 | uint16_t e_maxalloc; // Maximum extra paragraphs needed 50 | uint16_t e_ss; // Initial (relative) SS value // 0x0e 51 | uint16_t e_sp; // Initial SP value // 0x10 52 | uint16_t e_csum; // Checksum 53 | uint16_t e_ip; // Initial IP value // 0x14 54 | uint16_t e_cs; // Initial (relative) CS value // 0x16 55 | uint16_t e_lfarlc; // File address of relocation table // 0x18 56 | uint16_t e_ovno; // Overlay number 57 | uint16_t e_res[4]; // Reserved words 58 | uint16_t e_oemid; // OEM identifier (for e_oeminfo) 59 | uint16_t e_oeminfo; // OEM information; e_oemid specific 60 | uint16_t e_res2[10]; // Reserved words 61 | uint32_t e_lfanew; // File address of new exe header 62 | }; 63 | 64 | struct dos_reloc { // DOS relocation table entry 65 | uint16_t r_offset; // Offset of segment to reloc from r_seg 66 | uint16_t r_seg; // Segment relative to load segment 67 | }; 68 | 69 | struct exe { 70 | struct minix_exec_hdr aout; 71 | struct elks_supl_hdr eshdr; 72 | struct image_dos_header dos; 73 | int (*checkStack)(struct exe *e); 74 | int (*handleSyscall)(struct exe *e, int intno); 75 | /* disassembly */ 76 | unsigned char * syms; /* symbol table */ 77 | uint16_t textseg; /* text and data segments */ 78 | uint16_t ftextseg; 79 | uint16_t dataseg; 80 | /* break management */ 81 | uint16_t t_endseg; /* end of data segment (data+bss+heap+stack) */ 82 | uint16_t t_begstack; /* start SP */ 83 | uint16_t t_minstack; /* min stack size */ 84 | uint16_t t_enddata; /* start heap = end of data+bss */ 85 | uint16_t t_endbrk; /* current break (end of heap) */ 86 | 87 | /* stack overflow check */ 88 | uint32_t t_stackLow; /* lowest SS:SP allowed */ 89 | }; 90 | 91 | #define ELKSMAGIC 0x0301 /* magic number for ELKS executables */ 92 | #define DOSMAGIC 0x5a4d /* magic number for DOS MZ executables */ 93 | 94 | /* loader entry points */ 95 | void loadExecutableElks(struct exe *e, const char *filename, int argc, char **argv, char **envp); 96 | void loadExecutableDOS(struct exe *e, const char *filename, int argc, char **argv, char **envp); 97 | void loadExecutableBinary(struct exe *e, const char *filename, int argc, char **argv, char **envp); 98 | 99 | #endif /* EXE_H_ */ 100 | -------------------------------------------------------------------------------- /loader-bin.c: -------------------------------------------------------------------------------- 1 | /* 2 | * boot block binary loader for 8086 emulator 3 | * 4 | * Greg Haerr 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "8086.h" 15 | #include "exe.h" 16 | 17 | static void loadError(const char *msg, ...) 18 | { 19 | va_list args; 20 | va_start(args, msg); 21 | vfprintf(stderr, msg, args); 22 | va_end(args); 23 | exit(1); 24 | } 25 | 26 | int checkStackBinary(struct exe *e) 27 | { 28 | return 0; 29 | } 30 | 31 | int handleSyscallBinary(struct exe *e, int intno) 32 | { 33 | return 0; 34 | } 35 | 36 | void loadExecutableBinary(struct exe *e, const char *path, int argc, char **argv, char **envp) 37 | { 38 | struct stat sbuf; 39 | 40 | int fd = open(path, O_RDONLY); 41 | if (fd < 0) 42 | loadError("Can't open %s\n", path); 43 | if (fstat(fd, &sbuf) < 0) 44 | loadError("Can't stat %s\n", path); 45 | size_t filesize = sbuf.st_size; 46 | Word loadSegment = 0x07c0; 47 | int loadOffset = loadSegment << 4; 48 | if (filesize > RAMSIZE - loadOffset) 49 | loadError("Not enough memory to load %s, needs %d bytes have %d\n", 50 | path, filesize, RAMSIZE); 51 | if (read(fd, &ram[loadOffset], filesize) != filesize) 52 | loadError("Error reading executable: %s\n", path); 53 | close(fd); 54 | 55 | setES(0x0000); 56 | setShadowFlags(0, ES, 0x10000, fRead|fWrite); 57 | setES(0x1000); 58 | setShadowFlags(0, ES, 0x10000, fRead|fWrite); 59 | setES(0xB000); 60 | setShadowFlags(0, ES, 0x20000, fRead|fWrite); 61 | 62 | setES(0x0000); 63 | setDS(0x0000); 64 | setSS(0x0000); 65 | setSP(0x0000); 66 | setCS(0x0000); 67 | setIP(0x7c00); 68 | setAX(0x0000); 69 | setBX(0x0000); 70 | setCX(0x0000); 71 | setDX(0x0000); 72 | setBP(0x0000); 73 | setSI(0x0000); 74 | setDI(0x0000); 75 | setFlags(0xF202); /* Interrupts enabled and 8086 reserved bits on */ 76 | 77 | e->handleSyscall = handleSyscallBinary; 78 | e->checkStack = checkStackBinary; 79 | } 80 | -------------------------------------------------------------------------------- /loader-dos.c: -------------------------------------------------------------------------------- 1 | /* DOS executable loader for 8086 emulator */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "8086.h" 11 | #include "exe.h" 12 | 13 | extern int f_verbose; 14 | Word loadSegment; // FIXME remove as global 15 | 16 | static void loadError(const char *msg, ...) 17 | { 18 | va_list args; 19 | va_start(args, msg); 20 | vfprintf(stderr, msg, args); 21 | va_end(args); 22 | exit(1); 23 | } 24 | 25 | static void write_environ(int argc, char **argv, char **envp) 26 | { 27 | int envSegment = loadSegment - 0x10 - 0x0c; 28 | char *filename = argv[0]; 29 | int i; 30 | 31 | /* prepare environment segment */ 32 | setES(envSegment); 33 | setShadowFlags(0, ES, 0xc0, fRead); 34 | writeByte(0x00, 0, ES); // No environment for now 35 | writeWord(0x0001, 1, ES); 36 | for (i = 0; filename[i] != 0; ++i) { 37 | writeByte(filename[i], i + 3, ES); 38 | if (i + 4 >= 0xc0) 39 | loadError("Program name too long\n"); 40 | } 41 | writeWord(0x0000, i + 3, ES); 42 | 43 | /* prepare PSP */ 44 | setES(loadSegment - 0x10); 45 | setShadowFlags(0, ES, 0x0100, fRead); 46 | writeWord(0x9fff, 2, ES); 47 | writeWord(envSegment, 0x2c, ES); 48 | i = 0x81; 49 | for (int a = 2; a < argc; ++a) { 50 | if (a > 2) 51 | writeByte(' ', i++, ES); 52 | 53 | char* arg = argv[a]; 54 | int quote = strchr(arg, ' ') != 0; 55 | if (quote) 56 | writeByte('\"', i++, ES); 57 | 58 | for (; *arg != 0; ++arg) { 59 | if (*arg == '\"') 60 | writeByte('\\', i++, ES); 61 | writeByte(*arg, i++, ES); 62 | } 63 | if (quote) 64 | writeByte('\"', i++, ES); 65 | if (i > 0xff) 66 | loadError("Arguments too long\n"); 67 | } 68 | writeByte('\r', i, ES); 69 | writeByte(i - 0x81, 0x80, ES); 70 | } 71 | 72 | static void load_bios_values(void) 73 | { 74 | // Fill up parts of the interrupt vector table, the BIOS clock tick count, 75 | // and parts of the BIOS ROM area with stuff, for the benefit of the far 76 | // pointer tests. 77 | setES(0x0000); 78 | setShadowFlags(0x0080, ES, 0x0004, fRead); 79 | writeWord(0x0000, 0x0080, ES); 80 | writeWord(0xFFFF, 0x0082, ES); 81 | setShadowFlags(0x0400, ES, 0x00FF, fRead); 82 | writeWord(0x0058, 0x046C, ES); 83 | writeWord(0x000C, 0x046E, ES); 84 | writeByte(0x00, 0x0470, ES); 85 | setES(0xF000); 86 | setShadowFlags(0xFF00, ES, 0x0100, fRead); 87 | for (int i = 0; i < 0x100; i += 2) 88 | writeWord(0xF4F4, 0xFF00 + i, ES); 89 | // We need some variety in the ROM BIOS content... 90 | writeByte(0xEA, 0xFFF0, ES); 91 | writeWord(0xFFF0, 0xFFF1, ES); 92 | writeWord(0xF000, 0xFFF3, ES); 93 | } 94 | 95 | void loadExecutableDOS(struct exe *e, const char *path, int argc, char **argv, char **envp) 96 | { 97 | struct stat sbuf; 98 | 99 | int fd = open(path, O_RDONLY); 100 | if (fd < 0) 101 | loadError("Can't open %s\n", path); 102 | if (fstat(fd, &sbuf) < 0) 103 | loadError("Can't stat %s\n", path); 104 | size_t filesize = sbuf.st_size; 105 | 106 | loadSegment = 0x1000; 107 | int loadOffset = loadSegment << 4; 108 | if (filesize > RAMSIZE - loadOffset) 109 | loadError("Not enough memory to load %s, needs %d bytes have %d\n", 110 | path, filesize, RAMSIZE); 111 | if (read(fd, &ram[loadOffset], filesize) != filesize) 112 | loadError("Error reading executable: %s\n", path); 113 | close(fd); 114 | 115 | write_environ(argc, argv, envp); 116 | struct image_dos_header *hdr = (struct image_dos_header *)&ram[loadOffset]; 117 | if (filesize >= 2 && hdr->e_magic == DOSMAGIC) { // .exe file? 118 | if (filesize < 0x21) 119 | loadError("%s is too short to be an .exe file\n", path); 120 | Word bytesInLastBlock = hdr->e_cblp; 121 | int exeLength = ((hdr->e_cp - (bytesInLastBlock == 0 ? 0 : 1)) << 9) 122 | + bytesInLastBlock; 123 | Word headerParagraphs = hdr->e_cparhdr; 124 | Word headerLength = headerParagraphs << 4; 125 | if (exeLength > filesize || headerLength > filesize || headerLength > exeLength) 126 | loadError("%s is corrupt\n", path); 127 | Word imageSegment = loadSegment + headerParagraphs; 128 | struct dos_reloc *r = (struct dos_reloc *)&ram[loadOffset+hdr->e_lfarlc]; 129 | for (int i = 0; i < hdr->e_crlc; ++i) { 130 | Word offset = r->r_offset; 131 | setCS(imageSegment + r->r_seg); 132 | writeWord(readWordSeg(offset, CS) + imageSegment, offset, CS); 133 | r++; 134 | } 135 | setES(imageSegment); 136 | setShadowFlags(0, ES, exeLength - headerLength, fRead|fWrite); 137 | setES(loadSegment - 0x10); 138 | setDS(loadSegment - 0x10); 139 | setIP(hdr->e_ip); 140 | setCS(hdr->e_cs + imageSegment); 141 | Word ss = hdr->e_ss + imageSegment; 142 | setSS(ss); 143 | setSP(hdr->e_sp); 144 | e->t_stackLow = (((exeLength - headerLength + 15) >> 4) + imageSegment) << 4; 145 | if (e->t_stackLow < ((DWord)ss << 4) + 0x10) 146 | e->t_stackLow = ((DWord)ss << 4) + 0x10; 147 | if (e->t_stackLow > ((DWord)ss << 4) + sp()) /* disable for test.exe stub */ 148 | e->t_stackLow = 0; 149 | } else { 150 | if (filesize > 0xff00) 151 | loadError("%s is too long to be a .com file\n", path); 152 | setES(loadSegment); 153 | setShadowFlags(0, ES, filesize, fRead|fWrite); 154 | setES(loadSegment - 0x10); 155 | setDS(loadSegment); 156 | setSS(loadSegment); 157 | setSP(0xFFFE); 158 | setCS(loadSegment); 159 | setIP(0x0100); 160 | e->t_stackLow = ((DWord)loadSegment << 4) + filesize; 161 | } 162 | // Some testcases copy uninitialized stack data, so mark as initialized 163 | // any locations that could possibly be stack. 164 | //if (a < ((DWord)loadSegment << 4) - 0x100 && running) 165 | //bad = true; 166 | setShadowFlags(0, SS, sp(), fRead|fWrite); 167 | #if 0 168 | if (sp()) { 169 | Word d = 0; 170 | if (((DWord)ss() << 4) < stackLow) 171 | d = stackLow - ((DWord)ss() << 4); 172 | while (d < sp()) { 173 | writeByte(0, d, SS); 174 | ++d; 175 | } 176 | } else { 177 | Word d = 0; 178 | if (((DWord)ss() << 4) < stackLow) 179 | d = stackLow - ((DWord)ss() << 4); 180 | do { 181 | writeByte(0, d, SS); 182 | ++d; 183 | } while (d != 0); 184 | } 185 | #endif 186 | load_bios_values(); 187 | 188 | if (f_verbose) printf("CS:IP %04x:%04x DS %04x SS:SP %04x:%04x\n", 189 | cs(), getIP(), ds(), ss(), sp()); 190 | setES(loadSegment - 0x10); 191 | setAX(0x0000); 192 | setBX(0x0000); 193 | setCX(0x0000); 194 | setDX(0x0000); 195 | setBP(0x091C); 196 | setSI(0x0100); 197 | setDI(0xFFFE); 198 | setFlags(0xF202); /* Interrupts enabled and 8086 reserved bits on */ 199 | 200 | e->handleSyscall = handleSyscallDOS; 201 | e->checkStack = checkStackDOS; 202 | } 203 | -------------------------------------------------------------------------------- /loader-elks.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ELKS a.out executable loader for 8086 emulator 3 | * 4 | * Greg Haerr 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "8086.h" 15 | #include "exe.h" 16 | 17 | extern int f_verbose; 18 | 19 | static int calc_environ(int argc, char **argv, char **envp) 20 | { 21 | char **p; 22 | int argv_len=0, argv_count=0; 23 | int envp_len=0, envp_count=0; 24 | int stack_bytes; 25 | 26 | /* How much space for argv */ 27 | for(p=argv; p && *p && argv_len >= 0; p++) { 28 | argv_count++; 29 | argv_len += strlen(*p)+1; 30 | } 31 | 32 | /* How much space for envp */ 33 | for(p=envp; p && *p && envp_len >= 0; p++) { 34 | envp_count++; 35 | envp_len += strlen(*p)+1; 36 | } 37 | 38 | /* tot it all up */ 39 | stack_bytes = 2 /* argc */ 40 | + argv_count * 2 + 2 /* argv */ 41 | + argv_len 42 | + envp_count * 2 + 2 /* envp */ 43 | + envp_len; 44 | 45 | stack_bytes = (stack_bytes + 1) & ~1; 46 | return stack_bytes; 47 | } 48 | 49 | static void write_environ(int argc, char **argv, char **envp) 50 | { 51 | char **p; 52 | int argv_len=0, argv_count=0; 53 | int envp_len=0, envp_count=0; 54 | int stack_bytes; 55 | int pip; 56 | int pcp, baseoff; 57 | 58 | /* How much space for argv */ 59 | for(p=argv; p && *p && argv_len >= 0; p++) { 60 | argv_count++; 61 | argv_len += strlen(*p)+1; 62 | } 63 | 64 | /* How much space for envp */ 65 | for(p=envp; p && *p && envp_len >= 0; p++) { 66 | envp_count++; 67 | envp_len += strlen(*p)+1; 68 | } 69 | 70 | /* tot it all up */ 71 | stack_bytes = 2 /* argc */ 72 | + argv_count * 2 + 2 /* argv */ 73 | + argv_len 74 | + envp_count * 2 + 2 /* envp */ 75 | + envp_len; 76 | 77 | if (f_verbose) 78 | printf("argv = (%d,%d), envp=(%d,%d), size=0x%x\n", 79 | argv_count, argv_len, envp_count, envp_len, stack_bytes); 80 | 81 | stack_bytes = (stack_bytes + 1) & ~1; 82 | 83 | setSP(sp() - stack_bytes); 84 | int stk_ptr = sp(); 85 | 86 | /* Now copy in the strings */ 87 | pip = stk_ptr; 88 | pcp = stk_ptr+2*(1+argv_count+1+envp_count+1); 89 | 90 | /* baseoff = stk_ptr + stack_bytes; */ 91 | /* baseoff = stk_ptr; */ 92 | baseoff = 0; 93 | writeWord(argv_count, pip, SS); pip += 2; 94 | for(p=argv; p && *p; p++) { 95 | writeWord(pcp-baseoff, pip, SS); pip += 2; 96 | int n = strlen(*p)+1; 97 | for (int i = 0; iaout, sizeof(e->aout)) != sizeof(e->aout)) 135 | loadError("Can't read header: %s\n", path); 136 | if ((e->aout.type & 0xFFFF) != ELKSMAGIC) 137 | loadError("%s: not ELKS executable\n", path); 138 | if (e->aout.hlen != sizeof(e->aout)) 139 | loadError("Medium model programs not yet supported: %s\n", path); 140 | if (e->aout.version != 1) 141 | loadError("Version 0 header programs not yet supported: %s\n", path); 142 | if (fstat(fd, &sbuf) < 0) 143 | loadError("Can't stat %s\n", path); 144 | size_t filesize = sbuf.st_size - e->aout.syms - e->aout.hlen; /* text+data */ 145 | 146 | loadSegment = 0x1000; 147 | int loadOffset = loadSegment << 4; 148 | if (filesize > RAMSIZE - loadOffset) 149 | loadError("Not enough memory to load %s, needs %d bytes have %d\n", 150 | path, filesize, RAMSIZE); 151 | if (read(fd, &ram[loadOffset], filesize) != filesize) 152 | loadError("Error reading executable: %s\n", path); 153 | close(fd); 154 | 155 | unsigned int tseg = e->aout.tseg; 156 | tseg = (tseg + 15) & ~15; /* not strictly necessary */ 157 | unsigned int dseg = e->aout.dseg; 158 | unsigned int bseg = e->aout.bseg; 159 | unsigned int stack = e->aout.minstack? e->aout.minstack: 0x1000; 160 | unsigned int slen = calc_environ(argc, argv, envp); 161 | unsigned int len = dseg + bseg + stack + slen; 162 | unsigned int heap = e->aout.chmem? e->aout.chmem: 0x1000; 163 | if (heap >= 0xFFF0) { /* max heap specified */ 164 | if (len < 0xFFF0) 165 | len = 0xFFF0; 166 | } else { 167 | len += heap; 168 | } 169 | len = (len + 15) & ~15; 170 | if (f_verbose) 171 | printf("tseg %04x dseg %04x bseg %04x heap %04x stack %04x totdata %04x\n", 172 | tseg, dseg, bseg, heap, stack, len); 173 | if (len > 0xFFFF) 174 | loadError("Program heap+stack >= 64K: %s\n", path); 175 | 176 | setES(loadSegment); 177 | setShadowFlags(0, ES, tseg, fRead); /* text read-only */ 178 | setES(loadSegment + (tseg >> 4)); 179 | setSS(es()); /* SS just after text segment */ 180 | setDS(ss()); /* DS = SS */ 181 | //FIXME don't allow stack reads before written 182 | //FIXME don't allow use of area outside break 183 | setShadowFlags(0, ES, len, fRead|fWrite); 184 | 185 | setCS(loadSegment); 186 | setIP(e->aout.entry & 0xffff); 187 | 188 | e->t_endseg = len; 189 | e->t_begstack = len - slen; 190 | e->t_minstack = stack; 191 | e->t_enddata = dseg + bseg; 192 | e->t_endbrk = e->t_enddata; /* current break is end of data+bss */ 193 | e->t_begstack &= ~1; /* even SP */ 194 | if (e->t_endbrk & 1) /* even heap start */ 195 | e->t_endbrk++; 196 | setSP(e->t_begstack); 197 | e->t_stackLow = (ss() << 4) + e->t_begstack - e->t_minstack; 198 | 199 | write_environ(argc, argv, envp); 200 | if (f_verbose) 201 | printf("Text %04x Data %04x Stack %04x\n", tseg, len-stack, stack); 202 | 203 | //hexdump(sp(), &ram[physicalAddress(sp(), SS, false)], stack-sp(), 0); 204 | //for (int i=dseg; ihandleSyscall = handleSyscallElks; 222 | e->checkStack = checkStackElks; 223 | } 224 | -------------------------------------------------------------------------------- /opcodes.S: -------------------------------------------------------------------------------- 1 | .code16 2 | .text 3 | .global _start 4 | #define invop .byte 5 | //#define invop # 6 | 7 | _start: 8 | // 00 9 | add %dl,0x9090(%bx) 10 | add %dx,0x9090(%bx) 11 | add 0x9090(%bx),%dl 12 | add 0x9090(%bx),%dx 13 | add $0x90,%al 14 | add $0x9090,%ax 15 | push %es 16 | pop %es 17 | or %dl,0x9090(%bx) 18 | or %dx,0x9090(%bx) 19 | or 0x9090(%bx),%dl 20 | or 0x9090(%bx),%dx 21 | or $0x90,%al 22 | or $0x9090,%ax 23 | push %cs 24 | invop 0x0f 25 | 26 | // 10 27 | adc %dl,0x9090(%bx) 28 | adc %dx,0x9090(%bx) 29 | adc 0x9090(%bx),%dl 30 | adc 0x9090(%bx),%dx 31 | adc $0x90,%al 32 | adc $0x9090,%ax 33 | push %ss 34 | pop %ss 35 | sbb %dl,0x9090(%bx) 36 | sbb %dx,0x9090(%bx) 37 | sbb 0x9090(%bx),%dl 38 | sbb 0x9090(%bx),%dx 39 | sbb $0x90,%al 40 | sbb $0x9090,%ax 41 | push %ds 42 | pop %ds 43 | 44 | // 20 45 | and %dl,0x9090(%bx) 46 | and %dx,0x9090(%bx) 47 | and 0x9090(%bx),%dl 48 | and 0x9090(%bx),%dx 49 | and $0x90,%al 50 | and $0x9090,%ax 51 | es 52 | daa 53 | sub %dl,0x9090(%bx) 54 | sub %dx,0x9090(%bx) 55 | sub 0x9090(%bx),%dl 56 | sub 0x9090(%bx),%dx 57 | sub $0x90,%al 58 | sub $0x9090,%ax 59 | cs 60 | das 61 | 62 | // 30 63 | xor %dl,0x9090(%bx) 64 | xor %dx,0x9090(%bx) 65 | xor 0x9090(%bx),%dl 66 | xor 0x9090(%bx),%dx 67 | xor $0x90,%al 68 | xor $0x9090,%ax 69 | ss 70 | aaa 71 | cmp %dl,0x9090(%bx) 72 | cmp %dx,0x9090(%bx) 73 | cmp 0x9090(%bx),%dl 74 | cmp 0x9090(%bx),%dx 75 | cmp $0x90,%al 76 | cmp $0x9090,%ax 77 | ds 78 | aas 79 | 80 | // 40 81 | inc %ax 82 | inc %cx 83 | inc %dx 84 | inc %bx 85 | inc %sp 86 | inc %bp 87 | inc %si 88 | inc %di 89 | dec %ax 90 | dec %cx 91 | dec %dx 92 | dec %bx 93 | dec %sp 94 | dec %bp 95 | dec %si 96 | dec %di 97 | 98 | // 50 99 | push %ax 100 | push %cx 101 | push %dx 102 | push %bx 103 | push %sp 104 | push %bp 105 | push %si 106 | push %di 107 | pop %ax 108 | pop %cx 109 | pop %dx 110 | pop %bx 111 | pop %sp 112 | pop %bp 113 | pop %si 114 | pop %di 115 | 116 | // 60 117 | invop 0x60 118 | invop 0x61 119 | invop 0x62 120 | invop 0x63 121 | invop 0x64 122 | invop 0x65 123 | invop 0x66 124 | invop 0x67 125 | invop 0x68 126 | invop 0x69 127 | invop 0x6a 128 | invop 0x6b 129 | invop 0x6c 130 | invop 0x6d 131 | invop 0x6e 132 | invop 0x6f 133 | //pusha 134 | //popa 135 | //bound %dx,0x9090(%bx) 136 | //arpl %dx,0x9090(%bx) 137 | //push $0x9090 138 | //imul $0x9090,0x9090(%bx),%dx 139 | //push $0xff90 140 | //imul $0xff90,0x9090(%bx),%dx 141 | //insb (%dx),%es:(%di) 142 | //insw (%dx),%es:(%di) 143 | //outsb %ds:(%si),(%dx) 144 | //outsw %ds:(%si),(%dx) 145 | 146 | // 70 147 | jo .+2-0x70 148 | jno .+2-0x70 149 | jb .+2-0x70 150 | jae .+2-0x70 151 | je .+2-0x70 152 | jne .+2-0x70 153 | jbe .+2-0x70 154 | ja .+2-0x70 155 | js .+2-0x70 156 | jns .+2-0x70 157 | jp .+2-0x70 158 | jnp .+2-0x70 159 | jl .+2-0x70 160 | jge .+2-0x70 161 | jle .+2-0x70 162 | jg .+2-0x70 163 | 164 | // 80 165 | adcb $0x90,0x9090(%bx) 166 | adcw $0x9090,0x9090(%bx) 167 | // 82 is same as 80 for 8086? 168 | invop 0x82,0x87,0x90,0x90,0x90 // disassembles as addb $0x90,0x9090(%bx) 169 | //addb $0x90,0x9090(%bx) // generates 0x80 opcode 170 | adcw $0xff90,0x9090(%bx) 171 | test %dl,0x9090(%bx) 172 | test %dx,0x9090(%bx) 173 | xchg %dl,0x9090(%bx) 174 | xchg %dx,0x9090(%bx) 175 | mov %dl,0x9090(%bx) 176 | mov %dx,0x9090(%bx) 177 | mov 0x9090(%bx),%dl 178 | mov 0x9090(%bx),%dx 179 | mov %ss,0x9090(%bx) 180 | lea 0x9090(%bx),%dx 181 | mov 0x9090(%bx),%ss 182 | pop 0x9090(%bx) 183 | 184 | // 90 185 | xchg %ax,%ax // FIXME display as NOP? 186 | xchg %ax,%cx 187 | xchg %ax,%dx 188 | xchg %ax,%bx 189 | xchg %ax,%sp 190 | xchg %ax,%bp 191 | xchg %ax,%si 192 | xchg %ax,%di 193 | cbtw 194 | cwtd 195 | lcall $0x9090,$0x9090 196 | fwait 197 | pushf 198 | popf 199 | sahf 200 | lahf 201 | 202 | // A0 203 | mov 0x9090,%al 204 | mov 0x9090,%ax 205 | mov %al,0x9090 206 | mov %ax,0x9090 207 | movsb %ds:(%si),%es:(%di) 208 | movsw %ds:(%si),%es:(%di) 209 | cmpsb %es:(%di),%ds:(%si) 210 | cmpsw %es:(%di),%ds:(%si) 211 | test $0x90,%al 212 | test $0x9090,%ax 213 | stos %al,%es:(%di) 214 | stos %ax,%es:(%di) 215 | lods %ds:(%si),%al 216 | lods %ds:(%si),%ax 217 | scas %es:(%di),%al 218 | scas %es:(%di),%ax 219 | 220 | // B0 221 | mov $0x90,%al 222 | mov $0x90,%cl 223 | mov $0x90,%dl 224 | mov $0x90,%bl 225 | mov $0x90,%ah 226 | mov $0x90,%ch 227 | mov $0x90,%dh 228 | mov $0x90,%bh 229 | mov $0x9090,%ax 230 | mov $0x9090,%cx 231 | mov $0x9090,%dx 232 | mov $0x9090,%bx 233 | mov $0x9090,%sp 234 | mov $0x9090,%bp 235 | mov $0x9090,%si 236 | mov $0x9090,%di 237 | 238 | // C0 239 | invop 0xc0 240 | invop 0xc1 241 | //rclb $0x90,0x9090(%bx) 242 | //rclw $0x90,0x9090(%bx) 243 | ret $0x9090 244 | ret 245 | les 0x9090(%bx),%dx 246 | lds 0x9090(%bx),%dx 247 | movb $0x90,0x9090(%bx) 248 | movw $0x9090,0x9090(%bx) 249 | invop 0xc8 250 | invop 0xc9 251 | //enter $0x9090,$0x90 252 | //leave 253 | lret $0x9090 254 | lret 255 | int3 256 | int $0x90 257 | into 258 | iret 259 | 260 | // D0 261 | rclb 0x9090(%bx) 262 | rclw 0x9090(%bx) 263 | rclb %cl,0x9090(%bx) 264 | rclw %cl,0x9090(%bx) 265 | rclw $1,0x9090(%bx) 266 | shl $1,%ax 267 | shl %cl,%ax 268 | shlb %cl,0x9090(%bx) 269 | shlw %cl,0x9090(%bx) 270 | aam $0x90 // FIXME disasm arg 271 | aad $0x90 // FIXME disasm arg 272 | //salc // undocumented STC in AL (0xd6) 273 | invop 0xd6 274 | xlat %ds:(%bx) 275 | invop 0xd8 276 | invop 0xd9 277 | invop 0xda 278 | invop 0xdb 279 | invop 0xdc 280 | invop 0xdd 281 | invop 0xde 282 | invop 0xdf 283 | 284 | // E0 285 | loopne .+2-0x70 286 | loope .+2-0x70 287 | loop .+2-0x70 288 | jcxz .+2-0x70 289 | in $0x90,%al 290 | in $0x90,%ax 291 | out %al,$0x90 292 | out %ax,$0x90 293 | call .+5+0x9090 294 | jmp .+5+0x9090 295 | ljmp $0x9090,$0x9090 296 | jmp .+2-0x70 297 | in (%dx),%al 298 | in (%dx),%ax 299 | out %al,(%dx) 300 | out %ax,(%dx) 301 | 302 | // F0 303 | lock 304 | invop 0xf1 305 | repnz 306 | repz 307 | hlt 308 | cmc 309 | notb 0x9090(%bx) 310 | test $0x90,%bl 311 | testb $0xa0,0x9090(%bx) 312 | negb 0x9090(%bx) 313 | neg %bl 314 | imul %bl 315 | imulb 0x9090(%bx) 316 | idiv %bl 317 | mul %bl 318 | div %bl 319 | testw $0xa0a0,0x9090(%bx) 320 | notw 0x9090(%bx) 321 | negw 0x9090(%bx) 322 | test $0x90,%bx 323 | neg %bx 324 | mul %bx 325 | imul %bx 326 | imulw 0x9090(%bx) 327 | div %bx 328 | idiv %bx 329 | clc 330 | stc 331 | cli 332 | sti 333 | cld 334 | std 335 | inc %al 336 | incb 0x9090(%bx) 337 | incw 0x9090(%bx) 338 | dec %al 339 | decw 0x9090(%bx) 340 | call *0x9090(%bx) 341 | push 0x0000 342 | push 0x7fff 343 | push 0x8000 344 | push 0x8001 345 | 346 | #if 0 347 | // syscall exit 348 | mov $1,%ax 349 | mov $0,%bx 350 | int $0x80 351 | 352 | push $0x0000 353 | push $0x7fff 354 | push $0x8000 355 | push $0x8001 356 | add %dx,0x9090(%bx) 357 | add 0x9090(%bx),%dx 358 | add $0x9090,%ax 359 | pushw %es 360 | popw %es 361 | or %dx,0x9090(%bx) 362 | or 0x9090(%bx),%dx 363 | or $0x9090,%ax 364 | pushw %cs 365 | adc %dx,0x9090(%bx) 366 | adc 0x9090(%bx),%dx 367 | adc $0x9090,%ax 368 | pushw %ss 369 | popw %ss 370 | sbb %dx,0x9090(%bx) 371 | sbb 0x9090(%bx),%dx 372 | sbb $0x9090,%ax 373 | pushw %ds 374 | popw %ds 375 | and %dx,0x9090(%bx) 376 | and 0x9090(%bx),%dx 377 | and $0x9090,%ax 378 | sub %dx,0x9090(%bx) 379 | sub 0x9090(%bx),%dx 380 | sub $0x9090,%ax 381 | xor %dx,0x9090(%bx) 382 | xor 0x9090(%bx),%dx 383 | xor $0x9090,%ax 384 | cmp %dx,0x9090(%bx) 385 | cmp 0x9090(%bx),%dx 386 | cmp $0x9090,%ax 387 | inc %ax 388 | inc %cx 389 | inc %dx 390 | inc %bx 391 | inc %sp 392 | inc %bp 393 | inc %si 394 | inc %di 395 | dec %ax 396 | dec %cx 397 | dec %dx 398 | dec %bx 399 | dec %sp 400 | dec %bp 401 | dec %si 402 | dec %di 403 | push %ax 404 | push %cx 405 | push %dx 406 | push %bx 407 | push %sp 408 | push %bp 409 | push %si 410 | push %di 411 | pop %ax 412 | pop %cx 413 | pop %dx 414 | pop %bx 415 | pop %sp 416 | pop %bp 417 | pop %si 418 | pop %di 419 | //bound %dx,0x9090(%bx) 420 | //pushw $0x9090 421 | //imul $0x9090,0x9090(%bx),%dx 422 | //pushw $0xff90 423 | //imul $0xff90,0x9090(%bx),%dx 424 | //insw (%dx),%es:(%di) 425 | //outsw %ds:(%si),(%dx) 426 | adcw $0x9090,0x9090(%bx) 427 | adcw $0xff90,0x9090(%bx) 428 | test %dx,0x9090(%bx) 429 | xchg %dx,0x9090(%bx) 430 | mov %dx,0x9090(%bx) 431 | mov 0x9090(%bx),%dx 432 | movw %ss,0x9090(%bx) 433 | lea 0x9090(%bx),%dx 434 | popw 0x9090(%bx) 435 | xchg %ax,%ax 436 | xchg %ax,%cx 437 | xchg %ax,%dx 438 | xchg %ax,%bx 439 | xchg %ax,%sp 440 | xchg %ax,%bp 441 | xchg %ax,%si 442 | xchg %ax,%di 443 | cbtw 444 | cwtd 445 | lcallw $0x9090,$0x9090 446 | pushf 447 | popf 448 | mov 0x9090,%ax 449 | mov %ax,0x9090 450 | movsw %ds:(%si),%es:(%di) 451 | cmpsw %es:(%di),%ds:(%si) 452 | test $0x9090,%ax 453 | stos %ax,%es:(%di) 454 | lods %ds:(%si),%ax 455 | scas %es:(%di),%ax 456 | mov $0x9090,%ax 457 | mov $0x9090,%cx 458 | mov $0x9090,%dx 459 | mov $0x9090,%bx 460 | mov $0x9090,%sp 461 | mov $0x9090,%bp 462 | mov $0x9090,%si 463 | mov $0x9090,%di 464 | rclw $0x90,0x9090(%bx) 465 | rclb $0x90,0x9090(%bx) 466 | retw $0x9090 467 | retw 468 | les 0x9090(%bx),%dx 469 | lds 0x9090(%bx),%dx 470 | movw $0x9090,0x9090(%bx) 471 | enterw $0x9090,$0x90 472 | leavew 473 | lretw $0x9090 474 | lretw 475 | iretw 476 | rclw 0x9090(%bx) 477 | rclw %cl,0x9090(%bx) 478 | in $0x90,%ax 479 | out %ax,$0x90 480 | callw .+3+0x9090 481 | ljmpw $0x9090,$0x9090 482 | in (%dx),%ax 483 | out %ax,(%dx) 484 | notw 0x9090(%bx) 485 | callw *0x9090(%bx) 486 | 487 | test %ax,%bx 488 | test %bx,%ax 489 | test (%bx),%bx 490 | 491 | .byte 0x82, 0xc3, 0x01 492 | .byte 0x82, 0xf3, 0x01 493 | .byte 0x82, 0xd3, 0x01 494 | .byte 0x82, 0xdb, 0x01 495 | .byte 0x82, 0xe3, 0x01 496 | .byte 0x82, 0xeb, 0x01 497 | .byte 0x82, 0xf3, 0x01 498 | .byte 0x82, 0xfb, 0x01 499 | 500 | .byte 0xf6, 0xc9, 0x01 501 | //.byte 0x66, 0xf7, 0xc9, 0x02, 0x00 502 | //.byte 0xf7, 0xc9, 0x04, 0x00, 0x00, 0x00 503 | .byte 0xc0, 0xf0, 0x02 504 | .byte 0xc1, 0xf0, 0x01 505 | .byte 0xd0, 0xf0 506 | .byte 0xd1, 0xf0 507 | .byte 0xd2, 0xf0 508 | .byte 0xd3, 0xf0 509 | #endif 510 | -------------------------------------------------------------------------------- /opcodes.txt: -------------------------------------------------------------------------------- 1 | aaa A 2 | aad k 3 | aam n 4 | aas u 5 | adc a 6 | add + 7 | and & 8 | call c 9 | cbw b 10 | clc ' 11 | cld C 12 | cli ! 13 | cmc " 14 | cmp ? 15 | cmpsb 0 16 | cmpsw 1 17 | cs: % 18 | cwd w 19 | daa y 20 | das Y 21 | dec d 22 | div / 23 | ds: E 24 | es: e 25 | idiv \ 26 | imul # 27 | inc i 28 | int $ 29 | iret I 30 | ja ] 31 | jae ) 32 | jb [ 33 | jbe ( 34 | jcxz z 35 | je = 36 | jg > 37 | jge . 38 | jl < 39 | jle , 40 | jmp j 41 | jne J 42 | jno K 43 | jnp P 44 | jns g 45 | jo M 46 | jp p 47 | js G 48 | lahf L 49 | lds N 50 | lea l 51 | les T 52 | lodsb 2 53 | lodsw 3 54 | loop o 55 | loope q 56 | loopne Q 57 | mov m 58 | movsb 4 59 | movsw 5 60 | mul * 61 | neg _ 62 | nop ; 63 | not ~ 64 | or | 65 | pop } 66 | popf O 67 | push { 68 | pushf U 69 | rcl f 70 | rcr F 71 | rep r 72 | ret R 73 | rol h 74 | ror H 75 | sahf s 76 | salc S 77 | sar W 78 | sbb B 79 | scasb 6 80 | scasw 7 81 | shl v 82 | shr V 83 | ss: Z 84 | stc ` 85 | std D 86 | sti : 87 | stosb 8 88 | stosw 9 89 | sub - 90 | test t 91 | xchg x 92 | xlatb @ 93 | xor ^ 94 | -------------------------------------------------------------------------------- /sim86.c: -------------------------------------------------------------------------------- 1 | /* 2 | * sim86 - 8086 emulator for ELKS and DOS 3 | * 4 | * Emulator orginally from Andrew Jenner's reenigne project 5 | * DOS enhancements by TK Chia 6 | * ELKS executable support by Greg Haerr 7 | * Heavily rewritten and disassembler added by Greg Haerr 8 | */ 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "8086.h" 17 | #include "exe.h" 18 | #include "syms.h" 19 | #include "disasm.h" 20 | #include "discolor.h" 21 | 22 | int f_verbose; 23 | static int f_disasm; /* do disassembly */ 24 | static int f_showreps = 1; /* show repeating instructions */ 25 | static int f_octal; /* display bytecodes in octal */ 26 | static char *program_file; 27 | 28 | int nextbyte_mem(int cs, int ip) 29 | { 30 | return readByte(ip, CS); 31 | } 32 | 33 | void runtimeError(const char *msg, ...) 34 | { 35 | va_list args; 36 | va_start(args, msg); 37 | vfprintf(stderr, msg, args); 38 | va_end(args); 39 | fprintf(stderr, "\nCS:IP = %04x:%04x\n", cs(), getIP()); 40 | exit(1); 41 | } 42 | 43 | void handleInterrupt(struct exe *e, int intno) 44 | { 45 | if (intno == 0x80 || intno == 0x21) { 46 | if (!e->handleSyscall(e, intno)) 47 | runtimeError("Unimplemented system call\n"); 48 | return; 49 | } 50 | 51 | switch (intno) { 52 | case INT0_DIV_ERROR: 53 | runtimeError("Divide by zero"); 54 | return; 55 | case INT3_BREAKPOINT: 56 | runtimeError("Breakpoint trap"); 57 | return; 58 | case INT4_OVERFLOW: 59 | runtimeError("Overflow trap"); 60 | return; 61 | default: 62 | runtimeError("Unknown INT 0x%02x", intno); 63 | } 64 | } 65 | 66 | void usage(void) 67 | { 68 | printf("Usage: %s [-vaoc] \n", program_file); 69 | exit(0); 70 | } 71 | 72 | int main(int argc, char *argv[]) 73 | { 74 | int ch; 75 | extern char **environ; 76 | struct exe e = {}; 77 | struct dis d = {}; 78 | 79 | program_file = argv[0]; 80 | while ((ch = getopt(argc, argv, "vaoc")) != -1) { 81 | switch (ch) { 82 | case 'v': 83 | f_verbose = 1; 84 | break; 85 | case 'a': 86 | f_disasm = 1; 87 | break; 88 | case 'o': 89 | f_octal = 1; 90 | break; 91 | case 'c': 92 | g_high.enabled = 0; 93 | break; 94 | default: 95 | usage(); 96 | } 97 | } 98 | argc -= optind; 99 | argv += optind; 100 | if (argc < 1) 101 | usage(); 102 | 103 | initMachine(&e); 104 | char *p = strrchr(argv[0], '.'); 105 | if (!p) p = argv[0]; 106 | if (!strncmp(p, ".bin", 5)) 107 | loadExecutableBinary(&e, argv[0], argc, argv, environ); 108 | else if (!strncmp(p, ".exe", 5) || !strncmp(p, ".com", 5)) 109 | loadExecutableDOS(&e, argv[0], argc, argv, environ); 110 | else loadExecutableElks(&e, argv[0], argc, argv, environ); 111 | sym_read_exe_symbols(&e, argv[0]); 112 | 113 | #ifdef __APPLE__ /* macOS stdio drops characters! */ 114 | static char buf[1]; 115 | setvbuf(stdout, buf, _IOFBF, sizeof(buf)); 116 | #endif 117 | 118 | initExecute(); 119 | int flags = fDisCS | fDisIP | fDisBytes | fDisInst; 120 | if (f_octal) flags |= fDisOctal; 121 | d.e = &e; 122 | e.textseg = cs(); 123 | e.dataseg = ds(); 124 | Word lastIP = getIP(); 125 | for (;;) { 126 | if (f_disasm && (f_showreps || !isRepeating())) { 127 | disasm(&d, cs(), lastIP, nextbyte_mem, ds(), flags); 128 | printf("%s\n", colorInst(&d, d.buf)); 129 | } 130 | executeInstruction(); 131 | if (!isRepeating()) 132 | lastIP = getIP(); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /syms.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ELKS symbol table support 3 | * 4 | * July 2022 Greg Haerr 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "syms.h" 15 | 16 | #if __ia16__ 17 | #define ALLOC(s,n) ((int)(s = sbrk(n)) != -1) 18 | #else 19 | #define ALLOC(s,n) ((s = malloc(n)) != NULL) 20 | #endif 21 | 22 | #define MAGIC 0x0301 /* magic number for ELKS executable progs */ 23 | 24 | /* read symbol table from executable into memory */ 25 | unsigned char * noinstrument sym_read_exe_symbols(struct exe *e, char *path) 26 | { 27 | int fd; 28 | unsigned char *s; 29 | 30 | if (e->syms) return e->syms; 31 | if ((fd = open(path, O_RDONLY)) < 0) { 32 | #if __ia16__ 33 | char fullpath[128]; 34 | sprintf(fullpath, "/bin/%s", path); // FIXME use PATH 35 | if ((fd = open(fullpath, O_RDONLY)) < 0) 36 | #endif 37 | return NULL; 38 | } 39 | errno = 0; 40 | if (read(fd, &e->aout, sizeof(e->aout)) != sizeof(e->aout) 41 | || ((e->aout.type & 0xFFFF) != MAGIC) 42 | || e->aout.syms == 0 43 | #if __ia16__ 44 | || e->aout.syms > 32767 45 | #endif 46 | || (!ALLOC(s, (int)e->aout.syms)) 47 | || (lseek(fd, -(int)e->aout.syms, SEEK_END) < 0) 48 | || (read(fd, s, (int)e->aout.syms) != (int)e->aout.syms)) { 49 | int e = errno; 50 | close(fd); 51 | errno = e; 52 | return NULL; 53 | } 54 | close(fd); 55 | e->syms = s; 56 | return s; 57 | } 58 | 59 | /* read symbol table file into memory */ 60 | unsigned char * noinstrument sym_read_symbols(struct exe *e, char *path) 61 | { 62 | int fd; 63 | unsigned char *s; 64 | struct stat sbuf; 65 | 66 | if (e->syms) return e->syms; 67 | if ((fd = open(path, O_RDONLY)) < 0) 68 | return NULL; 69 | errno = 0; 70 | if (fstat(fd, &sbuf) < 0 71 | || sbuf.st_size == 0 72 | #if __ia16__ 73 | || sbuf.st_size > 32767 74 | #endif 75 | || (!ALLOC(s, (int)sbuf.st_size)) 76 | || (read(fd, s, (int)sbuf.st_size) != (int)sbuf.st_size)) { 77 | int e = errno; 78 | close(fd); 79 | errno = e; 80 | return NULL; 81 | } 82 | close(fd); 83 | e->syms = s; 84 | return s; 85 | } 86 | 87 | /* dealloate symbol table file in memory */ 88 | void noinstrument sym_free(struct exe *e) 89 | { 90 | #ifndef __ia16__ // FIXME ELKS uses sbrk() 91 | if (e->syms) 92 | free(e->syms); 93 | #endif 94 | e->syms = NULL; 95 | } 96 | 97 | static int noinstrument type_text(unsigned char *p) 98 | { 99 | return (p[TYPE] == 'T' || p[TYPE] == 't' || p[TYPE] == 'W'); 100 | } 101 | 102 | static int noinstrument type_ftext(unsigned char *p) 103 | { 104 | return (p[TYPE] == 'F' || p[TYPE] == 'f'); 105 | } 106 | 107 | static int noinstrument type_data(unsigned char *p) 108 | { 109 | return (p[TYPE] == 'D' || p[TYPE] == 'd' || 110 | p[TYPE] == 'B' || p[TYPE] == 'b' || 111 | p[TYPE] == 'V'); 112 | } 113 | 114 | /* map .text address to function start address */ 115 | addr_t noinstrument sym_fn_start_address(struct exe *e, addr_t addr) 116 | { 117 | unsigned char *p, *lastp; 118 | 119 | if (!e->syms) return -1; 120 | 121 | lastp = e->syms; 122 | for (p = next(lastp); ; lastp = p, p = next(p)) { 123 | if (!type_text(p) || ((unsigned short)addr < *(unsigned short *)(&p[ADDR]))) 124 | break; 125 | } 126 | return *(unsigned short *)(&lastp[ADDR]); 127 | } 128 | 129 | /* convert address to symbol string */ 130 | static char * noinstrument sym_string(struct exe *e, addr_t addr, int exact, 131 | int (*istype)(unsigned char *p)) 132 | { 133 | unsigned char *p, *lastp; 134 | static char buf[64]; 135 | 136 | if (!e->syms) { 137 | hex: 138 | sprintf(buf, "%.4x", (unsigned int)addr); 139 | return buf; 140 | } 141 | 142 | lastp = e->syms; 143 | while (!istype(lastp)) { 144 | lastp = next(lastp); 145 | if (!lastp[TYPE]) 146 | goto hex; 147 | } 148 | for (p = next(lastp); ; lastp = p, p = next(p)) { 149 | if (!istype(p) || ((unsigned short)addr < *(unsigned short *)(&p[ADDR]))) 150 | break; 151 | } 152 | int lastaddr = *(unsigned short *)(&lastp[ADDR]); 153 | if (exact && addr - lastaddr) { 154 | sprintf(buf, "%.*s+%xh", lastp[SYMLEN], lastp+SYMBOL, 155 | (unsigned int)addr - lastaddr); 156 | } else sprintf(buf, "%.*s", lastp[SYMLEN], lastp+SYMBOL); 157 | return buf; 158 | } 159 | 160 | /* convert .text address to symbol */ 161 | char * noinstrument sym_text_symbol(struct exe *e, addr_t addr, int exact) 162 | { 163 | return sym_string(e, addr, exact, type_text); 164 | } 165 | 166 | /* convert .fartext address to symbol */ 167 | char * noinstrument sym_ftext_symbol(struct exe *e, addr_t addr, int exact) 168 | { 169 | return sym_string(e, addr, exact, type_ftext); 170 | } 171 | 172 | /* convert .data address to symbol */ 173 | char * noinstrument sym_data_symbol(struct exe *e, addr_t addr, int exact) 174 | { 175 | return sym_string(e, addr, exact, type_data); 176 | } 177 | 178 | #if 0 179 | static int noinstrument type_any(unsigned char *p) 180 | { 181 | return p[TYPE] != '\0'; 182 | } 183 | 184 | /* convert (non-segmented local IP) address to symbol */ 185 | char * noinstrument sym_symbol(struct exe *e, addr_t addr, int exact) 186 | { 187 | return sym_string(e, addr, exact, type_any); 188 | } 189 | #endif 190 | -------------------------------------------------------------------------------- /syms.h: -------------------------------------------------------------------------------- 1 | #ifndef SYMS_H_ 2 | #define SYMS_H_ 3 | /* ELKS symbol table support */ 4 | 5 | #include "exe.h" 6 | 7 | /* symbol table format 8 | * | byte type | word address | byte symbol length | symbol | 9 | * type: (lower case means static) 10 | * T, t .text 11 | * F, f .fartext 12 | * D, d .data 13 | * B, b .bss 14 | * 0 end of symbol table 15 | */ 16 | 17 | #define next(sym) \ 18 | ((sym) + 1 + sizeof(unsigned short) + ((unsigned char *)sym)[SYMLEN] + 1) 19 | #define TYPE 0 20 | #define ADDR 1 21 | #define SYMLEN 3 22 | #define SYMBOL 4 23 | 24 | #ifndef noinstrument 25 | #define noinstrument __attribute__((no_instrument_function)) 26 | #endif 27 | 28 | typedef unsigned int addr_t; /* ELKS a.out address size (short) or larger */ 29 | 30 | unsigned char * noinstrument sym_read_exe_symbols(struct exe *e, char *path); 31 | unsigned char * noinstrument sym_read_symbols(struct exe *e, char *path); 32 | void noinstrument sym_free(struct exe *e); 33 | char * noinstrument sym_text_symbol(struct exe *e, addr_t addr, int exact); 34 | char * noinstrument sym_ftext_symbol(struct exe *e, addr_t addr, int exact); 35 | char * noinstrument sym_data_symbol(struct exe *e, addr_t addr, int exact); 36 | char * noinstrument sym_symbol(struct exe *e, addr_t addr, int exact); 37 | addr_t noinstrument sym_fn_start_address(struct exe *e, addr_t addr); 38 | 39 | #endif /* SYMS_H_ */ 40 | -------------------------------------------------------------------------------- /syscall-dos.c: -------------------------------------------------------------------------------- 1 | /* MSDOS system calls for 8086 emulator */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "8086.h" 11 | #include "exe.h" 12 | 13 | #if BLINK16 14 | #include "blink/machine.h" 15 | #define f_verbose 0 16 | #else 17 | extern int f_verbose; 18 | #endif 19 | 20 | extern Word loadSegment; 21 | 22 | static char* pathBuffers[2]; 23 | static int* fileDescriptors; 24 | static int fileDescriptorCount = 6; 25 | 26 | static void* alloc(size_t bytes) 27 | { 28 | void* r = malloc(bytes); 29 | if (r == 0) { 30 | runtimeError("Out of memory\n"); 31 | } 32 | return r; 33 | } 34 | 35 | static void init() 36 | { 37 | pathBuffers[0] = (char*)alloc(0x10000); 38 | pathBuffers[1] = (char*)alloc(0x10000); 39 | 40 | fileDescriptors = (int*)alloc(6*sizeof(int)); 41 | fileDescriptors[0] = STDIN_FILENO; 42 | fileDescriptors[1] = STDOUT_FILENO; 43 | fileDescriptors[2] = STDERR_FILENO; 44 | fileDescriptors[3] = STDOUT_FILENO; 45 | fileDescriptors[4] = STDOUT_FILENO; 46 | fileDescriptors[5] = -1; 47 | } 48 | 49 | static char* initString(Word offset, int seg, int write, int buffer, int bytes) 50 | { 51 | for (int i = 0; i < bytes; ++i) { 52 | char p; 53 | if (write) { 54 | p = pathBuffers[buffer][i]; 55 | ram[physicalAddress(offset + i, seg, true)] = p; 56 | } 57 | else { 58 | p = ram[physicalAddress(offset + i, seg, false)]; 59 | pathBuffers[buffer][i] = p; 60 | } 61 | if (p == 0 && bytes == 0x10000) 62 | break; 63 | } 64 | if (!write) 65 | pathBuffers[buffer][0xffff] = 0; 66 | return pathBuffers[buffer]; 67 | } 68 | 69 | static char* dsdxparms(int write, int bytes) 70 | { 71 | return initString(dx(), DS, write, 0, bytes); 72 | } 73 | 74 | static char *dsdx() 75 | { 76 | return dsdxparms(false, 0x10000); 77 | } 78 | 79 | static int dosError(int e) 80 | { 81 | if (e == ENOENT) 82 | return 2; 83 | runtimeError("%s\n", strerror(e)); 84 | return 0; 85 | } 86 | 87 | static int getDescriptor() 88 | { 89 | for (int i = 0; i < fileDescriptorCount; ++i) 90 | if (fileDescriptors[i] == -1) 91 | return i; 92 | int newCount = fileDescriptorCount << 1; 93 | int* newDescriptors = (int*)alloc(newCount*sizeof(int)); 94 | for (int i = 0; i < fileDescriptorCount; ++i) 95 | newDescriptors[i] = fileDescriptors[i]; 96 | free(fileDescriptors); 97 | int oldCount = fileDescriptorCount; 98 | fileDescriptorCount = newCount; 99 | fileDescriptors = newDescriptors; 100 | return oldCount; 101 | } 102 | 103 | int checkStackDOS(struct exe *e) 104 | { 105 | return (e->t_stackLow && ((DWord)ss() << 4) + sp() <= e->t_stackLow); 106 | } 107 | 108 | static int SysExit(struct exe *e, int rc) 109 | { 110 | if (f_verbose) printf("EXIT %d\n", rc); 111 | exit(rc); 112 | return -1; 113 | } 114 | 115 | static int SysWrite(struct exe *e, int fd, char *buf, size_t n) 116 | { 117 | #if BLINK16 118 | extern ssize_t ptyWrite(int fd, char *buf, int len); 119 | SetWriteAddr(g_machine, physicalAddress(dx(), DS, false), n); 120 | return ptyWrite(fd, buf, n); 121 | #else 122 | return write(fd, buf, n); 123 | #endif 124 | } 125 | 126 | int handleSyscallDOS(struct exe *e, int intno) 127 | { 128 | int fileDescriptor; 129 | char *p, *addr; 130 | DWord data; 131 | static int once = 0; 132 | 133 | if (!once) { 134 | init(); 135 | once = 1; 136 | } 137 | switch (intno << 8 | ah()) { 138 | case 0x1a00: 139 | data = es(); 140 | setES(0); 141 | setDX(readWordSeg(0x046c, ES)); 142 | setCX(readWordSeg(0x046e, ES)); 143 | setAL(readByte(0x0470, ES)); 144 | setES(data); 145 | break; 146 | case 0x2109: 147 | addr = dsdx(); 148 | p = strchr(addr, '$'); 149 | if (p) SysWrite(e, STDOUT_FILENO, addr, p-addr); 150 | break; 151 | case 0x2130: 152 | setAX(0x1403); 153 | setBX(0xff00); 154 | setCX(0); 155 | break; 156 | case 0x2139: 157 | if (mkdir(dsdx(), 0700) == 0) 158 | setCF(false); 159 | else { 160 | setCF(true); 161 | setAX(dosError(errno)); 162 | } 163 | break; 164 | case 0x213a: 165 | if (rmdir(dsdx()) == 0) 166 | setCF(false); 167 | else { 168 | setCF(true); 169 | setAX(dosError(errno)); 170 | } 171 | break; 172 | case 0x213b: 173 | if (chdir(dsdx()) == 0) 174 | setCF(false); 175 | else { 176 | setCF(true); 177 | setAX(dosError(errno)); 178 | } 179 | break; 180 | case 0x213c: 181 | fileDescriptor = creat(dsdx(), 0700); 182 | if (fileDescriptor != -1) { 183 | setCF(false); 184 | int guestDescriptor = getDescriptor(); 185 | setAX(guestDescriptor); 186 | fileDescriptors[guestDescriptor] = fileDescriptor; 187 | } 188 | else { 189 | setCF(true); 190 | setAX(dosError(errno)); 191 | } 192 | break; 193 | case 0x213d: 194 | fileDescriptor = open(dsdx(), al() & 3, 0700); 195 | if (fileDescriptor != -1) { 196 | setCF(false); 197 | setAX(getDescriptor()); 198 | fileDescriptors[ax()] = fileDescriptor; 199 | } 200 | else { 201 | setCF(true); 202 | setAX(dosError(errno)); 203 | } 204 | break; 205 | case 0x213e: 206 | fileDescriptor = fileDescriptors[bx()]; 207 | if (fileDescriptor == -1) { 208 | setCF(true); 209 | setAX(6); // Invalid handle 210 | break; 211 | } 212 | if (fileDescriptor >= 5 && 213 | close(fileDescriptor) != 0) { 214 | setCF(true); 215 | setAX(dosError(errno)); 216 | } 217 | else { 218 | fileDescriptors[bx()] = -1; 219 | setCF(false); 220 | } 221 | break; 222 | case 0x213f: 223 | fileDescriptor = fileDescriptors[bx()]; 224 | if (fileDescriptor == -1) { 225 | setCF(true); 226 | setAX(6); // Invalid handle 227 | break; 228 | } 229 | data = read(fileDescriptor, pathBuffers[0], cx()); 230 | dsdxparms(true, cx()); 231 | if (data == (DWord)-1) { 232 | setCF(true); 233 | setAX(dosError(errno)); 234 | } 235 | else { 236 | setCF(false); 237 | setAX(data); 238 | } 239 | break; 240 | case 0x2140: 241 | fileDescriptor = fileDescriptors[bx()]; 242 | if (fileDescriptor == -1) { 243 | setCF(true); 244 | setAX(6); // Invalid handle 245 | break; 246 | } 247 | data = SysWrite(e, fileDescriptor, dsdxparms(false, cx()), cx()); 248 | if (data == (DWord)-1) { 249 | setCF(true); 250 | setAX(dosError(errno)); 251 | } 252 | else { 253 | setCF(false); 254 | setAX(data); 255 | } 256 | break; 257 | case 0x2141: 258 | if (unlink(dsdx()) == 0) 259 | setCF(false); 260 | else { 261 | setCF(true); 262 | setAX(dosError(errno)); 263 | } 264 | break; 265 | case 0x2142: 266 | fileDescriptor = fileDescriptors[bx()]; 267 | if (fileDescriptor == -1) { 268 | setCF(true); 269 | setAX(6); // Invalid handle 270 | break; 271 | } 272 | data = lseek(fileDescriptor, (cx() << 16) + dx(), 273 | al()); 274 | if (data != (DWord)-1) { 275 | setCF(false); 276 | setDX(data >> 16); 277 | setAX(data); 278 | } 279 | else { 280 | setCF(true); 281 | setAX(dosError(errno)); 282 | } 283 | break; 284 | case 0x2144: 285 | if (al() != 0) 286 | runtimeError("Unknown IOCTL 0x%02x", al()); 287 | fileDescriptor = fileDescriptors[bx()]; 288 | if (fileDescriptor == -1) { 289 | setCF(true); 290 | setAX(6); // Invalid handle 291 | break; 292 | } 293 | data = isatty(fileDescriptor); 294 | if (data == 1) { 295 | setDX(0x80); 296 | setCF(false); 297 | } 298 | else { 299 | if (errno == ENOTTY) { 300 | setDX(0); 301 | setCF(false); 302 | } 303 | else { 304 | setAX(dosError(errno)); 305 | setCF(true); 306 | } 307 | } 308 | break; 309 | case 0x2147: 310 | if (getcwd(pathBuffers[0], 64) != 0) { 311 | setCF(false); 312 | initString(si(), DS, true, 0, 0x10000); 313 | } 314 | else { 315 | setCF(true); 316 | setAX(dosError(errno)); 317 | } 318 | break; 319 | case 0x214a: 320 | // Only allow attempts to "resize" the PSP segment, 321 | // and check that CS:IP and SS:SP do not overshoot the 322 | // segment end 323 | if (es() == loadSegment - 0x10) { 324 | DWord memEnd = (DWord)(es() + bx()) << 4; 325 | if (physicalAddress(getIP(), CS, false) < memEnd && 326 | physicalAddress(sp() - 1, SS, true) < memEnd) { 327 | setCF(false); 328 | break; 329 | } 330 | } 331 | runtimeError("Bad attempt to resize DOS memory " 332 | "block: int 0x21, ah = 0x4a, bx = 0x%04x, " 333 | "es = 0x%04x", (unsigned)bx(), (unsigned)es()); 334 | break; 335 | case 0x214c: 336 | //printf("*** Cycles: %i\n", ios); 337 | SysExit(e, 0); 338 | break; 339 | case 0x2156: 340 | if (rename(dsdx(), initString(di(), ES, false, 1, 0x10000)) == 0) 341 | setCF(false); 342 | else { 343 | setCF(true); 344 | setAX(dosError(errno)); 345 | } 346 | break; 347 | case 0x2157: 348 | switch (al()) { 349 | case 0x00: 350 | fileDescriptor = fileDescriptors[bx()]; 351 | if (fileDescriptor == -1) { 352 | setCF(true); 353 | setAX(6); // Invalid handle 354 | break; 355 | } 356 | setCX(0x0000); // Return a "reasonable" file 357 | setDX(0x0021); // time and file date 358 | setCF(false); 359 | break; 360 | default: 361 | runtimeError("Unknown DOS call: int 0x21, " 362 | "ax = 0x%04x", (unsigned)ax()); 363 | } 364 | break; 365 | default: 366 | runtimeError("Unknown DOS/BIOS call: int 0x%02x, " 367 | "ah = 0x%02x", intno, (unsigned)ah()); 368 | return 0; 369 | } 370 | return 1; 371 | } 372 | -------------------------------------------------------------------------------- /syscall-elks.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ELKS system calls for 8086 emulator 3 | * 4 | * Greg Haerr 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "8086.h" 15 | #include "exe.h" 16 | 17 | #if BLINK16 18 | #include "blink/machine.h" 19 | #define f_verbose 0 20 | #else 21 | extern int f_verbose; 22 | #endif 23 | 24 | /* return true on stack overflow */ 25 | int checkStackElks(struct exe *e) 26 | { 27 | return (e->t_stackLow && ((DWord)ss() << 4) + sp() < e->t_stackLow); 28 | //return (e->t_minstack && sp() < e->t_begstack - e->t_minstack); 29 | /* allow more than min stack down to break */ 30 | //return (e->t_endbrk && sp() < e->t_endbrk); 31 | } 32 | 33 | static int SysExit(struct exe *e, int rc) 34 | { 35 | if (f_verbose) printf("EXIT %d\n", rc); 36 | exit(rc); 37 | return -1; 38 | } 39 | 40 | static int SysWrite(struct exe *e, int fd, char *buf, size_t n) 41 | { 42 | #if BLINK16 43 | extern ssize_t ptyWrite(int fd, char *buf, int len); 44 | SetWriteAddr(g_machine, buf-(char *)ram, n); 45 | return ptyWrite(fd, buf, n); 46 | #else 47 | return write(fd, buf, n); 48 | #endif 49 | } 50 | 51 | static int SysRead(struct exe *e, int fd, char *buf, size_t n) 52 | { 53 | return read(fd, buf, n); 54 | } 55 | 56 | static int SysOpen(struct exe *e, char *path, int oflag, int mode) 57 | { 58 | if (f_verbose) 59 | printf("[sys_open '%s',%d,%x]\n", path, oflag, mode); 60 | int ret = open(path, oflag, mode); 61 | if (ret < 0) 62 | printf("[sys_open failed: %s\n", path); 63 | return ret; 64 | } 65 | 66 | static int SysClose(struct exe *e, int fd) 67 | { 68 | return close(fd); 69 | } 70 | 71 | static int SysBreak(struct exe *e, unsigned newbrk) 72 | { 73 | if (f_verbose) 74 | printf("[sys_brk old %04x new %04x]\n", e->t_endbrk, newbrk); 75 | if (newbrk < e->t_enddata) 76 | return -ENOMEM; 77 | if (newbrk > e->t_begstack - e->t_minstack) { 78 | printf("sys_brk fail: brk %04x over by %u bytes\n", 79 | newbrk, newbrk - (e->t_begstack - e->t_minstack)); 80 | return -ENOMEM; 81 | } 82 | e->t_endbrk = newbrk; 83 | return 0; 84 | } 85 | 86 | static int SysSbrk(struct exe *e, int incr, int offset_result) 87 | { 88 | unsigned int brk = e->t_endbrk; /* always return start of old break */ 89 | int err; 90 | 91 | if (f_verbose) 92 | printf("[sys_sbrk %d old %04x new %04x SP %04x\n", incr, brk, brk+incr, sp()); 93 | if (incr) { 94 | err = SysBreak(e, brk + incr); 95 | if (err) return err; 96 | } 97 | writeWord(brk, offset_result, SS); 98 | return 0; 99 | } 100 | 101 | #define CASE(OP, CODE) \ 102 | case OP: \ 103 | CODE; \ 104 | break 105 | 106 | #define SYSCALL(x, name, args) \ 107 | CASE(x, AX = name args ) 108 | 109 | #define rptr(off) ((char *)&ram[physicalAddress(off, SS, false)]) 110 | #define wptr(off) ((char *)&ram[physicalAddress(off, SS, true)]) 111 | 112 | int handleSyscallElks(struct exe *e, int intno) 113 | { 114 | unsigned int AX = ax(); 115 | unsigned int BX = bx(); 116 | unsigned int CX = cx(); 117 | unsigned int DX = dx(); 118 | 119 | /* syscall args: BX, CX, DX, DI, SI */ 120 | switch (AX) { 121 | SYSCALL(1, SysExit, (e, BX)); 122 | SYSCALL(3, SysRead, (e, BX, rptr(CX), DX)); 123 | SYSCALL(4, SysWrite, (e, BX, rptr(CX), DX)); 124 | SYSCALL(5, SysOpen, (e, rptr(BX), CX, DX)); 125 | SYSCALL(6, SysClose, (e, BX)); 126 | SYSCALL(17, SysBreak, (e, BX)); 127 | SYSCALL(69, SysSbrk, (e, BX, CX)); 128 | case 54: // ioctl FIXME 129 | if (f_verbose) 130 | printf("IOCTL %d,%c%02d,%x\n", BX, CX>>8, CX&0xff, DX); 131 | AX = BX < 3? 0: -1; 132 | break; 133 | default: 134 | runtimeError("Unknown SYS call %d: AX %04x BX %04x CX %04x DX %04x\n", 135 | AX, AX, BX, CX, DX); 136 | return 0; 137 | } 138 | setAX(AX); 139 | return 1; 140 | } 141 | --------------------------------------------------------------------------------