├── .gitattributes ├── Boot_Screen.ino ├── Display_Functions.ino ├── Info_Mode.ino ├── Input_Direction.ino ├── README.md ├── Register_Mode.ino ├── SB116_Programmers_Calculator.ino ├── Softkeys.ino ├── Stack_Mode.ino └── Store_Mode.ino /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /Boot_Screen.ino: -------------------------------------------------------------------------------- 1 | unsigned long previousMillis = 0; 2 | const long interval = 2000; 3 | 4 | void drawBootScreen() { 5 | unsigned long currentMillis = millis(); 6 | u8g2.drawXBM(47, 19, sb_solid_width, sb_solid_height, sb_solid_bits); 7 | 8 | if (currentMillis - previousMillis >= interval) { 9 | currentMode = REGISTER_DISPLAY; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Display_Functions.ino: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Draws the softkeys along the bottom edge of the screen with a label. 4 | * @param {int} keyIndex The key number 0 thru 4. 5 | * @param {String} keyLabel The key label, only the first 5 characters are printed. 6 | * @param {bool} hasSubKeys Changes style to show there are nested sub-keys. 7 | */ 8 | void drawSoftKey(int keyIndex, String keyLabel, bool hasSubKeys) { 9 | int keyWidth = 24; 10 | int keyHeight = 8; 11 | int screenBottom = u8g2.getDisplayHeight() - 1; 12 | 13 | int x = 26 * keyIndex; // X offset of the key calculated by it's index 14 | 15 | u8g2.setDrawColor(1); 16 | 17 | /* ___________ 18 | * / \ 19 | * | | 20 | */ 21 | 22 | u8g2.drawVLine(x, screenBottom-keyHeight+3, keyHeight-2); // left line 23 | u8g2.drawVLine(x+keyWidth-1, screenBottom-keyHeight+3, keyHeight-2); // right line 24 | 25 | u8g2.drawHLine(x+2, screenBottom-keyHeight+1, keyWidth-4); // top line 26 | 27 | u8g2.drawPixel(x+1, screenBottom-keyHeight+2); // top left corner 28 | u8g2.drawPixel(x+keyWidth-2, screenBottom-keyHeight+2); // top right corner 29 | 30 | // Fill in the lower right corner to show more 31 | if (hasSubKeys) { 32 | u8g2.drawPixel(x+keyWidth-2, screenBottom-1); 33 | u8g2.drawPixel(x+keyWidth-2, screenBottom); 34 | u8g2.drawPixel(x+keyWidth-3, screenBottom); 35 | } 36 | 37 | drawString(x+2, screenBottom, keyLabel, 0, false); 38 | } 39 | 40 | 41 | /** 42 | * Draw the string at the given coordinates and size, optionally choose to draw an inverted box over the text 43 | * @param {int} x The string X coordinate. 44 | * @param {int} y The string Y coordinate of the text baseline. 45 | * @param {String} stringToDraw A string containing the text to draw. 46 | * @param {int} fontSize Se the font size from 0-3 for the 4 available system font sizes small to large. 47 | */ 48 | void drawString(int x, int y, String stringToDraw, int fontSize, bool drawInverted) { 49 | switch (fontSize) { 50 | case 2: 51 | u8g2.setFont(u8g2_font_crox3h_tf); // Large 12px (was u8g2_font_freedoomr10_tu) 52 | break; 53 | case 1: 54 | u8g2.setFont(u8g2_font_t0_11_tf); // Small 8px 55 | break; 56 | default: 57 | u8g2.setFont(u8g2_font_micro_tr); // Extra small 5px - keep 58 | } 59 | 60 | u8g2.setDrawColor(1); 61 | u8g2.setCursor(x, y); 62 | u8g2.print(stringToDraw); 63 | 64 | if (drawInverted) { 65 | u8g2.setDrawColor(2); 66 | //int stringWidth = u8g2.getStrWidth(stringToDraw.c_str()); 67 | u8g2.drawBox(x-1, y-u8g2.getMaxCharHeight()-1, u8g2.getStrWidth(stringToDraw.c_str())+2, u8g2.getMaxCharHeight()+2); 68 | } 69 | } 70 | 71 | 72 | 73 | 74 | int asciiToInt(char asciiIn) { 75 | int returnVal = 0; 76 | 77 | // Convert the ASCII codes into numbers 0 to F 78 | if ((asciiIn >= 48) && (asciiIn <= 57)) { // numbers 0 to 9 in ASCII 79 | returnVal = asciiIn - 48; 80 | } else if ((asciiIn >= 97) && (asciiIn <= 102)) { // characters a to f 81 | returnVal = asciiIn - 87; 82 | } 83 | return returnVal; 84 | } 85 | -------------------------------------------------------------------------------- /Info_Mode.ino: -------------------------------------------------------------------------------- 1 | void drawInfoScreen() { 2 | u8g2.drawXBM(2, 2, sb_solid_width, sb_solid_height, sb_solid_bits); 3 | drawString(45, 15, "SB116", 2, false); 4 | drawString(45, 26, "PROGRAMMER", 1, false); 5 | 6 | 7 | drawString(2, 52, "FIRMWARE VERSION 1.0.0", 0, false); 8 | drawString(2, 60, "(C) 2022 SB ELECTRONICS LTD.", 0, false); 9 | } 10 | -------------------------------------------------------------------------------- /Input_Direction.ino: -------------------------------------------------------------------------------- 1 | void getInput() { 2 | char key = keypad.getKey(); 3 | 4 | // User has pressed a button in this loop 5 | if (key != NO_KEY) { 6 | if ( 7 | (key == '!') || 8 | (key == '@') || 9 | (key == '#') || 10 | (key == '$') || 11 | (key == '%') 12 | ) { 13 | pressedSoftkey(key); 14 | } else if (key == 'E') { // E for Exit out of the current mode, back to Register mode 15 | if (currentMode == REGISTER_DISPLAY) { 16 | X = 0; // bonus feature: Exit also clears X reg if in reg mode. 17 | } 18 | 19 | // Exit back to the main register display mode and reset any required variables 20 | currentMode = REGISTER_DISPLAY; 21 | currentSoftkeyMenu = MAIN_MENU; 22 | 23 | storageModeWaitingOnInput = false; 24 | storageOperation = 0; 25 | 26 | //stackModeWaitingOnInput = false; 27 | } else { 28 | // Send input to the current mode 29 | switch (currentMode) { 30 | case REGISTER_DISPLAY: 31 | registerModeInputHandler(key); 32 | break; 33 | case STORE_SCREEN: 34 | storeModeInputHandler(key); 35 | break; 36 | case STACK_SCREEN: 37 | stackModeInputHandler(key); 38 | break; 39 | } 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SB116 Programmers Calculator 2 | 3 | An Arduino Nano based 16-bit integer calculator designed for helping with 6502 programming projects. It can convert between binary, decimal, octal and hexadecimal and perform arithmetic and other logical operations in an RPN style. 4 | 5 | Use this repository to look at my terrible code! 6 | 7 | [Full details on my blog post](https://unimplementedtrap.com/sb116-programmers-calculator) 8 | 9 | ![SB116](https://unimplementedtrap.com/media/sb116/SB116-Hero-thumb.jpg) -------------------------------------------------------------------------------- /Register_Mode.ino: -------------------------------------------------------------------------------- 1 | void registerModeInputHandler(char key) { 2 | base = getBase(); 3 | 4 | if ( 5 | ((key >= 48) && (key <= 57)) || // numbers 0 to 9 in ASCII 6 | ((key >= 97) && (key <= 102)) // characters a to f 7 | ) { 8 | inputDigit(key); 9 | } else if (key == '=') { 10 | Z = Y; 11 | Y = X; 12 | X = 0; 13 | lastOperationStatus = SREG; 14 | } else if (key == 'D') { // Set decimal display 15 | setBase(10); 16 | } else if (key == 'H') { // Set hexadecimal display 17 | setBase(16); 18 | } else if (key == 'O') { // Set octal display 19 | setBase(8); 20 | } else if (key == 'B') { // Set binary display 21 | setBase(2); 22 | } else if (key == 'S') { // Swap the X and Y registers 23 | invisibleSwap = X; 24 | X = Y; 25 | Y = invisibleSwap; 26 | lastOperationStatus = SREG; 27 | } else if (key == 'R') { // Roll the registers downwards 28 | invisibleSwap = X; 29 | X = Y; 30 | Y = Z; 31 | Z = invisibleSwap; 32 | lastOperationStatus = SREG; 33 | } else if (key == 'X') { // Invert the sign of the X register 34 | X = X * -1; 35 | lastOperationStatus = SREG; 36 | } else if ( // These transform the value of the X register (i.e. not a calculation) 37 | (key == '~') || 38 | (key == '<') || 39 | (key == '>') 40 | ) { 41 | inputTransform(key); 42 | } else if ( // Perform operation on the X and Y registers. 43 | (key == '+') || 44 | (key == '-') || 45 | (key == '*') || 46 | (key == '/') || 47 | (key == '&') || 48 | (key == '|') || 49 | (key == '^') 50 | ) { 51 | inputOperator(key); 52 | } 53 | } 54 | 55 | 56 | void inputDigit(int inputNumber) { 57 | inputNumber = asciiToInt(inputNumber); 58 | 59 | 60 | // Allow only the correct input digits for the current base 61 | if ( 62 | ((base == 2) && (inputNumber > 1)) || 63 | ((base == 8) && (inputNumber > 7)) || 64 | ((base == 10) && (inputNumber > 9)) 65 | ) { 66 | return; 67 | } 68 | 69 | 70 | int shiftRegisterBy = 4; 71 | if (base == 8) { // if octal 72 | shiftRegisterBy = 3; 73 | } else if (base == 2) { // if binary 74 | shiftRegisterBy = 1; 75 | } 76 | 77 | if (base == 10) { 78 | if (X >= 0) { 79 | // X is currently postive. Only append the digit if we don't roll over 32,767 into a negative number 80 | if ( ((X * 10) + inputNumber) >= 0 ) { 81 | X = X * 10; 82 | X += inputNumber; 83 | } 84 | } else { 85 | // X is currently negative. Only append the digit if we don't roll up into a posative number 86 | if ( ((X * 10) + inputNumber) < 0 ) { 87 | X = X * 10; 88 | X += inputNumber; 89 | } 90 | } 91 | //X = X * 10; 92 | } else { 93 | X = X << shiftRegisterBy; 94 | X += inputNumber; 95 | } 96 | 97 | 98 | 99 | } 100 | 101 | 102 | void inputOperator(char inputOperator) { 103 | switch (inputOperator) { 104 | case '+': 105 | X = Y + X; 106 | break; 107 | case '-': 108 | X = Y - X; 109 | break; 110 | case '*': 111 | X = Y * X; 112 | break; 113 | case '/': 114 | X = Y / X; 115 | break; 116 | case '&': 117 | X = Y & X; 118 | break; 119 | case '|': 120 | X = Y | X; 121 | break; 122 | case '^': 123 | X = Y ^ X; 124 | break; 125 | } 126 | 127 | lastOperationStatus = SREG; 128 | Y = Z; 129 | } 130 | 131 | 132 | void inputTransform(char inputTransformer) { 133 | switch (inputTransformer) { 134 | case '>': 135 | X = X >> 1; 136 | break; 137 | case '<': 138 | X = X << 1; 139 | break; 140 | case '~': 141 | X = ~X; 142 | break; 143 | } 144 | 145 | lastOperationStatus = SREG; 146 | } 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | void drawValues() { 158 | u8g2.setFont(u8g2_font_9x15_mf); 159 | u8g2.setCursor(1, 22); 160 | u8g2.print("Z:"); 161 | printRegister(Z, 70, 22); // was 30 162 | 163 | u8g2.setFont(u8g2_font_9x15_mf); 164 | u8g2.setCursor(1, 36); 165 | u8g2.print("Y:"); 166 | printRegister(Y, 70, 36); 167 | 168 | u8g2.setFont(u8g2_font_9x15_mf); 169 | u8g2.setCursor(1, 50); 170 | u8g2.print("X:"); 171 | printRegister(X, 70, 50); 172 | } 173 | 174 | // Converts the binary value stored in the register to a string and prints it at the set location 175 | void printRegister(int registerToPrint, int xCoord, int yCoord) { 176 | int maxStringLength; 177 | String outp; 178 | bool registerIsNegative = false; 179 | 180 | switch (base) { 181 | case 10: 182 | maxStringLength = 6; 183 | outp += String(registerToPrint, DEC); 184 | break; 185 | case 16: 186 | maxStringLength = 6; 187 | outp += String(registerToPrint, HEX); 188 | break; 189 | case 8: 190 | maxStringLength = 6; 191 | outp += String(registerToPrint, OCT); 192 | break; 193 | case 2: 194 | maxStringLength = 16; 195 | outp += String(registerToPrint, BIN); 196 | break; 197 | } 198 | 199 | // Right aligning 200 | for ( int i=0; i< (outp.length() - maxStringLength); i++ ) { 201 | if (base == 2) { 202 | // Pad the binary value with 0 203 | outp = "0" + outp; 204 | } else { 205 | outp = " " + outp; 206 | } 207 | } 208 | 209 | 210 | 211 | if (base == 2) { 212 | u8g2.setFont(u8g2_font_5x8_mn); // Use a 5x8 px font 213 | u8g2.setCursor(32, yCoord); 214 | 215 | // split into groups of 4 216 | outp = outp.substring(0,4) + " " + 217 | outp.substring(4,8) + " " + 218 | outp.substring(8,12) + " " + 219 | outp.substring(12,16); 220 | 221 | u8g2.print(outp); 222 | } else { 223 | u8g2.setFont(u8g2_font_9x15_mf); 224 | u8g2.setCursor(xCoord, yCoord); 225 | 226 | outp.toUpperCase(); 227 | u8g2.print(outp); 228 | } 229 | 230 | 231 | } 232 | 233 | 234 | 235 | 236 | void drawStatus() { 237 | // Show base 238 | switch (base) { 239 | case 10: 240 | drawString(1, 6, "DEC", 0, false); 241 | break; 242 | case 16: 243 | drawString(1, 6, "HEX", 0, false); 244 | break; 245 | case 8: 246 | drawString(1, 6, "OCT", 0, false); 247 | break; 248 | case 2: 249 | drawString(1, 6, "BIN", 0, false); 250 | break; 251 | } 252 | 253 | 254 | 255 | 256 | // Status registers 257 | int statusX = 51; 258 | drawString(statusX, 6, "CRRY", 0, (lastOperationStatus & 0x01) ); 259 | 260 | statusX += 20; 261 | drawString(statusX, 6, "ZERO", 0, (lastOperationStatus & 0x02) ); 262 | 263 | statusX += 20; 264 | drawString(statusX, 6, "OVRFL", 0, (lastOperationStatus & 0x08) ); 265 | 266 | statusX += 24; 267 | drawString(statusX, 6, "NEG", 0, (lastOperationStatus & 0x04) ); 268 | 269 | 270 | 271 | 272 | 273 | // Status bar's horizontal line 274 | u8g2.setDrawColor(1); 275 | u8g2.drawHLine(0, 8, 128); 276 | } 277 | -------------------------------------------------------------------------------- /SB116_Programmers_Calculator.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | /* Constructor */ 8 | U8G2_SSD1309_128X64_NONAME0_2_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 13, /* data=*/ 11, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8); 9 | 10 | 11 | #define sb_solid_width 33 12 | #define sb_solid_height 26 13 | static char sb_solid_bits[] = { 14 | 0xFC, 0x3F, 0xFE, 0x7F, 0x00, 0xFE, 0x7F, 0xFE, 0xFF, 0x00, 0xFF, 0xFF, 15 | 0xFE, 0xFF, 0x01, 0xFF, 0xFF, 0xFE, 0xFF, 0x01, 0xFF, 0xFF, 0xFE, 0xFF, 16 | 0x01, 0xFF, 0xFF, 0xFE, 0xFF, 0x01, 0xFF, 0xFE, 0xFE, 0xFD, 0x01, 0xFF, 17 | 0xFE, 0xFE, 0xFD, 0x01, 0xFF, 0x00, 0xFE, 0xFD, 0x01, 0xFF, 0x3F, 0xFE, 18 | 0xFD, 0x01, 0xFF, 0x7F, 0xFE, 0xFD, 0x01, 0xFF, 0xFF, 0xFE, 0xFF, 0x01, 19 | 0xFF, 0xFF, 0xFE, 0xFF, 0x00, 0xFF, 0xFF, 0xFE, 0x3F, 0x00, 0xFE, 0xFF, 20 | 0xFE, 0xFF, 0x00, 0xFC, 0xFF, 0xFE, 0xFD, 0x01, 0x00, 0xFE, 0xFE, 0xFD, 21 | 0x01, 0xFF, 0xFE, 0xFE, 0xFD, 0x01, 0xFF, 0xFE, 0xFE, 0xFD, 0x01, 0xFF, 22 | 0xFE, 0xFE, 0xFF, 0x01, 0xFF, 0xFE, 0xFE, 0xFF, 0x01, 0xFF, 0xFF, 0xFE, 23 | 0xFF, 0x01, 0xFF, 0xFF, 0xFE, 0xFF, 0x01, 0xFF, 0xFF, 0xFE, 0xFF, 0x01, 24 | 0xFE, 0x7F, 0xFE, 0xFF, 0x00, 0xFC, 0x3F, 0xFE, 0x7F, 0x00, }; 25 | 26 | bool bootInProgress = true; 27 | 28 | const byte rows = 8; //four rows 29 | const byte cols = 5; //three columns 30 | const char keys[rows][cols] = { 31 | {'!','@','#','$','%'}, 32 | {'D','H','O','B','E'}, 33 | {'<','>','d','e','f'}, 34 | {'&','~','a','b','c'}, 35 | {'|','^','7','8','9'}, 36 | {'*','/','4','5','6'}, 37 | {'+','-','1','2','3'}, 38 | {'S','R','0','X','='} 39 | }; 40 | const byte rowPins[rows] = {A0, 12, 7, 6, 5, 4, 3, 2}; //connect to the row pinouts of the keypad 41 | const byte colPins[cols] = {A1, A2, A3, A4, A5}; //connect to the column pinouts of the keypad 42 | 43 | Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, rows, cols ); 44 | 45 | // Backwards: 46 | const bool binaryMap[16][4] = { 47 | {false, false, false, false}, // 0000 48 | {true, false, false, false}, // 1000 49 | {false, true, false, false}, // 0100 50 | {true, true, false, false}, // 1100 51 | {false, false, true, false}, // 0010 52 | {true, false, true, false}, // 1010 53 | {false, true, true, false}, // 0110 54 | {true, true, true, false}, // 1110 55 | {false, false, false, true }, // 0001 56 | {true, false, false, true }, // 1001 57 | {false, true, false, true }, // 0101 58 | {true, true, false, true }, // 1101 59 | {false, false, true, true }, // 0011 60 | {true, false, true, true }, // 1011 61 | {false, true, true, true }, // 0111 62 | {true, true, true, true } // 1111 63 | }; 64 | 65 | 66 | 67 | enum menu { 68 | MAIN_MENU, // Default softkey menu 69 | BASE, // Select the numerical base system to use 70 | WORD_SIZE, // Change the register word size 71 | INFO, // System info screen 72 | STORE_MENU, // 16 value heap storage display 73 | STACK_MENU // 16 value stack storage display 74 | }; 75 | 76 | menu currentSoftkeyMenu = MAIN_MENU; 77 | 78 | 79 | 80 | int base = 16; 81 | 82 | enum mode { 83 | BOOT_SCREEN, 84 | REGISTER_DISPLAY, 85 | INFO_SCREEN, 86 | STORE_SCREEN, 87 | STACK_SCREEN 88 | }; 89 | 90 | mode currentMode = BOOT_SCREEN; 91 | 92 | // Registers 93 | int Z = 0; 94 | int Y = 0; 95 | int X = 0; 96 | int invisibleSwap = 0; 97 | 98 | 99 | byte lastOperationStatus = 0%00000010; // All flags 0, Zero is 1. 100 | 101 | 102 | 103 | 104 | 105 | bool storageModeWaitingOnInput = false; 106 | int storageOperation = 0; // 0 = read, 1 = write 107 | 108 | 109 | int stackPointer = 0; 110 | 111 | 112 | int getBase() { 113 | return base; 114 | } 115 | 116 | void setBase(int baseToSet) { 117 | if ( 118 | (baseToSet == 2) || 119 | (baseToSet == 8) || 120 | (baseToSet == 10) || 121 | (baseToSet == 16) 122 | ) { 123 | base = baseToSet; 124 | EEPROM.put(1022, base); 125 | } 126 | } 127 | 128 | 129 | 130 | 131 | 132 | /* u8g2.begin() is required and will sent the setup/init sequence to the display */ 133 | void setup(void) { 134 | u8g2.begin(); 135 | Serial.begin(9600); 136 | //setBase(16); 137 | EEPROM.get(1022, base); 138 | 139 | EEPROM.get(64, stackPointer); 140 | } 141 | 142 | 143 | /* draw something on the display with the `firstPage()`/`nextPage()` loop*/ 144 | void loop(void) { 145 | 146 | if (Serial.available() > 0) { 147 | if (Serial.read() == 'S') { 148 | u8g2.writeBufferXBM(Serial); 149 | } 150 | } 151 | 152 | 153 | getInput(); 154 | 155 | 156 | 157 | 158 | u8g2.firstPage(); 159 | do { 160 | switch (currentMode) { 161 | case BOOT_SCREEN: 162 | drawBootScreen(); 163 | break; 164 | case REGISTER_DISPLAY: 165 | drawStatus(); 166 | drawValues(); 167 | break; 168 | case INFO_SCREEN: 169 | drawInfoScreen(); 170 | break; 171 | case STORE_SCREEN: 172 | drawStoreScreen(); 173 | break; 174 | case STACK_SCREEN: 175 | drawStackScreen(); 176 | break; 177 | } 178 | 179 | updateSoftkeys(); 180 | } while ( u8g2.nextPage() ); 181 | 182 | 183 | } 184 | -------------------------------------------------------------------------------- /Softkeys.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Softkeys manager 3 | * A really kludgy way of handling a nested menu structure that I just made up as I went along. 4 | */ 5 | 6 | 7 | /** 8 | * Displays the keys for the current softkey menu 9 | */ 10 | void updateSoftkeys() { 11 | if (currentMode != BOOT_SCREEN) { 12 | switch (currentSoftkeyMenu) { 13 | case MAIN_MENU: 14 | drawSoftKey(0, "STORE", true); 15 | drawSoftKey(1, "STACK", true); 16 | drawSoftKey(4, "INFO", true); 17 | break; 18 | case INFO: 19 | //drawSoftKey(4, "EXIT", true); 20 | break; 21 | case STORE_MENU: 22 | if (!storageModeWaitingOnInput) { 23 | drawSoftKey(0, "READ", false); 24 | drawSoftKey(1, "WRITE", false); 25 | drawSoftKey(3, "PREV", true); 26 | drawSoftKey(4, "NEXT", true); 27 | } 28 | break; 29 | case STACK_MENU: 30 | if (!storageModeWaitingOnInput) { 31 | drawSoftKey(0, "PULL", false); 32 | 33 | if (stackPointer < 15) { 34 | drawSoftKey(1, "PUSH", false); 35 | } 36 | 37 | drawSoftKey(2, "CLEAR", false); 38 | 39 | if (stackPointer > 3) { 40 | drawSoftKey(3, "PREV", true); 41 | drawSoftKey(4, "NEXT", true); 42 | } 43 | } 44 | break; 45 | } 46 | } 47 | } 48 | 49 | 50 | /** 51 | * Decide what to do depending on the current menu and softkey that is pressed 52 | */ 53 | void pressedSoftkey(char key) { 54 | switch (currentSoftkeyMenu) { 55 | case MAIN_MENU: 56 | switch (key) { 57 | case '!': 58 | currentSoftkeyMenu = STORE_MENU; 59 | currentMode = STORE_SCREEN; 60 | break; 61 | case '@': 62 | currentSoftkeyMenu = WORD_SIZE; 63 | currentSoftkeyMenu = STACK_MENU; 64 | currentMode = STACK_SCREEN; 65 | break; 66 | case '%': 67 | currentSoftkeyMenu = INFO; 68 | currentMode = INFO_SCREEN; 69 | break; 70 | } 71 | break; 72 | case INFO: 73 | /*switch (key) { 74 | 75 | }*/ 76 | break; 77 | case STORE_MENU: 78 | switch (key) { 79 | case '!': 80 | // Read handler 81 | storageOperation = 0; 82 | storageModeWaitingOnInput = true; 83 | break; 84 | case '@': 85 | // Write handler 86 | storageOperation = 1; 87 | storageModeWaitingOnInput = true; 88 | break; 89 | case '$': 90 | storeModePrevPage(); 91 | break; 92 | case '%': 93 | storeModeNextPage(); 94 | break; 95 | } 96 | break; 97 | case STACK_MENU: 98 | switch (key) { 99 | case '!': 100 | // Pull handler 101 | stackModePull(); 102 | break; 103 | case '@': 104 | // Push handler 105 | stackModePush(); 106 | break; 107 | case '#': 108 | stackModeClear(); 109 | break; 110 | case '$': 111 | stackModePrevPage(); 112 | break; 113 | case '%': 114 | stackModeNextPage(); 115 | break; 116 | } 117 | break; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /Stack_Mode.ino: -------------------------------------------------------------------------------- 1 | int currentStackPage = 1; 2 | const int stackBase = 32; 3 | int stackPageCount = 1; 4 | 5 | void stackModeInputHandler(char key) { 6 | if (key == 'D') { // Set decimal display 7 | setBase(10); 8 | } else if (key == 'H') { // Set hexadecimal display 9 | setBase(16); 10 | } else if (key == 'O') { // Set octal display 11 | setBase(8); 12 | } else if (key == 'B') { // Set binary display 13 | setBase(2); 14 | } 15 | } 16 | 17 | 18 | void drawStackScreen() { 19 | stackPageCount = 1; 20 | if (stackPointer >= 12) { 21 | stackPageCount = 4; 22 | } else if (stackPointer >= 8) { 23 | stackPageCount = 3; 24 | } else if (stackPointer >= 4) { 25 | stackPageCount = 2; 26 | } 27 | 28 | 29 | String statusString = "STACK - PAGE "; 30 | statusString += currentStackPage; 31 | statusString += "/" + String(stackPageCount); 32 | 33 | drawString(1, 6, statusString, 0, false); 34 | 35 | // Status bar's horizontal line 36 | u8g2.setDrawColor(1); 37 | u8g2.drawHLine(0, 8, 128); 38 | 39 | int y = 21; // Y coord of each line to show 40 | int stackAddress = ((currentStackPage - 1) * 4) + stackBase; 41 | 42 | 43 | 44 | for (int i = 0; i < 4; i++) { 45 | String stackLine; 46 | 47 | int stackItem; 48 | if (currentStackPage == 1) { 49 | stackItem = stackPointer - i; 50 | } else { 51 | //stackItem = (stackPointer - ((stackPageCount - currentStackPage) * 4)) + i; 52 | stackItem = stackPointer - ((currentStackPage - 1) * 4) - i; 53 | } 54 | 55 | if (stackItem < 0) { 56 | break; 57 | } 58 | 59 | stackLine += String(stackItem, HEX); 60 | stackLine += ": "; 61 | 62 | 63 | int stackVal; 64 | //EEPROM.get((stackAddress + stackBase) * 2, stackVal); 65 | EEPROM.get((stackItem * 2) + stackBase, stackVal); 66 | 67 | switch (base) { 68 | case 10: 69 | stackLine += String(stackVal, DEC); 70 | break; 71 | case 16: 72 | stackLine += String(stackVal, HEX); 73 | break; 74 | case 8: 75 | stackLine += String(stackVal, OCT); 76 | break; 77 | case 2: 78 | stackLine += String(stackVal, BIN); 79 | break; 80 | } 81 | 82 | stackLine.toUpperCase(); 83 | 84 | drawString(1, y, stackLine, 1, false); 85 | 86 | y += 10; 87 | } 88 | } 89 | 90 | 91 | void stackModePull() { 92 | if (stackPointer >= 0) { 93 | currentStackPage = 1; 94 | EEPROM.get((stackPointer * 2) + stackBase, X); 95 | 96 | //if (stackPointer > 0) { 97 | stackPointer--; 98 | EEPROM.put(64, stackPointer); 99 | //} 100 | } 101 | } 102 | 103 | void stackModePush() { 104 | if (stackPointer < 15) { 105 | currentStackPage = 1; 106 | stackPointer++; 107 | EEPROM.put((stackPointer * 2) + stackBase, X); 108 | EEPROM.put(64, stackPointer); 109 | } 110 | } 111 | 112 | 113 | void stackModeClear() { 114 | stackPointer = -1; 115 | EEPROM.put(64, stackPointer); 116 | EEPROM.put(stackBase, 0); 117 | currentStackPage = 1; 118 | stackPageCount = 1; 119 | } 120 | 121 | 122 | void stackModePrevPage() { 123 | currentStackPage--; 124 | 125 | if (currentStackPage < 1) { 126 | currentStackPage = stackPageCount; 127 | } 128 | } 129 | 130 | 131 | void stackModeNextPage() { 132 | currentStackPage++; 133 | 134 | if (currentStackPage > stackPageCount) { 135 | currentStackPage = 1; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /Store_Mode.ino: -------------------------------------------------------------------------------- 1 | int currentStoragePage = 1; 2 | 3 | void storeModeInputHandler(char key) { 4 | int inputNumber; 5 | 6 | if (storageModeWaitingOnInput) { 7 | if ( 8 | ((key >= 48) && (key <= 57)) || // numbers 0 to 9 in ASCII 9 | ((key >= 97) && (key <= 102)) // characters a to f 10 | ) { 11 | storageModeWaitingOnInput = false; 12 | 13 | inputNumber = asciiToInt(key); 14 | 15 | switch (storageOperation) { 16 | case 0: // 0 = read 17 | EEPROM.get(inputNumber * 2, X); 18 | currentMode = REGISTER_DISPLAY; 19 | currentSoftkeyMenu = MAIN_MENU; 20 | break; 21 | case 1: // 1 = write 22 | EEPROM.put(inputNumber * 2, X); 23 | break; 24 | } 25 | } 26 | } else if (key == 'D') { // Set decimal display 27 | setBase(10); 28 | } else if (key == 'H') { // Set hexadecimal display 29 | setBase(16); 30 | } else if (key == 'O') { // Set octal display 31 | setBase(8); 32 | } else if (key == 'B') { // Set binary display 33 | setBase(2); 34 | } 35 | } 36 | 37 | 38 | 39 | void drawStoreScreen() { 40 | String statusString = "STORAGE - PAGE "; 41 | statusString += currentStoragePage; 42 | statusString += "/4"; 43 | 44 | drawString(1, 6, statusString, 0, false); 45 | 46 | // Status bar's horizontal line 47 | u8g2.setDrawColor(1); 48 | u8g2.drawHLine(0, 8, 128); 49 | 50 | int y = 21; // Y coord of each line to show 51 | int storeAddress = (currentStoragePage - 1) * 4; 52 | 53 | for (int i = 0; i < 4; i++) { 54 | String storageLine; 55 | storageLine += String(storeAddress + i, HEX); 56 | storageLine += ": "; 57 | 58 | int storageVal; 59 | EEPROM.get((storeAddress + i) * 2, storageVal); 60 | 61 | switch (base) { 62 | case 10: 63 | storageLine += String(storageVal, DEC); 64 | break; 65 | case 16: 66 | storageLine += String(storageVal, HEX); 67 | break; 68 | case 8: 69 | storageLine += String(storageVal, OCT); 70 | break; 71 | case 2: 72 | storageLine += String(storageVal, BIN); 73 | break; 74 | } 75 | 76 | storageLine.toUpperCase(); 77 | 78 | drawString(1, y, storageLine, 1, false); 79 | 80 | y += 10; 81 | } 82 | 83 | 84 | 85 | // Show input bar 86 | if (storageModeWaitingOnInput) { 87 | switch (storageOperation) { 88 | case 0: // 0 = read 89 | drawString(1, 63, "READ FROM (0-F): ", 0, false); 90 | break; 91 | case 1: // 1 = write 92 | drawString(1, 63, "WRITE TO (0-F): ", 0, false); 93 | break; 94 | } 95 | 96 | } 97 | } 98 | 99 | 100 | 101 | void storeModePrevPage() { 102 | currentStoragePage--; 103 | 104 | if (currentStoragePage < 1) { 105 | currentStoragePage = 4; 106 | } 107 | } 108 | 109 | 110 | void storeModeNextPage() { 111 | currentStoragePage++; 112 | 113 | if (currentStoragePage > 4) { 114 | currentStoragePage = 1; 115 | } 116 | } 117 | --------------------------------------------------------------------------------