1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-19 23:32:28 +00:00
CLK/Machines/Apple/AppleIIgs/AppleIIgs.cpp

320 lines
9.9 KiB
C++
Raw Normal View History

//
// AppleIIgs.cpp
// Clock Signal
//
// Created by Thomas Harte on 20/10/2020.
// Copyright 2020 Thomas Harte. All rights reserved.
//
#include "AppleIIgs.hpp"
#include "../../MachineTypes.hpp"
#include "../../../Processors/65816/65816.hpp"
#include "../../../Analyser/Static/AppleIIgs/Target.hpp"
#include "MemoryMap.hpp"
2020-11-01 00:39:32 +00:00
#include "Video.hpp"
#include "../../../Components/8530/z8530.hpp"
#include "../../../Components/AppleClock/AppleClock.hpp"
#include "../../../Components/DiskII/IWM.hpp"
#include "../../Utility/MemoryFuzzer.hpp"
#include <cassert>
#include <array>
namespace Apple {
namespace IIgs {
class ConcreteMachine:
public Apple::IIgs::Machine,
public MachineTypes::TimedMachine,
public MachineTypes::ScanProducer,
public CPU::MOS6502Esque::BusHandler<uint32_t> {
public:
ConcreteMachine(const Analyser::Static::AppleIIgs::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
m65816_(*this) {
set_clock_rate(14318180.0);
using Target = Analyser::Static::AppleIIgs::Target;
std::vector<ROMMachine::ROM> rom_descriptions;
const std::string machine_name = "AppleIIgs";
switch(target.model) {
case Target::Model::ROM00:
/* TODO */
case Target::Model::ROM01:
rom_descriptions.emplace_back(machine_name, "the Apple IIgs ROM01", "apple2gs.rom", 128*1024, 0x42f124b0);
break;
case Target::Model::ROM03:
rom_descriptions.emplace_back(machine_name, "the Apple IIgs ROM03", "apple2gs.rom2", 256*1024, 0xde7ddf29);
break;
}
const auto roms = rom_fetcher(rom_descriptions);
if(!roms[0]) {
throw ROMMachine::Error::MissingROMs;
}
rom_ = *roms[0];
size_t ram_size = 0;
switch(target.memory_model) {
case Target::MemoryModel::TwoHundredAndFiftySixKB:
ram_size = 256;
break;
case Target::MemoryModel::OneMB:
ram_size = 128 + 1024;
break;
case Target::MemoryModel::EightMB:
ram_size = 128 + 8 * 1024;
break;
}
ram_.resize(ram_size * 1024);
memory_.set_storage(ram_, rom_);
2020-10-29 01:23:45 +00:00
// TODO: enable once machine is otherwise sane.
// Memory::Fuzz(ram_);
2020-10-29 01:23:45 +00:00
// Sync up initial values.
memory_.set_speed_register(speed_register_);
}
void run_for(const Cycles cycles) override {
m65816_.run_for(cycles);
}
void set_scan_target(Outputs::Display::ScanTarget *) override {
}
Outputs::Display::ScanStatus get_scaled_scan_status() const override {
return Outputs::Display::ScanStatus();
}
forceinline Cycles perform_bus_operation(const CPU::WDC65816::BusOperation operation, const uint32_t address, uint8_t *const value) {
const auto &region = MemoryMapRegion(memory_, address);
// TODO: potentially push time to clock_.
if(region.flags & MemoryMap::Region::IsIO) {
// Ensure classic auxiliary and language card accesses have effect.
const bool is_read = isReadOperation(operation);
memory_.access(uint16_t(address), is_read);
switch(address & 0xffff) {
// New video register.
case 0xc029:
if(is_read) {
*value = 0x01;
} else {
printf("New video: %02x\n", *value);
// TODO: this bit should affect memory bank selection, somehow?
// Cf. Page 90.
}
break;
// Shadow register.
case 0xc035:
if(is_read) {
*value = memory_.get_shadow_register();
} else {
memory_.set_shadow_register(*value);
}
break;
// Clock data.
case 0xc033:
if(is_read) {
*value = clock_.get_data();
} else {
clock_.set_data(*value);
}
break;
// Clock and border control.
case 0xc034:
if(is_read) {
*value = clock_.get_control();
} else {
clock_.set_control(*value);
// TODO: also set border colour.
}
break;
2020-10-29 01:23:45 +00:00
// Speed register.
case 0xc036:
if(is_read) {
2020-10-29 01:23:45 +00:00
*value = speed_register_;
} else {
memory_.set_speed_register(*value);
speed_register_ = *value;
printf("[Unimplemented] most of speed register: %02x\n", *value);
}
break;
// [Memory] State register.
case 0xc068:
if(is_read) {
*value = memory_.get_state_register();
} else {
memory_.set_state_register(*value);
}
break;
// Various independent memory switch reads [TODO: does the IIe-style keyboard the low seven?].
#define SwitchRead(s) *value = memory_.s ? 0x80 : 0x00
#define LanguageRead(s) SwitchRead(language_card_switches().state().s)
#define AuxiliaryRead(s) SwitchRead(auxiliary_switches().switches().s)
2020-11-01 00:39:32 +00:00
#define VideoRead(s) video_.s
case 0xc011: LanguageRead(bank1); break;
case 0xc012: LanguageRead(read); break;
case 0xc013: AuxiliaryRead(read_auxiliary_memory); break;
case 0xc014: AuxiliaryRead(write_auxiliary_memory); break;
case 0xc015: AuxiliaryRead(internal_CX_rom); break;
case 0xc016: AuxiliaryRead(alternative_zero_page); break;
case 0xc017: AuxiliaryRead(slot_C3_rom); break;
2020-11-01 00:39:32 +00:00
case 0xc018: VideoRead(get_80_store()); break;
// case 0xc019: VideoRead(get_is_vertical_blank(cycles_since_video_update_)); break;
case 0xc01a: VideoRead(get_text()); break;
case 0xc01b: VideoRead(get_mixed()); break;
case 0xc01c: VideoRead(get_page2()); break;
case 0xc01d: VideoRead(get_high_resolution()); break;
case 0xc01e: VideoRead(get_alternative_character_set()); break;
case 0xc01f: VideoRead(get_80_columns()); break;
case 0xc046: VideoRead(get_annunciator_3()); break;
#undef VideoRead
#undef AuxiliaryRead
#undef LanguageRead
#undef SwitchRead
2020-11-01 00:39:32 +00:00
// Video switches.
case 0xc050: case 0xc051:
video_.set_text(address & 1);
break;
case 0xc052: case 0xc053:
video_.set_mixed(address & 1);
break;
case 0xc054: case 0xc055:
video_.set_page2(address&1);
break;
case 0xc056: case 0xc057:
video_.set_high_resolution(address&1);
break;
case 0xc05e: case 0xc05f:
video_.set_annunciator_3(!(address&1));
break;
// The SCC.
case 0xc038: case 0xc039: case 0xc03a: case 0xc03b:
if(isReadOperation(operation)) {
*value = scc_.read(int(address));
} else {
scc_.write(int(address), *value);
}
break;
// These were all dealt with by the call to memory_.access.
// TODO: subject to read data? Does vapour lock apply?
case 0xc000: case 0xc001: case 0xc002: case 0xc003: case 0xc004: case 0xc005:
case 0xc006: case 0xc007: case 0xc008: case 0xc009: case 0xc00a: case 0xc00b:
break;
2020-10-31 01:42:43 +00:00
// Interrupt ROM addresses; Cf. P25 of the Hardware Reference.
case 0xc071: case 0xc072: case 0xc073: case 0xc074: case 0xc075: case 0xc076: case 0xc077:
case 0xc078: case 0xc079: case 0xc07a: case 0xc07b: case 0xc07c: case 0xc07d: case 0xc07e: case 0xc07f:
if(isReadOperation(operation)) {
*value = rom_[rom_.size() - 65536 + (address & 0xffff)];
}
break;
default:
if((address & 0xffff) < 0xc100) {
// TODO: all other IO accesses.
printf("Unhandled IO: %04x\n", address & 0xffff);
assert(false);
} else {
// Card IO. Not implemented!
if(isReadOperation(operation)) {
*value = 0xff;
}
}
}
} else {
2020-10-31 00:11:55 +00:00
// For debugging purposes; if execution heads off into an unmapped page then
// it's pretty certain that my 65816 still has issues.
assert(operation != CPU::WDC65816::BusOperation::ReadOpcode || region.read);
if(isReadOperation(operation)) {
MemoryMapRead(region, address, value);
} else {
MemoryMapWrite(memory_, region, address, value);
}
}
2020-10-31 00:11:55 +00:00
printf("%06x %s %02x", address, isReadOperation(operation) ? "->" : "<-", *value);
if(operation == CPU::WDC65816::BusOperation::ReadOpcode) {
printf(" a:%04x x:%04x y:%04x s:%04x e:%d p:%02x db:%02x pb:%02x d:%04x\n",
m65816_.get_value_of_register(CPU::WDC65816::Register::A),
m65816_.get_value_of_register(CPU::WDC65816::Register::X),
m65816_.get_value_of_register(CPU::WDC65816::Register::Y),
m65816_.get_value_of_register(CPU::WDC65816::Register::StackPointer),
m65816_.get_value_of_register(CPU::WDC65816::Register::EmulationFlag),
m65816_.get_value_of_register(CPU::WDC65816::Register::Flags),
m65816_.get_value_of_register(CPU::WDC65816::Register::DataBank),
m65816_.get_value_of_register(CPU::WDC65816::Register::ProgramBank),
m65816_.get_value_of_register(CPU::WDC65816::Register::Direct)
);
} else printf("\n");
Cycles duration = Cycles(5);
// TODO: determine the cost of this access.
// if((mapping.flags & BankMapping::Is1Mhz) || ((mapping.flags & BankMapping::IsShadowed) && !isReadOperation(operation))) {
// // TODO: (i) get into phase; (ii) allow for the 1Mhz bus length being sporadically 16 rather than 14.
// duration = Cycles(14);
// } else {
// // TODO: (i) get into phase; (ii) allow for collisions with the refresh cycle.
// duration = Cycles(5);
// }
fast_access_phase_ = (fast_access_phase_ + duration.as<int>()) % 5; // TODO: modulo something else, to allow for refresh.
slow_access_phase_ = (slow_access_phase_ + duration.as<int>()) % 14; // TODO: modulo something else, to allow for stretched cycles.
return duration;
}
private:
CPU::WDC65816::Processor<ConcreteMachine, false> m65816_;
MemoryMap memory_;
2020-11-01 00:39:32 +00:00
Apple::Clock::ParallelClock clock_;
2020-11-01 00:39:32 +00:00
Apple::IIgs::Video::Video video_;
int fast_access_phase_ = 0;
int slow_access_phase_ = 0;
2020-10-31 01:50:39 +00:00
uint8_t speed_register_ = 0x40; // i.e. Power-on status. (TODO: only if ROM03?)
2020-10-29 01:23:45 +00:00
// MARK: - Memory storage.
std::vector<uint8_t> ram_{};
std::vector<uint8_t> rom_;
// MARK: - Other components.
Zilog::SCC::z8530 scc_;
};
}
}
using namespace Apple::IIgs;
Machine *Machine::AppleIIgs(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
return new ConcreteMachine(*dynamic_cast<const Analyser::Static::AppleIIgs::Target *>(target), rom_fetcher);
}
Machine::~Machine() {}