2021-10-04 13:44:54 +00:00
|
|
|
|
//
|
|
|
|
|
// MemoryMap.hpp
|
|
|
|
|
// Clock Signal
|
|
|
|
|
//
|
|
|
|
|
// Created by Thomas Harte on 04/10/2021.
|
|
|
|
|
// Copyright © 2021 Thomas Harte. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
#ifndef MemoryMap_hpp
|
|
|
|
|
#define MemoryMap_hpp
|
|
|
|
|
|
2021-12-22 20:17:11 +00:00
|
|
|
|
#include "../../Analyser/Static/Amiga/Target.hpp"
|
|
|
|
|
|
2021-12-22 20:30:19 +00:00
|
|
|
|
#include <array>
|
|
|
|
|
#include <cassert>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
2021-10-04 13:44:54 +00:00
|
|
|
|
namespace Amiga {
|
|
|
|
|
|
2021-11-29 21:55:45 +00:00
|
|
|
|
class MemoryMap {
|
|
|
|
|
private:
|
2023-12-22 04:08:18 +00:00
|
|
|
|
static constexpr auto PermitRead = CPU::MC68000::Operation::PermitRead;
|
|
|
|
|
static constexpr auto PermitWrite = CPU::MC68000::Operation::PermitWrite;
|
2021-11-29 21:55:45 +00:00
|
|
|
|
static constexpr auto PermitReadWrite = PermitRead | PermitWrite;
|
|
|
|
|
|
2021-10-04 13:44:54 +00:00
|
|
|
|
public:
|
|
|
|
|
std::array<uint8_t, 512*1024> kickstart{0xff};
|
2021-12-22 20:30:19 +00:00
|
|
|
|
std::vector<uint8_t> chip_ram{};
|
2021-10-04 13:44:54 +00:00
|
|
|
|
|
|
|
|
|
struct MemoryRegion {
|
|
|
|
|
uint8_t *contents = nullptr;
|
|
|
|
|
unsigned int read_write_mask = 0;
|
|
|
|
|
} regions[64]; // i.e. top six bits are used as an index.
|
|
|
|
|
|
2021-12-22 20:17:11 +00:00
|
|
|
|
using FastRAM = Analyser::Static::Amiga::Target::FastRAM;
|
2021-12-22 20:30:19 +00:00
|
|
|
|
using ChipRAM = Analyser::Static::Amiga::Target::ChipRAM;
|
|
|
|
|
MemoryMap(ChipRAM chip_ram_size, FastRAM fast_ram_size) {
|
2021-10-04 13:44:54 +00:00
|
|
|
|
// Address spaces that matter:
|
|
|
|
|
//
|
|
|
|
|
// 00'0000 – 08'0000: chip RAM. [or overlayed KickStart]
|
|
|
|
|
// – 10'0000: extended chip ram for ECS.
|
2021-11-29 21:55:45 +00:00
|
|
|
|
// – 20'0000: slow RAM and further chip RAM.
|
|
|
|
|
// – a0'0000: auto-config space (/fast RAM).
|
2021-10-04 13:44:54 +00:00
|
|
|
|
// ...
|
|
|
|
|
// 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.
|
2021-11-29 21:55:45 +00:00
|
|
|
|
set_region(0xfc'0000, 0x1'00'0000, kickstart.data(), PermitRead);
|
2021-12-22 20:17:11 +00:00
|
|
|
|
|
2021-12-22 20:30:19 +00:00
|
|
|
|
switch(chip_ram_size) {
|
|
|
|
|
default:
|
|
|
|
|
case ChipRAM::FiveHundredAndTwelveKilobytes:
|
|
|
|
|
chip_ram.resize(512 * 1024);
|
|
|
|
|
break;
|
|
|
|
|
case ChipRAM::OneMegabyte:
|
|
|
|
|
chip_ram.resize(1 * 1024 * 1024);
|
|
|
|
|
break;
|
|
|
|
|
case ChipRAM::TwoMegabytes:
|
|
|
|
|
chip_ram.resize(2 * 1024 * 1024);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-22 20:17:11 +00:00
|
|
|
|
switch(fast_ram_size) {
|
|
|
|
|
default:
|
|
|
|
|
fast_autoconf_visible_ = false;
|
|
|
|
|
break;
|
|
|
|
|
case FastRAM::OneMegabyte:
|
|
|
|
|
fast_ram_.resize(1 * 1024 * 1024);
|
|
|
|
|
fast_ram_size_ = 5;
|
|
|
|
|
break;
|
|
|
|
|
case FastRAM::TwoMegabytes:
|
|
|
|
|
fast_ram_.resize(2 * 1024 * 1024);
|
|
|
|
|
fast_ram_size_ = 6;
|
|
|
|
|
break;
|
|
|
|
|
case FastRAM::FourMegabytes:
|
|
|
|
|
fast_ram_.resize(4 * 1024 * 1024);
|
|
|
|
|
fast_ram_size_ = 7;
|
|
|
|
|
break;
|
|
|
|
|
case FastRAM::EightMegabytes:
|
|
|
|
|
fast_ram_.resize(8 * 1024 * 1024);
|
|
|
|
|
fast_ram_size_ = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-04 13:44:54 +00:00
|
|
|
|
reset();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void reset() {
|
|
|
|
|
set_overlay(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void set_overlay(bool enabled) {
|
|
|
|
|
if(overlay_ == enabled) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
overlay_ = enabled;
|
|
|
|
|
|
2021-11-29 21:55:45 +00:00
|
|
|
|
set_region(0x00'0000, uint32_t(chip_ram.size()), chip_ram.data(), PermitReadWrite);
|
2021-10-04 13:44:54 +00:00
|
|
|
|
if(enabled) {
|
2021-11-29 21:55:45 +00:00
|
|
|
|
set_region(0x00'0000, 0x08'0000, kickstart.data(), PermitRead);
|
2021-10-04 13:44:54 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-22 20:17:11 +00:00
|
|
|
|
/// Performs the provided microcycle, which the caller guarantees to be a memory access,
|
|
|
|
|
/// and in the Zorro register range.
|
2023-12-22 04:08:18 +00:00
|
|
|
|
template <typename Microcycle>
|
|
|
|
|
bool perform(const Microcycle &cycle) {
|
2021-12-22 20:17:11 +00:00
|
|
|
|
if(!fast_autoconf_visible_) return false;
|
|
|
|
|
|
|
|
|
|
const uint32_t register_address = *cycle.address & 0xfe;
|
|
|
|
|
|
2023-12-21 21:03:53 +00:00
|
|
|
|
if(cycle.operation & CPU::MC68000::Operation::Read) {
|
2021-12-22 20:17:11 +00:00
|
|
|
|
// Re: Autoconf:
|
|
|
|
|
//
|
|
|
|
|
// "All read registers physically return only the top 4 bits of data, on D31-D28";
|
|
|
|
|
// (this is from Zorro III documentation; I'm assuming it to be D15–D11 for the
|
|
|
|
|
// 68000's 16-bit bus);
|
|
|
|
|
//
|
|
|
|
|
// "Every AUTOCONFIG register is logically considered to be 8 bits wide; the
|
|
|
|
|
// 8 bits actually being nybbles from two paired addresses."
|
|
|
|
|
|
|
|
|
|
uint8_t value = 0xf;
|
|
|
|
|
switch(register_address) {
|
|
|
|
|
default: break;
|
|
|
|
|
|
|
|
|
|
case 0x00: // er_Type (high)
|
|
|
|
|
value =
|
|
|
|
|
0xc | // Zoro II-style PIC.
|
|
|
|
|
0x2; // Memory will be linked into the free pool
|
|
|
|
|
break;
|
|
|
|
|
case 0x02: // er_Type (low)
|
|
|
|
|
value = fast_ram_size_;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// er_Manufacturer
|
|
|
|
|
//
|
|
|
|
|
// On the manufacturer number: this is supposed to be assigned
|
|
|
|
|
// by Commodore. TODO: find and crib a real fast RAM number, if it matters.
|
|
|
|
|
//
|
|
|
|
|
// (0xffff seems to be invalid, so _something_ needs to be supplied)
|
|
|
|
|
case 0x10: case 0x12:
|
|
|
|
|
value = 0xa; // Manufacturer's number, high byte.
|
|
|
|
|
break;
|
|
|
|
|
case 0x14: case 0x16:
|
|
|
|
|
value = 0xb; // Manufacturer's number, low byte.
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Shove the value into the top of the data bus.
|
|
|
|
|
cycle.set_value16(uint16_t(0x0fff | (value << 12)));
|
|
|
|
|
} else {
|
|
|
|
|
fast_autoconf_visible_ &= !(register_address >= 0x4c && register_address < 0x50);
|
|
|
|
|
|
|
|
|
|
switch(register_address) {
|
|
|
|
|
default: break;
|
|
|
|
|
|
|
|
|
|
case 0x48: { // ec_BaseAddress (A23–A16)
|
|
|
|
|
const auto address = uint32_t(cycle.value8_high()) << 16;
|
|
|
|
|
set_region(address, uint32_t(address + fast_ram_.size()), fast_ram_.data(), PermitRead | PermitWrite);
|
|
|
|
|
fast_autoconf_visible_ = false;
|
|
|
|
|
} break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-04 13:44:54 +00:00
|
|
|
|
private:
|
2021-12-22 20:17:11 +00:00
|
|
|
|
std::vector<uint8_t> fast_ram_{};
|
|
|
|
|
uint8_t fast_ram_size_ = 0;
|
|
|
|
|
|
|
|
|
|
bool fast_autoconf_visible_ = true;
|
2021-10-04 13:44:54 +00:00
|
|
|
|
bool overlay_ = false;
|
|
|
|
|
|
2021-11-29 21:55:45 +00:00
|
|
|
|
void set_region(uint32_t start, uint32_t end, uint8_t *base, unsigned int read_write_mask) {
|
|
|
|
|
[[maybe_unused]] constexpr uint32_t precision_loss_mask = uint32_t(~0xfc'0000);
|
|
|
|
|
assert(!(start & precision_loss_mask));
|
|
|
|
|
assert(!((end - (1 << 18)) & precision_loss_mask));
|
|
|
|
|
assert(end > start);
|
2021-10-04 13:44:54 +00:00
|
|
|
|
|
2021-11-29 21:55:45 +00:00
|
|
|
|
if(base) base -= start;
|
|
|
|
|
for(decltype(start) c = start >> 18; c < end >> 18; c++) {
|
2021-10-04 13:44:54 +00:00
|
|
|
|
regions[c].contents = base;
|
|
|
|
|
regions[c].read_write_mask = read_write_mask;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
#endif /* MemoryMap_hpp */
|