initial commit

This commit is contained in:
matthias@rpi 2024-01-17 12:47:10 +01:00
commit 43e1ed36d5
5 changed files with 759 additions and 0 deletions

365
6502_opcodes.hpp Normal file
View File

@ -0,0 +1,365 @@
#pragma once
#include <array>
#include <cassert>
#include <string>
#include <cstring>
enum AddressingMode {
NONE,
ABS_A,
ABS_I_IND_X,
ABS_I_X,
ABS_I_Y,
ABS_IND_A,
ACC,
IMMEDIATE,
IMPLIED,
PC_REL,
STACK,
ZP,
ZP_I_IND_X,
ZP_I_X,
ZP_I_Y,
ZP_IND,
ZP_I_IND_Y,
ADDRESSING_MODE_COUNT
};
constexpr std::array<const char*, ADDRESSING_MODE_COUNT> ADDRESSING_MODE_NAMES = {
"-", // NONE
"a", // ABS_A
"(a,x)", // ABS_I_IND_X
"a,x", // ABS_I_X
"a,y", // ABS_I_Y
"(a)", // ABS_IND_A
"A", // ACC
"#", // IMMEDIATE
"i", // IMPLIED
"r", // PC_REL
"s", // STACK
"zp", // ZP
"(zp,x)", // ZP_I_IND_X
"zp,x", // ZP_I_X
"zp,y", // ZP_I_Y
"(zp)", // ZP_IND
"(zp),y" // ZP_I_IND_Y
};
constexpr void strncpy_constexpr(char* dest, const char* src, size_t n) {
for (size_t i = 0; i < n; i++) {
dest[i] = src[i];
}
}
constexpr void strcpy_constexpr(char* dest, const char* src) {
size_t i = 0;
while (src[i] != '\0') {
dest[i] = src[i];
i++;
}
}
#define NAME_LENGTH 10
class OpCode {
public:
constexpr OpCode() : opName(""), mode(AddressingMode::NONE), name("") {};
OpCode(const char* opName_, const AddressingMode mode_) : mode(mode_) {
strncpy_constexpr(opName, opName_, 3);
opName[3] = '\0';
strncpy_constexpr(name, opName_, 3);
name[3] = ' ';
/* assert(ADDRESSING_MODE_NAMES.size() < mode); */
/* assert(strlen(ADDRESSING_MODE_NAMES.at(mode)) <= NAME_LENGTH - 4); // Mode name too long, 3 opcode name + 1 space */
strcpy_constexpr(&name[4], ADDRESSING_MODE_NAMES.at(mode));
}
constexpr const char* getOpName() const { return opName; }
constexpr AddressingMode getMode() const { return mode; }
constexpr const char* getName() const { return name; }
private:
char opName[4] = " ";
AddressingMode mode = AddressingMode::NONE;
char name[10] = " ";
};
// TODO: make constexpr
inline std::array<OpCode, 256> getOpCodesArray() {
using am = AddressingMode;
std::array<OpCode, 256> opCodes{};
opCodes[0x00] = { "brk", am::STACK };
opCodes[0x10] = { "bpl", am::PC_REL };
opCodes[0x20] = { "jsr", am::ABS_IND_A };
opCodes[0x30] = { "bmi", am::PC_REL };
opCodes[0x40] = { "rti", am::STACK };
opCodes[0x50] = { "bvc", am::PC_REL };
opCodes[0x60] = { "rts", am::STACK };
opCodes[0x70] = { "bvs", am::PC_REL };
opCodes[0x80] = { "bra", am::PC_REL }; // WD
opCodes[0x90] = { "bcc", am::PC_REL };
opCodes[0xa0] = { "ldy", am::IMMEDIATE };
opCodes[0xb0] = { "bcs", am::PC_REL };
opCodes[0xc0] = { "cpy", am::IMMEDIATE };
opCodes[0xd0] = { "bne", am::PC_REL };
opCodes[0xe0] = { "cpx", am::IMMEDIATE };
opCodes[0xf0] = { "beq", am::PC_REL };
opCodes[0x01] = { "ora", am::ZP_I_IND_X };
opCodes[0x11] = { "ora", am::ZP_I_IND_Y };
opCodes[0x21] = { "and", am::ZP_I_IND_X };
opCodes[0x31] = { "and", am::ZP_I_IND_Y };
opCodes[0x41] = { "eor", am::ZP_I_IND_X };
opCodes[0x51] = { "eor", am::ZP_I_IND_Y };
opCodes[0x61] = { "adc", am::ZP_I_IND_X };
opCodes[0x71] = { "adc", am::ZP_I_IND_Y };
opCodes[0x81] = { "sta", am::ZP_I_IND_X };
opCodes[0x91] = { "sta", am::ZP_I_IND_Y };
opCodes[0xa1] = { "lda", am::ZP_I_IND_X };
opCodes[0xb1] = { "lda", am::ZP_I_IND_Y };
opCodes[0xc1] = { "cmp", am::ZP_I_IND_X };
opCodes[0xd1] = { "cmp", am::ZP_I_IND_Y };
opCodes[0xe1] = { "sbc", am::ZP_I_IND_X };
opCodes[0xf1] = { "sbc", am::ZP_I_IND_Y };
/* opCodes[0x02] = { "", am:: }; */
opCodes[0x12] = { "ora", am::ZP_IND };
/* opCodes[0x22] = { "", am:: }; */
opCodes[0x32] = { "and", am::ZP_IND };
/* opCodes[0x42] = { "", am:: }; */
opCodes[0x52] = { "eor", am::ZP_IND };
/* opCodes[0x62] = { "", am:: }; */
opCodes[0x72] = { "adc", am::ZP_IND };
/* opCodes[0x82] = { "", am:: }; */
opCodes[0x92] = { "sta", am::ZP_IND };
opCodes[0xa2] = { "ldx", am::IMMEDIATE };
opCodes[0xb2] = { "lda", am::ZP_IND };
/* opCodes[0xc2] = { "", am:: }; */
opCodes[0xd2] = { "cmp", am::ZP_IND };
/* opCodes[0xe2] = { "brk", am:: }; */
opCodes[0xf2] = { "sbc", am::ZP_IND };
/* opCodes[0x03] = { "", am:: }; */
/* opCodes[0x13] = { "", am:: }; */
/* opCodes[0x23] = { "", am:: }; */
/* opCodes[0x33] = { "", am:: }; */
/* opCodes[0x43] = { "", am:: }; */
/* opCodes[0x53] = { "", am:: }; */
/* opCodes[0x63] = { "", am:: }; */
/* opCodes[0x73] = { "", am:: }; */
/* opCodes[0x83] = { "", am:: }; */
/* opCodes[0x93] = { "", am:: }; */
/* opCodes[0xa3] = { "", am:: }; */
/* opCodes[0xb3] = { "", am:: }; */
/* opCodes[0xc3] = { "", am:: }; */
/* opCodes[0xd3] = { "", am:: }; */
/* opCodes[0xe3] = { "", am:: }; */
/* opCodes[0xf3] = { "", am:: }; */
opCodes[0x04] = { "tsb", am::ZP }; // WD
opCodes[0x14] = { "trb", am::ZP }; // WD
opCodes[0x24] = { "bit", am::ZP };
opCodes[0x34] = { "bit", am::ZP_I_X }; // WD
/* opCodes[0x44] = { "", am:: }; */
/* opCodes[0x54] = { "", am:: }; */
opCodes[0x64] = { "stz", am::ZP }; // WD
opCodes[0x74] = { "stz", am::ZP_I_X }; // WD
opCodes[0x84] = { "sty", am::ZP };
opCodes[0x94] = { "sty", am::ZP_I_X };
opCodes[0xa4] = { "ldy", am::ZP };
opCodes[0xb4] = { "ldy", am::ZP_I_X };
opCodes[0xc4] = { "cpy", am::ZP };
/* opCodes[0xd4] = { "", am:: }; */
opCodes[0xe4] = { "cpx", am::ZP };
/* opCodes[0xf4] = { "", am:: }; */
opCodes[0x05] = { "ora", am::ZP };
opCodes[0x15] = { "ora", am::ZP_I_X };
opCodes[0x25] = { "and", am::ZP };
opCodes[0x35] = { "and", am::ZP_I_X };
opCodes[0x45] = { "eor", am::ZP };
opCodes[0x55] = { "eor", am::ZP_I_X };
opCodes[0x65] = { "adc", am::ZP };
opCodes[0x75] = { "adc", am::ZP_I_X };
opCodes[0x85] = { "sta", am::ZP };
opCodes[0x95] = { "sta", am::ZP_I_X };
opCodes[0xa5] = { "lda", am::ZP };
opCodes[0xb5] = { "lda", am::ZP_I_X };
opCodes[0xc5] = { "cmp", am::ZP };
opCodes[0xd5] = { "cmp", am::ZP_I_X };
opCodes[0xe5] = { "sbc", am::ZP };
opCodes[0xf5] = { "sbc", am::ZP_I_X };
opCodes[0x06] = { "asl", am::ZP };
opCodes[0x16] = { "asl", am::ZP_I_X };
opCodes[0x26] = { "rol", am::ZP };
opCodes[0x36] = { "rol", am::ZP_I_X };
opCodes[0x46] = { "lsr", am::ZP };
opCodes[0x56] = { "lsr", am::ZP_I_X };
opCodes[0x66] = { "ror", am::ZP };
opCodes[0x76] = { "ror", am::ZP_I_X };
opCodes[0x86] = { "stx", am::ZP };
opCodes[0x96] = { "stx", am::ZP_I_X };
opCodes[0xa6] = { "ldx", am::ZP };
opCodes[0xb6] = { "ldx", am::ZP_I_X };
opCodes[0xc6] = { "dec", am::ZP };
opCodes[0xd6] = { "dec", am::ZP_I_X };
opCodes[0xe6] = { "inc", am::ZP };
opCodes[0xf6] = { "inc", am::ZP_I_X };
opCodes[0x07] = { "rmb0", am::ZP }; // WD
opCodes[0x17] = { "rmb1", am::ZP }; // WD
opCodes[0x27] = { "rmb2", am::ZP }; // WD
opCodes[0x37] = { "rmb3", am::ZP }; // WD
opCodes[0x47] = { "rmb4", am::ZP }; // WD
opCodes[0x57] = { "rmb5", am::ZP }; // WD
opCodes[0x67] = { "rmb6", am::ZP }; // WD
opCodes[0x77] = { "rmb7", am::ZP }; // WD
opCodes[0x87] = { "smb0", am::ZP }; // WD
opCodes[0x97] = { "smb1", am::ZP }; // WD
opCodes[0xa7] = { "smb2", am::ZP }; // WD
opCodes[0xb7] = { "smb3", am::ZP }; // WD
opCodes[0xc7] = { "smb4", am::ZP }; // WD
opCodes[0xd7] = { "smb5", am::ZP }; // WD
opCodes[0xe7] = { "smb6", am::ZP }; // WD
opCodes[0xf7] = { "smb7", am::ZP }; // WD
opCodes[0x08] = { "php", am::STACK };
opCodes[0x18] = { "clc", am::IMPLIED };
opCodes[0x28] = { "plp", am::STACK };
opCodes[0x38] = { "sec", am::IMPLIED };
opCodes[0x48] = { "pha", am::STACK };
opCodes[0x58] = { "cli", am::IMPLIED };
opCodes[0x68] = { "pla", am::STACK };
opCodes[0x78] = { "sei", am::IMPLIED };
opCodes[0x88] = { "dey", am::IMPLIED };
opCodes[0x98] = { "tya", am::IMPLIED };
opCodes[0xa8] = { "tay", am::IMPLIED };
opCodes[0xb8] = { "clv", am::IMPLIED };
opCodes[0xc8] = { "iny", am::IMPLIED };
opCodes[0xd8] = { "cld", am::IMPLIED };
opCodes[0xe8] = { "inx", am::IMPLIED };
opCodes[0xf8] = { "sed", am::IMPLIED };
opCodes[0x09] = { "ora", am::IMMEDIATE };
opCodes[0x19] = { "ora", am::ABS_I_Y };
opCodes[0x29] = { "and", am::IMMEDIATE };
opCodes[0x39] = { "and", am::ABS_I_Y };
opCodes[0x49] = { "eor", am::IMMEDIATE };
opCodes[0x59] = { "eor", am::ABS_I_Y };
opCodes[0x69] = { "adc", am::IMMEDIATE };
opCodes[0x79] = { "adc", am::ABS_I_Y };
opCodes[0x89] = { "bit", am::IMMEDIATE }; // WD
opCodes[0x99] = { "sta", am::ABS_I_Y };
opCodes[0xa9] = { "lda", am::IMMEDIATE};
opCodes[0xb9] = { "lda", am::ABS_I_Y }; // TODO datasheet says A,y not a,y
opCodes[0xc9] = { "cmp", am::IMMEDIATE };
opCodes[0xd9] = { "cmp", am::ABS_I_Y };
opCodes[0xe9] = { "sbc", am::IMMEDIATE };
opCodes[0xf9] = { "sbc", am::ABS_I_Y };
opCodes[0x0a] = { "asl", am::ACC };
opCodes[0x1a] = { "inc", am::ACC }; // WD
opCodes[0x2a] = { "rol", am::ACC };
opCodes[0x3a] = { "dec", am::ACC }; // WD
opCodes[0x4a] = { "lsr", am::ACC };
opCodes[0x5a] = { "phy", am::STACK }; // WD
opCodes[0x6a] = { "ror", am::ACC };
opCodes[0x7a] = { "ply", am::STACK }; // WD
opCodes[0x8a] = { "txa", am::IMPLIED };
opCodes[0x9a] = { "txs", am::IMPLIED };
opCodes[0xaa] = { "tax", am::IMPLIED };
opCodes[0xba] = { "tsx", am::IMPLIED };
opCodes[0xca] = { "dex", am::IMPLIED };
opCodes[0xda] = { "phx", am::IMPLIED }; // WD
opCodes[0xea] = { "nop", am::IMPLIED };
opCodes[0xfa] = { "plx", am::IMPLIED }; // WD
/* opCodes[0x0b] = { "", am:: }; */
/* opCodes[0x1b] = { "", am:: }; */
/* opCodes[0x2b] = { "", am:: }; */
/* opCodes[0x3b] = { "", am:: }; */
/* opCodes[0x4b] = { "", am:: }; */
/* opCodes[0x5b] = { "", am:: }; */
/* opCodes[0x6b] = { "", am:: }; */
/* opCodes[0x7b] = { "", am:: }; */
/* opCodes[0x8b] = { "", am:: }; */
/* opCodes[0x9b] = { "", am:: }; */
/* opCodes[0xab] = { "", am:: }; */
/* opCodes[0xbb] = { "", am:: }; */
opCodes[0xcb] = { "wai", am::IMPLIED };
opCodes[0xdb] = { "stp", am::IMPLIED };
/* opCodes[0xeb] = { "", am:: }; */
/* opCodes[0xfb] = { "", am:: }; */
opCodes[0x0c] = { "tsb", am::ABS_A }; // WD
opCodes[0x1c] = { "trb", am::ABS_A }; // WD
opCodes[0x2c] = { "bit", am::ABS_A };
opCodes[0x3c] = { "bit", am::ABS_I_X }; // WD
opCodes[0x4c] = { "jmp", am::ABS_A };
/* opCodes[0x5c] = { "", am:: }; */
opCodes[0x6c] = { "jmp", am::ABS_IND_A };
opCodes[0x7c] = { "jmp", am::ABS_I_IND_X }; // WD
opCodes[0x8c] = { "sty", am::ABS_A };
opCodes[0x9c] = { "stz", am::ABS_A }; // WD
opCodes[0xac] = { "ldy", am::ACC };
opCodes[0xbc] = { "ldy", am::ABS_I_X };
opCodes[0xcc] = { "cpy", am::ABS_A };
/* opCodes[0xdc] = { "", am:: }; */
opCodes[0xec] = { "cpx", am::ABS_A };
/* opCodes[0xfc] = { "", am:: }; */
opCodes[0x0d] = { "ora", am::ABS_A };
opCodes[0x1d] = { "ora", am::ABS_I_X };
opCodes[0x2d] = { "and", am::ABS_A };
opCodes[0x3d] = { "and", am::ABS_I_X };
opCodes[0x4d] = { "eor", am::ABS_A };
opCodes[0x5d] = { "eor", am::ABS_I_X };
opCodes[0x6d] = { "adc", am::ABS_A };
opCodes[0x7d] = { "adc", am::ABS_I_X };
opCodes[0x8d] = { "sta", am::ABS_A };
opCodes[0x9d] = { "sta", am::ABS_I_X };
opCodes[0xad] = { "lda", am::ABS_A };
opCodes[0xbd] = { "lda", am::ABS_I_X };
opCodes[0xcd] = { "cmp", am::ABS_A };
opCodes[0xdd] = { "cmp", am::ABS_I_X };
opCodes[0xed] = { "sbc", am::ABS_A };
opCodes[0xfd] = { "sbc", am::ABS_I_X };
opCodes[0x0e] = { "asl", am::ABS_A };
opCodes[0x1e] = { "asl", am::ABS_I_X };
opCodes[0x2e] = { "rol", am::ABS_A };
opCodes[0x3e] = { "rol", am::ABS_I_X };
opCodes[0x4e] = { "lsr", am::ABS_A };
opCodes[0x5e] = { "lsr", am::ABS_I_X };
opCodes[0x6e] = { "ror", am::ABS_A };
opCodes[0x7e] = { "ror", am::ABS_I_X };
opCodes[0x8e] = { "stx", am::ABS_A };
opCodes[0x9e] = { "stz", am::ABS_I_X };
opCodes[0xae] = { "ldx", am::ABS_A };
opCodes[0xbe] = { "ldx", am::ABS_I_X };
opCodes[0xce] = { "dec", am::ABS_A };
opCodes[0xde] = { "dec", am::ABS_I_X };
opCodes[0xee] = { "inc", am::ABS_A };
opCodes[0xfe] = { "inc", am::ABS_I_X };
opCodes[0x0f] = { "bbr0", am::PC_REL }; // WD
opCodes[0x1f] = { "bbr1", am::PC_REL }; // WD
opCodes[0x2f] = { "bbr2", am::PC_REL }; // WD
opCodes[0x3f] = { "bbr3", am::PC_REL }; // WD
opCodes[0x4f] = { "bbr4", am::PC_REL }; // WD
opCodes[0x5f] = { "bbr5", am::PC_REL }; // WD
opCodes[0x6f] = { "bbr6", am::PC_REL }; // WD
opCodes[0x7f] = { "bbr7", am::PC_REL }; // WD
opCodes[0x8f] = { "bbs0", am::PC_REL }; // WD
opCodes[0x9f] = { "bbs1", am::PC_REL }; // WD
opCodes[0xaf] = { "bbs2", am::PC_REL }; // WD
opCodes[0xbf] = { "bbs3", am::PC_REL }; // WD
opCodes[0xcf] = { "bbs4", am::PC_REL }; // WD
opCodes[0xdf] = { "bbs5", am::PC_REL }; // WD
opCodes[0xef] = { "bbs6", am::PC_REL }; // WD
opCodes[0xff] = { "bbs7", am::PC_REL }; // WD
return opCodes;
}
// TODO fix constexpr
const std::array<OpCode, 256> opCodes = getOpCodesArray();

