1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-25 18:30:07 +00:00

Factors out auxiliary memory switches and related decisions.

This commit is contained in:
Thomas Harte 2020-10-22 22:33:31 -04:00
parent 410c99de54
commit 9371a8993f
4 changed files with 290 additions and 103 deletions

View File

@ -19,9 +19,10 @@
#include "../../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp"
#include "../../../Outputs/Log.hpp"
#include "AuxiliaryMemorySwitches.hpp"
#include "Card.hpp"
#include "DiskIICard.hpp"
#include "LanguageCard.hpp"
#include "LanguageCardSwitches.hpp"
#include "Video.hpp"
#include "../../../Analyser/Static/AppleII/Target.hpp"
@ -188,42 +189,39 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
}
}
// MARK: - The language card.
LanguageCard<ConcreteMachine> language_card_;
friend LanguageCard<ConcreteMachine>;
// MARK: The language card.
LanguageCardSwitches<ConcreteMachine> language_card_;
AuxiliaryMemorySwitches<ConcreteMachine> auxiliary_switches_;
friend LanguageCardSwitches<ConcreteMachine>;
friend AuxiliaryMemorySwitches<ConcreteMachine>;
void set_language_card_paging() {
uint8_t *const ram = alternative_zero_page_ ? aux_ram_ : ram_;
const auto language_state = language_card_.state();
const auto zero_state = auxiliary_switches_.zero_state();
uint8_t *const ram = zero_state ? aux_ram_ : ram_;
uint8_t *const rom = is_iie() ? &rom_[3840] : rom_.data();
const auto state = language_card_.state();
page(0xd0, 0xe0,
state.read ? &ram[state.bank1 ? 0xd000 : 0xc000] : rom,
state.write ? nullptr : &ram[state.bank1 ? 0xd000 : 0xc000]);
language_state.read ? &ram[language_state.bank1 ? 0xd000 : 0xc000] : rom,
language_state.write ? nullptr : &ram[language_state.bank1 ? 0xd000 : 0xc000]);
page(0xe0, 0x100,
state.read ? &ram[0xe000] : &rom[0x1000],
state.write ? nullptr : &ram[0xe000]);
language_state.read ? &ram[0xe000] : &rom[0x1000],
language_state.write ? nullptr : &ram[0xe000]);
}
// MARK - The IIe's ROM controls.
bool internal_CX_rom_ = false;
bool slot_C3_rom_ = false;
bool internal_c8_rom_ = false;
// MARK: Auxiliary memory and the other IIe improvements.
void set_card_paging() {
page(0xc1, 0xc8, internal_CX_rom_ ? rom_.data() : nullptr, nullptr);
const auto state = auxiliary_switches_.card_state();
if(!internal_CX_rom_) {
if(!slot_C3_rom_) read_pages_[0xc3] = &rom_[0xc300 - 0xc100];
}
page(0xc8, 0xd0, (internal_CX_rom_ || internal_c8_rom_) ? &rom_[0xc800 - 0xc100] : nullptr, nullptr);
page(0xc1, 0xc4, state.region_C1_C3 ? &rom_[0xc100 - 0xc100] : nullptr, nullptr);
read_pages_[0xc3] = state.region_C3 ? &rom_[0xc300 - 0xc100] : nullptr;
page(0xc4, 0xc8, state.region_C4_C8 ? &rom_[0xc400 - 0xc100] : nullptr, nullptr);
page(0xc8, 0xd0, state.region_C8_D0 ? &rom_[0xc800 - 0xc100] : nullptr, nullptr);
}
// MARK - The IIe's auxiliary RAM controls.
bool alternative_zero_page_ = false;
void set_zero_page_paging() {
if(alternative_zero_page_) {
if(auxiliary_switches_.zero_state()) {
write_pages_[0] = aux_ram_;
} else {
write_pages_[0] = ram_;
@ -231,27 +229,30 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
write_pages_[1] = write_pages_[0] + 256;
read_pages_[0] = write_pages_[0];
read_pages_[1] = write_pages_[1];
// Zero page banking also affects interpretation of the language card's switches.
set_language_card_paging();
}
bool read_auxiliary_memory_ = false;
bool write_auxiliary_memory_ = false;
void set_main_paging() {
page(0x02, 0xc0,
read_auxiliary_memory_ ? &aux_ram_[0x0200] : &ram_[0x0200],
write_auxiliary_memory_ ? &aux_ram_[0x0200] : &ram_[0x0200]);
const auto state = auxiliary_switches_.main_state();
if(video_.get_80_store()) {
bool use_aux_ram = video_.get_page2();
page(0x04, 0x08,
use_aux_ram ? &aux_ram_[0x0400] : &ram_[0x0400],
use_aux_ram ? &aux_ram_[0x0400] : &ram_[0x0400]);
page(0x02, 0x04,
state.base.read ? &aux_ram_[0x0200] : &ram_[0x0200],
state.base.write ? &aux_ram_[0x0200] : &ram_[0x0200]);
page(0x08, 0x20,
state.base.read ? &aux_ram_[0x0800] : &ram_[0x0800],
state.base.write ? &aux_ram_[0x0800] : &ram_[0x0800]);
page(0x40, 0xc0,
state.base.read ? &aux_ram_[0x4000] : &ram_[0x4000],
state.base.write ? &aux_ram_[0x4000] : &ram_[0x4000]);
if(video_.get_high_resolution()) {
page(0x20, 0x40,
use_aux_ram ? &aux_ram_[0x2000] : &ram_[0x2000],
use_aux_ram ? &aux_ram_[0x2000] : &ram_[0x2000]);
}
}
page(0x04, 0x08,
state.region_04_08.read ? &aux_ram_[0x0400] : &ram_[0x0400],
state.region_04_08.write ? &aux_ram_[0x0400] : &ram_[0x0400]);
page(0x20, 0x40,
state.region_20_40.read ? &aux_ram_[0x2000] : &ram_[0x2000],
state.region_20_40.write ? &aux_ram_[0x2000] : &ram_[0x2000]);
}
// MARK - typing
@ -318,7 +319,8 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
video_(video_bus_handler_),
audio_toggle_(audio_queue_),
speaker_(audio_toggle_),
language_card_(*this) {
language_card_(*this),
auxiliary_switches_(*this) {
// The system's master clock rate.
constexpr float master_clock = 14318180.0;
@ -398,9 +400,6 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
// Set the whole card area to initially backed by nothing.
page(0xc0, 0xd0, nullptr, nullptr);
// Set proper values for the language card/ROM area.
set_language_card_paging();
insert_media(target.media);
}
@ -455,14 +454,8 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
if(write_pages_[address >> 8]) write_pages_[address >> 8][address & 0xff] = *value;
}
if(is_iie() && address >= 0xc300 && address < 0xd000) {
bool internal_c8_rom = internal_c8_rom_;
internal_c8_rom |= ((address >> 8) == 0xc3) && !slot_C3_rom_;
internal_c8_rom &= (address != 0xcfff);
if(internal_c8_rom != internal_c8_rom_) {
internal_c8_rom_ = internal_c8_rom;
set_card_paging();
}
if(is_iie()) {
auxiliary_switches_.access(address, isReadOperation(operation));
}
} else {
// Assume a vapour read unless it turns out otherwise; this is a little
@ -534,11 +527,11 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
#define IIeSwitchRead(s) *value = get_keyboard_input(); if(is_iie()) *value = (*value & 0x7f) | (s ? 0x80 : 0x00);
case 0xc011: IIeSwitchRead(language_card_.state().bank1); break;
case 0xc012: IIeSwitchRead(language_card_.state().read); break;
case 0xc013: IIeSwitchRead(read_auxiliary_memory_); break;
case 0xc014: IIeSwitchRead(write_auxiliary_memory_); break;
case 0xc015: IIeSwitchRead(internal_CX_rom_); break;
case 0xc016: IIeSwitchRead(alternative_zero_page_); break;
case 0xc017: IIeSwitchRead(slot_C3_rom_); break;
case 0xc013: IIeSwitchRead(auxiliary_switches_.switches().read_auxiliary_memory); break;
case 0xc014: IIeSwitchRead(auxiliary_switches_.switches().write_auxiliary_memory); break;
case 0xc015: IIeSwitchRead(auxiliary_switches_.switches().internal_CX_rom); break;
case 0xc016: IIeSwitchRead(auxiliary_switches_.switches().alternative_zero_page); break;
case 0xc017: IIeSwitchRead(auxiliary_switches_.switches().slot_C3_rom); break;
case 0xc018: IIeSwitchRead(video_.get_80_store()); break;
case 0xc019: IIeSwitchRead(video_.get_is_vertical_blank(cycles_since_video_update_)); break;
case 0xc01a: IIeSwitchRead(video_.get_text()); break;
@ -556,6 +549,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
} else {
// Write-only switches. All IIe as currently implemented.
if(is_iie()) {
auxiliary_switches_.access(address, false);
switch(address) {
default: break;
@ -563,40 +557,6 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
case 0xc001:
update_video();
video_.set_80_store(!!(address&1));
set_main_paging();
break;
case 0xc002:
case 0xc003:
read_auxiliary_memory_ = !!(address&1);
set_main_paging();
break;
case 0xc004:
case 0xc005:
write_auxiliary_memory_ = !!(address&1);
set_main_paging();
break;
case 0xc006:
case 0xc007:
internal_CX_rom_ = !!(address&1);
set_card_paging();
break;
case 0xc008:
case 0xc009:
// The alternative zero page setting affects both bank 0 and any RAM
// that's paged as though it were on a language card.
alternative_zero_page_ = !!(address&1);
set_zero_page_paging();
set_language_card_paging();
break;
case 0xc00a:
case 0xc00b:
slot_C3_rom_ = !!(address&1);
set_card_paging();
break;
case 0xc00c:
@ -639,13 +599,13 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
case 0xc055:
update_video();
video_.set_page2(!!(address&1));
set_main_paging();
auxiliary_switches_.access(address, isReadOperation(operation));
break;
case 0xc056:
case 0xc057:
update_video();
video_.set_high_resolution(!!(address&1));
set_main_paging();
auxiliary_switches_.access(address, isReadOperation(operation));
break;
case 0xc05e:

View File

@ -0,0 +1,225 @@
//
// AuxiliaryMemorySwitches.hpp
// Clock Signal
//
// Created by Thomas Harte on 22/10/2020.
// Copyright © 2020 Thomas Harte. All rights reserved.
//
#ifndef AuxiliaryMemorySwitches_h
#define AuxiliaryMemorySwitches_h
namespace Apple {
namespace II {
template <typename Machine> class AuxiliaryMemorySwitches {
public:
static constexpr bool Auxiliary = true;
static constexpr bool Main = false;
static constexpr bool ROM = true;
static constexpr bool Card = false;
/// Describes banking state between $0200 and $BFFF.
struct MainState {
struct Region {
/// @c true indicates auxiliary memory should be read from in this region; @c false indicates that main memory should be used.
bool read = false;
/// @c true indicates auxiliary memory should be written to in this region; @c false indicates that main memory should be used.
bool write = false;
};
/// Describes banking state in the ranges $0200$3FFF, $0800$1FFF and $4000$BFFF.
Region base;
/// Describes banking state in the range $0400$7FFF.
Region region_04_08;
/// Describes banking state in the range $2000$3FFF.
Region region_20_40;
bool operator != (const MainState &rhs) {
return
base.read != rhs.base.read || base.write != rhs.base.write ||
region_04_08.read != rhs.region_04_08.read || region_04_08.write != rhs.region_04_08.write ||
region_20_40.read != rhs.region_20_40.read || region_20_40.write != rhs.region_20_40.write;
}
};
/// Describes banking state between $C100 and $Cfff.
struct CardState {
/// @c true indicates that the built-in ROM should appear from $C100 to $C2FF @c false indicates that cards should service those accesses.
bool region_C1_C3 = false;
/// @c true indicates that the built-in ROM should appear from $C300 to $C3FF; @c false indicates that cards should service those accesses.
bool region_C3 = false;
/// @c true indicates that the built-in ROM should appear from $C400 to $C7FF; @c false indicates that cards should service those accesses.
bool region_C4_C8 = false;
/// @c true indicates that the built-in ROM should appear from $C800 to $CFFF; @c false indicates that cards should service those accesses.
bool region_C8_D0 = false;
bool operator != (const CardState &rhs) {
return
region_C1_C3 != rhs.region_C1_C3 ||
region_C3 != rhs.region_C3 ||
region_C4_C8 != rhs.region_C4_C8 ||
region_C8_D0 != rhs.region_C8_D0;
}
};
/// Descibes banking state between $0000 and $01ff; @c true indicates that auxiliary memory should be used; @c false indicates main memory.
using ZeroState = bool;
/// Returns raw switch state for all switches that affect banking, even if they're logically video switches.
struct SwitchState {
bool read_auxiliary_memory = false;
bool write_auxiliary_memory = false;
bool internal_CX_rom = false;
bool slot_C3_rom = false;
bool internal_C8_rom = false;
bool store_80 = false;
bool alternative_zero_page = false;
bool video_page_2 = false;
bool high_resolution = false;
};
AuxiliaryMemorySwitches(Machine &machine) : machine_(machine) {}
/// Used by an owner to forward, at least, any access in the range $C000 to $C00B,
/// in $C054 to $C058, or in the range $C300 to $CFFF. Safe to call for any [16-bit] address.
void access(uint16_t address, bool is_read) {
if(address >= 0xc300 && address < 0xd000) {
switches_.internal_C8_rom |= ((address >> 8) == 0xc3) && !switches_.slot_C3_rom;
switches_.internal_C8_rom &= (address != 0xcfff);
set_card_paging();
return;
}
if(is_read) return;
switch(address) {
default: break;
case 0xc000: case 0xc001:
switches_.store_80 = address & 1;
set_main_paging();
break;
case 0xc002: case 0xc003:
switches_.read_auxiliary_memory = address & 1;
set_main_paging();
break;
case 0xc004: case 0xc005:
switches_.write_auxiliary_memory = address & 1;
set_main_paging();
break;
case 0xc006: case 0xc007:
switches_.internal_CX_rom = address & 1;
set_card_paging();
break;
case 0xc008: case 0xc009:
if(switches_.alternative_zero_page != bool(address & 1)) {
switches_.alternative_zero_page = address & 1;
set_zero_page_paging();
}
break;
case 0xc00a: case 0xc00b:
switches_.slot_C3_rom = address & 1;
set_card_paging();
break;
case 0xc054: case 0xc055:
switches_.video_page_2 = address & 1;
set_main_paging();
break;
case 0xc056: case 0xc057:
switches_.high_resolution = address & 1;
set_main_paging();
break;
}
}
const MainState &main_state() {
return main_state_;
}
const CardState &card_state() {
return card_state_;
}
const ZeroState zero_state() {
return switches_.alternative_zero_page;
}
const SwitchState switches() {
return switches_;
}
private:
Machine &machine_;
SwitchState switches_;
MainState main_state_;
void set_main_paging() {
MainState previous_state = main_state_;
// The two appropriately named switches provide the base case.
main_state_.base.read = switches_.read_auxiliary_memory;
main_state_.base.write = switches_.write_auxiliary_memory;
if(switches_.store_80) {
// If store 80 is set, use the page 2 flag for the lower carve out;
// if both store 80 and high resolution are set, use the page 2 flag for both carve outs.
main_state_.region_04_08.read = main_state_.region_04_08.write = switches_.video_page_2;
if(switches_.high_resolution) {
main_state_.region_20_40.read = main_state_.region_20_40.write = switches_.video_page_2;
} else {
main_state_.region_20_40 = main_state_.base;
}
} else {
main_state_.region_04_08 = main_state_.region_20_40 = main_state_.base;
}
if(previous_state != main_state_) {
machine_.set_main_paging();
}
}
CardState card_state_;
void set_card_paging() {
CardState previous_state = card_state_;
// By default apply the CX switch through to $C7FF.
card_state_.region_C1_C3 = card_state_.region_C4_C8 = switches_.internal_CX_rom;
// Allow the C3 region to be switched to internal ROM in isolation even if the rest of the
// first half of the CX region is diabled, if its specific switch is also disabled.
if(!switches_.internal_CX_rom && !switches_.slot_C3_rom) {
card_state_.region_C3 = true;
} else {
card_state_.region_C3 = card_state_.region_C1_C3;
}
// Apply the CX switch to $C800+, but also allow the C8 switch to select that region in isolation.
card_state_.region_C8_D0 = switches_.internal_CX_rom || switches_.internal_C8_rom;
if(previous_state != card_state_) {
machine_.set_card_paging();
}
}
void set_zero_page_paging() {
// Believe it or not, the zero page is just set or cleared by a single flag.
// As though life were rational.
machine_.set_zero_page_paging();
}
};
}
}
#endif /* AuxiliaryMemorySwitches_h */

View File

@ -1,18 +1,18 @@
//
// LanguageCard.hpp
// LanguageCardSwitches.hpp
// Clock Signal
//
// Created by Thomas Harte on 22/10/2020.
// Copyright © 2020 Thomas Harte. All rights reserved.
//
#ifndef LanguageCard_h
#define LanguageCard_h
#ifndef LanguageCardSwitches_h
#define LanguageCardSwitches_h
namespace Apple {
namespace II {
template <typename Machine> class LanguageCard {
template <typename Machine> class LanguageCardSwitches {
public:
struct State {
/// Indicates which 4kb chunk of RAM should be visible at $Dxxx if RAM is visible at all.
@ -30,9 +30,9 @@ template <typename Machine> class LanguageCard {
bool pre_write = false;
};
LanguageCard(Machine &machine) : machine_(machine) {}
LanguageCardSwitches(Machine &machine) : machine_(machine) {}
/// Should be used by an owner to forward any access to $c08x.
/// Used by an owner to forward any access to $c08x.
void access(uint16_t address, bool is_read) {
// Quotes below taken from Understanding the Apple II, p. 5-28 and 5-29.

View File

@ -1809,7 +1809,8 @@
4BEF6AA81D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DigitalPhaseLockedLoopBridge.h; sourceTree = "<group>"; };
4BEF6AA91D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DigitalPhaseLockedLoopBridge.mm; sourceTree = "<group>"; };
4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DPLLTests.swift; sourceTree = "<group>"; };
4BF40A5525424C770033EA39 /* LanguageCard.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = LanguageCard.hpp; sourceTree = "<group>"; };
4BF40A5525424C770033EA39 /* LanguageCardSwitches.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = LanguageCardSwitches.hpp; sourceTree = "<group>"; };
4BF40A5A254263140033EA39 /* AuxiliaryMemorySwitches.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = AuxiliaryMemorySwitches.hpp; sourceTree = "<group>"; };
4BF437EC209D0F7E008CBD6B /* SegmentParser.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SegmentParser.cpp; sourceTree = "<group>"; };
4BF437ED209D0F7E008CBD6B /* SegmentParser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SegmentParser.hpp; sourceTree = "<group>"; };
4BF437F0209D112F008CBD6B /* Sector.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Sector.hpp; sourceTree = "<group>"; };
@ -3744,10 +3745,11 @@
4BCE004E227CE8CA000CA200 /* DiskIICard.cpp */,
4BCE004D227CE8CA000CA200 /* Video.cpp */,
4BCE004A227CE8CA000CA200 /* AppleII.hpp */,
4BF40A5A254263140033EA39 /* AuxiliaryMemorySwitches.hpp */,
4BCE004B227CE8CA000CA200 /* Card.hpp */,
4BCE004C227CE8CA000CA200 /* DiskIICard.hpp */,
4BF40A5525424C770033EA39 /* LanguageCardSwitches.hpp */,
4BCE004F227CE8CA000CA200 /* Video.hpp */,
4BF40A5525424C770033EA39 /* LanguageCard.hpp */,
);
path = AppleII;
sourceTree = "<group>";