mirror of
https://github.com/MoleskiCoder/EightBit.git
synced 2024-06-01 07:41:51 +00:00
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:
parent
d37f130577
commit
90bfac83d5
|
@ -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;
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user