/* Copyright 2015 the unarr project authors (see AUTHORS file). License: LGPLv3 */ /* adapted from https://code.google.com/p/theunarchiver/source/browse/XADMaster/RARVirtualMachine.c */ #include "rarvm.h" #include "../common/allocator.h" #include <stdlib.h> #include <string.h> typedef struct RAROpcode_s RAROpcode; struct RAROpcode_s { uint8_t instruction; uint8_t bytemode; uint8_t addressingmode1; uint8_t addressingmode2; uint32_t value1; uint32_t value2; }; struct RARProgram_s { RAROpcode *opcodes; uint32_t length; uint32_t capacity; }; /* Program building */ RARProgram *RARCreateProgram() { return calloc(1, sizeof(RARProgram)); } void RARDeleteProgram(RARProgram *prog) { if (prog) free(prog->opcodes); free(prog); } bool RARProgramAddInstr(RARProgram *prog, uint8_t instruction, bool bytemode) { if (instruction >= RARNumberOfInstructions) return false; if (bytemode && !RARInstructionHasByteMode(instruction)) return false; if (prog->length + 1 >= prog->capacity) { /* in my small file sample, 16 is the value needed most often */ uint32_t newCapacity = prog->capacity ? prog->capacity * 4 : 32; RAROpcode *newCodes = calloc(newCapacity, sizeof(*prog->opcodes)); if (!newCodes) return false; memcpy(newCodes, prog->opcodes, prog->capacity * sizeof(*prog->opcodes)); free(prog->opcodes); prog->opcodes = newCodes; prog->capacity = newCapacity; } memset(&prog->opcodes[prog->length], 0, sizeof(prog->opcodes[prog->length])); prog->opcodes[prog->length].instruction = instruction; if (instruction == RARMovzxInstruction || instruction == RARMovsxInstruction) prog->opcodes[prog->length].bytemode = 2; /* second argument only */ else if (bytemode) prog->opcodes[prog->length].bytemode = (1 | 2); else prog->opcodes[prog->length].bytemode = 0; prog->length++; return true; } bool RARSetLastInstrOperands(RARProgram *prog, uint8_t addressingmode1, uint32_t value1, uint8_t addressingmode2, uint32_t value2) { RAROpcode *opcode = &prog->opcodes[prog->length - 1]; int numoperands; if (addressingmode1 >= RARNumberOfAddressingModes || addressingmode2 >= RARNumberOfAddressingModes) return false; if (!prog->length || opcode->addressingmode1 || opcode->value1 || opcode->addressingmode2 || opcode->value2) return false; numoperands = NumberOfRARInstructionOperands(opcode->instruction); if (numoperands == 0) return true; if (addressingmode1 == RARImmediateAddressingMode && RARInstructionWritesFirstOperand(opcode->instruction)) return false; opcode->addressingmode1 = addressingmode1; opcode->value1 = value1; if (numoperands == 2) { if (addressingmode2 == RARImmediateAddressingMode && RARInstructionWritesSecondOperand(opcode->instruction)) return false; opcode->addressingmode2 = addressingmode2; opcode->value2 = value2; } return true; } bool RARIsProgramTerminated(RARProgram *prog) { return prog->length > 0 && RARInstructionIsUnconditionalJump(prog->opcodes[prog->length - 1].instruction); } /* Execution */ #define EXTMACRO_BEGIN do { #ifdef _MSC_VER #define EXTMACRO_END } __pragma(warning(push)) __pragma(warning(disable:4127)) while (0) __pragma(warning(pop)) #else #define EXTMACRO_END } while (0) #endif #define CarryFlag 1 #define ZeroFlag 2 #define SignFlag 0x80000000 #define SignExtend(a) ((uint32_t)((int8_t)(a))) static uint32_t _RARGetOperand(RARVirtualMachine *vm, uint8_t addressingmode, uint32_t value, bool bytemode); static void _RARSetOperand(RARVirtualMachine *vm, uint8_t addressingmode, uint32_t value, bool bytemode, uint32_t data); #define GetOperand1() _RARGetOperand(vm, opcode->addressingmode1, opcode->value1, opcode->bytemode & 1) #define GetOperand2() _RARGetOperand(vm, opcode->addressingmode2, opcode->value2, opcode->bytemode & 2) #define SetOperand1(data) _RARSetOperand(vm, opcode->addressingmode1, opcode->value1, opcode->bytemode & 1, data) #define SetOperand2(data) _RARSetOperand(vm, opcode->addressingmode2, opcode->value2, opcode->bytemode & 2, data) #define SetFlagsWithCarry(res, carry) EXTMACRO_BEGIN uint32_t result = (res); flags = (result == 0 ? ZeroFlag : (result & SignFlag)) | ((carry) ? CarryFlag : 0); EXTMACRO_END #define SetByteFlagsWithCarry(res, carry) EXTMACRO_BEGIN uint8_t result = (res); flags = (result == 0 ? ZeroFlag : (SignExtend(result) & SignFlag)) | ((carry) ? CarryFlag : 0); EXTMACRO_END #define SetFlags(res) SetFlagsWithCarry(res, 0) #define SetOperand1AndFlagsWithCarry(res, carry) EXTMACRO_BEGIN uint32_t r = (res); SetFlagsWithCarry(r, carry); SetOperand1(r); EXTMACRO_END #define SetOperand1AndByteFlagsWithCarry(res, carry) EXTMACRO_BEGIN uint8_t r = (res); SetByteFlagsWithCarry(r, carry); SetOperand1(r); EXTMACRO_END #define SetOperand1AndFlags(res) EXTMACRO_BEGIN uint32_t r = (res); SetFlags(r); SetOperand1(r); EXTMACRO_END #define NextInstruction() { opcode++; continue; } #define Jump(offs) { uint32_t o = (offs); if (o >= prog->length) return false; opcode = &prog->opcodes[o]; continue; } bool RARExecuteProgram(RARVirtualMachine *vm, RARProgram *prog) { RAROpcode *opcode = prog->opcodes; uint32_t flags = 0; uint32_t op1, op2, carry, i; uint32_t counter = 0; if (!RARIsProgramTerminated(prog)) return false; while ((uint32_t)(opcode - prog->opcodes) < prog->length && counter++ < RARRuntimeMaxInstructions) { switch (opcode->instruction) { case RARMovInstruction: SetOperand1(GetOperand2()); NextInstruction(); case RARCmpInstruction: op1 = GetOperand1(); SetFlagsWithCarry(op1 - GetOperand2(), result > op1); NextInstruction(); case RARAddInstruction: op1 = GetOperand1(); if (opcode->bytemode) SetOperand1AndByteFlagsWithCarry((op1 + GetOperand2()) & 0xFF, result < op1); else SetOperand1AndFlagsWithCarry(op1 + GetOperand2(), result < op1); NextInstruction(); case RARSubInstruction: op1 = GetOperand1(); #if 0 /* apparently not correctly implemented in the RAR VM */ if (opcode->bytemode) SetOperand1AndByteFlagsWithCarry((op1 - GetOperand2()) & 0xFF, result > op1); else #endif SetOperand1AndFlagsWithCarry(op1 - GetOperand2(), result > op1); NextInstruction(); case RARJzInstruction: if ((flags & ZeroFlag)) Jump(GetOperand1()); NextInstruction(); case RARJnzInstruction: if (!(flags & ZeroFlag)) Jump(GetOperand1()); NextInstruction(); case RARIncInstruction: if (opcode->bytemode) SetOperand1AndFlags((GetOperand1() + 1) & 0xFF); else SetOperand1AndFlags(GetOperand1() + 1); NextInstruction(); case RARDecInstruction: if (opcode->bytemode) SetOperand1AndFlags((GetOperand1() - 1) & 0xFF); else SetOperand1AndFlags(GetOperand1() - 1); NextInstruction(); case RARJmpInstruction: Jump(GetOperand1()); case RARXorInstruction: SetOperand1AndFlags(GetOperand1() ^ GetOperand2()); NextInstruction(); case RARAndInstruction: SetOperand1AndFlags(GetOperand1() & GetOperand2()); NextInstruction(); case RAROrInstruction: SetOperand1AndFlags(GetOperand1() | GetOperand2()); NextInstruction(); case RARTestInstruction: SetFlags(GetOperand1() & GetOperand2()); NextInstruction(); case RARJsInstruction: if ((flags & SignFlag)) Jump(GetOperand1()); NextInstruction(); case RARJnsInstruction: if (!(flags & SignFlag)) Jump(GetOperand1()); NextInstruction(); case RARJbInstruction: if ((flags & CarryFlag)) Jump(GetOperand1()); NextInstruction(); case RARJbeInstruction: if ((flags & (CarryFlag | ZeroFlag))) Jump(GetOperand1()); NextInstruction(); case RARJaInstruction: if (!(flags & (CarryFlag | ZeroFlag))) Jump(GetOperand1()); NextInstruction(); case RARJaeInstruction: if (!(flags & CarryFlag)) Jump(GetOperand1()); NextInstruction(); case RARPushInstruction: vm->registers[7] -= 4; RARVirtualMachineWrite32(vm, vm->registers[7], GetOperand1()); NextInstruction(); case RARPopInstruction: SetOperand1(RARVirtualMachineRead32(vm, vm->registers[7])); vm->registers[7] += 4; NextInstruction(); case RARCallInstruction: vm->registers[7] -= 4; RARVirtualMachineWrite32(vm, vm->registers[7], (uint32_t)(opcode - prog->opcodes + 1)); Jump(GetOperand1()); case RARRetInstruction: if (vm->registers[7] >= RARProgramMemorySize) return true; i = RARVirtualMachineRead32(vm, vm->registers[7]); vm->registers[7] += 4; Jump(i); case RARNotInstruction: SetOperand1(~GetOperand1()); NextInstruction(); case RARShlInstruction: op1 = GetOperand1(); op2 = GetOperand2(); SetOperand1AndFlagsWithCarry(op1 << op2, ((op1 << (op2 - 1)) & 0x80000000) != 0); NextInstruction(); case RARShrInstruction: op1 = GetOperand1(); op2 = GetOperand2(); SetOperand1AndFlagsWithCarry(op1 >> op2, ((op1 >> (op2 - 1)) & 1) != 0); NextInstruction(); case RARSarInstruction: op1 = GetOperand1(); op2 = GetOperand2(); SetOperand1AndFlagsWithCarry(((int32_t)op1) >> op2, ((op1 >> (op2 - 1)) & 1) != 0); NextInstruction(); case RARNegInstruction: SetOperand1AndFlagsWithCarry(-(int32_t)GetOperand1(), result != 0); NextInstruction(); case RARPushaInstruction: vm->registers[7] -= 32; for (i = 0; i < 8; i++) RARVirtualMachineWrite32(vm, vm->registers[7] + (7 - i) * 4, vm->registers[i]); NextInstruction(); case RARPopaInstruction: for (i = 0; i < 8; i++) vm->registers[i] = RARVirtualMachineRead32(vm, vm->registers[7] + (7 - i) * 4); vm->registers[7] += 32; NextInstruction(); case RARPushfInstruction: vm->registers[7] -= 4; RARVirtualMachineWrite32(vm, vm->registers[7], flags); NextInstruction(); case RARPopfInstruction: flags = RARVirtualMachineRead32(vm, vm->registers[7]); vm->registers[7] += 4; NextInstruction(); case RARMovzxInstruction: SetOperand1(GetOperand2()); NextInstruction(); case RARMovsxInstruction: SetOperand1(SignExtend(GetOperand2())); NextInstruction(); case RARXchgInstruction: op1 = GetOperand1(); op2 = GetOperand2(); SetOperand1(op2); SetOperand2(op1); NextInstruction(); case RARMulInstruction: SetOperand1(GetOperand1() * GetOperand2()); NextInstruction(); case RARDivInstruction: op2 = GetOperand2(); if (op2 != 0) SetOperand1(GetOperand1() / op2); NextInstruction(); case RARAdcInstruction: op1 = GetOperand1(); carry = (flags & CarryFlag); if (opcode->bytemode) SetOperand1AndFlagsWithCarry((op1 + GetOperand2() + carry) & 0xFF, result < op1 || (result == op1 && carry)); /* does not correctly set sign bit */ else SetOperand1AndFlagsWithCarry(op1 + GetOperand2() + carry, result < op1 || (result == op1 && carry)); NextInstruction(); case RARSbbInstruction: op1 = GetOperand1(); carry = (flags & CarryFlag); if (opcode->bytemode) SetOperand1AndFlagsWithCarry((op1 - GetOperand2() - carry) & 0xFF, result > op1 || (result == op1 && carry)); /* does not correctly set sign bit */ else SetOperand1AndFlagsWithCarry(op1 - GetOperand2() - carry, result > op1 || (result == op1 && carry)); NextInstruction(); case RARPrintInstruction: /* TODO: ??? */ NextInstruction(); } } return false; } /* Memory and register access */ static uint32_t _RARRead32(const uint8_t *b) { return ((uint32_t)b[3] << 24) | ((uint32_t)b[2] << 16) | ((uint32_t)b[1] << 8) | (uint32_t)b[0]; } static void _RARWrite32(uint8_t *b, uint32_t n) { b[3] = (n >> 24) & 0xFF; b[2] = (n >> 16) & 0xFF; b[1] = (n >> 8) & 0xFF; b[0] = n & 0xFF; } void RARSetVirtualMachineRegisters(RARVirtualMachine *vm, uint32_t registers[8]) { if (registers) memcpy(vm->registers, registers, sizeof(vm->registers)); else memset(vm->registers, 0, sizeof(vm->registers)); } uint32_t RARVirtualMachineRead32(RARVirtualMachine *vm, uint32_t address) { return _RARRead32(&vm->memory[address & RARProgramMemoryMask]); } void RARVirtualMachineWrite32(RARVirtualMachine *vm, uint32_t address, uint32_t val) { _RARWrite32(&vm->memory[address & RARProgramMemoryMask], val); } uint8_t RARVirtualMachineRead8(RARVirtualMachine *vm, uint32_t address) { return vm->memory[address & RARProgramMemoryMask]; } void RARVirtualMachineWrite8(RARVirtualMachine *vm, uint32_t address, uint8_t val) { vm->memory[address & RARProgramMemoryMask] = val; } static uint32_t _RARGetOperand(RARVirtualMachine *vm, uint8_t addressingmode, uint32_t value, bool bytemode) { if (RARRegisterAddressingMode(0) <= addressingmode && addressingmode <= RARRegisterAddressingMode(7)) { uint32_t result = vm->registers[addressingmode % 8]; if (bytemode) result = result & 0xFF; return result; } if (RARRegisterIndirectAddressingMode(0) <= addressingmode && addressingmode <= RARRegisterIndirectAddressingMode(7)) { if (bytemode) return RARVirtualMachineRead8(vm, vm->registers[addressingmode % 8]); return RARVirtualMachineRead32(vm, vm->registers[addressingmode % 8]); } if (RARIndexedAbsoluteAddressingMode(0) <= addressingmode && addressingmode <= RARIndexedAbsoluteAddressingMode(7)) { if (bytemode) return RARVirtualMachineRead8(vm, value + vm->registers[addressingmode % 8]); return RARVirtualMachineRead32(vm, value + vm->registers[addressingmode % 8]); } if (addressingmode == RARAbsoluteAddressingMode) { if (bytemode) return RARVirtualMachineRead8(vm, value); return RARVirtualMachineRead32(vm, value); } /* if (addressingmode == RARImmediateAddressingMode) */ return value; } static void _RARSetOperand(RARVirtualMachine *vm, uint8_t addressingmode, uint32_t value, bool bytemode, uint32_t data) { if (RARRegisterAddressingMode(0) <= addressingmode && addressingmode <= RARRegisterAddressingMode(7)) { if (bytemode) data = data & 0xFF; vm->registers[addressingmode % 8] = data; } else if (RARRegisterIndirectAddressingMode(0) <= addressingmode && addressingmode <= RARRegisterIndirectAddressingMode(7)) { if (bytemode) RARVirtualMachineWrite8(vm, vm->registers[addressingmode % 8], (uint8_t)data); else RARVirtualMachineWrite32(vm, vm->registers[addressingmode % 8], data); } else if (RARIndexedAbsoluteAddressingMode(0) <= addressingmode && addressingmode <= RARIndexedAbsoluteAddressingMode(7)) { if (bytemode) RARVirtualMachineWrite8(vm, value + vm->registers[addressingmode % 8], (uint8_t)data); else RARVirtualMachineWrite32(vm, value + vm->registers[addressingmode % 8], data); } else if (addressingmode == RARAbsoluteAddressingMode) { if (bytemode) RARVirtualMachineWrite8(vm, value, (uint8_t)data); else RARVirtualMachineWrite32(vm, value, data); } } /* Instruction properties */ #define RAR0OperandsFlag 0 #define RAR1OperandFlag 1 #define RAR2OperandsFlag 2 #define RAROperandsFlag 3 #define RARHasByteModeFlag 4 #define RARIsUnconditionalJumpFlag 8 #define RARIsRelativeJumpFlag 16 #define RARWritesFirstOperandFlag 32 #define RARWritesSecondOperandFlag 64 #define RARReadsStatusFlag 128 #define RARWritesStatusFlag 256 static const int InstructionFlags[RARNumberOfInstructions] = { /*RARMovInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag, /*RARCmpInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesStatusFlag, /*RARAddInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, /*RARSubInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, /*RARJzInstruction*/ RAR1OperandFlag | RARIsUnconditionalJumpFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag, /*RARJnzInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag, /*RARIncInstruction*/ RAR1OperandFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, /*RARDecInstruction*/ RAR1OperandFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, /*RARJmpInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag, /*RARXorInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, /*RARAndInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, /*RAROrInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, /*RARTestInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesStatusFlag, /*RARJsInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag, /*RARJnsInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag, /*RARJbInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag, /*RARJbeInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag, /*RARJaInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag, /*RARJaeInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag, /*RARPushInstruction*/ RAR1OperandFlag, /*RARPopInstruction*/ RAR1OperandFlag, /*RARCallInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag, /*RARRetInstruction*/ RAR0OperandsFlag | RARIsUnconditionalJumpFlag, /*RARNotInstruction*/ RAR1OperandFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag, /*RARShlInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, /*RARShrInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, /*RARSarInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, /*RARNegInstruction*/ RAR1OperandFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, /*RARPushaInstruction*/ RAR0OperandsFlag, /*RARPopaInstruction*/ RAR0OperandsFlag, /*RARPushfInstruction*/ RAR0OperandsFlag | RARReadsStatusFlag, /*RARPopfInstruction*/ RAR0OperandsFlag | RARWritesStatusFlag, /*RARMovzxInstruction*/ RAR2OperandsFlag | RARWritesFirstOperandFlag, /*RARMovsxInstruction*/ RAR2OperandsFlag | RARWritesFirstOperandFlag, /*RARXchgInstruction*/ RAR2OperandsFlag | RARWritesFirstOperandFlag | RARWritesSecondOperandFlag | RARHasByteModeFlag, /*RARMulInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag, /*RARDivInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag, /*RARAdcInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARReadsStatusFlag | RARWritesStatusFlag, /*RARSbbInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARReadsStatusFlag | RARWritesStatusFlag, /*RARPrintInstruction*/ RAR0OperandsFlag }; int NumberOfRARInstructionOperands(uint8_t instruction) { if (instruction >= RARNumberOfInstructions) return 0; return InstructionFlags[instruction] & RAROperandsFlag; } bool RARInstructionHasByteMode(uint8_t instruction) { if (instruction >= RARNumberOfInstructions) return false; return (InstructionFlags[instruction] & RARHasByteModeFlag)!=0; } bool RARInstructionIsUnconditionalJump(uint8_t instruction) { if (instruction >= RARNumberOfInstructions) return false; return (InstructionFlags[instruction] & RARIsUnconditionalJumpFlag) != 0; } bool RARInstructionIsRelativeJump(uint8_t instruction) { if (instruction >= RARNumberOfInstructions) return false; return (InstructionFlags[instruction] & RARIsRelativeJumpFlag) != 0; } bool RARInstructionWritesFirstOperand(uint8_t instruction) { if (instruction >= RARNumberOfInstructions) return false; return (InstructionFlags[instruction] & RARWritesFirstOperandFlag) != 0; } bool RARInstructionWritesSecondOperand(uint8_t instruction) { if (instruction >= RARNumberOfInstructions) return false; return (InstructionFlags[instruction] & RARWritesSecondOperandFlag) != 0; } /* Program debugging */ #ifndef NDEBUG #include <stdio.h> static void RARPrintOperand(uint8_t addressingmode, uint32_t value) { if (RARRegisterAddressingMode(0) <= addressingmode && addressingmode <= RARRegisterAddressingMode(7)) printf("r%d", addressingmode % 8); else if (RARRegisterIndirectAddressingMode(0) <= addressingmode && addressingmode <= RARRegisterIndirectAddressingMode(7)) printf("@(r%d)", addressingmode % 8); else if (RARIndexedAbsoluteAddressingMode(0) <= addressingmode && addressingmode <= RARIndexedAbsoluteAddressingMode(7)) printf("@(r%d+$%02x)", addressingmode % 8, value); else if (addressingmode == RARAbsoluteAddressingMode) printf("@($%02x)", value); else if (addressingmode == RARImmediateAddressingMode) printf("$%02x", value); } void RARPrintProgram(RARProgram *prog) { static const char *instructionNames[RARNumberOfInstructions] = { "Mov", "Cmp", "Add", "Sub", "Jz", "Jnz", "Inc", "Dec", "Jmp", "Xor", "And", "Or", "Test", "Js", "Jns", "Jb", "Jbe", "Ja", "Jae", "Push", "Pop", "Call", "Ret", "Not", "Shl", "Shr", "Sar", "Neg", "Pusha", "Popa", "Pushf", "Popf", "Movzx", "Movsx", "Xchg", "Mul", "Div", "Adc", "Sbb", "Print", }; uint32_t i; for (i = 0; i < prog->length; i++) { RAROpcode *opcode = &prog->opcodes[i]; int numoperands = NumberOfRARInstructionOperands(opcode->instruction); printf(" %02x: %s", i, instructionNames[opcode->instruction]); if (opcode->bytemode) printf("B"); if (numoperands >= 1) { printf(" "); RARPrintOperand(opcode->addressingmode1, opcode->value1); } if (numoperands == 2) { printf(", "); RARPrintOperand(opcode->addressingmode2, opcode->value2); } printf("\n"); } } #endif