From e58608b25ab0da94cc2a5d8a080fe76126836e48 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 20 Jan 2021 18:15:24 -0500 Subject: [PATCH] Gets as far as executing a first loop. --- InstructionSets/CachingExecutor.hpp | 7 +- InstructionSets/M50740/Executor.cpp | 117 ++++++++++++++++++++++------ InstructionSets/M50740/Executor.hpp | 4 + 3 files changed, 102 insertions(+), 26 deletions(-) diff --git a/InstructionSets/CachingExecutor.hpp b/InstructionSets/CachingExecutor.hpp index 1c57b7393..ada7dffd7 100644 --- a/InstructionSets/CachingExecutor.hpp +++ b/InstructionSets/CachingExecutor.hpp @@ -118,9 +118,10 @@ template < // } } - void perform_all() { - // TEMPORARY (?). Execute all current instructions, unless - // and until one branches. + /*! + Executes up to the next branch. + */ + void run_to_branch() { has_branched_ = false; for(auto index: program_) { const auto performer = performers_[index]; diff --git a/InstructionSets/M50740/Executor.cpp b/InstructionSets/M50740/Executor.cpp index 5dcb5d5af..79c1c6b19 100644 --- a/InstructionSets/M50740/Executor.cpp +++ b/InstructionSets/M50740/Executor.cpp @@ -37,7 +37,9 @@ void Executor::set_rom(const std::vector &rom) { reset(); // TEMPORARY: just to test initial wiring. - perform_all(); + for(int c = 0; c < 130; c++) { + run_to_branch(); + } } void Executor::reset() { @@ -45,6 +47,49 @@ void Executor::reset() { set_program_counter(uint16_t(memory_[0x1ffe] | (memory_[0x1fff] << 8))); } +uint8_t Executor::read(uint16_t address) { + address &= 0x1fff; + switch(address) { + default: return memory_[address]; + + // TODO: external IO ports. + + // "Port R"; sixteen four-bit ports + case 0xd0: case 0xd1: case 0xd2: case 0xd3: case 0xd4: case 0xd5: case 0xd6: case 0xd7: + case 0xd8: case 0xd9: case 0xda: case 0xdb: case 0xdc: case 0xdd: case 0xde: case 0xdf: + printf("TODO: Port R\n"); + return 0xff; + + // Ports P0–P3. + case 0xe0: case 0xe1: + case 0xe2: case 0xe3: + case 0xe4: case 0xe5: + case 0xe8: case 0xe9: + printf("TODO: Ports P0–P3\n"); + return 0xff; + + // Timers. + case 0xf9: case 0xfa: case 0xfb: case 0xfc: case 0xfd: case 0xfe: case 0xff: + printf("TODO: Timers\n"); + return 0xff; + } +} + +void Executor::write(uint16_t address, uint8_t value) { + address &= 0x1fff; + if(address < 0x60) { + memory_[address] = value; + return; + } + + // TODO: all external IO ports. +} + +void Executor::push(uint8_t value) { + write(s_, value); + --s_; +} + template void Executor::perform() { // Deal with all modes that don't access memory up here; // those that access memory will go through a slightly longer @@ -55,7 +100,7 @@ template void Executor::pe #define next8() memory_[(program_counter_ + 1) & 0x1fff] #define next16() (memory_[(program_counter_ + 1) & 0x1fff] | (memory_[(program_counter_ + 2) & 0x1fff] << 8)) - printf("%d %d\n", int(operation), int(addressing_mode)); + printf("%04x\t%d %d\n", program_counter_ & 0x1fff, int(operation), int(addressing_mode)); // Underlying assumption below: the instruction stream will never // overlap with IO ports. @@ -78,17 +123,19 @@ template void Executor::pe program_counter_ += 2; return; -// case AddressingMode::Relative: - // These are all the branches... -// address = program_counter_ + size(addressing_mode) + int8_t(next8()); -// return; + // Special-purpose addressing modes. -// case AddressingMode::ImmediateZeroPage: + case AddressingMode::Relative: + address = program_counter_ + 1 + size(addressing_mode) + int8_t(next8()); + break; + + case AddressingMode::SpecialPage: address = 0x1f00 | next8(); break; + + case AddressingMode::ImmediateZeroPage: // LDM only... -// return; - -// case AddressingMode::SpecialPage: address = 0x1f00 | next8(); break; - // JSR only... + write(memory_[(program_counter_+2)&0x1fff], memory_[(program_counter_+1)&0x1fff]); + program_counter_ += 1 + size(addressing_mode); + return; /* TODO: @@ -136,16 +183,23 @@ template void Executor::pe // Check for a branch; those don't go through the memory accesses below. switch(operation) { - case Operation::JMP: + case Operation::BRA: case Operation::JMP: set_program_counter(uint16_t(address)); return; - case Operation::JSR: - // TODO: push! + case Operation::JSR: { + const auto return_address = program_counter_ - 1; + push(uint8_t(return_address >> 8)); + push(uint8_t(return_address & 0xff)); set_program_counter(uint16_t(address)); - return; + } return; - /* TODO: all other types of branches and calls. */ + case Operation::BPL: if(!(negative_result_&0x80)) set_program_counter(uint16_t(address)); return; + case Operation::BMI: if(negative_result_&0x80) set_program_counter(uint16_t(address)); return; + case Operation::BEQ: if(!zero_result_) set_program_counter(uint16_t(address)); return; + case Operation::BNE: if(zero_result_) set_program_counter(uint16_t(address)); return; + + /* TODO: all other types of branches. */ default: break; } @@ -153,18 +207,20 @@ template void Executor::pe assert(access_type(operation) != AccessType::None); - // TODO: full reading/writing logic here; only the first 96 bytes are RAM, - // there are also timers and IO ports to handle. - if constexpr(access_type(operation) == AccessType::Read) { - perform(&memory_[address & 0x1fff]); + uint8_t source = read(uint16_t(address)); + perform(&source); return; } - uint8_t value = memory_[address & 0x1fff]; + uint8_t value; + if constexpr(access_type(operation) == AccessType::ReadModifyWrite) { + value = read(uint16_t(address)); + } else { + value = 0xff; + } perform(&value); - - memory_[address & 0x1fff] = value; + write(uint16_t(address), value); } template void Executor::perform(uint8_t *operand [[maybe_unused]]) { @@ -201,6 +257,20 @@ template void Executor::perform(uint8_t *operand [[maybe_u case Operation::CLD: decimal_mode_ = false; break; case Operation::SED: decimal_mode_ = true; break; + case Operation::DEX: --x_; set_nz(x_); break; + case Operation::INX: ++x_; set_nz(x_); break; + case Operation::DEY: --y_; set_nz(y_); break; + case Operation::INY: ++y_; set_nz(y_); break; + case Operation::DEC: --*operand; set_nz(*operand); break; + case Operation::INC: ++*operand; set_nz(*operand); break; + + /* + Already removed from the instruction stream: + + * all branches and jumps; + * LDM. + */ + default: printf("Unimplemented operation: %d\n", int(operation)); assert(false); @@ -209,6 +279,7 @@ template void Executor::perform(uint8_t *operand [[maybe_u } void Executor::set_program_counter(uint16_t address) { + printf("--- %04x ---\n", (address & 0x1fff) - 0x1000); program_counter_ = address; CachingExecutor::set_program_counter(address); } diff --git a/InstructionSets/M50740/Executor.hpp b/InstructionSets/M50740/Executor.hpp index 2433c5573..0a5f4149c 100644 --- a/InstructionSets/M50740/Executor.hpp +++ b/InstructionSets/M50740/Executor.hpp @@ -117,6 +117,10 @@ class Executor: public CachingExecutor { uint8_t interrupt_disable_ = 0; bool index_mode_ = false; bool decimal_mode_ = false; + + inline uint8_t read(uint16_t address); + inline void write(uint16_t address, uint8_t value); + inline void push(uint8_t value); }; }