diff --git a/Components/6526/6526.hpp b/Components/6526/6526.hpp index 051eed71a..c4408a16c 100644 --- a/Components/6526/6526.hpp +++ b/Components/6526/6526.hpp @@ -16,8 +16,14 @@ namespace MOS { namespace MOS6526 { -class PortHandler { +enum Port { + A = 0, + B = 1 +}; +struct PortHandler { + /// Sets the current output value of @c port and provides @c direction_mask, indicating which pins are marked as output. + void set_port_output([[maybe_unused]] Port port, [[maybe_unused]] uint8_t value) {} }; enum class Personality { @@ -45,6 +51,8 @@ template class MOS6526: private: PortHandlerT &port_handler_; + + template void set_port_output(); }; } diff --git a/Components/6526/Implementation/6526Implementation.hpp b/Components/6526/Implementation/6526Implementation.hpp index 642b2abc8..83d6ff744 100644 --- a/Components/6526/Implementation/6526Implementation.hpp +++ b/Components/6526/Implementation/6526Implementation.hpp @@ -15,12 +15,34 @@ namespace MOS { namespace MOS6526 { +template +template void MOS6526::set_port_output() { + const uint8_t output = registers_.output[port] | (~registers_.data_direction[port]); + port_handler_.set_port_output(Port(port), output); +} + template void MOS6526::write(int address, uint8_t value) { address &= 0xf; switch(address) { - case 2: case 3: - registers_.data_direction[address - 2] = value; + // Port output. + case 0: + registers_.output[0] = value; + set_port_output<0>(); + break; + case 1: + registers_.output[1] = value; + set_port_output<1>(); + break; + + // Port direction. + case 2: + registers_.data_direction[0] = value; + set_port_output<0>(); + break; + case 3: + registers_.data_direction[1] = value; + set_port_output<1>(); break; default: @@ -34,6 +56,7 @@ template uint8_t MOS6526::read(int address) { address &= 0xf; switch(address) { + case 2: case 3: return registers_.data_direction[address - 2]; break; diff --git a/Machines/Amiga/Amiga.cpp b/Machines/Amiga/Amiga.cpp index 2b792df83..36cb70d57 100644 --- a/Machines/Amiga/Amiga.cpp +++ b/Machines/Amiga/Amiga.cpp @@ -29,6 +29,7 @@ class ConcreteMachine: public: ConcreteMachine(const Analyser::Static::Amiga::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : mc68000_(*this), + cia_a_handler_(memory_), cia_a_(cia_a_handler_), cia_b_(cia_b_handler_) { @@ -41,25 +42,7 @@ class ConcreteMachine: if(!request.validate(roms)) { throw ROMMachine::Error::MissingROMs; } - Memory::PackBigEndian16(roms.find(rom_name)->second, kickstart_.data()); - - // Address spaces that matter: - // - // 00'0000 – 08'0000: chip RAM. [or overlayed KickStart] - // – 10'0000: extended chip ram for ECS. - // – 20'0000: auto-config space (/fast RAM). - // ... - // bf'd000 – c0'0000: 8250s. - // c0'0000 – d8'0000: pseudo-fast RAM. - // ... - // dc'0000 – dd'0000: optional real-time clock. - // df'f000 - e0'0000: custom chip registers. - // ... - // f0'0000 — : 512kb Kickstart (or possibly just an extra 512kb reserved for hypothetical 1mb Kickstart?). - // f8'0000 — : 256kb Kickstart if 2.04 or higher. - // fc'0000 – : 256kb Kickstart otherwise. - set_region(0x00'0000, 0x08'00000, kickstart_.data(), CPU::MC68000::Microcycle::PermitRead); - set_region(0xfc'0000, 0x1'00'0000, kickstart_.data(), CPU::MC68000::Microcycle::PermitRead); + Memory::PackBigEndian16(roms.find(rom_name)->second, memory_.kickstart_.data()); // NTSC clock rate: 2*3.579545 = 7.15909Mhz. // PAL clock rate: 7.09379Mhz. @@ -91,23 +74,31 @@ class ConcreteMachine: printf("%06x\n", *cycle.address); } - if(!regions_[address >> 18].read_write_mask) { + if(!memory_.regions_[address >> 18].read_write_mask) { if((cycle.operation & (Microcycle::SelectByte | Microcycle::SelectWord))) { // Check for various potential chip accesses. + // Per the manual: + // // CIA A is: 101x xxxx xx01 rrrr xxxx xxx0 (i.e. loaded into high byte) // CIA B is: 101x xxxx xx10 rrrr xxxx xxx1 (i.e. loaded into low byte) + // + // but in order to map 0xbfexxx to CIA A and 0xbfdxxx to CIA B, I think + // these might be listed the wrong way around. + // + // Additional assumption: the relevant CIA select lines are connected + // directly to the chip enables. if((address & 0xe0'0000) == 0xa0'0000) { const int reg = address >> 8; if(cycle.operation & Microcycle::Read) { uint16_t result = 0xffff; - if(address & 0x1000) result &= 0x00ff | (cia_a_.read(reg) << 8); - if(address & 0x2000) result &= 0xff00 | (cia_b_.read(reg) << 0); + if(!(address & 0x1000)) result &= 0x00ff | (cia_a_.read(reg) << 8); + if(!(address & 0x2000)) result &= 0xff00 | (cia_b_.read(reg) << 0); cycle.set_value16(result); } else { - if(address & 0x1000) cia_a_.write(reg, cycle.value8_high()); - if(address & 0x2000) cia_b_.write(reg, cycle.value8_low()); + if(!(address & 0x1000)) cia_a_.write(reg, cycle.value8_high()); + if(!(address & 0x2000)) cia_b_.write(reg, cycle.value8_low()); } } else if(address >= 0xdf'f000 && address <= 0xdf'f1be) { printf("Unimplemented chipset access %06x\n", address); @@ -120,8 +111,8 @@ class ConcreteMachine: } else { // A regular memory access. cycle.apply( - ®ions_[address >> 18].contents[address], - regions_[address >> 18].read_write_mask + &memory_.regions_[address >> 18].contents[address], + memory_.regions_[address >> 18].read_write_mask ); } @@ -132,27 +123,89 @@ class ConcreteMachine: CPU::MC68000::Processor mc68000_; // MARK: - Memory map. - std::array ram_; - std::array kickstart_; + struct MemoryMap { + public: + std::array ram_; + std::array kickstart_; - struct MemoryRegion { - uint8_t *contents = nullptr; - int read_write_mask = 0; - } regions_[64]; // i.e. top six bits are used as an index. + struct MemoryRegion { + uint8_t *contents = nullptr; + int read_write_mask = 0; + } regions_[64]; // i.e. top six bits are used as an index. - void set_region(int start, int end, uint8_t *base, int read_write_mask) { - assert(!(start & ~0xfc'0000)); - assert(!((end - (1 << 18)) & ~0xfc'0000)); + MemoryMap() { + // Address spaces that matter: + // + // 00'0000 – 08'0000: chip RAM. [or overlayed KickStart] + // – 10'0000: extended chip ram for ECS. + // – 20'0000: auto-config space (/fast RAM). + // ... + // bf'd000 – c0'0000: 8250s. + // c0'0000 – d8'0000: pseudo-fast RAM. + // ... + // dc'0000 – dd'0000: optional real-time clock. + // df'f000 - e0'0000: custom chip registers. + // ... + // f0'0000 — : 512kb Kickstart (or possibly just an extra 512kb reserved for hypothetical 1mb Kickstart?). + // f8'0000 — : 256kb Kickstart if 2.04 or higher. + // fc'0000 – : 256kb Kickstart otherwise. + set_region(0xfc'0000, 0x1'00'0000, kickstart_.data(), CPU::MC68000::Microcycle::PermitRead); + set_overlay(true); + } - for(int c = start >> 18; c < end >> 18; c++) { - regions_[c].contents = base - (c << 18); - regions_[c].read_write_mask = read_write_mask; - } - } + void set_overlay(bool enabled) { + if(overlay_ == enabled) { + return; + } + overlay_ = enabled; + + if(enabled) { + set_region(0x00'0000, 0x08'00000, kickstart_.data(), CPU::MC68000::Microcycle::PermitRead); + } else { + set_region(0x00'0000, 0x08'00000, ram_.data(), CPU::MC68000::Microcycle::PermitRead | CPU::MC68000::Microcycle::PermitWrite); + } + } + + private: + bool overlay_ = false; + + void set_region(int start, int end, uint8_t *base, int read_write_mask) { + assert(!(start & ~0xfc'0000)); + assert(!((end - (1 << 18)) & ~0xfc'0000)); + + for(int c = start >> 18; c < end >> 18; c++) { + regions_[c].contents = base - (c << 18); + regions_[c].read_write_mask = read_write_mask; + } + } + } memory_; // MARK: - CIAs. - struct CIAAHandler: public MOS::MOS6526::PortHandler { + class CIAAHandler: public MOS::MOS6526::PortHandler { + public: + CIAAHandler(MemoryMap &map) : map_(map) {} + + void set_port_output(MOS::MOS6526::Port port, uint8_t value) { + if(port) { + // Parallel port output. + } else { + // b7: /FIR1 + // b6: /FIR0 + // b5: /RDY + // b4: /TRK0 + // b3: /WPRO + // b2: /CHNG + // b1: /LED [output] + // b0: OVL [output] + + // TODO: provide an output for LED. + map_.set_overlay(value & 1); + } + } + + private: + MemoryMap &map_; } cia_a_handler_; struct CIABHandler: public MOS::MOS6526::PortHandler {