initial commit
This commit is contained in:
parent
5ac2f9dc05
commit
bdb140c35f
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*eeprom_sim
|
365
cpp-sim-src/6502_opcodes.hpp
Normal file
365
cpp-sim-src/6502_opcodes.hpp
Normal 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();
|
10
cpp-sim-src/Makefile
Normal file
10
cpp-sim-src/Makefile
Normal file
@ -0,0 +1,10 @@
|
||||
CFLAGS = -std=c++20
|
||||
LFLAGS = -lbcm2835
|
||||
|
||||
TOOLS = eeprom_sim test/test_out test/test_in test/test_edge
|
||||
|
||||
default: $(TOOLS)
|
||||
|
||||
%: %.cpp
|
||||
g++ $< -o $@ $(CFLAGS) $(LFLAGS)
|
||||
|
256
cpp-sim-src/eeprom_sim.cpp
Normal file
256
cpp-sim-src/eeprom_sim.cpp
Normal file
@ -0,0 +1,256 @@
|
||||
#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 <stop_token>
|
||||
#include <thread>
|
||||
|
||||
#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, 18, 3, // A8-A11
|
||||
23, 17, 15 // 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(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() {
|
||||
for (int i = 0; i < addressPins.size(); i++) {
|
||||
bcm2835_gpio_fsel(addressPins.at(i), BCM2835_GPIO_FSEL_INPT);
|
||||
}
|
||||
for (int i = 0; i < controlPins.size(); i++) {
|
||||
bcm2835_gpio_fsel(controlPins.at(i), BCM2835_GPIO_FSEL_INPT);
|
||||
}
|
||||
for (int i = 0; i < dataPins.size(); i++) {
|
||||
bcm2835_gpio_fsel(dataPins.at(i), BCM2835_GPIO_FSEL_INPT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void setIODirection(uint8_t direction) {
|
||||
for (int i = 0; i < dataPins.size(); i++) {
|
||||
bcm2835_gpio_fsel(dataPins.at(i), direction);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool isChipEnabled() {
|
||||
// assumes the EEPROM is only hooked up via CE. If CE is low, it will ouput data
|
||||
if (bcm2835_gpio_lev(CEb) == LOW and bcm2835_gpio_lev(RWb) == HIGH) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool isRead() {
|
||||
return bcm2835_gpio_lev(CEb);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
void simulate(std::array<char, UINT16_MAX> data) {
|
||||
initPins();
|
||||
printf("Initalized Pins\n");
|
||||
|
||||
uint16_t lastAddress = 0;
|
||||
bool outputActive = false;
|
||||
|
||||
while (true) {
|
||||
uint16_t currentAddress = readAddress();
|
||||
bool readEnabled = isChipEnabled();
|
||||
if (readEnabled && (!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 << "> Address: " << gz::toBinString(currentAddress) << "[" << gz::toHexString(currentAddress) << "] - " << gz::toBinString(data.at(currentAddress)) << "[" << gz::toHexString(static_cast<uint16_t>(data.at(currentAddress)), 2) << "]";
|
||||
printData(data.at(currentAddress));
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
if (!readEnabled && outputActive) {
|
||||
setIODirection(BCM2835_GPIO_FSEL_INPT);
|
||||
outputActive = false;
|
||||
std::cout << "X Address: " << gz::toBinString(currentAddress) << "[" << gz::toHexString(currentAddress) << "]\n"; //" - " << gz::toBinString(data.at(address)) << std::endl;
|
||||
}
|
||||
|
||||
lastAddress = currentAddress;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void simulateEEPROM(std::stop_token token, const std::array<char, UINT16_MAX>& data) {
|
||||
printf("Begin EEPROM simulation\n");
|
||||
initPins();
|
||||
|
||||
while(!token.stop_requested()) {
|
||||
waitForFallingEdge(CEb);
|
||||
uint16_t currentAddress = readAddress();
|
||||
uint32_t valueMask = byteToMask(data.at(currentAddress), dataPins);
|
||||
setIODirection(BCM2835_GPIO_FSEL_OUTP);
|
||||
bcm2835_gpio_write_mask(valueMask, ioMask);
|
||||
std::cout << "> Address: " << gz::toBinString(currentAddress) << "[" << gz::toHexString(currentAddress) << "] - " << gz::toBinString(data.at(currentAddress)) << "[" << gz::toHexString(static_cast<uint16_t>(data.at(currentAddress)), 2) << "]";
|
||||
waitForRisingEdge(CEb);
|
||||
setIODirection(BCM2835_GPIO_FSEL_INPT);
|
||||
}
|
||||
printf("Exit 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()) {
|
||||
waitForFallingEdge(clockPin);
|
||||
state.RWb = isRead();
|
||||
state.addressBus = readAddress();
|
||||
state.dataBus = readData();
|
||||
std::cout << state.toString() << 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);
|
||||
|
||||
/* for (int i = 0; i < bytes.size(); i++) { */
|
||||
/* std::cout << gz::toHexString(static_cast<uint8_t>(bytes.at(i))) << " "; */
|
||||
/* if ((i+1) % 8 == 0) { std::cout << " "; } */
|
||||
/* if ((i+1) % 32 == 0) { std::cout << "\n"; } */
|
||||
/* } */
|
||||
/* std::cout << std::endl; */
|
||||
std::cout << "Reset Vector " << gz::toBinString(bytes.at(0x7ffc)) << " - " << gz::toBinString(0x77fd) << std::endl;
|
||||
/* simulate(bytes); */
|
||||
std::jthread eepromT(simulateEEPROM, std::ref(bytes));
|
||||
std::atomic<CurrentState> cs;
|
||||
std::jthread clockT(printBusWithClock, PHI2, std::ref(cs));
|
||||
|
||||
signal(SIGINT, signalHandler);
|
||||
|
||||
while(true);
|
||||
}
|
||||
|
353
eeprom.py
Normal file
353
eeprom.py
Normal file
@ -0,0 +1,353 @@
|
||||
from sys import argv
|
||||
from time import sleep
|
||||
from RPi.GPIO import IN, OUT
|
||||
import RPi.GPIO as GPIO
|
||||
from re import fullmatch
|
||||
# EEPROM AT28C256 Pin names and RPi GPIO Pin Numbers
|
||||
# b means bar = inverted
|
||||
|
||||
|
||||
gpio_l = [2, 3, 4, 17, 27, 22, 10, 9, 11, 5, 6, 13, 19, 26]
|
||||
gpio_r = [14, 15, 18, 23, 24, 25, 8, 7, 12, 16, 20, 21]
|
||||
#
|
||||
# Defining which 6502 pin goes to which GPIO pin
|
||||
#
|
||||
|
||||
A = [27, 22, 10, 9, 11, 5, 6, 13,
|
||||
17, 4, 3, 2,
|
||||
23, 18, 15]
|
||||
|
||||
IO = gpio_r[4:12] # 8 io pins
|
||||
IO.reverse()
|
||||
|
||||
OEb = 26 # Output Enable
|
||||
WEb = 19 # Write Enable
|
||||
CEb = 14 # Chip Enable is hooked up to A15 on the processor
|
||||
|
||||
controls = [CEb, WEb, OEb]
|
||||
|
||||
# TIMES
|
||||
# Read:
|
||||
t_ACC = 150 * 1e-9 # Address to Output Delay
|
||||
|
||||
# Write:
|
||||
t_AS = 0 # Address Setup time
|
||||
t_AH = 50 * 1e-9 # Address Hold Time
|
||||
t_CS = 0 # Chip Select Hold Time
|
||||
t_WP = 100 * 1e-9 # Write Pulse Width
|
||||
t_DS = 50 * 1e-9 # Data Setup Time_CS = 0
|
||||
t_DH = 0 # Data Hold Time
|
||||
t_WPH = 50 * 1e-9 # Write Puls High
|
||||
# t_WPH = 50 * 1e-4 # Write Pulse High !!!2*e5 longer than in Datasheet, since shorter high caused Problems with my Chip!!!
|
||||
|
||||
# setup the pins
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
for pin in controls:
|
||||
GPIO.setup(pin, OUT, initial=1) # inverted, is 1 means disable
|
||||
for pin in A:
|
||||
GPIO.setup(pin, OUT, initial=0)
|
||||
|
||||
|
||||
def setup_pins(IOdirection=OUT):
|
||||
# OUT when writing and IN when reading
|
||||
for pin in IO:
|
||||
GPIO.setup(pin, IOdirection)
|
||||
|
||||
|
||||
def print_pins():
|
||||
for i in range(len(A)):
|
||||
print(f"A{i} - {A[i]}")
|
||||
for i in range(len(IO)):
|
||||
print(f"IO{i} - {IO[i]}")
|
||||
print(f"CEb - {CEb}")
|
||||
print(f"WEb - {WEb}")
|
||||
print(f"OEb - {OEb}")
|
||||
|
||||
|
||||
def set_address(address: int, bits=8):
|
||||
"""
|
||||
set the address pins to the given value
|
||||
"""
|
||||
ad_bin = format(address, f"0{bits}b") # get the x-bit verion if the address, eg 12 -> 00001100
|
||||
for j in range(bits):
|
||||
# print("Address:", address, ad_bin, j)
|
||||
if ad_bin[bits-1-j] == "0":
|
||||
GPIO.output(A[j], 0)
|
||||
elif ad_bin[bits-1-j] == "1":
|
||||
GPIO.output(A[j], 1)
|
||||
return ad_bin
|
||||
|
||||
|
||||
def get_bits(i: int):
|
||||
"""
|
||||
return how many bits are needed to express the number in binary
|
||||
"""
|
||||
return len(bin(i)) - 2 # -2 for the "0x"
|
||||
|
||||
|
||||
def check_valid_list(l: list, bits=8):
|
||||
"""
|
||||
check if the list only has x-bit binary numbers
|
||||
"""
|
||||
for line in l:
|
||||
if not fullmatch("[01]{8}", line):
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_8_bit(l: list):
|
||||
for i in range(len(l)):
|
||||
l[i] = format(l[i], f"08b") # get the 8-bit bin value
|
||||
return l
|
||||
|
||||
def erase(from_ad=0, to_ad=32767, **keys):
|
||||
"""
|
||||
Write all 1 to the EEPROM
|
||||
WEb controlled
|
||||
"""
|
||||
data = [0xff for i in range(from_ad, to_ad)]
|
||||
write(data, from_ad=from_ad, **keys)
|
||||
print("Erased EEPROM - Done!")
|
||||
return
|
||||
|
||||
|
||||
def write_byte(byte, address, verbose=True):
|
||||
GPIO.output(OEb, 1)
|
||||
# setup the address
|
||||
ad_bin = set_address(address, bits=15)
|
||||
setup_pins(OUT)
|
||||
# Setup Data
|
||||
for j in range(8):
|
||||
if byte[7-j] == "1":
|
||||
bit = 1
|
||||
else:
|
||||
bit = 0
|
||||
GPIO.output(IO[j], bit)
|
||||
# wait "Address" Setup Time
|
||||
sleep(t_AS)
|
||||
# wait "Data Setup Time"
|
||||
sleep(t_DS)
|
||||
|
||||
GPIO.output(CEb, 0)
|
||||
# Start the write pulse -> enable WEb
|
||||
GPIO.output(WEb, 0)
|
||||
|
||||
# wait until minimum write pulse width is reached. in theory, should be t_WP-t_DS but this caused problems
|
||||
sleep(t_WP)
|
||||
|
||||
# End Write Pulse -> disable WEb
|
||||
GPIO.output(WEb, 1)
|
||||
GPIO.output(CEb, 1)
|
||||
|
||||
# wait "Data Hold"
|
||||
sleep(t_DH)
|
||||
|
||||
GPIO.output(CEb, 0)
|
||||
GPIO.cleanup(IO)
|
||||
setup_pins(IN)
|
||||
|
||||
# check the toggle bit IO6, if it stops toggling the write is done
|
||||
timeout = 0
|
||||
while timeout < 1e3:
|
||||
GPIO.output(OEb, 0)
|
||||
sleep(1e-9)
|
||||
bit1 = GPIO.input(IO[6])
|
||||
GPIO.output(OEb, 1)
|
||||
sleep(1e-9)
|
||||
GPIO.output(OEb, 0)
|
||||
sleep(1e-9)
|
||||
bit2 = GPIO.input(IO[6])
|
||||
GPIO.output(OEb, 1)
|
||||
sleep(1e-9)
|
||||
if bit1 == bit2:
|
||||
timeout = 1e3
|
||||
timeout += 1
|
||||
GPIO.output(CEb, 1)
|
||||
if verbose:
|
||||
print(f"Writing:\t0b{format(address, '015b')} - 0b{byte} ||| 0x{format(address, '04x')} - {hex(int(byte, 2))}")
|
||||
|
||||
def read_byte(address):
|
||||
GPIO.output(WEb, 1)
|
||||
setup_pins(IN)
|
||||
# set the address valid
|
||||
ad_bin = set_address(address, bits=15)
|
||||
|
||||
# low in chip/output enable -> enable
|
||||
GPIO.output(CEb, 0)
|
||||
GPIO.output(OEb, 0)
|
||||
|
||||
# wait the "Address to Output Delay" until the output is valid
|
||||
sleep(t_ACC)
|
||||
|
||||
byte = ""
|
||||
for j in range(8):
|
||||
if GPIO.input(IO[7-j]) == 1:
|
||||
byte += "1"
|
||||
else:
|
||||
byte += "0"
|
||||
|
||||
# high in OEb and CEb -> disable
|
||||
GPIO.output(OEb, 1)
|
||||
GPIO.output(CEb, 1)
|
||||
return int(byte, 2)
|
||||
|
||||
def read(from_ad=0, to_ad=255, delay=1e-3, ignore=[0xff], verbose=True, single_step=False, compare=None):
|
||||
"""
|
||||
from_ad: start address from where to read
|
||||
to_ad: end address to read to
|
||||
delay: delay between readings in s
|
||||
verbose wether to print the reading
|
||||
ignore list of values which are not printed
|
||||
"""
|
||||
content = []
|
||||
unequal = []
|
||||
for i in range(from_ad, to_ad + 1):
|
||||
byte = read_byte(i)
|
||||
content.append(byte)
|
||||
|
||||
if not compare and verbose and not byte in ignore:
|
||||
print(f"Reading:\t0b{format(i, '015b')} - 0b{format(byte, '08b')} ||| 0x{format(i, '04x')} - 0x{format(byte, '02x')}")
|
||||
elif compare:
|
||||
if not compare[i] == byte:
|
||||
unequal.append(i)
|
||||
print(f"Unequal at Address 0x{format(i, '04x')} ||| File: 0x{format(compare[i], '02x')} vs EEPROM: 0x{format(byte, '02x')}")
|
||||
|
||||
# wait artifical delay
|
||||
sleep(delay)
|
||||
if single_step:
|
||||
input("Press Return to read the next byte")
|
||||
if compare:
|
||||
return unequal
|
||||
return content
|
||||
|
||||
def write(content: list, from_ad=0, delay=0, single_step=False, verbose=True, check_written=True):
|
||||
"""
|
||||
Write a list if bytes to the eeprom.
|
||||
WEb controlled
|
||||
"""
|
||||
or_content = content.copy()
|
||||
content = get_8_bit(content)
|
||||
failed = []
|
||||
print(f"Writing to EEPROM: {len(content)} bytes from address {hex(from_ad)}.")
|
||||
|
||||
for i in range(len(content)):
|
||||
write_byte(content[i], from_ad + i, verbose=verbose)
|
||||
# wait artifical delay
|
||||
sleep(delay)
|
||||
if single_step:
|
||||
input("Press Return to write the next byte")
|
||||
print("Write to EEPROM - Done!")
|
||||
if check_written:
|
||||
print("Comparing EEPROM to file...")
|
||||
failed = read(from_ad=from_ad, delay=delay, single_step=single_step, verbose=False, compare=or_content)
|
||||
while len(failed) > 0:
|
||||
for ad in failed:
|
||||
write_byte(content[ad], ad, verbose=verbose)
|
||||
failed = read(from_ad=from_ad, delay=delay, single_step=single_step, verbose=False, compare=or_content)
|
||||
print("Comparing complete")
|
||||
return
|
||||
|
||||
def get_bytes(filepath, from_ad=0):
|
||||
with open(filepath, "rb") as file:
|
||||
bindata = []
|
||||
for byte in file.read():
|
||||
bindata.append(byte)
|
||||
return bindata[from_ad:]
|
||||
|
||||
|
||||
action = None
|
||||
file = None
|
||||
from_ad = 0
|
||||
to_ad = 32767 #2^15 -1
|
||||
delay = 0
|
||||
single_step = False
|
||||
verbose = False
|
||||
ignore = [0xff]
|
||||
content = []
|
||||
|
||||
if len(argv) > 1:
|
||||
for i in range(1, len(argv)):
|
||||
arg = argv[i]
|
||||
if argv[i-1] == "-w":
|
||||
action = "write_file"
|
||||
file = arg
|
||||
elif argv[i-1] == "-wh":
|
||||
action = "write_hex"
|
||||
content = arg.split(",")
|
||||
for i in range(len(content)):
|
||||
content[i] = int(content[i].replace("0x", ""), 16)
|
||||
elif arg == "-r":
|
||||
action = "read"
|
||||
verbose = True
|
||||
elif argv[i-1] == "-c":
|
||||
action = "compare"
|
||||
file = arg
|
||||
verbose = True
|
||||
elif arg == "-e":
|
||||
action = "erase"
|
||||
elif arg == "-h":
|
||||
action = "help"
|
||||
|
||||
# Addresses
|
||||
elif argv[i-1] == "--from":
|
||||
if "0x" in arg:
|
||||
from_ad = int(arg.replace("0x", ""), 16)
|
||||
else:
|
||||
from_ad = int(arg)
|
||||
elif argv[i-1] == "--to":
|
||||
if "0x" in arg:
|
||||
to_ad = int(arg.replace("0x", ""), 16)
|
||||
else:
|
||||
to_ad = int(arg)
|
||||
# options
|
||||
elif argv[i-1] == "--delay":
|
||||
delay = float(arg)
|
||||
elif arg == "--single_step":
|
||||
single_step = True
|
||||
elif arg == "--verbose":
|
||||
verbose = True
|
||||
elif argv[i-1] == "--ignore":
|
||||
ignore = arg.split(",")
|
||||
for i in range(len(ignore)):
|
||||
ignore[i] = int(ignore[i].replace("0x", ""), 16)
|
||||
|
||||
# print(action, file, from_ad, to_ad)
|
||||
if action == "write_file":
|
||||
write(get_bytes(file, from_ad=from_ad), from_ad=from_ad, delay=delay, single_step=single_step, verbose=verbose)
|
||||
elif action == "write_hex":
|
||||
write(content, from_ad=from_ad, delay=delay, single_step=single_step, verbose=verbose)
|
||||
elif action == "read":
|
||||
read(from_ad=from_ad, to_ad=to_ad, delay=delay, single_step=single_step, verbose=verbose, ignore=ignore)
|
||||
elif action == "compare":
|
||||
read(from_ad=from_ad, to_ad=to_ad, delay=delay, single_step=single_step, verbose=verbose, ignore=ignore, compare=get_bytes(file))
|
||||
elif action == "erase":
|
||||
erase(from_ad=from_ad, to_ad=to_ad, delay=delay, single_step=single_step, verbose=verbose)
|
||||
elif action == "help":
|
||||
print("""
|
||||
program options:
|
||||
-w file write file
|
||||
-e erase EEPROM
|
||||
-c compare EEPROM content to binary file
|
||||
-r read EEPROM
|
||||
-h print this
|
||||
--from x start at address x (can be int or hex with '0x' prefix))
|
||||
--to y end at address y
|
||||
--single_step single step the program
|
||||
--delay t extra delay t between cycles
|
||||
--verbose print extra information
|
||||
--ignore a,b,.. ignore the numbers a,b,... (in hex) when printing. Default is 0xff
|
||||
if no option is given the GPIO-Pin-settings are printed
|
||||
""")
|
||||
else:
|
||||
print("No valid action given. Printing Pin-Settings")
|
||||
print_pins()
|
||||
# if performing action from this script, put the code HERE:
|
||||
|
||||
|
||||
GPIO.cleanup()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
138
eeprom_sim.py
Normal file
138
eeprom_sim.py
Normal file
@ -0,0 +1,138 @@
|
||||
from time import time
|
||||
|
||||
from sys import argv
|
||||
from time import sleep
|
||||
from RPi.GPIO import IN, OUT
|
||||
import RPi.GPIO as GPIO
|
||||
|
||||
from opcodes import opcodes_d
|
||||
# EEPROM AT28C256 Pin names and RPi GPIO Pin Numbers
|
||||
# b means bar = inverted
|
||||
|
||||
|
||||
gpio_l = [2, 3, 4, 17, 27, 22, 10, 9, 11, 5, 6, 13, 19, 26]
|
||||
gpio_r = [14, 15, 18, 23, 24, 25, 8, 7, 12, 16, 20, 21]
|
||||
#
|
||||
# Defining which 6502 pin goes to which GPIO pin
|
||||
#
|
||||
"""
|
||||
IO = gpio_r[-8:] # 8 io pins
|
||||
IO.reverse()
|
||||
A = [13, 6, 5, 11, 9, 10, 22, 27,
|
||||
17, 4, 3, 2,
|
||||
23, 18, 15]
|
||||
|
||||
# OEb = 26 # Output Enable
|
||||
RWb = 19 # Write Enable
|
||||
CEb = 14 # Chip Enable is hooked up to A15 on the processor
|
||||
PHI2 = 26
|
||||
controls = [CEb, RWb, PHI2]
|
||||
"""
|
||||
A = [
|
||||
27, 22, 10, 9, 11, 5, 6, 13, # A0-A7
|
||||
4, 2, 18, 3, # A8-A11
|
||||
23, 17, 15 # A12-A14
|
||||
]
|
||||
|
||||
IO = [ 24, 25, 8, 7, 12, 16, 20, 21 ]
|
||||
|
||||
RWb = 19 # Write Enable
|
||||
CEb = 14 # Chip Enable is hooked up to A15 on the processor
|
||||
PHI2 = 26
|
||||
controls = [CEb, RWb, PHI2]
|
||||
|
||||
|
||||
# Address setup time
|
||||
t_AS = 300e-9
|
||||
# setup the pins
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
for pin in controls:
|
||||
GPIO.setup(pin, IN)
|
||||
for pin in A:
|
||||
GPIO.setup(pin, IN)
|
||||
|
||||
def setup_pins(IOdirection=OUT):
|
||||
# OUT when writing and IN when reading
|
||||
for pin in IO:
|
||||
if IOdirection == OUT:
|
||||
GPIO.setup(pin, IOdirection, initial=0)
|
||||
elif IOdirection == IN:
|
||||
GPIO.setup(pin, IOdirection)
|
||||
# print("setup pins", IOdirection)
|
||||
|
||||
def print_pins():
|
||||
for i in range(len(A)):
|
||||
print(f"A{i} - {A[i]}")
|
||||
for i in range(len(IO)):
|
||||
print(f"IO{i} - {IO[i]}")
|
||||
print(f"CEb - {CEb}")
|
||||
print(f"RWb - {RWb}")
|
||||
print(f"PHI2 - {PHI2}")
|
||||
|
||||
def get_8_bit(l: list):
|
||||
for i in range(len(l)):
|
||||
l[i] = format(l[i], f"08b") # get the 8-bit bin value
|
||||
return l
|
||||
|
||||
def check_enable():
|
||||
# assumes the EEPROM is only hooked up via CE. If CE is low, it will ouput data
|
||||
if GPIO.input(CEb) == 0 and GPIO.input(RWb) == 1:
|
||||
return True
|
||||
return False
|
||||
|
||||
def decode_address():
|
||||
ad_s = ""
|
||||
for i in range(len(A)):
|
||||
ad_s += str(GPIO.input(A[len(A)-1-i]))
|
||||
return int(ad_s, 2)
|
||||
|
||||
def simulate(path, verbose=True):
|
||||
with open(path, "rb") as file:
|
||||
bindata = file.read()
|
||||
data = []
|
||||
for i in range(len(bindata)):
|
||||
data.append(format(bindata[i], "08b"))
|
||||
while True:
|
||||
GPIO.cleanup(IO)
|
||||
setup_pins(IN)
|
||||
# address is set on falling edge
|
||||
channel = GPIO.wait_for_edge(PHI2, GPIO.FALLING, timeout=1000) # allow for KeyboardInterrupts
|
||||
if channel is None:
|
||||
continue
|
||||
# wait an address setup time, dependant on clock speed!
|
||||
sleep(t_AS)
|
||||
|
||||
enable = check_enable()
|
||||
address = decode_address()
|
||||
|
||||
# put the data on the bus
|
||||
if enable:
|
||||
setup_pins(OUT)
|
||||
for i in range(8):
|
||||
if data[address][i] == "0":
|
||||
GPIO.output(IO[7-i], 0)
|
||||
else:
|
||||
GPIO.output(IO[7-i], 1)
|
||||
# wait the output hold time
|
||||
if verbose:
|
||||
print(f"OUT|||{format(address + 0x8000, '015b')} - {data[address]}|||{format(address + 0x8000, '04x')} - {format(int(data[address], 2), '02x')}", end="")
|
||||
if int(data[address], 2) in opcodes_d:
|
||||
print(f" ||| {opcodes_d[int(data[address], 2)]}")
|
||||
else:
|
||||
print("")
|
||||
elif verbose:
|
||||
data_in = ""
|
||||
for i in range(8):
|
||||
data_in += str(GPIO.input(IO[7-i]))
|
||||
print(f"IN |||{format(address, '016b')} - {data_in}|||{format(address, '04x')} - {format(int(data_in, 2), '02x')}")
|
||||
if len(argv) > 1:
|
||||
try:
|
||||
simulate(argv[1])
|
||||
except KeyboardInterrupt:
|
||||
GPIO.cleanup()
|
||||
else:
|
||||
print("No filepath given. Printing Pin-Settings")
|
||||
print_pins()
|
||||
# if performing action from this script, put the code HERE:
|
||||
|
||||
GPIO.cleanup()
|
17
makerom.py
Normal file
17
makerom.py
Normal file
@ -0,0 +1,17 @@
|
||||
code = bytearray([0xa9, 0x69]) # lda 0x69
|
||||
code += bytearray([0x8d, 0x00, 0x00]) # sta 0x0000
|
||||
code += bytearray([0xa9, 0x42]) # lda 0x42
|
||||
code += bytearray([0x8d, 0x00, 0x00]) # sta 0x0000
|
||||
code += bytearray([0x4c, 0x00, 0x80]) # jmp 0x80000
|
||||
|
||||
|
||||
rom = code + bytearray([0xea] *(32768 - len(code)))
|
||||
|
||||
rom[0x7ffc] = 0x00 # beim reset program counter auf 0x8000 (entspricht 0x00 auf dem EEPROM)
|
||||
rom[0x7ffd] = 0x80
|
||||
|
||||
|
||||
with open("rom.bin", "wb") as file:
|
||||
file.write(rom)
|
||||
|
||||
|
71
monitor.py
Normal file
71
monitor.py
Normal file
@ -0,0 +1,71 @@
|
||||
import RPi.GPIO as GPIO
|
||||
from RPi.GPIO import IN
|
||||
|
||||
# Monitor the 6502 processor
|
||||
gpio_l = [2, 3, 4, 17, 27, 22, 10, 9, 11, 5, 6, 13, 19, 26]
|
||||
gpio_r = [14, 15, 18, 23, 24, 25, 8, 7, 12, 16, 20, 21]
|
||||
#
|
||||
# Defining which 6502 pin goes to which GPIO pin
|
||||
#
|
||||
|
||||
A = gpio_l[0:12]
|
||||
A.reverse() # first 11 address pins, left side of 6502
|
||||
A += [23, 18, 15, 14] # last 4 address pins
|
||||
|
||||
D = gpio_r[-8:] # 8 io pins
|
||||
D.reverse()
|
||||
|
||||
PHI2 = 26
|
||||
RWB = 19
|
||||
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
|
||||
# OUT when writing and IN when reading
|
||||
for pin in [PHI2, RWB]:
|
||||
GPIO.setup(pin, IN)
|
||||
|
||||
for pin in A:
|
||||
GPIO.setup(pin, IN)
|
||||
|
||||
for pin in D:
|
||||
GPIO.setup(pin, IN)
|
||||
|
||||
def print_pins():
|
||||
for i in range(len(A)):
|
||||
print(f"A{i} - {A[i]}")
|
||||
for i in range(len(D)):
|
||||
print(f"D{i} - {D[i]}")
|
||||
print(f"PHI2 - {PHI2}")
|
||||
print(f"RWB - {RWB}")
|
||||
|
||||
def start_monitor():
|
||||
run = True
|
||||
while run:
|
||||
# wait for a rising clock edge before outputting
|
||||
channel = GPIO.wait_for_edge(PHI2, GPIO.FALLING, timeout=1000) # at least every 1 seconds
|
||||
if channel is None:
|
||||
continue
|
||||
address = ""
|
||||
data = ""
|
||||
for i in range(len(A)):
|
||||
address += str(GPIO.input(A[len(A)-1-i]))
|
||||
for i in range(len(D)):
|
||||
data += str(GPIO.input(D[len(D)-1-i]))
|
||||
if GPIO.input(RWB):
|
||||
rwb = "r"
|
||||
else:
|
||||
rwb = "w"
|
||||
line = f"0b{address} - 0b{data}|||0x{format(int(address, 2), '04x')} - 0x{format(int(data, 2), '02x')}|||{rwb} "
|
||||
print(line)
|
||||
|
||||
|
||||
print_pins()
|
||||
try:
|
||||
start_monitor()
|
||||
except KeyboardInterrupt:
|
||||
GPIO.cleanup()
|
||||
|
||||
|
||||
|
||||
|
||||
|
23
opcodes.py
Normal file
23
opcodes.py
Normal file
@ -0,0 +1,23 @@
|
||||
opcodes_d = {
|
||||
|
||||
# branches
|
||||
0xf0: "beq r",
|
||||
0xd0: "bne r",
|
||||
# jump
|
||||
0x4c: "jmp a",
|
||||
0x20: "jsr s",
|
||||
0x60: "rts s",
|
||||
# load
|
||||
0xad: "lda a",
|
||||
0xa9: "lda #",
|
||||
0xa2: "ldx #",
|
||||
# store
|
||||
0x8d: "sta a",
|
||||
# compare
|
||||
0xc9: "cmp #",
|
||||
# increments
|
||||
0xee: "inc a",
|
||||
0x1a: "inc A",
|
||||
0xe8: "inx i",
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user