Get the M6532 chip implementation to the point of being OK to test.

Signed-off-by: Adrian Conlon <Adrian.conlon@gmail.com>
This commit is contained in:
Adrian Conlon 2019-05-27 13:21:35 +01:00
parent d37f130577
commit 90bfac83d5
2 changed files with 107 additions and 85 deletions

View File

@ -3,6 +3,8 @@
#include <cstdint> #include <cstdint>
#include <ClockedChip.h> #include <ClockedChip.h>
#include <Signal.h>
#include <Ram.h>
/* /*
PIA 6532 combined timer, IO and 128 bytes RAM PIA 6532 combined timer, IO and 128 bytes RAM
@ -190,16 +192,16 @@ namespace EightBit {
put into an OFF-STATE during Reset. Interrupt capability is disabled with the RES signal. The RES signal put into an OFF-STATE during Reset. Interrupt capability is disabled with the RES signal. The RES signal
must be held low for at least one clock period when reset is required. must be held low for at least one clock period when reset is required.
*/ */
PinLevel& RES() { return m_res; } DECLARE_PIN_INPUT(RES)
/* /* _
Read/Write (R/W) Read/Write (R/W)
The R/W signal is supplied by the microprocessor array and is used to control the transfer of data to and The R/W signal is supplied by the microprocessor array and is used to control the transfer of data to and
from the microprocessor array and the 6532. A high on the R/W pin allows the processor to read (with proper from the microprocessor array and the 6532. A high on the R/W pin allows the processor to read (with proper
addressing) the data supplied by the 6532. A low on the R/W pin allows a write (with proper addressing) to addressing) the data supplied by the 6532. A low on the R/W pin allows a write (with proper addressing) to
the 6532. the 6532.
*/ */
PinLevel& RW() { return m_rw; } DECLARE_PIN_INPUT(RW)
/* ___ /* ___
Interrupt Request (IRQ) Interrupt Request (IRQ)
@ -207,7 +209,7 @@ namespace EightBit {
an interrupt from the 6532. An external pull-up device is required. The IRQ pin may be activated by a an interrupt from the 6532. An external pull-up device is required. The IRQ pin may be activated by a
transition on PA7 or timeout of the interval timer. transition on PA7 or timeout of the interval timer.
*/ */
PinLevel& IRQ() { return m_irq; } DECLARE_PIN_OUTPUT(IRQ)
/* /*
Data Bus (D0-D7) Data Bus (D0-D7)
@ -215,7 +217,7 @@ namespace EightBit {
allow transfer of data to and from the microprocessor array. The output buffers remain in the off state except allow transfer of data to and from the microprocessor array. The output buffers remain in the off state except
when a Read operation occurs and are capable of driving one standard TTL load and 130 pf. when a Read operation occurs and are capable of driving one standard TTL load and 130 pf.
*/ */
uint8_t& data() { return m_data; } auto& DATA() { return m_data; }
/* /*
Peripheral Data Ports Peripheral Data Ports
@ -236,28 +238,32 @@ namespace EightBit {
/* /*
Address Lines (A0-A6) Address Lines (A0-A6)
There are 7 address pins. In addition to these 7, there is 9 RAM SELECT pin. These pins, A0-A6 and RAM There are 7 address pins. In addition to these 7, there is a RAM SELECT pin. These pins, A0-A6 and RAM
SELECT, are always used as addressing pins. There are two additional pins which are used as CHIP SELECT, are always used as addressing pins. There are two additional pins which are used as CHIP
SELECTS. They are pins CS1 and CS2. SELECTS. They are pins CS1 and CS2.
*/ */
uint8_t& address() { return m_address; } uint8_t& address() { return m_address; }
// RAM SELECT, active low // RAM SELECT, active low
PinLevel& RS() { return m_rs; } DECLARE_PIN_INPUT(RS)
// CHIP SELECT 1, active high // CHIP SELECT 1, active high
PinLevel& CS1() { return m_cs1; } DECLARE_PIN_INPUT(CS1)
// CHIP SELECT 2, active low // CHIP SELECT 2, active low
PinLevel& CS2() { return m_cs2; } DECLARE_PIN_INPUT(CS2)
void tick(); bool activated() { return powered() && selected(); }
bool selected() { return raised(CS1()) && lowered(CS2()); }
virtual void initialise() final; Signal<EventArgs> Accessing;
Signal<EventArgs> Accessed;
private:
void step();
void reset(); void reset();
private:
enum EdgeDetect { Positive, Negative }; enum EdgeDetect { Positive, Negative };
enum TimerIncrement { One = 1, Eight = 8, SixtyFour = 64, OneThousandAndTwentyFour = 1024 }; enum TimerIncrement { One = 1, Eight = 8, SixtyFour = 64, OneThousandAndTwentyFour = 1024 };
@ -268,8 +274,6 @@ namespace EightBit {
auto& IF() { return m_interruptFlags; } auto& IF() { return m_interruptFlags; }
bool selected() { return raised(CS1()) && lowered(CS2()); }
uint8_t m_address; uint8_t m_address;
uint8_t m_data; uint8_t m_data;
@ -281,13 +285,6 @@ namespace EightBit {
uint8_t m_drb; uint8_t m_drb;
uint8_t m_ddrb; uint8_t m_ddrb;
PinLevel m_res;
PinLevel m_rw;
PinLevel m_irq;
PinLevel m_rs;
PinLevel m_cs1;
PinLevel m_cs2;
Ram m_ram = 0x80; Ram m_ram = 0x80;
bool m_allowTimerInterrupts; bool m_allowTimerInterrupts;

View File

@ -4,98 +4,123 @@
#include <cassert> #include <cassert>
EightBit::M6532::M6532() noexcept { EightBit::M6532::M6532() noexcept {
Ticked.connect([this](EightBit::EventArgs&) {
step();
});
} }
void EightBit::M6532::tick() { DEFINE_PIN_LEVEL_CHANGERS(RES, M6532);
DEFINE_PIN_LEVEL_CHANGERS(RW, M6532);
DEFINE_PIN_LEVEL_CHANGERS(IRQ, M6532);
DEFINE_PIN_LEVEL_CHANGERS(RS, M6532);
DEFINE_PIN_LEVEL_CHANGERS(CS1, M6532);
DEFINE_PIN_LEVEL_CHANGERS(CS2, M6532);
if (selected()) { void EightBit::M6532::step() {
// Process interrupts resetCycles();
if (--m_currentIncrement == 0) { if (!activated())
m_currentIncrement = m_timerIncrement; return;
--m_timerInterval;
}
if (m_allowPA7Interrupts && (PA() & 0x80))
IF() &= 0x40;
if (m_allowTimerInterrupts && (m_timerInterval == 0)) Accessing.fire(EventArgs::empty());
IF() &= 0x80;
const auto read = raised(RW()); if (lowered(RES())) {
const auto write = lowered(RW()); reset();
assert(read == !write); raise(RES());
return;
}
const auto ram = lowered(RS()); if (--m_currentIncrement == 0) {
if (ram) { m_currentIncrement = m_timerIncrement;
--m_timerInterval;
}
auto& cell = RAM().reference(address() & 0x7f); const bool interruptPA7 = m_allowPA7Interrupts && (PA() & Bit7);
read ? data() = cell : cell = data(); if (interruptPA7)
setFlag(IF(), Bit6);
const bool interruptTimer = m_allowTimerInterrupts && (m_timerInterval == 0);
if (interruptTimer)
setFlag(IF(), Bit7);
interruptPA7 || interruptTimer ? lower(IRQ()) : raise(IRQ());
const auto read = raised(RW());
const auto write = lowered(RW());
assert(read == !write);
const auto ram = lowered(RS());
if (ram) {
auto& cell = RAM().reference(address() & 0x7f);
read ? DATA() = cell : cell = DATA();
} else {
const auto a0 = address() & 0b00001;
const auto a1 = address() & 0b00010;
const auto a2 = address() & 0b00100;
const auto a3 = address() & 0b01000;
const auto a4 = address() & 0b10000;
const auto portControls = a2 == 0;
const auto otherControls = a2 == 1;
if (portControls) {
switch (a0 | a1) {
case 0b00:
// R/W output reg A
break;
case 0b01:
read ? DATA() = DDRA() : DDRA() = DATA();
break;
case 0b10:
// R/W output reg B
break;
case 0b11:
read ? DATA() = DDRB() : DDRB() = DATA();
break;
}
} else { } else {
const auto a0 = address() & 0b00001; if (read && !a4 && a2) {
const auto a1 = address() & 0b00010; m_allowPA7Interrupts = !a1;
const auto a2 = address() & 0b00100; m_edgeDetection = a0 ? Positive : Negative;
const auto a3 = address() & 0b01000; }
const auto a4 = address() & 0b10000;
const auto portControls = a2 == 0; if (read && a2 && a0) {
const auto otherControls = a2 == 1; DATA() = IF();
clearFlag(IF(), Bit6);
}
if (portControls) { m_allowTimerInterrupts = !!a3;
switch (a0 | a1) { if (write && a4) {
m_timerInterval = DATA();
switch (a1 | a0) {
case 0b00: case 0b00:
// R/W output reg A m_timerIncrement = One;
break; break;
case 0b01: case 0b01:
read ? data() = DDRA() : DDRA() = data(); m_timerIncrement = Eight;
break; break;
case 0b10: case 0b10:
// R/W output reg B m_timerIncrement = SixtyFour;
break; break;
case 0b11: case 0b11:
read ? data() = DDRB() : DDRB() = data(); m_timerIncrement = OneThousandAndTwentyFour;
break; break;
} }
m_currentIncrement = m_timerIncrement;
} else { clearFlag(IF(), Bit7);
if (read && !a4 && a2) {
m_allowPA7Interrupts = !a1;
m_edgeDetection = a0 ? Positive : Negative;
}
if (read && a2 && a0)
data() = IF() & (0x80 & 0x40);
m_allowTimerInterrupts = !!a3;
if (write && a4) {
m_timerInterval = data();
switch (a1 | a0) {
case 0b00:
m_timerIncrement = One;
break;
case 0b01:
m_timerIncrement = Eight;
break;
case 0b10:
m_timerIncrement = SixtyFour;
break;
case 0b11:
m_timerIncrement = OneThousandAndTwentyFour;
break;
}
m_currentIncrement = m_timerIncrement;
}
} }
} }
} }
}
void EightBit::M6532::initialise() { Accessed.fire(EventArgs::empty());
} }
void EightBit::M6532::reset() { void EightBit::M6532::reset() {