mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-28 07:29:45 +00:00
Merge pull request #204 from TomHarte/VicInline
Brings the Vic-20 into line with the new idiom on machine declaration
This commit is contained in:
commit
bcd7a312a4
@ -8,15 +8,216 @@
|
||||
|
||||
#include "Vic20.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include "../../../Storage/Tape/Formats/TapePRG.hpp"
|
||||
#include "../../../Storage/Tape/Parsers/Commodore.hpp"
|
||||
#include "../../../StaticAnalyser/StaticAnalyser.hpp"
|
||||
#include "CharacterMapper.hpp"
|
||||
|
||||
using namespace Commodore::Vic20;
|
||||
#include "../../../Processors/6502/6502.hpp"
|
||||
#include "../../../Components/6560/6560.hpp"
|
||||
#include "../../../Components/6522/6522.hpp"
|
||||
|
||||
Machine::Machine() :
|
||||
#include "../../../Storage/Tape/Parsers/Commodore.hpp"
|
||||
|
||||
#include "../SerialBus.hpp"
|
||||
#include "../1540/C1540.hpp"
|
||||
|
||||
#include "../../../Storage/Tape/Tape.hpp"
|
||||
#include "../../../Storage/Disk/Disk.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace Commodore {
|
||||
namespace Vic20 {
|
||||
|
||||
/*!
|
||||
Models the user-port VIA, which is the Vic's connection point for controlling its tape recorder —
|
||||
sensing the presence or absence of a tape and controlling the tape motor — and reading the current
|
||||
state from its serial port. Most of the joystick input is also exposed here.
|
||||
*/
|
||||
class UserPortVIA: public MOS::MOS6522<UserPortVIA>, public MOS::MOS6522IRQDelegate {
|
||||
public:
|
||||
UserPortVIA() : port_a_(0xbf) {}
|
||||
using MOS6522IRQDelegate::set_interrupt_status;
|
||||
|
||||
/// Reports the current input to the 6522 port @c port.
|
||||
uint8_t get_port_input(Port port) {
|
||||
// Port A provides information about the presence or absence of a tape, and parts of
|
||||
// the joystick and serial port state, both of which have been statefully collected
|
||||
// into port_a_.
|
||||
if(!port) {
|
||||
return port_a_ | (tape_->has_tape() ? 0x00 : 0x40);
|
||||
}
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
/// Receives announcements of control line output change from the 6522.
|
||||
void set_control_line_output(Port port, Line line, bool value) {
|
||||
// The CA2 output is used to control the tape motor.
|
||||
if(port == Port::A && line == Line::Two) {
|
||||
tape_->set_motor_control(!value);
|
||||
}
|
||||
}
|
||||
|
||||
/// Receives announcements of changes in the serial bus connected to the serial port and propagates them into Port A.
|
||||
void set_serial_line_state(::Commodore::Serial::Line line, bool value) {
|
||||
switch(line) {
|
||||
default: break;
|
||||
case ::Commodore::Serial::Line::Data: port_a_ = (port_a_ & ~0x02) | (value ? 0x02 : 0x00); break;
|
||||
case ::Commodore::Serial::Line::Clock: port_a_ = (port_a_ & ~0x01) | (value ? 0x01 : 0x00); break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows the current joystick input to be set.
|
||||
void set_joystick_state(JoystickInput input, bool value) {
|
||||
if(input != JoystickInput::Right) {
|
||||
port_a_ = (port_a_ & ~input) | (value ? 0 : input);
|
||||
}
|
||||
}
|
||||
|
||||
/// Receives announcements from the 6522 of user-port output, which might affect what's currently being presented onto the serial bus.
|
||||
void set_port_output(Port port, uint8_t value, uint8_t mask) {
|
||||
// Line 7 of port A is inverted and output as serial ATN.
|
||||
if(!port) {
|
||||
std::shared_ptr<::Commodore::Serial::Port> serialPort = serial_port_.lock();
|
||||
if(serialPort) serialPort->set_output(::Commodore::Serial::Line::Attention, (::Commodore::Serial::LineLevel)!(value&0x80));
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets @serial_port as this VIA's connection to the serial bus.
|
||||
void set_serial_port(std::shared_ptr<::Commodore::Serial::Port> serial_port) {
|
||||
serial_port_ = serial_port;
|
||||
}
|
||||
|
||||
/// Sets @tape as the tape player connected to this VIA.
|
||||
void set_tape(std::shared_ptr<Storage::Tape::BinaryTapePlayer> tape) {
|
||||
tape_ = tape;
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t port_a_;
|
||||
std::weak_ptr<::Commodore::Serial::Port> serial_port_;
|
||||
std::shared_ptr<Storage::Tape::BinaryTapePlayer> tape_;
|
||||
};
|
||||
|
||||
/*!
|
||||
Models the keyboard VIA, which is used by the Vic for reading its keyboard, to output to its serial port,
|
||||
and for the small portion of joystick input not connected to the user-port VIA.
|
||||
*/
|
||||
class KeyboardVIA: public MOS::MOS6522<KeyboardVIA>, public MOS::MOS6522IRQDelegate {
|
||||
public:
|
||||
KeyboardVIA() : port_b_(0xff) {
|
||||
clear_all_keys();
|
||||
}
|
||||
|
||||
using MOS6522IRQDelegate::set_interrupt_status;
|
||||
|
||||
/// Sets whether @c key @c is_pressed.
|
||||
void set_key_state(uint16_t key, bool is_pressed) {
|
||||
if(is_pressed)
|
||||
columns_[key & 7] &= ~(key >> 3);
|
||||
else
|
||||
columns_[key & 7] |= (key >> 3);
|
||||
}
|
||||
|
||||
/// Sets all keys as unpressed.
|
||||
void clear_all_keys() {
|
||||
memset(columns_, 0xff, sizeof(columns_));
|
||||
}
|
||||
|
||||
/// Called by the 6522 to get input. Reads the keyboard on Port A, returns a small amount of joystick state on Port B.
|
||||
uint8_t get_port_input(Port port) {
|
||||
if(!port) {
|
||||
uint8_t result = 0xff;
|
||||
for(int c = 0; c < 8; c++) {
|
||||
if(!(activation_mask_&(1 << c)))
|
||||
result &= columns_[c];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return port_b_;
|
||||
}
|
||||
|
||||
/// Called by the 6522 to set output. The value of Port B selects which part of the keyboard to read.
|
||||
void set_port_output(Port port, uint8_t value, uint8_t mask) {
|
||||
if(port) activation_mask_ = (value & mask) | (~mask);
|
||||
}
|
||||
|
||||
/// Called by the 6522 to set control line output. Which affects the serial port.
|
||||
void set_control_line_output(Port port, Line line, bool value) {
|
||||
if(line == Line::Two) {
|
||||
std::shared_ptr<::Commodore::Serial::Port> serialPort = serial_port_.lock();
|
||||
if(serialPort) {
|
||||
// CB2 is inverted to become serial data; CA2 is inverted to become serial clock
|
||||
if(port == Port::A)
|
||||
serialPort->set_output(::Commodore::Serial::Line::Clock, (::Commodore::Serial::LineLevel)!value);
|
||||
else
|
||||
serialPort->set_output(::Commodore::Serial::Line::Data, (::Commodore::Serial::LineLevel)!value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets whether the joystick input @c input is pressed.
|
||||
void set_joystick_state(JoystickInput input, bool value) {
|
||||
if(input == JoystickInput::Right) {
|
||||
port_b_ = (port_b_ & ~input) | (value ? 0 : input);
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the serial port to which this VIA is connected.
|
||||
void set_serial_port(std::shared_ptr<::Commodore::Serial::Port> serialPort) {
|
||||
serial_port_ = serialPort;
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t port_b_;
|
||||
uint8_t columns_[8];
|
||||
uint8_t activation_mask_;
|
||||
std::weak_ptr<::Commodore::Serial::Port> serial_port_;
|
||||
};
|
||||
|
||||
/*!
|
||||
Models the Vic's serial port, providing the receipticle for input.
|
||||
*/
|
||||
class SerialPort : public ::Commodore::Serial::Port {
|
||||
public:
|
||||
/// Receives an input change from the base serial port class, and communicates it to the user-port VIA.
|
||||
void set_input(::Commodore::Serial::Line line, ::Commodore::Serial::LineLevel level) {
|
||||
std::shared_ptr<UserPortVIA> userPortVIA = user_port_via_.lock();
|
||||
if(userPortVIA) userPortVIA->set_serial_line_state(line, (bool)level);
|
||||
}
|
||||
|
||||
/// Sets the user-port VIA with which this serial port communicates.
|
||||
void set_user_port_via(std::shared_ptr<UserPortVIA> userPortVIA) {
|
||||
user_port_via_ = userPortVIA;
|
||||
}
|
||||
|
||||
private:
|
||||
std::weak_ptr<UserPortVIA> user_port_via_;
|
||||
};
|
||||
|
||||
/*!
|
||||
Provides the bus over which the Vic 6560 fetches memory in a Vic-20.
|
||||
*/
|
||||
class Vic6560: public MOS::MOS6560<Vic6560> {
|
||||
public:
|
||||
/// Performs a read on behalf of the 6560; in practice uses @c video_memory_map and @c colour_memory to find data.
|
||||
inline void perform_read(uint16_t address, uint8_t *pixel_data, uint8_t *colour_data) {
|
||||
*pixel_data = video_memory_map[address >> 10] ? video_memory_map[address >> 10][address & 0x3ff] : 0xff; // TODO
|
||||
*colour_data = colour_memory[address & 0x03ff];
|
||||
}
|
||||
|
||||
// It is assumed that these pointers have been filled in by the machine.
|
||||
uint8_t *video_memory_map[16]; // Segments video memory into 1kb portions.
|
||||
uint8_t *colour_memory; // Colour memory must be contiguous.
|
||||
};
|
||||
|
||||
class ConcreteMachine:
|
||||
public CPU::MOS6502::BusHandler,
|
||||
public MOS::MOS6522IRQDelegate::Delegate,
|
||||
public Utility::TypeRecipient,
|
||||
public Storage::Tape::BinaryTapePlayer::Delegate,
|
||||
public Machine {
|
||||
public:
|
||||
ConcreteMachine() :
|
||||
m6502_(*this),
|
||||
rom_(nullptr),
|
||||
is_running_at_zero_cost_(false),
|
||||
@ -46,12 +247,92 @@ Machine::Machine() :
|
||||
|
||||
// set the NTSC clock rate
|
||||
set_region(NTSC);
|
||||
// _debugPort.reset(new ::Commodore::Serial::DebugPort);
|
||||
// _debugPort->set_serial_bus(serial_bus_);
|
||||
// serial_bus_->add_port(_debugPort);
|
||||
}
|
||||
}
|
||||
|
||||
void Machine::set_memory_size(MemorySize size) {
|
||||
~ConcreteMachine() {
|
||||
delete[] rom_;
|
||||
}
|
||||
|
||||
void set_rom(ROMSlot slot, size_t length, const uint8_t *data) {
|
||||
uint8_t *target = nullptr;
|
||||
size_t max_length = 0x2000;
|
||||
switch(slot) {
|
||||
case Kernel: target = kernel_rom_; break;
|
||||
case Characters: target = character_rom_; max_length = 0x1000; break;
|
||||
case BASIC: target = basic_rom_; break;
|
||||
case Drive:
|
||||
drive_rom_.resize(length);
|
||||
memcpy(drive_rom_.data(), data, length);
|
||||
install_disk_rom();
|
||||
return;
|
||||
}
|
||||
|
||||
if(target) {
|
||||
size_t length_to_copy = std::min(max_length, length);
|
||||
memcpy(target, data, length_to_copy);
|
||||
}
|
||||
}
|
||||
|
||||
void configure_as_target(const StaticAnalyser::Target &target) {
|
||||
if(target.tapes.size()) {
|
||||
tape_->set_tape(target.tapes.front());
|
||||
}
|
||||
|
||||
if(target.disks.size()) {
|
||||
// construct the 1540
|
||||
c1540_.reset(new ::Commodore::C1540::Machine);
|
||||
|
||||
// attach it to the serial bus
|
||||
c1540_->set_serial_bus(serial_bus_);
|
||||
|
||||
// hand it the disk
|
||||
c1540_->set_disk(target.disks.front());
|
||||
|
||||
// install the ROM if it was previously set
|
||||
install_disk_rom();
|
||||
}
|
||||
|
||||
if(target.cartridges.size()) {
|
||||
rom_address_ = 0xa000;
|
||||
std::vector<uint8_t> rom_image = target.cartridges.front()->get_segments().front().data;
|
||||
rom_length_ = (uint16_t)(rom_image.size());
|
||||
|
||||
rom_ = new uint8_t[0x2000];
|
||||
memcpy(rom_, rom_image.data(), rom_image.size());
|
||||
write_to_map(processor_read_memory_map_, rom_, rom_address_, 0x2000);
|
||||
}
|
||||
|
||||
if(target.loadingCommand.length()) {
|
||||
set_typer_for_string(target.loadingCommand.c_str());
|
||||
}
|
||||
|
||||
switch(target.vic20.memory_model) {
|
||||
case StaticAnalyser::Vic20MemoryModel::Unexpanded:
|
||||
set_memory_size(Default);
|
||||
break;
|
||||
case StaticAnalyser::Vic20MemoryModel::EightKB:
|
||||
set_memory_size(ThreeKB);
|
||||
break;
|
||||
case StaticAnalyser::Vic20MemoryModel::ThirtyTwoKB:
|
||||
set_memory_size(ThirtyTwoKB);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void set_key_state(uint16_t key, bool isPressed) {
|
||||
keyboard_via_->set_key_state(key, isPressed);
|
||||
}
|
||||
|
||||
void clear_all_keys() {
|
||||
keyboard_via_->clear_all_keys();
|
||||
}
|
||||
|
||||
void set_joystick_state(JoystickInput input, bool isPressed) {
|
||||
user_port_via_->set_joystick_state(input, isPressed);
|
||||
keyboard_via_->set_joystick_state(input, isPressed);
|
||||
}
|
||||
|
||||
void set_memory_size(MemorySize size) {
|
||||
memset(processor_read_memory_map_, 0, sizeof(processor_read_memory_map_));
|
||||
memset(processor_write_memory_map_, 0, sizeof(processor_write_memory_map_));
|
||||
|
||||
@ -83,23 +364,34 @@ void Machine::set_memory_size(MemorySize size) {
|
||||
if(rom_) {
|
||||
write_to_map(processor_read_memory_map_, rom_, rom_address_, rom_length_);
|
||||
}
|
||||
}
|
||||
|
||||
void Machine::write_to_map(uint8_t **map, uint8_t *area, uint16_t address, uint16_t length) {
|
||||
address >>= 10;
|
||||
length >>= 10;
|
||||
while(length--) {
|
||||
map[address] = area;
|
||||
area += 0x400;
|
||||
address++;
|
||||
}
|
||||
}
|
||||
|
||||
Machine::~Machine() {
|
||||
delete[] rom_;
|
||||
}
|
||||
void set_region(Region region) {
|
||||
region_ = region;
|
||||
switch(region) {
|
||||
case PAL:
|
||||
set_clock_rate(1108404);
|
||||
if(mos6560_) {
|
||||
mos6560_->set_output_mode(MOS::MOS6560<Commodore::Vic20::Vic6560>::OutputMode::PAL);
|
||||
mos6560_->set_clock_rate(1108404);
|
||||
}
|
||||
break;
|
||||
case NTSC:
|
||||
set_clock_rate(1022727);
|
||||
if(mos6560_) {
|
||||
mos6560_->set_output_mode(MOS::MOS6560<Commodore::Vic20::Vic6560>::OutputMode::NTSC);
|
||||
mos6560_->set_clock_rate(1022727);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Cycles Machine::perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
|
||||
void set_use_fast_tape_hack(bool activate) {
|
||||
use_fast_tape_hack_ = activate;
|
||||
}
|
||||
|
||||
// to satisfy CPU::MOS6502::Processor
|
||||
Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
|
||||
// run the phase-1 part of this cycle, in which the VIC accesses memory
|
||||
if(!is_running_at_zero_cost_) mos6560_->run_for(Cycles(1));
|
||||
|
||||
@ -193,42 +485,17 @@ Cycles Machine::perform_bus_operation(CPU::MOS6502::BusOperation operation, uint
|
||||
if(c1540_) c1540_->run_for(Cycles(1));
|
||||
|
||||
return Cycles(1);
|
||||
}
|
||||
}
|
||||
|
||||
void Machine::run_for(const Cycles cycles) {
|
||||
void flush() {
|
||||
mos6560_->flush();
|
||||
}
|
||||
|
||||
void run_for(const Cycles cycles) {
|
||||
m6502_.run_for(cycles);
|
||||
}
|
||||
|
||||
#pragma mark - 6522 delegate
|
||||
|
||||
void Machine::mos6522_did_change_interrupt_status(void *mos6522) {
|
||||
m6502_.set_nmi_line(user_port_via_->get_interrupt_line());
|
||||
m6502_.set_irq_line(keyboard_via_->get_interrupt_line());
|
||||
}
|
||||
|
||||
#pragma mark - Setup
|
||||
|
||||
void Machine::set_region(Commodore::Vic20::Region region) {
|
||||
region_ = region;
|
||||
switch(region) {
|
||||
case PAL:
|
||||
set_clock_rate(1108404);
|
||||
if(mos6560_) {
|
||||
mos6560_->set_output_mode(MOS::MOS6560<Commodore::Vic20::Vic6560>::OutputMode::PAL);
|
||||
mos6560_->set_clock_rate(1108404);
|
||||
}
|
||||
break;
|
||||
case NTSC:
|
||||
set_clock_rate(1022727);
|
||||
if(mos6560_) {
|
||||
mos6560_->set_output_mode(MOS::MOS6560<Commodore::Vic20::Vic6560>::OutputMode::NTSC);
|
||||
mos6560_->set_clock_rate(1022727);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Machine::setup_output(float aspect_ratio) {
|
||||
void setup_output(float aspect_ratio) {
|
||||
mos6560_.reset(new Vic6560());
|
||||
mos6560_->get_speaker()->set_high_frequency_cut_off(1600); // There is a 1.6Khz low-pass filter in the Vic-20.
|
||||
set_region(region_);
|
||||
@ -238,212 +505,93 @@ void Machine::setup_output(float aspect_ratio) {
|
||||
write_to_map(mos6560_->video_memory_map, user_basic_memory_, 0x2000, sizeof(user_basic_memory_));
|
||||
write_to_map(mos6560_->video_memory_map, screen_memory_, 0x3000, sizeof(screen_memory_));
|
||||
mos6560_->colour_memory = colour_memory_;
|
||||
}
|
||||
}
|
||||
|
||||
void Machine::close_output() {
|
||||
void close_output() {
|
||||
mos6560_ = nullptr;
|
||||
}
|
||||
|
||||
void Machine::set_rom(ROMSlot slot, size_t length, const uint8_t *data) {
|
||||
uint8_t *target = nullptr;
|
||||
size_t max_length = 0x2000;
|
||||
switch(slot) {
|
||||
case Kernel: target = kernel_rom_; break;
|
||||
case Characters: target = character_rom_; max_length = 0x1000; break;
|
||||
case BASIC: target = basic_rom_; break;
|
||||
case Drive:
|
||||
drive_rom_.resize(length);
|
||||
memcpy(drive_rom_.data(), data, length);
|
||||
install_disk_rom();
|
||||
return;
|
||||
}
|
||||
|
||||
if(target) {
|
||||
size_t length_to_copy = std::min(max_length, length);
|
||||
memcpy(target, data, length_to_copy);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mar - Tape
|
||||
|
||||
void Machine::configure_as_target(const StaticAnalyser::Target &target) {
|
||||
if(target.tapes.size()) {
|
||||
tape_->set_tape(target.tapes.front());
|
||||
std::shared_ptr<Outputs::CRT::CRT> get_crt() {
|
||||
return mos6560_->get_crt();
|
||||
}
|
||||
|
||||
if(target.disks.size()) {
|
||||
// construct the 1540
|
||||
c1540_.reset(new ::Commodore::C1540::Machine);
|
||||
|
||||
// attach it to the serial bus
|
||||
c1540_->set_serial_bus(serial_bus_);
|
||||
|
||||
// hand it the disk
|
||||
c1540_->set_disk(target.disks.front());
|
||||
|
||||
// install the ROM if it was previously set
|
||||
install_disk_rom();
|
||||
std::shared_ptr<Outputs::Speaker> get_speaker() {
|
||||
return mos6560_->get_speaker();
|
||||
}
|
||||
|
||||
if(target.cartridges.size()) {
|
||||
rom_address_ = 0xa000;
|
||||
std::vector<uint8_t> rom_image = target.cartridges.front()->get_segments().front().data;
|
||||
rom_length_ = (uint16_t)(rom_image.size());
|
||||
|
||||
rom_ = new uint8_t[0x2000];
|
||||
memcpy(rom_, rom_image.data(), rom_image.size());
|
||||
write_to_map(processor_read_memory_map_, rom_, rom_address_, 0x2000);
|
||||
void mos6522_did_change_interrupt_status(void *mos6522) {
|
||||
m6502_.set_nmi_line(user_port_via_->get_interrupt_line());
|
||||
m6502_.set_irq_line(keyboard_via_->get_interrupt_line());
|
||||
}
|
||||
|
||||
if(target.loadingCommand.length()) {
|
||||
set_typer_for_string(target.loadingCommand.c_str());
|
||||
}
|
||||
|
||||
switch(target.vic20.memory_model) {
|
||||
case StaticAnalyser::Vic20MemoryModel::Unexpanded:
|
||||
set_memory_size(Default);
|
||||
break;
|
||||
case StaticAnalyser::Vic20MemoryModel::EightKB:
|
||||
set_memory_size(ThreeKB);
|
||||
break;
|
||||
case StaticAnalyser::Vic20MemoryModel::ThirtyTwoKB:
|
||||
set_memory_size(ThirtyTwoKB);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Machine::set_typer_for_string(const char *string) {
|
||||
void set_typer_for_string(const char *string) {
|
||||
std::unique_ptr<CharacterMapper> mapper(new CharacterMapper());
|
||||
Utility::TypeRecipient::set_typer_for_string(string, std::move(mapper));
|
||||
}
|
||||
}
|
||||
|
||||
void Machine::tape_did_change_input(Storage::Tape::BinaryTapePlayer *tape) {
|
||||
void tape_did_change_input(Storage::Tape::BinaryTapePlayer *tape) {
|
||||
keyboard_via_->set_control_line_input(KeyboardVIA::Port::A, KeyboardVIA::Line::One, !tape->get_input());
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Disc
|
||||
private:
|
||||
CPU::MOS6502::Processor<ConcreteMachine> m6502_;
|
||||
|
||||
void Machine::install_disk_rom() {
|
||||
uint8_t character_rom_[0x1000];
|
||||
uint8_t basic_rom_[0x2000];
|
||||
uint8_t kernel_rom_[0x2000];
|
||||
uint8_t expansion_ram_[0x8000];
|
||||
|
||||
uint8_t *rom_;
|
||||
uint16_t rom_address_, rom_length_;
|
||||
|
||||
uint8_t user_basic_memory_[0x0400];
|
||||
uint8_t screen_memory_[0x1000];
|
||||
uint8_t colour_memory_[0x0400];
|
||||
std::vector<uint8_t> drive_rom_;
|
||||
|
||||
uint8_t *processor_read_memory_map_[64];
|
||||
uint8_t *processor_write_memory_map_[64];
|
||||
void write_to_map(uint8_t **map, uint8_t *area, uint16_t address, uint16_t length) {
|
||||
address >>= 10;
|
||||
length >>= 10;
|
||||
while(length--) {
|
||||
map[address] = area;
|
||||
area += 0x400;
|
||||
address++;
|
||||
}
|
||||
}
|
||||
|
||||
Region region_;
|
||||
|
||||
std::unique_ptr<Vic6560> mos6560_;
|
||||
std::shared_ptr<UserPortVIA> user_port_via_;
|
||||
std::shared_ptr<KeyboardVIA> keyboard_via_;
|
||||
std::shared_ptr<SerialPort> serial_port_;
|
||||
std::shared_ptr<::Commodore::Serial::Bus> serial_bus_;
|
||||
|
||||
// Tape
|
||||
std::shared_ptr<Storage::Tape::BinaryTapePlayer> tape_;
|
||||
bool use_fast_tape_hack_;
|
||||
bool is_running_at_zero_cost_;
|
||||
|
||||
// Disk
|
||||
std::shared_ptr<::Commodore::C1540::Machine> c1540_;
|
||||
void install_disk_rom() {
|
||||
if(!drive_rom_.empty() && c1540_) {
|
||||
c1540_->set_rom(drive_rom_);
|
||||
c1540_->run_for(Cycles(2000000));
|
||||
drive_rom_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - UserPortVIA
|
||||
|
||||
uint8_t UserPortVIA::get_port_input(Port port) {
|
||||
if(!port) {
|
||||
return port_a_ | (tape_->has_tape() ? 0x00 : 0x40);
|
||||
}
|
||||
return 0xff;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void UserPortVIA::set_control_line_output(Port port, Line line, bool value) {
|
||||
if(port == Port::A && line == Line::Two) {
|
||||
tape_->set_motor_control(!value);
|
||||
}
|
||||
using namespace Commodore::Vic20;
|
||||
|
||||
Machine *Machine::Vic20() {
|
||||
return new Vic20::ConcreteMachine;
|
||||
}
|
||||
|
||||
void UserPortVIA::set_serial_line_state(::Commodore::Serial::Line line, bool value) {
|
||||
switch(line) {
|
||||
default: break;
|
||||
case ::Commodore::Serial::Line::Data: port_a_ = (port_a_ & ~0x02) | (value ? 0x02 : 0x00); break;
|
||||
case ::Commodore::Serial::Line::Clock: port_a_ = (port_a_ & ~0x01) | (value ? 0x01 : 0x00); break;
|
||||
}
|
||||
}
|
||||
|
||||
void UserPortVIA::set_joystick_state(JoystickInput input, bool value) {
|
||||
if(input != JoystickInput::Right) {
|
||||
port_a_ = (port_a_ & ~input) | (value ? 0 : input);
|
||||
}
|
||||
}
|
||||
|
||||
void UserPortVIA::set_port_output(Port port, uint8_t value, uint8_t mask) {
|
||||
// Line 7 of port A is inverted and output as serial ATN
|
||||
if(!port) {
|
||||
std::shared_ptr<::Commodore::Serial::Port> serialPort = serial_port_.lock();
|
||||
if(serialPort)
|
||||
serialPort->set_output(::Commodore::Serial::Line::Attention, (::Commodore::Serial::LineLevel)!(value&0x80));
|
||||
}
|
||||
}
|
||||
|
||||
UserPortVIA::UserPortVIA() : port_a_(0xbf) {}
|
||||
|
||||
void UserPortVIA::set_serial_port(std::shared_ptr<::Commodore::Serial::Port> serialPort) {
|
||||
serial_port_ = serialPort;
|
||||
}
|
||||
|
||||
void UserPortVIA::set_tape(std::shared_ptr<Storage::Tape::BinaryTapePlayer> tape) {
|
||||
tape_ = tape;
|
||||
}
|
||||
|
||||
#pragma mark - KeyboardVIA
|
||||
|
||||
KeyboardVIA::KeyboardVIA() : port_b_(0xff) {
|
||||
clear_all_keys();
|
||||
}
|
||||
|
||||
void KeyboardVIA::set_key_state(uint16_t key, bool isPressed) {
|
||||
if(isPressed)
|
||||
columns_[key & 7] &= ~(key >> 3);
|
||||
else
|
||||
columns_[key & 7] |= (key >> 3);
|
||||
}
|
||||
|
||||
void KeyboardVIA::clear_all_keys() {
|
||||
memset(columns_, 0xff, sizeof(columns_));
|
||||
}
|
||||
|
||||
uint8_t KeyboardVIA::get_port_input(Port port) {
|
||||
if(!port) {
|
||||
uint8_t result = 0xff;
|
||||
for(int c = 0; c < 8; c++) {
|
||||
if(!(activation_mask_&(1 << c)))
|
||||
result &= columns_[c];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return port_b_;
|
||||
}
|
||||
|
||||
void KeyboardVIA::set_port_output(Port port, uint8_t value, uint8_t mask) {
|
||||
if(port)
|
||||
activation_mask_ = (value & mask) | (~mask);
|
||||
}
|
||||
|
||||
void KeyboardVIA::set_control_line_output(Port port, Line line, bool value) {
|
||||
if(line == Line::Two) {
|
||||
std::shared_ptr<::Commodore::Serial::Port> serialPort = serial_port_.lock();
|
||||
if(serialPort) {
|
||||
// CB2 is inverted to become serial data; CA2 is inverted to become serial clock
|
||||
if(port == Port::A)
|
||||
serialPort->set_output(::Commodore::Serial::Line::Clock, (::Commodore::Serial::LineLevel)!value);
|
||||
else
|
||||
serialPort->set_output(::Commodore::Serial::Line::Data, (::Commodore::Serial::LineLevel)!value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardVIA::set_joystick_state(JoystickInput input, bool value) {
|
||||
if(input == JoystickInput::Right) {
|
||||
port_b_ = (port_b_ & ~input) | (value ? 0 : input);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardVIA::set_serial_port(std::shared_ptr<::Commodore::Serial::Port> serialPort) {
|
||||
serial_port_ = serialPort;
|
||||
}
|
||||
|
||||
#pragma mark - SerialPort
|
||||
|
||||
void SerialPort::set_input(::Commodore::Serial::Line line, ::Commodore::Serial::LineLevel level) {
|
||||
std::shared_ptr<UserPortVIA> userPortVIA = user_port_via_.lock();
|
||||
if(userPortVIA) userPortVIA->set_serial_line_state(line, (bool)level);
|
||||
}
|
||||
|
||||
void SerialPort::set_user_port_via(std::shared_ptr<UserPortVIA> userPortVIA) {
|
||||
user_port_via_ = userPortVIA;
|
||||
}
|
||||
Machine::~Machine() {}
|
||||
|
@ -13,15 +13,7 @@
|
||||
#include "../../CRTMachine.hpp"
|
||||
#include "../../Typer.hpp"
|
||||
|
||||
#include "../../../Processors/6502/6502.hpp"
|
||||
#include "../../../Components/6560/6560.hpp"
|
||||
#include "../../../Components/6522/6522.hpp"
|
||||
|
||||
#include "../SerialBus.hpp"
|
||||
#include "../1540/C1540.hpp"
|
||||
|
||||
#include "../../../Storage/Tape/Tape.hpp"
|
||||
#include "../../../Storage/Disk/Disk.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
namespace Commodore {
|
||||
namespace Vic20 {
|
||||
@ -44,9 +36,8 @@ enum Region {
|
||||
PAL
|
||||
};
|
||||
|
||||
#define key(line, mask) (((mask) << 3) | (line))
|
||||
|
||||
enum Key: uint16_t {
|
||||
#define key(line, mask) (((mask) << 3) | (line))
|
||||
Key2 = key(7, 0x01), Key4 = key(7, 0x02), Key6 = key(7, 0x04), Key8 = key(7, 0x08),
|
||||
Key0 = key(7, 0x10), KeyDash = key(7, 0x20), KeyHome = key(7, 0x40), KeyF7 = key(7, 0x80),
|
||||
KeyQ = key(6, 0x01), KeyE = key(6, 0x02), KeyT = key(6, 0x04), KeyU = key(6, 0x08),
|
||||
@ -63,6 +54,7 @@ enum Key: uint16_t {
|
||||
KeyI = key(1, 0x10), KeyP = key(1, 0x20), KeyAsterisk = key(1, 0x40), KeyReturn = key(1, 0x80),
|
||||
Key1 = key(0, 0x01), Key3 = key(0, 0x02), Key5 = key(0, 0x04), Key7 = key(0, 0x08),
|
||||
Key9 = key(0, 0x10), KeyPlus = key(0, 0x20), KeyGBP = key(0, 0x40), KeyDelete = key(0, 0x80),
|
||||
#undef key
|
||||
};
|
||||
|
||||
enum JoystickInput {
|
||||
@ -73,155 +65,31 @@ enum JoystickInput {
|
||||
Fire = 0x20
|
||||
};
|
||||
|
||||
class UserPortVIA: public MOS::MOS6522<UserPortVIA>, public MOS::MOS6522IRQDelegate {
|
||||
public:
|
||||
UserPortVIA();
|
||||
using MOS6522IRQDelegate::set_interrupt_status;
|
||||
|
||||
uint8_t get_port_input(Port port);
|
||||
void set_control_line_output(Port port, Line line, bool value);
|
||||
void set_serial_line_state(::Commodore::Serial::Line line, bool value);
|
||||
void set_joystick_state(JoystickInput input, bool value);
|
||||
void set_port_output(Port port, uint8_t value, uint8_t mask);
|
||||
|
||||
void set_serial_port(std::shared_ptr<::Commodore::Serial::Port> serialPort);
|
||||
void set_tape(std::shared_ptr<Storage::Tape::BinaryTapePlayer> tape);
|
||||
|
||||
private:
|
||||
uint8_t port_a_;
|
||||
std::weak_ptr<::Commodore::Serial::Port> serial_port_;
|
||||
std::shared_ptr<Storage::Tape::BinaryTapePlayer> tape_;
|
||||
};
|
||||
|
||||
class KeyboardVIA: public MOS::MOS6522<KeyboardVIA>, public MOS::MOS6522IRQDelegate {
|
||||
public:
|
||||
KeyboardVIA();
|
||||
using MOS6522IRQDelegate::set_interrupt_status;
|
||||
|
||||
void set_key_state(uint16_t key, bool isPressed);
|
||||
void clear_all_keys();
|
||||
|
||||
// to satisfy MOS::MOS6522
|
||||
uint8_t get_port_input(Port port);
|
||||
|
||||
void set_port_output(Port port, uint8_t value, uint8_t mask);
|
||||
void set_control_line_output(Port port, Line line, bool value);
|
||||
|
||||
void set_joystick_state(JoystickInput input, bool value);
|
||||
|
||||
void set_serial_port(std::shared_ptr<::Commodore::Serial::Port> serialPort);
|
||||
|
||||
private:
|
||||
uint8_t port_b_;
|
||||
uint8_t columns_[8];
|
||||
uint8_t activation_mask_;
|
||||
std::weak_ptr<::Commodore::Serial::Port> serial_port_;
|
||||
};
|
||||
|
||||
class SerialPort : public ::Commodore::Serial::Port {
|
||||
public:
|
||||
void set_input(::Commodore::Serial::Line line, ::Commodore::Serial::LineLevel level);
|
||||
void set_user_port_via(std::shared_ptr<UserPortVIA> userPortVIA);
|
||||
|
||||
private:
|
||||
std::weak_ptr<UserPortVIA> user_port_via_;
|
||||
};
|
||||
|
||||
class Vic6560: public MOS::MOS6560<Vic6560> {
|
||||
public:
|
||||
inline void perform_read(uint16_t address, uint8_t *pixel_data, uint8_t *colour_data) {
|
||||
*pixel_data = video_memory_map[address >> 10] ? video_memory_map[address >> 10][address & 0x3ff] : 0xff; // TODO
|
||||
*colour_data = colour_memory[address & 0x03ff];
|
||||
}
|
||||
|
||||
uint8_t *video_memory_map[16];
|
||||
uint8_t *colour_memory;
|
||||
};
|
||||
|
||||
class Machine:
|
||||
public CPU::MOS6502::BusHandler,
|
||||
public ConfigurationTarget::Machine,
|
||||
public CRTMachine::Machine,
|
||||
public MOS::MOS6522IRQDelegate::Delegate,
|
||||
public Utility::TypeRecipient,
|
||||
public Storage::Tape::BinaryTapePlayer::Delegate,
|
||||
public ConfigurationTarget::Machine {
|
||||
|
||||
public KeyboardMachine::Machine {
|
||||
public:
|
||||
Machine();
|
||||
~Machine();
|
||||
virtual ~Machine();
|
||||
|
||||
void set_rom(ROMSlot slot, size_t length, const uint8_t *data);
|
||||
void configure_as_target(const StaticAnalyser::Target &target);
|
||||
/// Creates and returns a Vic-20.
|
||||
static Machine *Vic20();
|
||||
|
||||
void set_key_state(uint16_t key, bool isPressed) { keyboard_via_->set_key_state(key, isPressed); }
|
||||
void clear_all_keys() { keyboard_via_->clear_all_keys(); }
|
||||
void set_joystick_state(JoystickInput input, bool isPressed) {
|
||||
user_port_via_->set_joystick_state(input, isPressed);
|
||||
keyboard_via_->set_joystick_state(input, isPressed);
|
||||
}
|
||||
/// Sets the contents of the rom in @c slot to the buffer @c data of length @c length.
|
||||
virtual void set_rom(ROMSlot slot, size_t length, const uint8_t *data) = 0;
|
||||
// TODO: take a std::vector<uint8_t> to collapse length and data.
|
||||
|
||||
void set_memory_size(MemorySize size);
|
||||
void set_region(Region region);
|
||||
/// Sets the state of joystick input @c input.
|
||||
virtual void set_joystick_state(JoystickInput input, bool isPressed) = 0;
|
||||
|
||||
inline void set_use_fast_tape_hack(bool activate) { use_fast_tape_hack_ = activate; }
|
||||
/// Sets the memory size of this Vic-20.
|
||||
virtual void set_memory_size(MemorySize size) = 0;
|
||||
|
||||
// to satisfy CPU::MOS6502::Processor
|
||||
Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value);
|
||||
void flush() { mos6560_->flush(); }
|
||||
/// Sets the region of this Vic-20.
|
||||
virtual void set_region(Region region) = 0;
|
||||
|
||||
// to satisfy CRTMachine::Machine
|
||||
virtual void setup_output(float aspect_ratio);
|
||||
virtual void close_output();
|
||||
virtual std::shared_ptr<Outputs::CRT::CRT> get_crt() { return mos6560_->get_crt(); }
|
||||
virtual std::shared_ptr<Outputs::Speaker> get_speaker() { return mos6560_->get_speaker(); }
|
||||
virtual void run_for(const Cycles cycles);
|
||||
|
||||
// to satisfy MOS::MOS6522::Delegate
|
||||
virtual void mos6522_did_change_interrupt_status(void *mos6522);
|
||||
|
||||
// for Utility::TypeRecipient
|
||||
uint16_t *sequence_for_character(Utility::Typer *typer, char character);
|
||||
void set_typer_for_string(const char *string);
|
||||
|
||||
// for Tape::Delegate
|
||||
virtual void tape_did_change_input(Storage::Tape::BinaryTapePlayer *tape);
|
||||
|
||||
private:
|
||||
CPU::MOS6502::Processor<Machine> m6502_;
|
||||
|
||||
uint8_t character_rom_[0x1000];
|
||||
uint8_t basic_rom_[0x2000];
|
||||
uint8_t kernel_rom_[0x2000];
|
||||
uint8_t expansion_ram_[0x8000];
|
||||
|
||||
uint8_t *rom_;
|
||||
uint16_t rom_address_, rom_length_;
|
||||
|
||||
uint8_t user_basic_memory_[0x0400];
|
||||
uint8_t screen_memory_[0x1000];
|
||||
uint8_t colour_memory_[0x0400];
|
||||
std::vector<uint8_t> drive_rom_;
|
||||
|
||||
uint8_t *processor_read_memory_map_[64];
|
||||
uint8_t *processor_write_memory_map_[64];
|
||||
void write_to_map(uint8_t **map, uint8_t *area, uint16_t address, uint16_t length);
|
||||
|
||||
Region region_;
|
||||
|
||||
std::unique_ptr<Vic6560> mos6560_;
|
||||
std::shared_ptr<UserPortVIA> user_port_via_;
|
||||
std::shared_ptr<KeyboardVIA> keyboard_via_;
|
||||
std::shared_ptr<SerialPort> serial_port_;
|
||||
std::shared_ptr<::Commodore::Serial::Bus> serial_bus_;
|
||||
|
||||
// Tape
|
||||
std::shared_ptr<Storage::Tape::BinaryTapePlayer> tape_;
|
||||
bool use_fast_tape_hack_;
|
||||
bool is_running_at_zero_cost_;
|
||||
|
||||
// Disk
|
||||
std::shared_ptr<::Commodore::C1540::Machine> c1540_;
|
||||
void install_disk_rom();
|
||||
/// Enables or disables turbo-speed tape loading.
|
||||
virtual void set_use_fast_tape_hack(bool activate) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -18,17 +18,21 @@
|
||||
using namespace Commodore::Vic20;
|
||||
|
||||
@implementation CSVic20 {
|
||||
Machine _vic20;
|
||||
std::unique_ptr<Machine> _vic20;
|
||||
BOOL _joystickMode;
|
||||
}
|
||||
|
||||
- (CRTMachine::Machine * const)machine { return &_vic20; }
|
||||
- (CRTMachine::Machine * const)machine {
|
||||
if(!_vic20) {
|
||||
_vic20.reset(Commodore::Vic20::Machine::Vic20());
|
||||
}
|
||||
return _vic20.get();
|
||||
}
|
||||
- (NSString *)userDefaultsPrefix { return @"vic20"; }
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if(self)
|
||||
{
|
||||
if(self) {
|
||||
[self setDriveROM:[[NSBundle mainBundle] dataForResource:@"1540" withExtension:@"bin" subdirectory:@"ROMImages/Commodore1540"]];
|
||||
[self setBASICROM:[self rom:@"basic"]];
|
||||
[self setCountry:CSVic20CountryEuropean];
|
||||
@ -36,8 +40,7 @@ using namespace Commodore::Vic20;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSData *)rom:(NSString *)name
|
||||
{
|
||||
- (NSData *)rom:(NSString *)name {
|
||||
return [[NSBundle mainBundle] dataForResource:name withExtension:@"bin" subdirectory:@"ROMImages/Vic20"];
|
||||
}
|
||||
|
||||
@ -45,7 +48,7 @@ using namespace Commodore::Vic20;
|
||||
|
||||
- (void)setROM:(nonnull NSData *)rom slot:(ROMSlot)slot {
|
||||
@synchronized(self) {
|
||||
_vic20.set_rom(slot, rom.length, (const uint8_t *)rom.bytes);
|
||||
_vic20->set_rom(slot, rom.length, (const uint8_t *)rom.bytes);
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,32 +123,26 @@ using namespace Commodore::Vic20;
|
||||
// KeyPlus
|
||||
// KeyGBP
|
||||
|
||||
if(key == VK_Tab && isPressed)
|
||||
{
|
||||
if(key == VK_Tab && isPressed) {
|
||||
_joystickMode ^= YES;
|
||||
}
|
||||
|
||||
@synchronized(self) {
|
||||
if(_joystickMode)
|
||||
{
|
||||
switch(key)
|
||||
{
|
||||
case VK_UpArrow: _vic20.set_joystick_state(JoystickInput::Up, isPressed); break;
|
||||
case VK_DownArrow: _vic20.set_joystick_state(JoystickInput::Down, isPressed); break;
|
||||
case VK_LeftArrow: _vic20.set_joystick_state(JoystickInput::Left, isPressed); break;
|
||||
case VK_RightArrow: _vic20.set_joystick_state(JoystickInput::Right, isPressed); break;
|
||||
case VK_ANSI_A: _vic20.set_joystick_state(JoystickInput::Fire, isPressed); break;
|
||||
if(_joystickMode) {
|
||||
switch(key) {
|
||||
case VK_UpArrow: _vic20->set_joystick_state(JoystickInput::Up, isPressed); break;
|
||||
case VK_DownArrow: _vic20->set_joystick_state(JoystickInput::Down, isPressed); break;
|
||||
case VK_LeftArrow: _vic20->set_joystick_state(JoystickInput::Left, isPressed); break;
|
||||
case VK_RightArrow: _vic20->set_joystick_state(JoystickInput::Right, isPressed); break;
|
||||
case VK_ANSI_A: _vic20->set_joystick_state(JoystickInput::Fire, isPressed); break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(key)
|
||||
{
|
||||
} else {
|
||||
switch(key) {
|
||||
default: {
|
||||
NSNumber *targetKey = vicKeysByKeys[@(key)];
|
||||
if(targetKey)
|
||||
{
|
||||
_vic20.set_key_state((Key)targetKey.integerValue, isPressed);
|
||||
_vic20->set_key_state((Key)targetKey.integerValue, isPressed);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -155,8 +152,8 @@ using namespace Commodore::Vic20;
|
||||
|
||||
case VK_Shift:
|
||||
// Yuck
|
||||
_vic20.set_key_state(Key::KeyLShift, isPressed);
|
||||
_vic20.set_key_state(Key::KeyRShift, isPressed);
|
||||
_vic20->set_key_state(Key::KeyLShift, isPressed);
|
||||
_vic20->set_key_state(Key::KeyRShift, isPressed);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -165,7 +162,7 @@ using namespace Commodore::Vic20;
|
||||
|
||||
- (void)clearAllKeys {
|
||||
@synchronized(self) {
|
||||
_vic20.clear_all_keys();
|
||||
_vic20->clear_all_keys();
|
||||
}
|
||||
}
|
||||
|
||||
@ -174,7 +171,7 @@ using namespace Commodore::Vic20;
|
||||
- (void)setUseFastLoadingHack:(BOOL)useFastLoadingHack {
|
||||
_useFastLoadingHack = useFastLoadingHack;
|
||||
@synchronized(self) {
|
||||
_vic20.set_use_fast_tape_hack(useFastLoadingHack ? true : false);
|
||||
_vic20->set_use_fast_tape_hack(useFastLoadingHack ? true : false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,8 +179,7 @@ using namespace Commodore::Vic20;
|
||||
_country = country;
|
||||
NSString *charactersROM, *kernelROM;
|
||||
Commodore::Vic20::Region region;
|
||||
switch(country)
|
||||
{
|
||||
switch(country) {
|
||||
case CSVic20CountryDanish:
|
||||
region = Commodore::Vic20::Region::PAL;
|
||||
charactersROM = @"characters-danish";
|
||||
@ -212,7 +208,7 @@ using namespace Commodore::Vic20;
|
||||
}
|
||||
|
||||
@synchronized(self) {
|
||||
_vic20.set_region(region);
|
||||
_vic20->set_region(region);
|
||||
[self setCharactersROM:[self rom:charactersROM]];
|
||||
[self setKernelROM:[self rom:kernelROM]];
|
||||
}
|
||||
@ -222,9 +218,9 @@ using namespace Commodore::Vic20;
|
||||
_memorySize = memorySize;
|
||||
@synchronized(self) {
|
||||
switch(memorySize) {
|
||||
case CSVic20MemorySize5Kb: _vic20.set_memory_size(Commodore::Vic20::Default); break;
|
||||
case CSVic20MemorySize8Kb: _vic20.set_memory_size(Commodore::Vic20::ThreeKB); break;
|
||||
case CSVic20MemorySize32Kb: _vic20.set_memory_size(Commodore::Vic20::ThirtyTwoKB); break;
|
||||
case CSVic20MemorySize5Kb: _vic20->set_memory_size(Commodore::Vic20::Default); break;
|
||||
case CSVic20MemorySize8Kb: _vic20->set_memory_size(Commodore::Vic20::ThreeKB); break;
|
||||
case CSVic20MemorySize32Kb: _vic20->set_memory_size(Commodore::Vic20::ThirtyTwoKB); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user