mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-12 15:31:09 +00:00
Merge pull request #169 from TomHarte/Z80HasA
Converts the Z80 into a BusAdaptor-type component
This commit is contained in:
commit
985fbf59c2
@ -16,7 +16,7 @@
|
||||
|
||||
#include "../../Storage/Tape/Tape.hpp"
|
||||
|
||||
using namespace AmstradCPC;
|
||||
namespace AmstradCPC {
|
||||
|
||||
/*!
|
||||
Models the CPC's interrupt timer. Inputs are vsync, hsync, interrupt acknowledge and reset, and its output
|
||||
@ -478,10 +478,11 @@ class i8255PortHandler : public Intel::i8255::PortHandler {
|
||||
The actual Amstrad CPC implementation; tying the 8255, 6845 and AY to the Z80.
|
||||
*/
|
||||
class ConcreteMachine:
|
||||
public CPU::Z80::Processor<ConcreteMachine>,
|
||||
public CPU::Z80::BusHandler,
|
||||
public Machine {
|
||||
public:
|
||||
ConcreteMachine() :
|
||||
z80_(*this),
|
||||
crtc_counter_(HalfCycles(4)), // This starts the CRTC exactly out of phase with the memory accesses
|
||||
crtc_(crtc_bus_handler_),
|
||||
crtc_bus_handler_(ram_, interrupt_timer_),
|
||||
@ -496,7 +497,7 @@ class ConcreteMachine:
|
||||
inline HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
|
||||
// Amstrad CPC timing scheme: assert WAIT for three out of four cycles
|
||||
clock_offset_ = (clock_offset_ + cycle.length) & HalfCycles(7);
|
||||
set_wait_line(clock_offset_ >= HalfCycles(2));
|
||||
z80_.set_wait_line(clock_offset_ >= HalfCycles(2));
|
||||
|
||||
// Update the CRTC once every eight half cycles; aiming for half-cycle 4 as
|
||||
// per the initial seed to the crtc_counter_, but any time in the final four
|
||||
@ -505,7 +506,7 @@ class ConcreteMachine:
|
||||
crtc_counter_ += cycle.length;
|
||||
int crtc_cycles = crtc_counter_.divide(HalfCycles(8)).as_int();
|
||||
if(crtc_cycles) crtc_.run_for(Cycles(1));
|
||||
set_interrupt_line(interrupt_timer_.get_request());
|
||||
z80_.set_interrupt_line(interrupt_timer_.get_request());
|
||||
|
||||
// TODO (in the player, not here): adapt it to accept an input clock rate and
|
||||
// run_for as HalfCycles
|
||||
@ -629,7 +630,7 @@ class ConcreteMachine:
|
||||
|
||||
/// Wires virtual-dispatched CRTMachine run_for requests to the static Z80 method.
|
||||
void run_for(const Cycles cycles) {
|
||||
CPU::Z80::Processor<ConcreteMachine>::run_for(cycles);
|
||||
z80_.run_for(cycles);
|
||||
}
|
||||
|
||||
/// The ConfigurationTarget entry point; should configure this meachine as described by @c target.
|
||||
@ -674,6 +675,8 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
private:
|
||||
CPU::Z80::Processor<ConcreteMachine> z80_;
|
||||
|
||||
CRTCBusHandler crtc_bus_handler_;
|
||||
Motorola::CRTC::CRTC6845<CRTCBusHandler> crtc_;
|
||||
|
||||
@ -697,7 +700,11 @@ class ConcreteMachine:
|
||||
KeyboardState key_state_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using namespace AmstradCPC;
|
||||
|
||||
// See header; constructs and returns an instance of the Amstrad CPC.
|
||||
Machine *Machine::AmstradCPC() {
|
||||
return new ConcreteMachine;
|
||||
return new AmstradCPC::ConcreteMachine;
|
||||
}
|
||||
|
@ -6,149 +6,150 @@
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "Typer.hpp"
|
||||
#include "ZX8081.hpp"
|
||||
|
||||
uint16_t *ZX8081::Machine::sequence_for_character(Utility::Typer *typer, char character) {
|
||||
#define KEYS(...) {__VA_ARGS__, EndSequence}
|
||||
#define SHIFT(...) {KeyShift, __VA_ARGS__, EndSequence}
|
||||
#define X {NotMapped}
|
||||
static KeySequence zx81_key_sequences[] = {
|
||||
/* NUL */ X, /* SOH */ X,
|
||||
/* STX */ X, /* ETX */ X,
|
||||
/* EOT */ X, /* ENQ */ X,
|
||||
/* ACK */ X, /* BEL */ X,
|
||||
/* BS */ SHIFT(Key0), /* HT */ X,
|
||||
/* LF */ KEYS(KeyEnter), /* VT */ X,
|
||||
/* FF */ X, /* CR */ X,
|
||||
/* SO */ X, /* SI */ X,
|
||||
/* DLE */ X, /* DC1 */ X,
|
||||
/* DC2 */ X, /* DC3 */ X,
|
||||
/* DC4 */ X, /* NAK */ X,
|
||||
/* SYN */ X, /* ETB */ X,
|
||||
/* CAN */ X, /* EM */ X,
|
||||
/* SUB */ X, /* ESC */ X,
|
||||
/* FS */ X, /* GS */ X,
|
||||
/* RS */ X, /* US */ X,
|
||||
/* space */ KEYS(KeySpace), /* ! */ X,
|
||||
/* " */ SHIFT(KeyP), /* # */ X,
|
||||
/* $ */ SHIFT(KeyU), /* % */ X,
|
||||
/* & */ X, /* ' */ X,
|
||||
/* ( */ SHIFT(KeyI), /* ) */ SHIFT(KeyO),
|
||||
/* * */ SHIFT(KeyB), /* + */ SHIFT(KeyK),
|
||||
/* , */ SHIFT(KeyDot), /* - */ SHIFT(KeyJ),
|
||||
/* . */ KEYS(KeyDot), /* / */ SHIFT(KeyV),
|
||||
/* 0 */ KEYS(Key0), /* 1 */ KEYS(Key1),
|
||||
/* 2 */ KEYS(Key2), /* 3 */ KEYS(Key3),
|
||||
/* 4 */ KEYS(Key4), /* 5 */ KEYS(Key5),
|
||||
/* 6 */ KEYS(Key6), /* 7 */ KEYS(Key7),
|
||||
/* 8 */ KEYS(Key8), /* 9 */ KEYS(Key9),
|
||||
/* : */ SHIFT(KeyZ), /* ; */ SHIFT(KeyX),
|
||||
/* < */ SHIFT(KeyN), /* = */ SHIFT(KeyL),
|
||||
/* > */ SHIFT(KeyM), /* ? */ SHIFT(KeyC),
|
||||
/* @ */ X, /* A */ KEYS(KeyA),
|
||||
/* B */ KEYS(KeyB), /* C */ KEYS(KeyC),
|
||||
/* D */ KEYS(KeyD), /* E */ KEYS(KeyE),
|
||||
/* F */ KEYS(KeyF), /* G */ KEYS(KeyG),
|
||||
/* H */ KEYS(KeyH), /* I */ KEYS(KeyI),
|
||||
/* J */ KEYS(KeyJ), /* K */ KEYS(KeyK),
|
||||
/* L */ KEYS(KeyL), /* M */ KEYS(KeyM),
|
||||
/* N */ KEYS(KeyN), /* O */ KEYS(KeyO),
|
||||
/* P */ KEYS(KeyP), /* Q */ KEYS(KeyQ),
|
||||
/* R */ KEYS(KeyR), /* S */ KEYS(KeyS),
|
||||
/* T */ KEYS(KeyT), /* U */ KEYS(KeyU),
|
||||
/* V */ KEYS(KeyV), /* W */ KEYS(KeyW),
|
||||
/* X */ KEYS(KeyX), /* Y */ KEYS(KeyY),
|
||||
/* Z */ KEYS(KeyZ), /* [ */ X,
|
||||
/* \ */ X, /* ] */ X,
|
||||
/* ^ */ X, /* _ */ X,
|
||||
/* ` */ X, /* a */ KEYS(KeyA),
|
||||
/* b */ KEYS(KeyB), /* c */ KEYS(KeyC),
|
||||
/* d */ KEYS(KeyD), /* e */ KEYS(KeyE),
|
||||
/* f */ KEYS(KeyF), /* g */ KEYS(KeyG),
|
||||
/* h */ KEYS(KeyH), /* i */ KEYS(KeyI),
|
||||
/* j */ KEYS(KeyJ), /* k */ KEYS(KeyK),
|
||||
/* l */ KEYS(KeyL), /* m */ KEYS(KeyM),
|
||||
/* n */ KEYS(KeyN), /* o */ KEYS(KeyO),
|
||||
/* p */ KEYS(KeyP), /* q */ KEYS(KeyQ),
|
||||
/* r */ KEYS(KeyR), /* s */ KEYS(KeyS),
|
||||
/* t */ KEYS(KeyT), /* u */ KEYS(KeyU),
|
||||
/* v */ KEYS(KeyV), /* w */ KEYS(KeyW),
|
||||
/* x */ KEYS(KeyX), /* y */ KEYS(KeyY),
|
||||
/* z */ KEYS(KeyZ), /* { */ X,
|
||||
/* | */ X, /* } */ X,
|
||||
};
|
||||
|
||||
static KeySequence zx80_key_sequences[] = {
|
||||
/* NUL */ X, /* SOH */ X,
|
||||
/* STX */ X, /* ETX */ X,
|
||||
/* EOT */ X, /* ENQ */ X,
|
||||
/* ACK */ X, /* BEL */ X,
|
||||
/* BS */ SHIFT(Key0), /* HT */ X,
|
||||
/* LF */ KEYS(KeyEnter), /* VT */ X,
|
||||
/* FF */ X, /* CR */ X,
|
||||
/* SO */ X, /* SI */ X,
|
||||
/* DLE */ X, /* DC1 */ X,
|
||||
/* DC2 */ X, /* DC3 */ X,
|
||||
/* DC4 */ X, /* NAK */ X,
|
||||
/* SYN */ X, /* ETB */ X,
|
||||
/* CAN */ X, /* EM */ X,
|
||||
/* SUB */ X, /* ESC */ X,
|
||||
/* FS */ X, /* GS */ X,
|
||||
/* RS */ X, /* US */ X,
|
||||
/* space */ KEYS(KeySpace), /* ! */ X,
|
||||
/* " */ SHIFT(KeyY), /* # */ X,
|
||||
/* $ */ SHIFT(KeyU), /* % */ X,
|
||||
/* & */ X, /* ' */ X,
|
||||
/* ( */ SHIFT(KeyI), /* ) */ SHIFT(KeyO),
|
||||
/* * */ SHIFT(KeyP), /* + */ SHIFT(KeyK),
|
||||
/* , */ SHIFT(KeyDot), /* - */ SHIFT(KeyJ),
|
||||
/* . */ KEYS(KeyDot), /* / */ SHIFT(KeyV),
|
||||
/* 0 */ KEYS(Key0), /* 1 */ KEYS(Key1),
|
||||
/* 2 */ KEYS(Key2), /* 3 */ KEYS(Key3),
|
||||
/* 4 */ KEYS(Key4), /* 5 */ KEYS(Key5),
|
||||
/* 6 */ KEYS(Key6), /* 7 */ KEYS(Key7),
|
||||
/* 8 */ KEYS(Key8), /* 9 */ KEYS(Key9),
|
||||
/* : */ SHIFT(KeyZ), /* ; */ SHIFT(KeyX),
|
||||
/* < */ SHIFT(KeyN), /* = */ SHIFT(KeyL),
|
||||
/* > */ SHIFT(KeyM), /* ? */ SHIFT(KeyC),
|
||||
/* @ */ X, /* A */ KEYS(KeyA),
|
||||
/* B */ KEYS(KeyB), /* C */ KEYS(KeyC),
|
||||
/* D */ KEYS(KeyD), /* E */ KEYS(KeyE),
|
||||
/* F */ KEYS(KeyF), /* G */ KEYS(KeyG),
|
||||
/* H */ KEYS(KeyH), /* I */ KEYS(KeyI),
|
||||
/* J */ KEYS(KeyJ), /* K */ KEYS(KeyK),
|
||||
/* L */ KEYS(KeyL), /* M */ KEYS(KeyM),
|
||||
/* N */ KEYS(KeyN), /* O */ KEYS(KeyO),
|
||||
/* P */ KEYS(KeyP), /* Q */ KEYS(KeyQ),
|
||||
/* R */ KEYS(KeyR), /* S */ KEYS(KeyS),
|
||||
/* T */ KEYS(KeyT), /* U */ KEYS(KeyU),
|
||||
/* V */ KEYS(KeyV), /* W */ KEYS(KeyW),
|
||||
/* X */ KEYS(KeyX), /* Y */ KEYS(KeyY),
|
||||
/* Z */ KEYS(KeyZ), /* [ */ X,
|
||||
/* \ */ X, /* ] */ X,
|
||||
/* ^ */ X, /* _ */ X,
|
||||
/* ` */ X, /* a */ KEYS(KeyA),
|
||||
/* b */ KEYS(KeyB), /* c */ KEYS(KeyC),
|
||||
/* d */ KEYS(KeyD), /* e */ KEYS(KeyE),
|
||||
/* f */ KEYS(KeyF), /* g */ KEYS(KeyG),
|
||||
/* h */ KEYS(KeyH), /* i */ KEYS(KeyI),
|
||||
/* j */ KEYS(KeyJ), /* k */ KEYS(KeyK),
|
||||
/* l */ KEYS(KeyL), /* m */ KEYS(KeyM),
|
||||
/* n */ KEYS(KeyN), /* o */ KEYS(KeyO),
|
||||
/* p */ KEYS(KeyP), /* q */ KEYS(KeyQ),
|
||||
/* r */ KEYS(KeyR), /* s */ KEYS(KeyS),
|
||||
/* t */ KEYS(KeyT), /* u */ KEYS(KeyU),
|
||||
/* v */ KEYS(KeyV), /* w */ KEYS(KeyW),
|
||||
/* x */ KEYS(KeyX), /* y */ KEYS(KeyY),
|
||||
/* z */ KEYS(KeyZ), /* { */ X,
|
||||
/* | */ X, /* } */ X,
|
||||
};
|
||||
#undef KEYS
|
||||
#undef SHIFT
|
||||
#undef X
|
||||
|
||||
if(is_zx81_)
|
||||
return table_lookup_sequence_for_character(zx81_key_sequences, sizeof(zx81_key_sequences), character);
|
||||
else
|
||||
return table_lookup_sequence_for_character(zx80_key_sequences, sizeof(zx80_key_sequences), character);
|
||||
}
|
||||
//Utility::KeySequence *ZX8081::SequenceForCharacter(char character, bool is_zx81) {
|
||||
//#define KEYS(...) {__VA_ARGS__, Utility::EndSequence}
|
||||
//#define SHIFT(...) {KeyShift, __VA_ARGS__, Utility::EndSequence}
|
||||
//#define X {Utility::NotMapped}
|
||||
// static Utility::KeySequence zx81_key_sequences[] = {
|
||||
// /* NUL */ X, /* SOH */ X,
|
||||
// /* STX */ X, /* ETX */ X,
|
||||
// /* EOT */ X, /* ENQ */ X,
|
||||
// /* ACK */ X, /* BEL */ X,
|
||||
// /* BS */ SHIFT(Key0), /* HT */ X,
|
||||
// /* LF */ KEYS(KeyEnter), /* VT */ X,
|
||||
// /* FF */ X, /* CR */ X,
|
||||
// /* SO */ X, /* SI */ X,
|
||||
// /* DLE */ X, /* DC1 */ X,
|
||||
// /* DC2 */ X, /* DC3 */ X,
|
||||
// /* DC4 */ X, /* NAK */ X,
|
||||
// /* SYN */ X, /* ETB */ X,
|
||||
// /* CAN */ X, /* EM */ X,
|
||||
// /* SUB */ X, /* ESC */ X,
|
||||
// /* FS */ X, /* GS */ X,
|
||||
// /* RS */ X, /* US */ X,
|
||||
// /* space */ KEYS(KeySpace), /* ! */ X,
|
||||
// /* " */ SHIFT(KeyP), /* # */ X,
|
||||
// /* $ */ SHIFT(KeyU), /* % */ X,
|
||||
// /* & */ X, /* ' */ X,
|
||||
// /* ( */ SHIFT(KeyI), /* ) */ SHIFT(KeyO),
|
||||
// /* * */ SHIFT(KeyB), /* + */ SHIFT(KeyK),
|
||||
// /* , */ SHIFT(KeyDot), /* - */ SHIFT(KeyJ),
|
||||
// /* . */ KEYS(KeyDot), /* / */ SHIFT(KeyV),
|
||||
// /* 0 */ KEYS(Key0), /* 1 */ KEYS(Key1),
|
||||
// /* 2 */ KEYS(Key2), /* 3 */ KEYS(Key3),
|
||||
// /* 4 */ KEYS(Key4), /* 5 */ KEYS(Key5),
|
||||
// /* 6 */ KEYS(Key6), /* 7 */ KEYS(Key7),
|
||||
// /* 8 */ KEYS(Key8), /* 9 */ KEYS(Key9),
|
||||
// /* : */ SHIFT(KeyZ), /* ; */ SHIFT(KeyX),
|
||||
// /* < */ SHIFT(KeyN), /* = */ SHIFT(KeyL),
|
||||
// /* > */ SHIFT(KeyM), /* ? */ SHIFT(KeyC),
|
||||
// /* @ */ X, /* A */ KEYS(KeyA),
|
||||
// /* B */ KEYS(KeyB), /* C */ KEYS(KeyC),
|
||||
// /* D */ KEYS(KeyD), /* E */ KEYS(KeyE),
|
||||
// /* F */ KEYS(KeyF), /* G */ KEYS(KeyG),
|
||||
// /* H */ KEYS(KeyH), /* I */ KEYS(KeyI),
|
||||
// /* J */ KEYS(KeyJ), /* K */ KEYS(KeyK),
|
||||
// /* L */ KEYS(KeyL), /* M */ KEYS(KeyM),
|
||||
// /* N */ KEYS(KeyN), /* O */ KEYS(KeyO),
|
||||
// /* P */ KEYS(KeyP), /* Q */ KEYS(KeyQ),
|
||||
// /* R */ KEYS(KeyR), /* S */ KEYS(KeyS),
|
||||
// /* T */ KEYS(KeyT), /* U */ KEYS(KeyU),
|
||||
// /* V */ KEYS(KeyV), /* W */ KEYS(KeyW),
|
||||
// /* X */ KEYS(KeyX), /* Y */ KEYS(KeyY),
|
||||
// /* Z */ KEYS(KeyZ), /* [ */ X,
|
||||
// /* \ */ X, /* ] */ X,
|
||||
// /* ^ */ X, /* _ */ X,
|
||||
// /* ` */ X, /* a */ KEYS(KeyA),
|
||||
// /* b */ KEYS(KeyB), /* c */ KEYS(KeyC),
|
||||
// /* d */ KEYS(KeyD), /* e */ KEYS(KeyE),
|
||||
// /* f */ KEYS(KeyF), /* g */ KEYS(KeyG),
|
||||
// /* h */ KEYS(KeyH), /* i */ KEYS(KeyI),
|
||||
// /* j */ KEYS(KeyJ), /* k */ KEYS(KeyK),
|
||||
// /* l */ KEYS(KeyL), /* m */ KEYS(KeyM),
|
||||
// /* n */ KEYS(KeyN), /* o */ KEYS(KeyO),
|
||||
// /* p */ KEYS(KeyP), /* q */ KEYS(KeyQ),
|
||||
// /* r */ KEYS(KeyR), /* s */ KEYS(KeyS),
|
||||
// /* t */ KEYS(KeyT), /* u */ KEYS(KeyU),
|
||||
// /* v */ KEYS(KeyV), /* w */ KEYS(KeyW),
|
||||
// /* x */ KEYS(KeyX), /* y */ KEYS(KeyY),
|
||||
// /* z */ KEYS(KeyZ), /* { */ X,
|
||||
// /* | */ X, /* } */ X,
|
||||
// };
|
||||
//
|
||||
// static KeySequence zx80_key_sequences[] = {
|
||||
// /* NUL */ X, /* SOH */ X,
|
||||
// /* STX */ X, /* ETX */ X,
|
||||
// /* EOT */ X, /* ENQ */ X,
|
||||
// /* ACK */ X, /* BEL */ X,
|
||||
// /* BS */ SHIFT(Key0), /* HT */ X,
|
||||
// /* LF */ KEYS(KeyEnter), /* VT */ X,
|
||||
// /* FF */ X, /* CR */ X,
|
||||
// /* SO */ X, /* SI */ X,
|
||||
// /* DLE */ X, /* DC1 */ X,
|
||||
// /* DC2 */ X, /* DC3 */ X,
|
||||
// /* DC4 */ X, /* NAK */ X,
|
||||
// /* SYN */ X, /* ETB */ X,
|
||||
// /* CAN */ X, /* EM */ X,
|
||||
// /* SUB */ X, /* ESC */ X,
|
||||
// /* FS */ X, /* GS */ X,
|
||||
// /* RS */ X, /* US */ X,
|
||||
// /* space */ KEYS(KeySpace), /* ! */ X,
|
||||
// /* " */ SHIFT(KeyY), /* # */ X,
|
||||
// /* $ */ SHIFT(KeyU), /* % */ X,
|
||||
// /* & */ X, /* ' */ X,
|
||||
// /* ( */ SHIFT(KeyI), /* ) */ SHIFT(KeyO),
|
||||
// /* * */ SHIFT(KeyP), /* + */ SHIFT(KeyK),
|
||||
// /* , */ SHIFT(KeyDot), /* - */ SHIFT(KeyJ),
|
||||
// /* . */ KEYS(KeyDot), /* / */ SHIFT(KeyV),
|
||||
// /* 0 */ KEYS(Key0), /* 1 */ KEYS(Key1),
|
||||
// /* 2 */ KEYS(Key2), /* 3 */ KEYS(Key3),
|
||||
// /* 4 */ KEYS(Key4), /* 5 */ KEYS(Key5),
|
||||
// /* 6 */ KEYS(Key6), /* 7 */ KEYS(Key7),
|
||||
// /* 8 */ KEYS(Key8), /* 9 */ KEYS(Key9),
|
||||
// /* : */ SHIFT(KeyZ), /* ; */ SHIFT(KeyX),
|
||||
// /* < */ SHIFT(KeyN), /* = */ SHIFT(KeyL),
|
||||
// /* > */ SHIFT(KeyM), /* ? */ SHIFT(KeyC),
|
||||
// /* @ */ X, /* A */ KEYS(KeyA),
|
||||
// /* B */ KEYS(KeyB), /* C */ KEYS(KeyC),
|
||||
// /* D */ KEYS(KeyD), /* E */ KEYS(KeyE),
|
||||
// /* F */ KEYS(KeyF), /* G */ KEYS(KeyG),
|
||||
// /* H */ KEYS(KeyH), /* I */ KEYS(KeyI),
|
||||
// /* J */ KEYS(KeyJ), /* K */ KEYS(KeyK),
|
||||
// /* L */ KEYS(KeyL), /* M */ KEYS(KeyM),
|
||||
// /* N */ KEYS(KeyN), /* O */ KEYS(KeyO),
|
||||
// /* P */ KEYS(KeyP), /* Q */ KEYS(KeyQ),
|
||||
// /* R */ KEYS(KeyR), /* S */ KEYS(KeyS),
|
||||
// /* T */ KEYS(KeyT), /* U */ KEYS(KeyU),
|
||||
// /* V */ KEYS(KeyV), /* W */ KEYS(KeyW),
|
||||
// /* X */ KEYS(KeyX), /* Y */ KEYS(KeyY),
|
||||
// /* Z */ KEYS(KeyZ), /* [ */ X,
|
||||
// /* \ */ X, /* ] */ X,
|
||||
// /* ^ */ X, /* _ */ X,
|
||||
// /* ` */ X, /* a */ KEYS(KeyA),
|
||||
// /* b */ KEYS(KeyB), /* c */ KEYS(KeyC),
|
||||
// /* d */ KEYS(KeyD), /* e */ KEYS(KeyE),
|
||||
// /* f */ KEYS(KeyF), /* g */ KEYS(KeyG),
|
||||
// /* h */ KEYS(KeyH), /* i */ KEYS(KeyI),
|
||||
// /* j */ KEYS(KeyJ), /* k */ KEYS(KeyK),
|
||||
// /* l */ KEYS(KeyL), /* m */ KEYS(KeyM),
|
||||
// /* n */ KEYS(KeyN), /* o */ KEYS(KeyO),
|
||||
// /* p */ KEYS(KeyP), /* q */ KEYS(KeyQ),
|
||||
// /* r */ KEYS(KeyR), /* s */ KEYS(KeyS),
|
||||
// /* t */ KEYS(KeyT), /* u */ KEYS(KeyU),
|
||||
// /* v */ KEYS(KeyV), /* w */ KEYS(KeyW),
|
||||
// /* x */ KEYS(KeyX), /* y */ KEYS(KeyY),
|
||||
// /* z */ KEYS(KeyZ), /* { */ X,
|
||||
// /* | */ X, /* } */ X,
|
||||
// };
|
||||
//#undef KEYS
|
||||
//#undef SHIFT
|
||||
//#undef X
|
||||
//
|
||||
// if(is_zx81)
|
||||
// return table_lookup_sequence_for_character(zx81_key_sequences, sizeof(zx81_key_sequences), character);
|
||||
// else
|
||||
// return table_lookup_sequence_for_character(zx80_key_sequences, sizeof(zx80_key_sequences), character);
|
||||
//}
|
||||
|
21
Machines/ZX8081/Typer.hpp
Normal file
21
Machines/ZX8081/Typer.hpp
Normal file
@ -0,0 +1,21 @@
|
||||
//
|
||||
// Typer.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 02/08/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Typer_h
|
||||
#define Typer_h
|
||||
|
||||
#include <cstdint>
|
||||
#include "../Typer.hpp"
|
||||
|
||||
namespace ZX8081 {
|
||||
|
||||
//Utility::KeySequence *ZX8081::SequenceForCharacter(char character, bool is_zx81);
|
||||
|
||||
}
|
||||
|
||||
#endif /* Typer_h */
|
@ -8,291 +8,517 @@
|
||||
|
||||
#include "ZX8081.hpp"
|
||||
|
||||
#include "../MemoryFuzzer.hpp"
|
||||
#include "../../Processors/Z80/Z80.hpp"
|
||||
#include "../../Storage/Tape/Tape.hpp"
|
||||
#include "../../Storage/Tape/Parsers/ZX8081.hpp"
|
||||
|
||||
using namespace ZX8081;
|
||||
#include "../MemoryFuzzer.hpp"
|
||||
#include "../Typer.hpp"
|
||||
|
||||
#include "Video.hpp"
|
||||
|
||||
namespace {
|
||||
// The clock rate is 3.25Mhz.
|
||||
const unsigned int ZX8081ClockRate = 3250000;
|
||||
}
|
||||
|
||||
Machine::Machine() :
|
||||
vsync_(false),
|
||||
hsync_(false),
|
||||
nmi_is_enabled_(false),
|
||||
tape_player_(ZX8081ClockRate),
|
||||
use_fast_tape_hack_(false),
|
||||
tape_advance_delay_(0),
|
||||
has_latched_video_byte_(false) {
|
||||
set_clock_rate(ZX8081ClockRate);
|
||||
clear_all_keys();
|
||||
}
|
||||
namespace ZX8081 {
|
||||
|
||||
HalfCycles Machine::perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
|
||||
HalfCycles previous_counter = horizontal_counter_;
|
||||
horizontal_counter_ += cycle.length;
|
||||
|
||||
if(previous_counter < vsync_start_ && horizontal_counter_ >= vsync_start_) {
|
||||
video_->run_for(vsync_start_ - previous_counter);
|
||||
set_hsync(true);
|
||||
line_counter_ = (line_counter_ + 1) & 7;
|
||||
if(nmi_is_enabled_) {
|
||||
set_non_maskable_interrupt_line(true);
|
||||
class ConcreteMachine:
|
||||
public Utility::TypeRecipient,
|
||||
public CPU::Z80::BusHandler,
|
||||
public Machine {
|
||||
public:
|
||||
ConcreteMachine() :
|
||||
z80_(*this),
|
||||
vsync_(false),
|
||||
hsync_(false),
|
||||
nmi_is_enabled_(false),
|
||||
tape_player_(ZX8081ClockRate),
|
||||
use_fast_tape_hack_(false),
|
||||
tape_advance_delay_(0),
|
||||
has_latched_video_byte_(false) {
|
||||
set_clock_rate(ZX8081ClockRate);
|
||||
clear_all_keys();
|
||||
}
|
||||
video_->run_for(horizontal_counter_ - vsync_start_);
|
||||
} else if(previous_counter < vsync_end_ && horizontal_counter_ >= vsync_end_) {
|
||||
video_->run_for(vsync_end_ - previous_counter);
|
||||
set_hsync(false);
|
||||
if(nmi_is_enabled_) {
|
||||
set_non_maskable_interrupt_line(false);
|
||||
set_wait_line(false);
|
||||
}
|
||||
video_->run_for(horizontal_counter_ - vsync_end_);
|
||||
} else {
|
||||
video_->run_for(cycle.length);
|
||||
}
|
||||
|
||||
if(is_zx81_) horizontal_counter_ %= HalfCycles(Cycles(207));
|
||||
if(!tape_advance_delay_) {
|
||||
tape_player_.run_for(cycle.length);
|
||||
} else {
|
||||
tape_advance_delay_ = std::max(tape_advance_delay_ - cycle.length, HalfCycles(0));
|
||||
}
|
||||
HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
|
||||
HalfCycles previous_counter = horizontal_counter_;
|
||||
horizontal_counter_ += cycle.length;
|
||||
|
||||
if(nmi_is_enabled_ && !get_halt_line() && get_non_maskable_interrupt_line()) {
|
||||
set_wait_line(true);
|
||||
}
|
||||
|
||||
if(!cycle.is_terminal()) {
|
||||
return Cycles(0);
|
||||
}
|
||||
|
||||
uint16_t address = cycle.address ? *cycle.address : 0;
|
||||
bool is_opcode_read = false;
|
||||
switch(cycle.operation) {
|
||||
case CPU::Z80::PartialMachineCycle::Output:
|
||||
if(!(address & 2)) nmi_is_enabled_ = false;
|
||||
if(!(address & 1)) nmi_is_enabled_ = is_zx81_;
|
||||
if(!nmi_is_enabled_) {
|
||||
// Line counter reset is held low while vsync is active; simulate that lazily by performing
|
||||
// an instant reset upon the transition from active to inactive.
|
||||
if(vsync_) line_counter_ = 0;
|
||||
set_vsync(false);
|
||||
}
|
||||
break;
|
||||
|
||||
case CPU::Z80::PartialMachineCycle::Input: {
|
||||
uint8_t value = 0xff;
|
||||
if(!(address&1)) {
|
||||
if(!nmi_is_enabled_) set_vsync(true);
|
||||
|
||||
uint16_t mask = 0x100;
|
||||
for(int c = 0; c < 8; c++) {
|
||||
if(!(address & mask)) value &= key_states_[c];
|
||||
mask <<= 1;
|
||||
if(previous_counter < vsync_start_ && horizontal_counter_ >= vsync_start_) {
|
||||
video_->run_for(vsync_start_ - previous_counter);
|
||||
set_hsync(true);
|
||||
line_counter_ = (line_counter_ + 1) & 7;
|
||||
if(nmi_is_enabled_) {
|
||||
z80_.set_non_maskable_interrupt_line(true);
|
||||
}
|
||||
|
||||
value &= ~(tape_player_.get_input() ? 0x00 : 0x80);
|
||||
}
|
||||
*cycle.value = value;
|
||||
} break;
|
||||
|
||||
case CPU::Z80::PartialMachineCycle::Interrupt:
|
||||
// resetting event is M1 and IOREQ both simultaneously having leading edges;
|
||||
// that happens 2 cycles before the end of INTACK. So the timer was reset and
|
||||
// now has advanced twice.
|
||||
horizontal_counter_ = HalfCycles(2);
|
||||
|
||||
*cycle.value = 0xff;
|
||||
break;
|
||||
|
||||
case CPU::Z80::PartialMachineCycle::Refresh:
|
||||
// The ZX80 and 81 signal an interrupt while refresh is active and bit 6 of the refresh
|
||||
// address is low. The Z80 signals a refresh, providing the refresh address during the
|
||||
// final two cycles of an opcode fetch. Therefore communicate a transient signalling
|
||||
// of the IRQ line if necessary.
|
||||
if(!(address & 0x40)) {
|
||||
set_interrupt_line(true, Cycles(-2));
|
||||
set_interrupt_line(false);
|
||||
}
|
||||
if(has_latched_video_byte_) {
|
||||
size_t char_address = (size_t)((address & 0xfe00) | ((latched_video_byte_ & 0x3f) << 3) | line_counter_);
|
||||
uint8_t mask = (latched_video_byte_ & 0x80) ? 0x00 : 0xff;
|
||||
if(char_address < ram_base_) {
|
||||
latched_video_byte_ = rom_[char_address & rom_mask_] ^ mask;
|
||||
} else {
|
||||
latched_video_byte_ = ram_[address & ram_mask_] ^ mask;
|
||||
video_->run_for(horizontal_counter_ - vsync_start_);
|
||||
} else if(previous_counter < vsync_end_ && horizontal_counter_ >= vsync_end_) {
|
||||
video_->run_for(vsync_end_ - previous_counter);
|
||||
set_hsync(false);
|
||||
if(nmi_is_enabled_) {
|
||||
z80_.set_non_maskable_interrupt_line(false);
|
||||
z80_.set_wait_line(false);
|
||||
}
|
||||
|
||||
video_->output_byte(latched_video_byte_);
|
||||
has_latched_video_byte_ = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case CPU::Z80::PartialMachineCycle::ReadOpcode:
|
||||
// Check for use of the fast tape hack.
|
||||
if(use_fast_tape_hack_ && address == tape_trap_address_ && tape_player_.has_tape()) {
|
||||
uint64_t prior_offset = tape_player_.get_tape()->get_offset();
|
||||
int next_byte = parser_.get_next_byte(tape_player_.get_tape());
|
||||
if(next_byte != -1) {
|
||||
uint16_t hl = get_value_of_register(CPU::Z80::Register::HL);
|
||||
ram_[hl & ram_mask_] = (uint8_t)next_byte;
|
||||
*cycle.value = 0x00;
|
||||
set_value_of_register(CPU::Z80::Register::ProgramCounter, tape_return_address_ - 1);
|
||||
|
||||
// Assume that having read one byte quickly, we're probably going to be asked to read
|
||||
// another shortly. Therefore, temporarily disable the tape motor for 1000 cycles in order
|
||||
// to avoid fighting with real time. This is a stop-gap fix.
|
||||
tape_advance_delay_ = 1000;
|
||||
return 0;
|
||||
} else {
|
||||
tape_player_.get_tape()->set_offset(prior_offset);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for automatic tape control.
|
||||
if(use_automatic_tape_motor_control_) {
|
||||
tape_player_.set_motor_control((address >= automatic_tape_motor_start_address_) && (address < automatic_tape_motor_end_address_));
|
||||
}
|
||||
is_opcode_read = true;
|
||||
|
||||
case CPU::Z80::PartialMachineCycle::Read:
|
||||
if(address < ram_base_) {
|
||||
*cycle.value = rom_[address & rom_mask_];
|
||||
video_->run_for(horizontal_counter_ - vsync_end_);
|
||||
} else {
|
||||
uint8_t value = ram_[address & ram_mask_];
|
||||
|
||||
// If this is an M1 cycle reading from above the 32kb mark and HALT is not
|
||||
// currently active, latch for video output and return a NOP. Otherwise,
|
||||
// just return the value as read.
|
||||
if(is_opcode_read && address&0x8000 && !(value & 0x40) && !get_halt_line()) {
|
||||
latched_video_byte_ = value;
|
||||
has_latched_video_byte_ = true;
|
||||
*cycle.value = 0;
|
||||
} else *cycle.value = value;
|
||||
video_->run_for(cycle.length);
|
||||
}
|
||||
break;
|
||||
|
||||
case CPU::Z80::PartialMachineCycle::Write:
|
||||
if(address >= ram_base_) {
|
||||
ram_[address & ram_mask_] = *cycle.value;
|
||||
if(is_zx81_) horizontal_counter_ %= HalfCycles(Cycles(207));
|
||||
if(!tape_advance_delay_) {
|
||||
tape_player_.run_for(cycle.length);
|
||||
} else {
|
||||
tape_advance_delay_ = std::max(tape_advance_delay_ - cycle.length, HalfCycles(0));
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
if(nmi_is_enabled_ && !z80_.get_halt_line() && z80_.get_non_maskable_interrupt_line()) {
|
||||
z80_.set_wait_line(true);
|
||||
}
|
||||
|
||||
if(typer_) typer_->run_for(cycle.length);
|
||||
if(!cycle.is_terminal()) {
|
||||
return Cycles(0);
|
||||
}
|
||||
|
||||
return HalfCycles(0);
|
||||
}
|
||||
uint16_t address = cycle.address ? *cycle.address : 0;
|
||||
bool is_opcode_read = false;
|
||||
switch(cycle.operation) {
|
||||
case CPU::Z80::PartialMachineCycle::Output:
|
||||
if(!(address & 2)) nmi_is_enabled_ = false;
|
||||
if(!(address & 1)) nmi_is_enabled_ = is_zx81_;
|
||||
if(!nmi_is_enabled_) {
|
||||
// Line counter reset is held low while vsync is active; simulate that lazily by performing
|
||||
// an instant reset upon the transition from active to inactive.
|
||||
if(vsync_) line_counter_ = 0;
|
||||
set_vsync(false);
|
||||
}
|
||||
break;
|
||||
|
||||
void Machine::flush() {
|
||||
video_->flush();
|
||||
}
|
||||
case CPU::Z80::PartialMachineCycle::Input: {
|
||||
uint8_t value = 0xff;
|
||||
if(!(address&1)) {
|
||||
if(!nmi_is_enabled_) set_vsync(true);
|
||||
|
||||
void Machine::setup_output(float aspect_ratio) {
|
||||
video_.reset(new Video);
|
||||
}
|
||||
uint16_t mask = 0x100;
|
||||
for(int c = 0; c < 8; c++) {
|
||||
if(!(address & mask)) value &= key_states_[c];
|
||||
mask <<= 1;
|
||||
}
|
||||
|
||||
void Machine::close_output() {
|
||||
video_.reset();
|
||||
}
|
||||
value &= ~(tape_player_.get_input() ? 0x00 : 0x80);
|
||||
}
|
||||
*cycle.value = value;
|
||||
} break;
|
||||
|
||||
std::shared_ptr<Outputs::CRT::CRT> Machine::get_crt() {
|
||||
return video_->get_crt();
|
||||
}
|
||||
case CPU::Z80::PartialMachineCycle::Interrupt:
|
||||
// resetting event is M1 and IOREQ both simultaneously having leading edges;
|
||||
// that happens 2 cycles before the end of INTACK. So the timer was reset and
|
||||
// now has advanced twice.
|
||||
horizontal_counter_ = HalfCycles(2);
|
||||
|
||||
std::shared_ptr<Outputs::Speaker> Machine::get_speaker() {
|
||||
return nullptr;
|
||||
}
|
||||
*cycle.value = 0xff;
|
||||
break;
|
||||
|
||||
void Machine::run_for(const Cycles cycles) {
|
||||
CPU::Z80::Processor<Machine>::run_for(cycles);
|
||||
}
|
||||
case CPU::Z80::PartialMachineCycle::Refresh:
|
||||
// The ZX80 and 81 signal an interrupt while refresh is active and bit 6 of the refresh
|
||||
// address is low. The Z80 signals a refresh, providing the refresh address during the
|
||||
// final two cycles of an opcode fetch. Therefore communicate a transient signalling
|
||||
// of the IRQ line if necessary.
|
||||
if(!(address & 0x40)) {
|
||||
z80_.set_interrupt_line(true, Cycles(-2));
|
||||
z80_.set_interrupt_line(false);
|
||||
}
|
||||
if(has_latched_video_byte_) {
|
||||
size_t char_address = (size_t)((address & 0xfe00) | ((latched_video_byte_ & 0x3f) << 3) | line_counter_);
|
||||
uint8_t mask = (latched_video_byte_ & 0x80) ? 0x00 : 0xff;
|
||||
if(char_address < ram_base_) {
|
||||
latched_video_byte_ = rom_[char_address & rom_mask_] ^ mask;
|
||||
} else {
|
||||
latched_video_byte_ = ram_[address & ram_mask_] ^ mask;
|
||||
}
|
||||
|
||||
void Machine::configure_as_target(const StaticAnalyser::Target &target) {
|
||||
is_zx81_ = target.zx8081.isZX81;
|
||||
if(is_zx81_) {
|
||||
rom_ = zx81_rom_;
|
||||
tape_trap_address_ = 0x37c;
|
||||
tape_return_address_ = 0x380;
|
||||
vsync_start_ = HalfCycles(32);
|
||||
vsync_end_ = HalfCycles(64);
|
||||
automatic_tape_motor_start_address_ = 0x0340;
|
||||
automatic_tape_motor_end_address_ = 0x03c3;
|
||||
} else {
|
||||
rom_ = zx80_rom_;
|
||||
tape_trap_address_ = 0x220;
|
||||
tape_return_address_ = 0x248;
|
||||
vsync_start_ = HalfCycles(26);
|
||||
vsync_end_ = HalfCycles(66);
|
||||
automatic_tape_motor_start_address_ = 0x0206;
|
||||
automatic_tape_motor_end_address_ = 0x024d;
|
||||
}
|
||||
rom_mask_ = (uint16_t)(rom_.size() - 1);
|
||||
video_->output_byte(latched_video_byte_);
|
||||
has_latched_video_byte_ = false;
|
||||
}
|
||||
break;
|
||||
|
||||
switch(target.zx8081.memory_model) {
|
||||
case StaticAnalyser::ZX8081MemoryModel::Unexpanded:
|
||||
ram_.resize(1024);
|
||||
ram_base_ = 16384;
|
||||
ram_mask_ = 1023;
|
||||
break;
|
||||
case StaticAnalyser::ZX8081MemoryModel::SixteenKB:
|
||||
ram_.resize(16384);
|
||||
ram_base_ = 16384;
|
||||
ram_mask_ = 16383;
|
||||
break;
|
||||
case StaticAnalyser::ZX8081MemoryModel::SixtyFourKB:
|
||||
ram_.resize(65536);
|
||||
ram_base_ = 8192;
|
||||
ram_mask_ = 65535;
|
||||
break;
|
||||
}
|
||||
Memory::Fuzz(ram_);
|
||||
case CPU::Z80::PartialMachineCycle::ReadOpcode:
|
||||
// Check for use of the fast tape hack.
|
||||
if(use_fast_tape_hack_ && address == tape_trap_address_ && tape_player_.has_tape()) {
|
||||
uint64_t prior_offset = tape_player_.get_tape()->get_offset();
|
||||
int next_byte = parser_.get_next_byte(tape_player_.get_tape());
|
||||
if(next_byte != -1) {
|
||||
uint16_t hl = z80_.get_value_of_register(CPU::Z80::Register::HL);
|
||||
ram_[hl & ram_mask_] = (uint8_t)next_byte;
|
||||
*cycle.value = 0x00;
|
||||
z80_.set_value_of_register(CPU::Z80::Register::ProgramCounter, tape_return_address_ - 1);
|
||||
|
||||
if(target.tapes.size()) {
|
||||
tape_player_.set_tape(target.tapes.front());
|
||||
}
|
||||
// Assume that having read one byte quickly, we're probably going to be asked to read
|
||||
// another shortly. Therefore, temporarily disable the tape motor for 1000 cycles in order
|
||||
// to avoid fighting with real time. This is a stop-gap fix.
|
||||
tape_advance_delay_ = 1000;
|
||||
return 0;
|
||||
} else {
|
||||
tape_player_.get_tape()->set_offset(prior_offset);
|
||||
}
|
||||
}
|
||||
|
||||
if(target.loadingCommand.length()) {
|
||||
set_typer_for_string(target.loadingCommand.c_str());
|
||||
}
|
||||
}
|
||||
// Check for automatic tape control.
|
||||
if(use_automatic_tape_motor_control_) {
|
||||
tape_player_.set_motor_control((address >= automatic_tape_motor_start_address_) && (address < automatic_tape_motor_end_address_));
|
||||
}
|
||||
is_opcode_read = true;
|
||||
|
||||
void Machine::set_rom(ROMType type, std::vector<uint8_t> data) {
|
||||
switch(type) {
|
||||
case ZX80: zx80_rom_ = data; break;
|
||||
case ZX81: zx81_rom_ = data; break;
|
||||
}
|
||||
}
|
||||
case CPU::Z80::PartialMachineCycle::Read:
|
||||
if(address < ram_base_) {
|
||||
*cycle.value = rom_[address & rom_mask_];
|
||||
} else {
|
||||
uint8_t value = ram_[address & ram_mask_];
|
||||
|
||||
#pragma mark - Video
|
||||
// If this is an M1 cycle reading from above the 32kb mark and HALT is not
|
||||
// currently active, latch for video output and return a NOP. Otherwise,
|
||||
// just return the value as read.
|
||||
if(is_opcode_read && address&0x8000 && !(value & 0x40) && !z80_.get_halt_line()) {
|
||||
latched_video_byte_ = value;
|
||||
has_latched_video_byte_ = true;
|
||||
*cycle.value = 0;
|
||||
} else *cycle.value = value;
|
||||
}
|
||||
break;
|
||||
|
||||
void Machine::set_vsync(bool sync) {
|
||||
vsync_ = sync;
|
||||
update_sync();
|
||||
}
|
||||
case CPU::Z80::PartialMachineCycle::Write:
|
||||
if(address >= ram_base_) {
|
||||
ram_[address & ram_mask_] = *cycle.value;
|
||||
}
|
||||
break;
|
||||
|
||||
void Machine::set_hsync(bool sync) {
|
||||
hsync_ = sync;
|
||||
update_sync();
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
void Machine::update_sync() {
|
||||
video_->set_sync(vsync_ || hsync_);
|
||||
}
|
||||
if(typer_) typer_->run_for(cycle.length);
|
||||
|
||||
return HalfCycles(0);
|
||||
}
|
||||
|
||||
void flush() {
|
||||
video_->flush();
|
||||
}
|
||||
|
||||
void setup_output(float aspect_ratio) {
|
||||
video_.reset(new Video);
|
||||
}
|
||||
|
||||
void close_output() {
|
||||
video_.reset();
|
||||
}
|
||||
|
||||
std::shared_ptr<Outputs::CRT::CRT> get_crt() {
|
||||
return video_->get_crt();
|
||||
}
|
||||
|
||||
std::shared_ptr<Outputs::Speaker> get_speaker() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void run_for(const Cycles cycles) {
|
||||
z80_.run_for(cycles);
|
||||
}
|
||||
|
||||
void configure_as_target(const StaticAnalyser::Target &target) {
|
||||
is_zx81_ = target.zx8081.isZX81;
|
||||
if(is_zx81_) {
|
||||
rom_ = zx81_rom_;
|
||||
tape_trap_address_ = 0x37c;
|
||||
tape_return_address_ = 0x380;
|
||||
vsync_start_ = HalfCycles(32);
|
||||
vsync_end_ = HalfCycles(64);
|
||||
automatic_tape_motor_start_address_ = 0x0340;
|
||||
automatic_tape_motor_end_address_ = 0x03c3;
|
||||
} else {
|
||||
rom_ = zx80_rom_;
|
||||
tape_trap_address_ = 0x220;
|
||||
tape_return_address_ = 0x248;
|
||||
vsync_start_ = HalfCycles(26);
|
||||
vsync_end_ = HalfCycles(66);
|
||||
automatic_tape_motor_start_address_ = 0x0206;
|
||||
automatic_tape_motor_end_address_ = 0x024d;
|
||||
}
|
||||
rom_mask_ = (uint16_t)(rom_.size() - 1);
|
||||
|
||||
switch(target.zx8081.memory_model) {
|
||||
case StaticAnalyser::ZX8081MemoryModel::Unexpanded:
|
||||
ram_.resize(1024);
|
||||
ram_base_ = 16384;
|
||||
ram_mask_ = 1023;
|
||||
break;
|
||||
case StaticAnalyser::ZX8081MemoryModel::SixteenKB:
|
||||
ram_.resize(16384);
|
||||
ram_base_ = 16384;
|
||||
ram_mask_ = 16383;
|
||||
break;
|
||||
case StaticAnalyser::ZX8081MemoryModel::SixtyFourKB:
|
||||
ram_.resize(65536);
|
||||
ram_base_ = 8192;
|
||||
ram_mask_ = 65535;
|
||||
break;
|
||||
}
|
||||
Memory::Fuzz(ram_);
|
||||
|
||||
if(target.tapes.size()) {
|
||||
tape_player_.set_tape(target.tapes.front());
|
||||
}
|
||||
|
||||
if(target.loadingCommand.length()) {
|
||||
set_typer_for_string(target.loadingCommand.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void set_rom(ROMType type, std::vector<uint8_t> data) {
|
||||
switch(type) {
|
||||
case ZX80: zx80_rom_ = data; break;
|
||||
case ZX81: zx81_rom_ = data; break;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Keyboard
|
||||
|
||||
void Machine::set_key_state(uint16_t key, bool isPressed) {
|
||||
if(isPressed)
|
||||
key_states_[key >> 8] &= (uint8_t)(~key);
|
||||
else
|
||||
key_states_[key >> 8] |= (uint8_t)key;
|
||||
void set_key_state(uint16_t key, bool isPressed) {
|
||||
if(isPressed)
|
||||
key_states_[key >> 8] &= (uint8_t)(~key);
|
||||
else
|
||||
key_states_[key >> 8] |= (uint8_t)key;
|
||||
}
|
||||
|
||||
void clear_all_keys() {
|
||||
memset(key_states_, 0xff, 8);
|
||||
}
|
||||
|
||||
#pragma mark - Tape control
|
||||
|
||||
void set_use_fast_tape_hack(bool activate) {
|
||||
use_fast_tape_hack_ = activate;
|
||||
}
|
||||
|
||||
void set_use_automatic_tape_motor_control(bool enabled) {
|
||||
use_automatic_tape_motor_control_ = enabled;
|
||||
if(!enabled) {
|
||||
tape_player_.set_motor_control(false);
|
||||
}
|
||||
}
|
||||
void set_tape_is_playing(bool is_playing) {
|
||||
tape_player_.set_motor_control(is_playing);
|
||||
}
|
||||
|
||||
#pragma mark - Typer
|
||||
|
||||
// for Utility::TypeRecipient::Delegate
|
||||
HalfCycles get_typer_delay() { return Cycles(7000000); }
|
||||
HalfCycles get_typer_frequency() { return Cycles(390000); }
|
||||
|
||||
uint16_t *sequence_for_character(Utility::Typer *typer, char character) {
|
||||
#define KEYS(...) {__VA_ARGS__, EndSequence}
|
||||
#define SHIFT(...) {KeyShift, __VA_ARGS__, EndSequence}
|
||||
#define X {NotMapped}
|
||||
static KeySequence zx81_key_sequences[] = {
|
||||
/* NUL */ X, /* SOH */ X,
|
||||
/* STX */ X, /* ETX */ X,
|
||||
/* EOT */ X, /* ENQ */ X,
|
||||
/* ACK */ X, /* BEL */ X,
|
||||
/* BS */ SHIFT(Key0), /* HT */ X,
|
||||
/* LF */ KEYS(KeyEnter), /* VT */ X,
|
||||
/* FF */ X, /* CR */ X,
|
||||
/* SO */ X, /* SI */ X,
|
||||
/* DLE */ X, /* DC1 */ X,
|
||||
/* DC2 */ X, /* DC3 */ X,
|
||||
/* DC4 */ X, /* NAK */ X,
|
||||
/* SYN */ X, /* ETB */ X,
|
||||
/* CAN */ X, /* EM */ X,
|
||||
/* SUB */ X, /* ESC */ X,
|
||||
/* FS */ X, /* GS */ X,
|
||||
/* RS */ X, /* US */ X,
|
||||
/* space */ KEYS(KeySpace), /* ! */ X,
|
||||
/* " */ SHIFT(KeyP), /* # */ X,
|
||||
/* $ */ SHIFT(KeyU), /* % */ X,
|
||||
/* & */ X, /* ' */ X,
|
||||
/* ( */ SHIFT(KeyI), /* ) */ SHIFT(KeyO),
|
||||
/* * */ SHIFT(KeyB), /* + */ SHIFT(KeyK),
|
||||
/* , */ SHIFT(KeyDot), /* - */ SHIFT(KeyJ),
|
||||
/* . */ KEYS(KeyDot), /* / */ SHIFT(KeyV),
|
||||
/* 0 */ KEYS(Key0), /* 1 */ KEYS(Key1),
|
||||
/* 2 */ KEYS(Key2), /* 3 */ KEYS(Key3),
|
||||
/* 4 */ KEYS(Key4), /* 5 */ KEYS(Key5),
|
||||
/* 6 */ KEYS(Key6), /* 7 */ KEYS(Key7),
|
||||
/* 8 */ KEYS(Key8), /* 9 */ KEYS(Key9),
|
||||
/* : */ SHIFT(KeyZ), /* ; */ SHIFT(KeyX),
|
||||
/* < */ SHIFT(KeyN), /* = */ SHIFT(KeyL),
|
||||
/* > */ SHIFT(KeyM), /* ? */ SHIFT(KeyC),
|
||||
/* @ */ X, /* A */ KEYS(KeyA),
|
||||
/* B */ KEYS(KeyB), /* C */ KEYS(KeyC),
|
||||
/* D */ KEYS(KeyD), /* E */ KEYS(KeyE),
|
||||
/* F */ KEYS(KeyF), /* G */ KEYS(KeyG),
|
||||
/* H */ KEYS(KeyH), /* I */ KEYS(KeyI),
|
||||
/* J */ KEYS(KeyJ), /* K */ KEYS(KeyK),
|
||||
/* L */ KEYS(KeyL), /* M */ KEYS(KeyM),
|
||||
/* N */ KEYS(KeyN), /* O */ KEYS(KeyO),
|
||||
/* P */ KEYS(KeyP), /* Q */ KEYS(KeyQ),
|
||||
/* R */ KEYS(KeyR), /* S */ KEYS(KeyS),
|
||||
/* T */ KEYS(KeyT), /* U */ KEYS(KeyU),
|
||||
/* V */ KEYS(KeyV), /* W */ KEYS(KeyW),
|
||||
/* X */ KEYS(KeyX), /* Y */ KEYS(KeyY),
|
||||
/* Z */ KEYS(KeyZ), /* [ */ X,
|
||||
/* \ */ X, /* ] */ X,
|
||||
/* ^ */ X, /* _ */ X,
|
||||
/* ` */ X, /* a */ KEYS(KeyA),
|
||||
/* b */ KEYS(KeyB), /* c */ KEYS(KeyC),
|
||||
/* d */ KEYS(KeyD), /* e */ KEYS(KeyE),
|
||||
/* f */ KEYS(KeyF), /* g */ KEYS(KeyG),
|
||||
/* h */ KEYS(KeyH), /* i */ KEYS(KeyI),
|
||||
/* j */ KEYS(KeyJ), /* k */ KEYS(KeyK),
|
||||
/* l */ KEYS(KeyL), /* m */ KEYS(KeyM),
|
||||
/* n */ KEYS(KeyN), /* o */ KEYS(KeyO),
|
||||
/* p */ KEYS(KeyP), /* q */ KEYS(KeyQ),
|
||||
/* r */ KEYS(KeyR), /* s */ KEYS(KeyS),
|
||||
/* t */ KEYS(KeyT), /* u */ KEYS(KeyU),
|
||||
/* v */ KEYS(KeyV), /* w */ KEYS(KeyW),
|
||||
/* x */ KEYS(KeyX), /* y */ KEYS(KeyY),
|
||||
/* z */ KEYS(KeyZ), /* { */ X,
|
||||
/* | */ X, /* } */ X,
|
||||
};
|
||||
|
||||
static KeySequence zx80_key_sequences[] = {
|
||||
/* NUL */ X, /* SOH */ X,
|
||||
/* STX */ X, /* ETX */ X,
|
||||
/* EOT */ X, /* ENQ */ X,
|
||||
/* ACK */ X, /* BEL */ X,
|
||||
/* BS */ SHIFT(Key0), /* HT */ X,
|
||||
/* LF */ KEYS(KeyEnter), /* VT */ X,
|
||||
/* FF */ X, /* CR */ X,
|
||||
/* SO */ X, /* SI */ X,
|
||||
/* DLE */ X, /* DC1 */ X,
|
||||
/* DC2 */ X, /* DC3 */ X,
|
||||
/* DC4 */ X, /* NAK */ X,
|
||||
/* SYN */ X, /* ETB */ X,
|
||||
/* CAN */ X, /* EM */ X,
|
||||
/* SUB */ X, /* ESC */ X,
|
||||
/* FS */ X, /* GS */ X,
|
||||
/* RS */ X, /* US */ X,
|
||||
/* space */ KEYS(KeySpace), /* ! */ X,
|
||||
/* " */ SHIFT(KeyY), /* # */ X,
|
||||
/* $ */ SHIFT(KeyU), /* % */ X,
|
||||
/* & */ X, /* ' */ X,
|
||||
/* ( */ SHIFT(KeyI), /* ) */ SHIFT(KeyO),
|
||||
/* * */ SHIFT(KeyP), /* + */ SHIFT(KeyK),
|
||||
/* , */ SHIFT(KeyDot), /* - */ SHIFT(KeyJ),
|
||||
/* . */ KEYS(KeyDot), /* / */ SHIFT(KeyV),
|
||||
/* 0 */ KEYS(Key0), /* 1 */ KEYS(Key1),
|
||||
/* 2 */ KEYS(Key2), /* 3 */ KEYS(Key3),
|
||||
/* 4 */ KEYS(Key4), /* 5 */ KEYS(Key5),
|
||||
/* 6 */ KEYS(Key6), /* 7 */ KEYS(Key7),
|
||||
/* 8 */ KEYS(Key8), /* 9 */ KEYS(Key9),
|
||||
/* : */ SHIFT(KeyZ), /* ; */ SHIFT(KeyX),
|
||||
/* < */ SHIFT(KeyN), /* = */ SHIFT(KeyL),
|
||||
/* > */ SHIFT(KeyM), /* ? */ SHIFT(KeyC),
|
||||
/* @ */ X, /* A */ KEYS(KeyA),
|
||||
/* B */ KEYS(KeyB), /* C */ KEYS(KeyC),
|
||||
/* D */ KEYS(KeyD), /* E */ KEYS(KeyE),
|
||||
/* F */ KEYS(KeyF), /* G */ KEYS(KeyG),
|
||||
/* H */ KEYS(KeyH), /* I */ KEYS(KeyI),
|
||||
/* J */ KEYS(KeyJ), /* K */ KEYS(KeyK),
|
||||
/* L */ KEYS(KeyL), /* M */ KEYS(KeyM),
|
||||
/* N */ KEYS(KeyN), /* O */ KEYS(KeyO),
|
||||
/* P */ KEYS(KeyP), /* Q */ KEYS(KeyQ),
|
||||
/* R */ KEYS(KeyR), /* S */ KEYS(KeyS),
|
||||
/* T */ KEYS(KeyT), /* U */ KEYS(KeyU),
|
||||
/* V */ KEYS(KeyV), /* W */ KEYS(KeyW),
|
||||
/* X */ KEYS(KeyX), /* Y */ KEYS(KeyY),
|
||||
/* Z */ KEYS(KeyZ), /* [ */ X,
|
||||
/* \ */ X, /* ] */ X,
|
||||
/* ^ */ X, /* _ */ X,
|
||||
/* ` */ X, /* a */ KEYS(KeyA),
|
||||
/* b */ KEYS(KeyB), /* c */ KEYS(KeyC),
|
||||
/* d */ KEYS(KeyD), /* e */ KEYS(KeyE),
|
||||
/* f */ KEYS(KeyF), /* g */ KEYS(KeyG),
|
||||
/* h */ KEYS(KeyH), /* i */ KEYS(KeyI),
|
||||
/* j */ KEYS(KeyJ), /* k */ KEYS(KeyK),
|
||||
/* l */ KEYS(KeyL), /* m */ KEYS(KeyM),
|
||||
/* n */ KEYS(KeyN), /* o */ KEYS(KeyO),
|
||||
/* p */ KEYS(KeyP), /* q */ KEYS(KeyQ),
|
||||
/* r */ KEYS(KeyR), /* s */ KEYS(KeyS),
|
||||
/* t */ KEYS(KeyT), /* u */ KEYS(KeyU),
|
||||
/* v */ KEYS(KeyV), /* w */ KEYS(KeyW),
|
||||
/* x */ KEYS(KeyX), /* y */ KEYS(KeyY),
|
||||
/* z */ KEYS(KeyZ), /* { */ X,
|
||||
/* | */ X, /* } */ X,
|
||||
};
|
||||
#undef KEYS
|
||||
#undef SHIFT
|
||||
#undef X
|
||||
|
||||
if(is_zx81_)
|
||||
return table_lookup_sequence_for_character(zx81_key_sequences, sizeof(zx81_key_sequences), character);
|
||||
else
|
||||
return table_lookup_sequence_for_character(zx80_key_sequences, sizeof(zx80_key_sequences), character);
|
||||
}
|
||||
|
||||
private:
|
||||
CPU::Z80::Processor<ConcreteMachine> z80_;
|
||||
|
||||
std::shared_ptr<Video> video_;
|
||||
std::vector<uint8_t> zx81_rom_, zx80_rom_;
|
||||
|
||||
uint16_t tape_trap_address_, tape_return_address_;
|
||||
uint16_t automatic_tape_motor_start_address_, automatic_tape_motor_end_address_;
|
||||
|
||||
std::vector<uint8_t> ram_;
|
||||
uint16_t ram_mask_, ram_base_;
|
||||
|
||||
std::vector<uint8_t> rom_;
|
||||
uint16_t rom_mask_;
|
||||
|
||||
bool vsync_, hsync_;
|
||||
int line_counter_;
|
||||
|
||||
uint8_t key_states_[8];
|
||||
|
||||
HalfClockReceiver<Storage::Tape::BinaryTapePlayer> tape_player_;
|
||||
Storage::Tape::ZX8081::Parser parser_;
|
||||
|
||||
bool is_zx81_;
|
||||
bool nmi_is_enabled_;
|
||||
|
||||
HalfCycles vsync_start_, vsync_end_;
|
||||
HalfCycles horizontal_counter_;
|
||||
|
||||
uint8_t latched_video_byte_;
|
||||
bool has_latched_video_byte_;
|
||||
|
||||
bool use_fast_tape_hack_;
|
||||
bool use_automatic_tape_motor_control_;
|
||||
HalfCycles tape_advance_delay_;
|
||||
|
||||
#pragma mark - Video
|
||||
|
||||
void set_vsync(bool sync) {
|
||||
vsync_ = sync;
|
||||
update_sync();
|
||||
}
|
||||
|
||||
void set_hsync(bool sync) {
|
||||
hsync_ = sync;
|
||||
update_sync();
|
||||
}
|
||||
|
||||
void update_sync() {
|
||||
video_->set_sync(vsync_ || hsync_);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void Machine::clear_all_keys() {
|
||||
memset(key_states_, 0xff, 8);
|
||||
using namespace ZX8081;
|
||||
|
||||
// See header; constructs and returns an instance of the ZX80/81.
|
||||
Machine *Machine::ZX8081() {
|
||||
return new ZX8081::ConcreteMachine;
|
||||
}
|
||||
|
@ -11,13 +11,6 @@
|
||||
|
||||
#include "../ConfigurationTarget.hpp"
|
||||
#include "../CRTMachine.hpp"
|
||||
#include "../Typer.hpp"
|
||||
|
||||
#include "../../Processors/Z80/Z80.hpp"
|
||||
#include "../../Storage/Tape/Tape.hpp"
|
||||
#include "../../Storage/Tape/Parsers/ZX8081.hpp"
|
||||
|
||||
#include "Video.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
@ -40,81 +33,18 @@ enum Key: uint16_t {
|
||||
};
|
||||
|
||||
class Machine:
|
||||
public CPU::Z80::Processor<Machine>,
|
||||
public CRTMachine::Machine,
|
||||
public Utility::TypeRecipient,
|
||||
public ConfigurationTarget::Machine {
|
||||
public:
|
||||
Machine();
|
||||
static Machine *ZX8081();
|
||||
|
||||
HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle);
|
||||
void flush();
|
||||
virtual void set_rom(ROMType type, std::vector<uint8_t> data) = 0;
|
||||
virtual void set_key_state(uint16_t key, bool isPressed) = 0;
|
||||
virtual void clear_all_keys() = 0;
|
||||
|
||||
void setup_output(float aspect_ratio);
|
||||
void close_output();
|
||||
|
||||
std::shared_ptr<Outputs::CRT::CRT> get_crt();
|
||||
std::shared_ptr<Outputs::Speaker> get_speaker();
|
||||
|
||||
void run_for(const Cycles cycles);
|
||||
|
||||
void configure_as_target(const StaticAnalyser::Target &target);
|
||||
|
||||
void set_rom(ROMType type, std::vector<uint8_t> data);
|
||||
void set_key_state(uint16_t key, bool isPressed);
|
||||
void clear_all_keys();
|
||||
|
||||
inline void set_use_fast_tape_hack(bool activate) { use_fast_tape_hack_ = activate; }
|
||||
inline void set_use_automatic_tape_motor_control(bool enabled) {
|
||||
use_automatic_tape_motor_control_ = enabled;
|
||||
if(!enabled) {
|
||||
tape_player_.set_motor_control(false);
|
||||
}
|
||||
}
|
||||
inline void set_tape_is_playing(bool is_playing) { tape_player_.set_motor_control(is_playing); }
|
||||
|
||||
// for Utility::TypeRecipient::Delegate
|
||||
uint16_t *sequence_for_character(Utility::Typer *typer, char character);
|
||||
HalfCycles get_typer_delay() { return Cycles(7000000); }
|
||||
HalfCycles get_typer_frequency() { return Cycles(390000); }
|
||||
|
||||
private:
|
||||
std::shared_ptr<Video> video_;
|
||||
std::vector<uint8_t> zx81_rom_, zx80_rom_;
|
||||
|
||||
uint16_t tape_trap_address_, tape_return_address_;
|
||||
uint16_t automatic_tape_motor_start_address_, automatic_tape_motor_end_address_;
|
||||
|
||||
std::vector<uint8_t> ram_;
|
||||
uint16_t ram_mask_, ram_base_;
|
||||
|
||||
std::vector<uint8_t> rom_;
|
||||
uint16_t rom_mask_;
|
||||
|
||||
bool vsync_, hsync_;
|
||||
int line_counter_;
|
||||
|
||||
uint8_t key_states_[8];
|
||||
|
||||
void set_vsync(bool sync);
|
||||
void set_hsync(bool sync);
|
||||
void update_sync();
|
||||
|
||||
HalfClockReceiver<Storage::Tape::BinaryTapePlayer> tape_player_;
|
||||
Storage::Tape::ZX8081::Parser parser_;
|
||||
|
||||
bool is_zx81_;
|
||||
bool nmi_is_enabled_;
|
||||
|
||||
HalfCycles vsync_start_, vsync_end_;
|
||||
HalfCycles horizontal_counter_;
|
||||
|
||||
uint8_t latched_video_byte_;
|
||||
bool has_latched_video_byte_;
|
||||
|
||||
bool use_fast_tape_hack_;
|
||||
bool use_automatic_tape_motor_control_;
|
||||
HalfCycles tape_advance_delay_;
|
||||
virtual void set_use_fast_tape_hack(bool activate) = 0;
|
||||
virtual void set_tape_is_playing(bool is_playing) = 0;
|
||||
virtual void set_use_automatic_tape_motor_control(bool enabled) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -960,6 +960,7 @@
|
||||
4BC3B74E1CD194CC00F86E85 /* Shader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Shader.hpp; sourceTree = "<group>"; };
|
||||
4BC3B7501CD1956900F86E85 /* OutputShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OutputShader.cpp; sourceTree = "<group>"; };
|
||||
4BC3B7511CD1956900F86E85 /* OutputShader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OutputShader.hpp; sourceTree = "<group>"; };
|
||||
4BC542621F32B985001FF613 /* Typer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Typer.hpp; path = ZX8081/Typer.hpp; sourceTree = "<group>"; };
|
||||
4BC5E4901D7ED365008CF980 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StaticAnalyser.cpp; path = ../../StaticAnalyser/Commodore/StaticAnalyser.cpp; sourceTree = "<group>"; };
|
||||
4BC5E4911D7ED365008CF980 /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = StaticAnalyser.hpp; path = ../../StaticAnalyser/Commodore/StaticAnalyser.hpp; sourceTree = "<group>"; };
|
||||
4BC751B11D157E61006C31D9 /* 6522Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6522Tests.swift; sourceTree = "<group>"; };
|
||||
@ -1131,11 +1132,12 @@
|
||||
4B1497931EE4B5AC00CE2596 /* ZX8081 */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B1497901EE4B5A800CE2596 /* ZX8081.cpp */,
|
||||
4B1497911EE4B5A800CE2596 /* ZX8081.hpp */,
|
||||
4BD3A3091EE755C800B5B501 /* Video.cpp */,
|
||||
4BD3A30A1EE755C800B5B501 /* Video.hpp */,
|
||||
4B6A84BB1F130DA6001F28C9 /* Typer.cpp */,
|
||||
4BD3A3091EE755C800B5B501 /* Video.cpp */,
|
||||
4B1497901EE4B5A800CE2596 /* ZX8081.cpp */,
|
||||
4BC542621F32B985001FF613 /* Typer.hpp */,
|
||||
4BD3A30A1EE755C800B5B501 /* Video.hpp */,
|
||||
4B1497911EE4B5A800CE2596 /* ZX8081.hpp */,
|
||||
);
|
||||
name = ZX8081;
|
||||
sourceTree = "<group>";
|
||||
|
@ -15,18 +15,21 @@
|
||||
#import "NSBundle+DataResource.h"
|
||||
|
||||
@implementation CSZX8081 {
|
||||
ZX8081::Machine _zx8081;
|
||||
std::unique_ptr<ZX8081::Machine> _zx8081;
|
||||
}
|
||||
|
||||
- (CRTMachine::Machine * const)machine {
|
||||
return &_zx8081;
|
||||
if(!_zx8081) {
|
||||
_zx8081.reset(ZX8081::Machine::ZX8081());
|
||||
}
|
||||
return _zx8081.get();
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if(self) {
|
||||
_zx8081.set_rom(ZX8081::ROMType::ZX80, [self rom:@"zx80"].stdVector8);
|
||||
_zx8081.set_rom(ZX8081::ROMType::ZX81, [self rom:@"zx81"].stdVector8);
|
||||
_zx8081->set_rom(ZX8081::ROMType::ZX80, [self rom:@"zx80"].stdVector8);
|
||||
_zx8081->set_rom(ZX8081::ROMType::ZX81, [self rom:@"zx81"].stdVector8);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@ -39,62 +42,61 @@
|
||||
|
||||
- (void)clearAllKeys {
|
||||
@synchronized(self) {
|
||||
_zx8081.clear_all_keys();
|
||||
_zx8081->clear_all_keys();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setKey:(uint16_t)key isPressed:(BOOL)isPressed {
|
||||
@synchronized(self) {
|
||||
switch(key)
|
||||
{
|
||||
case VK_ANSI_0: _zx8081.set_key_state(ZX8081::Key::Key0, isPressed); break;
|
||||
case VK_ANSI_1: _zx8081.set_key_state(ZX8081::Key::Key1, isPressed); break;
|
||||
case VK_ANSI_2: _zx8081.set_key_state(ZX8081::Key::Key2, isPressed); break;
|
||||
case VK_ANSI_3: _zx8081.set_key_state(ZX8081::Key::Key3, isPressed); break;
|
||||
case VK_ANSI_4: _zx8081.set_key_state(ZX8081::Key::Key4, isPressed); break;
|
||||
case VK_ANSI_5: _zx8081.set_key_state(ZX8081::Key::Key5, isPressed); break;
|
||||
case VK_ANSI_6: _zx8081.set_key_state(ZX8081::Key::Key6, isPressed); break;
|
||||
case VK_ANSI_7: _zx8081.set_key_state(ZX8081::Key::Key7, isPressed); break;
|
||||
case VK_ANSI_8: _zx8081.set_key_state(ZX8081::Key::Key8, isPressed); break;
|
||||
case VK_ANSI_9: _zx8081.set_key_state(ZX8081::Key::Key9, isPressed); break;
|
||||
switch(key) {
|
||||
case VK_ANSI_0: _zx8081->set_key_state(ZX8081::Key::Key0, isPressed); break;
|
||||
case VK_ANSI_1: _zx8081->set_key_state(ZX8081::Key::Key1, isPressed); break;
|
||||
case VK_ANSI_2: _zx8081->set_key_state(ZX8081::Key::Key2, isPressed); break;
|
||||
case VK_ANSI_3: _zx8081->set_key_state(ZX8081::Key::Key3, isPressed); break;
|
||||
case VK_ANSI_4: _zx8081->set_key_state(ZX8081::Key::Key4, isPressed); break;
|
||||
case VK_ANSI_5: _zx8081->set_key_state(ZX8081::Key::Key5, isPressed); break;
|
||||
case VK_ANSI_6: _zx8081->set_key_state(ZX8081::Key::Key6, isPressed); break;
|
||||
case VK_ANSI_7: _zx8081->set_key_state(ZX8081::Key::Key7, isPressed); break;
|
||||
case VK_ANSI_8: _zx8081->set_key_state(ZX8081::Key::Key8, isPressed); break;
|
||||
case VK_ANSI_9: _zx8081->set_key_state(ZX8081::Key::Key9, isPressed); break;
|
||||
|
||||
case VK_ANSI_Q: _zx8081.set_key_state(ZX8081::Key::KeyQ, isPressed); break;
|
||||
case VK_ANSI_W: _zx8081.set_key_state(ZX8081::Key::KeyW, isPressed); break;
|
||||
case VK_ANSI_E: _zx8081.set_key_state(ZX8081::Key::KeyE, isPressed); break;
|
||||
case VK_ANSI_R: _zx8081.set_key_state(ZX8081::Key::KeyR, isPressed); break;
|
||||
case VK_ANSI_T: _zx8081.set_key_state(ZX8081::Key::KeyT, isPressed); break;
|
||||
case VK_ANSI_Y: _zx8081.set_key_state(ZX8081::Key::KeyY, isPressed); break;
|
||||
case VK_ANSI_U: _zx8081.set_key_state(ZX8081::Key::KeyU, isPressed); break;
|
||||
case VK_ANSI_I: _zx8081.set_key_state(ZX8081::Key::KeyI, isPressed); break;
|
||||
case VK_ANSI_O: _zx8081.set_key_state(ZX8081::Key::KeyO, isPressed); break;
|
||||
case VK_ANSI_P: _zx8081.set_key_state(ZX8081::Key::KeyP, isPressed); break;
|
||||
case VK_ANSI_Q: _zx8081->set_key_state(ZX8081::Key::KeyQ, isPressed); break;
|
||||
case VK_ANSI_W: _zx8081->set_key_state(ZX8081::Key::KeyW, isPressed); break;
|
||||
case VK_ANSI_E: _zx8081->set_key_state(ZX8081::Key::KeyE, isPressed); break;
|
||||
case VK_ANSI_R: _zx8081->set_key_state(ZX8081::Key::KeyR, isPressed); break;
|
||||
case VK_ANSI_T: _zx8081->set_key_state(ZX8081::Key::KeyT, isPressed); break;
|
||||
case VK_ANSI_Y: _zx8081->set_key_state(ZX8081::Key::KeyY, isPressed); break;
|
||||
case VK_ANSI_U: _zx8081->set_key_state(ZX8081::Key::KeyU, isPressed); break;
|
||||
case VK_ANSI_I: _zx8081->set_key_state(ZX8081::Key::KeyI, isPressed); break;
|
||||
case VK_ANSI_O: _zx8081->set_key_state(ZX8081::Key::KeyO, isPressed); break;
|
||||
case VK_ANSI_P: _zx8081->set_key_state(ZX8081::Key::KeyP, isPressed); break;
|
||||
|
||||
case VK_ANSI_A: _zx8081.set_key_state(ZX8081::Key::KeyA, isPressed); break;
|
||||
case VK_ANSI_S: _zx8081.set_key_state(ZX8081::Key::KeyS, isPressed); break;
|
||||
case VK_ANSI_D: _zx8081.set_key_state(ZX8081::Key::KeyD, isPressed); break;
|
||||
case VK_ANSI_F: _zx8081.set_key_state(ZX8081::Key::KeyF, isPressed); break;
|
||||
case VK_ANSI_G: _zx8081.set_key_state(ZX8081::Key::KeyG, isPressed); break;
|
||||
case VK_ANSI_H: _zx8081.set_key_state(ZX8081::Key::KeyH, isPressed); break;
|
||||
case VK_ANSI_J: _zx8081.set_key_state(ZX8081::Key::KeyJ, isPressed); break;
|
||||
case VK_ANSI_K: _zx8081.set_key_state(ZX8081::Key::KeyK, isPressed); break;
|
||||
case VK_ANSI_L: _zx8081.set_key_state(ZX8081::Key::KeyL, isPressed); break;
|
||||
case VK_ANSI_A: _zx8081->set_key_state(ZX8081::Key::KeyA, isPressed); break;
|
||||
case VK_ANSI_S: _zx8081->set_key_state(ZX8081::Key::KeyS, isPressed); break;
|
||||
case VK_ANSI_D: _zx8081->set_key_state(ZX8081::Key::KeyD, isPressed); break;
|
||||
case VK_ANSI_F: _zx8081->set_key_state(ZX8081::Key::KeyF, isPressed); break;
|
||||
case VK_ANSI_G: _zx8081->set_key_state(ZX8081::Key::KeyG, isPressed); break;
|
||||
case VK_ANSI_H: _zx8081->set_key_state(ZX8081::Key::KeyH, isPressed); break;
|
||||
case VK_ANSI_J: _zx8081->set_key_state(ZX8081::Key::KeyJ, isPressed); break;
|
||||
case VK_ANSI_K: _zx8081->set_key_state(ZX8081::Key::KeyK, isPressed); break;
|
||||
case VK_ANSI_L: _zx8081->set_key_state(ZX8081::Key::KeyL, isPressed); break;
|
||||
|
||||
case VK_ANSI_Z: _zx8081.set_key_state(ZX8081::Key::KeyZ, isPressed); break;
|
||||
case VK_ANSI_X: _zx8081.set_key_state(ZX8081::Key::KeyX, isPressed); break;
|
||||
case VK_ANSI_C: _zx8081.set_key_state(ZX8081::Key::KeyC, isPressed); break;
|
||||
case VK_ANSI_V: _zx8081.set_key_state(ZX8081::Key::KeyV, isPressed); break;
|
||||
case VK_ANSI_B: _zx8081.set_key_state(ZX8081::Key::KeyB, isPressed); break;
|
||||
case VK_ANSI_N: _zx8081.set_key_state(ZX8081::Key::KeyN, isPressed); break;
|
||||
case VK_ANSI_M: _zx8081.set_key_state(ZX8081::Key::KeyM, isPressed); break;
|
||||
case VK_ANSI_Z: _zx8081->set_key_state(ZX8081::Key::KeyZ, isPressed); break;
|
||||
case VK_ANSI_X: _zx8081->set_key_state(ZX8081::Key::KeyX, isPressed); break;
|
||||
case VK_ANSI_C: _zx8081->set_key_state(ZX8081::Key::KeyC, isPressed); break;
|
||||
case VK_ANSI_V: _zx8081->set_key_state(ZX8081::Key::KeyV, isPressed); break;
|
||||
case VK_ANSI_B: _zx8081->set_key_state(ZX8081::Key::KeyB, isPressed); break;
|
||||
case VK_ANSI_N: _zx8081->set_key_state(ZX8081::Key::KeyN, isPressed); break;
|
||||
case VK_ANSI_M: _zx8081->set_key_state(ZX8081::Key::KeyM, isPressed); break;
|
||||
|
||||
case VK_Shift:
|
||||
case VK_RightShift:
|
||||
_zx8081.set_key_state(ZX8081::Key::KeyShift, isPressed); break;
|
||||
_zx8081->set_key_state(ZX8081::Key::KeyShift, isPressed); break;
|
||||
break;
|
||||
|
||||
case VK_ANSI_Period:_zx8081.set_key_state(ZX8081::Key::KeyDot, isPressed); break;
|
||||
case VK_Return: _zx8081.set_key_state(ZX8081::Key::KeyEnter, isPressed); break;
|
||||
case VK_Space: _zx8081.set_key_state(ZX8081::Key::KeySpace, isPressed); break;
|
||||
case VK_ANSI_Period:_zx8081->set_key_state(ZX8081::Key::KeyDot, isPressed); break;
|
||||
case VK_Return: _zx8081->set_key_state(ZX8081::Key::KeyEnter, isPressed); break;
|
||||
case VK_Space: _zx8081->set_key_state(ZX8081::Key::KeySpace, isPressed); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -106,21 +108,21 @@
|
||||
- (void)setUseFastLoadingHack:(BOOL)useFastLoadingHack {
|
||||
@synchronized(self) {
|
||||
_useFastLoadingHack = useFastLoadingHack;
|
||||
_zx8081.set_use_fast_tape_hack(useFastLoadingHack ? true : false);
|
||||
_zx8081->set_use_fast_tape_hack(useFastLoadingHack ? true : false);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setTapeIsPlaying:(BOOL)tapeIsPlaying {
|
||||
@synchronized(self) {
|
||||
_tapeIsPlaying = tapeIsPlaying;
|
||||
_zx8081.set_tape_is_playing(tapeIsPlaying ? true : false);
|
||||
_zx8081->set_tape_is_playing(tapeIsPlaying ? true : false);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setUseAutomaticTapeMotorControl:(BOOL)useAutomaticTapeMotorControl {
|
||||
@synchronized(self) {
|
||||
_useAutomaticTapeMotorControl = useAutomaticTapeMotorControl;
|
||||
_zx8081.set_use_automatic_tape_motor_control(useAutomaticTapeMotorControl ? true : false);
|
||||
_zx8081->set_use_automatic_tape_motor_control(useAutomaticTapeMotorControl ? true : false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,6 +108,14 @@ struct PartialMachineCycle {
|
||||
}
|
||||
};
|
||||
|
||||
class BusHandler {
|
||||
public:
|
||||
void flush() {}
|
||||
HalfCycles perform_machine_cycle(const PartialMachineCycle &cycle) {
|
||||
return HalfCycles(0);
|
||||
}
|
||||
};
|
||||
|
||||
// Elemental bus operations
|
||||
#define ReadOpcodeStart() {PartialMachineCycle::ReadOpcodeStart, HalfCycles(3), &pc_.full, &operation_, false}
|
||||
#define ReadOpcodeWait(f) {PartialMachineCycle::ReadOpcodeWait, HalfCycles(2), &pc_.full, &operation_, f}
|
||||
@ -174,6 +182,8 @@ struct PartialMachineCycle {
|
||||
*/
|
||||
template <class T> class Processor {
|
||||
private:
|
||||
T &bus_handler_;
|
||||
|
||||
uint8_t a_;
|
||||
RegisterPair bc_, de_, hl_;
|
||||
RegisterPair afDash_, bcDash_, deDash_, hlDash_;
|
||||
@ -296,7 +306,6 @@ template <class T> class Processor {
|
||||
};
|
||||
const MicroOp *scheduled_program_counter_;
|
||||
|
||||
|
||||
struct InstructionPage {
|
||||
std::vector<MicroOp *> instructions;
|
||||
std::vector<MicroOp> all_operations;
|
||||
@ -774,7 +783,7 @@ template <class T> class Processor {
|
||||
}
|
||||
|
||||
public:
|
||||
Processor() :
|
||||
Processor(T &bus_handler) :
|
||||
halt_mask_(0xff),
|
||||
interrupt_mode_(0),
|
||||
wait_line_(false),
|
||||
@ -784,7 +793,8 @@ template <class T> class Processor {
|
||||
nmi_line_(false),
|
||||
bus_request_line_(false),
|
||||
pc_increment_(1),
|
||||
scheduled_program_counter_(nullptr) {
|
||||
scheduled_program_counter_(nullptr),
|
||||
bus_handler_(bus_handler) {
|
||||
set_flags(0xff);
|
||||
|
||||
MicroOp conditional_call_untaken_program[] = Sequence(ReadInc(pc_, temp16_.bytes.high));
|
||||
@ -901,9 +911,9 @@ template <class T> class Processor {
|
||||
|
||||
while(bus_request_line_) {
|
||||
static PartialMachineCycle bus_acknowledge_cycle = {PartialMachineCycle::BusAcknowledge, HalfCycles(2), nullptr, nullptr, false};
|
||||
number_of_cycles_ -= static_cast<T *>(this)->perform_machine_cycle(bus_acknowledge_cycle) + HalfCycles(1);
|
||||
number_of_cycles_ -= bus_handler_.perform_machine_cycle(bus_acknowledge_cycle) + HalfCycles(1);
|
||||
if(!number_of_cycles_) {
|
||||
static_cast<T *>(this)->flush();
|
||||
bus_handler_.flush();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -922,7 +932,7 @@ template <class T> class Processor {
|
||||
case MicroOp::BusOperation:
|
||||
if(number_of_cycles_ < operation->machine_cycle.length) {
|
||||
scheduled_program_counter_--;
|
||||
static_cast<T *>(this)->flush();
|
||||
bus_handler_.flush();
|
||||
return;
|
||||
}
|
||||
if(operation->machine_cycle.was_requested) {
|
||||
@ -934,7 +944,7 @@ template <class T> class Processor {
|
||||
}
|
||||
number_of_cycles_ -= operation->machine_cycle.length;
|
||||
last_request_status_ = request_status_;
|
||||
number_of_cycles_ -= static_cast<T *>(this)->perform_machine_cycle(operation->machine_cycle);
|
||||
number_of_cycles_ -= bus_handler_.perform_machine_cycle(operation->machine_cycle);
|
||||
break;
|
||||
case MicroOp::MoveToNextProgram:
|
||||
advance_operation();
|
||||
|
@ -12,9 +12,9 @@
|
||||
using namespace CPU::Z80;
|
||||
namespace {
|
||||
|
||||
class ConcreteAllRAMProcessor: public AllRAMProcessor, public Processor<ConcreteAllRAMProcessor> {
|
||||
class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler {
|
||||
public:
|
||||
ConcreteAllRAMProcessor() : AllRAMProcessor() {}
|
||||
ConcreteAllRAMProcessor() : AllRAMProcessor(), z80_(*this) {}
|
||||
|
||||
inline HalfCycles perform_machine_cycle(const PartialMachineCycle &cycle) {
|
||||
timestamp_ += cycle.length;
|
||||
@ -64,36 +64,39 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public Processor<Concrete
|
||||
}
|
||||
|
||||
void run_for(const Cycles cycles) {
|
||||
CPU::Z80::Processor<ConcreteAllRAMProcessor>::run_for(cycles);
|
||||
z80_.run_for(cycles);
|
||||
}
|
||||
|
||||
uint16_t get_value_of_register(Register r) {
|
||||
return CPU::Z80::Processor<ConcreteAllRAMProcessor>::get_value_of_register(r);
|
||||
return z80_.get_value_of_register(r);
|
||||
}
|
||||
|
||||
void set_value_of_register(Register r, uint16_t value) {
|
||||
CPU::Z80::Processor<ConcreteAllRAMProcessor>::set_value_of_register(r, value);
|
||||
z80_.set_value_of_register(r, value);
|
||||
}
|
||||
|
||||
bool get_halt_line() {
|
||||
return CPU::Z80::Processor<ConcreteAllRAMProcessor>::get_halt_line();
|
||||
return z80_.get_halt_line();
|
||||
}
|
||||
|
||||
void reset_power_on() {
|
||||
return CPU::Z80::Processor<ConcreteAllRAMProcessor>::reset_power_on();
|
||||
return z80_.reset_power_on();
|
||||
}
|
||||
|
||||
void set_interrupt_line(bool value) {
|
||||
CPU::Z80::Processor<ConcreteAllRAMProcessor>::set_interrupt_line(value);
|
||||
z80_.set_interrupt_line(value);
|
||||
}
|
||||
|
||||
void set_non_maskable_interrupt_line(bool value) {
|
||||
CPU::Z80::Processor<ConcreteAllRAMProcessor>::set_non_maskable_interrupt_line(value);
|
||||
z80_.set_non_maskable_interrupt_line(value);
|
||||
}
|
||||
|
||||
void set_wait_line(bool value) {
|
||||
CPU::Z80::Processor<ConcreteAllRAMProcessor>::set_wait_line(value);
|
||||
z80_.set_wait_line(value);
|
||||
}
|
||||
|
||||
private:
|
||||
CPU::Z80::Processor<ConcreteAllRAMProcessor> z80_;
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user