From bdb140c35f851e849c4b3bd35bf4798ec18bb44d Mon Sep 17 00:00:00 2001 From: "matthias@arch" Date: Thu, 26 Oct 2023 19:00:17 +0200 Subject: [PATCH] initial commit --- .gitignore | 1 + cpp-sim-src/6502_opcodes.hpp | 365 +++++++++++++++++++++++++++++++++++ cpp-sim-src/Makefile | 10 + cpp-sim-src/eeprom_sim.cpp | 256 ++++++++++++++++++++++++ eeprom.py | 353 +++++++++++++++++++++++++++++++++ eeprom_sim.py | 138 +++++++++++++ makerom.py | 17 ++ monitor.py | 71 +++++++ opcodes.py | 23 +++ 9 files changed, 1234 insertions(+) create mode 100644 .gitignore create mode 100644 cpp-sim-src/6502_opcodes.hpp create mode 100644 cpp-sim-src/Makefile create mode 100644 cpp-sim-src/eeprom_sim.cpp create mode 100644 eeprom.py create mode 100644 eeprom_sim.py create mode 100644 makerom.py create mode 100644 monitor.py create mode 100644 opcodes.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ae77c8 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*eeprom_sim diff --git a/cpp-sim-src/6502_opcodes.hpp b/cpp-sim-src/6502_opcodes.hpp new file mode 100644 index 0000000..4aac73e --- /dev/null +++ b/cpp-sim-src/6502_opcodes.hpp @@ -0,0 +1,365 @@ +#pragma once + +#include +#include +#include +#include + +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 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 getOpCodesArray() { + using am = AddressingMode; + std::array 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 opCodes = getOpCodesArray(); diff --git a/cpp-sim-src/Makefile b/cpp-sim-src/Makefile new file mode 100644 index 0000000..3494fc1 --- /dev/null +++ b/cpp-sim-src/Makefile @@ -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) + diff --git a/cpp-sim-src/eeprom_sim.cpp b/cpp-sim-src/eeprom_sim.cpp new file mode 100644 index 0000000..3173fdb --- /dev/null +++ b/cpp-sim-src/eeprom_sim.cpp @@ -0,0 +1,256 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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 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 controlPins { CEb, RWb, PHI2 }; + +template +constexpr uint32_t getMask(const std::array& 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& 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 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 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(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& 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(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) { + 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& 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 bytes{}; + readFile(argv[1], bytes); + + /* for (int i = 0; i < bytes.size(); i++) { */ + /* std::cout << gz::toHexString(static_cast(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 cs; + std::jthread clockT(printBusWithClock, PHI2, std::ref(cs)); + + signal(SIGINT, signalHandler); + + while(true); +} + diff --git a/eeprom.py b/eeprom.py new file mode 100644 index 0000000..f3c6d17 --- /dev/null +++ b/eeprom.py @@ -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() + + + + + + + diff --git a/eeprom_sim.py b/eeprom_sim.py new file mode 100644 index 0000000..6e13a84 --- /dev/null +++ b/eeprom_sim.py @@ -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() diff --git a/makerom.py b/makerom.py new file mode 100644 index 0000000..d1b0cb7 --- /dev/null +++ b/makerom.py @@ -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) + + diff --git a/monitor.py b/monitor.py new file mode 100644 index 0000000..418286d --- /dev/null +++ b/monitor.py @@ -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() + + + + + diff --git a/opcodes.py b/opcodes.py new file mode 100644 index 0000000..799c941 --- /dev/null +++ b/opcodes.py @@ -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", + +}