Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a simulacrum of C16+4 emulation. #1434

Open
wants to merge 62 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
949cfcf
Load ROMs.
TomHarte Dec 9, 2024
cbde504
Add a memory map of sorts and a 6502.
TomHarte Dec 9, 2024
064c4b4
Add some logging.
TomHarte Dec 10, 2024
ab2a576
Merge branch 'master' into Plus4Startup
TomHarte Dec 10, 2024
6b7edac
Add timers.
TomHarte Dec 10, 2024
891d5c2
Separate out TED calls, to aid with logging.
TomHarte Dec 10, 2024
3832574
Forward address information to a video stub.
TomHarte Dec 11, 2024
aed8f8e
Transcribe some timing numbers.
TomHarte Dec 11, 2024
84d178c
Transcribe event times into [mostly] non-action.
TomHarte Dec 11, 2024
389ba95
Template out the usual repetitive stuff of segment finding.
TomHarte Dec 12, 2024
0eab614
Introduce a CRT.
TomHarte Dec 12, 2024
a487619
Track basic frame events.
TomHarte Dec 12, 2024
8854ffd
Include possible clock divider.
TomHarte Dec 12, 2024
f7750af
Provide bus visibility to video; mark vertical portion of display.
TomHarte Dec 12, 2024
41c6ed7
Further restrict 'active' area of the display.
TomHarte Dec 12, 2024
1d07b82
Add a crop rectangle.
TomHarte Dec 12, 2024
ed766c7
Add some paging.
TomHarte Dec 13, 2024
58b464b
Attempt to add interrupts.
TomHarte Dec 13, 2024
c2fc260
Add background colour reading, fix writing.
TomHarte Dec 13, 2024
663acd3
Map initial border colour, white and black.
TomHarte Dec 13, 2024
363ad73
Add memory fuzzing, some text output.
TomHarte Dec 13, 2024
ff92bdb
Add buffer for pixels, output _something_.
TomHarte Dec 13, 2024
1d9c3fb
Hack in some text output.
TomHarte Dec 13, 2024
83a9ef7
Add TODO explaining all currently-unhandled writes.
TomHarte Dec 13, 2024
1b5d446
Fix: writes to interrupt status _clear_.
TomHarte Dec 13, 2024
1628af2
Provide a stuck down key 'a'.
TomHarte Dec 13, 2024
a1f6e93
Add most of the keyboard.
TomHarte Dec 14, 2024
700b848
Add overt `nil`s to aid with debugging.
TomHarte Dec 14, 2024
f41b54d
Make a close-enough guess at chrominances.
TomHarte Dec 14, 2024
589903c
Add safety rail.
TomHarte Dec 14, 2024
3e93004
Double nominal clock, to hit normative values.
TomHarte Dec 14, 2024
702b6d6
Add extra note to self.
TomHarte Dec 14, 2024
b76104d
Start edging towards proper video timing.
TomHarte Dec 15, 2024
3d7e016
Name horizontal events.
TomHarte Dec 15, 2024
709f350
Begin usage of RDY.
TomHarte Dec 16, 2024
9d5c10d
Edge towards realistic video collection.
TomHarte Dec 17, 2024
2240ada
Avoid going out of bounds below.
TomHarte Dec 17, 2024
5acdf39
Use a normative, unique_ptr-based cache.
TomHarte Dec 17, 2024
15583e7
Avoid risk of unbounded memory consumption.
TomHarte Dec 17, 2024
c8fdde4
Clarify clock rates.
TomHarte Dec 17, 2024
7466da5
Add Keyboard.
TomHarte Dec 18, 2024
81398d5
Improve `get_rect_for_area`, use in C16.
TomHarte Dec 19, 2024
096f48b
Fix top line of cursor, add pretend cursor, page video separately.
TomHarte Dec 19, 2024
4f93dc0
Support attribute bytes.
TomHarte Dec 19, 2024
b08cc9c
Use attributes, attempt real cursor.
TomHarte Dec 19, 2024
c8ad8c7
Factor in x_scroll_.
TomHarte Dec 19, 2024
e6523f3
Shuffle colour conversion moment; move ownership of clock rate.
TomHarte Dec 20, 2024
4bb53ed
Start trending towards an FPGA-inspired implementation.
TomHarte Dec 27, 2024
6b90de5
Add data delays.
TomHarte Dec 27, 2024
863b09c
Increase key bindings.
TomHarte Dec 27, 2024
9670d5f
Add cursor, proving things generally to be off by one.
TomHarte Dec 27, 2024
e19fe5d
Reintroduce colour burst.
TomHarte Dec 27, 2024
ff12bbb
Simplify delay state.
TomHarte Dec 27, 2024
2cfd2ff
Start adding tape player.
TomHarte Dec 28, 2024
01aeb46
Bump version.
TomHarte Dec 28, 2024
80f5d7c
Attempt full tape input.
TomHarte Dec 28, 2024
b4b216d
Attempt press-play feedback.
TomHarte Dec 28, 2024
c92b0dc
Start normalising Commodore serial bus interface.
TomHarte Dec 29, 2024
6f63880
Further eliminate std::shared_ptr connections.
TomHarte Dec 29, 2024
570f1ca
Attempt also to integrate a C1541.
TomHarte Dec 29, 2024
da6efe5
Accept implicit bool, eliminate rom name repetition.
TomHarte Dec 29, 2024
34938e8
Fully connect serial port.
TomHarte Dec 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 25 additions & 15 deletions Analyser/Static/Commodore/Disk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <limits>
#include <vector>
#include <array>
#include <unordered_map>