15
Makefile Normal file
View File

@ -0,0 +1,15 @@
CFLAGS = -std=c++20 -O2
LFLAGS = -lbcm2835
TOOLS = eeprom_sim #test/test_out test/test_in test/test_edge
default: $(TOOLS)
.PHONY = test clean
test: test.cpp
%: %.cpp
g++ $< -o $@ $(CFLAGS) $(LFLAGS)
clean:
rm $(TOOLS)

120
eeprom.cpp Normal file
View File

@ -0,0 +1,120 @@
#include <ranges>
#include <bcm2835.h>
#include "util.hpp"
/* ADDRESS SETUP TIME
* depends on clock frequency, see datasheet. max 150@2MHz
* address is set after setup time (tADS) and is being hold for address hold time (tAH)
*
*
*/
constexpr uint64_t addressSetupTimeUS = 1; // 300e-9 s
class EEPROM {
EEPROM(const IOPins& ioPins, const AddressPins& addressPins, const Pin& pinCEb, const Pin& pinWEb, const Pin& pinOEb) {
}
}
void initPins() {
// set to input and pullup
auto setPinsInput = []<std::ranges::forward_range T>(const T& t) {
for (auto it = t.begin(); it != t.end(); it++) {
bcm2835_gpio_fsel(*it, BCM2835_GPIO_FSEL_INPT);
/* bcm2835_gpio_set_pud(*it, BCM2835_GPIO_PUD_UP); */
bcm2835_gpio_set_pud(*it, BCM2835_GPIO_PUD_OFF);
}
};
setPinsInput(addressPins);
setPinsInput(dataPins);
setPinsInput(controlPins);
}
void setIODirection(uint8_t direction) {
for (int i = 0; i < dataPins.size(); i++) {
bcm2835_gpio_fsel(dataPins.at(i), direction);
}
}
bool isChipEnabled() {
return !bcm2835_gpio_lev(CEb);
}
inline bool isRead() {
return bcm2835_gpio_lev(RWb);
}
uint16_t readAddress() {
uint16_t address = 0;
for (int i = 0; i < addressPins.size(); i++) {
address |= (bcm2835_gpio_lev(addressPins.at(i)) << i);
}
return address;
}
uint8_t readData() {
uint8_t address = 0;
for (int i = 0; i < dataPins.size(); i++) {
address |= (bcm2835_gpio_lev(dataPins.at(i)) << i);
}
return address;
}
inline void waitForFallingEdge(uint8_t pin) {
while (bcm2835_gpio_lev(pin) == LOW);
while (bcm2835_gpio_lev(pin) == HIGH);
}
inline void waitForRisingEdge(uint8_t pin) {
while (bcm2835_gpio_lev(pin) == HIGH);
while (bcm2835_gpio_lev(pin) == LOW);
}
inline void waitForEdge(uint8_t pin) {
if (bcm2835_gpio_lev(pin) == HIGH) {
while (bcm2835_gpio_lev(pin) == HIGH);
return;
}
else {
while (bcm2835_gpio_lev(pin) == LOW);
return;
}
}
/**
* @brief Simulate an AT28C256 EEPROM
* @details
* While the CEb is low, constantly read the address and put the corresponding data on the bus.
* While CEb is high, set data pins to input.
*/
void simulateEEPROM(std::stop_token token, std::array<char, UINT16_MAX> data) {
initPins();
printf("Begin EEPROM simulation\n");
uint16_t lastAddress = 0;
bool outputActive = false;
while (!token.stop_requested()) {
uint16_t currentAddress = readAddress();
bool chipEnabled = isChipEnabled();
if (chipEnabled && (!outputActive || (currentAddress != lastAddress))) {
setIODirection(BCM2835_GPIO_FSEL_OUTP);
outputActive = true;
uint32_t valueMask = byteToMask(data.at(currentAddress), dataPins);
bcm2835_gpio_write_mask(valueMask, ioMask);
/* std::cout << ">-" << gz::toBinString(currentAddress) << "[" << gz::toHexString(currentAddress) << "]-" << gz::toBinString(data.at(currentAddress)) << "[" << gz::toHexString(static_cast<uint16_t>(data.at(currentAddress)), 2) << "]"; */
/* std::cout << std::endl; */
}
if (!chipEnabled && outputActive) {
setIODirection(BCM2835_GPIO_FSEL_INPT);
outputActive = false;
}
lastAddress = currentAddress;
}
printf("End EEPROM simulation\n");
}

