mirror of
https://github.com/TomHarte/CLK.git
synced 2025-10-25 09:27:01 +00:00
Compare commits
84 Commits
2018-08-05
...
2018-09-09
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab02f82470 | ||
|
|
1e3318816c | ||
|
|
3a3dec92c7 | ||
|
|
5a5fc1ae1a | ||
|
|
8d79a1e381 | ||
|
|
d70f5da94e | ||
|
|
05d4274019 | ||
|
|
afeec09902 | ||
|
|
0526ac2ee2 | ||
|
|
6725ee2190 | ||
|
|
8b661fb90f | ||
|
|
dab7d3db1b | ||
|
|
1cba3d48d9 | ||
|
|
d53b38ec7e | ||
|
|
5d0f47eda2 | ||
|
|
2e04c4442c | ||
|
|
f639cdc8ad | ||
|
|
71ec7624ca | ||
|
|
0599d9602e | ||
|
|
234bef2a88 | ||
|
|
adb574e1cd | ||
|
|
1f491e764e | ||
|
|
114a43a662 | ||
|
|
5547c39c91 | ||
|
|
97a89aaf4d | ||
|
|
61e46399dc | ||
|
|
e802f6ecc2 | ||
|
|
4209f0e044 | ||
|
|
33576aa2c4 | ||
|
|
17bf1a64bf | ||
|
|
f8d46f8f3d | ||
|
|
8787d85e64 | ||
|
|
7f0f17f435 | ||
|
|
0e7f54f375 | ||
|
|
b3bdfa9f46 | ||
|
|
592ec69d36 | ||
|
|
60e00ddd02 | ||
|
|
6806193dc2 | ||
|
|
c35dca783f | ||
|
|
901e0d65b9 | ||
|
|
ddf45a0010 | ||
|
|
1eca4463b3 | ||
|
|
be01203cc1 | ||
|
|
4d1d19a464 | ||
|
|
760817eb3b | ||
|
|
cb47575860 | ||
|
|
434d184503 | ||
|
|
7374c665e8 | ||
|
|
10c930a59d | ||
|
|
60ab6f0c2a | ||
|
|
a13eb351da | ||
|
|
4b91910fab | ||
|
|
f46d52364c | ||
|
|
878c63dcd2 | ||
|
|
261fb3d4f8 | ||
|
|
b63e0cff72 | ||
|
|
5d6e479338 | ||
|
|
90094529a5 | ||
|
|
aed4c0539e | ||
|
|
8b50ab2593 | ||
|
|
95164b79c9 | ||
|
|
6f838fe190 | ||
|
|
bb680b40d8 | ||
|
|
e3f6da6994 | ||
|
|
e46bde35f5 | ||
|
|
32338bea4d | ||
|
|
5c881bd19d | ||
|
|
1a44ef0469 | ||
|
|
ebce9a2e51 | ||
|
|
633af4d404 | ||
|
|
76a73c835c | ||
|
|
c1d1c451ef | ||
|
|
3be30d8c71 | ||
|
|
d4c1244485 | ||
|
|
c61b9dca17 | ||
|
|
39bf682016 | ||
|
|
60ac9b49ea | ||
|
|
a8bb18e2cf | ||
|
|
1852786609 | ||
|
|
31df8c7e91 | ||
|
|
832939f5b7 | ||
|
|
bcd0479074 | ||
|
|
d72dd8c4ff | ||
|
|
c939a274be |
@@ -19,7 +19,8 @@ struct Target: public ::Analyser::Static::Target {
|
||||
enum class Model {
|
||||
II,
|
||||
IIplus,
|
||||
IIe
|
||||
IIe,
|
||||
EnhancedIIe
|
||||
};
|
||||
enum class DiskController {
|
||||
None,
|
||||
|
||||
81
ClockReceiver/ClockDeferrer.hpp
Normal file
81
ClockReceiver/ClockDeferrer.hpp
Normal file
@@ -0,0 +1,81 @@
|
||||
//
|
||||
// ClockDeferrer.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 23/08/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef ClockDeferrer_h
|
||||
#define ClockDeferrer_h
|
||||
|
||||
#include <vector>
|
||||
|
||||
/*!
|
||||
A ClockDeferrer maintains a list of ordered actions and the times at which
|
||||
they should happen, and divides a total execution period up into the portions
|
||||
that occur between those actions, triggering each action when it is reached.
|
||||
*/
|
||||
template <typename TimeUnit> class ClockDeferrer {
|
||||
public:
|
||||
/// Constructs a ClockDeferrer that will call target(period) in between deferred actions.
|
||||
ClockDeferrer(std::function<void(TimeUnit)> &&target) : target_(std::move(target)) {}
|
||||
|
||||
/*!
|
||||
Schedules @c action to occur in @c delay units of time.
|
||||
|
||||
Actions must be scheduled in the order they will occur. It is undefined behaviour
|
||||
to schedule them out of order.
|
||||
*/
|
||||
void defer(TimeUnit delay, const std::function<void(void)> &action) {
|
||||
pending_actions_.emplace_back(delay, action);
|
||||
}
|
||||
|
||||
/*!
|
||||
Runs for @c length units of time.
|
||||
|
||||
The constructor-supplied target will be called with one or more periods that add up to @c length;
|
||||
any scheduled actions will be called between periods.
|
||||
*/
|
||||
void run_for(TimeUnit length) {
|
||||
// If there are no pending actions, just run for the entire length.
|
||||
// This should be the normal branch.
|
||||
if(pending_actions_.empty()) {
|
||||
target_(length);
|
||||
return;
|
||||
}
|
||||
|
||||
// Divide the time to run according to the pending actions.
|
||||
while(length > TimeUnit(0)) {
|
||||
TimeUnit next_period = pending_actions_.empty() ? length : std::min(length, pending_actions_[0].delay);
|
||||
target_(next_period);
|
||||
length -= next_period;
|
||||
|
||||
off_t performances = 0;
|
||||
for(auto &action: pending_actions_) {
|
||||
action.delay -= next_period;
|
||||
if(!action.delay) {
|
||||
action.action();
|
||||
++performances;
|
||||
}
|
||||
}
|
||||
if(performances) {
|
||||
pending_actions_.erase(pending_actions_.begin(), pending_actions_.begin() + performances);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void(TimeUnit)> target_;
|
||||
|
||||
// The list of deferred actions.
|
||||
struct DeferredAction {
|
||||
TimeUnit delay;
|
||||
std::function<void(void)> action;
|
||||
|
||||
DeferredAction(TimeUnit delay, const std::function<void(void)> &action) : delay(delay), action(std::move(action)) {}
|
||||
};
|
||||
std::vector<DeferredAction> pending_actions_;
|
||||
};
|
||||
|
||||
#endif /* ClockDeferrer_h */
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "../../Components/AudioToggle/AudioToggle.hpp"
|
||||
|
||||
#include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp"
|
||||
#include "../../Outputs/Log.hpp"
|
||||
|
||||
#include "Card.hpp"
|
||||
#include "DiskIICard.hpp"
|
||||
@@ -34,7 +35,9 @@
|
||||
|
||||
namespace {
|
||||
|
||||
template <bool is_iie> class ConcreteMachine:
|
||||
#define is_iie() ((model == Analyser::Static::AppleII::Target::Model::IIe) || (model == Analyser::Static::AppleII::Target::Model::EnhancedIIe))
|
||||
|
||||
template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
public CRTMachine::Machine,
|
||||
public MediaTarget::Machine,
|
||||
public KeyboardMachine::Machine,
|
||||
@@ -49,20 +52,18 @@ template <bool is_iie> class ConcreteMachine:
|
||||
public:
|
||||
VideoBusHandler(uint8_t *ram, uint8_t *aux_ram) : ram_(ram), aux_ram_(aux_ram) {}
|
||||
|
||||
uint8_t perform_read(uint16_t address) {
|
||||
return ram_[address];
|
||||
}
|
||||
uint16_t perform_aux_read(uint16_t address) {
|
||||
return static_cast<uint16_t>(ram_[address] | (aux_ram_[address] << 8));
|
||||
void perform_read(uint16_t address, size_t count, uint8_t *base_target, uint8_t *auxiliary_target) {
|
||||
memcpy(base_target, &ram_[address], count);
|
||||
memcpy(auxiliary_target, &aux_ram_[address], count);
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t *ram_, *aux_ram_;
|
||||
};
|
||||
|
||||
CPU::MOS6502::Processor<ConcreteMachine, false> m6502_;
|
||||
CPU::MOS6502::Processor<(model == Analyser::Static::AppleII::Target::Model::EnhancedIIe) ? CPU::MOS6502::Personality::PSynertek65C02 : CPU::MOS6502::Personality::P6502, ConcreteMachine, false> m6502_;
|
||||
VideoBusHandler video_bus_handler_;
|
||||
std::unique_ptr<AppleII::Video::Video<VideoBusHandler, is_iie>> video_;
|
||||
std::unique_ptr<AppleII::Video::Video<VideoBusHandler, is_iie()>> video_;
|
||||
int cycles_into_current_line_ = 0;
|
||||
Cycles cycles_since_video_update_;
|
||||
|
||||
@@ -87,6 +88,14 @@ template <bool is_iie> class ConcreteMachine:
|
||||
uint8_t keyboard_input_ = 0x00;
|
||||
bool key_is_down_ = false;
|
||||
|
||||
uint8_t get_keyboard_input() {
|
||||
if(string_serialiser_) {
|
||||
return string_serialiser_->head() | 0x80;
|
||||
} else {
|
||||
return keyboard_input_;
|
||||
}
|
||||
}
|
||||
|
||||
Concurrency::DeferringAsyncTaskQueue audio_queue_;
|
||||
Audio::Toggle audio_toggle_;
|
||||
Outputs::Speaker::LowpassSpeaker<Audio::Toggle> speaker_;
|
||||
@@ -179,7 +188,7 @@ template <bool is_iie> class ConcreteMachine:
|
||||
bool has_language_card_ = true;
|
||||
void set_language_card_paging() {
|
||||
uint8_t *const ram = alternative_zero_page_ ? aux_ram_ : ram_;
|
||||
uint8_t *const rom = is_iie ? &rom_[3840] : rom_.data();
|
||||
uint8_t *const rom = is_iie() ? &rom_[3840] : rom_.data();
|
||||
|
||||
page(0xd0, 0xe0,
|
||||
language_card_.read ? &ram[language_card_.bank1 ? 0xd000 : 0xc000] : rom,
|
||||
@@ -298,7 +307,7 @@ template <bool is_iie> class ConcreteMachine:
|
||||
|
||||
public:
|
||||
ConcreteMachine(const Analyser::Static::AppleII::Target &target, const ROMMachine::ROMFetcher &rom_fetcher):
|
||||
m6502_(*this),
|
||||
m6502_(*this),
|
||||
video_bus_handler_(ram_, aux_ram_),
|
||||
audio_toggle_(audio_queue_),
|
||||
speaker_(audio_toggle_) {
|
||||
@@ -345,6 +354,11 @@ template <bool is_iie> class ConcreteMachine:
|
||||
rom_names.push_back("apple2eu-character.rom");
|
||||
rom_names.push_back("apple2eu.rom");
|
||||
break;
|
||||
case Target::Model::EnhancedIIe:
|
||||
rom_size += 3840;
|
||||
rom_names.push_back("apple2e-character.rom");
|
||||
rom_names.push_back("apple2e.rom");
|
||||
break;
|
||||
}
|
||||
const auto roms = rom_fetcher("AppleII", rom_names);
|
||||
|
||||
@@ -383,7 +397,7 @@ template <bool is_iie> class ConcreteMachine:
|
||||
}
|
||||
|
||||
void setup_output(float aspect_ratio) override {
|
||||
video_.reset(new AppleII::Video::Video<VideoBusHandler, is_iie>(video_bus_handler_));
|
||||
video_.reset(new AppleII::Video::Video<VideoBusHandler, is_iie()>(video_bus_handler_));
|
||||
video_->set_character_rom(character_rom_);
|
||||
}
|
||||
|
||||
@@ -420,9 +434,12 @@ template <bool is_iie> class ConcreteMachine:
|
||||
bool has_updated_cards = false;
|
||||
if(read_pages_[address >> 8]) {
|
||||
if(isReadOperation(operation)) *value = read_pages_[address >> 8][address & 0xff];
|
||||
else if(write_pages_[address >> 8]) write_pages_[address >> 8][address & 0xff] = *value;
|
||||
else {
|
||||
if(address >= 0x200 && address < 0x6000) update_video();
|
||||
if(write_pages_[address >> 8]) write_pages_[address >> 8][address & 0xff] = *value;
|
||||
}
|
||||
|
||||
if(is_iie && address >= 0xc300 && address < 0xd000) {
|
||||
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);
|
||||
@@ -457,18 +474,18 @@ template <bool is_iie> class ConcreteMachine:
|
||||
default: break;
|
||||
|
||||
case 0xc000:
|
||||
if(string_serialiser_) {
|
||||
*value = string_serialiser_->head() | 0x80;
|
||||
} else {
|
||||
*value = keyboard_input_;
|
||||
}
|
||||
*value = get_keyboard_input();
|
||||
break;
|
||||
case 0xc001: case 0xc002: case 0xc003: case 0xc004: case 0xc005: case 0xc006: case 0xc007:
|
||||
case 0xc008: case 0xc009: case 0xc00a: case 0xc00b: case 0xc00c: case 0xc00d: case 0xc00e: case 0xc00f:
|
||||
*value = (*value & 0x80) | (get_keyboard_input() & 0x7f);
|
||||
break;
|
||||
|
||||
case 0xc061: // Switch input 0.
|
||||
*value &= 0x7f;
|
||||
if(
|
||||
static_cast<Joystick *>(joysticks_[0].get())->buttons[0] || static_cast<Joystick *>(joysticks_[1].get())->buttons[2] ||
|
||||
(is_iie && open_apple_is_pressed_)
|
||||
(is_iie() && open_apple_is_pressed_)
|
||||
)
|
||||
*value |= 0x80;
|
||||
break;
|
||||
@@ -476,7 +493,7 @@ template <bool is_iie> class ConcreteMachine:
|
||||
*value &= 0x7f;
|
||||
if(
|
||||
static_cast<Joystick *>(joysticks_[0].get())->buttons[1] || static_cast<Joystick *>(joysticks_[1].get())->buttons[1] ||
|
||||
(is_iie && closed_apple_is_pressed_)
|
||||
(is_iie() && closed_apple_is_pressed_)
|
||||
)
|
||||
*value |= 0x80;
|
||||
break;
|
||||
@@ -498,28 +515,33 @@ template <bool is_iie> class ConcreteMachine:
|
||||
} break;
|
||||
|
||||
// The IIe-only state reads follow...
|
||||
case 0xc011: if(is_iie) *value = (*value & 0x7f) | (language_card_.bank1 ? 0x80 : 0x00); break;
|
||||
case 0xc012: if(is_iie) *value = (*value & 0x7f) | (language_card_.read ? 0x80 : 0x00); break;
|
||||
case 0xc013: if(is_iie) *value = (*value & 0x7f) | (read_auxiliary_memory_ ? 0x80 : 0x00); break;
|
||||
case 0xc014: if(is_iie) *value = (*value & 0x7f) | (write_auxiliary_memory_ ? 0x80 : 0x00); break;
|
||||
case 0xc015: if(is_iie) *value = (*value & 0x7f) | (internal_CX_rom_ ? 0x80 : 0x00); break;
|
||||
case 0xc016: if(is_iie) *value = (*value & 0x7f) | (alternative_zero_page_ ? 0x80 : 0x00); break;
|
||||
case 0xc017: if(is_iie) *value = (*value & 0x7f) | (slot_C3_rom_ ? 0x80 : 0x00); break;
|
||||
case 0xc018: if(is_iie) *value = (*value & 0x7f) | (video_->get_80_store() ? 0x80 : 0x00); break;
|
||||
case 0xc019: if(is_iie) *value = (*value & 0x7f) | (video_->get_is_vertical_blank(cycles_since_video_update_) ? 0x00 : 0x80); break;
|
||||
case 0xc01a: if(is_iie) *value = (*value & 0x7f) | (video_->get_text() ? 0x80 : 0x00); break;
|
||||
case 0xc01b: if(is_iie) *value = (*value & 0x7f) | (video_->get_mixed() ? 0x80 : 0x00); break;
|
||||
case 0xc01c: if(is_iie) *value = (*value & 0x7f) | (video_->get_page2() ? 0x80 : 0x00); break;
|
||||
case 0xc01d: if(is_iie) *value = (*value & 0x7f) | (video_->get_high_resolution() ? 0x80 : 0x00); break;
|
||||
case 0xc01e: if(is_iie) *value = (*value & 0x7f) | (video_->get_alternative_character_set() ? 0x80 : 0x00); break;
|
||||
case 0xc01f: if(is_iie) *value = (*value & 0x7f) | (video_->get_80_columns() ? 0x80 : 0x00); break;
|
||||
case 0xc07f: if(is_iie) *value = (*value & 0x7f) | (video_->get_double_high_resolution() ? 0x80 : 0x00); break;
|
||||
#define IIeSwitchRead(s) *value = get_keyboard_input(); if(is_iie()) *value = (*value & 0x7f) | (s ? 0x80 : 0x00);
|
||||
case 0xc011: IIeSwitchRead(language_card_.bank1); break;
|
||||
case 0xc012: IIeSwitchRead(language_card_.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 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;
|
||||
case 0xc01b: IIeSwitchRead(video_->get_mixed()); break;
|
||||
case 0xc01c: IIeSwitchRead(video_->get_page2()); break;
|
||||
case 0xc01d: IIeSwitchRead(video_->get_high_resolution()); break;
|
||||
case 0xc01e: IIeSwitchRead(video_->get_alternative_character_set()); break;
|
||||
case 0xc01f: IIeSwitchRead(video_->get_80_columns()); break;
|
||||
#undef IIeSwitchRead
|
||||
|
||||
case 0xc07f:
|
||||
if(is_iie()) *value = (*value & 0x7f) | (video_->get_annunciator_3() ? 0x80 : 0x00);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Write-only switches. All IIe as currently implemented.
|
||||
if(is_iie) {
|
||||
if(is_iie()) {
|
||||
switch(address) {
|
||||
default: printf("Write %04x?\n", address); break;
|
||||
default: break;
|
||||
|
||||
case 0xc000:
|
||||
case 0xc001:
|
||||
@@ -589,7 +611,7 @@ template <bool is_iie> class ConcreteMachine:
|
||||
analogue_charge_ = 0.0f;
|
||||
} break;
|
||||
|
||||
/* Read-write switches. */
|
||||
/* Switches triggered by reading or writing. */
|
||||
case 0xc050:
|
||||
case 0xc051:
|
||||
update_video();
|
||||
@@ -612,9 +634,9 @@ template <bool is_iie> class ConcreteMachine:
|
||||
|
||||
case 0xc05e:
|
||||
case 0xc05f:
|
||||
if(is_iie) {
|
||||
if(is_iie()) {
|
||||
update_video();
|
||||
video_->set_double_high_resolution(!(address&1));
|
||||
video_->set_annunciator_3(!(address&1));
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -626,12 +648,13 @@ template <bool is_iie> class ConcreteMachine:
|
||||
}
|
||||
|
||||
// On the IIe, reading C010 returns additional key info.
|
||||
if(is_iie && isReadOperation(operation)) {
|
||||
if(is_iie() && isReadOperation(operation)) {
|
||||
*value = (key_is_down_ ? 0x80 : 0x00) | (keyboard_input_ & 0x7f);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xc030:
|
||||
case 0xc030: case 0xc031: case 0xc032: case 0xc033: case 0xc034: case 0xc035: case 0xc036: case 0xc037:
|
||||
case 0xc038: case 0xc039: case 0xc03a: case 0xc03b: case 0xc03c: case 0xc03d: case 0xc03e: case 0xc03f:
|
||||
update_audio();
|
||||
audio_toggle_.set_output(!audio_toggle_.get_output());
|
||||
break;
|
||||
@@ -769,7 +792,7 @@ template <bool is_iie> class ConcreteMachine:
|
||||
}
|
||||
|
||||
// Prior to the IIe, the keyboard could produce uppercase only.
|
||||
if(!is_iie) value = static_cast<char>(toupper(value));
|
||||
if(!is_iie()) value = static_cast<char>(toupper(value));
|
||||
|
||||
if(is_pressed) {
|
||||
keyboard_input_ = static_cast<uint8_t>(value | 0x80);
|
||||
@@ -818,10 +841,12 @@ using namespace AppleII;
|
||||
Machine *Machine::AppleII(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
|
||||
using Target = Analyser::Static::AppleII::Target;
|
||||
const Target *const appleii_target = dynamic_cast<const Target *>(target);
|
||||
if(appleii_target->model == Target::Model::IIe) {
|
||||
return new ConcreteMachine<true>(*appleii_target, rom_fetcher);
|
||||
} else {
|
||||
return new ConcreteMachine<false>(*appleii_target, rom_fetcher);
|
||||
switch(appleii_target->model) {
|
||||
default: return nullptr;
|
||||
case Target::Model::II: return new ConcreteMachine<Target::Model::II>(*appleii_target, rom_fetcher);
|
||||
case Target::Model::IIplus: return new ConcreteMachine<Target::Model::IIplus>(*appleii_target, rom_fetcher);
|
||||
case Target::Model::IIe: return new ConcreteMachine<Target::Model::IIe>(*appleii_target, rom_fetcher);
|
||||
case Target::Model::EnhancedIIe: return new ConcreteMachine<Target::Model::EnhancedIIe>(*appleii_target, rom_fetcher);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,10 @@
|
||||
|
||||
using namespace AppleII::Video;
|
||||
|
||||
VideoBase::VideoBase() :
|
||||
crt_(new Outputs::CRT::CRT(910, 1, Outputs::CRT::DisplayType::NTSC60, 1)) {
|
||||
VideoBase::VideoBase(bool is_iie, std::function<void(Cycles)> &&target) :
|
||||
crt_(new Outputs::CRT::CRT(910, 1, Outputs::CRT::DisplayType::NTSC60, 1)),
|
||||
is_iie_(is_iie),
|
||||
deferrer_(std::move(target)) {
|
||||
|
||||
// Set a composite sampling function that assumes one byte per pixel input, and
|
||||
// accepts any non-zero value as being fully on, zero being fully off.
|
||||
@@ -23,8 +25,25 @@ VideoBase::VideoBase() :
|
||||
|
||||
// Show only the centre 75% of the TV frame.
|
||||
crt_->set_video_signal(Outputs::CRT::VideoSignal::Composite);
|
||||
crt_->set_visible_area(Outputs::CRT::Rect(0.115f, 0.122f, 0.77f, 0.77f));
|
||||
crt_->set_visible_area(Outputs::CRT::Rect(0.118f, 0.122f, 0.77f, 0.77f));
|
||||
crt_->set_immediate_default_phase(0.0f);
|
||||
|
||||
character_zones[0].xor_mask = 0;
|
||||
character_zones[0].address_mask = 0x3f;
|
||||
character_zones[1].xor_mask = 0;
|
||||
character_zones[1].address_mask = 0x3f;
|
||||
character_zones[2].xor_mask = 0;
|
||||
character_zones[2].address_mask = 0x3f;
|
||||
character_zones[3].xor_mask = 0;
|
||||
character_zones[3].address_mask = 0x3f;
|
||||
|
||||
if(is_iie) {
|
||||
character_zones[0].xor_mask =
|
||||
character_zones[2].xor_mask =
|
||||
character_zones[3].xor_mask = 0xff;
|
||||
character_zones[2].address_mask =
|
||||
character_zones[3].address_mask = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
Outputs::CRT::CRT *VideoBase::get_crt() {
|
||||
@@ -35,67 +54,93 @@ Outputs::CRT::CRT *VideoBase::get_crt() {
|
||||
Rote setters and getters.
|
||||
*/
|
||||
void VideoBase::set_alternative_character_set(bool alternative_character_set) {
|
||||
alternative_character_set_ = alternative_character_set;
|
||||
set_alternative_character_set_ = alternative_character_set;
|
||||
deferrer_.defer(Cycles(2), [=] {
|
||||
alternative_character_set_ = alternative_character_set;
|
||||
if(alternative_character_set) {
|
||||
character_zones[1].address_mask = 0xff;
|
||||
character_zones[1].xor_mask = 0;
|
||||
} else {
|
||||
character_zones[1].address_mask = 0x3f;
|
||||
character_zones[1].xor_mask = flash_mask();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool VideoBase::get_alternative_character_set() {
|
||||
return alternative_character_set_;
|
||||
return set_alternative_character_set_;
|
||||
}
|
||||
|
||||
void VideoBase::set_80_columns(bool columns_80) {
|
||||
columns_80_ = columns_80;
|
||||
set_columns_80_ = columns_80;
|
||||
deferrer_.defer(Cycles(2), [=] {
|
||||
columns_80_ = columns_80;
|
||||
});
|
||||
}
|
||||
|
||||
bool VideoBase::get_80_columns() {
|
||||
return columns_80_;
|
||||
return set_columns_80_;
|
||||
}
|
||||
|
||||
void VideoBase::set_80_store(bool store_80) {
|
||||
store_80_ = store_80;
|
||||
set_store_80_ = store_80_ = store_80;
|
||||
}
|
||||
|
||||
bool VideoBase::get_80_store() {
|
||||
return store_80_;
|
||||
return set_store_80_;
|
||||
}
|
||||
|
||||
void VideoBase::set_page2(bool page2) {
|
||||
page2_ = page2;
|
||||
set_page2_ = page2_ = page2;
|
||||
}
|
||||
|
||||
bool VideoBase::get_page2() {
|
||||
return page2_;
|
||||
return set_page2_;
|
||||
}
|
||||
|
||||
void VideoBase::set_text(bool text) {
|
||||
text_ = text;
|
||||
set_text_ = text;
|
||||
deferrer_.defer(Cycles(2), [=] {
|
||||
text_ = text;
|
||||
});
|
||||
}
|
||||
|
||||
bool VideoBase::get_text() {
|
||||
return text_;
|
||||
return set_text_;
|
||||
}
|
||||
|
||||
void VideoBase::set_mixed(bool mixed) {
|
||||
mixed_ = mixed;
|
||||
set_mixed_ = mixed;
|
||||
deferrer_.defer(Cycles(2), [=] {
|
||||
mixed_ = mixed;
|
||||
});
|
||||
}
|
||||
|
||||
bool VideoBase::get_mixed() {
|
||||
return mixed_;
|
||||
return set_mixed_;
|
||||
}
|
||||
|
||||
void VideoBase::set_high_resolution(bool high_resolution) {
|
||||
high_resolution_ = high_resolution;
|
||||
set_high_resolution_ = high_resolution;
|
||||
deferrer_.defer(Cycles(2), [=] {
|
||||
high_resolution_ = high_resolution;
|
||||
});
|
||||
}
|
||||
|
||||
bool VideoBase::get_high_resolution() {
|
||||
return high_resolution_;
|
||||
return set_high_resolution_;
|
||||
}
|
||||
|
||||
void VideoBase::set_double_high_resolution(bool double_high_resolution) {
|
||||
double_high_resolution_ = double_high_resolution;
|
||||
void VideoBase::set_annunciator_3(bool annunciator_3) {
|
||||
set_annunciator_3_ = annunciator_3;
|
||||
deferrer_.defer(Cycles(2), [=] {
|
||||
annunciator_3_ = annunciator_3;
|
||||
high_resolution_mask_ = annunciator_3_ ? 0x7f : 0xff;
|
||||
});
|
||||
}
|
||||
|
||||
bool VideoBase::get_double_high_resolution() {
|
||||
return double_high_resolution_;
|
||||
bool VideoBase::get_annunciator_3() {
|
||||
return set_annunciator_3_;
|
||||
}
|
||||
|
||||
void VideoBase::set_character_rom(const std::vector<uint8_t> &character_rom) {
|
||||
@@ -115,3 +160,180 @@ void VideoBase::set_character_rom(const std::vector<uint8_t> &character_rom) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VideoBase::output_text(uint8_t *target, const uint8_t *const source, size_t length, size_t pixel_row) const {
|
||||
for(size_t c = 0; c < length; ++c) {
|
||||
const int character = source[c] & character_zones[source[c] >> 6].address_mask;
|
||||
const uint8_t xor_mask = character_zones[source[c] >> 6].xor_mask;
|
||||
const std::size_t character_address = static_cast<std::size_t>(character << 3) + pixel_row;
|
||||
const uint8_t character_pattern = character_rom_[character_address] ^ xor_mask;
|
||||
|
||||
// The character ROM is output MSB to LSB rather than LSB to MSB.
|
||||
target[0] = target[1] = character_pattern & 0x40;
|
||||
target[2] = target[3] = character_pattern & 0x20;
|
||||
target[4] = target[5] = character_pattern & 0x10;
|
||||
target[6] = target[7] = character_pattern & 0x08;
|
||||
target[8] = target[9] = character_pattern & 0x04;
|
||||
target[10] = target[11] = character_pattern & 0x02;
|
||||
target[12] = target[13] = character_pattern & 0x01;
|
||||
graphics_carry_ = character_pattern & 0x01;
|
||||
target += 14;
|
||||
}
|
||||
}
|
||||
|
||||
void VideoBase::output_double_text(uint8_t *target, const uint8_t *const source, const uint8_t *const auxiliary_source, size_t length, size_t pixel_row) const {
|
||||
for(size_t c = 0; c < length; ++c) {
|
||||
const std::size_t character_addresses[2] = {
|
||||
static_cast<std::size_t>(
|
||||
(auxiliary_source[c] & character_zones[auxiliary_source[c] >> 6].address_mask) << 3
|
||||
) + pixel_row,
|
||||
static_cast<std::size_t>(
|
||||
(source[c] & character_zones[source[c] >> 6].address_mask) << 3
|
||||
) + pixel_row
|
||||
};
|
||||
|
||||
const uint8_t character_patterns[2] = {
|
||||
static_cast<uint8_t>(
|
||||
character_rom_[character_addresses[0]] ^ character_zones[auxiliary_source[c] >> 6].xor_mask
|
||||
),
|
||||
static_cast<uint8_t>(
|
||||
character_rom_[character_addresses[1]] ^ character_zones[source[c] >> 6].xor_mask
|
||||
)
|
||||
};
|
||||
|
||||
// The character ROM is output MSB to LSB rather than LSB to MSB.
|
||||
target[0] = character_patterns[0] & 0x40;
|
||||
target[1] = character_patterns[0] & 0x20;
|
||||
target[2] = character_patterns[0] & 0x10;
|
||||
target[3] = character_patterns[0] & 0x08;
|
||||
target[4] = character_patterns[0] & 0x04;
|
||||
target[5] = character_patterns[0] & 0x02;
|
||||
target[6] = character_patterns[0] & 0x01;
|
||||
target[7] = character_patterns[1] & 0x40;
|
||||
target[8] = character_patterns[1] & 0x20;
|
||||
target[9] = character_patterns[1] & 0x10;
|
||||
target[10] = character_patterns[1] & 0x08;
|
||||
target[11] = character_patterns[1] & 0x04;
|
||||
target[12] = character_patterns[1] & 0x02;
|
||||
target[13] = character_patterns[1] & 0x01;
|
||||
graphics_carry_ = character_patterns[1] & 0x01;
|
||||
target += 14;
|
||||
}
|
||||
}
|
||||
|
||||
void VideoBase::output_low_resolution(uint8_t *target, const uint8_t *const source, size_t length, int column, int row) const {
|
||||
const int row_shift = row&4;
|
||||
for(size_t c = 0; c < length; ++c) {
|
||||
// Low-resolution graphics mode shifts the colour code on a loop, but has to account for whether this
|
||||
// 14-sample output window is starting at the beginning of a colour cycle or halfway through.
|
||||
if((column + static_cast<int>(c))&1) {
|
||||
target[0] = target[4] = target[8] = target[12] = (source[c] >> row_shift) & 4;
|
||||
target[1] = target[5] = target[9] = target[13] = (source[c] >> row_shift) & 8;
|
||||
target[2] = target[6] = target[10] = (source[c] >> row_shift) & 1;
|
||||
target[3] = target[7] = target[11] = (source[c] >> row_shift) & 2;
|
||||
graphics_carry_ = (source[c] >> row_shift) & 8;
|
||||
} else {
|
||||
target[0] = target[4] = target[8] = target[12] = (source[c] >> row_shift) & 1;
|
||||
target[1] = target[5] = target[9] = target[13] = (source[c] >> row_shift) & 2;
|
||||
target[2] = target[6] = target[10] = (source[c] >> row_shift) & 4;
|
||||
target[3] = target[7] = target[11] = (source[c] >> row_shift) & 8;
|
||||
graphics_carry_ = (source[c] >> row_shift) & 2;
|
||||
}
|
||||
target += 14;
|
||||
}
|
||||
}
|
||||
|
||||
void VideoBase::output_fat_low_resolution(uint8_t *target, const uint8_t *const source, size_t length, int column, int row) const {
|
||||
const int row_shift = row&4;
|
||||
for(size_t c = 0; c < length; ++c) {
|
||||
// Fat low-resolution mode appears not to do anything to try to make odd and
|
||||
// even columns compatible.
|
||||
target[0] = target[1] = target[8] = target[9] = (source[c] >> row_shift) & 1;
|
||||
target[2] = target[3] = target[10] = target[11] = (source[c] >> row_shift) & 2;
|
||||
target[4] = target[5] = target[12] = target[13] = (source[c] >> row_shift) & 4;
|
||||
target[6] = target[7] = (source[c] >> row_shift) & 8;
|
||||
graphics_carry_ = (source[c] >> row_shift) & 4;
|
||||
target += 14;
|
||||
}
|
||||
}
|
||||
|
||||
void VideoBase::output_double_low_resolution(uint8_t *target, const uint8_t *const source, const uint8_t *const auxiliary_source, size_t length, int column, int row) const {
|
||||
const int row_shift = row&4;
|
||||
for(size_t c = 0; c < length; ++c) {
|
||||
if((column + static_cast<int>(c))&1) {
|
||||
target[0] = target[4] = (auxiliary_source[c] >> row_shift) & 2;
|
||||
target[1] = target[5] = (auxiliary_source[c] >> row_shift) & 4;
|
||||
target[2] = target[6] = (auxiliary_source[c] >> row_shift) & 8;
|
||||
target[3] = (auxiliary_source[c] >> row_shift) & 1;
|
||||
|
||||
target[8] = target[12] = (source[c] >> row_shift) & 4;
|
||||
target[9] = target[13] = (source[c] >> row_shift) & 8;
|
||||
target[10] = (source[c] >> row_shift) & 1;
|
||||
target[7] = target[11] = (source[c] >> row_shift) & 2;
|
||||
graphics_carry_ = (source[c] >> row_shift) & 8;
|
||||
} else {
|
||||
target[0] = target[4] = (auxiliary_source[c] >> row_shift) & 8;
|
||||
target[1] = target[5] = (auxiliary_source[c] >> row_shift) & 1;
|
||||
target[2] = target[6] = (auxiliary_source[c] >> row_shift) & 2;
|
||||
target[3] = (auxiliary_source[c] >> row_shift) & 4;
|
||||
|
||||
target[8] = target[12] = (source[c] >> row_shift) & 1;
|
||||
target[9] = target[13] = (source[c] >> row_shift) & 2;
|
||||
target[10] = (source[c] >> row_shift) & 4;
|
||||
target[7] = target[11] = (source[c] >> row_shift) & 8;
|
||||
graphics_carry_ = (source[c] >> row_shift) & 2;
|
||||
}
|
||||
target += 14;
|
||||
}
|
||||
}
|
||||
|
||||
void VideoBase::output_high_resolution(uint8_t *target, const uint8_t *const source, size_t length) const {
|
||||
for(size_t c = 0; c < length; ++c) {
|
||||
// High resolution graphics shift out LSB to MSB, optionally with a delay of half a pixel.
|
||||
// If there is a delay, the previous output level is held to bridge the gap.
|
||||
// Delays may be ignored on a IIe if Annunciator 3 is set; that's the state that
|
||||
// high_resolution_mask_ models.
|
||||
if(source[c] & high_resolution_mask_ & 0x80) {
|
||||
target[0] = graphics_carry_;
|
||||
target[1] = target[2] = source[c] & 0x01;
|
||||
target[3] = target[4] = source[c] & 0x02;
|
||||
target[5] = target[6] = source[c] & 0x04;
|
||||
target[7] = target[8] = source[c] & 0x08;
|
||||
target[9] = target[10] = source[c] & 0x10;
|
||||
target[11] = target[12] = source[c] & 0x20;
|
||||
target[13] = source[c] & 0x40;
|
||||
} else {
|
||||
target[0] = target[1] = source[c] & 0x01;
|
||||
target[2] = target[3] = source[c] & 0x02;
|
||||
target[4] = target[5] = source[c] & 0x04;
|
||||
target[6] = target[7] = source[c] & 0x08;
|
||||
target[8] = target[9] = source[c] & 0x10;
|
||||
target[10] = target[11] = source[c] & 0x20;
|
||||
target[12] = target[13] = source[c] & 0x40;
|
||||
}
|
||||
graphics_carry_ = source[c] & 0x40;
|
||||
target += 14;
|
||||
}
|
||||
}
|
||||
|
||||
void VideoBase::output_double_high_resolution(uint8_t *target, const uint8_t *const source, const uint8_t *const auxiliary_source, size_t length) const {
|
||||
for(size_t c = 0; c < length; ++c) {
|
||||
target[0] = auxiliary_source[c] & 0x01;
|
||||
target[1] = auxiliary_source[c] & 0x02;
|
||||
target[2] = auxiliary_source[c] & 0x04;
|
||||
target[3] = auxiliary_source[c] & 0x08;
|
||||
target[4] = auxiliary_source[c] & 0x10;
|
||||
target[5] = auxiliary_source[c] & 0x20;
|
||||
target[6] = auxiliary_source[c] & 0x40;
|
||||
target[7] = source[c] & 0x01;
|
||||
target[8] = source[c] & 0x02;
|
||||
target[9] = source[c] & 0x04;
|
||||
target[10] = source[c] & 0x08;
|
||||
target[11] = source[c] & 0x10;
|
||||
target[12] = source[c] & 0x20;
|
||||
target[13] = source[c] & 0x40;
|
||||
|
||||
graphics_carry_ = auxiliary_source[c] & 0x40;
|
||||
target += 14;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
|
||||
#include "../../Outputs/CRT/CRT.hpp"
|
||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||
#include "../../ClockReceiver/ClockDeferrer.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
namespace AppleII {
|
||||
@@ -20,25 +22,19 @@ namespace Video {
|
||||
class BusHandler {
|
||||
public:
|
||||
/*!
|
||||
Reads an 8-bit value from the ordinary II/II+ memory pool.
|
||||
*/
|
||||
uint8_t perform_read(uint16_t address) {
|
||||
return 0xff;
|
||||
}
|
||||
Requests fetching of the @c count bytes starting from @c address.
|
||||
|
||||
/*!
|
||||
Reads two 8-bit values, from the same address — one from
|
||||
main RAM, one from auxiliary. Should return as
|
||||
(main) | (aux << 8).
|
||||
The handler should write the values from base memory to @c base_target, and those
|
||||
from auxiliary memory to @c auxiliary_target. If the machine has no axiliary memory,
|
||||
it needn't write anything to auxiliary_target.
|
||||
*/
|
||||
uint16_t perform_aux_read(uint16_t address) {
|
||||
return 0xffff;
|
||||
void perform_read(uint16_t address, size_t count, uint8_t *base_target, uint8_t *auxiliary_target) {
|
||||
}
|
||||
};
|
||||
|
||||
class VideoBase {
|
||||
public:
|
||||
VideoBase();
|
||||
VideoBase(bool is_iie, std::function<void(Cycles)> &&target);
|
||||
|
||||
/// @returns The CRT this video feed is feeding.
|
||||
Outputs::CRT::CRT *get_crt();
|
||||
@@ -132,18 +128,19 @@ class VideoBase {
|
||||
bool get_high_resolution();
|
||||
|
||||
/*!
|
||||
Setter for DHIRES ($C05E/$C05F; triggers on write only).
|
||||
Setter for annunciator 3.
|
||||
|
||||
* On: turn on double-high resolution.
|
||||
* Off: turn off double-high resolution.
|
||||
* On: turn on annunciator 3.
|
||||
* Off: turn off annunciator 3.
|
||||
|
||||
DHIRES doesn't exist on a II/II+. On the IIe there is another
|
||||
register usually grouped with the graphics setters called IOUDIS
|
||||
that affects visibility of this switch. But it has no effect on
|
||||
video, so it's not modelled by this class.
|
||||
This exists on both the II/II+ and the IIe, but has no effect on
|
||||
video on the older machines. It's intended to be used on the IIe
|
||||
to confirm double-high resolution mode but has side effects in
|
||||
selecting mixed mode output and discarding high-resolution
|
||||
delay bits.
|
||||
*/
|
||||
void set_double_high_resolution(bool);
|
||||
bool get_double_high_resolution();
|
||||
void set_annunciator_3(bool);
|
||||
bool get_annunciator_3();
|
||||
|
||||
// Setup for text mode.
|
||||
void set_character_rom(const std::vector<uint8_t> &);
|
||||
@@ -153,353 +150,119 @@ class VideoBase {
|
||||
|
||||
// State affecting output video stream generation.
|
||||
uint8_t *pixel_pointer_ = nullptr;
|
||||
int pixel_pointer_column_ = 0;
|
||||
bool pixels_are_high_density_ = false;
|
||||
|
||||
// State affecting logical state.
|
||||
int row_ = 0, column_ = 0, flash_ = 0;
|
||||
uint8_t flash_mask() {
|
||||
return static_cast<uint8_t>((flash_ / flash_length) * 0xff);
|
||||
}
|
||||
|
||||
// Enumerates all Apple II and IIe display modes.
|
||||
enum class GraphicsMode {
|
||||
LowRes,
|
||||
DoubleLowRes,
|
||||
Text = 0,
|
||||
DoubleText,
|
||||
HighRes,
|
||||
DoubleHighRes,
|
||||
Text,
|
||||
DoubleText
|
||||
LowRes,
|
||||
DoubleLowRes,
|
||||
FatLowRes
|
||||
};
|
||||
bool is_text_mode(GraphicsMode m) { return m >= GraphicsMode::Text; }
|
||||
bool is_text_mode(GraphicsMode m) { return m <= GraphicsMode::DoubleText; }
|
||||
bool is_double_mode(GraphicsMode m) { return !!(static_cast<int>(m)&1); }
|
||||
|
||||
// Various soft-switch values.
|
||||
bool alternative_character_set_ = false;
|
||||
bool columns_80_ = false;
|
||||
bool store_80_ = false;
|
||||
bool page2_ = false;
|
||||
bool text_ = true;
|
||||
bool mixed_ = false;
|
||||
bool high_resolution_ = false;
|
||||
bool double_high_resolution_ = false;
|
||||
bool alternative_character_set_ = false, set_alternative_character_set_ = false;
|
||||
bool columns_80_ = false, set_columns_80_ = false;
|
||||
bool store_80_ = false, set_store_80_ = false;
|
||||
bool page2_ = false, set_page2_ = false;
|
||||
bool text_ = true, set_text_ = true;
|
||||
bool mixed_ = false, set_mixed_ = false;
|
||||
bool high_resolution_ = false, set_high_resolution_ = false;
|
||||
bool annunciator_3_ = false, set_annunciator_3_ = false;
|
||||
|
||||
// Graphics carry is the final level output in a fetch window;
|
||||
// it carries on into the next if it's high resolution with
|
||||
// the delay bit set.
|
||||
uint8_t graphics_carry_ = 0;
|
||||
mutable uint8_t graphics_carry_ = 0;
|
||||
bool was_double_ = false;
|
||||
uint8_t high_resolution_mask_ = 0xff;
|
||||
|
||||
// This holds a copy of the character ROM. The regular character
|
||||
// set is assumed to be in the first 64*8 bytes; the alternative
|
||||
// is in the 128*8 bytes after that.
|
||||
std::vector<uint8_t> character_rom_;
|
||||
|
||||
// Memory is fetched ahead of time into this array;
|
||||
// this permits the correct delay between fetching
|
||||
// without having to worry about a rolling buffer.
|
||||
std::array<uint8_t, 40> base_stream_;
|
||||
std::array<uint8_t, 40> auxiliary_stream_;
|
||||
|
||||
bool is_iie_ = false;
|
||||
static const int flash_length = 8406;
|
||||
|
||||
// Describes the current text mode mapping from in-memory character index
|
||||
// to output character.
|
||||
struct CharacterMapping {
|
||||
uint8_t address_mask;
|
||||
uint8_t xor_mask;
|
||||
};
|
||||
CharacterMapping character_zones[4];
|
||||
|
||||
/*!
|
||||
Outputs 40-column text to @c target, using @c length bytes from @c source.
|
||||
*/
|
||||
void output_text(uint8_t *target, const uint8_t *source, size_t length, size_t pixel_row) const;
|
||||
|
||||
/*!
|
||||
Outputs 80-column text to @c target, drawing @c length columns from @c source and @c auxiliary_source.
|
||||
*/
|
||||
void output_double_text(uint8_t *target, const uint8_t *source, const uint8_t *auxiliary_source, size_t length, size_t pixel_row) const;
|
||||
|
||||
/*!
|
||||
Outputs 40-column low-resolution graphics to @c target, drawing @c length columns from @c source.
|
||||
*/
|
||||
void output_low_resolution(uint8_t *target, const uint8_t *source, size_t length, int column, int row) const;
|
||||
|
||||
/*!
|
||||
Outputs 80-column low-resolution graphics to @c target, drawing @c length columns from @c source and @c auxiliary_source.
|
||||
*/
|
||||
void output_double_low_resolution(uint8_t *target, const uint8_t *source, const uint8_t *auxiliary_source, size_t length, int column, int row) const;
|
||||
|
||||
/*!
|
||||
Outputs 40-column high-resolution graphics to @c target, drawing @c length columns from @c source.
|
||||
*/
|
||||
void output_high_resolution(uint8_t *target, const uint8_t *source, size_t length) const;
|
||||
|
||||
/*!
|
||||
Outputs 80-column double-high-resolution graphics to @c target, drawing @c length columns from @c source.
|
||||
*/
|
||||
void output_double_high_resolution(uint8_t *target, const uint8_t *source, const uint8_t *auxiliary_source, size_t length) const;
|
||||
|
||||
/*!
|
||||
Outputs 40-column "fat low resolution" graphics to @c target, drawing @c length columns from @c source.
|
||||
|
||||
Fat low-resolution mode is like regular low-resolution mode except that data is shifted out on the 7M
|
||||
clock rather than the 14M.
|
||||
*/
|
||||
void output_fat_low_resolution(uint8_t *target, const uint8_t *source, size_t length, int column, int row) const;
|
||||
|
||||
// Maintain a ClockDeferrer for delayed mode switches.
|
||||
ClockDeferrer<Cycles> deferrer_;
|
||||
};
|
||||
|
||||
template <class BusHandler, bool is_iie> class Video: public VideoBase {
|
||||
public:
|
||||
/// Constructs an instance of the video feed; a CRT is also created.
|
||||
Video(BusHandler &bus_handler) :
|
||||
VideoBase(),
|
||||
VideoBase(is_iie, [=] (Cycles cycles) { advance(cycles); }),
|
||||
bus_handler_(bus_handler) {}
|
||||
|
||||
/*!
|
||||
Advances time by @c cycles; expects to be fed by the CPU clock.
|
||||
Implicitly adds an extra half a colour clock at the end of every
|
||||
line.
|
||||
Runs video for @c cycles.
|
||||
*/
|
||||
void run_for(const Cycles cycles) {
|
||||
/*
|
||||
Addressing scheme used throughout is that column 0 is the first column with pixels in it;
|
||||
row 0 is the first row with pixels in it.
|
||||
|
||||
A frame is oriented around 65 cycles across, 262 lines down.
|
||||
*/
|
||||
static const int first_sync_line = 220; // A complete guess. Information needed.
|
||||
static const int first_sync_column = 49; // Also a guess.
|
||||
static const int sync_length = 4; // One of the two likely candidates.
|
||||
|
||||
int int_cycles = cycles.as_int();
|
||||
while(int_cycles) {
|
||||
const int cycles_this_line = std::min(65 - column_, int_cycles);
|
||||
const int ending_column = column_ + cycles_this_line;
|
||||
|
||||
if(row_ >= first_sync_line && row_ < first_sync_line + 3) {
|
||||
// In effect apply an XOR to HSYNC and VSYNC flags in order to include equalising
|
||||
// pulses (and hencce keep hsync approximately where it should be during vsync).
|
||||
const int blank_start = std::max(first_sync_column - sync_length, column_);
|
||||
const int blank_end = std::min(first_sync_column, ending_column);
|
||||
if(blank_end > blank_start) {
|
||||
if(blank_start > column_) {
|
||||
crt_->output_sync(static_cast<unsigned int>(blank_start - column_) * 14);
|
||||
}
|
||||
crt_->output_blank(static_cast<unsigned int>(blank_end - blank_start) * 14);
|
||||
if(blank_end < ending_column) {
|
||||
crt_->output_sync(static_cast<unsigned int>(ending_column - blank_end) * 14);
|
||||
}
|
||||
} else {
|
||||
crt_->output_sync(static_cast<unsigned int>(cycles_this_line) * 14);
|
||||
}
|
||||
} else {
|
||||
const GraphicsMode line_mode = graphics_mode(row_);
|
||||
|
||||
// The first 40 columns are submitted to the CRT only upon completion;
|
||||
// they'll be either graphics or blank, depending on which side we are
|
||||
// of line 192.
|
||||
if(column_ < 40) {
|
||||
if(row_ < 192) {
|
||||
const bool requires_high_density = line_mode != GraphicsMode::Text;
|
||||
if(!column_ || requires_high_density != pixels_are_high_density_) {
|
||||
if(column_) output_data_to_column(column_);
|
||||
pixel_pointer_ = crt_->allocate_write_area(561);
|
||||
pixel_pointer_column_ = column_;
|
||||
pixels_are_high_density_ = requires_high_density;
|
||||
graphics_carry_ = 0;
|
||||
}
|
||||
|
||||
const int pixel_end = std::min(40, ending_column);
|
||||
const int character_row = row_ >> 3;
|
||||
const int pixel_row = row_ & 7;
|
||||
const uint16_t row_address = static_cast<uint16_t>((character_row >> 3) * 40 + ((character_row&7) << 7));
|
||||
const uint16_t text_address = static_cast<uint16_t>(((video_page()+1) * 0x400) + row_address);
|
||||
|
||||
switch(line_mode) {
|
||||
case GraphicsMode::Text: {
|
||||
const uint8_t inverses[] = {
|
||||
0xff,
|
||||
alternative_character_set_ ? static_cast<uint8_t>(0xff) : static_cast<uint8_t>((flash_ / flash_length) * 0xff),
|
||||
0x00,
|
||||
0x00
|
||||
};
|
||||
const uint8_t masks[] = {
|
||||
alternative_character_set_ ? static_cast<uint8_t>(0x7f) : static_cast<uint8_t>(0x3f),
|
||||
is_iie ? 0x7f : 0x3f,
|
||||
};
|
||||
for(int c = column_; c < pixel_end; ++c) {
|
||||
const uint8_t character = bus_handler_.perform_read(static_cast<uint16_t>(text_address + c));
|
||||
const uint8_t xor_mask = inverses[character >> 6];
|
||||
const std::size_t character_address = static_cast<std::size_t>(((character & masks[character >> 7]) << 3) + pixel_row);
|
||||
const uint8_t character_pattern = character_rom_[character_address] ^ xor_mask;
|
||||
|
||||
// The character ROM is output MSB to LSB rather than LSB to MSB.
|
||||
pixel_pointer_[0] = character_pattern & 0x40;
|
||||
pixel_pointer_[1] = character_pattern & 0x20;
|
||||
pixel_pointer_[2] = character_pattern & 0x10;
|
||||
pixel_pointer_[3] = character_pattern & 0x08;
|
||||
pixel_pointer_[4] = character_pattern & 0x04;
|
||||
pixel_pointer_[5] = character_pattern & 0x02;
|
||||
pixel_pointer_[6] = character_pattern & 0x01;
|
||||
graphics_carry_ = character_pattern & 0x01;
|
||||
pixel_pointer_ += 7;
|
||||
}
|
||||
} break;
|
||||
|
||||
case GraphicsMode::DoubleText: {
|
||||
const uint8_t inverses[] = {
|
||||
0xff,
|
||||
alternative_character_set_ ? static_cast<uint8_t>(0xff) : static_cast<uint8_t>((flash_ / flash_length) * 0xff),
|
||||
0x00,
|
||||
0x00
|
||||
};
|
||||
const uint8_t masks[] = {
|
||||
alternative_character_set_ ? static_cast<uint8_t>(0x7f) : static_cast<uint8_t>(0x3f),
|
||||
is_iie ? 0x7f : 0x3f,
|
||||
};
|
||||
for(int c = column_; c < pixel_end; ++c) {
|
||||
const uint16_t characters = bus_handler_.perform_aux_read(static_cast<uint16_t>(text_address + c));
|
||||
const std::size_t character_addresses[2] = {
|
||||
static_cast<std::size_t>((((characters >> 8) & masks[characters >> 15]) << 3) + pixel_row),
|
||||
static_cast<std::size_t>(((characters & masks[(characters >> 7)&1]) << 3) + pixel_row),
|
||||
};
|
||||
|
||||
const uint8_t character_patterns[2] = {
|
||||
static_cast<uint8_t>(character_rom_[character_addresses[0]] ^ inverses[(characters >> 14) & 3]),
|
||||
static_cast<uint8_t>(character_rom_[character_addresses[1]] ^ inverses[(characters >> 6) & 3]),
|
||||
};
|
||||
|
||||
// The character ROM is output MSB to LSB rather than LSB to MSB.
|
||||
pixel_pointer_[0] = character_patterns[0] & 0x40;
|
||||
pixel_pointer_[1] = character_patterns[0] & 0x20;
|
||||
pixel_pointer_[2] = character_patterns[0] & 0x10;
|
||||
pixel_pointer_[3] = character_patterns[0] & 0x08;
|
||||
pixel_pointer_[4] = character_patterns[0] & 0x04;
|
||||
pixel_pointer_[5] = character_patterns[0] & 0x02;
|
||||
pixel_pointer_[6] = character_patterns[0] & 0x01;
|
||||
pixel_pointer_[7] = character_patterns[1] & 0x40;
|
||||
pixel_pointer_[8] = character_patterns[1] & 0x20;
|
||||
pixel_pointer_[9] = character_patterns[1] & 0x10;
|
||||
pixel_pointer_[10] = character_patterns[1] & 0x08;
|
||||
pixel_pointer_[11] = character_patterns[1] & 0x04;
|
||||
pixel_pointer_[12] = character_patterns[1] & 0x02;
|
||||
pixel_pointer_[13] = character_patterns[1] & 0x01;
|
||||
graphics_carry_ = character_patterns[1] & 0x01;
|
||||
pixel_pointer_ += 14;
|
||||
}
|
||||
} break;
|
||||
|
||||
case GraphicsMode::DoubleLowRes: {
|
||||
const int row_shift = (row_&4);
|
||||
for(int c = column_; c < pixel_end; ++c) {
|
||||
const uint16_t nibble = (bus_handler_.perform_aux_read(static_cast<uint16_t>(text_address + c)) >> row_shift) & 0xf0f;
|
||||
|
||||
if(c&1) {
|
||||
pixel_pointer_[0] = pixel_pointer_[4] = (nibble >> 8) & 4;
|
||||
pixel_pointer_[1] = pixel_pointer_[5] = (nibble >> 8) & 8;
|
||||
pixel_pointer_[2] = pixel_pointer_[6] = (nibble >> 8) & 1;
|
||||
pixel_pointer_[3] = (nibble >> 8) & 2;
|
||||
|
||||
pixel_pointer_[8] = pixel_pointer_[12] = nibble & 4;
|
||||
pixel_pointer_[9] = pixel_pointer_[13] = nibble & 8;
|
||||
pixel_pointer_[10] = nibble & 1;
|
||||
pixel_pointer_[7] = pixel_pointer_[11] = nibble & 2;
|
||||
graphics_carry_ = nibble & 8;
|
||||
} else {
|
||||
pixel_pointer_[0] = pixel_pointer_[4] = (nibble >> 8) & 1;
|
||||
pixel_pointer_[1] = pixel_pointer_[5] = (nibble >> 8) & 2;
|
||||
pixel_pointer_[2] = pixel_pointer_[6] = (nibble >> 8) & 4;
|
||||
pixel_pointer_[3] = (nibble >> 8) & 8;
|
||||
|
||||
pixel_pointer_[8] = pixel_pointer_[12] = nibble & 1;
|
||||
pixel_pointer_[9] = pixel_pointer_[13] = nibble & 2;
|
||||
pixel_pointer_[10] = nibble & 4;
|
||||
pixel_pointer_[7] = pixel_pointer_[11] = nibble & 8;
|
||||
graphics_carry_ = nibble & 2;
|
||||
}
|
||||
pixel_pointer_ += 14;
|
||||
}
|
||||
} break;
|
||||
|
||||
case GraphicsMode::LowRes: {
|
||||
const int row_shift = (row_&4);
|
||||
// TODO: decompose into two loops, possibly.
|
||||
for(int c = column_; c < pixel_end; ++c) {
|
||||
const uint8_t nibble = (bus_handler_.perform_read(static_cast<uint16_t>(text_address + c)) >> row_shift) & 0x0f;
|
||||
|
||||
// Low-resolution graphics mode shifts the colour code on a loop, but has to account for whether this
|
||||
// 14-sample output window is starting at the beginning of a colour cycle or halfway through.
|
||||
if(c&1) {
|
||||
pixel_pointer_[0] = pixel_pointer_[4] = pixel_pointer_[8] = pixel_pointer_[12] = nibble & 4;
|
||||
pixel_pointer_[1] = pixel_pointer_[5] = pixel_pointer_[9] = pixel_pointer_[13] = nibble & 8;
|
||||
pixel_pointer_[2] = pixel_pointer_[6] = pixel_pointer_[10] = nibble & 1;
|
||||
pixel_pointer_[3] = pixel_pointer_[7] = pixel_pointer_[11] = nibble & 2;
|
||||
graphics_carry_ = nibble & 8;
|
||||
} else {
|
||||
pixel_pointer_[0] = pixel_pointer_[4] = pixel_pointer_[8] = pixel_pointer_[12] = nibble & 1;
|
||||
pixel_pointer_[1] = pixel_pointer_[5] = pixel_pointer_[9] = pixel_pointer_[13] = nibble & 2;
|
||||
pixel_pointer_[2] = pixel_pointer_[6] = pixel_pointer_[10] = nibble & 4;
|
||||
pixel_pointer_[3] = pixel_pointer_[7] = pixel_pointer_[11] = nibble & 8;
|
||||
graphics_carry_ = nibble & 2;
|
||||
}
|
||||
pixel_pointer_ += 14;
|
||||
}
|
||||
} break;
|
||||
|
||||
case GraphicsMode::HighRes: {
|
||||
const uint16_t graphics_address = static_cast<uint16_t>(((video_page()+1) * 0x2000) + row_address + ((pixel_row&7) << 10));
|
||||
for(int c = column_; c < pixel_end; ++c) {
|
||||
const uint8_t graphic = bus_handler_.perform_read(static_cast<uint16_t>(graphics_address + c));
|
||||
|
||||
// High resolution graphics shift out LSB to MSB, optionally with a delay of half a pixel.
|
||||
// If there is a delay, the previous output level is held to bridge the gap.
|
||||
if(graphic & 0x80) {
|
||||
pixel_pointer_[0] = graphics_carry_;
|
||||
pixel_pointer_[1] = pixel_pointer_[2] = graphic & 0x01;
|
||||
pixel_pointer_[3] = pixel_pointer_[4] = graphic & 0x02;
|
||||
pixel_pointer_[5] = pixel_pointer_[6] = graphic & 0x04;
|
||||
pixel_pointer_[7] = pixel_pointer_[8] = graphic & 0x08;
|
||||
pixel_pointer_[9] = pixel_pointer_[10] = graphic & 0x10;
|
||||
pixel_pointer_[11] = pixel_pointer_[12] = graphic & 0x20;
|
||||
pixel_pointer_[13] = graphic & 0x40;
|
||||
} else {
|
||||
pixel_pointer_[0] = pixel_pointer_[1] = graphic & 0x01;
|
||||
pixel_pointer_[2] = pixel_pointer_[3] = graphic & 0x02;
|
||||
pixel_pointer_[4] = pixel_pointer_[5] = graphic & 0x04;
|
||||
pixel_pointer_[6] = pixel_pointer_[7] = graphic & 0x08;
|
||||
pixel_pointer_[8] = pixel_pointer_[9] = graphic & 0x10;
|
||||
pixel_pointer_[10] = pixel_pointer_[11] = graphic & 0x20;
|
||||
pixel_pointer_[12] = pixel_pointer_[13] = graphic & 0x40;
|
||||
}
|
||||
graphics_carry_ = graphic & 0x40;
|
||||
pixel_pointer_ += 14;
|
||||
}
|
||||
} break;
|
||||
|
||||
case GraphicsMode::DoubleHighRes: {
|
||||
const uint16_t graphics_address = static_cast<uint16_t>(((video_page()+1) * 0x2000) + row_address + ((pixel_row&7) << 10));
|
||||
for(int c = column_; c < pixel_end; ++c) {
|
||||
const uint16_t graphic = bus_handler_.perform_aux_read(static_cast<uint16_t>(graphics_address + c));
|
||||
|
||||
pixel_pointer_[0] = graphics_carry_;
|
||||
pixel_pointer_[1] = (graphic >> 8) & 0x01;
|
||||
pixel_pointer_[2] = (graphic >> 8) & 0x02;
|
||||
pixel_pointer_[3] = (graphic >> 8) & 0x04;
|
||||
pixel_pointer_[4] = (graphic >> 8) & 0x08;
|
||||
pixel_pointer_[5] = (graphic >> 8) & 0x10;
|
||||
pixel_pointer_[6] = (graphic >> 8) & 0x20;
|
||||
pixel_pointer_[7] = (graphic >> 8) & 0x40;
|
||||
pixel_pointer_[8] = graphic & 0x01;
|
||||
pixel_pointer_[9] = graphic & 0x02;
|
||||
pixel_pointer_[10] = graphic & 0x04;
|
||||
pixel_pointer_[11] = graphic & 0x08;
|
||||
pixel_pointer_[12] = graphic & 0x10;
|
||||
pixel_pointer_[13] = graphic & 0x20;
|
||||
graphics_carry_ = graphic & 0x40;
|
||||
pixel_pointer_ += 14;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
if(ending_column >= 40) {
|
||||
output_data_to_column(40);
|
||||
}
|
||||
} else {
|
||||
if(ending_column >= 40) {
|
||||
crt_->output_blank(560);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
The left border, sync, right border pattern doesn't depend on whether
|
||||
there were pixels this row and is output as soon as it is known.
|
||||
*/
|
||||
|
||||
const int first_blank_start = std::max(40, column_);
|
||||
const int first_blank_end = std::min(first_sync_column, ending_column);
|
||||
if(first_blank_end > first_blank_start) {
|
||||
crt_->output_blank(static_cast<unsigned int>(first_blank_end - first_blank_start) * 14);
|
||||
}
|
||||
|
||||
const int sync_start = std::max(first_sync_column, column_);
|
||||
const int sync_end = std::min(first_sync_column + sync_length, ending_column);
|
||||
if(sync_end > sync_start) {
|
||||
crt_->output_sync(static_cast<unsigned int>(sync_end - sync_start) * 14);
|
||||
}
|
||||
|
||||
int second_blank_start;
|
||||
if(!is_text_mode(graphics_mode(row_+1))) {
|
||||
const int colour_burst_start = std::max(first_sync_column + sync_length + 1, column_);
|
||||
const int colour_burst_end = std::min(first_sync_column + sync_length + 4, ending_column);
|
||||
if(colour_burst_end > colour_burst_start) {
|
||||
crt_->output_colour_burst(static_cast<unsigned int>(colour_burst_end - colour_burst_start) * 14, 128);
|
||||
}
|
||||
|
||||
second_blank_start = std::max(first_sync_column + 7, column_);
|
||||
} else {
|
||||
second_blank_start = std::max(first_sync_column + 4, column_);
|
||||
}
|
||||
|
||||
if(ending_column > second_blank_start) {
|
||||
crt_->output_blank(static_cast<unsigned int>(ending_column - second_blank_start) * 14);
|
||||
}
|
||||
}
|
||||
|
||||
int_cycles -= cycles_this_line;
|
||||
column_ = (column_ + cycles_this_line) % 65;
|
||||
if(!column_) {
|
||||
row_ = (row_ + 1) % 262;
|
||||
flash_ = (flash_ + 1) % (2 * flash_length);
|
||||
|
||||
// Add an extra half a colour cycle of blank; this isn't counted in the run_for
|
||||
// count explicitly but is promised.
|
||||
crt_->output_blank(2);
|
||||
}
|
||||
}
|
||||
void run_for(Cycles cycles) {
|
||||
deferrer_.run_for(cycles);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -537,7 +300,9 @@ template <class BusHandler, bool is_iie> class Video: public VideoBase {
|
||||
|
||||
// Calculate the address and return the value.
|
||||
uint16_t read_address = static_cast<uint16_t>(get_row_address(mapped_row) + mapped_column - 25);
|
||||
return bus_handler_.perform_read(read_address);
|
||||
uint8_t value, aux_value;
|
||||
bus_handler_.perform_read(read_address, 1, &value, &aux_value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -558,15 +323,253 @@ template <class BusHandler, bool is_iie> class Video: public VideoBase {
|
||||
}
|
||||
|
||||
private:
|
||||
GraphicsMode graphics_mode(int row) {
|
||||
if(text_) return columns_80_ ? GraphicsMode::DoubleText : GraphicsMode::Text;
|
||||
if(mixed_ && row >= 160 && row < 192) {
|
||||
return (columns_80_ || double_high_resolution_) ? GraphicsMode::DoubleText : GraphicsMode::Text;
|
||||
/*!
|
||||
Advances time by @c cycles; expects to be fed by the CPU clock.
|
||||
Implicitly adds an extra half a colour clock at the end of
|
||||
line.
|
||||
*/
|
||||
void advance(Cycles cycles) {
|
||||
/*
|
||||
Addressing scheme used throughout is that column 0 is the first column with pixels in it;
|
||||
row 0 is the first row with pixels in it.
|
||||
|
||||
A frame is oriented around 65 cycles across, 262 lines down.
|
||||
*/
|
||||
static const int first_sync_line = 220; // A complete guess. Information needed.
|
||||
static const int first_sync_column = 49; // Also a guess.
|
||||
static const int sync_length = 4; // One of the two likely candidates.
|
||||
|
||||
int int_cycles = cycles.as_int();
|
||||
while(int_cycles) {
|
||||
const int cycles_this_line = std::min(65 - column_, int_cycles);
|
||||
const int ending_column = column_ + cycles_this_line;
|
||||
|
||||
if(row_ >= first_sync_line && row_ < first_sync_line + 3) {
|
||||
// In effect apply an XOR to HSYNC and VSYNC flags in order to include equalising
|
||||
// pulses (and hencce keep hsync approximately where it should be during vsync).
|
||||
const int blank_start = std::max(first_sync_column - sync_length, column_);
|
||||
const int blank_end = std::min(first_sync_column, ending_column);
|
||||
if(blank_end > blank_start) {
|
||||
if(blank_start > column_) {
|
||||
crt_->output_sync(static_cast<unsigned int>(blank_start - column_) * 14);
|
||||
}
|
||||
crt_->output_blank(static_cast<unsigned int>(blank_end - blank_start) * 14);
|
||||
if(blank_end < ending_column) {
|
||||
crt_->output_sync(static_cast<unsigned int>(ending_column - blank_end) * 14);
|
||||
}
|
||||
} else {
|
||||
crt_->output_sync(static_cast<unsigned int>(cycles_this_line) * 14);
|
||||
}
|
||||
} else {
|
||||
const GraphicsMode line_mode = graphics_mode(row_);
|
||||
|
||||
// Determine whether there's any fetching to do. Fetching occurs during the first
|
||||
// 40 columns of rows prior to 192.
|
||||
if(row_ < 192 && column_ < 40) {
|
||||
const int character_row = row_ >> 3;
|
||||
const uint16_t row_address = static_cast<uint16_t>((character_row >> 3) * 40 + ((character_row&7) << 7));
|
||||
|
||||
// Grab the memory contents that'll be needed momentarily.
|
||||
const int fetch_end = std::min(40, ending_column);
|
||||
uint16_t fetch_address;
|
||||
switch(line_mode) {
|
||||
default:
|
||||
case GraphicsMode::Text:
|
||||
case GraphicsMode::DoubleText:
|
||||
case GraphicsMode::LowRes:
|
||||
case GraphicsMode::FatLowRes:
|
||||
case GraphicsMode::DoubleLowRes: {
|
||||
const uint16_t text_address = static_cast<uint16_t>(((video_page()+1) * 0x400) + row_address);
|
||||
fetch_address = static_cast<uint16_t>(text_address + column_);
|
||||
} break;
|
||||
|
||||
case GraphicsMode::HighRes:
|
||||
case GraphicsMode::DoubleHighRes:
|
||||
fetch_address = static_cast<uint16_t>(((video_page()+1) * 0x2000) + row_address + ((row_&7) << 10) + column_);
|
||||
break;
|
||||
}
|
||||
|
||||
bus_handler_.perform_read(
|
||||
fetch_address,
|
||||
static_cast<size_t>(fetch_end - column_),
|
||||
&base_stream_[static_cast<size_t>(column_)],
|
||||
&auxiliary_stream_[static_cast<size_t>(column_)]);
|
||||
// TODO: should character modes be mapped to character pixel outputs here?
|
||||
}
|
||||
|
||||
if(row_ < 192) {
|
||||
// The pixel area is the first 40.5 columns; base contents
|
||||
// remain where they would naturally be but auxiliary
|
||||
// graphics appear to the left of that.
|
||||
if(!column_) {
|
||||
pixel_pointer_ = crt_->allocate_write_area(568);
|
||||
graphics_carry_ = 0;
|
||||
was_double_ = true;
|
||||
}
|
||||
|
||||
if(column_ < 40) {
|
||||
const int pixel_start = std::max(0, column_);
|
||||
const int pixel_end = std::min(40, ending_column);
|
||||
const int pixel_row = row_ & 7;
|
||||
|
||||
const bool is_double = Video::is_double_mode(line_mode);
|
||||
if(!is_double && was_double_) {
|
||||
pixel_pointer_[pixel_start*14 + 0] =
|
||||
pixel_pointer_[pixel_start*14 + 1] =
|
||||
pixel_pointer_[pixel_start*14 + 2] =
|
||||
pixel_pointer_[pixel_start*14 + 3] =
|
||||
pixel_pointer_[pixel_start*14 + 4] =
|
||||
pixel_pointer_[pixel_start*14 + 5] =
|
||||
pixel_pointer_[pixel_start*14 + 6] = 0;
|
||||
}
|
||||
was_double_ = is_double;
|
||||
|
||||
switch(line_mode) {
|
||||
case GraphicsMode::Text:
|
||||
output_text(
|
||||
&pixel_pointer_[pixel_start * 14 + 7],
|
||||
&base_stream_[static_cast<size_t>(pixel_start)],
|
||||
static_cast<size_t>(pixel_end - pixel_start),
|
||||
static_cast<size_t>(pixel_row));
|
||||
break;
|
||||
|
||||
case GraphicsMode::DoubleText:
|
||||
output_double_text(
|
||||
&pixel_pointer_[pixel_start * 14],
|
||||
&base_stream_[static_cast<size_t>(pixel_start)],
|
||||
&auxiliary_stream_[static_cast<size_t>(pixel_start)],
|
||||
static_cast<size_t>(pixel_end - pixel_start),
|
||||
static_cast<size_t>(pixel_row));
|
||||
break;
|
||||
|
||||
case GraphicsMode::LowRes:
|
||||
output_low_resolution(
|
||||
&pixel_pointer_[pixel_start * 14 + 7],
|
||||
&base_stream_[static_cast<size_t>(pixel_start)],
|
||||
static_cast<size_t>(pixel_end - pixel_start),
|
||||
pixel_start,
|
||||
pixel_row);
|
||||
break;
|
||||
|
||||
case GraphicsMode::FatLowRes:
|
||||
output_fat_low_resolution(
|
||||
&pixel_pointer_[pixel_start * 14 + 7],
|
||||
&base_stream_[static_cast<size_t>(pixel_start)],
|
||||
static_cast<size_t>(pixel_end - pixel_start),
|
||||
pixel_start,
|
||||
pixel_row);
|
||||
break;
|
||||
|
||||
case GraphicsMode::DoubleLowRes:
|
||||
output_double_low_resolution(
|
||||
&pixel_pointer_[pixel_start * 14],
|
||||
&base_stream_[static_cast<size_t>(pixel_start)],
|
||||
&auxiliary_stream_[static_cast<size_t>(pixel_start)],
|
||||
static_cast<size_t>(pixel_end - pixel_start),
|
||||
pixel_start,
|
||||
pixel_row);
|
||||
break;
|
||||
|
||||
case GraphicsMode::HighRes:
|
||||
output_high_resolution(
|
||||
&pixel_pointer_[pixel_start * 14 + 7],
|
||||
&base_stream_[static_cast<size_t>(pixel_start)],
|
||||
static_cast<size_t>(pixel_end - pixel_start));
|
||||
break;
|
||||
|
||||
case GraphicsMode::DoubleHighRes:
|
||||
output_double_high_resolution(
|
||||
&pixel_pointer_[pixel_start * 14],
|
||||
&base_stream_[static_cast<size_t>(pixel_start)],
|
||||
&auxiliary_stream_[static_cast<size_t>(pixel_start)],
|
||||
static_cast<size_t>(pixel_end - pixel_start));
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
if(pixel_end == 40) {
|
||||
if(was_double_) {
|
||||
pixel_pointer_[563] =
|
||||
pixel_pointer_[564] =
|
||||
pixel_pointer_[565] =
|
||||
pixel_pointer_[566] =
|
||||
pixel_pointer_[567] = 0;
|
||||
} else {
|
||||
if(line_mode == GraphicsMode::HighRes && base_stream_[39]&0x80)
|
||||
pixel_pointer_[567] = graphics_carry_;
|
||||
else
|
||||
pixel_pointer_[567] = 0;
|
||||
}
|
||||
|
||||
crt_->output_data(568, 568);
|
||||
pixel_pointer_ = nullptr;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(column_ < 40 && ending_column >= 40) {
|
||||
crt_->output_blank(568);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
The left border, sync, right border pattern doesn't depend on whether
|
||||
there were pixels this row and is output as soon as it is known.
|
||||
*/
|
||||
|
||||
if(column_ < first_sync_column && ending_column >= first_sync_column) {
|
||||
crt_->output_blank((first_sync_column - 41)*14 - 1);
|
||||
}
|
||||
|
||||
if(column_ < (first_sync_column + sync_length) && ending_column >= (first_sync_column + sync_length)) {
|
||||
crt_->output_sync(sync_length*14);
|
||||
}
|
||||
|
||||
int second_blank_start;
|
||||
if(!is_text_mode(graphics_mode(row_+1))) {
|
||||
const int colour_burst_start = std::max(first_sync_column + sync_length + 1, column_);
|
||||
const int colour_burst_end = std::min(first_sync_column + sync_length + 4, ending_column);
|
||||
if(colour_burst_end > colour_burst_start) {
|
||||
crt_->output_colour_burst(static_cast<unsigned int>(colour_burst_end - colour_burst_start) * 14, 192);
|
||||
}
|
||||
|
||||
second_blank_start = std::max(first_sync_column + sync_length + 3, column_);
|
||||
} else {
|
||||
second_blank_start = std::max(first_sync_column + sync_length, column_);
|
||||
}
|
||||
|
||||
if(ending_column > second_blank_start) {
|
||||
crt_->output_blank(static_cast<unsigned int>(ending_column - second_blank_start) * 14);
|
||||
}
|
||||
}
|
||||
|
||||
int_cycles -= cycles_this_line;
|
||||
column_ = (column_ + cycles_this_line) % 65;
|
||||
if(!column_) {
|
||||
row_ = (row_ + 1) % 262;
|
||||
flash_ = (flash_ + 1) % (2 * flash_length);
|
||||
if(!alternative_character_set_) {
|
||||
character_zones[1].xor_mask = flash_mask();
|
||||
}
|
||||
|
||||
// Add an extra half a colour cycle of blank; this isn't counted in the run_for
|
||||
// count explicitly but is promised.
|
||||
crt_->output_blank(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GraphicsMode graphics_mode(int row) {
|
||||
if(
|
||||
text_ ||
|
||||
(mixed_ && row >= 160 && row < 192)
|
||||
) return columns_80_ ? GraphicsMode::DoubleText : GraphicsMode::Text;
|
||||
if(high_resolution_) {
|
||||
return double_high_resolution_ ? GraphicsMode::DoubleHighRes : GraphicsMode::HighRes;
|
||||
return (annunciator_3_ && columns_80_) ? GraphicsMode::DoubleHighRes : GraphicsMode::HighRes;
|
||||
} else {
|
||||
return double_high_resolution_ ? GraphicsMode::DoubleLowRes : GraphicsMode::LowRes;
|
||||
if(columns_80_) return GraphicsMode::DoubleLowRes;
|
||||
if(annunciator_3_) return GraphicsMode::FatLowRes;
|
||||
return GraphicsMode::LowRes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -585,13 +588,7 @@ template <class BusHandler, bool is_iie> class Video: public VideoBase {
|
||||
static_cast<uint16_t>(((video_page()+1) * 0x400) + row_address);
|
||||
}
|
||||
|
||||
static const int flash_length = 8406;
|
||||
BusHandler &bus_handler_;
|
||||
void output_data_to_column(int column) {
|
||||
int length = column - pixel_pointer_column_;
|
||||
crt_->output_data(static_cast<unsigned int>(length*14), static_cast<unsigned int>(length * (pixels_are_high_density_ ? 14 : 7)));
|
||||
pixel_pointer_ = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ template<class T> class Cartridge:
|
||||
}
|
||||
|
||||
protected:
|
||||
CPU::MOS6502::Processor<Cartridge<T>, true> m6502_;
|
||||
CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, Cartridge<T>, true> m6502_;
|
||||
std::vector<uint8_t> rom_;
|
||||
|
||||
private:
|
||||
|
||||
@@ -143,7 +143,7 @@ class MachineBase:
|
||||
void set_activity_observer(Activity::Observer *observer);
|
||||
|
||||
protected:
|
||||
CPU::MOS6502::Processor<MachineBase, false> m6502_;
|
||||
CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, MachineBase, false> m6502_;
|
||||
std::shared_ptr<Storage::Disk::Drive> drive_;
|
||||
|
||||
uint8_t ram_[0x800];
|
||||
|
||||
@@ -703,7 +703,7 @@ class ConcreteMachine:
|
||||
void update_video() {
|
||||
mos6560_->run_for(cycles_since_mos6560_update_.flush());
|
||||
}
|
||||
CPU::MOS6502::Processor<ConcreteMachine, false> m6502_;
|
||||
CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, ConcreteMachine, false> m6502_;
|
||||
|
||||
std::vector<uint8_t> character_rom_;
|
||||
std::vector<uint8_t> basic_rom_;
|
||||
|
||||
@@ -541,7 +541,7 @@ class ConcreteMachine:
|
||||
m6502_.set_irq_line(interrupt_status_ & 1);
|
||||
}
|
||||
|
||||
CPU::MOS6502::Processor<ConcreteMachine, false> m6502_;
|
||||
CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, ConcreteMachine, false> m6502_;
|
||||
|
||||
// Things that directly constitute the memory map.
|
||||
uint8_t roms_[16][16384];
|
||||
|
||||
@@ -575,7 +575,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
const uint16_t basic_invisible_ram_top_ = 0xffff;
|
||||
const uint16_t basic_visible_ram_top_ = 0xbfff;
|
||||
|
||||
CPU::MOS6502::Processor<ConcreteMachine, false> m6502_;
|
||||
CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, ConcreteMachine, false> m6502_;
|
||||
|
||||
// RAM and ROM
|
||||
std::vector<uint8_t> rom_, microdisc_rom_, colour_rom_;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
4B018B89211930DE002A3937 /* 65C02_extended_opcodes_test.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4B018B88211930DE002A3937 /* 65C02_extended_opcodes_test.bin */; };
|
||||
4B01A6881F22F0DB001FD6E3 /* Z80MemptrTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B01A6871F22F0DB001FD6E3 /* Z80MemptrTests.swift */; };
|
||||
4B0333AF2094081A0050B93D /* AppleDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0333AD2094081A0050B93D /* AppleDSK.cpp */; };
|
||||
4B0333B02094081A0050B93D /* AppleDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0333AD2094081A0050B93D /* AppleDSK.cpp */; };
|
||||
@@ -690,6 +691,7 @@
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
4B018B88211930DE002A3937 /* 65C02_extended_opcodes_test.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; name = 65C02_extended_opcodes_test.bin; path = "Klaus Dormann/65C02_extended_opcodes_test.bin"; sourceTree = "<group>"; };
|
||||
4B01A6871F22F0DB001FD6E3 /* Z80MemptrTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Z80MemptrTests.swift; sourceTree = "<group>"; };
|
||||
4B0333AD2094081A0050B93D /* AppleDSK.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = AppleDSK.cpp; sourceTree = "<group>"; };
|
||||
4B0333AE2094081A0050B93D /* AppleDSK.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = AppleDSK.hpp; sourceTree = "<group>"; };
|
||||
@@ -1005,6 +1007,7 @@
|
||||
4B894516201967B4007DE474 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; };
|
||||
4B894517201967B4007DE474 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; };
|
||||
4B894540201967D6007DE474 /* Machines.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Machines.hpp; sourceTree = "<group>"; };
|
||||
4B8A7E85212F988200F2BBC6 /* ClockDeferrer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ClockDeferrer.hpp; sourceTree = "<group>"; };
|
||||
4B8D287E1F77207100645199 /* TrackSerialiser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TrackSerialiser.hpp; sourceTree = "<group>"; };
|
||||
4B8E4ECD1DCE483D003716C3 /* KeyboardMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = KeyboardMachine.hpp; sourceTree = "<group>"; };
|
||||
4B8EF6071FE5AF830076CCDD /* LowpassSpeaker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LowpassSpeaker.hpp; sourceTree = "<group>"; };
|
||||
@@ -1545,14 +1548,15 @@
|
||||
4B1414631B588A1100E04248 /* Test Binaries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B98A1CD1FFADEC400ADF63B /* MSX ROMs */,
|
||||
4B9252CD1E74D28200B76AF1 /* Atari ROMs */,
|
||||
4B44EBF81DC9898E00A7820C /* BCDTEST_beeb */,
|
||||
4B98A1CD1FFADEC400ADF63B /* MSX ROMs */,
|
||||
4B018B88211930DE002A3937 /* 65C02_extended_opcodes_test.bin */,
|
||||
4B44EBF61DC9883B00A7820C /* 6502_functional_test.bin */,
|
||||
4B44EBF41DC987AE00A7820C /* AllSuiteA.bin */,
|
||||
4BE9A6B21EDE294200CBCB47 /* Zexall */,
|
||||
4BBF49B41ED2881600AB3669 /* FUSE */,
|
||||
4BB297E41B587D8300A49093 /* Wolfgang Lorenz 6502 test suite */,
|
||||
4BE9A6B21EDE294200CBCB47 /* Zexall */,
|
||||
);
|
||||
name = "Test Binaries";
|
||||
sourceTree = "<group>";
|
||||
@@ -3146,6 +3150,7 @@
|
||||
4BB06B211F316A3F00600C7A /* ForceInline.hpp */,
|
||||
4BB146C61F49D7D700253439 /* ClockingHintSource.hpp */,
|
||||
4B449C942063389900A095C8 /* TimeTypes.hpp */,
|
||||
4B8A7E85212F988200F2BBC6 /* ClockDeferrer.hpp */,
|
||||
);
|
||||
name = ClockReceiver;
|
||||
path = ../../ClockReceiver;
|
||||
@@ -3330,6 +3335,7 @@
|
||||
4BB2998A1B587D8400A49093 /* lseix in Resources */,
|
||||
4BB2994E1B587D8400A49093 /* dexn in Resources */,
|
||||
4BB299971B587D8400A49093 /* nopa in Resources */,
|
||||
4B018B89211930DE002A3937 /* 65C02_extended_opcodes_test.bin in Resources */,
|
||||
4BFCA1291ECBE7A700AC40C1 /* zexall.com in Resources */,
|
||||
4BB299521B587D8400A49093 /* eoray in Resources */,
|
||||
4BB299411B587D8400A49093 /* cpyb in Resources */,
|
||||
|
||||
@@ -116,6 +116,11 @@
|
||||
<action selector="saveScreenshot:" target="-1" id="7ky-xD-tip"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Insert..." keyEquivalent="O" id="qQa-kh-4nz">
|
||||
<connections>
|
||||
<action selector="insertMedia:" target="-1" id="9Hs-9J-dlY"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="rXU-KX-GkZ"/>
|
||||
<menuItem title="Page Setup…" enabled="NO" keyEquivalent="P" id="qIS-W8-SiK">
|
||||
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
|
||||
|
||||
@@ -56,7 +56,6 @@ class MachineDocument:
|
||||
super.windowControllerDidLoadNib(aController)
|
||||
aController.window?.contentAspectRatio = self.aspectRatio()
|
||||
setupMachineOutput()
|
||||
|
||||
}
|
||||
|
||||
// Attempting to show a sheet before the window is visible (such as when the NIB is loaded) results in
|
||||
@@ -213,6 +212,7 @@ class MachineDocument:
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Runtime media insertion.
|
||||
final func openGLView(_ view: CSOpenGLView, didReceiveFileAt URL: URL) {
|
||||
let mediaSet = CSMediaSet(fileAt: URL)
|
||||
if let mediaSet = mediaSet {
|
||||
@@ -220,6 +220,21 @@ class MachineDocument:
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction final func insertMedia(_ sender: AnyObject!) {
|
||||
let openPanel = NSOpenPanel()
|
||||
openPanel.message = "Hint: you can also insert media by dragging and dropping it onto the machine's window."
|
||||
openPanel.beginSheetModal(for: self.windowControllers[0].window!) { (response) in
|
||||
if response == .OK {
|
||||
for url in openPanel.urls {
|
||||
let mediaSet = CSMediaSet(fileAt: url)
|
||||
if let mediaSet = mediaSet {
|
||||
mediaSet.apply(to: self.machine)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: NSDocument overrides
|
||||
override func data(ofType typeName: String) throws -> Data {
|
||||
throw NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil)
|
||||
@@ -266,7 +281,7 @@ class MachineDocument:
|
||||
@IBAction func createMachine(_ sender: NSButton?) {
|
||||
self.configureAs(machinePicker!.selectedMachine())
|
||||
machinePicker = nil
|
||||
sender?.window?.close()
|
||||
self.windowControllers[0].window?.endSheet(self.machinePickerPanel!)
|
||||
}
|
||||
|
||||
@IBAction func cancelCreateMachine(_ sender: NSButton?) {
|
||||
@@ -287,6 +302,7 @@ class MachineDocument:
|
||||
switch item.action {
|
||||
case #selector(self.useKeyboardAsKeyboard):
|
||||
if machine == nil || !machine.hasKeyboard {
|
||||
menuItem.state = .off
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -295,6 +311,7 @@ class MachineDocument:
|
||||
|
||||
case #selector(self.useKeyboardAsJoystick):
|
||||
if machine == nil || !machine.hasJoystick {
|
||||
menuItem.state = .off
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -304,6 +321,9 @@ class MachineDocument:
|
||||
case #selector(self.showActivity(_:)):
|
||||
return self.activityPanel != nil
|
||||
|
||||
case #selector(self.insertMedia(_:)):
|
||||
return self.machine != nil && self.machine.canInsertMedia
|
||||
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,39 +13,43 @@
|
||||
<string>bin</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>cartridge</string>
|
||||
<string>cartridge.png</string>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array>
|
||||
<string>????</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Atari 2600 Cartridge</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.item</string>
|
||||
</array>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<integer>0</integer>
|
||||
<false/>
|
||||
<key>NSDocumentClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>rom</string>
|
||||
</array>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array>
|
||||
<string>????</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>chip</string>
|
||||
<string>chip.png</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>ROM Image</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.item</string>
|
||||
</array>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<integer>0</integer>
|
||||
<false/>
|
||||
<key>NSDocumentClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
@@ -53,100 +57,110 @@
|
||||
<string>uef</string>
|
||||
<string>uef.gz</string>
|
||||
</array>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array>
|
||||
<string>????</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>cassette</string>
|
||||
<string>cassette.png</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Electron/BBC UEF Image</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.item</string>
|
||||
</array>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<integer>0</integer>
|
||||
<false/>
|
||||
<key>NSDocumentClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>prg</string>
|
||||
</array>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array>
|
||||
<string>????</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>floppy525</string>
|
||||
<string>floppy525.png</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Commodore Program</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.item</string>
|
||||
</array>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<integer>0</integer>
|
||||
<false/>
|
||||
<key>NSDocumentClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>tap</string>
|
||||
</array>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array>
|
||||
<string>????</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>cassette</string>
|
||||
<string>cassette.png</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Tape Image</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.item</string>
|
||||
</array>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<integer>0</integer>
|
||||
<false/>
|
||||
<key>NSDocumentClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>g64</string>
|
||||
</array>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array>
|
||||
<string>????</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>floppy525</string>
|
||||
<string>floppy525.png</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Commodore Disk</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.item</string>
|
||||
</array>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<integer>0</integer>
|
||||
<false/>
|
||||
<key>NSDocumentClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>d64</string>
|
||||
</array>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array>
|
||||
<string>????</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>floppy525</string>
|
||||
<string>floppy525.png</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Commodore 1540/1 Disk</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.item</string>
|
||||
</array>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<integer>0</integer>
|
||||
<false/>
|
||||
<key>NSDocumentClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
@@ -157,40 +171,44 @@
|
||||
<string>adl</string>
|
||||
<string>adm</string>
|
||||
</array>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array>
|
||||
<string>????</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>floppy35</string>
|
||||
<string>floppy35.png</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Electron/BBC Disk Image</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.item</string>
|
||||
</array>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<integer>0</integer>
|
||||
<false/>
|
||||
<key>NSDocumentClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>dsk</string>
|
||||
</array>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array>
|
||||
<string>????</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>floppy35</string>
|
||||
<string>floppy35.png</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Disk Image</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.item</string>
|
||||
</array>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<integer>0</integer>
|
||||
<false/>
|
||||
<key>NSDocumentClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
@@ -198,20 +216,22 @@
|
||||
<string>o</string>
|
||||
<string>80</string>
|
||||
</array>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array>
|
||||
<string>????</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>cassette</string>
|
||||
<string>cassette.png</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>ZX80 Tape Image</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.item</string>
|
||||
</array>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<integer>0</integer>
|
||||
<false/>
|
||||
<key>NSDocumentClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
@@ -220,178 +240,196 @@
|
||||
<string>81</string>
|
||||
<string>p81</string>
|
||||
</array>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array>
|
||||
<string>????</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>cassette</string>
|
||||
<string>cassette.png</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>ZX81 Tape Image</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.item</string>
|
||||
</array>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<integer>0</integer>
|
||||
<false/>
|
||||
<key>NSDocumentClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>csw</string>
|
||||
</array>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array>
|
||||
<string>????</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>cassette</string>
|
||||
<string>cassette.png</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Tape Image</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.item</string>
|
||||
</array>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<integer>0</integer>
|
||||
<false/>
|
||||
<key>NSDocumentClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>tzx</string>
|
||||
</array>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array>
|
||||
<string>????</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>cassette</string>
|
||||
<string>cassette.png</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Tape Image</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.item</string>
|
||||
</array>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<integer>0</integer>
|
||||
<false/>
|
||||
<key>NSDocumentClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>cdt</string>
|
||||
</array>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array>
|
||||
<string>????</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>cassette</string>
|
||||
<string>cassette.png</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Amstrad CPC Tape Image</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.item</string>
|
||||
</array>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<integer>0</integer>
|
||||
<false/>
|
||||
<key>NSDocumentClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>hfe</string>
|
||||
</array>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array>
|
||||
<string>????</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>floppy35</string>
|
||||
<string>floppy35.png</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>HxC Disk Image</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.item</string>
|
||||
</array>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<integer>0</integer>
|
||||
<false/>
|
||||
<key>NSDocumentClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>cas</string>
|
||||
</array>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array>
|
||||
<string>????</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>cassette</string>
|
||||
<string>cassette.png</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>MSX Tape Image</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.item</string>
|
||||
</array>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<integer>0</integer>
|
||||
<false/>
|
||||
<key>NSDocumentClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>dmk</string>
|
||||
</array>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array>
|
||||
<string>????</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>floppy35</string>
|
||||
<string>floppy35.png</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Disk Image</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.item</string>
|
||||
</array>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<integer>0</integer>
|
||||
<false/>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>tsx</string>
|
||||
</array>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array>
|
||||
<string>????</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>cassette</string>
|
||||
<string>cassette.png</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>MSX Tape Image</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.item</string>
|
||||
</array>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<integer>0</integer>
|
||||
<false/>
|
||||
<key>NSDocumentClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>col</string>
|
||||
</array>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array>
|
||||
<string>????</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>cartridge</string>
|
||||
<string>cartridge.png</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>ColecoVision Cartridge</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.item</string>
|
||||
</array>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<integer>0</integer>
|
||||
<false/>
|
||||
<key>NSDocumentClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
@@ -401,20 +439,22 @@
|
||||
<string>do</string>
|
||||
<string>po</string>
|
||||
</array>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array>
|
||||
<string>????</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>floppy525</string>
|
||||
<string>floppy525.png</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Apple II Disk Image</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.item</string>
|
||||
</array>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<integer>0</integer>
|
||||
<false/>
|
||||
<key>NSDocumentClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleExecutable</key>
|
||||
@@ -436,7 +476,7 @@
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string></string>
|
||||
<string>public.app-category.entertainment</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
|
||||
@@ -39,6 +39,7 @@ typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) {
|
||||
@interface CSMachine : NSObject
|
||||
|
||||
- (nonnull instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/*!
|
||||
Initialises an instance of CSMachine.
|
||||
|
||||
@@ -70,6 +71,8 @@ typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) {
|
||||
@property (nonatomic, assign) CSMachineVideoSignal videoSignal;
|
||||
@property (nonatomic, assign) BOOL useAutomaticTapeMotorControl;
|
||||
|
||||
@property (nonatomic, readonly) BOOL canInsertMedia;
|
||||
|
||||
- (bool)supportsVideoSignal:(CSMachineVideoSignal)videoSignal;
|
||||
|
||||
// Input control.
|
||||
|
||||
@@ -516,6 +516,10 @@ struct ActivityObserver: public Activity::Observer {
|
||||
return [[NSString stringWithUTF8String:name.c_str()] lowercaseString];
|
||||
}
|
||||
|
||||
- (BOOL)canInsertMedia {
|
||||
return !!_machine->media_target();
|
||||
}
|
||||
|
||||
#pragma mark - Special machines
|
||||
|
||||
- (CSAtari2600 *)atari2600 {
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
typedef NS_ENUM(NSInteger, CSMachineAppleIIModel) {
|
||||
CSMachineAppleIIModelAppleII,
|
||||
CSMachineAppleIIModelAppleIIPlus,
|
||||
CSMachineAppleIIModelAppleIIe
|
||||
CSMachineAppleIIModelAppleIIe,
|
||||
CSMachineAppleIIModelAppleEnhancedIIe
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, CSMachineAppleIIDiskController) {
|
||||
|
||||
@@ -169,9 +169,10 @@ static Analyser::Static::ZX8081::Target::MemoryModel ZX8081MemoryModelFromSize(K
|
||||
std::unique_ptr<Target> target(new Target);
|
||||
target->machine = Analyser::Machine::AppleII;
|
||||
switch(model) {
|
||||
default: target->model = Target::Model::II; break;
|
||||
case CSMachineAppleIIModelAppleIIPlus: target->model = Target::Model::IIplus; break;
|
||||
case CSMachineAppleIIModelAppleIIe: target->model = Target::Model::IIe; break;
|
||||
default: target->model = Target::Model::II; break;
|
||||
case CSMachineAppleIIModelAppleIIPlus: target->model = Target::Model::IIplus; break;
|
||||
case CSMachineAppleIIModelAppleIIe: target->model = Target::Model::IIe; break;
|
||||
case CSMachineAppleIIModelAppleEnhancedIIe: target->model = Target::Model::EnhancedIIe; break;
|
||||
}
|
||||
switch(diskController) {
|
||||
default:
|
||||
|
||||
@@ -84,7 +84,7 @@ Gw
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="jli-ac-Sij">
|
||||
<rect key="frame" x="65" y="67" width="91" height="26"/>
|
||||
<rect key="frame" x="65" y="67" width="115" height="26"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Apple II" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="VBQ-JG-AeM" id="U6V-us-O2F">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@@ -93,6 +93,7 @@ Gw
|
||||
<menuItem title="Apple II" state="on" id="VBQ-JG-AeM"/>
|
||||
<menuItem title="Apple II+" tag="1" id="Yme-Wn-Obh"/>
|
||||
<menuItem title="Apple IIe" tag="2" id="AMt-WU-a0H"/>
|
||||
<menuItem title="Enhanced IIe" tag="3" id="kUz-FG-lqW"/>
|
||||
</items>
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
|
||||
@@ -131,6 +131,7 @@ class MachinePicker: NSObject {
|
||||
switch appleIIModelButton!.selectedTag() {
|
||||
case 1: model = .appleIIPlus
|
||||
case 2: model = .appleIIe
|
||||
case 3: model = .appleEnhancedIIe
|
||||
case 0: fallthrough
|
||||
default: model = .appleII
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ class MOS6502InterruptTests: XCTestCase {
|
||||
super.setUp()
|
||||
|
||||
// create a machine full of NOPs
|
||||
machine = CSTestMachine6502()
|
||||
machine = CSTestMachine6502(is65C02: false)
|
||||
for c in 0...65535 {
|
||||
machine.setValue(0xea, forAddress: UInt16(c))
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import XCTest
|
||||
class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
|
||||
private var endTime: UInt32 = 0
|
||||
private let machine = CSTestMachine6502()
|
||||
private let machine = CSTestMachine6502(is65C02: false)
|
||||
|
||||
func testImplied() {
|
||||
let code: [UInt8] = [
|
||||
|
||||
@@ -13,7 +13,7 @@ class AllSuiteATests: XCTestCase {
|
||||
func testAllSuiteA() {
|
||||
if let filename = Bundle(for: type(of: self)).path(forResource: "AllSuiteA", ofType: "bin") {
|
||||
if let allSuiteA = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
|
||||
let machine = CSTestMachine6502()
|
||||
let machine = CSTestMachine6502(is65C02: false)
|
||||
|
||||
machine.setData(allSuiteA, atAddress: 0x4000)
|
||||
machine.setValue(CSTestMachine6502JamOpcode, forAddress:0x45c0); // end
|
||||
|
||||
@@ -14,7 +14,7 @@ class BCDTest: XCTestCase, CSTestMachineTrapHandler {
|
||||
func testBCD() {
|
||||
if let filename = Bundle(for: type(of: self)).path(forResource: "BCDTEST_beeb", ofType: nil) {
|
||||
if let bcdTest = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
|
||||
let machine = CSTestMachine6502()
|
||||
let machine = CSTestMachine6502(is65C02: false)
|
||||
machine.trapHandler = self
|
||||
|
||||
machine.setData(bcdTest, atAddress: 0x2900)
|
||||
|
||||
@@ -32,8 +32,8 @@ class VanillaSerialPort: public Commodore::Serial::Port {
|
||||
_serialBus.reset(new ::Commodore::Serial::Bus);
|
||||
_serialPort.reset(new VanillaSerialPort);
|
||||
|
||||
_c1540.reset(new Commodore::C1540::Machine(Commodore::C1540::Machine::C1540));
|
||||
_c1540->set_rom_fetcher(CSROMFetcher());
|
||||
auto rom_fetcher = CSROMFetcher();
|
||||
_c1540.reset(new Commodore::C1540::Machine(Commodore::C1540::Personality::C1540, rom_fetcher));
|
||||
_c1540->set_serial_bus(_serialBus);
|
||||
Commodore::Serial::AttachPortAndBus(_serialPort, _serialBus);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,11 @@ extern const uint8_t CSTestMachine6502JamOpcode;
|
||||
|
||||
@interface CSTestMachine6502 : CSTestMachine
|
||||
|
||||
- (void)setData:(NSData *)data atAddress:(uint16_t)startAddress;
|
||||
- (nonnull instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
- (nonnull instancetype)initIs65C02:(BOOL)is65C02;
|
||||
|
||||
- (void)setData:(nonnull NSData *)data atAddress:(uint16_t)startAddress;
|
||||
- (void)runForNumberOfCycles:(int)cycles;
|
||||
|
||||
- (void)setValue:(uint8_t)value forAddress:(uint16_t)address;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#import "TestMachine6502.h"
|
||||
#include <stdint.h>
|
||||
#include "6502AllRAM.hpp"
|
||||
#include "../../../../Processors/6502/AllRAM/6502AllRAM.hpp"
|
||||
#import "TestMachine+ForSubclassEyesOnly.h"
|
||||
|
||||
const uint8_t CSTestMachine6502JamOpcode = CPU::MOS6502::JamOpcode;
|
||||
@@ -35,11 +35,12 @@ static CPU::MOS6502::Register registerForRegister(CSTestMachine6502Register reg)
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
- (instancetype)init {
|
||||
- (instancetype)initIs65C02:(BOOL)is65C02 {
|
||||
self = [super init];
|
||||
|
||||
if(self) {
|
||||
_processor = CPU::MOS6502::AllRAMProcessor::Processor();
|
||||
_processor = CPU::MOS6502::AllRAMProcessor::Processor(
|
||||
is65C02 ? CPU::MOS6502::Personality::PWDC65C02 : CPU::MOS6502::Personality::P6502);
|
||||
}
|
||||
|
||||
return self;
|
||||
|
||||
Binary file not shown.
@@ -11,10 +11,37 @@ import XCTest
|
||||
|
||||
class KlausDormannTests: XCTestCase {
|
||||
|
||||
func testKlausDormann() {
|
||||
fileprivate func runTest(resource: String, is65C02: Bool) -> UInt16 {
|
||||
if let filename = Bundle(for: type(of: self)).path(forResource: resource, ofType: "bin") {
|
||||
if let functionalTest = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
|
||||
let machine = CSTestMachine6502(is65C02: is65C02)
|
||||
|
||||
machine.setData(functionalTest, atAddress: 0)
|
||||
machine.setValue(0x400, for: .programCounter)
|
||||
|
||||
while true {
|
||||
let oldPC = machine.value(for: .lastOperationAddress)
|
||||
machine.runForNumber(ofCycles: 1000)
|
||||
let newPC = machine.value(for: .lastOperationAddress)
|
||||
|
||||
if newPC == oldPC {
|
||||
machine.runForNumber(ofCycles: 7)
|
||||
|
||||
let retestPC = machine.value(for: .lastOperationAddress)
|
||||
if retestPC == oldPC {
|
||||
return newPC
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
/// Runs Klaus Dorman's 6502 tests.
|
||||
func test6502() {
|
||||
func errorForTrapAddress(_ address: UInt16) -> String? {
|
||||
let hexAddress = String(format:"%04x", address)
|
||||
switch address {
|
||||
case 0x3399: return nil // success!
|
||||
|
||||
@@ -28,29 +55,63 @@ class KlausDormannTests: XCTestCase {
|
||||
case 0x26d2: return "ASL zpg,x produced incorrect flags"
|
||||
case 0x36c6: return "Unexpected RESET"
|
||||
|
||||
default: return "Unknown error at \(hexAddress)"
|
||||
case 0: return "Didn't find tests"
|
||||
|
||||
default: return "Unknown error at \(String(format:"%04x", address))"
|
||||
}
|
||||
}
|
||||
|
||||
if let filename = Bundle(for: type(of: self)).path(forResource: "6502_functional_test", ofType: "bin") {
|
||||
if let functionalTest = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
|
||||
let machine = CSTestMachine6502()
|
||||
let destination = runTest(resource: "6502_functional_test", is65C02: false)
|
||||
let error = errorForTrapAddress(destination)
|
||||
XCTAssert(error == nil, "Failed with error \(error!)")
|
||||
}
|
||||
|
||||
machine.setData(functionalTest, atAddress: 0)
|
||||
machine.setValue(0x400, for: .programCounter)
|
||||
/// Runs Klaus Dorman's 65C02 tests.
|
||||
func test65C02() {
|
||||
func errorForTrapAddress(_ address: UInt16) -> String? {
|
||||
switch address {
|
||||
case 0x24f1: return nil // success!
|
||||
|
||||
while true {
|
||||
let oldPC = machine.value(for: .lastOperationAddress)
|
||||
machine.runForNumber(ofCycles: 1000)
|
||||
let newPC = machine.value(for: .lastOperationAddress)
|
||||
case 0x0423: return "PHX: value of X not on stack page"
|
||||
case 0x0428: return "PHX: stack pointer not decremented"
|
||||
case 0x042d: return "PLY: didn't acquire value 0xaa from stack"
|
||||
case 0x0432: return "PLY: didn't acquire value 0x55 from stack"
|
||||
case 0x0437: return "PLY: stack pointer not incremented"
|
||||
case 0x043c: return "PLY: stack pointer not incremented"
|
||||
|
||||
if newPC == oldPC {
|
||||
let error = errorForTrapAddress(oldPC)
|
||||
XCTAssert(error == nil, "Failed with error \(error!)")
|
||||
return
|
||||
}
|
||||
}
|
||||
case 0x066a: return "BRA: branch not taken"
|
||||
case 0x0730: return "BBS: branch not taken"
|
||||
case 0x0733: return "BBR: branch taken"
|
||||
|
||||
case 0x2884: return "JMP (abs) exhibited 6502 page-crossing bug"
|
||||
case 0x16ca: return "JMP (abs, x) failed"
|
||||
|
||||
case 0x2785: return "BRK didn't clear the decimal mode flag"
|
||||
|
||||
case 0x177b: return "INC A didn't function"
|
||||
|
||||
case 0x1834: return "LDA (zp) acted as JAM"
|
||||
case 0x183a: return "STA (zp) acted as JAM"
|
||||
case 0x1849: return "LDA/STA (zp) left flags in incorrect state"
|
||||
|
||||
case 0x1983: return "STZ didn't store zero"
|
||||
|
||||
case 0x1b03: return "BIT didn't set flags correctly"
|
||||
case 0x1c6c: return "BIT immediate didn't set flags correctly"
|
||||
|
||||
case 0x1d88: return "TRB set Z flag incorrectly"
|
||||
case 0x1e7c: return "RMB set flags incorrectly"
|
||||
|
||||
case 0x2245: return "CMP (zero) didn't work"
|
||||
case 0x2506: return "Decimal ADC set flags incorrectly"
|
||||
|
||||
case 0: return "Didn't find tests"
|
||||
default: return "Unknown error at \(String(format:"%04x", address))"
|
||||
}
|
||||
}
|
||||
|
||||
let destination = runTest(resource: "65C02_extended_opcodes_test", is65C02: true)
|
||||
let error = errorForTrapAddress(destination)
|
||||
XCTAssert(error == nil, "Failed with error \(error!)")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,7 +200,7 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
if let filename = Bundle(for: type(of: self)).path(forResource: name, ofType: nil) {
|
||||
if let testData = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
|
||||
|
||||
machine = CSTestMachine6502()
|
||||
machine = CSTestMachine6502(is65C02: false)
|
||||
machine.trapHandler = self
|
||||
// machine.logActivity = true
|
||||
output = ""
|
||||
|
||||
@@ -33,6 +33,22 @@ enum Register {
|
||||
S
|
||||
};
|
||||
|
||||
/*
|
||||
The list of 6502 variants supported by this implementation.
|
||||
*/
|
||||
enum Personality {
|
||||
P6502, // the original [NMOS] 6502, replete with various undocumented instructions
|
||||
PNES6502, // the NES's 6502, which is like a 6502 but lacks decimal mode (though it retains the decimal flag)
|
||||
PSynertek65C02, // a 6502 extended with BRA, P[H/L][X/Y], STZ, TRB, TSB and the (zp) addressing mode and a few other additions
|
||||
PWDC65C02, // like the Synertek, but with BBR, BBS, RMB and SMB
|
||||
PRockwell65C02, // like the WDC, but with STP and WAI
|
||||
};
|
||||
|
||||
#define is_65c02(p) ((p) >= Personality::PSynertek65C02)
|
||||
#define has_bbrbbsrmbsmb(p) ((p) >= Personality::PWDC65C02)
|
||||
#define has_stpwai(p) ((p) >= Personality::PRockwell65C02)
|
||||
#define has_decimal_mode(p) ((p) != Personality::PNES6502)
|
||||
|
||||
/*
|
||||
Flags as defined on the 6502; can be used to decode the result of @c get_value_of_register(Flags) or to form a value for
|
||||
the corresponding set.
|
||||
@@ -110,6 +126,8 @@ class BusHandler {
|
||||
*/
|
||||
class ProcessorBase: public ProcessorStorage {
|
||||
public:
|
||||
ProcessorBase(Personality personality) : ProcessorStorage(personality) {}
|
||||
|
||||
/*!
|
||||
Gets the value of a register.
|
||||
|
||||
@@ -188,12 +206,12 @@ class ProcessorBase: public ProcessorStorage {
|
||||
can also nominate whether the processor includes support for the ready line. Declining to support the ready line
|
||||
can produce a minor runtime performance improvement.
|
||||
*/
|
||||
template <typename T, bool uses_ready_line> class Processor: public ProcessorBase {
|
||||
template <Personality personality, typename T, bool uses_ready_line> class Processor: public ProcessorBase {
|
||||
public:
|
||||
/*!
|
||||
Constructs an instance of the 6502 that will use @c bus_handler for all bus communications.
|
||||
*/
|
||||
Processor(T &bus_handler) : bus_handler_(bus_handler) {}
|
||||
Processor(T &bus_handler) : ProcessorBase(personality), bus_handler_(bus_handler) {}
|
||||
|
||||
/*!
|
||||
Runs the 6502 for a supplied number of cycles.
|
||||
|
||||
@@ -15,7 +15,7 @@ using namespace CPU::MOS6502;
|
||||
|
||||
namespace {
|
||||
|
||||
class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler {
|
||||
template <Personality personality> class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler {
|
||||
public:
|
||||
ConcreteAllRAMProcessor() :
|
||||
mos6502_(*this) {
|
||||
@@ -63,11 +63,20 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler {
|
||||
}
|
||||
|
||||
private:
|
||||
CPU::MOS6502::Processor<ConcreteAllRAMProcessor, false> mos6502_;
|
||||
CPU::MOS6502::Processor<personality, ConcreteAllRAMProcessor, false> mos6502_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
AllRAMProcessor *AllRAMProcessor::Processor() {
|
||||
return new ConcreteAllRAMProcessor;
|
||||
AllRAMProcessor *AllRAMProcessor::Processor(Personality personality) {
|
||||
#define Bind(p) case p: return new ConcreteAllRAMProcessor<p>();
|
||||
switch(personality) {
|
||||
default:
|
||||
Bind(Personality::P6502)
|
||||
Bind(Personality::PNES6502)
|
||||
Bind(Personality::PSynertek65C02)
|
||||
Bind(Personality::PWDC65C02)
|
||||
Bind(Personality::PRockwell65C02)
|
||||
}
|
||||
#undef Bind
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ class AllRAMProcessor:
|
||||
public ::CPU::AllRAMProcessor {
|
||||
|
||||
public:
|
||||
static AllRAMProcessor *Processor();
|
||||
static AllRAMProcessor *Processor(Personality personality);
|
||||
virtual ~AllRAMProcessor() {}
|
||||
|
||||
virtual void run_for(const Cycles cycles) = 0;
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
6502.hpp, but it's implementation stuff.
|
||||
*/
|
||||
|
||||
template <typename T, bool uses_ready_line> void Processor<T, uses_ready_line>::run_for(const Cycles cycles) {
|
||||
static const MicroOp doBranch[] = {
|
||||
template <Personality personality, typename T, bool uses_ready_line> void Processor<personality, T, uses_ready_line>::run_for(const Cycles cycles) {
|
||||
static const MicroOp do_branch[] = {
|
||||
CycleReadFromPC,
|
||||
CycleAddSignedOperandToPC,
|
||||
OperationMoveToNextProgram
|
||||
@@ -34,40 +34,62 @@ template <typename T, bool uses_ready_line> void Processor<T, uses_ready_line>::
|
||||
uint8_t *busValue = bus_value_;
|
||||
|
||||
#define checkSchedule(op) \
|
||||
if(!scheduled_program_counter_) {\
|
||||
if(interrupt_requests_) {\
|
||||
if(interrupt_requests_ & (InterruptRequestFlags::Reset | InterruptRequestFlags::PowerOn)) {\
|
||||
interrupt_requests_ &= ~InterruptRequestFlags::PowerOn;\
|
||||
scheduled_program_counter_ = get_reset_program();\
|
||||
} else if(interrupt_requests_ & InterruptRequestFlags::NMI) {\
|
||||
interrupt_requests_ &= ~InterruptRequestFlags::NMI;\
|
||||
scheduled_program_counter_ = get_nmi_program();\
|
||||
} else if(interrupt_requests_ & InterruptRequestFlags::IRQ) {\
|
||||
scheduled_program_counter_ = get_irq_program();\
|
||||
} \
|
||||
} else {\
|
||||
scheduled_program_counter_ = fetch_decode_execute;\
|
||||
}\
|
||||
op;\
|
||||
}
|
||||
if(!scheduled_program_counter_) {\
|
||||
if(interrupt_requests_) {\
|
||||
if(interrupt_requests_ & (InterruptRequestFlags::Reset | InterruptRequestFlags::PowerOn)) {\
|
||||
interrupt_requests_ &= ~InterruptRequestFlags::PowerOn;\
|
||||
scheduled_program_counter_ = get_reset_program();\
|
||||
} else if(interrupt_requests_ & InterruptRequestFlags::NMI) {\
|
||||
interrupt_requests_ &= ~InterruptRequestFlags::NMI;\
|
||||
scheduled_program_counter_ = get_nmi_program();\
|
||||
} else if(interrupt_requests_ & InterruptRequestFlags::IRQ) {\
|
||||
scheduled_program_counter_ = get_irq_program();\
|
||||
} \
|
||||
} else {\
|
||||
scheduled_program_counter_ = fetch_decode_execute;\
|
||||
}\
|
||||
op;\
|
||||
}
|
||||
|
||||
#define bus_access() \
|
||||
interrupt_requests_ = (interrupt_requests_ & ~InterruptRequestFlags::IRQ) | irq_request_history_; \
|
||||
irq_request_history_ = irq_line_ & inverse_interrupt_flag_; \
|
||||
number_of_cycles -= bus_handler_.perform_bus_operation(nextBusOperation, busAddress, busValue); \
|
||||
nextBusOperation = BusOperation::None; \
|
||||
if(number_of_cycles <= Cycles(0)) break;
|
||||
interrupt_requests_ = (interrupt_requests_ & ~InterruptRequestFlags::IRQ) | irq_request_history_; \
|
||||
irq_request_history_ = irq_line_ & inverse_interrupt_flag_; \
|
||||
number_of_cycles -= bus_handler_.perform_bus_operation(nextBusOperation, busAddress, busValue); \
|
||||
nextBusOperation = BusOperation::None; \
|
||||
if(number_of_cycles <= Cycles(0)) break;
|
||||
|
||||
checkSchedule();
|
||||
Cycles number_of_cycles = cycles + cycles_left_to_run_;
|
||||
|
||||
while(number_of_cycles > Cycles(0)) {
|
||||
|
||||
// Deal with a potential RDY state, if this 6502 has anything connected to ready.
|
||||
while(uses_ready_line && ready_is_active_ && number_of_cycles > Cycles(0)) {
|
||||
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
|
||||
}
|
||||
|
||||
if(!uses_ready_line || !ready_is_active_) {
|
||||
// Deal with a potential STP state, if this 6502 implements STP.
|
||||
while(has_stpwai(personality) && stop_is_active_ && number_of_cycles > Cycles(0)) {
|
||||
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
|
||||
if(interrupt_requests_ & InterruptRequestFlags::Reset) {
|
||||
stop_is_active_ = false;
|
||||
checkSchedule();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Deal with a potential WAI state, if this 6502 implements WAI.
|
||||
while(has_stpwai(personality) && wait_is_active_ && number_of_cycles > Cycles(0)) {
|
||||
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
|
||||
interrupt_requests_ |= (irq_line_ & inverse_interrupt_flag_);
|
||||
if(interrupt_requests_ & InterruptRequestFlags::NMI || irq_line_) {
|
||||
wait_is_active_ = false;
|
||||
checkSchedule();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if((!uses_ready_line || !ready_is_active_) && (!has_stpwai(personality) || (!wait_is_active_ && !stop_is_active_))) {
|
||||
if(nextBusOperation != BusOperation::None) {
|
||||
bus_access();
|
||||
}
|
||||
@@ -93,11 +115,25 @@ if(number_of_cycles <= Cycles(0)) break;
|
||||
} break;
|
||||
|
||||
case CycleFetchOperand:
|
||||
read_mem(operand_, pc_.full);
|
||||
// This is supposed to produce the 65C02's 1-cycle NOPs; they're
|
||||
// treated as a special case because they break the rule that
|
||||
// governs everything else on the 6502: that two bytes will always
|
||||
// be fetched.
|
||||
if(
|
||||
!is_65c02(personality) ||
|
||||
(operation_&7) != 3 ||
|
||||
operation_ == 0xcb ||
|
||||
operation_ == 0xdb
|
||||
) {
|
||||
read_mem(operand_, pc_.full);
|
||||
break;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
case OperationDecodeOperation:
|
||||
scheduled_program_counter_ = operations[operation_];
|
||||
scheduled_program_counter_ = operations_[operation_];
|
||||
continue;
|
||||
|
||||
case OperationMoveToNextProgram:
|
||||
@@ -115,6 +151,8 @@ if(number_of_cycles <= Cycles(0)) break;
|
||||
case CyclePushPCL: push(pc_.bytes.low); break;
|
||||
case CyclePushOperand: push(operand_); break;
|
||||
case CyclePushA: push(a_); break;
|
||||
case CyclePushX: push(x_); break;
|
||||
case CyclePushY: push(y_); break;
|
||||
case CycleNoWritePush: {
|
||||
uint16_t targetAddress = s_ | 0x100; s_--;
|
||||
read_mem(operand_, targetAddress);
|
||||
@@ -127,28 +165,44 @@ if(number_of_cycles <= Cycles(0)) break;
|
||||
case CycleReadFromPC: throwaway_read(pc_.full); break;
|
||||
|
||||
case OperationBRKPickVector:
|
||||
// NMI can usurp BRK-vector operations
|
||||
nextAddress.full = (interrupt_requests_ & InterruptRequestFlags::NMI) ? 0xfffa : 0xfffe;
|
||||
interrupt_requests_ &= ~InterruptRequestFlags::NMI; // TODO: this probably doesn't happen now?
|
||||
if(is_65c02(personality)) {
|
||||
nextAddress.full = 0xfffe;
|
||||
} else {
|
||||
// NMI can usurp BRK-vector operations on the pre-C 6502s.
|
||||
nextAddress.full = (interrupt_requests_ & InterruptRequestFlags::NMI) ? 0xfffa : 0xfffe;
|
||||
interrupt_requests_ &= ~InterruptRequestFlags::NMI;
|
||||
}
|
||||
continue;
|
||||
case OperationNMIPickVector: nextAddress.full = 0xfffa; continue;
|
||||
case OperationRSTPickVector: nextAddress.full = 0xfffc; continue;
|
||||
case CycleReadVectorLow: read_mem(pc_.bytes.low, nextAddress.full); break;
|
||||
case CycleReadVectorHigh: read_mem(pc_.bytes.high, nextAddress.full+1); break;
|
||||
case OperationSetI: inverse_interrupt_flag_ = 0; continue;
|
||||
case OperationSetIRQFlags:
|
||||
inverse_interrupt_flag_ = 0;
|
||||
if(is_65c02(personality)) decimal_flag_ = false;
|
||||
continue;
|
||||
case OperationSetNMIRSTFlags:
|
||||
if(is_65c02(personality)) decimal_flag_ = false;
|
||||
continue;
|
||||
|
||||
case CyclePullPCL: s_++; read_mem(pc_.bytes.low, s_ | 0x100); break;
|
||||
case CyclePullPCH: s_++; read_mem(pc_.bytes.high, s_ | 0x100); break;
|
||||
case CyclePullA: s_++; read_mem(a_, s_ | 0x100); break;
|
||||
case CyclePullX: s_++; read_mem(x_, s_ | 0x100); break;
|
||||
case CyclePullY: s_++; read_mem(y_, s_ | 0x100); break;
|
||||
case CyclePullOperand: s_++; read_mem(operand_, s_ | 0x100); break;
|
||||
case OperationSetFlagsFromOperand: set_flags(operand_); continue;
|
||||
case OperationSetOperandFromFlagsWithBRKSet: operand_ = get_flags() | Flag::Break; continue;
|
||||
case OperationSetOperandFromFlags: operand_ = get_flags(); continue;
|
||||
case OperationSetFlagsFromA: zero_result_ = negative_result_ = a_; continue;
|
||||
case OperationSetFlagsFromX: zero_result_ = negative_result_ = x_; continue;
|
||||
case OperationSetFlagsFromY: zero_result_ = negative_result_ = y_; continue;
|
||||
|
||||
case CycleIncrementPCAndReadStack: pc_.full++; throwaway_read(s_ | 0x100); break;
|
||||
case CycleReadPCLFromAddress: read_mem(pc_.bytes.low, address_.full); break;
|
||||
case CycleReadPCHFromAddress: address_.bytes.low++; read_mem(pc_.bytes.high, address_.full); break;
|
||||
case CycleIncrementPCAndReadStack: pc_.full++; throwaway_read(s_ | 0x100); break;
|
||||
case CycleReadPCLFromAddress: read_mem(pc_.bytes.low, address_.full); break;
|
||||
case CycleReadPCHFromAddressLowInc: address_.bytes.low++; read_mem(pc_.bytes.high, address_.full); break;
|
||||
case CycleReadPCHFromAddressFixed: if(!address_.bytes.low) address_.bytes.high++; read_mem(pc_.bytes.high, address_.full); break;
|
||||
case CycleReadPCHFromAddressInc: address_.full++; read_mem(pc_.bytes.high, address_.full); break;
|
||||
|
||||
case CycleReadAndIncrementPC: {
|
||||
uint16_t oldPC = pc_.full;
|
||||
@@ -156,13 +210,21 @@ if(number_of_cycles <= Cycles(0)) break;
|
||||
throwaway_read(oldPC);
|
||||
} break;
|
||||
|
||||
// MARK: - JAM
|
||||
// MARK: - JAM, WAI, STP
|
||||
|
||||
case CycleScheduleJam: {
|
||||
case OperationScheduleJam: {
|
||||
is_jammed_ = true;
|
||||
scheduled_program_counter_ = operations[CPU::MOS6502::JamOpcode];
|
||||
scheduled_program_counter_ = operations_[CPU::MOS6502::JamOpcode];
|
||||
} continue;
|
||||
|
||||
case OperationScheduleStop:
|
||||
stop_is_active_ = true;
|
||||
break;
|
||||
|
||||
case OperationScheduleWait:
|
||||
wait_is_active_ = true;
|
||||
break;
|
||||
|
||||
// MARK: - Bitwise
|
||||
|
||||
case OperationORA: a_ |= operand_; negative_result_ = zero_result_ = a_; continue;
|
||||
@@ -175,10 +237,12 @@ if(number_of_cycles <= Cycles(0)) break;
|
||||
case OperationLDX: x_ = negative_result_ = zero_result_ = operand_; continue;
|
||||
case OperationLDY: y_ = negative_result_ = zero_result_ = operand_; continue;
|
||||
case OperationLAX: a_ = x_ = negative_result_ = zero_result_ = operand_; continue;
|
||||
case OperationCopyOperandToA: a_ = operand_; continue;
|
||||
|
||||
case OperationSTA: operand_ = a_; continue;
|
||||
case OperationSTX: operand_ = x_; continue;
|
||||
case OperationSTY: operand_ = y_; continue;
|
||||
case OperationSTZ: operand_ = 0; continue;
|
||||
case OperationSAX: operand_ = a_ & x_; continue;
|
||||
case OperationSHA: operand_ = a_ & x_ & (address_.bytes.high+1); continue;
|
||||
case OperationSHX: operand_ = x_ & (address_.bytes.high+1); continue;
|
||||
@@ -208,20 +272,40 @@ if(number_of_cycles <= Cycles(0)) break;
|
||||
carry_flag_ = ((~temp16) >> 8)&1;
|
||||
} continue;
|
||||
|
||||
// MARK: - BIT
|
||||
// MARK: - BIT, TSB, TRB
|
||||
|
||||
case OperationBIT:
|
||||
zero_result_ = operand_ & a_;
|
||||
negative_result_ = operand_;
|
||||
overflow_flag_ = operand_&Flag::Overflow;
|
||||
continue;
|
||||
case OperationBITNoNV:
|
||||
zero_result_ = operand_ & a_;
|
||||
continue;
|
||||
case OperationTRB:
|
||||
zero_result_ = operand_ & a_;
|
||||
operand_ &= ~a_;
|
||||
continue;
|
||||
case OperationTSB:
|
||||
zero_result_ = operand_ & a_;
|
||||
operand_ |= a_;
|
||||
continue;
|
||||
|
||||
// MARK: - RMB and SMB
|
||||
|
||||
case OperationRMB:
|
||||
operand_ &= ~(1 << (operation_ >> 4));
|
||||
continue;
|
||||
case OperationSMB:
|
||||
operand_ |= 1 << ((operation_ >> 4)&7);
|
||||
continue;
|
||||
|
||||
// MARK: - ADC/SBC (and INS)
|
||||
|
||||
case OperationINS:
|
||||
operand_++; // deliberate fallthrough
|
||||
case OperationSBC:
|
||||
if(decimal_flag_) {
|
||||
if(decimal_flag_ && has_decimal_mode(personality)) {
|
||||
const uint16_t notCarry = carry_flag_ ^ 0x1;
|
||||
const uint16_t decimalResult = static_cast<uint16_t>(a_) - static_cast<uint16_t>(operand_) - notCarry;
|
||||
uint16_t temp16;
|
||||
@@ -239,6 +323,12 @@ if(number_of_cycles <= Cycles(0)) break;
|
||||
|
||||
carry_flag_ = (temp16 > 0xff) ? 0 : Flag::Carry;
|
||||
a_ = static_cast<uint8_t>(temp16);
|
||||
|
||||
if(is_65c02(personality)) {
|
||||
negative_result_ = zero_result_ = a_;
|
||||
read_mem(operand_, address_.full);
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
operand_ = ~operand_;
|
||||
@@ -246,7 +336,7 @@ if(number_of_cycles <= Cycles(0)) break;
|
||||
|
||||
// deliberate fallthrough
|
||||
case OperationADC:
|
||||
if(decimal_flag_) {
|
||||
if(decimal_flag_ && has_decimal_mode(personality)) {
|
||||
const uint16_t decimalResult = static_cast<uint16_t>(a_) + static_cast<uint16_t>(operand_) + static_cast<uint16_t>(carry_flag_);
|
||||
|
||||
uint8_t low_nibble = (a_ & 0xf) + (operand_ & 0xf) + carry_flag_;
|
||||
@@ -259,6 +349,12 @@ if(number_of_cycles <= Cycles(0)) break;
|
||||
carry_flag_ = (result >> 8) ? 1 : 0;
|
||||
a_ = static_cast<uint8_t>(result);
|
||||
zero_result_ = static_cast<uint8_t>(decimalResult);
|
||||
|
||||
if(is_65c02(personality)) {
|
||||
negative_result_ = zero_result_ = a_;
|
||||
read_mem(operand_, address_.full);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
const uint16_t result = static_cast<uint16_t>(a_) + static_cast<uint16_t>(operand_) + static_cast<uint16_t>(carry_flag_);
|
||||
overflow_flag_ = (( (result^a_)&(result^operand_) )&0x80) >> 1;
|
||||
@@ -345,6 +441,8 @@ if(number_of_cycles <= Cycles(0)) break;
|
||||
|
||||
case OperationINC: operand_++; negative_result_ = zero_result_ = operand_; continue;
|
||||
case OperationDEC: operand_--; negative_result_ = zero_result_ = operand_; continue;
|
||||
case OperationINA: a_++; negative_result_ = zero_result_ = a_; continue;
|
||||
case OperationDEA: a_--; negative_result_ = zero_result_ = a_; continue;
|
||||
case OperationINX: x_++; negative_result_ = zero_result_ = x_; continue;
|
||||
case OperationDEX: x_--; negative_result_ = zero_result_ = x_; continue;
|
||||
case OperationINY: y_++; negative_result_ = zero_result_ = y_; continue;
|
||||
@@ -368,32 +466,42 @@ if(number_of_cycles <= Cycles(0)) break;
|
||||
|
||||
// MARK: - Addressing Mode Work
|
||||
|
||||
#define page_crossing_stall_read() \
|
||||
if(is_65c02(personality)) { \
|
||||
throwaway_read(pc_.full - 1); \
|
||||
} else { \
|
||||
throwaway_read(address_.full); \
|
||||
}
|
||||
|
||||
case CycleAddXToAddressLow:
|
||||
nextAddress.full = address_.full + x_;
|
||||
address_.bytes.low = nextAddress.bytes.low;
|
||||
if(address_.bytes.high != nextAddress.bytes.high) {
|
||||
throwaway_read(address_.full);
|
||||
if(address_.bytes.high != nextAddress.bytes.high) {
|
||||
page_crossing_stall_read();
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case CycleAddXToAddressLowRead:
|
||||
nextAddress.full = address_.full + x_;
|
||||
address_.bytes.low = nextAddress.bytes.low;
|
||||
throwaway_read(address_.full);
|
||||
page_crossing_stall_read();
|
||||
break;
|
||||
case CycleAddYToAddressLow:
|
||||
nextAddress.full = address_.full + y_;
|
||||
address_.bytes.low = nextAddress.bytes.low;
|
||||
if(address_.bytes.high != nextAddress.bytes.high) {
|
||||
throwaway_read(address_.full);
|
||||
page_crossing_stall_read();
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case CycleAddYToAddressLowRead:
|
||||
nextAddress.full = address_.full + y_;
|
||||
address_.bytes.low = nextAddress.bytes.low;
|
||||
throwaway_read(address_.full);
|
||||
page_crossing_stall_read();
|
||||
break;
|
||||
|
||||
#undef page_crossing_stall_read
|
||||
|
||||
case OperationCorrectAddressHigh:
|
||||
address_.full = nextAddress.full;
|
||||
continue;
|
||||
@@ -405,6 +513,9 @@ if(number_of_cycles <= Cycles(0)) break;
|
||||
operand_ += x_;
|
||||
read_mem(address_.bytes.low, operand_);
|
||||
break;
|
||||
case CycleFetchAddressLowFromOperand:
|
||||
read_mem(address_.bytes.low, operand_);
|
||||
break;
|
||||
case CycleIncrementOperandFetchAddressHigh:
|
||||
operand_++;
|
||||
read_mem(address_.bytes.high, operand_);
|
||||
@@ -449,12 +560,14 @@ if(number_of_cycles <= Cycles(0)) break;
|
||||
case OperationIncrementPC: pc_.full++; continue;
|
||||
case CycleFetchOperandFromAddress: read_mem(operand_, address_.full); break;
|
||||
case CycleWriteOperandToAddress: write_mem(operand_, address_.full); break;
|
||||
case OperationCopyOperandFromA: operand_ = a_; continue;
|
||||
case OperationCopyOperandToA: a_ = operand_; continue;
|
||||
|
||||
// MARK: - Branching
|
||||
|
||||
#define BRA(condition) pc_.full++; if(condition) scheduled_program_counter_ = doBranch
|
||||
#define BRA(condition) \
|
||||
pc_.full++; \
|
||||
if(condition) { \
|
||||
scheduled_program_counter_ = do_branch; \
|
||||
}
|
||||
|
||||
case OperationBPL: BRA(!(negative_result_&0x80)); continue;
|
||||
case OperationBMI: BRA(negative_result_&0x80); continue;
|
||||
@@ -464,6 +577,9 @@ if(number_of_cycles <= Cycles(0)) break;
|
||||
case OperationBCS: BRA(carry_flag_); continue;
|
||||
case OperationBNE: BRA(zero_result_); continue;
|
||||
case OperationBEQ: BRA(!zero_result_); continue;
|
||||
case OperationBRA: BRA(true); continue;
|
||||
|
||||
#undef BRA
|
||||
|
||||
case CycleAddSignedOperandToPC:
|
||||
nextAddress.full = static_cast<uint16_t>(pc_.full + (int8_t)operand_);
|
||||
@@ -473,10 +589,46 @@ if(number_of_cycles <= Cycles(0)) break;
|
||||
pc_.full = nextAddress.full;
|
||||
throwaway_read(halfUpdatedPc);
|
||||
break;
|
||||
} else if(is_65c02(personality)) {
|
||||
// 65C02 modification to all branches: a branch that is taken but requires only a single cycle
|
||||
// to target its destination skips any pending interrupts.
|
||||
// Cf. http://forum.6502.org/viewtopic.php?f=4&t=1634
|
||||
scheduled_program_counter_ = fetch_decode_execute;
|
||||
}
|
||||
continue;
|
||||
|
||||
#undef BRA
|
||||
case CycleFetchFromHalfUpdatedPC: {
|
||||
uint16_t halfUpdatedPc = static_cast<uint16_t>(((pc_.bytes.low + (int8_t)operand_) & 0xff) | (pc_.bytes.high << 8));
|
||||
throwaway_read(halfUpdatedPc);
|
||||
} break;
|
||||
|
||||
case OperationAddSignedOperandToPC16:
|
||||
pc_.full = static_cast<uint16_t>(pc_.full + (int8_t)operand_);
|
||||
continue;
|
||||
|
||||
case OperationBBRBBS: {
|
||||
// To reach here, the 6502 has (i) read the operation; (ii) read the first operand;
|
||||
// and (iii) read from the corresponding zero page.
|
||||
const uint8_t mask = static_cast<uint8_t>(1 << ((operation_ >> 4)&7));
|
||||
if((operand_ & mask) == ((operation_ & 0x80) ? mask : 0)) {
|
||||
static const MicroOp do_branch[] = {
|
||||
CycleFetchOperand, // Fetch offset.
|
||||
OperationIncrementPC,
|
||||
CycleFetchFromHalfUpdatedPC,
|
||||
OperationAddSignedOperandToPC16,
|
||||
OperationMoveToNextProgram
|
||||
};
|
||||
scheduled_program_counter_ = do_branch;
|
||||
} else {
|
||||
static const MicroOp do_not_branch[] = {
|
||||
CycleFetchOperand,
|
||||
OperationIncrementPC,
|
||||
CycleFetchFromHalfUpdatedPC,
|
||||
OperationMoveToNextProgram
|
||||
};
|
||||
scheduled_program_counter_ = do_not_branch;
|
||||
}
|
||||
} break;
|
||||
|
||||
// MARK: - Transfers
|
||||
|
||||
@@ -517,7 +669,10 @@ if(number_of_cycles <= Cycles(0)) break;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(uses_ready_line && ready_line_is_enabled_ && isReadOperation(nextBusOperation)) {
|
||||
if(has_stpwai(personality) && (stop_is_active_ || wait_is_active_)) {
|
||||
break;
|
||||
}
|
||||
if(uses_ready_line && ready_line_is_enabled_ && (is_65c02(personality) || isReadOperation(nextBusOperation))) {
|
||||
ready_is_active_ = true;
|
||||
break;
|
||||
}
|
||||
@@ -535,7 +690,7 @@ if(number_of_cycles <= Cycles(0)) break;
|
||||
bus_handler_.flush();
|
||||
}
|
||||
|
||||
template <typename T, bool uses_ready_line> void Processor<T, uses_ready_line>::set_ready_line(bool active) {
|
||||
template <Personality personality, typename T, bool uses_ready_line> void Processor<personality, T, uses_ready_line>::set_ready_line(bool active) {
|
||||
assert(uses_ready_line);
|
||||
if(active) {
|
||||
ready_line_is_enabled_ = true;
|
||||
@@ -583,6 +738,7 @@ inline const ProcessorStorage::MicroOp *ProcessorStorage::get_reset_program() {
|
||||
CycleNoWritePush,
|
||||
OperationRSTPickVector,
|
||||
CycleNoWritePush,
|
||||
OperationSetNMIRSTFlags,
|
||||
CycleReadVectorLow,
|
||||
CycleReadVectorHigh,
|
||||
OperationMoveToNextProgram
|
||||
@@ -599,7 +755,7 @@ inline const ProcessorStorage::MicroOp *ProcessorStorage::get_irq_program() {
|
||||
OperationBRKPickVector,
|
||||
OperationSetOperandFromFlags,
|
||||
CyclePushOperand,
|
||||
OperationSetI,
|
||||
OperationSetIRQFlags,
|
||||
CycleReadVectorLow,
|
||||
CycleReadVectorHigh,
|
||||
OperationMoveToNextProgram
|
||||
@@ -616,6 +772,7 @@ inline const ProcessorStorage::MicroOp *ProcessorStorage::get_nmi_program() {
|
||||
OperationNMIPickVector,
|
||||
OperationSetOperandFromFlags,
|
||||
CyclePushOperand,
|
||||
OperationSetNMIRSTFlags,
|
||||
CycleReadVectorLow,
|
||||
CycleReadVectorHigh,
|
||||
OperationMoveToNextProgram
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#include "../6502.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
using namespace CPU::MOS6502;
|
||||
|
||||
#define Program(...) {__VA_ARGS__, OperationMoveToNextProgram}
|
||||
@@ -20,13 +22,14 @@ using namespace CPU::MOS6502;
|
||||
#define Zero OperationLoadAddressZeroPage
|
||||
#define ZeroX CycleLoadAddessZeroX
|
||||
#define ZeroY CycleLoadAddessZeroY
|
||||
#define ZeroIndirect OperationLoadAddressZeroPage, CycleFetchAddressLowFromOperand, CycleIncrementOperandFetchAddressHigh
|
||||
#define IndexedIndirect CycleIncrementPCFetchAddressLowFromOperand, CycleAddXToOperandFetchAddressLow, CycleIncrementOperandFetchAddressHigh
|
||||
#define IndirectIndexedr CycleIncrementPCFetchAddressLowFromOperand, CycleIncrementOperandFetchAddressHigh, CycleAddYToAddressLow, OperationCorrectAddressHigh
|
||||
#define IndirectIndexed CycleIncrementPCFetchAddressLowFromOperand, CycleIncrementOperandFetchAddressHigh, CycleAddYToAddressLowRead, OperationCorrectAddressHigh
|
||||
|
||||
#define Read(...) CycleFetchOperandFromAddress, __VA_ARGS__
|
||||
#define Write(...) __VA_ARGS__, CycleWriteOperandToAddress
|
||||
#define ReadModifyWrite(...) CycleFetchOperandFromAddress, CycleWriteOperandToAddress, __VA_ARGS__, CycleWriteOperandToAddress
|
||||
#define ReadModifyWrite(...) CycleFetchOperandFromAddress, is_65c02(personality) ? CycleFetchOperandFromAddress : CycleWriteOperandToAddress, __VA_ARGS__, CycleWriteOperandToAddress
|
||||
|
||||
#define AbsoluteRead(op) Program(Absolute, Read(op))
|
||||
#define AbsoluteXRead(op) Program(AbsoluteXr, Read(op))
|
||||
@@ -34,6 +37,7 @@ using namespace CPU::MOS6502;
|
||||
#define ZeroRead(...) Program(Zero, Read(__VA_ARGS__))
|
||||
#define ZeroXRead(op) Program(ZeroX, Read(op))
|
||||
#define ZeroYRead(op) Program(ZeroY, Read(op))
|
||||
#define ZeroIndirectRead(op) Program(ZeroIndirect, Read(op))
|
||||
#define IndexedIndirectRead(op) Program(IndexedIndirect, Read(op))
|
||||
#define IndirectIndexedRead(op) Program(IndirectIndexedr, Read(op))
|
||||
|
||||
@@ -43,6 +47,7 @@ using namespace CPU::MOS6502;
|
||||
#define ZeroWrite(op) Program(Zero, Write(op))
|
||||
#define ZeroXWrite(op) Program(ZeroX, Write(op))
|
||||
#define ZeroYWrite(op) Program(ZeroY, Write(op))
|
||||
#define ZeroIndirectWrite(op) Program(ZeroIndirect, Write(op))
|
||||
#define IndexedIndirectWrite(op) Program(IndexedIndirect, Write(op))
|
||||
#define IndirectIndexedWrite(op) Program(IndirectIndexed, Write(op))
|
||||
|
||||
@@ -55,8 +60,11 @@ using namespace CPU::MOS6502;
|
||||
#define IndexedIndirectReadModifyWrite(...) Program(IndexedIndirect, ReadModifyWrite(__VA_ARGS__))
|
||||
#define IndirectIndexedReadModifyWrite(...) Program(IndirectIndexed, ReadModifyWrite(__VA_ARGS__))
|
||||
|
||||
#define FastAbsoluteXReadModifyWrite(...) Program(AbsoluteXr, ReadModifyWrite(__VA_ARGS__))
|
||||
#define FastAbsoluteYReadModifyWrite(...) Program(AbsoluteYr, ReadModifyWrite(__VA_ARGS__))
|
||||
|
||||
#define Immediate(op) Program(OperationIncrementPC, op)
|
||||
#define Implied(op) Program(OperationCopyOperandFromA, op, OperationCopyOperandToA)
|
||||
#define Implied(op) Program(OperationSTA, op, OperationCopyOperandToA)
|
||||
|
||||
#define ZeroNop() Program(Zero, CycleFetchOperandFromAddress)
|
||||
#define ZeroXNop() Program(ZeroX, CycleFetchOperandFromAddress)
|
||||
@@ -65,189 +73,315 @@ using namespace CPU::MOS6502;
|
||||
#define ImpliedNop() {OperationMoveToNextProgram}
|
||||
#define ImmediateNop() Program(OperationIncrementPC)
|
||||
|
||||
#define JAM {CycleFetchOperand, CycleScheduleJam}
|
||||
#define JAM {CycleFetchOperand, OperationScheduleJam}
|
||||
|
||||
const ProcessorStorage::MicroOp ProcessorStorage::operations[256][10] = {
|
||||
/* 0x00 BRK */ Program(CycleIncPCPushPCH, CyclePushPCL, OperationBRKPickVector, OperationSetOperandFromFlagsWithBRKSet, CyclePushOperand, OperationSetI, CycleReadVectorLow, CycleReadVectorHigh),
|
||||
/* 0x01 ORA x, ind */ IndexedIndirectRead(OperationORA),
|
||||
/* 0x02 JAM */ JAM, /* 0x03 ASO x, ind */ IndexedIndirectReadModifyWrite(OperationASO),
|
||||
/* 0x04 NOP zpg */ ZeroNop(), /* 0x05 ORA zpg */ ZeroRead(OperationORA),
|
||||
/* 0x06 ASL zpg */ ZeroReadModifyWrite(OperationASL), /* 0x07 ASO zpg */ ZeroReadModifyWrite(OperationASO),
|
||||
/* 0x08 PHP */ Program(OperationSetOperandFromFlagsWithBRKSet, CyclePushOperand),
|
||||
/* 0x09 ORA # */ Immediate(OperationORA),
|
||||
/* 0x0a ASL A */ Implied(OperationASL), /* 0x0b ANC # */ Immediate(OperationANC),
|
||||
/* 0x0c NOP abs */ AbsoluteNop(), /* 0x0d ORA abs */ AbsoluteRead(OperationORA),
|
||||
/* 0x0e ASL abs */ AbsoluteReadModifyWrite(OperationASL), /* 0x0f ASO abs */ AbsoluteReadModifyWrite(OperationASO),
|
||||
/* 0x10 BPL */ Program(OperationBPL), /* 0x11 ORA ind, y */ IndirectIndexedRead(OperationORA),
|
||||
/* 0x12 JAM */ JAM, /* 0x13 ASO ind, y */ IndirectIndexedReadModifyWrite(OperationASO),
|
||||
/* 0x14 NOP zpg, x */ ZeroXNop(), /* 0x15 ORA zpg, x */ ZeroXRead(OperationORA),
|
||||
/* 0x16 ASL zpg, x */ ZeroXReadModifyWrite(OperationASL), /* 0x17 ASO zpg, x */ ZeroXReadModifyWrite(OperationASO),
|
||||
/* 0x18 CLC */ Program(OperationCLC), /* 0x19 ORA abs, y */ AbsoluteYRead(OperationORA),
|
||||
/* 0x1a NOP # */ ImpliedNop(), /* 0x1b ASO abs, y */ AbsoluteYReadModifyWrite(OperationASO),
|
||||
/* 0x1c NOP abs, x */ AbsoluteXNop(), /* 0x1d ORA abs, x */ AbsoluteXRead(OperationORA),
|
||||
/* 0x1e ASL abs, x */ AbsoluteXReadModifyWrite(OperationASL), /* 0x1f ASO abs, x */ AbsoluteXReadModifyWrite(OperationASO),
|
||||
/* 0x20 JSR abs */ Program(CycleIncrementPCAndReadStack, CyclePushPCH, CyclePushPCL, CycleReadPCHLoadPCL),
|
||||
/* 0x21 AND x, ind */ IndexedIndirectRead(OperationAND),
|
||||
/* 0x22 JAM */ JAM, /* 0x23 RLA x, ind */ IndexedIndirectReadModifyWrite(OperationRLA),
|
||||
/* 0x24 BIT zpg */ ZeroRead(OperationBIT), /* 0x25 AND zpg */ ZeroRead(OperationAND),
|
||||
/* 0x26 ROL zpg */ ZeroReadModifyWrite(OperationROL), /* 0x27 RLA zpg */ ZeroReadModifyWrite(OperationRLA),
|
||||
/* 0x28 PLP */ Program(CycleReadFromS, CyclePullOperand, OperationSetFlagsFromOperand),
|
||||
/* 0x29 AND A # */ Immediate(OperationAND),
|
||||
/* 0x2a ROL A */ Implied(OperationROL), /* 0x2b ANC # */ Immediate(OperationANC),
|
||||
/* 0x2c BIT abs */ AbsoluteRead(OperationBIT), /* 0x2d AND abs */ AbsoluteRead(OperationAND),
|
||||
/* 0x2e ROL abs */ AbsoluteReadModifyWrite(OperationROL), /* 0x2f RLA abs */ AbsoluteReadModifyWrite(OperationRLA),
|
||||
/* 0x30 BMI */ Program(OperationBMI), /* 0x31 AND ind, y */ IndirectIndexedRead(OperationAND),
|
||||
/* 0x32 JAM */ JAM, /* 0x33 RLA ind, y */ IndirectIndexedReadModifyWrite(OperationRLA),
|
||||
/* 0x34 NOP zpg, x */ ZeroXNop(), /* 0x35 AND zpg, x */ ZeroXRead(OperationAND),
|
||||
/* 0x36 ROL zpg, x */ ZeroXReadModifyWrite(OperationROL), /* 0x37 RLA zpg, x */ ZeroXReadModifyWrite(OperationRLA),
|
||||
/* 0x38 SEC */ Program(OperationSEC), /* 0x39 AND abs, y */ AbsoluteYRead(OperationAND),
|
||||
/* 0x3a NOP # */ ImpliedNop(), /* 0x3b RLA abs, y */ AbsoluteYReadModifyWrite(OperationRLA),
|
||||
/* 0x3c NOP abs, x */ AbsoluteXNop(), /* 0x3d AND abs, x */ AbsoluteXRead(OperationAND),
|
||||
/* 0x3e ROL abs, x */ AbsoluteXReadModifyWrite(OperationROL), /* 0x3f RLA abs, x */ AbsoluteXReadModifyWrite(OperationRLA),
|
||||
/* 0x40 RTI */ Program(CycleReadFromS, CyclePullOperand, OperationSetFlagsFromOperand, CyclePullPCL, CyclePullPCH),
|
||||
/* 0x41 EOR x, ind */ IndexedIndirectRead(OperationEOR),
|
||||
/* 0x42 JAM */ JAM, /* 0x43 LSE x, ind */ IndexedIndirectReadModifyWrite(OperationLSE),
|
||||
/* 0x44 NOP zpg */ ZeroNop(), /* 0x45 EOR zpg */ ZeroRead(OperationEOR),
|
||||
/* 0x46 LSR zpg */ ZeroReadModifyWrite(OperationLSR), /* 0x47 LSE zpg */ ZeroReadModifyWrite(OperationLSE),
|
||||
/* 0x48 PHA */ Program(CyclePushA), /* 0x49 EOR # */ Immediate(OperationEOR),
|
||||
/* 0x4a LSR A */ Implied(OperationLSR), /* 0x4b ASR A */ Immediate(OperationASR),
|
||||
/* 0x4c JMP abs */ Program(CycleIncrementPCReadPCHLoadPCL), /* 0x4d EOR abs */ AbsoluteRead(OperationEOR),
|
||||
/* 0x4e LSR abs */ AbsoluteReadModifyWrite(OperationLSR), /* 0x4f LSE abs */ AbsoluteReadModifyWrite(OperationLSE),
|
||||
/* 0x50 BVC */ Program(OperationBVC), /* 0x51 EOR ind, y */ IndirectIndexedRead(OperationEOR),
|
||||
/* 0x52 JAM */ JAM, /* 0x53 LSE ind, y */ IndirectIndexedReadModifyWrite(OperationLSE),
|
||||
/* 0x54 NOP zpg, x */ ZeroXNop(), /* 0x55 EOR zpg, x */ ZeroXRead(OperationEOR),
|
||||
/* 0x56 LSR zpg, x */ ZeroXReadModifyWrite(OperationLSR), /* 0x57 LSE zpg, x */ ZeroXReadModifyWrite(OperationLSE),
|
||||
/* 0x58 CLI */ Program(OperationCLI), /* 0x59 EOR abs, y */ AbsoluteYRead(OperationEOR),
|
||||
/* 0x5a NOP # */ ImpliedNop(), /* 0x5b LSE abs, y */ AbsoluteYReadModifyWrite(OperationLSE),
|
||||
/* 0x5c NOP abs, x */ AbsoluteXNop(), /* 0x5d EOR abs, x */ AbsoluteXRead(OperationEOR),
|
||||
/* 0x5e LSR abs, x */ AbsoluteXReadModifyWrite(OperationLSR), /* 0x5f LSE abs, x */ AbsoluteXReadModifyWrite(OperationLSE),
|
||||
/* 0x60 RTS */ Program(CycleReadFromS, CyclePullPCL, CyclePullPCH, CycleReadAndIncrementPC),
|
||||
/* 0x61 ADC x, ind */ IndexedIndirectRead(OperationADC),
|
||||
/* 0x62 JAM */ JAM, /* 0x63 RRA x, ind */ IndexedIndirectReadModifyWrite(OperationRRA, OperationADC),
|
||||
/* 0x64 NOP zpg */ ZeroNop(), /* 0x65 ADC zpg */ ZeroRead(OperationADC),
|
||||
/* 0x66 ROR zpg */ ZeroReadModifyWrite(OperationROR), /* 0x67 RRA zpg */ ZeroReadModifyWrite(OperationRRA, OperationADC),
|
||||
/* 0x68 PLA */ Program(CycleReadFromS, CyclePullA, OperationSetFlagsFromA), /* 0x69 ADC # */ Immediate(OperationADC),
|
||||
/* 0x6a ROR A */ Implied(OperationROR), /* 0x6b ARR # */ Immediate(OperationARR),
|
||||
/* 0x6c JMP (abs) */ Program(CycleReadAddressHLoadAddressL, CycleReadPCLFromAddress, CycleReadPCHFromAddress),
|
||||
/* 0x6d ADC abs */ AbsoluteRead(OperationADC),
|
||||
/* 0x6e ROR abs */ AbsoluteReadModifyWrite(OperationROR), /* 0x6f RRA abs */ AbsoluteReadModifyWrite(OperationRRA, OperationADC),
|
||||
/* 0x70 BVS */ Program(OperationBVS), /* 0x71 ADC ind, y */ IndirectIndexedRead(OperationADC),
|
||||
/* 0x72 JAM */ JAM, /* 0x73 RRA ind, y */ IndirectIndexedReadModifyWrite(OperationRRA, OperationADC),
|
||||
/* 0x74 NOP zpg, x */ ZeroXNop(), /* 0x75 ADC zpg, x */ ZeroXRead(OperationADC),
|
||||
/* 0x76 ROR zpg, x */ ZeroXReadModifyWrite(OperationROR), /* 0x77 RRA zpg, x */ ZeroXReadModifyWrite(OperationRRA, OperationADC),
|
||||
/* 0x78 SEI */ Program(OperationSEI), /* 0x79 ADC abs, y */ AbsoluteYRead(OperationADC),
|
||||
/* 0x7a NOP # */ ImpliedNop(), /* 0x7b RRA abs, y */ AbsoluteYReadModifyWrite(OperationRRA, OperationADC),
|
||||
/* 0x7c NOP abs, x */ AbsoluteXNop(), /* 0x7d ADC abs, x */ AbsoluteXRead(OperationADC),
|
||||
/* 0x7e ROR abs, x */ AbsoluteXReadModifyWrite(OperationROR), /* 0x7f RRA abs, x */ AbsoluteXReadModifyWrite(OperationRRA, OperationADC),
|
||||
/* 0x80 NOP # */ ImmediateNop(), /* 0x81 STA x, ind */ IndexedIndirectWrite(OperationSTA),
|
||||
/* 0x82 NOP # */ ImmediateNop(), /* 0x83 SAX x, ind */ IndexedIndirectWrite(OperationSAX),
|
||||
/* 0x84 STY zpg */ ZeroWrite(OperationSTY), /* 0x85 STA zpg */ ZeroWrite(OperationSTA),
|
||||
/* 0x86 STX zpg */ ZeroWrite(OperationSTX), /* 0x87 SAX zpg */ ZeroWrite(OperationSAX),
|
||||
/* 0x88 DEY */ Program(OperationDEY), /* 0x89 NOP # */ ImmediateNop(),
|
||||
/* 0x8a TXA */ Program(OperationTXA), /* 0x8b ANE # */ Immediate(OperationANE),
|
||||
/* 0x8c STY abs */ AbsoluteWrite(OperationSTY), /* 0x8d STA abs */ AbsoluteWrite(OperationSTA),
|
||||
/* 0x8e STX abs */ AbsoluteWrite(OperationSTX), /* 0x8f SAX abs */ AbsoluteWrite(OperationSAX),
|
||||
/* 0x90 BCC */ Program(OperationBCC), /* 0x91 STA ind, y */ IndirectIndexedWrite(OperationSTA),
|
||||
/* 0x92 JAM */ JAM, /* 0x93 SHA ind, y */ IndirectIndexedWrite(OperationSHA),
|
||||
/* 0x94 STY zpg, x */ ZeroXWrite(OperationSTY), /* 0x95 STA zpg, x */ ZeroXWrite(OperationSTA),
|
||||
/* 0x96 STX zpg, y */ ZeroYWrite(OperationSTX), /* 0x97 SAX zpg, y */ ZeroYWrite(OperationSAX),
|
||||
/* 0x98 TYA */ Program(OperationTYA), /* 0x99 STA abs, y */ AbsoluteYWrite(OperationSTA),
|
||||
/* 0x9a TXS */ Program(OperationTXS), /* 0x9b SHS abs, y */ AbsoluteYWrite(OperationSHS),
|
||||
/* 0x9c SHY abs, x */ AbsoluteXWrite(OperationSHY), /* 0x9d STA abs, x */ AbsoluteXWrite(OperationSTA),
|
||||
/* 0x9e SHX abs, y */ AbsoluteYWrite(OperationSHX), /* 0x9f SHA abs, y */ AbsoluteYWrite(OperationSHA),
|
||||
/* 0xa0 LDY # */ Immediate(OperationLDY), /* 0xa1 LDA x, ind */ IndexedIndirectRead(OperationLDA),
|
||||
/* 0xa2 LDX # */ Immediate(OperationLDX), /* 0xa3 LAX x, ind */ IndexedIndirectRead(OperationLAX),
|
||||
/* 0xa4 LDY zpg */ ZeroRead(OperationLDY), /* 0xa5 LDA zpg */ ZeroRead(OperationLDA),
|
||||
/* 0xa6 LDX zpg */ ZeroRead(OperationLDX), /* 0xa7 LAX zpg */ ZeroRead(OperationLAX),
|
||||
/* 0xa8 TAY */ Program(OperationTAY), /* 0xa9 LDA # */ Immediate(OperationLDA),
|
||||
/* 0xaa TAX */ Program(OperationTAX), /* 0xab LXA # */ Immediate(OperationLXA),
|
||||
/* 0xac LDY abs */ AbsoluteRead(OperationLDY), /* 0xad LDA abs */ AbsoluteRead(OperationLDA),
|
||||
/* 0xae LDX abs */ AbsoluteRead(OperationLDX), /* 0xaf LAX abs */ AbsoluteRead(OperationLAX),
|
||||
/* 0xb0 BCS */ Program(OperationBCS), /* 0xb1 LDA ind, y */ IndirectIndexedRead(OperationLDA),
|
||||
/* 0xb2 JAM */ JAM, /* 0xb3 LAX ind, y */ IndirectIndexedRead(OperationLAX),
|
||||
/* 0xb4 LDY zpg, x */ ZeroXRead(OperationLDY), /* 0xb5 LDA zpg, x */ ZeroXRead(OperationLDA),
|
||||
/* 0xb6 LDX zpg, y */ ZeroYRead(OperationLDX), /* 0xb7 LAX zpg, x */ ZeroYRead(OperationLAX),
|
||||
/* 0xb8 CLV */ Program(OperationCLV), /* 0xb9 LDA abs, y */ AbsoluteYRead(OperationLDA),
|
||||
/* 0xba TSX */ Program(OperationTSX), /* 0xbb LAS abs, y */ AbsoluteYRead(OperationLAS),
|
||||
/* 0xbc LDY abs, x */ AbsoluteXRead(OperationLDY), /* 0xbd LDA abs, x */ AbsoluteXRead(OperationLDA),
|
||||
/* 0xbe LDX abs, y */ AbsoluteYRead(OperationLDX), /* 0xbf LAX abs, y */ AbsoluteYRead(OperationLAX),
|
||||
/* 0xc0 CPY # */ Immediate(OperationCPY), /* 0xc1 CMP x, ind */ IndexedIndirectRead(OperationCMP),
|
||||
/* 0xc2 NOP # */ ImmediateNop(), /* 0xc3 DCP x, ind */ IndexedIndirectReadModifyWrite(OperationDecrementOperand, OperationCMP),
|
||||
/* 0xc4 CPY zpg */ ZeroRead(OperationCPY), /* 0xc5 CMP zpg */ ZeroRead(OperationCMP),
|
||||
/* 0xc6 DEC zpg */ ZeroReadModifyWrite(OperationDEC), /* 0xc7 DCP zpg */ ZeroReadModifyWrite(OperationDecrementOperand, OperationCMP),
|
||||
/* 0xc8 INY */ Program(OperationINY), /* 0xc9 CMP # */ Immediate(OperationCMP),
|
||||
/* 0xca DEX */ Program(OperationDEX), /* 0xcb ARR # */ Immediate(OperationSBX),
|
||||
/* 0xcc CPY abs */ AbsoluteRead(OperationCPY), /* 0xcd CMP abs */ AbsoluteRead(OperationCMP),
|
||||
/* 0xce DEC abs */ AbsoluteReadModifyWrite(OperationDEC), /* 0xcf DCP abs */ AbsoluteReadModifyWrite(OperationDecrementOperand, OperationCMP),
|
||||
/* 0xd0 BNE */ Program(OperationBNE), /* 0xd1 CMP ind, y */ IndirectIndexedRead(OperationCMP),
|
||||
/* 0xd2 JAM */ JAM, /* 0xd3 DCP ind, y */ IndirectIndexedReadModifyWrite(OperationDecrementOperand, OperationCMP),
|
||||
/* 0xd4 NOP zpg, x */ ZeroXNop(), /* 0xd5 CMP zpg, x */ ZeroXRead(OperationCMP),
|
||||
/* 0xd6 DEC zpg, x */ ZeroXReadModifyWrite(OperationDEC), /* 0xd7 DCP zpg, x */ ZeroXReadModifyWrite(OperationDecrementOperand, OperationCMP),
|
||||
/* 0xd8 CLD */ Program(OperationCLD), /* 0xd9 CMP abs, y */ AbsoluteYRead(OperationCMP),
|
||||
/* 0xda NOP # */ ImpliedNop(), /* 0xdb DCP abs, y */ AbsoluteYReadModifyWrite(OperationDecrementOperand, OperationCMP),
|
||||
/* 0xdc NOP abs, x */ AbsoluteXNop(), /* 0xdd CMP abs, x */ AbsoluteXRead(OperationCMP),
|
||||
/* 0xde DEC abs, x */ AbsoluteXReadModifyWrite(OperationDEC), /* 0xdf DCP abs, x */ AbsoluteXReadModifyWrite(OperationDecrementOperand, OperationCMP),
|
||||
/* 0xe0 CPX # */ Immediate(OperationCPX), /* 0xe1 SBC x, ind */ IndexedIndirectRead(OperationSBC),
|
||||
/* 0xe2 NOP # */ ImmediateNop(), /* 0xe3 INS x, ind */ IndexedIndirectReadModifyWrite(OperationINS),
|
||||
/* 0xe4 CPX zpg */ ZeroRead(OperationCPX), /* 0xe5 SBC zpg */ ZeroRead(OperationSBC),
|
||||
/* 0xe6 INC zpg */ ZeroReadModifyWrite(OperationINC), /* 0xe7 INS zpg */ ZeroReadModifyWrite(OperationINS),
|
||||
/* 0xe8 INX */ Program(OperationINX), /* 0xe9 SBC # */ Immediate(OperationSBC),
|
||||
/* 0xea NOP # */ ImpliedNop(), /* 0xeb SBC # */ Immediate(OperationSBC),
|
||||
/* 0xec CPX abs */ AbsoluteRead(OperationCPX), /* 0xed SBC abs */ AbsoluteRead(OperationSBC),
|
||||
/* 0xee INC abs */ AbsoluteReadModifyWrite(OperationINC), /* 0xef INS abs */ AbsoluteReadModifyWrite(OperationINS),
|
||||
/* 0xf0 BEQ */ Program(OperationBEQ), /* 0xf1 SBC ind, y */ IndirectIndexedRead(OperationSBC),
|
||||
/* 0xf2 JAM */ JAM, /* 0xf3 INS ind, y */ IndirectIndexedReadModifyWrite(OperationINS),
|
||||
/* 0xf4 NOP zpg, x */ ZeroXNop(), /* 0xf5 SBC zpg, x */ ZeroXRead(OperationSBC),
|
||||
/* 0xf6 INC zpg, x */ ZeroXReadModifyWrite(OperationINC), /* 0xf7 INS zpg, x */ ZeroXReadModifyWrite(OperationINS),
|
||||
/* 0xf8 SED */ Program(OperationSED), /* 0xf9 SBC abs, y */ AbsoluteYRead(OperationSBC),
|
||||
/* 0xfa NOP # */ ImpliedNop(), /* 0xfb INS abs, y */ AbsoluteYReadModifyWrite(OperationINS),
|
||||
/* 0xfc NOP abs, x */ AbsoluteXNop(), /* 0xfd SBC abs, x */ AbsoluteXRead(OperationSBC),
|
||||
/* 0xfe INC abs, x */ AbsoluteXReadModifyWrite(OperationINC), /* 0xff INS abs, x */ AbsoluteXReadModifyWrite(OperationINS),
|
||||
};
|
||||
|
||||
#undef Program
|
||||
#undef Absolute
|
||||
#undef AbsoluteX
|
||||
#undef AbsoluteY
|
||||
#undef Zero
|
||||
#undef ZeroX
|
||||
#undef ZeroY
|
||||
#undef IndexedIndirect
|
||||
#undef IndirectIndexed
|
||||
#undef Read
|
||||
#undef Write
|
||||
#undef ReadModifyWrite
|
||||
#undef AbsoluteRead
|
||||
#undef AbsoluteXRead
|
||||
#undef AbsoluteYRead
|
||||
#undef ZeroRead
|
||||
#undef ZeroXRead
|
||||
#undef ZeroYRead
|
||||
#undef IndexedIndirectRead
|
||||
#undef IndirectIndexedRead
|
||||
#undef AbsoluteWrite
|
||||
#undef AbsoluteXWrite
|
||||
#undef AbsoluteYWrite
|
||||
#undef ZeroWrite
|
||||
#undef ZeroXWrite
|
||||
#undef ZeroYWrite
|
||||
#undef IndexedIndirectWrite
|
||||
#undef IndirectIndexedWrite
|
||||
#undef AbsoluteReadModifyWrite
|
||||
#undef AbsoluteXReadModifyWrite
|
||||
#undef AbsoluteYReadModifyWrite
|
||||
#undef ZeroReadModifyWrite
|
||||
#undef ZeroXReadModifyWrite
|
||||
#undef ZeroYReadModify
|
||||
#undef IndexedIndirectReadModify
|
||||
#undef IndirectIndexedReadModify
|
||||
#undef Immediate
|
||||
#undef Implied
|
||||
|
||||
ProcessorStorage::ProcessorStorage() {
|
||||
ProcessorStorage::ProcessorStorage(Personality personality) {
|
||||
// only the interrupt flag is defined upon reset but get_flags isn't going to
|
||||
// mask the other flags so we need to do that, at least
|
||||
carry_flag_ &= Flag::Carry;
|
||||
decimal_flag_ &= Flag::Decimal;
|
||||
overflow_flag_ &= Flag::Overflow;
|
||||
|
||||
const InstructionList operations_6502[256] = {
|
||||
/* 0x00 BRK */ Program(CycleIncPCPushPCH, CyclePushPCL, OperationBRKPickVector, OperationSetOperandFromFlagsWithBRKSet, CyclePushOperand, OperationSetIRQFlags, CycleReadVectorLow, CycleReadVectorHigh),
|
||||
/* 0x01 ORA x, ind */ IndexedIndirectRead(OperationORA),
|
||||
/* 0x02 JAM */ JAM, /* 0x03 ASO x, ind */ IndexedIndirectReadModifyWrite(OperationASO),
|
||||
/* 0x04 NOP zpg */ ZeroNop(), /* 0x05 ORA zpg */ ZeroRead(OperationORA),
|
||||
/* 0x06 ASL zpg */ ZeroReadModifyWrite(OperationASL), /* 0x07 ASO zpg */ ZeroReadModifyWrite(OperationASO),
|
||||
/* 0x08 PHP */ Program(OperationSetOperandFromFlagsWithBRKSet, CyclePushOperand),
|
||||
/* 0x09 ORA # */ Immediate(OperationORA),
|
||||
/* 0x0a ASL A */ Implied(OperationASL), /* 0x0b ANC # */ Immediate(OperationANC),
|
||||
/* 0x0c NOP abs */ AbsoluteNop(), /* 0x0d ORA abs */ AbsoluteRead(OperationORA),
|
||||
/* 0x0e ASL abs */ AbsoluteReadModifyWrite(OperationASL), /* 0x0f ASO abs */ AbsoluteReadModifyWrite(OperationASO),
|
||||
/* 0x10 BPL */ Program(OperationBPL), /* 0x11 ORA ind, y */ IndirectIndexedRead(OperationORA),
|
||||
/* 0x12 JAM */ JAM, /* 0x13 ASO ind, y */ IndirectIndexedReadModifyWrite(OperationASO),
|
||||
/* 0x14 NOP zpg, x */ ZeroXNop(), /* 0x15 ORA zpg, x */ ZeroXRead(OperationORA),
|
||||
/* 0x16 ASL zpg, x */ ZeroXReadModifyWrite(OperationASL), /* 0x17 ASO zpg, x */ ZeroXReadModifyWrite(OperationASO),
|
||||
/* 0x18 CLC */ Program(OperationCLC), /* 0x19 ORA abs, y */ AbsoluteYRead(OperationORA),
|
||||
/* 0x1a NOP # */ ImpliedNop(), /* 0x1b ASO abs, y */ AbsoluteYReadModifyWrite(OperationASO),
|
||||
/* 0x1c NOP abs, x */ AbsoluteXNop(), /* 0x1d ORA abs, x */ AbsoluteXRead(OperationORA),
|
||||
/* 0x1e ASL abs, x */ AbsoluteXReadModifyWrite(OperationASL), /* 0x1f ASO abs, x */ AbsoluteXReadModifyWrite(OperationASO),
|
||||
/* 0x20 JSR abs */ Program(CycleIncrementPCAndReadStack, CyclePushPCH, CyclePushPCL, CycleReadPCHLoadPCL),
|
||||
/* 0x21 AND x, ind */ IndexedIndirectRead(OperationAND),
|
||||
/* 0x22 JAM */ JAM, /* 0x23 RLA x, ind */ IndexedIndirectReadModifyWrite(OperationRLA),
|
||||
/* 0x24 BIT zpg */ ZeroRead(OperationBIT), /* 0x25 AND zpg */ ZeroRead(OperationAND),
|
||||
/* 0x26 ROL zpg */ ZeroReadModifyWrite(OperationROL), /* 0x27 RLA zpg */ ZeroReadModifyWrite(OperationRLA),
|
||||
/* 0x28 PLP */ Program(CycleReadFromS, CyclePullOperand, OperationSetFlagsFromOperand),
|
||||
/* 0x29 AND A # */ Immediate(OperationAND),
|
||||
/* 0x2a ROL A */ Implied(OperationROL), /* 0x2b ANC # */ Immediate(OperationANC),
|
||||
/* 0x2c BIT abs */ AbsoluteRead(OperationBIT), /* 0x2d AND abs */ AbsoluteRead(OperationAND),
|
||||
/* 0x2e ROL abs */ AbsoluteReadModifyWrite(OperationROL), /* 0x2f RLA abs */ AbsoluteReadModifyWrite(OperationRLA),
|
||||
/* 0x30 BMI */ Program(OperationBMI), /* 0x31 AND ind, y */ IndirectIndexedRead(OperationAND),
|
||||
/* 0x32 JAM */ JAM, /* 0x33 RLA ind, y */ IndirectIndexedReadModifyWrite(OperationRLA),
|
||||
/* 0x34 NOP zpg, x */ ZeroXNop(), /* 0x35 AND zpg, x */ ZeroXRead(OperationAND),
|
||||
/* 0x36 ROL zpg, x */ ZeroXReadModifyWrite(OperationROL), /* 0x37 RLA zpg, x */ ZeroXReadModifyWrite(OperationRLA),
|
||||
/* 0x38 SEC */ Program(OperationSEC), /* 0x39 AND abs, y */ AbsoluteYRead(OperationAND),
|
||||
/* 0x3a NOP # */ ImpliedNop(), /* 0x3b RLA abs, y */ AbsoluteYReadModifyWrite(OperationRLA),
|
||||
/* 0x3c NOP abs, x */ AbsoluteXNop(), /* 0x3d AND abs, x */ AbsoluteXRead(OperationAND),
|
||||
/* 0x3e ROL abs, x */ AbsoluteXReadModifyWrite(OperationROL), /* 0x3f RLA abs, x */ AbsoluteXReadModifyWrite(OperationRLA),
|
||||
/* 0x40 RTI */ Program(CycleReadFromS, CyclePullOperand, OperationSetFlagsFromOperand, CyclePullPCL, CyclePullPCH),
|
||||
/* 0x41 EOR x, ind */ IndexedIndirectRead(OperationEOR),
|
||||
/* 0x42 JAM */ JAM, /* 0x43 LSE x, ind */ IndexedIndirectReadModifyWrite(OperationLSE),
|
||||
/* 0x44 NOP zpg */ ZeroNop(), /* 0x45 EOR zpg */ ZeroRead(OperationEOR),
|
||||
/* 0x46 LSR zpg */ ZeroReadModifyWrite(OperationLSR), /* 0x47 LSE zpg */ ZeroReadModifyWrite(OperationLSE),
|
||||
/* 0x48 PHA */ Program(CyclePushA), /* 0x49 EOR # */ Immediate(OperationEOR),
|
||||
/* 0x4a LSR A */ Implied(OperationLSR), /* 0x4b ASR A */ Immediate(OperationASR),
|
||||
/* 0x4c JMP abs */ Program(CycleIncrementPCReadPCHLoadPCL), /* 0x4d EOR abs */ AbsoluteRead(OperationEOR),
|
||||
/* 0x4e LSR abs */ AbsoluteReadModifyWrite(OperationLSR), /* 0x4f LSE abs */ AbsoluteReadModifyWrite(OperationLSE),
|
||||
/* 0x50 BVC */ Program(OperationBVC), /* 0x51 EOR ind, y */ IndirectIndexedRead(OperationEOR),
|
||||
/* 0x52 JAM */ JAM, /* 0x53 LSE ind, y */ IndirectIndexedReadModifyWrite(OperationLSE),
|
||||
/* 0x54 NOP zpg, x */ ZeroXNop(), /* 0x55 EOR zpg, x */ ZeroXRead(OperationEOR),
|
||||
/* 0x56 LSR zpg, x */ ZeroXReadModifyWrite(OperationLSR), /* 0x57 LSE zpg, x */ ZeroXReadModifyWrite(OperationLSE),
|
||||
/* 0x58 CLI */ Program(OperationCLI), /* 0x59 EOR abs, y */ AbsoluteYRead(OperationEOR),
|
||||
/* 0x5a NOP # */ ImpliedNop(), /* 0x5b LSE abs, y */ AbsoluteYReadModifyWrite(OperationLSE),
|
||||
/* 0x5c NOP abs, x */ AbsoluteXNop(), /* 0x5d EOR abs, x */ AbsoluteXRead(OperationEOR),
|
||||
/* 0x5e LSR abs, x */ AbsoluteXReadModifyWrite(OperationLSR), /* 0x5f LSE abs, x */ AbsoluteXReadModifyWrite(OperationLSE),
|
||||
/* 0x60 RTS */ Program(CycleReadFromS, CyclePullPCL, CyclePullPCH, CycleReadAndIncrementPC),
|
||||
/* 0x61 ADC x, ind */ IndexedIndirectRead(OperationADC),
|
||||
/* 0x62 JAM */ JAM, /* 0x63 RRA x, ind */ IndexedIndirectReadModifyWrite(OperationRRA, OperationADC),
|
||||
/* 0x64 NOP zpg */ ZeroNop(), /* 0x65 ADC zpg */ ZeroRead(OperationADC),
|
||||
/* 0x66 ROR zpg */ ZeroReadModifyWrite(OperationROR), /* 0x67 RRA zpg */ ZeroReadModifyWrite(OperationRRA, OperationADC),
|
||||
/* 0x68 PLA */ Program(CycleReadFromS, CyclePullA, OperationSetFlagsFromA), /* 0x69 ADC # */ Immediate(OperationADC),
|
||||
/* 0x6a ROR A */ Implied(OperationROR), /* 0x6b ARR # */ Immediate(OperationARR),
|
||||
/* 0x6c JMP (abs) */ Program(CycleReadAddressHLoadAddressL, CycleReadPCLFromAddress, CycleReadPCHFromAddressLowInc),
|
||||
/* 0x6d ADC abs */ AbsoluteRead(OperationADC),
|
||||
/* 0x6e ROR abs */ AbsoluteReadModifyWrite(OperationROR), /* 0x6f RRA abs */ AbsoluteReadModifyWrite(OperationRRA, OperationADC),
|
||||
/* 0x70 BVS */ Program(OperationBVS), /* 0x71 ADC ind, y */ IndirectIndexedRead(OperationADC),
|
||||
/* 0x72 JAM */ JAM, /* 0x73 RRA ind, y */ IndirectIndexedReadModifyWrite(OperationRRA, OperationADC),
|
||||
/* 0x74 NOP zpg, x */ ZeroXNop(), /* 0x75 ADC zpg, x */ ZeroXRead(OperationADC),
|
||||
/* 0x76 ROR zpg, x */ ZeroXReadModifyWrite(OperationROR), /* 0x77 RRA zpg, x */ ZeroXReadModifyWrite(OperationRRA, OperationADC),
|
||||
/* 0x78 SEI */ Program(OperationSEI), /* 0x79 ADC abs, y */ AbsoluteYRead(OperationADC),
|
||||
/* 0x7a NOP # */ ImpliedNop(), /* 0x7b RRA abs, y */ AbsoluteYReadModifyWrite(OperationRRA, OperationADC),
|
||||
/* 0x7c NOP abs, x */ AbsoluteXNop(), /* 0x7d ADC abs, x */ AbsoluteXRead(OperationADC),
|
||||
/* 0x7e ROR abs, x */ AbsoluteXReadModifyWrite(OperationROR), /* 0x7f RRA abs, x */ AbsoluteXReadModifyWrite(OperationRRA, OperationADC),
|
||||
/* 0x80 NOP # */ ImmediateNop(), /* 0x81 STA x, ind */ IndexedIndirectWrite(OperationSTA),
|
||||
/* 0x82 NOP # */ ImmediateNop(), /* 0x83 SAX x, ind */ IndexedIndirectWrite(OperationSAX),
|
||||
/* 0x84 STY zpg */ ZeroWrite(OperationSTY), /* 0x85 STA zpg */ ZeroWrite(OperationSTA),
|
||||
/* 0x86 STX zpg */ ZeroWrite(OperationSTX), /* 0x87 SAX zpg */ ZeroWrite(OperationSAX),
|
||||
/* 0x88 DEY */ Program(OperationDEY), /* 0x89 NOP # */ ImmediateNop(),
|
||||
/* 0x8a TXA */ Program(OperationTXA), /* 0x8b ANE # */ Immediate(OperationANE),
|
||||
/* 0x8c STY abs */ AbsoluteWrite(OperationSTY), /* 0x8d STA abs */ AbsoluteWrite(OperationSTA),
|
||||
/* 0x8e STX abs */ AbsoluteWrite(OperationSTX), /* 0x8f SAX abs */ AbsoluteWrite(OperationSAX),
|
||||
/* 0x90 BCC */ Program(OperationBCC), /* 0x91 STA ind, y */ IndirectIndexedWrite(OperationSTA),
|
||||
/* 0x92 JAM */ JAM, /* 0x93 SHA ind, y */ IndirectIndexedWrite(OperationSHA),
|
||||
/* 0x94 STY zpg, x */ ZeroXWrite(OperationSTY), /* 0x95 STA zpg, x */ ZeroXWrite(OperationSTA),
|
||||
/* 0x96 STX zpg, y */ ZeroYWrite(OperationSTX), /* 0x97 SAX zpg, y */ ZeroYWrite(OperationSAX),
|
||||
/* 0x98 TYA */ Program(OperationTYA), /* 0x99 STA abs, y */ AbsoluteYWrite(OperationSTA),
|
||||
/* 0x9a TXS */ Program(OperationTXS), /* 0x9b SHS abs, y */ AbsoluteYWrite(OperationSHS),
|
||||
/* 0x9c SHY abs, x */ AbsoluteXWrite(OperationSHY), /* 0x9d STA abs, x */ AbsoluteXWrite(OperationSTA),
|
||||
/* 0x9e SHX abs, y */ AbsoluteYWrite(OperationSHX), /* 0x9f SHA abs, y */ AbsoluteYWrite(OperationSHA),
|
||||
/* 0xa0 LDY # */ Immediate(OperationLDY), /* 0xa1 LDA x, ind */ IndexedIndirectRead(OperationLDA),
|
||||
/* 0xa2 LDX # */ Immediate(OperationLDX), /* 0xa3 LAX x, ind */ IndexedIndirectRead(OperationLAX),
|
||||
/* 0xa4 LDY zpg */ ZeroRead(OperationLDY), /* 0xa5 LDA zpg */ ZeroRead(OperationLDA),
|
||||
/* 0xa6 LDX zpg */ ZeroRead(OperationLDX), /* 0xa7 LAX zpg */ ZeroRead(OperationLAX),
|
||||
/* 0xa8 TAY */ Program(OperationTAY), /* 0xa9 LDA # */ Immediate(OperationLDA),
|
||||
/* 0xaa TAX */ Program(OperationTAX), /* 0xab LXA # */ Immediate(OperationLXA),
|
||||
/* 0xac LDY abs */ AbsoluteRead(OperationLDY), /* 0xad LDA abs */ AbsoluteRead(OperationLDA),
|
||||
/* 0xae LDX abs */ AbsoluteRead(OperationLDX), /* 0xaf LAX abs */ AbsoluteRead(OperationLAX),
|
||||
/* 0xb0 BCS */ Program(OperationBCS), /* 0xb1 LDA ind, y */ IndirectIndexedRead(OperationLDA),
|
||||
/* 0xb2 JAM */ JAM, /* 0xb3 LAX ind, y */ IndirectIndexedRead(OperationLAX),
|
||||
/* 0xb4 LDY zpg, x */ ZeroXRead(OperationLDY), /* 0xb5 LDA zpg, x */ ZeroXRead(OperationLDA),
|
||||
/* 0xb6 LDX zpg, y */ ZeroYRead(OperationLDX), /* 0xb7 LAX zpg, x */ ZeroYRead(OperationLAX),
|
||||
/* 0xb8 CLV */ Program(OperationCLV), /* 0xb9 LDA abs, y */ AbsoluteYRead(OperationLDA),
|
||||
/* 0xba TSX */ Program(OperationTSX), /* 0xbb LAS abs, y */ AbsoluteYRead(OperationLAS),
|
||||
/* 0xbc LDY abs, x */ AbsoluteXRead(OperationLDY), /* 0xbd LDA abs, x */ AbsoluteXRead(OperationLDA),
|
||||
/* 0xbe LDX abs, y */ AbsoluteYRead(OperationLDX), /* 0xbf LAX abs, y */ AbsoluteYRead(OperationLAX),
|
||||
/* 0xc0 CPY # */ Immediate(OperationCPY), /* 0xc1 CMP x, ind */ IndexedIndirectRead(OperationCMP),
|
||||
/* 0xc2 NOP # */ ImmediateNop(), /* 0xc3 DCP x, ind */ IndexedIndirectReadModifyWrite(OperationDecrementOperand, OperationCMP),
|
||||
/* 0xc4 CPY zpg */ ZeroRead(OperationCPY), /* 0xc5 CMP zpg */ ZeroRead(OperationCMP),
|
||||
/* 0xc6 DEC zpg */ ZeroReadModifyWrite(OperationDEC), /* 0xc7 DCP zpg */ ZeroReadModifyWrite(OperationDecrementOperand, OperationCMP),
|
||||
/* 0xc8 INY */ Program(OperationINY), /* 0xc9 CMP # */ Immediate(OperationCMP),
|
||||
/* 0xca DEX */ Program(OperationDEX), /* 0xcb ARR # */ Immediate(OperationSBX),
|
||||
/* 0xcc CPY abs */ AbsoluteRead(OperationCPY), /* 0xcd CMP abs */ AbsoluteRead(OperationCMP),
|
||||
/* 0xce DEC abs */ AbsoluteReadModifyWrite(OperationDEC), /* 0xcf DCP abs */ AbsoluteReadModifyWrite(OperationDecrementOperand, OperationCMP),
|
||||
/* 0xd0 BNE */ Program(OperationBNE), /* 0xd1 CMP ind, y */ IndirectIndexedRead(OperationCMP),
|
||||
/* 0xd2 JAM */ JAM, /* 0xd3 DCP ind, y */ IndirectIndexedReadModifyWrite(OperationDecrementOperand, OperationCMP),
|
||||
/* 0xd4 NOP zpg, x */ ZeroXNop(), /* 0xd5 CMP zpg, x */ ZeroXRead(OperationCMP),
|
||||
/* 0xd6 DEC zpg, x */ ZeroXReadModifyWrite(OperationDEC), /* 0xd7 DCP zpg, x */ ZeroXReadModifyWrite(OperationDecrementOperand, OperationCMP),
|
||||
/* 0xd8 CLD */ Program(OperationCLD), /* 0xd9 CMP abs, y */ AbsoluteYRead(OperationCMP),
|
||||
/* 0xda NOP # */ ImpliedNop(), /* 0xdb DCP abs, y */ AbsoluteYReadModifyWrite(OperationDecrementOperand, OperationCMP),
|
||||
/* 0xdc NOP abs, x */ AbsoluteXNop(), /* 0xdd CMP abs, x */ AbsoluteXRead(OperationCMP),
|
||||
/* 0xde DEC abs, x */ AbsoluteXReadModifyWrite(OperationDEC), /* 0xdf DCP abs, x */ AbsoluteXReadModifyWrite(OperationDecrementOperand, OperationCMP),
|
||||
/* 0xe0 CPX # */ Immediate(OperationCPX), /* 0xe1 SBC x, ind */ IndexedIndirectRead(OperationSBC),
|
||||
/* 0xe2 NOP # */ ImmediateNop(), /* 0xe3 INS x, ind */ IndexedIndirectReadModifyWrite(OperationINS),
|
||||
/* 0xe4 CPX zpg */ ZeroRead(OperationCPX), /* 0xe5 SBC zpg */ ZeroRead(OperationSBC),
|
||||
/* 0xe6 INC zpg */ ZeroReadModifyWrite(OperationINC), /* 0xe7 INS zpg */ ZeroReadModifyWrite(OperationINS),
|
||||
/* 0xe8 INX */ Program(OperationINX), /* 0xe9 SBC # */ Immediate(OperationSBC),
|
||||
/* 0xea NOP # */ ImpliedNop(), /* 0xeb SBC # */ Immediate(OperationSBC),
|
||||
/* 0xec CPX abs */ AbsoluteRead(OperationCPX), /* 0xed SBC abs */ AbsoluteRead(OperationSBC),
|
||||
/* 0xee INC abs */ AbsoluteReadModifyWrite(OperationINC), /* 0xef INS abs */ AbsoluteReadModifyWrite(OperationINS),
|
||||
/* 0xf0 BEQ */ Program(OperationBEQ), /* 0xf1 SBC ind, y */ IndirectIndexedRead(OperationSBC),
|
||||
/* 0xf2 JAM */ JAM, /* 0xf3 INS ind, y */ IndirectIndexedReadModifyWrite(OperationINS),
|
||||
/* 0xf4 NOP zpg, x */ ZeroXNop(), /* 0xf5 SBC zpg, x */ ZeroXRead(OperationSBC),
|
||||
/* 0xf6 INC zpg, x */ ZeroXReadModifyWrite(OperationINC), /* 0xf7 INS zpg, x */ ZeroXReadModifyWrite(OperationINS),
|
||||
/* 0xf8 SED */ Program(OperationSED), /* 0xf9 SBC abs, y */ AbsoluteYRead(OperationSBC),
|
||||
/* 0xfa NOP # */ ImpliedNop(), /* 0xfb INS abs, y */ AbsoluteYReadModifyWrite(OperationINS),
|
||||
/* 0xfc NOP abs, x */ AbsoluteXNop(), /* 0xfd SBC abs, x */ AbsoluteXRead(OperationSBC),
|
||||
/* 0xfe INC abs, x */ AbsoluteXReadModifyWrite(OperationINC), /* 0xff INS abs, x */ AbsoluteXReadModifyWrite(OperationINS),
|
||||
};
|
||||
|
||||
// Install the basic 6502 table.
|
||||
memcpy(operations_, operations_6502, sizeof(operations_));
|
||||
|
||||
// Patch the table according to the chip's personality.
|
||||
//
|
||||
// The 6502 and NES 6502 both have the same mapping of operation codes to actions
|
||||
// (respect for the decimal mode flag aside); included in that are 'unofficial'
|
||||
// operations — spots that are not formally defined to do anything but which the
|
||||
// processor makes no particular effort to react to in a well-defined way.
|
||||
//
|
||||
// The 65C02s add some official instructions but also ensure that all of the
|
||||
// undefined ones act as no-ops of various addressing modes.
|
||||
//
|
||||
// So the branch below has to add a bunch of new actions but also removes various
|
||||
// others by dint of replacing them with NOPs.
|
||||
//
|
||||
// Those 6502 opcodes that need redefining, one way or the other, are:
|
||||
//
|
||||
// 0x02, 0x03, 0x04, 0x07, 0x0b, 0x0c, 0x0f, 0x12, 0x13, 0x14, 0x17, 0x1a, 0x1b, 0x1c, 0x1f,
|
||||
// 0x22, 0x23, 0x27, 0x2b, 0x2f, 0x32, 0x33, 0x34, 0x37, 0x3a, 0x3b, 0x3c, 0x3f,
|
||||
// 0x42, 0x43, 0x47, 0x4b, 0x4f, 0x52, 0x53, 0x57, 0x5a, 0x5b, 0x5f,
|
||||
// 0x62, 0x63, 0x64, 0x67, 0x6b, 0x6f, 0x72, 0x73, 0x74, 0x77, 0x7b, 0x7a, 0x7c, 0x7f,
|
||||
// 0x80, 0x82, 0x83, 0x87, 0x89, 0x8b, 0x8f, 0x92, 0x93, 0x97, 0x9b, 0x9e, 0x9c, 0x9f,
|
||||
// 0xa3, 0xa7, 0xab, 0xaf, 0xb2, 0xb3, 0xb7, 0xbb, 0xbf,
|
||||
// 0xc3, 0xc7, 0xcb, 0xcf, 0xd2, 0xd3, 0xd7, 0xda, 0xdb, 0xdf,
|
||||
// 0xe3, 0xe7, 0xeb, 0xef, 0xf2, 0xf3, 0xf7, 0xfa, 0xfb, 0xff
|
||||
//
|
||||
// ... not including those that aren't defined on the 6502 but perform NOPs exactly like they
|
||||
// would on a 65C02.
|
||||
|
||||
#define Install(location, instructions) {\
|
||||
const InstructionList code = instructions; \
|
||||
memcpy(&operations_[location], code, sizeof(InstructionList)); \
|
||||
}
|
||||
if(is_65c02(personality)) {
|
||||
// Add P[L/H][X/Y].
|
||||
Install(0x5a, Program(CyclePushY));
|
||||
Install(0xda, Program(CyclePushX));
|
||||
Install(0x7a, Program(CycleReadFromS, CyclePullY, OperationSetFlagsFromY));
|
||||
Install(0xfa, Program(CycleReadFromS, CyclePullX, OperationSetFlagsFromX));
|
||||
|
||||
// Add BRA.
|
||||
Install(0x80, Program(OperationBRA));
|
||||
|
||||
// The 1-byte, 1-cycle (!) NOPs.
|
||||
for(int c = 0x03; c <= 0xf3; c += 0x10) {
|
||||
Install(c, ImpliedNop());
|
||||
}
|
||||
for(int c = 0x0b; c <= 0xbb; c += 0x10) {
|
||||
Install(c, ImpliedNop());
|
||||
}
|
||||
for(int c = 0xeb; c <= 0xfb; c += 0x10) {
|
||||
Install(c, ImpliedNop());
|
||||
}
|
||||
|
||||
// The 2-byte, 2-cycle NOPs that the 6502 doesn't have.
|
||||
for(int c = 0x02; c <= 0x62; c += 0x10) {
|
||||
Install(c, ImmediateNop());
|
||||
}
|
||||
|
||||
// Correct JMP (abs) and install JMP (abs, x).
|
||||
Install(0x6c, Program(CycleReadAddressHLoadAddressL, CycleReadPCLFromAddress, CycleReadPCHFromAddressLowInc, CycleReadPCHFromAddressFixed));
|
||||
Install(0x7c, Program(
|
||||
CycleReadAddressHLoadAddressL, // (3) read second byte of (addr)
|
||||
CycleAddXToAddressLowRead, // (4) calculate addr+x, read from (addr+x) with high byte not yet calculated
|
||||
OperationCorrectAddressHigh, CycleReadPCLFromAddress, // (5) read from real (addr+x)
|
||||
CycleReadPCHFromAddressInc // (6) read from addr+x+1
|
||||
));
|
||||
|
||||
// Add INA and DEA.
|
||||
Install(0x1a, Program(OperationINA));
|
||||
Install(0x3a, Program(OperationDEA));
|
||||
|
||||
// Add (zp) operations.
|
||||
Install(0x12, ZeroIndirectRead(OperationORA));
|
||||
Install(0x32, ZeroIndirectRead(OperationAND));
|
||||
Install(0x52, ZeroIndirectRead(OperationEOR));
|
||||
Install(0x72, ZeroIndirectRead(OperationADC));
|
||||
Install(0x92, ZeroIndirectWrite(OperationSTA));
|
||||
Install(0xb2, ZeroIndirectRead(OperationLDA));
|
||||
Install(0xd2, ZeroIndirectRead(OperationCMP));
|
||||
Install(0xf2, ZeroIndirectRead(OperationSBC));
|
||||
|
||||
// Add STZ.
|
||||
Install(0x9c, AbsoluteWrite(OperationSTZ));
|
||||
Install(0x9e, AbsoluteXWrite(OperationSTZ));
|
||||
Install(0x64, ZeroWrite(OperationSTZ));
|
||||
Install(0x74, ZeroXWrite(OperationSTZ));
|
||||
|
||||
// Add the extra BITs.
|
||||
Install(0x34, ZeroXRead(OperationBIT));
|
||||
Install(0x3c, AbsoluteXRead(OperationBIT));
|
||||
Install(0x89, Immediate(OperationBITNoNV));
|
||||
|
||||
// Add TRB and TSB.
|
||||
Install(0x04, ZeroReadModifyWrite(OperationTSB));
|
||||
Install(0x0c, AbsoluteReadModifyWrite(OperationTSB));
|
||||
Install(0x14, ZeroReadModifyWrite(OperationTRB));
|
||||
Install(0x1c, AbsoluteReadModifyWrite(OperationTRB));
|
||||
|
||||
// Install faster ASL, LSR, ROL, ROR abs,[x/y]. Note: INC, DEC deliberately not improved.
|
||||
Install(0x1e, FastAbsoluteXReadModifyWrite(OperationASL));
|
||||
Install(0x1f, FastAbsoluteXReadModifyWrite(OperationASO));
|
||||
Install(0x3e, FastAbsoluteXReadModifyWrite(OperationROL));
|
||||
Install(0x3f, FastAbsoluteXReadModifyWrite(OperationRLA));
|
||||
Install(0x5e, FastAbsoluteXReadModifyWrite(OperationLSR));
|
||||
Install(0x5f, FastAbsoluteXReadModifyWrite(OperationLSE));
|
||||
Install(0x7e, FastAbsoluteXReadModifyWrite(OperationROR));
|
||||
Install(0x7f, FastAbsoluteXReadModifyWrite(OperationRRA, OperationADC));
|
||||
|
||||
// Outstanding:
|
||||
// 0x07, 0x0f, 0x17, 0x1f,
|
||||
// 0x27, 0x2f, 0x37, 0x3f,
|
||||
// 0x47, 0x4f, 0x57, 0x5f,
|
||||
// 0x67, 0x6f, 0x77, 0x7f,
|
||||
// 0x87, 0x8f, 0x97, 0x9f,
|
||||
// 0xa7, 0xaf, 0xb7, 0xbf,
|
||||
// 0xc7, 0xcb, 0xcf, 0xd7, 0xdb, 0xdf,
|
||||
// 0xe7, 0xef, 0xf7, 0xff
|
||||
if(has_bbrbbsrmbsmb(personality)) {
|
||||
// Add BBS and BBR. These take five cycles. My guessed breakdown is:
|
||||
// 1. read opcode
|
||||
// 2. read operand
|
||||
// 3. read zero page
|
||||
// 4. read second operand
|
||||
// 5. read from PC without top byte fixed yet
|
||||
// ... with the caveat that (3) and (4) could be the other way around.
|
||||
for(int location = 0x0f; location <= 0xff; location += 0x10) {
|
||||
Install(location, Program(OperationLoadAddressZeroPage, CycleFetchOperandFromAddress, OperationBBRBBS));
|
||||
}
|
||||
|
||||
// Add RMB and SMB.
|
||||
for(int c = 0x07; c <= 0x77; c += 0x10) {
|
||||
Install(c, ZeroReadModifyWrite(OperationRMB));
|
||||
}
|
||||
for(int c = 0x87; c <= 0xf7; c += 0x10) {
|
||||
Install(c, ZeroReadModifyWrite(OperationSMB));
|
||||
}
|
||||
} else {
|
||||
for(int location = 0x0f; location <= 0xef; location += 0x20) {
|
||||
Install(location, AbsoluteNop());
|
||||
}
|
||||
for(int location = 0x1f; location <= 0xff; location += 0x20) {
|
||||
Install(location, AbsoluteXNop());
|
||||
}
|
||||
for(int c = 0x07; c <= 0xe7; c += 0x20) {
|
||||
Install(c, ZeroNop());
|
||||
}
|
||||
for(int c = 0x17; c <= 0xf7; c += 0x20) {
|
||||
Install(c, ZeroXNop());
|
||||
}
|
||||
}
|
||||
|
||||
// Outstanding:
|
||||
// 0xcb, 0xdb,
|
||||
if(has_stpwai(personality)) {
|
||||
Install(0xcb, Program(OperationScheduleWait));
|
||||
Install(0xdb, Program(OperationScheduleStop));
|
||||
} else {
|
||||
Install(0xcb, ImpliedNop());
|
||||
Install(0xdb, ZeroXNop());
|
||||
}
|
||||
}
|
||||
#undef Install
|
||||
}
|
||||
|
||||
@@ -15,54 +15,190 @@
|
||||
*/
|
||||
class ProcessorStorage {
|
||||
protected:
|
||||
ProcessorStorage();
|
||||
ProcessorStorage(Personality);
|
||||
|
||||
/*
|
||||
/*!
|
||||
This emulation functions by decomposing instructions into micro programs, consisting of the micro operations
|
||||
as per the enum below. Each micro op takes at most one cycle. By convention, those called CycleX take a cycle
|
||||
defined by MicroOp. Each micro op takes at most one cycle. By convention, those called CycleX take a cycle
|
||||
to perform whereas those called OperationX occur for free (so, in effect, their cost is loaded onto the next cycle).
|
||||
|
||||
This micro-instruction set was put together in a fairly ad hoc fashion, I'm afraid, so is unlikely to be optimal.
|
||||
*/
|
||||
enum MicroOp {
|
||||
CycleFetchOperation, CycleFetchOperand, OperationDecodeOperation, CycleIncPCPushPCH,
|
||||
CyclePushPCH, CyclePushPCL, CyclePushA, CyclePushOperand,
|
||||
OperationSetI,
|
||||
CycleFetchOperation, // fetches (PC) to operation_, storing PC to last_operation_pc_ before incrementing it
|
||||
CycleFetchOperand, // 6502: fetches from (PC) to operand_; 65C02: as 6502 unless operation_ indicates a one-cycle NOP, in which case this is a no0op
|
||||
OperationDecodeOperation, // schedules the microprogram associated with operation_
|
||||
OperationMoveToNextProgram, // either schedules the next fetch-decode-execute or an interrupt response if a request has been pending for at least one cycle
|
||||
|
||||
OperationBRKPickVector, OperationNMIPickVector, OperationRSTPickVector,
|
||||
CycleReadVectorLow, CycleReadVectorHigh,
|
||||
CycleIncPCPushPCH, // increments the PC and pushes PC.h to the stack
|
||||
CyclePushPCL, // pushes PC.l to the stack
|
||||
CyclePushPCH, // pushes PC.h to the stack
|
||||
CyclePushA, // pushes A to the stack
|
||||
CyclePushX, // pushes X to the stack
|
||||
CyclePushY, // pushes Y to the stack
|
||||
CyclePushOperand, // pushes operand_ to the stack
|
||||
|
||||
CycleReadFromS, CycleReadFromPC,
|
||||
CyclePullOperand, CyclePullPCL, CyclePullPCH, CyclePullA,
|
||||
CycleNoWritePush,
|
||||
CycleReadAndIncrementPC, CycleIncrementPCAndReadStack, CycleIncrementPCReadPCHLoadPCL, CycleReadPCHLoadPCL,
|
||||
CycleReadAddressHLoadAddressL, CycleReadPCLFromAddress, CycleReadPCHFromAddress, CycleLoadAddressAbsolute,
|
||||
OperationLoadAddressZeroPage, CycleLoadAddessZeroX, CycleLoadAddessZeroY, CycleAddXToAddressLow,
|
||||
CycleAddYToAddressLow, CycleAddXToAddressLowRead, OperationCorrectAddressHigh, CycleAddYToAddressLowRead,
|
||||
OperationMoveToNextProgram, OperationIncrementPC,
|
||||
CycleFetchOperandFromAddress, CycleWriteOperandToAddress, OperationCopyOperandFromA, OperationCopyOperandToA,
|
||||
CycleIncrementPCFetchAddressLowFromOperand, CycleAddXToOperandFetchAddressLow, CycleIncrementOperandFetchAddressHigh, OperationDecrementOperand,
|
||||
OperationIncrementOperand, OperationORA, OperationAND, OperationEOR,
|
||||
OperationINS, OperationADC, OperationSBC, OperationLDA,
|
||||
OperationLDX, OperationLDY, OperationLAX, OperationSTA,
|
||||
OperationSTX, OperationSTY, OperationSAX, OperationSHA,
|
||||
OperationSHX, OperationSHY, OperationSHS, OperationCMP,
|
||||
OperationCPX, OperationCPY, OperationBIT, OperationASL,
|
||||
OperationASO, OperationROL, OperationRLA, OperationLSR,
|
||||
OperationLSE, OperationASR, OperationROR, OperationRRA,
|
||||
OperationCLC, OperationCLI, OperationCLV, OperationCLD,
|
||||
OperationSEC, OperationSEI, OperationSED, OperationINC,
|
||||
OperationDEC, OperationINX, OperationDEX, OperationINY,
|
||||
OperationDEY, OperationBPL, OperationBMI, OperationBVC,
|
||||
OperationBVS, OperationBCC, OperationBCS, OperationBNE,
|
||||
OperationBEQ, OperationTXA, OperationTYA, OperationTXS,
|
||||
OperationTAY, OperationTAX, OperationTSX, OperationARR,
|
||||
OperationSBX, OperationLXA, OperationANE, OperationANC,
|
||||
OperationLAS, CycleAddSignedOperandToPC, OperationSetFlagsFromOperand, OperationSetOperandFromFlagsWithBRKSet,
|
||||
OperationSetOperandFromFlags,
|
||||
OperationSetFlagsFromA,
|
||||
CycleScheduleJam
|
||||
OperationSetIRQFlags, // 6502: sets I; 65C02: sets I and resets D
|
||||
OperationSetNMIRSTFlags, // 6502: no-op. 65C02: resets D
|
||||
|
||||
OperationBRKPickVector, // 65C02: sets next_address_ to the BRK vector location; 6502: as 65C02 if no NMI is pending; otherwise sets next_address_ to the NMI address and resets the internal NMI-pending flag
|
||||
OperationNMIPickVector, // sets next_address_ to the NMI vector
|
||||
OperationRSTPickVector, // sets next_address_ to the RST vector
|
||||
CycleReadVectorLow, // reads PC.l from next_address_
|
||||
CycleReadVectorHigh, // reads PC.h from (next_address_+1)
|
||||
|
||||
CycleReadFromS, // performs a read from the stack pointer, throwing the result away
|
||||
CycleReadFromPC, // performs a read from the program counter, throwing the result away
|
||||
|
||||
CyclePullPCL, // pulls PC.l from the stack
|
||||
CyclePullPCH, // pulls PC.h from the stack
|
||||
CyclePullA, // pulls A from the stack
|
||||
CyclePullX, // pulls X from the stack
|
||||
CyclePullY, // pulls Y from the stack
|
||||
CyclePullOperand, // pulls operand_ from the stack
|
||||
|
||||
CycleNoWritePush, // decrements S as though it were a push, but reads from the new stack address instead of writing
|
||||
CycleReadAndIncrementPC, // reads from the PC, throwing away the result, and increments the PC
|
||||
CycleIncrementPCAndReadStack, // increments the PC and reads from the stack pointer, throwing away the result
|
||||
CycleIncrementPCReadPCHLoadPCL, // increments the PC, schedules a read of PC.h from the post-incremented PC, then copies operand_ to PC.l
|
||||
CycleReadPCHLoadPCL, // schedules a read of PC.h from the post-incremented PC, then copies operand_ to PC.l
|
||||
CycleReadAddressHLoadAddressL, // increments the PC; copies operand_ to address_.l; reads address_.h from the new PC
|
||||
|
||||
CycleReadPCLFromAddress, // reads PC.l from address_
|
||||
CycleReadPCHFromAddressLowInc, // increments address_.l and reads PC.h from address_
|
||||
CycleReadPCHFromAddressFixed, // if address_.l is 0, increments address_.h; and reads PC.h from address_
|
||||
CycleReadPCHFromAddressInc, // increments address_ and reads PC.h from it
|
||||
|
||||
CycleLoadAddressAbsolute, // copies operand_ to address_.l, increments the PC, reads address_.h from PC, increments the PC again
|
||||
OperationLoadAddressZeroPage, // copies operand_ to address_ and increments the PC
|
||||
CycleLoadAddessZeroX, // copies (operand_+x)&0xff to address_, increments the PC, and reads from operand_, throwing away the result
|
||||
CycleLoadAddessZeroY, // copies (operand_+y)&0xff to address_, increments the PC, and reads from operand_, throwing away the result
|
||||
|
||||
CycleAddXToAddressLow, // calculates address_ + x and stores it to next_address_; copies next_address_.l back to address_.l; 6502: if address_ now does not equal next_address_, schedules a throwaway read from address_; 65C02: schedules a throaway read from PC-1
|
||||
CycleAddYToAddressLow, // calculates address_ + y and stores it to next_address_; copies next_address_.l back to address_.l; 6502: if address_ now does not equal next_address_, schedules a throwaway read from address_; 65C02: schedules a throaway read from PC-1
|
||||
CycleAddXToAddressLowRead, // calculates address_ + x and stores it to next_address; copies next_address.l back to address_.l; 6502: schedules a throwaway read from address_; 65C02: schedules a throaway read from PC-1
|
||||
CycleAddYToAddressLowRead, // calculates address_ + y and stores it to next_address; copies next_address.l back to address_.l; 6502: schedules a throwaway read from address_; 65C02: schedules a throaway read from PC-1
|
||||
OperationCorrectAddressHigh, // copies next_address_ to address_
|
||||
|
||||
OperationIncrementPC, // increments the PC
|
||||
CycleFetchOperandFromAddress, // fetches operand_ from address_
|
||||
CycleWriteOperandToAddress, // writes operand_ to address_
|
||||
|
||||
CycleIncrementPCFetchAddressLowFromOperand, // increments the PC and loads address_.l from (operand_)
|
||||
CycleAddXToOperandFetchAddressLow, // adds x [in]to operand_, producing an 8-bit result, and reads address_.l from (operand_)
|
||||
CycleIncrementOperandFetchAddressHigh, // increments operand_, producing an 8-bit result, and reads address_.h from (operand_)
|
||||
OperationDecrementOperand, // decrements operand_
|
||||
OperationIncrementOperand, // increments operand_
|
||||
CycleFetchAddressLowFromOperand, // reads address_.l from (operand_)
|
||||
|
||||
OperationORA, // ORs operand_ into a, setting the negative and zero flags
|
||||
OperationAND, // ANDs operand_ into a, setting the negative and zero flags
|
||||
OperationEOR, // EORs operand_ into a, setting the negative and zero flags
|
||||
|
||||
OperationINS, // increments operand_, then performs an SBC of operand_ from a
|
||||
OperationADC, // performs an ADC of operand_ into a_; if this is a 65C02 and decimal mode is set, performs an extra read to operand_ from address_
|
||||
OperationSBC, // performs an SBC of operand_ from a_; if this is a 65C02 and decimal mode is set, performs an extra read to operand_ from address_
|
||||
|
||||
OperationCMP, // CMPs a and operand_, setting negative, zero and carry flags
|
||||
OperationCPX, // CMPs x and operand_, setting negative, zero and carry flags
|
||||
OperationCPY, // CMPs y and operand_, setting negative, zero and carry flags
|
||||
OperationBIT, // sets the zero, negative and overflow flags as per a BIT of operand_ against a
|
||||
OperationBITNoNV, // sets the zero flag as per a BIT of operand_ against a
|
||||
|
||||
OperationLDA, // loads a with operand_, setting the negative and zero flags
|
||||
OperationLDX, // loads x with operand_, setting the negative and zero flags
|
||||
OperationLDY, // loads y with operand_, setting the negative and zero flags
|
||||
OperationLAX, // loads a and x with operand_, setting the negative and zero flags
|
||||
OperationCopyOperandToA, // sets a_ = operand_, not setting any flags
|
||||
|
||||
OperationSTA, // loads operand_ with a
|
||||
OperationSTX, // loads operand_ with x
|
||||
OperationSTY, // loads operand_ with y
|
||||
OperationSTZ, // loads operand_ with 0
|
||||
OperationSAX, // loads operand_ with a & x
|
||||
OperationSHA, // loads operand_ with a & x & (address.h+1)
|
||||
OperationSHX, // loads operand_ with x & (address.h+1)
|
||||
OperationSHY, // loads operand_ with y & (address.h+1)
|
||||
OperationSHS, // loads s with a & x, then loads operand_ with s & (address.h+1)
|
||||
|
||||
OperationASL, // shifts operand_ left, moving the top bit into carry and setting the negative and zero flags
|
||||
OperationASO, // performs an ASL of operand and ORs it into a
|
||||
OperationROL, // performs a ROL of operand_
|
||||
OperationRLA, // performs a ROL of operand_ and ANDs it into a
|
||||
OperationLSR, // shifts operand_ right, setting carry, negative and zero flags
|
||||
OperationLSE, // performs an LSR and EORs the result into a
|
||||
OperationASR, // ANDs operand_ into a, then performs an LSR
|
||||
OperationROR, // performs a ROR of operand_, setting carry, negative and zero flags
|
||||
OperationRRA, // performs a ROR of operand_ but sets only the carry flag
|
||||
|
||||
OperationCLC, // resets the carry flag
|
||||
OperationCLI, // resets I
|
||||
OperationCLV, // resets the overflow flag
|
||||
OperationCLD, // resets the decimal flag
|
||||
OperationSEC, // sets the carry flag
|
||||
OperationSEI, // sets I
|
||||
OperationSED, // sets the decimal flag
|
||||
|
||||
OperationRMB, // resets the bit in operand_ implied by operatiopn_
|
||||
OperationSMB, // sets the bit in operand_ implied by operatiopn_
|
||||
OperationTRB, // sets zero according to operand_ & a, then resets any bits in operand_ that are set in a
|
||||
OperationTSB, // sets zero according to operand_ & a, then sets any bits in operand_ that are set in a
|
||||
|
||||
OperationINC, // increments operand_, setting the negative and zero flags
|
||||
OperationDEC, // decrements operand_, setting the negative and zero flags
|
||||
OperationINX, // increments x, setting the negative and zero flags
|
||||
OperationDEX, // decrements x, setting the negative and zero flags
|
||||
OperationINY, // increments y, setting the negative and zero flags
|
||||
OperationDEY, // decrements y, setting the negative and zero flags
|
||||
OperationINA, // increments a, setting the negative and zero flags
|
||||
OperationDEA, // decrements a, setting the negative and zero flags
|
||||
|
||||
OperationBPL, // schedules the branch program if the negative flag is clear
|
||||
OperationBMI, // schedules the branch program if the negative flag is set
|
||||
OperationBVC, // schedules the branch program if the overflow flag is clear
|
||||
OperationBVS, // schedules the branch program if the overflow flag is set
|
||||
OperationBCC, // schedules the branch program if the carry flag is clear
|
||||
OperationBCS, // schedules the branch program if the carry flag is set
|
||||
OperationBNE, // schedules the branch program if the zero flag is clear
|
||||
OperationBEQ, // schedules the branch program if the zero flag is set; 65C02: otherwise jumps straight into a fetch-decode-execute without considering whether to take an interrupt
|
||||
OperationBRA, // schedules the branch program
|
||||
|
||||
OperationBBRBBS, // inspecting the operation_, if the appropriate bit of operand_ is set or clear schedules a program to read and act upon the second operand; otherwise schedule a program to read and discard it
|
||||
|
||||
OperationTXA, // copies x to a, setting the zero and negative flags
|
||||
OperationTYA, // copies y to a, setting the zero and negative flags
|
||||
OperationTXS, // copies x to s
|
||||
OperationTAY, // copies a to y, setting the zero and negative flags
|
||||
OperationTAX, // copies a to x, setting the zero and negative flags
|
||||
OperationTSX, // copies s to x, setting the zero and negative flags
|
||||
|
||||
/* The following are amongst the 6502's undocumented (/unintended) operations */
|
||||
OperationARR, // performs a mixture of ANDing operand_ into a, and shifting the result right
|
||||
OperationSBX, // performs a mixture of an SBC of x&a and operand_, mutating x
|
||||
OperationLXA, // loads a and x with (a | 0xee) & operand, setting the negative and zero flags
|
||||
OperationANE, // loads a_ with (a | 0xee) & operand & x, setting the negative and zero flags
|
||||
OperationANC, // ANDs operand_ into a, setting the negative and zero flags, and loading carry as if the result were shifted right
|
||||
OperationLAS, // loads a, x and s with s & operand, setting the negative and zero flags
|
||||
|
||||
CycleFetchFromHalfUpdatedPC, // performs a throwaway read from (PC + (signed)operand).l combined with PC.h
|
||||
CycleAddSignedOperandToPC, // sets next_address to PC + (signed)operand. If the high byte of next_address differs from the PC, schedules a throwaway read from the half-updated PC. 65C02 specific: if the top two bytes are the same, proceeds directly to fetch-decode-execute, ignoring any pending interrupts.
|
||||
OperationAddSignedOperandToPC16, // adds (signed)operand into the PC
|
||||
|
||||
OperationSetFlagsFromOperand, // sets all flags based on operand_
|
||||
OperationSetOperandFromFlagsWithBRKSet, // sets operand_ to the value of all flags, with the break flag set
|
||||
OperationSetOperandFromFlags, // sets operand_ to the value of all flags
|
||||
|
||||
OperationSetFlagsFromA, // sets the zero and negative flags based on the value of a
|
||||
OperationSetFlagsFromX, // sets the zero and negative flags based on the value of x
|
||||
OperationSetFlagsFromY, // sets the zero and negative flags based on the value of y
|
||||
|
||||
OperationScheduleJam, // schedules the program for operation F2
|
||||
OperationScheduleWait, // puts the processor into WAI mode (i.e. it'll do nothing until an interrupt is received)
|
||||
OperationScheduleStop, // puts the processor into STP mode (i.e. it'll do nothing until a reset is received)
|
||||
};
|
||||
|
||||
static const MicroOp operations[256][10];
|
||||
using InstructionList = MicroOp[10];
|
||||
InstructionList operations_[256];
|
||||
|
||||
const MicroOp *scheduled_program_counter_ = nullptr;
|
||||
|
||||
@@ -119,6 +255,8 @@ class ProcessorStorage {
|
||||
|
||||
bool ready_is_active_ = false;
|
||||
bool ready_line_is_enabled_ = false;
|
||||
bool stop_is_active_ = false;
|
||||
bool wait_is_active_ = false;
|
||||
|
||||
uint8_t irq_line_ = 0, irq_request_history_ = 0;
|
||||
bool nmi_line_is_enabled_ = false, set_overflow_line_is_enabled_ = false;
|
||||
|
||||
@@ -14,7 +14,7 @@ So its aims are:
|
||||
It currently contains emulations of the:
|
||||
* Acorn Electron;
|
||||
* Amstrad CPC;
|
||||
* Apple II/II+;
|
||||
* Apple II/II+ and IIe;
|
||||
* Atari 2600;
|
||||
* ColecoVision;
|
||||
* Commodore Vic-20 (and Commodore 1540/1);
|
||||
@@ -71,7 +71,7 @@ Cycle-accurate emulation for the supported target machines is fairly trite; this
|
||||
Self-ratings:
|
||||
* the Electron, Oric and Vic-20 are pretty much perfect;
|
||||
* the ZX80, ZX81, ColecoVision and MSX 1 are very strong;
|
||||
* the Apple II/II+ should be strong by design, but is currently largely untested;
|
||||
* the Apple II/II+ and IIe should be strong by design, but are relatively new;
|
||||
* the Amstrad CPC has known accuracy deficiencies in its 8272 and 6845;
|
||||
* the Atari 2600 has some known accuracy deficiencies in its TIA;
|
||||
* the C-1540(/1) is locked in reading mode and doesn't yet support writing.
|
||||
|
||||
@@ -9,5 +9,6 @@ apple2eu.rom — as per apple2e.rom, but for the Unenhanced Apple II.
|
||||
|
||||
apple2-character.rom — a 2kb image of the Apple IIe's character ROM.
|
||||
apple2eu-character.rom — a 4kb image of the Unenhanced IIe's character ROM.
|
||||
apple2e-character.rom — a 4kb image of the Enhanced IIe's character ROM.
|
||||
|
||||
Apologies for the wackiness around "at least xkb big", it's to allow for use of files such as those on ftp.apple.asimov.net, which tend to be a bunch of other things, then the system ROM.
|
||||
@@ -75,19 +75,24 @@ std::shared_ptr<Track> AppleDSK::get_track_at_position(Track::Address address) {
|
||||
// In either case below, the code aims for exactly 50,000 bits per track.
|
||||
if(sectors_per_track_ == 16) {
|
||||
// Write gap 1.
|
||||
segment += Encodings::AppleGCR::six_and_two_sync(16);
|
||||
segment += Encodings::AppleGCR::six_and_two_sync(24);
|
||||
|
||||
// Write the sectors.
|
||||
for(uint8_t c = 0; c < 16; ++c) {
|
||||
segment += Encodings::AppleGCR::header(254, track, c);
|
||||
segment += Encodings::AppleGCR::header(is_prodos_ ? 0x01 : 0xfe, track, c); // Volume number is 0xfe for DOS 3.3, 0x01 for Pro-DOS.
|
||||
segment += Encodings::AppleGCR::six_and_two_sync(7); // Gap 2: 7 sync words.
|
||||
segment += Encodings::AppleGCR::six_and_two_data(&track_data[logical_sector_for_physical_sector(c) * 256]);
|
||||
segment += Encodings::AppleGCR::six_and_two_sync(16); // Gap 3: 16 sync words.
|
||||
segment += Encodings::AppleGCR::six_and_two_sync(20); // Gap 3: 20 sync words.
|
||||
}
|
||||
} else {
|
||||
// TODO: 5 and 3, 13-sector format. If DSK actually supports it?
|
||||
}
|
||||
|
||||
// Apply inter-track skew; skew is about 40ms between each track; assuming 300RPM that's
|
||||
// 1/5th of a revolution.
|
||||
const size_t offset_in_fifths = address.position.as_int() % 5;
|
||||
segment.rotate_right(offset_in_fifths * segment.data.size() / 5);
|
||||
|
||||
return std::make_shared<PCMTrack>(segment);
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,25 @@ PCMSegment &PCMSegment::operator +=(const PCMSegment &rhs) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
void PCMSegment::rotate_right(size_t length) {
|
||||
length %= data.size();
|
||||
if(!length) return;
|
||||
|
||||
// To rotate to the right, front-insert the proper number
|
||||
// of bits from the end and then resize. To rotate to
|
||||
// the left, do the opposite.
|
||||
std::vector<uint8_t> data_copy;
|
||||
if(length > 0) {
|
||||
data_copy.insert(data_copy.end(), data.end() - static_cast<off_t>(length), data.end());
|
||||
data.erase(data.end() - static_cast<off_t>(length), data.end());
|
||||
data.insert(data.begin(), data_copy.begin(), data_copy.end());
|
||||
} else {
|
||||
data_copy.insert(data_copy.end(), data.begin(), data.begin() - static_cast<off_t>(length));
|
||||
data.erase(data.begin(), data.begin() - static_cast<off_t>(length));
|
||||
data.insert(data.end(), data_copy.begin(), data_copy.end());
|
||||
}
|
||||
}
|
||||
|
||||
Storage::Disk::Track::Event PCMSegmentEventSource::get_next_event() {
|
||||
// track the initial bit pointer for potentially considering whether this was an
|
||||
// initial index hole or a subsequent one later on
|
||||
|
||||
@@ -105,6 +105,13 @@ struct PCMSegment {
|
||||
data.clear();
|
||||
}
|
||||
|
||||
/*!
|
||||
Rotates all bits in this segment by @c length bits.
|
||||
|
||||
@c length is signed; to rotate left provide a negative number.
|
||||
*/
|
||||
void rotate_right(size_t length);
|
||||
|
||||
/*!
|
||||
Produces a byte buffer where the contents of @c data are serialised into bytes
|
||||
|
||||
|
||||
Reference in New Issue
Block a user