mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-23 03:32:32 +00:00
Merge pull request #76 from TomHarte/Disassembler
Introduces a first attempt at 6502 disassembly
This commit is contained in:
commit
f84a28b566
@ -14,7 +14,8 @@ using namespace Oric;
|
||||
Machine::Machine() :
|
||||
_cycles_since_video_update(0),
|
||||
_use_fast_tape_hack(false),
|
||||
_typer_delay(2500000)
|
||||
_typer_delay(2500000),
|
||||
_keyboard_read_count(0)
|
||||
{
|
||||
set_clock_rate(1000000);
|
||||
_via.set_interrupt_delegate(this);
|
||||
@ -36,11 +37,30 @@ void Machine::configure_as_target(const StaticAnalyser::Target &target)
|
||||
{
|
||||
set_typer_for_string(target.loadingCommand.c_str());
|
||||
}
|
||||
|
||||
if(target.oric.use_atmos_rom)
|
||||
{
|
||||
memcpy(_rom, _basic11.data(), std::min(_basic11.size(), sizeof(_rom)));
|
||||
|
||||
_is_using_basic11 = true;
|
||||
_tape_get_byte_address = 0xe6c9;
|
||||
_scan_keyboard_address = 0xf495;
|
||||
_tape_speed_address = 0x024d;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(_rom, _basic10.data(), std::min(_basic10.size(), sizeof(_rom)));
|
||||
|
||||
_is_using_basic11 = false;
|
||||
_tape_get_byte_address = 0xe630;
|
||||
_scan_keyboard_address = 0xf43c;
|
||||
_tape_speed_address = 0x67;
|
||||
}
|
||||
}
|
||||
|
||||
void Machine::set_rom(std::vector<uint8_t> data)
|
||||
void Machine::set_rom(ROM rom, const std::vector<uint8_t> &data)
|
||||
{
|
||||
memcpy(_rom, data.data(), std::min(data.size(), sizeof(_rom)));
|
||||
if(rom == BASIC11) _basic11 = std::move(data); else _basic10 = std::move(data);
|
||||
}
|
||||
|
||||
unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value)
|
||||
@ -51,9 +71,9 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
|
||||
|
||||
// 024D = 0 => fast; otherwise slow
|
||||
// E6C9 = read byte: return byte in A
|
||||
if(address == 0xe6c9 && _use_fast_tape_hack && operation == CPU6502::BusOperation::ReadOpcode && _via.tape->has_tape() && !_via.tape->get_tape()->is_at_end())
|
||||
if(address == _tape_get_byte_address && _use_fast_tape_hack && operation == CPU6502::BusOperation::ReadOpcode && _via.tape->has_tape() && !_via.tape->get_tape()->is_at_end())
|
||||
{
|
||||
uint8_t next_byte = _via.tape->get_next_byte(!_ram[0x024d]);
|
||||
uint8_t next_byte = _via.tape->get_next_byte(!_ram[_tape_speed_address]);
|
||||
set_value_of_register(CPU6502::A, next_byte);
|
||||
set_value_of_register(CPU6502::Flags, next_byte ? 0 : CPU6502::Flag::Zero);
|
||||
*value = 0x60; // i.e. RTS
|
||||
@ -78,9 +98,12 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
|
||||
}
|
||||
}
|
||||
|
||||
if(_typer && operation == CPU6502::BusOperation::ReadOpcode && address == 0xF495)
|
||||
if(_typer && address == _scan_keyboard_address && operation == CPU6502::BusOperation::ReadOpcode)
|
||||
{
|
||||
if(!_typer->type_next_character())
|
||||
// the Oric 1 misses any key pressed on the very first entry into the read keyboard routine, so don't
|
||||
// do anything until at least the second, regardless of machine
|
||||
if(!_keyboard_read_count) _keyboard_read_count++;
|
||||
else if(!_typer->type_next_character())
|
||||
{
|
||||
clear_all_keys();
|
||||
_typer.reset();
|
||||
|
@ -51,6 +51,10 @@ enum Key: uint16_t {
|
||||
TerminateSequence = 0xffff, NotMapped = 0xfffe
|
||||
};
|
||||
|
||||
enum ROM {
|
||||
BASIC10, BASIC11
|
||||
};
|
||||
|
||||
class Machine:
|
||||
public CPU6502::Processor<Machine>,
|
||||
public CRTMachine::Machine,
|
||||
@ -62,7 +66,7 @@ class Machine:
|
||||
public:
|
||||
Machine();
|
||||
|
||||
void set_rom(std::vector<uint8_t> data);
|
||||
void set_rom(ROM rom, const std::vector<uint8_t> &data);
|
||||
void set_key_state(uint16_t key, bool isPressed);
|
||||
void clear_all_keys();
|
||||
|
||||
@ -93,10 +97,16 @@ class Machine:
|
||||
|
||||
private:
|
||||
// RAM and ROM
|
||||
std::vector<uint8_t> _basic11, _basic10;
|
||||
uint8_t _ram[65536], _rom[16384];
|
||||
int _cycles_since_video_update;
|
||||
inline void update_video();
|
||||
|
||||
// ROM bookkeeping
|
||||
bool _is_using_basic11;
|
||||
uint16_t _tape_get_byte_address, _scan_keyboard_address, _tape_speed_address;
|
||||
int _keyboard_read_count;
|
||||
|
||||
// Outputs
|
||||
std::unique_ptr<VideoOutput> _videoOutput;
|
||||
|
||||
|
@ -50,6 +50,7 @@
|
||||
4B55CE5D1C3B7D6F0093A61B /* CSOpenGLView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE5C1C3B7D6F0093A61B /* CSOpenGLView.m */; };
|
||||
4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE5E1C3B7D960093A61B /* MachineDocument.swift */; };
|
||||
4B59199C1DAC6C46005BB85C /* OricTAP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B59199A1DAC6C46005BB85C /* OricTAP.cpp */; };
|
||||
4B5A12571DD55862007A2231 /* Disassembler6502.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5A12551DD55862007A2231 /* Disassembler6502.cpp */; };
|
||||
4B643F3A1D77AD1900D431D6 /* CSStaticAnalyser.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B643F391D77AD1900D431D6 /* CSStaticAnalyser.mm */; };
|
||||
4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B643F3E1D77B88000D431D6 /* DocumentController.swift */; };
|
||||
4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */; };
|
||||
@ -490,6 +491,8 @@
|
||||
4B55CE5E1C3B7D960093A61B /* MachineDocument.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MachineDocument.swift; sourceTree = "<group>"; };
|
||||
4B59199A1DAC6C46005BB85C /* OricTAP.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OricTAP.cpp; sourceTree = "<group>"; };
|
||||
4B59199B1DAC6C46005BB85C /* OricTAP.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OricTAP.hpp; sourceTree = "<group>"; };
|
||||
4B5A12551DD55862007A2231 /* Disassembler6502.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Disassembler6502.cpp; path = ../../StaticAnalyser/Disassembler/Disassembler6502.cpp; sourceTree = "<group>"; };
|
||||
4B5A12561DD55862007A2231 /* Disassembler6502.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Disassembler6502.hpp; path = ../../StaticAnalyser/Disassembler/Disassembler6502.hpp; sourceTree = "<group>"; };
|
||||
4B643F381D77AD1900D431D6 /* CSStaticAnalyser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSStaticAnalyser.h; path = StaticAnalyser/CSStaticAnalyser.h; sourceTree = "<group>"; };
|
||||
4B643F391D77AD1900D431D6 /* CSStaticAnalyser.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CSStaticAnalyser.mm; path = StaticAnalyser/CSStaticAnalyser.mm; sourceTree = "<group>"; };
|
||||
4B643F3C1D77AE5C00D431D6 /* CSMachine+Target.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CSMachine+Target.h"; sourceTree = "<group>"; };
|
||||
@ -1145,6 +1148,15 @@
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B5A12581DD55873007A2231 /* Disassembler */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B5A12551DD55862007A2231 /* Disassembler6502.cpp */,
|
||||
4B5A12561DD55862007A2231 /* Disassembler6502.hpp */,
|
||||
);
|
||||
name = Disassembler;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B643F3B1D77AD6D00D431D6 /* StaticAnalyser */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -1851,6 +1863,7 @@
|
||||
4BA799961D8B65730045123D /* Atari */,
|
||||
4BC830D21D6E7C6D0000A26F /* Commodore */,
|
||||
4BCF1FAC1DADD41F0039D2E7 /* Oric */,
|
||||
4B5A12581DD55873007A2231 /* Disassembler */,
|
||||
);
|
||||
name = StaticAnalyser;
|
||||
sourceTree = "<group>";
|
||||
@ -2313,6 +2326,7 @@
|
||||
4B6C73BD1D387AE500AFCFCA /* DiskController.cpp in Sources */,
|
||||
4B643F3A1D77AD1900D431D6 /* CSStaticAnalyser.mm in Sources */,
|
||||
4B4DC8281D2C2470003C5BF8 /* C1540.cpp in Sources */,
|
||||
4B5A12571DD55862007A2231 /* Disassembler6502.cpp in Sources */,
|
||||
4B1E85751D170228001EF87D /* Typer.cpp in Sources */,
|
||||
4BF829631D8F536B001BAE39 /* SSD.cpp in Sources */,
|
||||
4B2E2D9D1C3A070400138695 /* Electron.cpp in Sources */,
|
||||
|
@ -25,8 +25,11 @@
|
||||
self = [super init];
|
||||
if(self)
|
||||
{
|
||||
NSData *rom = [self rom:@"basic11"]; // test108j
|
||||
if(rom) _oric.set_rom(rom.stdVector8);
|
||||
NSData *basic10 = [self rom:@"basic10"];
|
||||
NSData *basic11 = [self rom:@"basic11"]; // test108j
|
||||
|
||||
if(basic10) _oric.set_rom(Oric::BASIC10, basic10.stdVector8);
|
||||
if(basic11) _oric.set_rom(Oric::BASIC11, basic11.stdVector8);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
317
StaticAnalyser/Disassembler/Disassembler6502.cpp
Normal file
317
StaticAnalyser/Disassembler/Disassembler6502.cpp
Normal file
@ -0,0 +1,317 @@
|
||||
//
|
||||
// Disassembler6502.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 10/11/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "Disassembler6502.hpp"
|
||||
#include <map>
|
||||
|
||||
using namespace StaticAnalyser::MOS6502;
|
||||
|
||||
struct PartialDisassembly {
|
||||
Disassembly disassembly;
|
||||
std::vector<uint16_t> remaining_entry_points;
|
||||
};
|
||||
|
||||
static void AddToDisassembly(PartialDisassembly &disassembly, const std::vector<uint8_t> &memory, uint16_t start_address, uint16_t entry_point)
|
||||
{
|
||||
uint16_t address = entry_point;
|
||||
while(1)
|
||||
{
|
||||
uint16_t local_address = address - start_address;
|
||||
if(local_address >= memory.size()) return;
|
||||
|
||||
struct Instruction instruction;
|
||||
instruction.address = address;
|
||||
address++;
|
||||
|
||||
// get operation
|
||||
uint8_t operation = memory[local_address];
|
||||
|
||||
// decode addressing mode
|
||||
switch(operation&0x1f)
|
||||
{
|
||||
case 0x00:
|
||||
if(operation >= 0x80) instruction.addressing_mode = Instruction::Immediate;
|
||||
else if(operation == 0x20) instruction.addressing_mode = Instruction::Absolute;
|
||||
else instruction.addressing_mode = Instruction::Implied;
|
||||
break;
|
||||
case 0x08: case 0x18: case 0x0a: case 0x1a: case 0x12:
|
||||
instruction.addressing_mode = Instruction::Implied;
|
||||
break;
|
||||
case 0x10:
|
||||
instruction.addressing_mode = Instruction::Relative;
|
||||
break;
|
||||
case 0x01: case 0x03:
|
||||
instruction.addressing_mode = Instruction::IndexedIndirectX;
|
||||
break;
|
||||
case 0x02: case 0x09: case 0x0b:
|
||||
instruction.addressing_mode = Instruction::Immediate;
|
||||
break;
|
||||
case 0x04: case 0x05: case 0x06: case 0x07:
|
||||
instruction.addressing_mode = Instruction::ZeroPage;
|
||||
break;
|
||||
case 0x0c: case 0x0d: case 0x0e: case 0x0f:
|
||||
instruction.addressing_mode = (operation == 0x6c) ? Instruction::Indirect : Instruction::Absolute;
|
||||
break;
|
||||
case 0x11: case 0x13:
|
||||
instruction.addressing_mode = Instruction::IndirectIndexedY;
|
||||
break;
|
||||
case 0x14: case 0x15: case 0x16: case 0x17:
|
||||
instruction.addressing_mode =
|
||||
(operation == 0x96 || operation == 0xb6 || operation == 0x97 || operation == 0xb7)
|
||||
? Instruction::ZeroPageY : Instruction::ZeroPageX;
|
||||
break;
|
||||
case 0x19: case 0x1b:
|
||||
instruction.addressing_mode = Instruction::AbsoluteY;
|
||||
break;
|
||||
case 0x1c: case 0x1d: case 0x1e: case 0x1f:
|
||||
instruction.addressing_mode =
|
||||
(operation == 0x9e || operation == 0xbe || operation == 0x9f || operation == 0xbf)
|
||||
? Instruction::AbsoluteY : Instruction::AbsoluteX;
|
||||
break;
|
||||
}
|
||||
|
||||
// decode operation
|
||||
#define RM_INSTRUCTION(base, op) \
|
||||
case base+0x09: case base+0x05: case base+0x15: case base+0x01: case base+0x11: case base+0x0d: case base+0x1d: case base+0x19: \
|
||||
instruction.operation = op; \
|
||||
break;
|
||||
|
||||
#define URM_INSTRUCTION(base, op) \
|
||||
case base+0x07: case base+0x17: case base+0x03: case base+0x13: case base+0x0f: case base+0x1f: case base+0x1b: \
|
||||
instruction.operation = op; \
|
||||
break;
|
||||
|
||||
#define M_INSTRUCTION(base, op) \
|
||||
case base+0x0a: case base+0x06: case base+0x16: case base+0x0e: case base+0x1e: \
|
||||
instruction.operation = op; \
|
||||
break;
|
||||
|
||||
#define IM_INSTRUCTION(base, op) \
|
||||
case base: instruction.operation = op; break;
|
||||
switch(operation)
|
||||
{
|
||||
IM_INSTRUCTION(0x00, Instruction::BRK)
|
||||
IM_INSTRUCTION(0x20, Instruction::JSR)
|
||||
IM_INSTRUCTION(0x40, Instruction::RTI)
|
||||
IM_INSTRUCTION(0x60, Instruction::RTS)
|
||||
case 0x4c: case 0x6c:
|
||||
instruction.operation = Instruction::JMP;
|
||||
break;
|
||||
|
||||
IM_INSTRUCTION(0x10, Instruction::BPL)
|
||||
IM_INSTRUCTION(0x30, Instruction::BMI)
|
||||
IM_INSTRUCTION(0x50, Instruction::BVC)
|
||||
IM_INSTRUCTION(0x70, Instruction::BVS)
|
||||
IM_INSTRUCTION(0x90, Instruction::BCC)
|
||||
IM_INSTRUCTION(0xb0, Instruction::BCS)
|
||||
IM_INSTRUCTION(0xd0, Instruction::BNE)
|
||||
IM_INSTRUCTION(0xf0, Instruction::BEQ)
|
||||
|
||||
IM_INSTRUCTION(0xca, Instruction::DEX)
|
||||
IM_INSTRUCTION(0x88, Instruction::DEY)
|
||||
IM_INSTRUCTION(0xe8, Instruction::INX)
|
||||
IM_INSTRUCTION(0xc8, Instruction::INY)
|
||||
|
||||
IM_INSTRUCTION(0xaa, Instruction::TAX)
|
||||
IM_INSTRUCTION(0x8a, Instruction::TXA)
|
||||
IM_INSTRUCTION(0xa8, Instruction::TAY)
|
||||
IM_INSTRUCTION(0x98, Instruction::TYA)
|
||||
IM_INSTRUCTION(0xba, Instruction::TSX)
|
||||
IM_INSTRUCTION(0x9a, Instruction::TXS)
|
||||
|
||||
IM_INSTRUCTION(0x68, Instruction::PLA)
|
||||
IM_INSTRUCTION(0x48, Instruction::PHA)
|
||||
IM_INSTRUCTION(0x28, Instruction::PLP)
|
||||
IM_INSTRUCTION(0x08, Instruction::PHP)
|
||||
|
||||
IM_INSTRUCTION(0x18, Instruction::CLC)
|
||||
IM_INSTRUCTION(0x38, Instruction::SEC)
|
||||
IM_INSTRUCTION(0xd8, Instruction::CLD)
|
||||
IM_INSTRUCTION(0xf8, Instruction::SED)
|
||||
IM_INSTRUCTION(0x58, Instruction::CLI)
|
||||
IM_INSTRUCTION(0x78, Instruction::SEI)
|
||||
IM_INSTRUCTION(0xb8, Instruction::CLV)
|
||||
|
||||
URM_INSTRUCTION(0x00, Instruction::SLO)
|
||||
URM_INSTRUCTION(0x20, Instruction::RLA)
|
||||
URM_INSTRUCTION(0x40, Instruction::SRE)
|
||||
URM_INSTRUCTION(0x60, Instruction::RRA)
|
||||
|
||||
RM_INSTRUCTION(0x00, Instruction::ORA)
|
||||
RM_INSTRUCTION(0x20, Instruction::AND)
|
||||
RM_INSTRUCTION(0x40, Instruction::EOR)
|
||||
case 0x24: case 0x2c:
|
||||
instruction.operation = Instruction::BIT;
|
||||
break;
|
||||
RM_INSTRUCTION(0x60, Instruction::ADC)
|
||||
RM_INSTRUCTION(0xc0, Instruction::CMP)
|
||||
RM_INSTRUCTION(0xe0, Instruction::SBC)
|
||||
|
||||
M_INSTRUCTION(0x00, Instruction::ASL)
|
||||
M_INSTRUCTION(0x20, Instruction::ROL)
|
||||
M_INSTRUCTION(0x40, Instruction::LSR)
|
||||
M_INSTRUCTION(0x60, Instruction::ROR)
|
||||
|
||||
case 0xe0: case 0xe4: case 0xec: instruction.operation = Instruction::CPX; break;
|
||||
case 0xc0: case 0xc4: case 0xcc: instruction.operation = Instruction::CPY; break;
|
||||
case 0xc6: case 0xd6: case 0xce: case 0xde: instruction.operation = Instruction::DEC; break;
|
||||
case 0xe6: case 0xf6: case 0xee: case 0xfe: instruction.operation = Instruction::INC; break;
|
||||
|
||||
RM_INSTRUCTION(0xa0, Instruction::LDA)
|
||||
case 0x85: case 0x95: case 0x81: case 0x91: case 0x8d: case 0x9d: case 0x99:
|
||||
instruction.operation = Instruction::STA;
|
||||
break;
|
||||
case 0xa2: case 0xa6: case 0xb6: case 0xae: case 0xbe:
|
||||
instruction.operation = Instruction::LDX;
|
||||
break;
|
||||
case 0x86: case 0x96: case 0x8e:
|
||||
instruction.operation = Instruction::STX;
|
||||
break;
|
||||
case 0xa0: case 0xa4: case 0xb4: case 0xac: case 0xbc:
|
||||
instruction.operation = Instruction::LDY;
|
||||
break;
|
||||
case 0x84: case 0x94: case 0x8c:
|
||||
instruction.operation = Instruction::STY;
|
||||
break;
|
||||
|
||||
case 0x04: case 0x0c: case 0x14: case 0x1a: case 0x1c:
|
||||
case 0x34: case 0x3a: case 0x3c: case 0x44: case 0x54: case 0x5a: case 0x5c:
|
||||
case 0x64: case 0x74: case 0x7a: case 0x7c:
|
||||
case 0x80: case 0x82: case 0x89:
|
||||
case 0xc2: case 0xd4: case 0xda: case 0xdc:
|
||||
case 0xe2: case 0xea: case 0xf4: case 0xfa: case 0xfc:
|
||||
instruction.operation = Instruction::NOP;
|
||||
break;
|
||||
|
||||
case 0x87: case 0x97: case 0x83: case 0x8f:
|
||||
instruction.operation = Instruction::SAX;
|
||||
break;
|
||||
case 0xa7: case 0xb7: case 0xa3: case 0xb3: case 0xaf: case 0xbf:
|
||||
instruction.operation = Instruction::LAX;
|
||||
break;
|
||||
URM_INSTRUCTION(0xc0, Instruction::DCP)
|
||||
URM_INSTRUCTION(0xe0, Instruction::ISC)
|
||||
|
||||
case 0x0b: case 0x2b:
|
||||
instruction.operation = Instruction::ANC;
|
||||
break;
|
||||
|
||||
IM_INSTRUCTION(0x4b, Instruction::ALR)
|
||||
IM_INSTRUCTION(0x6b, Instruction::ARR)
|
||||
IM_INSTRUCTION(0x8b, Instruction::XAA)
|
||||
IM_INSTRUCTION(0xab, Instruction::LAX)
|
||||
IM_INSTRUCTION(0xcb, Instruction::AXS)
|
||||
IM_INSTRUCTION(0xeb, Instruction::SBC)
|
||||
case 0x93: case 0x9f:
|
||||
instruction.operation = Instruction::AHX;
|
||||
break;
|
||||
IM_INSTRUCTION(0x9c, Instruction::SHY)
|
||||
IM_INSTRUCTION(0x9e, Instruction::SHX)
|
||||
IM_INSTRUCTION(0x9b, Instruction::TAS)
|
||||
IM_INSTRUCTION(0xbb, Instruction::LAS)
|
||||
}
|
||||
#undef RM_INSTRUCTION
|
||||
#undef URM_INSTRUCTION
|
||||
#undef M_INSTRUCTION
|
||||
#undef IM_INSTRUCTION
|
||||
|
||||
// get operand
|
||||
switch(instruction.addressing_mode)
|
||||
{
|
||||
// zero-byte operands
|
||||
case Instruction::Implied:
|
||||
instruction.operand = 0;
|
||||
break;
|
||||
|
||||
// one-byte operands
|
||||
case Instruction::Immediate:
|
||||
case Instruction::ZeroPage: case Instruction::ZeroPageX: case Instruction::ZeroPageY:
|
||||
case Instruction::IndexedIndirectX: case Instruction::IndirectIndexedY:
|
||||
case Instruction::Relative:
|
||||
{
|
||||
uint16_t operand_address = address - start_address;
|
||||
if(operand_address >= memory.size()) return;
|
||||
address++;
|
||||
|
||||
instruction.operand = memory[operand_address];
|
||||
}
|
||||
break;
|
||||
|
||||
// two-byte operands
|
||||
case Instruction::Absolute: case Instruction::AbsoluteX: case Instruction::AbsoluteY:
|
||||
case Instruction::Indirect:
|
||||
{
|
||||
uint16_t operand_address = address - start_address;
|
||||
if(operand_address >= memory.size()-1) return;
|
||||
address += 2;
|
||||
|
||||
instruction.operand = memory[operand_address] | (uint16_t)(memory[operand_address+1] << 8);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// store the instruction away
|
||||
disassembly.disassembly.instructions_by_address[instruction.address] = instruction;
|
||||
|
||||
// TODO: something wider-ranging than this
|
||||
if(instruction.addressing_mode == Instruction::Absolute && (instruction.operand < start_address || instruction.operand >= start_address + memory.size()))
|
||||
{
|
||||
if( instruction.operation == Instruction::STY ||
|
||||
instruction.operation == Instruction::STX ||
|
||||
instruction.operation == Instruction::STA)
|
||||
disassembly.disassembly.external_stores.insert(instruction.operand);
|
||||
if( instruction.operation == Instruction::LDY ||
|
||||
instruction.operation == Instruction::LDX ||
|
||||
instruction.operation == Instruction::LDA)
|
||||
disassembly.disassembly.external_loads.insert(instruction.operand);
|
||||
}
|
||||
|
||||
// decide on overall flow control
|
||||
if(instruction.operation == Instruction::RTS || instruction.operation == Instruction::RTI) return;
|
||||
if(instruction.operation == Instruction::BRK) return; // TODO: check whether IRQ vector is within memory range
|
||||
if(instruction.operation == Instruction::JSR)
|
||||
{
|
||||
disassembly.remaining_entry_points.push_back(instruction.operand);
|
||||
}
|
||||
if(instruction.operation == Instruction::JMP)
|
||||
{
|
||||
if(instruction.addressing_mode == Instruction::Absolute)
|
||||
disassembly.remaining_entry_points.push_back(instruction.operand);
|
||||
return;
|
||||
}
|
||||
if(instruction.addressing_mode == Instruction::Relative)
|
||||
{
|
||||
uint16_t destination = (uint16_t)(address + (int8_t)instruction.operand);
|
||||
disassembly.remaining_entry_points.push_back(destination);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Disassembly StaticAnalyser::MOS6502::Disassemble(const std::vector<uint8_t> &memory, uint16_t start_address, std::vector<uint16_t> entry_points)
|
||||
{
|
||||
PartialDisassembly partialDisassembly;
|
||||
partialDisassembly.remaining_entry_points = entry_points;
|
||||
|
||||
while(!partialDisassembly.remaining_entry_points.empty())
|
||||
{
|
||||
// pull the next entry point from the back of the vector
|
||||
uint16_t next_entry_point = partialDisassembly.remaining_entry_points.back();
|
||||
partialDisassembly.remaining_entry_points.pop_back();
|
||||
|
||||
// if that address has already bene visited, forget about it
|
||||
if(partialDisassembly.disassembly.instructions_by_address.find(next_entry_point) != partialDisassembly.disassembly.instructions_by_address.end()) continue;
|
||||
|
||||
// if it's outgoing, log it as such and forget about it; otherwise disassemble
|
||||
if(next_entry_point < start_address || next_entry_point >= start_address + memory.size())
|
||||
partialDisassembly.disassembly.outward_calls.insert(next_entry_point);
|
||||
else
|
||||
AddToDisassembly(partialDisassembly, memory, start_address, next_entry_point);
|
||||
}
|
||||
|
||||
return std::move(partialDisassembly.disassembly);
|
||||
}
|
75
StaticAnalyser/Disassembler/Disassembler6502.hpp
Normal file
75
StaticAnalyser/Disassembler/Disassembler6502.hpp
Normal file
@ -0,0 +1,75 @@
|
||||
//
|
||||
// Disassembler6502.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 10/11/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Disassembler6502_hpp
|
||||
#define Disassembler6502_hpp
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
namespace StaticAnalyser {
|
||||
namespace MOS6502 {
|
||||
|
||||
struct Instruction {
|
||||
uint16_t address;
|
||||
enum {
|
||||
BRK, JSR, RTI, RTS, JMP,
|
||||
CLC, SEC, CLD, SED, CLI, SEI, CLV,
|
||||
NOP,
|
||||
|
||||
SLO, RLA, SRE, RRA, ALR, ARR,
|
||||
SAX, LAX, DCP, ISC,
|
||||
ANC, XAA, AXS,
|
||||
AND, EOR, ORA, BIT,
|
||||
ADC, SBC,
|
||||
AHX, SHY, SHX, TAS, LAS,
|
||||
|
||||
LDA, STA, LDX, STX, LDY, STY,
|
||||
|
||||
BPL, BMI, BVC, BVS, BCC, BCS, BNE, BEQ,
|
||||
|
||||
CMP, CPX, CPY,
|
||||
INC, DEC, DEX, DEY, INX, INY,
|
||||
ASL, ROL, LSR, ROR,
|
||||
TAX, TXA, TAY, TYA, TSX, TXS,
|
||||
PLA, PHA, PLP, PHP,
|
||||
|
||||
KIL
|
||||
} operation;
|
||||
enum {
|
||||
Absolute,
|
||||
AbsoluteX,
|
||||
AbsoluteY,
|
||||
Immediate,
|
||||
Implied,
|
||||
ZeroPage,
|
||||
ZeroPageX,
|
||||
ZeroPageY,
|
||||
Indirect,
|
||||
IndexedIndirectX,
|
||||
IndirectIndexedY,
|
||||
Relative,
|
||||
} addressing_mode;
|
||||
uint16_t operand;
|
||||
};
|
||||
|
||||
struct Disassembly {
|
||||
std::map<uint16_t, Instruction> instructions_by_address;
|
||||
std::set<uint16_t> outward_calls;
|
||||
std::set<uint16_t> external_stores, external_loads, external_modifies;
|
||||
};
|
||||
|
||||
Disassembly Disassemble(const std::vector<uint8_t> &memory, uint16_t start_address, std::vector<uint16_t> entry_points);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* Disassembler6502_hpp */
|
@ -9,9 +9,72 @@
|
||||
#include "StaticAnalyser.hpp"
|
||||
|
||||
#include "Tape.hpp"
|
||||
#include "../Disassembler/Disassembler6502.hpp"
|
||||
|
||||
using namespace StaticAnalyser::Oric;
|
||||
|
||||
static int Score(const StaticAnalyser::MOS6502::Disassembly &disassembly, const std::set<uint16_t> &rom_functions, const std::set<uint16_t> &variable_locations)
|
||||
{
|
||||
int score = 0;
|
||||
|
||||
for(auto address : disassembly.outward_calls) score += (rom_functions.find(address) != rom_functions.end()) ? 1 : -1;
|
||||
for(auto address : disassembly.external_stores) score += (variable_locations.find(address) != variable_locations.end()) ? 1 : -1;
|
||||
for(auto address : disassembly.external_loads) score += (variable_locations.find(address) != variable_locations.end()) ? 1 : -1;
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
static int Basic10Score(const StaticAnalyser::MOS6502::Disassembly &disassembly)
|
||||
{
|
||||
std::set<uint16_t> rom_functions = {
|
||||
0x0228, 0x022b,
|
||||
0xc3ca, 0xc3f8, 0xc448, 0xc47c, 0xc4b5, 0xc4e3, 0xc4e0, 0xc524, 0xc56f, 0xc5a2, 0xc5f8, 0xc60a, 0xc6a5, 0xc6de, 0xc719, 0xc738,
|
||||
0xc773, 0xc824, 0xc832, 0xc841, 0xc8c1, 0xc8fe, 0xc91f, 0xc93f, 0xc941, 0xc91e, 0xc98b, 0xc996, 0xc9b3, 0xc9e0, 0xca0a, 0xca1c,
|
||||
0xca1f, 0xca3e, 0xca61, 0xca78, 0xca98, 0xcad2, 0xcb61, 0xcb9f, 0xcc59, 0xcbed, 0xcc0a, 0xcc8c, 0xcc8f, 0xccba, 0xccc9, 0xccfd,
|
||||
0xce0c, 0xce77, 0xce8b, 0xcfac, 0xcf74, 0xd03c, 0xd059, 0xcff0, 0xd087, 0xd0f2, 0xd0fc, 0xd361, 0xd3eb, 0xd47e, 0xd4a6, 0xd401,
|
||||
0xd593, 0xd5a3, 0xd4fa, 0xd595, 0xd730, 0xd767, 0xd816, 0xd82a, 0xd856, 0xd861, 0xd8a6, 0xd8b5, 0xd80a, 0xd867, 0xd938, 0xd894,
|
||||
0xd89d, 0xd8ac, 0xd983, 0xd993, 0xd9b5, 0xd93d, 0xd965, 0xda3f, 0xd9c6, 0xda16, 0xdaab, 0xdada, 0xda6b, 0xdb92, 0xdbb9, 0xdc79,
|
||||
0xdd4d, 0xdda3, 0xddbf, 0xd0d0, 0xde77, 0xdef4, 0xdf0b, 0xdf0f, 0xdf04, 0xdf12, 0xdf31, 0xdf4c, 0xdf8c, 0xdfa5, 0xdfcf, 0xe076,
|
||||
0xe0c1, 0xe22a, 0xe27c, 0xe2a6, 0xe313, 0xe34b, 0xe387, 0xe38e, 0xe3d7, 0xe407, 0xe43b, 0xe46f, 0xe4a8, 0xe4f2, 0xe554, 0xe57d,
|
||||
0xe585, 0xe58c, 0xe594, 0xe5a4, 0xe5ab, 0xe5b6, 0xe5ea, 0xe563, 0xe5c6, 0xe630, 0xe696, 0xe6ba, 0xe6ca, 0xe725, 0xe7aa, 0xe903,
|
||||
0xe7db, 0xe80d, 0xe987, 0xe9d1, 0xe87d, 0xe905, 0xe965, 0xe974, 0xe994, 0xe9a9, 0xe9bb, 0xec45, 0xeccc, 0xedc4, 0xecc7, 0xed01,
|
||||
0xed09, 0xed70, 0xed81, 0xed8f, 0xe0ad, 0xeee8, 0xeef8, 0xebdf, 0xebe2, 0xebe5, 0xebeb, 0xebee, 0xebf4, 0xebf7, 0xebfa, 0xebe8,
|
||||
0xf43c, 0xf4ef, 0xf523, 0xf561, 0xf535, 0xf57b, 0xf5d3, 0xf71a, 0xf73f, 0xf7e4, 0xf7e0, 0xf82f, 0xf88f, 0xf8af, 0xf8b5, 0xf920,
|
||||
0xf967, 0xf960, 0xf9c9, 0xfa14, 0xfa85, 0xfa9b, 0xfab1, 0xfac7, 0xfafa, 0xfb10, 0xfb26, 0xfbb6, 0xfbfe
|
||||
};
|
||||
std::set<uint16_t> variable_locations = {
|
||||
0x0228, 0x0229, 0x022a, 0x022b, 0x022c, 0x022d, 0x0230
|
||||
};
|
||||
|
||||
return Score(disassembly, rom_functions, variable_locations);
|
||||
}
|
||||
|
||||
static int Basic11Score(const StaticAnalyser::MOS6502::Disassembly &disassembly)
|
||||
{
|
||||
std::set<uint16_t> rom_functions = {
|
||||
0x0238, 0x023b, 0x023e, 0x0241, 0x0244, 0x0247,
|
||||
0xc3c6, 0xc3f4, 0xc444, 0xc47c, 0xc4a8, 0xc4d3, 0xc4e0, 0xc524, 0xc55f, 0xc592, 0xc5e8, 0xc5fa, 0xc692, 0xc6b3, 0xc6ee, 0xc70d,
|
||||
0xc748, 0xc7fd, 0xc809, 0xc816, 0xc82f, 0xc855, 0xc8c1, 0xc915, 0xc952, 0xc971, 0xc973, 0xc9a0, 0xc9bd, 0xc9c8, 0xc9e5, 0xca12,
|
||||
0xca3c, 0xca4e, 0xca51, 0xca70, 0xca99, 0xcac2, 0xcae2, 0xcb1c, 0xcbab, 0xcbf0, 0xcc59, 0xccb0, 0xccce, 0xcd16, 0xcd19, 0xcd46,
|
||||
0xcd55, 0xcd89, 0xce98, 0xcf03, 0xcf17, 0xcfac, 0xd000, 0xd03c, 0xd059, 0xd07c, 0xd113, 0xd17e, 0xd188, 0xd361, 0xd3eb, 0xd47e,
|
||||
0xd4a6, 0xd4ba, 0xd593, 0xd5a3, 0xd5b5, 0xd650, 0xd730, 0xd767, 0xd816, 0xd82a, 0xd856, 0xd861, 0xd8a6, 0xd8b5, 0xd8c5, 0xd922,
|
||||
0xd938, 0xd94f, 0xd958, 0xd967, 0xd983, 0xd993, 0xd9b5, 0xd9de, 0xda0c, 0xda3f, 0xda51, 0xdaa1, 0xdaab, 0xdada, 0xdaf6, 0xdb92,
|
||||
0xdbb9, 0xdcaf, 0xdd51, 0xdda7, 0xddc3, 0xddd4, 0xde77, 0xdef4, 0xdf0b, 0xdf0f, 0xdf13, 0xdf21, 0xdf49, 0xdf4c, 0xdf8c, 0xdfbd,
|
||||
0xdfe7, 0xe076, 0xe0c5, 0xe22e, 0xe27c, 0xe2aa, 0xe313, 0xe34f, 0xe38b, 0xe392, 0xe3db, 0xe407, 0xe43f, 0xe46f, 0xe4ac, 0xe4e0,
|
||||
0xe4f2, 0xe56c, 0xe57d, 0xe585, 0xe58c, 0xe594, 0xe5a4, 0xe5ab, 0xe5b6, 0xe5ea, 0xe5f5, 0xe607, 0xe65e, 0xe6c9, 0xe735, 0xe75a,
|
||||
0xe76a, 0xe7b2, 0xe85b, 0xe903, 0xe909, 0xe946, 0xe987, 0xe9d1, 0xeaf0, 0xeb78, 0xebce, 0xebe7, 0xec0c, 0xec21, 0xec33, 0xec45,
|
||||
0xeccc, 0xedc4, 0xede0, 0xee1a, 0xee22, 0xee8c, 0xee9d, 0xeeab, 0xeec9, 0xeee8, 0xeef8, 0xf0c8, 0xf0fd, 0xf110, 0xf11d, 0xf12d,
|
||||
0xf204, 0xf210, 0xf268, 0xf37f, 0xf495, 0xf4ef, 0xf523, 0xf561, 0xf590, 0xf5c1, 0xf602, 0xf71a, 0xf77c, 0xf7e4, 0xf816, 0xf865,
|
||||
0xf88f, 0xf8af, 0xf8b5, 0xf920, 0xf967, 0xf9aa, 0xf9c9, 0xfa14, 0xfa9f, 0xfab5, 0xfacb, 0xfae1, 0xfb14, 0xfb2a, 0xfb40, 0xfbd0,
|
||||
0xfc18
|
||||
};
|
||||
std::set<uint16_t> variable_locations = {
|
||||
0x0244, 0x0245, 0x0246, 0x0247, 0x0248, 0x0249, 0x024a, 0x024b, 0x024c
|
||||
};
|
||||
|
||||
return Score(disassembly, rom_functions, variable_locations);
|
||||
}
|
||||
|
||||
void StaticAnalyser::Oric::AddTargets(
|
||||
const std::list<std::shared_ptr<Storage::Disk::Disk>> &disks,
|
||||
const std::list<std::shared_ptr<Storage::Tape::Tape>> &tapes,
|
||||
@ -22,16 +85,36 @@ void StaticAnalyser::Oric::AddTargets(
|
||||
target.machine = Target::Oric;
|
||||
target.probability = 1.0;
|
||||
|
||||
int basic10_votes = 0;
|
||||
int basic11_votes = 0;
|
||||
|
||||
for(auto tape : tapes)
|
||||
{
|
||||
std::list<File> tape_files = GetFiles(tape);
|
||||
if(tape_files.size())
|
||||
{
|
||||
for(auto file : tape_files)
|
||||
{
|
||||
if(file.data_type == File::MachineCode)
|
||||
{
|
||||
std::vector<uint16_t> entry_points = {file.starting_address};
|
||||
StaticAnalyser::MOS6502::Disassembly disassembly =
|
||||
StaticAnalyser::MOS6502::Disassemble(file.data, file.starting_address, entry_points);
|
||||
|
||||
int basic10_score = Basic10Score(disassembly);
|
||||
int basic11_score = Basic11Score(disassembly);
|
||||
if(basic10_score > basic11_score) basic10_votes++; else basic11_votes++;
|
||||
}
|
||||
}
|
||||
|
||||
target.tapes.push_back(tape);
|
||||
target.loadingCommand = "CLOAD\"\"\n";
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: really this should add two targets if not all votes agree
|
||||
target.oric.use_atmos_rom = basic11_votes >= basic10_votes;
|
||||
|
||||
if(target.tapes.size() || target.disks.size() || target.cartridges.size())
|
||||
destination.push_back(target);
|
||||
}
|
||||
|
@ -49,6 +49,10 @@ struct Target {
|
||||
bool has_dfs;
|
||||
bool should_hold_shift;
|
||||
} acorn;
|
||||
|
||||
struct {
|
||||
bool use_atmos_rom;
|
||||
} oric;
|
||||
};
|
||||
|
||||
std::string loadingCommand;
|
||||
|
@ -97,6 +97,10 @@ Tape::Pulse OricTAP::virtual_get_next_pulse()
|
||||
{
|
||||
_next_phase = Gap;
|
||||
}
|
||||
if(feof(_file))
|
||||
{
|
||||
_next_phase = End;
|
||||
}
|
||||
_phase_counter++;
|
||||
break;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user