2021-07-17 00:30:48 +00:00
|
|
|
//
|
|
|
|
// Amiga.cpp
|
|
|
|
// Clock Signal
|
|
|
|
//
|
|
|
|
// Created by Thomas Harte on 16/07/2021.
|
|
|
|
// Copyright © 2021 Thomas Harte. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#include "Amiga.hpp"
|
|
|
|
|
2021-07-20 02:30:34 +00:00
|
|
|
#include "../../Activity/Source.hpp"
|
2021-07-17 00:49:12 +00:00
|
|
|
#include "../MachineTypes.hpp"
|
|
|
|
|
2021-07-17 01:07:12 +00:00
|
|
|
#include "../../Processors/68000/68000.hpp"
|
|
|
|
|
2021-07-17 00:30:48 +00:00
|
|
|
#include "../../Analyser/Static/Amiga/Target.hpp"
|
|
|
|
|
2021-07-17 01:07:12 +00:00
|
|
|
#include "../Utility/MemoryPacker.hpp"
|
|
|
|
#include "../Utility/MemoryFuzzer.hpp"
|
|
|
|
|
2021-07-22 01:49:20 +00:00
|
|
|
//#define NDEBUG
|
2021-07-19 00:25:43 +00:00
|
|
|
#define LOG_PREFIX "[Amiga] "
|
|
|
|
#include "../../Outputs/Log.hpp"
|
|
|
|
|
2021-07-23 01:16:23 +00:00
|
|
|
#include "Chipset.hpp"
|
2021-10-04 13:44:54 +00:00
|
|
|
#include "MemoryMap.hpp"
|
2021-07-22 22:43:07 +00:00
|
|
|
|
2021-10-04 15:12:13 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
// NTSC clock rate: 2*3.579545 = 7.15909Mhz.
|
|
|
|
// PAL clock rate: 7.09379Mhz; 227 cycles/line.
|
|
|
|
constexpr int PALClockRate = 7'093'790;
|
|
|
|
constexpr int NTSCClockRate = 7'159'090;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-07-17 00:30:48 +00:00
|
|
|
namespace Amiga {
|
|
|
|
|
|
|
|
class ConcreteMachine:
|
2021-07-20 02:30:34 +00:00
|
|
|
public Activity::Source,
|
2021-07-17 01:07:12 +00:00
|
|
|
public CPU::MC68000::BusHandler,
|
2021-07-17 00:49:12 +00:00
|
|
|
public MachineTypes::ScanProducer,
|
|
|
|
public MachineTypes::TimedMachine,
|
2021-07-17 00:30:48 +00:00
|
|
|
public Machine {
|
|
|
|
public:
|
2021-07-17 01:07:12 +00:00
|
|
|
ConcreteMachine(const Analyser::Static::Amiga::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
2021-07-18 15:49:10 +00:00
|
|
|
mc68000_(*this),
|
2021-10-04 15:12:13 +00:00
|
|
|
chipset_(memory_, PALClockRate)
|
2021-07-17 01:07:12 +00:00
|
|
|
{
|
2021-07-17 00:30:48 +00:00
|
|
|
(void)target;
|
2021-07-17 00:49:12 +00:00
|
|
|
|
2021-07-17 01:07:12 +00:00
|
|
|
// Temporary: use a hard-coded Kickstart selection.
|
|
|
|
constexpr ROM::Name rom_name = ROM::Name::AmigaA500Kickstart13;
|
|
|
|
ROM::Request request(rom_name);
|
|
|
|
auto roms = rom_fetcher(request);
|
|
|
|
if(!request.validate(roms)) {
|
|
|
|
throw ROMMachine::Error::MissingROMs;
|
|
|
|
}
|
2021-07-22 22:43:07 +00:00
|
|
|
Memory::PackBigEndian16(roms.find(rom_name)->second, memory_.kickstart.data());
|
2021-07-18 01:10:06 +00:00
|
|
|
|
2021-10-04 15:12:13 +00:00
|
|
|
set_clock_rate(PALClockRate);
|
2021-07-18 01:10:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - MC68000::BusHandler.
|
|
|
|
using Microcycle = CPU::MC68000::Microcycle;
|
|
|
|
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int) {
|
2021-08-09 01:52:28 +00:00
|
|
|
|
|
|
|
// Do a quick advance check for Chip RAM access; add a suitable delay if required.
|
|
|
|
HalfCycles access_delay;
|
|
|
|
if(cycle.operation & Microcycle::NewAddress && *cycle.address < 0x20'0000) {
|
2021-10-04 13:44:54 +00:00
|
|
|
access_delay = chipset_.run_until_cpu_slot().duration;
|
2021-08-09 01:52:28 +00:00
|
|
|
}
|
2021-07-18 01:10:06 +00:00
|
|
|
|
2021-08-09 01:52:28 +00:00
|
|
|
// Compute total length.
|
|
|
|
const HalfCycles total_length = cycle.length + access_delay;
|
2021-07-19 23:03:37 +00:00
|
|
|
|
2021-10-04 13:44:54 +00:00
|
|
|
chipset_.run_for(total_length);
|
2021-07-28 23:36:30 +00:00
|
|
|
mc68000_.set_interrupt_level(chipset_.get_interrupt_level());
|
2021-07-23 01:45:51 +00:00
|
|
|
|
2021-07-20 01:50:35 +00:00
|
|
|
// Check for assertion of reset.
|
|
|
|
if(cycle.operation & Microcycle::Reset) {
|
2021-07-20 02:17:40 +00:00
|
|
|
memory_.reset();
|
2021-07-22 01:49:20 +00:00
|
|
|
LOG("Reset; PC is around " << PADHEX(8) << mc68000_.get_state().program_counter);
|
2021-07-20 01:50:35 +00:00
|
|
|
}
|
|
|
|
|
2021-07-26 20:40:42 +00:00
|
|
|
// Autovector interrupts.
|
|
|
|
if(cycle.operation & Microcycle::InterruptAcknowledge) {
|
|
|
|
mc68000_.set_is_peripheral_address(true);
|
2021-08-09 01:52:28 +00:00
|
|
|
return access_delay;
|
2021-07-26 20:40:42 +00:00
|
|
|
}
|
|
|
|
|
2021-07-18 15:49:10 +00:00
|
|
|
// Do nothing if no address is exposed.
|
2021-08-09 01:52:28 +00:00
|
|
|
if(!(cycle.operation & (Microcycle::NewAddress | Microcycle::SameAddress))) return access_delay;
|
2021-07-18 15:49:10 +00:00
|
|
|
|
2021-07-18 01:10:06 +00:00
|
|
|
// TODO: interrupt acknowledgement.
|
|
|
|
|
|
|
|
// Grab the target address to pick a memory source.
|
|
|
|
const uint32_t address = cycle.host_endian_byte_address();
|
2021-08-06 00:06:48 +00:00
|
|
|
|
|
|
|
// Set VPA if this is [going to be] a CIA access.
|
|
|
|
mc68000_.set_is_peripheral_address((address & 0xe0'0000) == 0xa0'0000);
|
2021-07-18 01:36:20 +00:00
|
|
|
|
2021-07-22 22:43:07 +00:00
|
|
|
if(!memory_.regions[address >> 18].read_write_mask) {
|
2021-07-18 01:36:20 +00:00
|
|
|
if((cycle.operation & (Microcycle::SelectByte | Microcycle::SelectWord))) {
|
|
|
|
// Check for various potential chip accesses.
|
2021-07-18 16:13:56 +00:00
|
|
|
|
2021-07-18 21:17:41 +00:00
|
|
|
// Per the manual:
|
|
|
|
//
|
2021-07-18 16:13:56 +00:00
|
|
|
// 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)
|
2021-07-18 21:17:41 +00:00
|
|
|
//
|
|
|
|
// 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.
|
2021-07-18 16:13:56 +00:00
|
|
|
if((address & 0xe0'0000) == 0xa0'0000) {
|
|
|
|
const int reg = address >> 8;
|
|
|
|
|
|
|
|
if(cycle.operation & Microcycle::Read) {
|
|
|
|
uint16_t result = 0xffff;
|
2021-10-04 13:44:54 +00:00
|
|
|
if(!(address & 0x1000)) result &= 0xff00 | (chipset_.cia_a.read(reg) << 0);
|
|
|
|
if(!(address & 0x2000)) result &= 0x00ff | (chipset_.cia_b.read(reg) << 8);
|
2021-07-18 16:13:56 +00:00
|
|
|
cycle.set_value16(result);
|
|
|
|
} else {
|
2021-10-04 13:44:54 +00:00
|
|
|
if(!(address & 0x1000)) chipset_.cia_a.write(reg, cycle.value8_low());
|
|
|
|
if(!(address & 0x2000)) chipset_.cia_b.write(reg, cycle.value8_high());
|
2021-07-18 16:13:56 +00:00
|
|
|
}
|
2021-07-30 22:22:59 +00:00
|
|
|
|
2021-08-09 00:20:12 +00:00
|
|
|
LOG("CIA " << (((address >> 12) & 3)^3) << " " << (cycle.operation & Microcycle::Read ? "read " : "write ") << std::dec << (reg & 0xf) << " of " << PADHEX(2) << +cycle.value8_low());
|
2021-07-18 01:36:20 +00:00
|
|
|
} else if(address >= 0xdf'f000 && address <= 0xdf'f1be) {
|
2021-07-23 01:16:23 +00:00
|
|
|
chipset_.perform(cycle);
|
2021-07-18 01:36:20 +00:00
|
|
|
} else {
|
|
|
|
// This'll do for open bus, for now.
|
2021-07-22 01:49:20 +00:00
|
|
|
if(cycle.operation & Microcycle::Read) {
|
|
|
|
cycle.set_value16(0xffff);
|
|
|
|
}
|
2021-07-26 21:02:30 +00:00
|
|
|
|
|
|
|
// Don't log for the region that is definitely just ROM this machine doesn't have.
|
|
|
|
if(address < 0xf0'0000) {
|
2021-07-30 22:22:59 +00:00
|
|
|
LOG("Unmapped " << (cycle.operation & Microcycle::Read ? "read from " : "write to ") << PADHEX(6) << ((*cycle.address)&0xffffff) << " of " << cycle.value16());
|
2021-07-26 21:02:30 +00:00
|
|
|
}
|
2021-07-18 01:36:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// A regular memory access.
|
|
|
|
cycle.apply(
|
2021-07-22 22:43:07 +00:00
|
|
|
&memory_.regions[address >> 18].contents[address],
|
|
|
|
memory_.regions[address >> 18].read_write_mask
|
2021-07-18 01:36:20 +00:00
|
|
|
);
|
2021-07-18 01:10:06 +00:00
|
|
|
}
|
2021-07-17 01:07:12 +00:00
|
|
|
|
2021-08-09 01:52:28 +00:00
|
|
|
return access_delay;
|
2021-07-17 01:07:12 +00:00
|
|
|
}
|
2021-07-17 00:49:12 +00:00
|
|
|
|
|
|
|
private:
|
2021-07-17 01:07:12 +00:00
|
|
|
CPU::MC68000::Processor<ConcreteMachine, true> mc68000_;
|
2021-07-17 01:41:32 +00:00
|
|
|
|
|
|
|
// MARK: - Memory map.
|
|
|
|
|
2021-10-04 13:44:54 +00:00
|
|
|
MemoryMap memory_;
|
2021-07-18 01:10:06 +00:00
|
|
|
|
2021-07-23 01:16:23 +00:00
|
|
|
// MARK: - Chipset.
|
2021-07-19 00:25:43 +00:00
|
|
|
|
2021-07-23 01:16:23 +00:00
|
|
|
Chipset chipset_;
|
2021-10-04 13:44:54 +00:00
|
|
|
|
2021-07-20 02:30:34 +00:00
|
|
|
// MARK: - Activity Source
|
|
|
|
void set_activity_observer(Activity::Observer *observer) final {
|
2021-10-04 13:44:54 +00:00
|
|
|
chipset_.set_activity_observer(observer);
|
2021-07-20 02:30:34 +00:00
|
|
|
}
|
|
|
|
|
2021-07-17 00:49:12 +00:00
|
|
|
// MARK: - MachineTypes::ScanProducer.
|
|
|
|
|
|
|
|
void set_scan_target(Outputs::Display::ScanTarget *scan_target) final {
|
2021-07-26 22:44:01 +00:00
|
|
|
chipset_.set_scan_target(scan_target);
|
2021-07-17 00:49:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Outputs::Display::ScanStatus get_scaled_scan_status() const {
|
2021-07-26 22:44:01 +00:00
|
|
|
return chipset_.get_scaled_scan_status();
|
2021-07-17 00:30:48 +00:00
|
|
|
}
|
|
|
|
|
2021-07-17 00:49:12 +00:00
|
|
|
// MARK: - MachineTypes::TimedMachine.
|
|
|
|
|
|
|
|
void run_for(const Cycles cycles) {
|
2021-07-17 01:07:12 +00:00
|
|
|
mc68000_.run_for(cycles);
|
2021-07-17 00:49:12 +00:00
|
|
|
}
|
2021-07-17 00:30:48 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
using namespace Amiga;
|
|
|
|
|
|
|
|
Machine *Machine::Amiga(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
|
|
|
|
using Target = Analyser::Static::Amiga::Target;
|
|
|
|
const Target *const amiga_target = dynamic_cast<const Target *>(target);
|
|
|
|
return new Amiga::ConcreteMachine(*amiga_target, rom_fetcher);
|
|
|
|
}
|
|
|
|
|
|
|
|
Machine::~Machine() {}
|