mirror of
https://github.com/MacPaw/XADMaster.git
synced 2025-08-28 19:13:49 +02:00
408 lines
10 KiB
Objective-C
408 lines
10 KiB
Objective-C
/*
|
|
* XADRARVirtualMachine.m
|
|
*
|
|
* Copyright (c) 2017-present, MacPaw Inc. All rights reserved.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
* MA 02110-1301 USA
|
|
*/
|
|
#import "XADRARVirtualMachine.h"
|
|
#import "XADException.h"
|
|
#import "CRC.h"
|
|
|
|
|
|
|
|
|
|
uint32_t CSInputNextRARVMNumber(CSInputBuffer *input)
|
|
{
|
|
switch(CSInputNextBitString(input,2))
|
|
{
|
|
case 0: return CSInputNextBitString(input,4);
|
|
case 1:
|
|
{
|
|
int val=CSInputNextBitString(input,8);
|
|
if(val>=16) return val;
|
|
else return 0xffffff00|(val<<4)|CSInputNextBitString(input,4);
|
|
}
|
|
case 2: return CSInputNextBitString(input,16);
|
|
default: return CSInputNextLongBitString(input,32);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@implementation XADRARVirtualMachine
|
|
|
|
-(id)init
|
|
{
|
|
if((self=[super init]))
|
|
{
|
|
InitializeRARVirtualMachine(&vm);
|
|
}
|
|
return self;
|
|
}
|
|
|
|
-(void)dealloc
|
|
{
|
|
//CleanupRARVirutalMachine(&vm);
|
|
[super dealloc];
|
|
}
|
|
|
|
-(uint8_t *)memory { return vm.memory; }
|
|
|
|
-(void)setRegisters:(uint32_t *)newregisters
|
|
{
|
|
SetRARVirtualMachineRegisters(&vm,newregisters);
|
|
}
|
|
|
|
-(void)readMemoryAtAddress:(uint32_t)address length:(int)length toBuffer:(uint8_t *)buffer
|
|
{
|
|
memcpy(buffer,&vm.memory[address],length);
|
|
}
|
|
|
|
-(void)readMemoryAtAddress:(uint32_t)address length:(int)length toMutableData:(NSMutableData *)data
|
|
{
|
|
[self readMemoryAtAddress:address length:length toBuffer:[data mutableBytes]];
|
|
}
|
|
|
|
-(void)writeMemoryAtAddress:(uint32_t)address length:(int)length fromBuffer:(const uint8_t *)buffer
|
|
{
|
|
memcpy(&vm.memory[address],buffer,length);
|
|
}
|
|
|
|
-(void)writeMemoryAtAddress:(uint32_t)address length:(int)length fromData:(NSData *)data
|
|
{
|
|
[self writeMemoryAtAddress:address length:length fromBuffer:[data bytes]];
|
|
}
|
|
|
|
-(uint32_t)readWordAtAddress:(uint32_t)address
|
|
{
|
|
return RARVirtualMachineRead32(&vm,address);
|
|
}
|
|
|
|
-(void)writeWordAtAddress:(uint32_t)address value:(uint32_t)value
|
|
{
|
|
RARVirtualMachineWrite32(&vm,address,value);
|
|
}
|
|
|
|
-(BOOL)executeProgramCode:(XADRARProgramCode *)code
|
|
{
|
|
return ExecuteRARCode(&vm,[code opcodes],[code numberOfOpcodes]);
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
@implementation XADRARProgramCode
|
|
|
|
-(id)initWithByteCode:(const uint8_t *)bytes length:(int)length
|
|
{
|
|
if((self=[super init]))
|
|
{
|
|
opcodes=[NSMutableData new];
|
|
staticdata=nil;
|
|
globalbackup=[NSMutableData new];
|
|
|
|
if([self parseByteCode:bytes length:length]) return self;
|
|
|
|
[self release];
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
-(void)dealloc
|
|
{
|
|
[opcodes release];
|
|
[staticdata release];
|
|
[globalbackup release];
|
|
[super dealloc];
|
|
}
|
|
|
|
-(BOOL)parseByteCode:(const uint8_t *)bytes length:(int)length
|
|
{
|
|
// TODO: deal with exceptions causing memory leaks
|
|
|
|
if(length==0) return NO;
|
|
|
|
// Check XOR sum.
|
|
uint8_t xor=0;
|
|
for(int i=1;i<length;i++) xor^=bytes[i];
|
|
if(xor!=bytes[0]) return NO;
|
|
|
|
// Calculate CRC for fast native path replacements.
|
|
fingerprint=XADCalculateCRC(0xffffffff,bytes,length,XADCRCTable_edb88320)^0xffffffff;
|
|
fingerprint|=(uint64_t)length<<32;
|
|
|
|
CSInputBuffer *input=CSInputBufferAllocWithBuffer(&bytes[1],length-1,0);
|
|
|
|
// Read static data, if any.
|
|
if(CSInputNextBit(input))
|
|
{
|
|
int length=CSInputNextRARVMNumber(input)+1;
|
|
NSMutableData *data=[NSMutableData dataWithLength:length];
|
|
uint8_t *databytes=[data mutableBytes];
|
|
|
|
for(int i=0;i<length;i++) databytes[i]=CSInputNextBitString(input,8);
|
|
|
|
staticdata=[data retain];
|
|
}
|
|
|
|
// Read instructions.
|
|
while(CSInputBitsLeftInBuffer(input)>=8)
|
|
{
|
|
[opcodes increaseLengthBy:sizeof(RAROpcode)];
|
|
RAROpcode *opcodearray=[self opcodes];
|
|
int currinstruction=[self numberOfOpcodes]-1;
|
|
RAROpcode *opcode=&opcodearray[currinstruction];
|
|
|
|
int instruction=CSInputNextBitString(input,4);
|
|
if(instruction&0x08) instruction=((instruction<<2)|CSInputNextBitString(input,2))-24;
|
|
|
|
BOOL bytemode=NO;
|
|
if(RARInstructionHasByteMode(instruction)) bytemode=CSInputNextBitString(input,1);
|
|
|
|
SetRAROpcodeInstruction(opcode,instruction,bytemode);
|
|
|
|
int numargs=NumberOfRARInstructionOperands(instruction);
|
|
|
|
if(numargs>=1)
|
|
{
|
|
unsigned int addressingmode=0;
|
|
uint32_t value=0;
|
|
[self parseOperandFromBuffer:input addressingMode:&addressingmode value:&value
|
|
byteMode:bytemode isRelativeJump:RARInstructionIsRelativeJump(instruction)
|
|
currentInstructionOffset:currinstruction];
|
|
SetRAROpcodeOperand1(opcode,addressingmode,value);
|
|
}
|
|
if(numargs==2)
|
|
{
|
|
unsigned int addressingmode=0;
|
|
uint32_t value=0;
|
|
[self parseOperandFromBuffer:input addressingMode:&addressingmode value:&value
|
|
byteMode:bytemode isRelativeJump:NO currentInstructionOffset:0];
|
|
SetRAROpcodeOperand2(opcode,addressingmode,value);
|
|
}
|
|
}
|
|
|
|
// Check if program is properly terminated, if not, add a ret opcode.
|
|
if(!IsProgramTerminated([self opcodes],[self numberOfOpcodes]))
|
|
{
|
|
[opcodes increaseLengthBy:sizeof(RAROpcode)];
|
|
RAROpcode *opcodearray=[self opcodes];
|
|
int currinstruction=[self numberOfOpcodes]-1;
|
|
RAROpcode *opcode=&opcodearray[currinstruction];
|
|
|
|
SetRAROpcodeInstruction(opcode,RARRetInstruction,false);
|
|
}
|
|
|
|
CSInputBufferFree(input);
|
|
|
|
return PrepareRAROpcodes([self opcodes],[self numberOfOpcodes]);
|
|
}
|
|
|
|
-(void)parseOperandFromBuffer:(CSInputBuffer *)input addressingMode:(unsigned int *)modeptr
|
|
value:(uint32_t *)valueptr byteMode:(BOOL)bytemode isRelativeJump:(BOOL)isrel currentInstructionOffset:(int)instructionoffset
|
|
{
|
|
if(CSInputNextBit(input))
|
|
{
|
|
int reg=CSInputNextBitString(input,3);
|
|
*modeptr=RARRegisterAddressingMode(reg);
|
|
}
|
|
else
|
|
{
|
|
if(CSInputNextBit(input))
|
|
{
|
|
if(CSInputNextBit(input))
|
|
{
|
|
if(CSInputNextBit(input))
|
|
{
|
|
*valueptr=CSInputNextRARVMNumber(input);
|
|
*modeptr=RARAbsoluteAddressingMode;
|
|
}
|
|
else
|
|
{
|
|
int reg=CSInputNextBitString(input,3);
|
|
*valueptr=CSInputNextRARVMNumber(input);
|
|
*modeptr=RARIndexedAbsoluteAddressingMode(reg);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int reg=CSInputNextBitString(input,3);
|
|
*modeptr=RARRegisterIndirectAddressingMode(reg);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(bytemode)
|
|
{
|
|
*valueptr=CSInputNextBitString(input,8);
|
|
*modeptr=RARImmediateAddressingMode;
|
|
//return [NSString stringWithFormat:@"%d",val];
|
|
}
|
|
else
|
|
{
|
|
*valueptr=CSInputNextRARVMNumber(input);
|
|
*modeptr=RARImmediateAddressingMode;
|
|
}
|
|
|
|
if(isrel)
|
|
{
|
|
if(*valueptr>=256) *valueptr-=256; // Absolute address
|
|
else
|
|
{
|
|
// Relative address
|
|
if(*valueptr>=136) *valueptr-=264;
|
|
else if(*valueptr>=16) *valueptr-=8;
|
|
else if(*valueptr>=8) *valueptr-=16;
|
|
*valueptr+=instructionoffset;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
-(RAROpcode *)opcodes { return [opcodes mutableBytes]; }
|
|
|
|
-(int)numberOfOpcodes { return [opcodes length]/sizeof(RAROpcode); }
|
|
|
|
-(NSData *)staticData { return staticdata; }
|
|
|
|
-(NSMutableData *)globalBackup { return globalbackup; }
|
|
|
|
-(uint64_t)fingerprint { return fingerprint; }
|
|
|
|
-(NSString *)disassemble
|
|
{
|
|
RAROpcode *opcodearray=[self opcodes];
|
|
int numopcodes=[self numberOfOpcodes];
|
|
|
|
NSMutableString *disassembly=[NSMutableString string];
|
|
|
|
for(int i=0;i<numopcodes;i++)
|
|
{
|
|
[disassembly appendFormat:@"%04x\t%s\n",i,DescribeRAROpcode(&opcodearray[i])];
|
|
}
|
|
|
|
return disassembly;
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation XADRARProgramInvocation
|
|
|
|
-(id)initWithProgramCode:(XADRARProgramCode *)code globalData:(NSData *)data registers:(uint32_t *)registers
|
|
{
|
|
if((self=[super init]))
|
|
{
|
|
programcode=[code retain];
|
|
|
|
if(data)
|
|
{
|
|
globaldata=[[NSMutableData alloc] initWithData:data];
|
|
if([globaldata length]<RARProgramSystemGlobalSize) [globaldata setLength:RARProgramSystemGlobalSize];
|
|
}
|
|
else globaldata=[[NSMutableData alloc] initWithLength:RARProgramSystemGlobalSize];
|
|
|
|
if(registers) memcpy(initialregisters,registers,sizeof(initialregisters));
|
|
else memset(initialregisters,0,sizeof(initialregisters));
|
|
}
|
|
return self;
|
|
}
|
|
|
|
-(void)dealloc
|
|
{
|
|
[programcode release];
|
|
[globaldata release];
|
|
[super dealloc];
|
|
}
|
|
|
|
-(XADRARProgramCode *)programCode { return programcode; }
|
|
|
|
-(NSData *)globalData { return globaldata; }
|
|
|
|
-(uint32_t)initialRegisterState:(int)n
|
|
{
|
|
if(n<0||n>=8) [NSException raise:NSRangeException format:@"Attempted to set non-existent register"];
|
|
|
|
return initialregisters[n];
|
|
}
|
|
|
|
-(void)setInitialRegisterState:(int)n toValue:(uint32_t)val
|
|
{
|
|
if(n<0||n>=8) [NSException raise:NSRangeException format:@"Attempted to set non-existent register"];
|
|
|
|
initialregisters[n]=val;
|
|
}
|
|
|
|
-(void)setGlobalValueAtOffset:(int)offs toValue:(uint32_t)val
|
|
{
|
|
if(offs<0||offs+4>[globaldata length]) [NSException raise:NSRangeException format:@"Attempted to write outside global memory"];
|
|
|
|
uint8_t *bytes=[globaldata mutableBytes];
|
|
CSSetUInt32LE(&bytes[offs],val);
|
|
}
|
|
|
|
-(void)backupGlobalData
|
|
{
|
|
NSMutableData *backup=[programcode globalBackup];
|
|
if([globaldata length]>RARProgramSystemGlobalSize) [backup setData:globaldata];
|
|
else [backup setLength:0];
|
|
}
|
|
|
|
-(void)restoreGlobalDataIfAvailable
|
|
{
|
|
NSMutableData *backup=[programcode globalBackup];
|
|
if([backup length]>RARProgramSystemGlobalSize) [globaldata setData:backup];
|
|
}
|
|
|
|
-(BOOL)executeOnVitualMachine:(XADRARVirtualMachine *)vm
|
|
{
|
|
int globallength=[globaldata length];
|
|
if(globallength>RARProgramSystemGlobalSize) globallength=RARProgramSystemGlobalSize;
|
|
[vm writeMemoryAtAddress:RARProgramSystemGlobalAddress length:globallength fromData:globaldata];
|
|
|
|
NSData *staticdata=[programcode staticData];
|
|
if(staticdata)
|
|
{
|
|
int staticlength=[staticdata length];
|
|
if(staticlength>RARProgramUserGlobalSize-globallength) staticlength=RARProgramUserGlobalSize-globallength;
|
|
[vm writeMemoryAtAddress:RARProgramUserGlobalAddress length:staticlength fromData:staticdata];
|
|
}
|
|
|
|
[vm setRegisters:initialregisters];
|
|
|
|
if(![vm executeProgramCode:programcode]) return NO;
|
|
|
|
uint32_t newgloballength=[vm readWordAtAddress:RARProgramSystemGlobalAddress+0x30];
|
|
if(newgloballength>RARProgramUserGlobalSize) newgloballength=RARProgramUserGlobalSize;
|
|
if(newgloballength>0)
|
|
{
|
|
[vm readMemoryAtAddress:RARProgramSystemGlobalAddress
|
|
length:RARProgramSystemGlobalSize+newgloballength
|
|
toMutableData:[globaldata mutableBytes]];
|
|
}
|
|
else [globaldata setLength:0];
|
|
|
|
return YES;
|
|
}
|
|
|
|
@end
|