using namespace Analyser::Static::Commodore;

Expand All @@ -37,7 +38,7 @@ class CommodoreGCRParser: public Storage::Disk::Controller {

@returns a sector if one was found; @c nullptr otherwise.
*/
std::shared_ptr<Sector> sector(const uint8_t track, const uint8_t sector) {
const Sector *sector(const uint8_t track, const uint8_t sector) {
int difference = int(track) - int(track_);
track_ = track;

Expand Down Expand Up @@ -68,7 +69,7 @@ class CommodoreGCRParser: public Storage::Disk::Controller {
int index_count_;
int bit_count_;
uint8_t track_;
std::shared_ptr<Sector> sector_cache_[65536];
std::unordered_map<uint16_t, std::unique_ptr<Sector>> sector_cache_;

void process_input_bit(const int value) override {
shift_register_ = ((shift_register_ << 1) | unsigned(value)) & 0x3ff;
Expand Down Expand Up @@ -111,23 +112,24 @@ class CommodoreGCRParser: public Storage::Disk::Controller {
index_count_++;
}

std::shared_ptr<Sector> get_sector(const uint8_t sector) {
const Sector *get_sector(const uint8_t sector) {
const uint16_t sector_address = uint16_t((track_ << 8) | sector);
if(sector_cache_[sector_address]) return sector_cache_[sector_address];
auto existing = sector_cache_.find(sector_address);
if(existing != sector_cache_.end()) return existing->second.get();

const std::shared_ptr<Sector> first_sector = get_next_sector();
const auto first_sector = get_next_sector();
if(!first_sector) return first_sector;
if(first_sector->sector == sector) return first_sector;

while(1) {
const std::shared_ptr<Sector> next_sector = get_next_sector();
while(true) {
const auto next_sector = get_next_sector();
if(next_sector->sector == first_sector->sector) return nullptr;
if(next_sector->sector == sector) return next_sector;
}
}

std::shared_ptr<Sector> get_next_sector() {
auto sector = std::make_shared<Sector>();
const Sector *get_next_sector() {
auto sector = std::make_unique<Sector>();
const int max_index_count = index_count_ + 2;

while(index_count_ < max_index_count) {
Expand Down Expand Up @@ -160,8 +162,8 @@ class CommodoreGCRParser: public Storage::Disk::Controller {

if(checksum == get_next_byte()) {
uint16_t sector_address = uint16_t((sector->track << 8) | sector->sector);
sector_cache_[sector_address] = sector;
return sector;
auto pair = sector_cache_.emplace(sector_address, std::move(sector));
return pair.first->second.get();
}
}

Expand All @@ -171,21 +173,29 @@ class CommodoreGCRParser: public Storage::Disk::Controller {

std::vector<File> Analyser::Static::Commodore::GetFiles(const std::shared_ptr<Storage::Disk::Disk> &disk) {
std::vector<File> files;
auto parser = std::make_unique<CommodoreGCRParser>();
parser->set_disk(disk);
CommodoreGCRParser parser;
parser.set_disk(disk);

// Assemble directory.
std::vector<uint8_t> directory;
uint8_t next_track = 18;
uint8_t next_sector = 1;
directory.reserve(20 * 1024); // Probably more than plenty.
std::set<std::pair<uint8_t, uint8_t>> visited;
while(true) {
auto sector = parser->sector(next_track, next_sector);
// Don't be fooled by disks that are encoded with a looping directory.
const auto key = std::make_pair(next_track, next_sector);
if(visited.find(key) != visited.end()) break;
visited.insert(key);

// Append sector to directory and follow next link.
const auto sector = parser.sector(next_track, next_sector);
if(!sector) break;
directory.insert(directory.end(), sector->data.begin(), sector->data.end());
next_track = sector->data[0];
next_sector = sector->data[1];

// Check for end-of-directory.
if(!next_track) break;
}

Expand Down Expand Up @@ -222,7 +232,7 @@ std::vector<File> Analyser::Static::Commodore::GetFiles(const std::shared_ptr<St

bool is_first_sector = true;
while(next_track) {
auto sector = parser->sector(next_track, next_sector);
const auto sector = parser.sector(next_track, next_sector);
if(!sector) break;

next_track = sector->data[0];
Expand Down
6 changes: 1 addition & 5 deletions Analyser/Static/Commodore/StaticAnalyser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ std::optional<BASICAnalysis> analyse(const File &file) {
while(true) {
// Analysis has failed if there isn't at least one complete BASIC line from here.
// Fall back on guessing the start address as a machine code entrypoint.
if(size_t(line_address - file.starting_address) + 5 >= file.data.size()) {
if(size_t(line_address - file.starting_address) + 5 >= file.data.size() || line_address < file.starting_address) {
analysis.machine_code_addresses.push_back(file.starting_address);
break;
}
Expand Down Expand Up @@ -209,10 +209,6 @@ Analyser::Static::TargetList Analyser::Static::Commodore::GetTargets(
// Inspect discovered files to try to divine machine and memory model.
auto vic_memory_model = Target::MemoryModel::Unexpanded;

if(files.size() > 1) {
printf("");
}

auto it = files.begin();
while(it != files.end()) {
const auto &file = *it;
Expand Down
5 changes: 4 additions & 1 deletion Machines/Atari/ST/Video.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,10 @@ Video::Video() :

// Show a total of 260 lines; a little short for PAL but a compromise between that and the ST's
// usual output height of 200 lines.
crt_.set_visible_area(crt_.get_rect_for_area(33, 260, 440, 1700, 4.0f / 3.0f));
crt_.set_visible_area(crt_.get_rect_for_area(
33, 260,
480, 1280,
4.0f / 3.0f));
}

void Video::set_ram(uint16_t *ram, size_t size) {
Expand Down
22 changes: 11 additions & 11 deletions Machines/Commodore/1540/C1540.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,20 @@ namespace Commodore::C1540 {
Provides an emulation of the C1540.
*/
class Machine final: public MachineBase {
public:
static ROM::Request rom_request(Personality personality);
Machine(Personality personality, const ROM::Map &roms);
public:
static ROM::Request rom_request(Personality personality);
Machine(Personality personality, const ROM::Map &roms);

/*!
Sets the serial bus to which this drive should attach itself.
*/
void set_serial_bus(std::shared_ptr<::Commodore::Serial::Bus> serial_bus);
/*!
Sets the serial bus to which this drive should attach itself.
*/
void set_serial_bus(Commodore::Serial::Bus &);

/// Advances time.
void run_for(const Cycles cycles);
/// Advances time.
void run_for(Cycles);

/// Inserts @c disk into the drive.
void set_disk(std::shared_ptr<Storage::Disk::Disk> disk);
/// Inserts @c disk into the drive.
void set_disk(std::shared_ptr<Storage::Disk::Disk> disk);
};

}
101 changes: 46 additions & 55 deletions Machines/Commodore/1540/Implementation/C1540.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,56 +16,54 @@

using namespace Commodore::C1540;

ROM::Request Machine::rom_request(Personality personality) {
namespace {
ROM::Name rom_name(Personality personality) {
switch(personality) {
default:
case Personality::C1540: return ROM::Request(ROM::Name::Commodore1540);
case Personality::C1541: return ROM::Request(ROM::Name::Commodore1541);
case Personality::C1540: return ROM::Name::Commodore1540;
case Personality::C1541: return ROM::Name::Commodore1541;
}
}
}

ROM::Request Machine::rom_request(Personality personality) {
return ROM::Request(rom_name(personality));
}

MachineBase::MachineBase(Personality personality, const ROM::Map &roms) :
Storage::Disk::Controller(1000000),
m6502_(*this),
serial_port_VIA_port_handler_(new SerialPortVIA(serial_port_VIA_)),
serial_port_(new SerialPort),
serial_port_VIA_port_handler_(serial_port_VIA_),
drive_VIA_(drive_VIA_port_handler_),
serial_port_VIA_(*serial_port_VIA_port_handler_) {
// attach the serial port to its VIA and vice versa
serial_port_->set_serial_port_via(serial_port_VIA_port_handler_);
serial_port_VIA_port_handler_->set_serial_port(serial_port_);
serial_port_VIA_(serial_port_VIA_port_handler_) {
// Attach the serial port to its VIA and vice versa.
serial_port_.set_serial_port_via(serial_port_VIA_port_handler_);
serial_port_VIA_port_handler_.set_serial_port(serial_port_);

// set this instance as the delegate to receive interrupt requests from both VIAs
serial_port_VIA_port_handler_->set_interrupt_delegate(this);
// Set this instance as the delegate to receive interrupt requests from both VIAs.
serial_port_VIA_port_handler_.set_interrupt_delegate(this);
drive_VIA_port_handler_.set_interrupt_delegate(this);
drive_VIA_port_handler_.set_delegate(this);

// set a bit rate
// Set a bit rate.
set_expected_bit_length(Storage::Encodings::CommodoreGCR::length_of_a_bit_in_time_zone(3));

// attach the only drive there is
// Attach the only drive there is.
emplace_drive(1000000, 300, 2);
set_drive(1);

ROM::Name rom_name;
switch(personality) {
default:
case Personality::C1540: rom_name = ROM::Name::Commodore1540; break;
case Personality::C1541: rom_name = ROM::Name::Commodore1541; break;
}

auto rom = roms.find(rom_name);
const auto rom = roms.find(rom_name(personality));
if(rom == roms.end()) {
throw ROMMachine::Error::MissingROMs;
}
std::memcpy(rom_, roms.find(rom_name)->second.data(), std::min(sizeof(rom_), roms.find(rom_name)->second.size()));
std::memcpy(rom_, rom->second.data(), std::min(sizeof(rom_), rom->second.size()));
}

Machine::Machine(Personality personality, const ROM::Map &roms) :
MachineBase(personality, roms) {}

void Machine::set_serial_bus(std::shared_ptr<::Commodore::Serial::Bus> serial_bus) {
Commodore::Serial::AttachPortAndBus(serial_port_, serial_bus);
void Machine::set_serial_bus(Commodore::Serial::Bus &serial_bus) {
Commodore::Serial::attach(serial_port_, serial_bus);
}

Cycles MachineBase::perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
Expand Down Expand Up @@ -155,11 +153,11 @@ void MachineBase::process_index_hole() {}

// MARK: - Drive VIA delegate

void MachineBase::drive_via_did_step_head(void *, int direction) {
void MachineBase::drive_via_did_step_head(void *, const int direction) {
get_drive().step(Storage::Disk::HeadPosition(direction, 2));
}

void MachineBase::drive_via_did_set_data_density(void *, int density) {
void MachineBase::drive_via_did_set_data_density(void *, const int density) {
set_expected_bit_length(Storage::Encodings::CommodoreGCR::length_of_a_bit_in_time_zone(unsigned(density)));
}

Expand All @@ -174,14 +172,11 @@ uint8_t SerialPortVIA::get_port_input(MOS::MOS6522::Port port) {

void SerialPortVIA::set_port_output(MOS::MOS6522::Port port, uint8_t value, uint8_t) {
if(port) {
std::shared_ptr<::Commodore::Serial::Port> serialPort = serial_port_.lock();
if(serialPort) {
attention_acknowledge_level_ = !(value&0x10);
data_level_output_ = (value&0x02);
attention_acknowledge_level_ = !(value&0x10);
data_level_output_ = (value&0x02);

serialPort->set_output(::Commodore::Serial::Line::Clock, ::Commodore::Serial::LineLevel(!(value&0x08)));
update_data_line();
}
serial_port_->set_output(::Commodore::Serial::Line::Clock, ::Commodore::Serial::LineLevel(!(value&0x08)));
update_data_line();
}
}

Expand All @@ -199,17 +194,14 @@ void SerialPortVIA::set_serial_line_state(::Commodore::Serial::Line line, bool v
}
}

void SerialPortVIA::set_serial_port(const std::shared_ptr<::Commodore::Serial::Port> &serialPort) {
serial_port_ = serialPort;
void SerialPortVIA::set_serial_port(Commodore::Serial::Port &port) {
serial_port_ = &port;
}

void SerialPortVIA::update_data_line() {
std::shared_ptr<::Commodore::Serial::Port> serialPort = serial_port_.lock();
if(serialPort) {
// "ATN (Attention) is an input on pin 3 of P2 and P3 that is sensed at PB7 and CA1 of UC3 after being inverted by UA1"
serialPort->set_output(::Commodore::Serial::Line::Data,
::Commodore::Serial::LineLevel(!data_level_output_ && (attention_level_input_ != attention_acknowledge_level_)));
}
// "ATN (Attention) is an input on pin 3 of P2 and P3 that is sensed at PB7 and CA1 of UC3 after being inverted by UA1"
serial_port_->set_output(::Commodore::Serial::Line::Data,
Serial::LineLevel(!data_level_output_ && (attention_level_input_ != attention_acknowledge_level_)));
}

// MARK: - DriveVIA
Expand Down Expand Up @@ -248,23 +240,23 @@ void DriveVIA::set_control_line_output(MOS::MOS6522::Port port, MOS::MOS6522::Li
void DriveVIA::set_port_output(MOS::MOS6522::Port port, uint8_t value, uint8_t) {
if(port) {
if(previous_port_b_output_ != value) {
// record drive motor state
drive_motor_ = !!(value&4);
// Record drive motor state.
drive_motor_ = value&4;

// check for a head step
int step_difference = ((value&3) - (previous_port_b_output_&3))&3;
// Check for a head step.
const int step_difference = ((value&3) - (previous_port_b_output_&3))&3;
if(step_difference) {
if(delegate_) delegate_->drive_via_did_step_head(this, (step_difference == 1) ? 1 : -1);
}

// check for a change in density
int density_difference = (previous_port_b_output_^value) & (3 << 5);
// Check for a change in density.
const int density_difference = (previous_port_b_output_^value) & (3 << 5);
if(density_difference && delegate_) {
delegate_->drive_via_did_set_data_density(this, (value >> 5)&3);
}

// post the LED status
if(observer_) observer_->set_led_status("Drive", !!(value&8));
// Post the LED status.
if(observer_) observer_->set_led_status("Drive", value&8);

previous_port_b_output_ = value;
}
Expand All @@ -275,17 +267,16 @@ void DriveVIA::set_activity_observer(Activity::Observer *observer) {
observer_ = observer;
if(observer) {
observer->register_led("Drive");
observer->set_led_status("Drive", !!(previous_port_b_output_&8));
observer->set_led_status("Drive", previous_port_b_output_&8);
}
}

// MARK: - SerialPort

void SerialPort::set_input(::Commodore::Serial::Line line, ::Commodore::Serial::LineLevel level) {
std::shared_ptr<SerialPortVIA> serialPortVIA = serial_port_VIA_.lock();
if(serialPortVIA) serialPortVIA->set_serial_line_state(line, bool(level));
void SerialPort::set_input(Serial::Line line, Serial::LineLevel level) {
serial_port_VIA_->set_serial_line_state(line, bool(level));
}

void SerialPort::set_serial_port_via(const std::shared_ptr<SerialPortVIA> &serialPortVIA) {
serial_port_VIA_ = serialPortVIA;
void SerialPort::set_serial_port_via(SerialPortVIA &serialPortVIA) {
serial_port_VIA_ = &serialPortVIA;
}
Loading
Loading