253
main.cpp Normal file
View File

@ -0,0 +1,253 @@
#include <stdio.h>
#include <bcm2835.h>
#include <signal.h>
#include <array>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <gz-util/string/conversion.hpp>
#include <cassert>
#include <thread>
#include <functional>
#include "6502_opcodes.hpp"
/* ADDRESS SETUP TIME
* depends on clock frequency, see datasheet. max 150@2MHz
* address is set after setup time (tADS) and is being hold for address hold time (tAH)
*
*
*/
constexpr uint64_t addressSetupTimeUS = 1; // 300e-9 s
constexpr std::array<uint8_t, 15> addressPins {
27, 22, 10, 9, 11, 5, 6, 13, // A0-A7
4, 2, 15, 3, // A8-A11
18, 17, 23 // A12-A14
};
constexpr std::array<uint8_t, 8> dataPins { 24, 25, 8, 7, 12, 16, 20, 21 };
constexpr uint8_t RWb = 19; // Read/Write
constexpr uint8_t CEb = 14; // Chip Enable
constexpr uint8_t PHI2 = 26; // clock
constexpr std::array<uint8_t, 3> controlPins { CEb, RWb, PHI2 };
template<size_t N>
constexpr uint32_t getMask(const std::array<uint8_t, N>& pins) {
uint32_t mask = 0;
for (int i = 0; i < pins.size(); i++) {
/* const uint8_t bit = pins.at(i) - 1; */
mask |= (1 << pins.at(i));
}
return mask;
}
constexpr uint32_t ioMask = getMask(dataPins);
constexpr uint32_t addressMask = getMask(addressPins);
struct CurrentState {
uint16_t addressBus = 0;
uint8_t dataBus = 0;
bool RWb = true;
std::string toString() const {
std::string s;
if (RWb) { s = "R-"; }
else { s = "W-"; }
s += gz::toBinString(addressBus) + "[" + gz::toHexString(addressBus, 4) + "]-"
+ gz::toBinString(dataBus) + "[" + gz::toHexString(static_cast<uint16_t>(dataBus), 2) + "]";
return s;
}
};
uint32_t byteToMask(char byte, const std::array<uint8_t, 8>& pins) {
uint32_t mask = 0;
for (int i = 0; i < 8; i++) {
if ((1 << i) & byte) { // if ith bit is set, set bit of corresponding pin
mask |= (1 << pins.at(i));
}
}
return mask;
}
void initPins() {
// set to input and pullup
auto setPinsInput = []<std::ranges::forward_range T>(const T& t) {
for (auto it = t.begin(); it != t.end(); it++) {
bcm2835_gpio_fsel(*it, BCM2835_GPIO_FSEL_INPT);
/* bcm2835_gpio_set_pud(*it, BCM2835_GPIO_PUD_UP); */
bcm2835_gpio_set_pud(*it, BCM2835_GPIO_PUD_OFF);
}
};
setPinsInput(addressPins);
setPinsInput(dataPins);
setPinsInput(controlPins);
}
void setIODirection(uint8_t direction) {
for (int i = 0; i < dataPins.size(); i++) {
bcm2835_gpio_fsel(dataPins.at(i), direction);
}
}
bool isChipEnabled() {
return !bcm2835_gpio_lev(CEb);
}
inline bool isRead() {
return bcm2835_gpio_lev(RWb);
}
uint16_t readAddress() {
uint16_t address = 0;
for (int i = 0; i < addressPins.size(); i++) {
address |= (bcm2835_gpio_lev(addressPins.at(i)) << i);
}
return address;
}
uint8_t readData() {
uint8_t address = 0;
for (int i = 0; i < dataPins.size(); i++) {
address |= (bcm2835_gpio_lev(dataPins.at(i)) << i);
}
return address;
}
constexpr std::array<const char*, 256> asciiChars {
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?", "@", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", "\"", "]", "^", "_", "`", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "{", "|", "}", "~", ""
};
inline void printData(const char& data) {
std::cout << '\'' << data << asciiChars.at(data) << "' "<< opCodes.at(data).getName();
}
inline void waitForFallingEdge(uint8_t pin) {
while (bcm2835_gpio_lev(pin) == LOW);
while (bcm2835_gpio_lev(pin) == HIGH);
}
inline void waitForRisingEdge(uint8_t pin) {
while (bcm2835_gpio_lev(pin) == HIGH);
while (bcm2835_gpio_lev(pin) == LOW);
}
inline void waitForEdge(uint8_t pin) {
if (bcm2835_gpio_lev(pin) == HIGH) {
while (bcm2835_gpio_lev(pin) == HIGH);
return;
}
else {
while (bcm2835_gpio_lev(pin) == LOW);
return;
}
}
/**
* @brief Simulate an AT28C256 EEPROM
* @details
* While the CEb is low, constantly read the address and put the corresponding data on the bus.
* While CEb is high, set data pins to input.
*/
void simulateEEPROM(std::stop_token token, std::array<char, UINT16_MAX> data) {
initPins();
printf("Begin EEPROM simulation\n");
uint16_t lastAddress = 0;
bool outputActive = false;
while (!token.stop_requested()) {
uint16_t currentAddress = readAddress();
bool chipEnabled = isChipEnabled();
if (chipEnabled && (!outputActive || (currentAddress != lastAddress))) {
setIODirection(BCM2835_GPIO_FSEL_OUTP);
outputActive = true;
uint32_t valueMask = byteToMask(data.at(currentAddress), dataPins);
bcm2835_gpio_write_mask(valueMask, ioMask);
/* std::cout << ">-" << gz::toBinString(currentAddress) << "[" << gz::toHexString(currentAddress) << "]-" << gz::toBinString(data.at(currentAddress)) << "[" << gz::toHexString(static_cast<uint16_t>(data.at(currentAddress)), 2) << "]"; */
/* std::cout << std::endl; */
}
if (!chipEnabled && outputActive) {
setIODirection(BCM2835_GPIO_FSEL_INPT);
outputActive = false;
}
lastAddress = currentAddress;
}
printf("End EEPROM simulation\n");
}
void printBusWithClock(std::stop_token token, uint8_t clockPin, const std::atomic<CurrentState>& currentState) {
printf("Begin bus printing\n");
auto state = currentState.load();
while (!token.stop_requested()) {
waitForRisingEdge(clockPin);
uint8_t chipEnabled = !bcm2835_gpio_lev(CEb);
uint8_t clock = bcm2835_gpio_lev(PHI2);
state.RWb = isRead();
state.addressBus = readAddress();
state.addressBus |= (chipEnabled << 15);
state.dataBus = readData();
if (clock == 0) { std::cout << "L-"; }
else { std::cout << "H-"; }
std::cout << state.toString();
if (chipEnabled) {
std::cout << " " << opCodes.at(state.dataBus).getName();
}
std::cout << std::endl;
}
printf("End bus printing\n");
}
void readFile(const char* filepath, std::array<char, UINT16_MAX>& bytes) {
std::ifstream file(filepath, std::ios_base::binary | std::ios_base::ate);
if (!file.is_open()) {
throw std::runtime_error("Error: Could not open file");
}
auto size = file.tellg();
if (size > UINT16_MAX) {
throw std::runtime_error("File is larger than UINT16_MAX");
}
file.seekg (0, std::ios::beg);
file.read(bytes.data(), size);
file.close();
}
void signalHandler(int signal) {
setIODirection(BCM2835_GPIO_FSEL_INPT);
bcm2835_close();
printf("Caught signal %d, exiting.\n", signal);
exit(0);
}
int main(int argc, const char** argv) {
if (bcm2835_init() != 1) {
printf("Error: Could not initalise gpio libraray\n");
return 1;
}
if (argc != 2) {
printf("Error: Expected exactly one argument (filename), got %d\n", argc);
return 1;
}
/* std::cout << "IOMask=" << gz::toBinString(ioMask) << ", AddressMask=" << gz::toBinString(addressMask) << std::endl; */
std::array<char, UINT16_MAX> bytes{};
readFile(argv[1], bytes);
std::cout << "Reset Vector " << gz::toBinString(bytes.at(0x7ffc)) << " - " << gz::toBinString(0x77fd) << std::endl;
std::jthread eepromT(simulateEEPROM, bytes);
std::atomic<CurrentState> cs;
std::jthread clockT(printBusWithClock, PHI2, std::ref(cs));
/* auto handler = std::bind(signalHandler, std::placeholders::_1, std::move(eepromT)); */
signal(SIGINT, signalHandler);
while(true) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}

6
util.hpp Normal file
View File

@ -0,0 +1,6 @@
#include <cstdint>
#include <array>
using Pin = uint8_t;
using IOPins = std::array<Pin, 8>;
using AddressPins = std::array<Pin, 15>;