mirror of
https://github.com/TomHarte/CLK.git
synced 2024-09-29 16:55:59 +00:00
Gets as far as executing a first loop.
This commit is contained in:
parent
e502d76371
commit
e58608b25a
@ -118,9 +118,10 @@ template <
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
void perform_all() {
|
/*!
|
||||||
// TEMPORARY (?). Execute all current instructions, unless
|
Executes up to the next branch.
|
||||||
// and until one branches.
|
*/
|
||||||
|
void run_to_branch() {
|
||||||
has_branched_ = false;
|
has_branched_ = false;
|
||||||
for(auto index: program_) {
|
for(auto index: program_) {
|
||||||
const auto performer = performers_[index];
|
const auto performer = performers_[index];
|
||||||
|
@ -37,7 +37,9 @@ void Executor::set_rom(const std::vector<uint8_t> &rom) {
|
|||||||
reset();
|
reset();
|
||||||
|
|
||||||
// TEMPORARY: just to test initial wiring.
|
// TEMPORARY: just to test initial wiring.
|
||||||
perform_all();
|
for(int c = 0; c < 130; c++) {
|
||||||
|
run_to_branch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Executor::reset() {
|
void Executor::reset() {
|
||||||
@ -45,6 +47,49 @@ void Executor::reset() {
|
|||||||
set_program_counter(uint16_t(memory_[0x1ffe] | (memory_[0x1fff] << 8)));
|
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 <Operation operation, AddressingMode addressing_mode> void Executor::perform() {
|
template <Operation operation, AddressingMode addressing_mode> void Executor::perform() {
|
||||||
// Deal with all modes that don't access memory up here;
|
// Deal with all modes that don't access memory up here;
|
||||||
// those that access memory will go through a slightly longer
|
// those that access memory will go through a slightly longer
|
||||||
@ -55,7 +100,7 @@ template <Operation operation, AddressingMode addressing_mode> void Executor::pe
|
|||||||
#define next8() memory_[(program_counter_ + 1) & 0x1fff]
|
#define next8() memory_[(program_counter_ + 1) & 0x1fff]
|
||||||
#define next16() (memory_[(program_counter_ + 1) & 0x1fff] | (memory_[(program_counter_ + 2) & 0x1fff] << 8))
|
#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
|
// Underlying assumption below: the instruction stream will never
|
||||||
// overlap with IO ports.
|
// overlap with IO ports.
|
||||||
@ -78,17 +123,19 @@ template <Operation operation, AddressingMode addressing_mode> void Executor::pe
|
|||||||
program_counter_ += 2;
|
program_counter_ += 2;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// case AddressingMode::Relative:
|
// Special-purpose addressing modes.
|
||||||
// These are all the branches...
|
|
||||||
// address = program_counter_ + size(addressing_mode) + int8_t(next8());
|
|
||||||
// return;
|
|
||||||
|
|
||||||
// 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...
|
// LDM only...
|
||||||
// return;
|
write(memory_[(program_counter_+2)&0x1fff], memory_[(program_counter_+1)&0x1fff]);
|
||||||
|
program_counter_ += 1 + size(addressing_mode);
|
||||||
// case AddressingMode::SpecialPage: address = 0x1f00 | next8(); break;
|
return;
|
||||||
// JSR only...
|
|
||||||
|
|
||||||
/* TODO:
|
/* TODO:
|
||||||
|
|
||||||
@ -136,16 +183,23 @@ template <Operation operation, AddressingMode addressing_mode> void Executor::pe
|
|||||||
|
|
||||||
// Check for a branch; those don't go through the memory accesses below.
|
// Check for a branch; those don't go through the memory accesses below.
|
||||||
switch(operation) {
|
switch(operation) {
|
||||||
case Operation::JMP:
|
case Operation::BRA: case Operation::JMP:
|
||||||
set_program_counter(uint16_t(address));
|
set_program_counter(uint16_t(address));
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case Operation::JSR:
|
case Operation::JSR: {
|
||||||
// TODO: push!
|
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));
|
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;
|
default: break;
|
||||||
}
|
}
|
||||||
@ -153,18 +207,20 @@ template <Operation operation, AddressingMode addressing_mode> void Executor::pe
|
|||||||
|
|
||||||
assert(access_type(operation) != AccessType::None);
|
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) {
|
if constexpr(access_type(operation) == AccessType::Read) {
|
||||||
perform<operation>(&memory_[address & 0x1fff]);
|
uint8_t source = read(uint16_t(address));
|
||||||
|
perform<operation>(&source);
|
||||||
return;
|
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<operation>(&value);
|
perform<operation>(&value);
|
||||||
|
write(uint16_t(address), value);
|
||||||
memory_[address & 0x1fff] = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <Operation operation> void Executor::perform(uint8_t *operand [[maybe_unused]]) {
|
template <Operation operation> void Executor::perform(uint8_t *operand [[maybe_unused]]) {
|
||||||
@ -201,6 +257,20 @@ template <Operation operation> void Executor::perform(uint8_t *operand [[maybe_u
|
|||||||
case Operation::CLD: decimal_mode_ = false; break;
|
case Operation::CLD: decimal_mode_ = false; break;
|
||||||
case Operation::SED: decimal_mode_ = true; 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:
|
default:
|
||||||
printf("Unimplemented operation: %d\n", int(operation));
|
printf("Unimplemented operation: %d\n", int(operation));
|
||||||
assert(false);
|
assert(false);
|
||||||
@ -209,6 +279,7 @@ template <Operation operation> void Executor::perform(uint8_t *operand [[maybe_u
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Executor::set_program_counter(uint16_t address) {
|
void Executor::set_program_counter(uint16_t address) {
|
||||||
|
printf("--- %04x ---\n", (address & 0x1fff) - 0x1000);
|
||||||
program_counter_ = address;
|
program_counter_ = address;
|
||||||
CachingExecutor::set_program_counter(address);
|
CachingExecutor::set_program_counter(address);
|
||||||
}
|
}
|
||||||
|
@ -117,6 +117,10 @@ class Executor: public CachingExecutor {
|
|||||||
uint8_t interrupt_disable_ = 0;
|
uint8_t interrupt_disable_ = 0;
|
||||||
bool index_mode_ = false;
|
bool index_mode_ = false;
|
||||||
bool decimal_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);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user