Word ea() { modRM = fetchByte(); useMemory = true; switch (modRM & 7) { case 0: segment = 3; address = bx() + si(); break; case 1: segment = 3; address = bx() + di(); break; case 2: segment = 2; address = bp() + si(); break; case 3: segment = 2; address = bp() + di(); break; case 4: segment = 3; address = si(); break; case 5: segment = 3; address = di(); break; case 6: segment = 2; address = bp(); break; case 7: segment = 3; address = bx(); break; } switch (modRM & 0xc0) { case 0x00: if ((modRM & 0xc7) == 6) { segment = 3; address = fetchWord(); } break; case 0x40: address += signExtend(fetchByte()); break; case 0x80: address += fetchWord(); break; case 0xc0: useMemory = false; address = modRM & 7; } return address; }
/*Load Register Signed Byte (immediate) Encoding T1 LDRSB<c> <Rt>,[<Rn>,#<imm12>] 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | 1 1 1 1 1| 0 0| 1 1 0 0 1| Rn | Rt | imm12 | where: <c><q> See Standard assembler syntax fields on page A6-7. <Rt> Specifies the destination register. <Rn> Specifies the base register. This register is allowed to be the SP. If this register is the PC, see LDRSB (literal) on page A6-120. +/- Is + or omitted to indicate that the immediate offset is added to the base register value (add == TRUE), or – to indicate that the offset is to be subtracted (add == FALSE). Different instructions are generated for #0 and #-0. <imm> Specifies the immediate offset added to or subtracted from the value of <Rn> to form the address. The range of allowed values is 0-4095 for encoding T1, and 0-255 for encoding T2. For the offset addressing syntax, <imm> can be omitted, meaning an offset of 0. */ void LDRSBImmediateT1(uint32_t instruction) { uint32_t Rn = getBits(instruction,19,16); uint32_t Rt = getBits(instruction,15,12); uint32_t imm12 = getBits(instruction,11,0); uint32_t address = coreReg[Rn] + imm12; if(inITBlock()) { if( checkCondition(cond) ) writeToCoreRegisters(Rt , signExtend( loadByteFromMemory(address, 1), 8) ); shiftITState(); } else writeToCoreRegisters(Rt , signExtend( loadByteFromMemory(address, 1), 8) ); coreReg[PC] += 4; }
/*Load Register Signed Halfword (immediate) Encoding T2 LDRSH<c> <Rt>,[<Rn>,#-<imm8>] LDRSH<c> <Rt>,[<Rn>],#+/-<imm8> LDRSH<c> <Rt>,[<Rn>,#+/-<imm8>]! 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | 1 1 1 1 1| 0 0| 1 0 0 1 1| Rn | Rt | 1| P|U|W| imm8 | where: <c><q> See Standard assembler syntax fields on page A6-7. <Rt> Specifies the destination register. <Rn> Specifies the base register. This register is allowed to be the SP. If this register is the PC, see LDRSH (literal) on page A6-120. +/- Is + or omitted to indicate that the immediate offset is added to the base register value (add == TRUE), or – to indicate that the offset is to be subtracted (add == FALSE). Different instructions are generated for #0 and #-0. <imm> Specifies the immediate offset added to or subtracted from the value of <Rn> to form the address. The range of allowed values is 0-4095 for encoding T1, and 0-255 for encoding T2. For the offset addressing syntax, <imm> can be omitted, meaning an offset of 0. */ void LDRSHImmediateT2(uint32_t instruction) { uint32_t imm8 = getBits(instruction, 7, 0); uint32_t Rt = getBits(instruction,15,12); uint32_t Rn = getBits(instruction,19,16); uint32_t W = getBits(instruction,8,8); uint32_t U = getBits(instruction,9,9); uint32_t P = getBits(instruction,10,10); uint32_t address; if(U == 1) address = coreReg[Rn] + imm8; else address = coreReg[Rn] - imm8; int check = isOffPostOrPreIndex(P,W); if(check == UNDEFINED || Rt == 0b1111 || Rn == 0b1111) ThrowError(); if(inITBlock()) { if( checkCondition(cond) ) { if(check == OFFINDEX) writeToCoreRegisters(Rt , signExtend( loadByteFromMemory(address, 2), 16) ); else if(check == PREINDEX) { writeToCoreRegisters(Rt , signExtend( loadByteFromMemory(address, 2), 16) ); coreReg[Rn] = address; } else { writeToCoreRegisters(Rt , signExtend( loadByteFromMemory(coreReg[Rn], 2), 16) ); coreReg[Rn] = address; } } shiftITState(); } else { if(check == OFFINDEX) writeToCoreRegisters(Rt , signExtend( loadByteFromMemory(address, 2), 16) ); else if(check == PREINDEX) { writeToCoreRegisters(Rt , signExtend( loadByteFromMemory(address, 2), 16) ); coreReg[Rn] = address; } else { writeToCoreRegisters(Rt , signExtend( loadByteFromMemory(coreReg[Rn], 2), 16) ); coreReg[Rn] = address; } } coreReg[PC] += 4; }
/* SW */ void mips_sw(Instruction *instruction) { InstructionTypeI i = instruction->i; storeWord(registers[i.rt], registers[i.rs] + (signed)signExtend(i.immediate)); }
/* LW */ void mips_lw(Instruction *instruction) { InstructionTypeI i = instruction->i; registers[i.rt] = loadWord(registers[i.rs] + (signed)signExtend(i.immediate)); }
/* ADDI */ void mips_addi(Instruction *instruction) { InstructionTypeI i = instruction->i; registers[i.rt] = (signed)registers[i.rs] + (signed)signExtend(i.immediate); }
/* VCVT, VCVTR (between floating-point and integer) Floating-point Convert (between floating-point and integer) converts a value in a register from floating-point to a 32-bit integer, or from a 32-bit integer to floating-point, and places the result in a second register. The fixed-point value can be 16-bit or 32-bit. Conversions from fixed-point values take their operand from the low-order bits of the source register and ignore any remaining bits. Signed conversions to fixed-point values sign-extend the result value to the destination register width. Unsigned conversions to fixed-point values zero-extend the result value to the destination register width. VCVT<c>.<Td>.F32 <Sd>, <Sd>, #<fbits> VCVT<c>.F32.<Td> <Sd>, <Sd>, #<fbits> 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 |1 1 1 0| 1 1 1 0 1| D| 1 1 1 op| 1 U| Vd | 1 0 1|sf sx 1 i 0| imm4 | where : <c>, <q> See Standard assembler syntax fields on page A7-175. <Td> The data type for the fixed-point number. It must be one of: S16 Encoded as U = 0, sx = 0. U16 Encoded as U = 1, sx = 0 S32 Encoded as U = 0, sx = 1. U32 Encoded as U = 1, sx = 1. <Sd> The destination and operand register, for a single-precision operand. <Dd> The destination and operand register, for a double-precision operand. <fbits> The number of fraction bits in the fixed-point number: • If <Td> is S16 or U16, <fbits> must be in the range 0-16. (16 - <fbits>) is encoded in [imm4,i] • I f <Td> is S32 or U32, <fbits> must be in the range 1-32. (32 - <fbits>) is encoded in [imm4,i]. */ void VCVTT2(uint32_t instruction) { uint32_t Vd = getBits(instruction,15,12); uint32_t imm4 = getBits(instruction,3,0); uint32_t sf = getBits(instruction,8,8); uint32_t sx = getBits(instruction,7,7); uint32_t i = getBits(instruction,5,5); uint32_t op = getBits(instruction,18,18); uint32_t U = getBits(instruction,16,16); uint32_t D = getBits(instruction,22,22); uint32_t d = determineRegisterBasedOnSZ(D, Vd, sf); uint32_t result; executeFPUChecking(); bool roundNearest = false; bool roundZero = false; bool toFixed = (op == 1); bool dpOperation = (sf == 1); bool unsign = (U == 1); uint32_t size = (sx == 0) ? 16 : 32; uint32_t fracBits = size - ( (imm4 << 1) | i); if(toFixed) roundZero = true; else roundNearest = true; if(inITBlock()) { if( checkCondition(cond) ) { if(toFixed) { if(dpOperation) ThrowError(); else { result = FPToFixed(fpuSinglePrecision[d], size, fracBits, unsign, roundZero, fPSCR); if(unsign) writeSinglePrecision(d, result); else writeSinglePrecision(d, signExtend(result, 32) ); } } else { if(dpOperation) ThrowError(); else result = FixedToFP( getBits(fpuSinglePrecision[d], (size-1), 0), 32, fracBits, unsign, roundNearest, fPSCR); writeSinglePrecision(d, result); } } shiftITState(); } else { if(toFixed) { if(dpOperation) ThrowError(); else { result = FPToFixed(fpuSinglePrecision[d], size, fracBits, unsign, roundZero, fPSCR); if(unsign) writeSinglePrecision(d, result); else writeSinglePrecision(d, signExtend(result, 32) ); } } else { if(dpOperation) ThrowError(); else result = FixedToFP( getBits(fpuSinglePrecision[d], (size-1), 0), 32, fracBits, unsign, roundNearest, fPSCR); writeSinglePrecision(d, result); } } coreReg[PC] += 4; }
int main(int argc, char* argv[]) { if (argc < 2) { printf("Usage: %s <program name>\n", argv[0]); exit(0); } filename = argv[1]; FILE* fp = fopen(filename, "rb"); if (fp == 0) error("opening"); ram = (Byte*)malloc(0x10000); memset(ram, 0, 0x10000); if (ram == 0) { fprintf(stderr, "Out of memory\n"); exit(1); } if (fseek(fp, 0, SEEK_END) != 0) error("seeking"); length = ftell(fp); if (length == -1) error("telling"); if (fseek(fp, 0, SEEK_SET) != 0) error("seeking"); if (length > 0x10000 - 0x100) { fprintf(stderr, "%s is too long to be a .com file\n", filename); exit(1); } if (fread(&ram[0x100], length, 1, fp) != 1) error("reading"); fclose(fp); Word segment = 0x1000; setAX(0x0000); setCX(0x00FF); setDX(segment); registers[3] = 0x0000; setSP(0xFFFE); registers[5] = 0x091C; setSI(0x0100); setDI(0xFFFE); for (int i = 0; i < 4; ++i) registers[8 + i] = segment; Byte* byteData = (Byte*)®isters[0]; int bigEndian = (byteData[2] == 0 ? 1 : 0); int byteNumbers[8] = {0, 2, 4, 6, 1, 3, 5, 7}; for (int i = 0 ; i < 8; ++i) byteRegisters[i] = &byteData[byteNumbers[i] ^ bigEndian]; bool prefix = false; for (int i = 0; i < 1000000000; ++i) { if (!repeating) { if (!prefix) { segmentOverride = -1; rep = 0; } prefix = false; opcode = fetchByte(); } wordSize = ((opcode & 1) != 0); bool sourceIsRM = ((opcode & 2) != 0); int operation = (opcode >> 3) & 7; bool jump; switch (opcode) { case 0x00: case 0x01: case 0x02: case 0x03: case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x10: case 0x11: case 0x12: case 0x13: case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x20: case 0x21: case 0x22: case 0x23: case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x30: case 0x31: case 0x32: case 0x33: case 0x38: case 0x39: case 0x3a: case 0x3b: // alu rmv,rmv data = readEA(); if (!sourceIsRM) { destination = data; source = getReg(); } else { destination = getReg(); source = data; } aluOperation = operation; doALUOperation(); if (aluOperation != 7) { if (!sourceIsRM) finishWriteEA(data); else setReg(data); } break; case 0x04: case 0x05: case 0x0c: case 0x0d: case 0x14: case 0x15: case 0x1c: case 0x1d: case 0x24: case 0x25: case 0x2c: case 0x2d: case 0x34: case 0x35: case 0x3c: case 0x3d: // alu accum,i destination = getAccum(); source = !wordSize ? fetchByte() : fetchWord(); aluOperation = operation; doALUOperation(); if (aluOperation != 7) setAccum(); break; case 0x06: case 0x0e: case 0x16: case 0x1e: // PUSH segreg push(registers[operation + 8]); break; case 0x07: case 0x17: case 0x1f: // POP segreg registers[operation + 8] = pop(); break; case 0x26: case 0x2e: case 0x36: case 0x3e: // segment override segmentOverride = operation; prefix = true; break; case 0x27: case 0x2f: // DA if (af() || (al() & 0x0f) > 9) { data = al() + (opcode == 0x27 ? 6 : -6); setAL(data); setAF(true); if ((data & 0x100) != 0) setCF(true); } setCF(cf() || al() > 0x9f); if (cf()) setAL(al() + (opcode == 0x27 ? 0x60 : -0x60)); wordSize = false; data = al(); setPZS(); break; case 0x37: case 0x3f: // AA if (af() || (al() & 0xf) > 9) { setAL(al() + (opcode == 0x37 ? 6 : -6)); setAH(ah() + (opcode == 0x37 ? 1 : -1)); setCA(); } else clearCA(); setAL(al() & 0x0f); break; case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47: case 0x48: case 0x49: case 0x4a: case 0x4b: case 0x4c: case 0x4d: case 0x4e: case 0x4f: // incdec rw destination = rw(); wordSize = true; setRW(incdec((opcode & 8) != 0)); break; case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57: // PUSH rw push(rw()); break; case 0x58: case 0x59: case 0x5a: case 0x5b: case 0x5c: case 0x5d: case 0x5e: case 0x5f: // POP rw setRW(pop()); break; case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67: case 0x68: case 0x69: case 0x6a: case 0x6b: case 0x6c: case 0x6d: case 0x6e: case 0x6f: case 0xc0: case 0xc1: case 0xc8: case 0xc9: // invalid case 0xcc: case 0xf0: case 0xf1: case 0xf4: // INT 3, LOCK, HLT case 0x9b: case 0xce: case 0x0f: // WAIT, INTO, POP CS case 0xd8: case 0xd9: case 0xda: case 0xdb: case 0xdc: case 0xdd: case 0xde: case 0xdf: // escape case 0xe4: case 0xe5: case 0xe6: case 0xe7: case 0xec: case 0xed: case 0xee: case 0xef: // IN, OUT fprintf(stderr, "Invalid opcode %02x", opcode); runtimeError(""); break; case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77: case 0x78: case 0x79: case 0x7a: case 0x7b: case 0x7c: case 0x7d: case 0x7e: case 0x7f: // Jcond cb switch (opcode & 0x0e) { case 0x00: jump = of(); break; case 0x02: jump = cf(); break; case 0x04: jump = zf(); break; case 0x06: jump = cf() || zf(); break; case 0x08: jump = sf(); break; case 0x0a: jump = pf(); break; case 0x0c: jump = sf() != of(); break; default: jump = sf() != of() || zf(); break; } jumpShort(fetchByte(), jump == ((opcode & 1) == 0)); break; case 0x80: case 0x81: case 0x82: case 0x83: // alu rmv,iv destination = readEA(); data = fetch(opcode == 0x81); if (opcode != 0x83) source = data; else source = signExtend(data); aluOperation = modRMReg(); doALUOperation(); if (aluOperation != 7) finishWriteEA(data); break; case 0x84: case 0x85: // TEST rmv,rv data = readEA(); test(data, getReg()); break; case 0x86: case 0x87: // XCHG rmv,rv data = readEA(); finishWriteEA(getReg()); setReg(data); break; case 0x88: case 0x89: // MOV rmv,rv ea(); finishWriteEA(getReg()); break; case 0x8a: case 0x8b: // MOV rv,rmv setReg(readEA()); break; case 0x8c: // MOV rmw,segreg ea(); wordSize = 1; finishWriteEA(registers[modRMReg() + 8]); break; case 0x8d: // LEA address = ea(); if (!useMemory) runtimeError("LEA needs a memory address"); setReg(address); break; case 0x8e: // MOV segreg,rmw wordSize = 1; data = readEA(); registers[modRMReg() + 8] = data; break; case 0x8f: // POP rmw writeEA(pop()); break; case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97: // XCHG AX,rw data = ax(); setAX(rw()); setRW(data); break; case 0x98: // CBW setAX(signExtend(al())); break; case 0x99: // CWD setDX((ax() & 0x8000) == 0 ? 0x0000 : 0xffff); break; case 0x9a: // CALL cp savedIP = fetchWord(); savedCS = fetchWord(); farCall(); break; case 0x9c: // PUSHF push((flags & 0x0fd7) | 0xf000); break; case 0x9d: // POPF flags = pop() | 2; break; case 0x9e: // SAHF flags = (flags & 0xff02) | ah(); break; case 0x9f: // LAHF setAH(flags & 0xd7); break; case 0xa0: case 0xa1: // MOV accum,xv data = read(fetchWord()); setAccum(); break; case 0xa2: case 0xa3: // MOV xv,accum write(getAccum(), fetchWord()); break; case 0xa4: case 0xa5: // MOVSv stoS(lodS()); doRep(); break; case 0xa6: case 0xa7: // CMPSv lodDIS(); source = data; sub(); doRep(); break; case 0xa8: case 0xa9: // TEST accum,iv data = fetch(wordSize); test(getAccum(), data); break; case 0xaa: case 0xab: // STOSv stoS(getAccum()); doRep(); break; case 0xac: case 0xad: // LODSv data = lodS(); setAccum(); doRep(); break; case 0xae: case 0xaf: // SCASv lodDIS(); destination = getAccum(); source = data; sub(); doRep(); break; case 0xb0: case 0xb1: case 0xb2: case 0xb3: case 0xb4: case 0xb5: case 0xb6: case 0xb7: setRB(fetchByte()); break; case 0xb8: case 0xb9: case 0xba: case 0xbb: case 0xbc: case 0xbd: case 0xbe: case 0xbf: // MOV rv,iv setRW(fetchWord()); break; case 0xc2: case 0xc3: case 0xca: case 0xcb: // RET savedIP = pop(); savedCS = (opcode & 8) == 0 ? cs() : pop(); if (!wordSize) setSP(sp() + fetchWord()); farJump(); break; case 0xc4: case 0xc5: // LES/LDS ea(); farLoad(); *modRMRW() = savedIP; registers[8 + (!wordSize ? 0 : 3)] = savedCS; break; case 0xc6: case 0xc7: // MOV rmv,iv ea(); finishWriteEA(fetch(wordSize)); break; case 0xcd: data = fetchByte(); if (data != 0x21) { fprintf(stderr, "Unknown interrupt 0x%02x", data); runtimeError(""); } switch (ah()) { case 2: printf("%c", dl()); break; case 0x4c: printf("*** Bytes: %i\n", length); printf("*** Cycles: %i\n", ios); printf("*** EXIT code %i\n", al()); exit(0); break; default: fprintf(stderr, "Unknown DOS call 0x%02x", data); runtimeError(""); } break; case 0xcf: ip = pop(); setCS(pop()); flags = pop() | 2; break; case 0xd0: case 0xd1: case 0xd2: case 0xd3: // rot rmv,n data = readEA(); if ((opcode & 2) == 0) source = 1; else source = cl(); while (source != 0) { destination = data; switch (modRMReg()) { case 0: // ROL data <<= 1; doCF(); data |= (cf() ? 1 : 0); setOFRotate(); break; case 1: // ROR setCF((data & 1) != 0); data >>= 1; if (cf()) data |= (!wordSize ? 0x80 : 0x8000); setOFRotate(); break; case 2: // RCL data = (data << 1) | (cf() ? 1 : 0); doCF(); setOFRotate(); break; case 3: // RCR data >>= 1; if (cf()) data |= (!wordSize ? 0x80 : 0x8000); setCF((destination & 1) != 0); setOFRotate(); break; case 4: // SHL case 6: data <<= 1; doCF(); setOFRotate(); setPZS(); break; case 5: // SHR setCF((data & 1) != 0); data >>= 1; setOFRotate(); setAF(true); setPZS(); break; case 7: // SAR setCF((data & 1) != 0); data >>= 1; if (!wordSize) data |= (destination & 0x80); else data |= (destination & 0x8000); setOFRotate(); setAF(true); setPZS(); break; } --source; } finishWriteEA(data); break; case 0xd4: // AAM data = fetchByte(); if (data == 0) divideOverflow(); setAH(al() / data); setAL(al() % data); wordSize = true; setPZS(); break; case 0xd5: // AAD data = fetchByte(); setAL(al() + ah()*data); setAH(0); setPZS(); break; case 0xd6: // SALC setAL(cf() ? 0xff : 0x00); break; case 0xd7: // XLATB setAL(readByte(bx() + al())); break; case 0xe0: case 0xe1: case 0xe2: // LOOPc cb setCX(cx() - 1); jump = (cx() != 0); switch (opcode) { case 0xe0: if (zf()) jump = false; break; case 0xe1: if (!zf()) jump = false; break; } jumpShort(fetchByte(), jump); break; case 0xe3: // JCXZ cb jumpShort(fetchByte(), cx() == 0); break; case 0xe8: // CALL cw call(ip + fetchWord()); break; case 0xe9: // JMP cw ip += fetchWord(); break; case 0xea: // JMP cp savedIP = fetchWord(); savedCS = fetchWord(); farJump(); break; case 0xeb: // JMP cb jumpShort(fetchByte(), true); break; case 0xf2: case 0xf3: // REP rep = opcode == 0xf2 ? 1 : 2; prefix = true; break; case 0xf5: // CMC flags ^= 1; break; case 0xf6: case 0xf7: // math rmv data = readEA(); switch (modRMReg()) { case 0: case 1: // TEST rmv,iv test(data, fetch(wordSize)); break; case 2: // NOT iv finishWriteEA(~data); break; case 3: // NEG iv source = data; destination = 0; sub(); finishWriteEA(data); break; case 4: case 5: // MUL rmv, IMUL rmv source = data; destination = getAccum(); data = destination; setSF(); setPF(); data *= source; setAX(data); if (!wordSize) { if (modRMReg() == 4) setCF(ah() != 0); else { if ((source & 0x80) != 0) setAH(ah() - destination); if ((destination & 0x80) != 0) setAH(ah() - source); setCF(ah() == ((al() & 0x80) == 0 ? 0 : 0xff)); } } else { setDX(data >> 16); if (modRMReg() == 4) { data |= dx(); setCF(dx() != 0); } else { if ((source & 0x8000) != 0) setDX(dx() - destination); if ((destination & 0x8000) != 0) setDX(dx() - source); data |= dx(); setCF(dx() == ((ax() & 0x8000) == 0 ? 0 : 0xffff)); } } setZF(); setOF(cf()); break; case 6: case 7: // DIV rmv, IDIV rmv source = data; if (source == 0) divideOverflow(); if (!wordSize) { destination = ax(); if (modRMReg() == 6) { div(); if (data > 0xff) divideOverflow(); } else { destination = ax(); if ((destination & 0x8000) != 0) destination |= 0xffff0000; source = signExtend(source); div(); if (data > 0x7f && data < 0xffffff80) divideOverflow(); } setAH(remainder); setAL(data); } else { destination = (dx() << 16) + ax(); div(); if (modRMReg() == 6) { if (data > 0xffff) divideOverflow(); } else { if (data > 0x7fff && data < 0xffff8000) divideOverflow(); } setDX(remainder); setAX(data); } break; } break; case 0xf8: case 0xf9: // STC/CLC setCF(wordSize); break; case 0xfa: case 0xfb: // STI/CLI setIF(wordSize); break; case 0xfc: case 0xfd: // STD/CLD setDF(wordSize); break; case 0xfe: case 0xff: // misc ea(); if ((!wordSize && modRMReg() >= 2 && modRMReg() <= 6) || modRMReg() == 7) { fprintf(stderr, "Invalid instruction %02x %02x", opcode, modRM); runtimeError(""); } switch (modRMReg()) { case 0: case 1: // incdec rmv destination = readEA2(); finishWriteEA(incdec(modRMReg() != 0)); break; case 2: // CALL rmv call(readEA2()); break; case 3: // CALL mp farLoad(); farCall(); break; case 4: // JMP rmw ip = readEA2(); break; case 5: // JMP mp farLoad(); farJump(); break; case 6: // PUSH rmw push(readEA2()); break; } break; } } runtimeError("Timed out"); }
void jumpShort(Byte data, bool jump) { if (jump) ip += signExtend(data); }
bool ArmElfRelocator::relocateOpcode(int type, RelocationData& data) { int t = (data.targetSymbolType == STT_FUNC && data.targetSymbolInfo != 0) ? 1 : 0; int p = data.opcodeOffset; int s = data.relocationBase; switch (type) { case R_ARM_ABS32: // (S + A) | T case R_ARM_TARGET1: data.opcode = (data.opcode + data.relocationBase) | t; break; case R_ARM_THM_CALL: // ((S + A) | T) – P { unsigned short first = data.opcode & 0xFFFF; unsigned short second = (data.opcode >> 16) & 0xFFFF; int opField = ((first & 0x7FF) << 11) | (second & 0x7FF); int a = signExtend(opField << 1,23); int value = (s+a) - p; first &= ~0x7FF; second &= ~0x7FF; if (t == 1) { if (data.relocationBase % 2) { data.errorMessage = L"Branch target must be halfword aligned"; return false; } } else { if (arm9 == false) { data.errorMessage = L"Cannot call ARM function from THUMB code without stub"; return false; } if (data.relocationBase % 4) { data.errorMessage = L"Branch target must be word aligned"; return false; } second = 0xE800; } if (abs(value) >= 0x400000) { data.errorMessage = formatString(L"Branch target %08X out of range",data.relocationBase); return false; } value >>= 1; first |= (value >> 11) & 0x7FF; second |= value & 0x7FF; data.opcode = first | (second << 16); } break; case R_ARM_CALL: // ((S + A) | T) – P case R_ARM_JUMP24: // ((S + A) | T) – P { int condField = (data.opcode >> 28) & 0xF; int opField = (data.opcode & 0xFFFFFF) << 2; data.opcode &= ~0xFFFFFF; int a = signExtend(opField,26); int value = (s+a) - p; if (t == 1) { if (data.relocationBase % 2) { data.errorMessage = L"Branch target must be halfword aligned"; return false; } if (type == R_ARM_JUMP24) { data.errorMessage = L"Cannot jump from ARM to THUMB without link"; return false; } if (arm9 == false) { data.errorMessage = L"Cannot call THUMB function from ARM code without stub"; return false; } if (condField != 0xE) { data.errorMessage = L"Cannot convert conditional bl into blx"; return false; } data.opcode = 0xFA000000; if (value & 2) data.opcode |= (1 << 24); } else { if (data.relocationBase % 4) { data.errorMessage = L"Branch target must be word aligned"; return false; } } if (abs(value) >= 0x2000000) { data.errorMessage = formatString(L"Branch target %08X out of range",data.relocationBase); return false; } data.opcode |= (value >> 2) & 0xFFFFFF; } break; default: data.errorMessage = formatString(L"Unknown ARM relocation type %d",type); return false; } return true; }