#include #include #include #include "util/font.h" #include "assembler/assembler.h" #include "util/texteditor.h" #include "util/hexdump.h" #include "util/cpustatusui.h" //Screen dimension constants const int SCREEN_WIDTH = 1280; const int SCREEN_HEIGHT = 720; const int targetFPS = 60; const int delayNeeded = 1000 / targetFPS; //The window we'll be rendering to SDL_Window *window = NULL; //The surface contained by the window SDL_Renderer *renderer = NULL; #define biggerFont fonts[0] #define smallFont fonts[1] #define smallerFont fonts[2] #define fontCount 3 BitmapFont fonts[fontCount]; CPU cpu; SDL_Texture *cpuStatsTexture; SDL_Texture *cpuStateTexture; #define codeEditor editors[0] #define memoryViewer editors[1] #define editorCount 2 #define activeEditor editors[activeEditorIndex] int activeEditorIndex = 0; TextEditor editors[editorCount]; unsigned long frames = 0; bool cursor = true; char *read_file_as_string(const char *filename) { FILE *file = fopen(filename, "rb"); // Open file in binary mode if (file == NULL) { perror("Error opening file"); return NULL; } // Seek to the end of the file to determine size fseek(file, 0, SEEK_END); long filesize = ftell(file); rewind(file); // Go back to the beginning // Allocate memory for file content (+1 for null terminator) char *buffer = malloc(filesize + 1); if (buffer == NULL) { perror("Memory allocation failed"); fclose(file); return NULL; } // Read entire file into buffer fread(buffer, 1, filesize, file); buffer[filesize] = '\0'; // Null-terminate the string fclose(file); return buffer; // Caller must free the memory } void updateState(bool full) { renderVals(&cpu, &smallFont, &smallerFont, renderer, &cpuStatsTexture); renderState(&cpu, &biggerFont, renderer, &cpuStateTexture); char *dump = hexdump_to_string(cpu.memory, full ? 0 : memoryViewer.cursor_line_offset * 16, full ? MEM_SIZE : (memoryViewer.cursor_line_offset * 16) + (memoryViewer.displayLineCount * 16)); fill_editor_from_string(&memoryViewer, dump, full ? 0 : memoryViewer.cursor_line_offset, full, renderer); free(dump); } void compile(bool erase) { generate_string(&codeEditor); completePass(codeEditor.outputString, &cpu, erase); updateState(true); } int init() { //Initialize SDL if (SDL_Init(SDL_INIT_VIDEO) < 0) { printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError()); return 1; } //Initialize SDL_ttf if (TTF_Init() == -1) { printf("SDL_ttf could not initialize! SDL_ttf Error: %s\n", TTF_GetError()); return 1; } //Create window window = SDL_CreateWindow("RISC-B simulator", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE); if (window == NULL) { printf("Window could not be created! SDL_Error: %s\n", SDL_GetError()); return 1; } //Get window surface renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); if (renderer == NULL) { printf("Renderer could not be created SDL_Error: %s\n", SDL_GetError()); return 1; } SDL_Rect viewport = {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT}; SDL_RenderSetViewport(renderer, &viewport); SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); biggerFont = prepText(renderer, 16, "PublicPixel.ttf", 255, 255, 255, 255); smallFont = prepText(renderer, 12, "PublicPixel.ttf", 255, 255, 255, 255); smallerFont = prepText(renderer, 8, "PublicPixel.ttf", 255, 255, 255, 255); init_editor(&codeEditor, &smallFont, 10, 80, renderer, 35, 1000, 48, false); init_editor(&memoryViewer, &smallerFont, 738, 80, renderer, 59, MEM_SIZE / 16 + 2, 70, true); SDL_RenderSetLogicalSize(renderer, SCREEN_WIDTH, SCREEN_HEIGHT); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, NULL); for (int i = 0; i < editorCount; i++) { generate_string_display(&codeEditor, renderer); } init_cpu(&cpu); compile(true); return 0; } int render() { SDL_SetRenderDrawColor(renderer, 32, 32, 32, 255); SDL_RenderClear(renderer); for (int i = 0; i < editorCount; i++) { editor_render(&editors[i], renderer, &cpu, i, activeEditorIndex == i, cursor); } SDL_Rect rect2; rect2.x = 9; rect2.y = 0; rect2.w = 0; rect2.h = 0; SDL_QueryTexture(cpuStatsTexture, NULL, NULL, &rect2.w, &rect2.h); SDL_RenderCopy(renderer, cpuStatsTexture, NULL, &rect2); rect2.x = 100; rect2.y = 46; rect2.w = 0; rect2.h = 0; SDL_QueryTexture(cpuStateTexture, NULL, NULL, &rect2.w, &rect2.h); SDL_RenderCopy(renderer, cpuStateTexture, NULL, &rect2); SDL_RenderPresent(renderer); frames++; if (!(frames % 60)) { cursor = !cursor; } return 0; } SDL_Keycode ConvertKPToNonKP(SDL_Keycode keycode) { switch (keycode) { case SDLK_KP_0: return SDLK_0; case SDLK_KP_1: return SDLK_1; case SDLK_KP_2: return SDLK_2; case SDLK_KP_3: return SDLK_3; case SDLK_KP_4: return SDLK_4; case SDLK_KP_5: return SDLK_5; case SDLK_KP_6: return SDLK_6; case SDLK_KP_7: return SDLK_7; case SDLK_KP_8: return SDLK_8; case SDLK_KP_9: return SDLK_9; case SDLK_KP_PERIOD: return SDLK_PERIOD; case SDLK_KP_COMMA: return SDLK_COMMA; case SDLK_KP_DIVIDE: return SDLK_SLASH; case SDLK_KP_MULTIPLY: return SDLK_ASTERISK; case SDLK_KP_MINUS: return SDLK_MINUS; case SDLK_KP_PLUS: return SDLK_PLUS; case SDLK_KP_ENTER: return SDLK_RETURN; case SDLK_KP_EQUALS: return SDLK_EQUALS; case SDLK_KP_LEFTPAREN: return SDLK_LEFTPAREN; case SDLK_KP_RIGHTPAREN: return SDLK_RIGHTPAREN; case SDLK_KP_LEFTBRACE: return SDLK_LEFTBRACKET; case SDLK_KP_RIGHTBRACE: return SDLK_RIGHTBRACKET; case SDLK_KP_TAB: return SDLK_TAB; case SDLK_KP_BACKSPACE: return SDLK_BACKSPACE; case SDLK_KP_A: return SDLK_a; case SDLK_KP_B: return SDLK_b; case SDLK_KP_C: return SDLK_c; case SDLK_KP_D: return SDLK_d; case SDLK_KP_E: return SDLK_e; case SDLK_KP_F: return SDLK_f; case SDLK_KP_XOR: return SDLK_CARET; case SDLK_KP_PERCENT: return SDLK_PERCENT; case SDLK_KP_LESS: return SDLK_LESS; case SDLK_KP_GREATER: return SDLK_GREATER; case SDLK_KP_AMPERSAND: case SDLK_KP_DBLAMPERSAND: return SDLK_AMPERSAND; // No direct match, best alternative case SDLK_KP_VERTICALBAR: case SDLK_KP_DBLVERTICALBAR: return SDLK_BACKSLASH; // No direct match case SDLK_KP_COLON: return SDLK_COLON; case SDLK_KP_HASH: return SDLK_HASH; case SDLK_KP_SPACE: return SDLK_SPACE; case SDLK_KP_AT: return SDLK_AT; case SDLK_KP_EXCLAM: return SDLK_EXCLAIM; case SDLK_KP_MEMADD: return SDLK_PLUS; case SDLK_KP_MEMSUBTRACT: return SDLK_MINUS; case SDLK_KP_MEMMULTIPLY: return SDLK_ASTERISK; case SDLK_KP_MEMDIVIDE: return SDLK_SLASH; case SDLK_KP_MEMSTORE: case SDLK_KP_MEMRECALL: case SDLK_KP_MEMCLEAR: case SDLK_KP_PLUSMINUS: case SDLK_KP_CLEAR: case SDLK_KP_CLEARENTRY: case SDLK_KP_BINARY: case SDLK_KP_OCTAL: case SDLK_KP_DECIMAL: case SDLK_KP_HEXADECIMAL: return SDLK_UNKNOWN; default: return keycode; // If it's not a KP key, return it unchanged } } uint8_t cpuSpeedTemp = 0; int processEvent(SDL_Event e) { if (e.type == SDL_QUIT) { return 0; } else if (e.type == SDL_WINDOWEVENT && e.window.event == SDL_WINDOWEVENT_RESIZED) { int newWidth = e.window.data1; int newHeight = e.window.data2; // Adjust the viewport to match the new window size; SDL_Rect viewport = {0, 0, newWidth, newHeight}; SDL_RenderSetViewport(renderer, &viewport); } else if (e.type == SDL_KEYDOWN) { int keySym = ConvertKPToNonKP(e.key.keysym.sym); int keyMod = e.key.keysym.mod; cursor = true; bool moved = false; switch (keySym) { case SDLK_UP: move_cursor_relative(&activeEditor, -1, 0, false, renderer); moved = true; break; case SDLK_PAGEUP: move_cursor_relative(&activeEditor, -activeEditor.displayLineCount, -1, true, renderer); moved = true; break; case SDLK_DOWN: move_cursor_relative(&activeEditor, 1, 0, false, renderer); moved = true; break; case SDLK_PAGEDOWN: move_cursor_relative(&activeEditor, activeEditor.displayLineCount, 0, true, renderer); moved = true; break; case SDLK_LEFT: move_cursor_relative(&activeEditor, 0, -1, false, renderer); moved = true; break; case SDLK_HOME: if (keyMod & KMOD_CTRL) { move_cursor(&activeEditor, 0, 0, false, renderer); moved = true; break; } move_cursor(&activeEditor, activeEditor.cursor_line, 0, false, renderer); moved = true; break; case SDLK_RIGHT: move_cursor_relative(&activeEditor, 0, 1, false, renderer); moved = true; break; case SDLK_END: int lineLen = strlen(activeEditor.lines[activeEditor.cursor_line].text); if (keyMod & KMOD_CTRL) { move_cursor(&activeEditor, activeEditor.maxLines - 1, lineLen, false, renderer); moved = true; break; } move_cursor(&activeEditor, activeEditor.cursor_line, lineLen, false, renderer); moved = true; break; case SDLK_F9: cpu.mode ^= CPU_MODE_LOOP; updateState(false); return 1; case SDLK_F8: if (++cpuSpeedTemp == 3) { cpuSpeedTemp = 0; } cpu.mode &= ~(CPU_MODE_SECOND | CPU_MODE_STEP); cpu.mode |= cpuSpeedTemp << 4; updateState(false); return 1; case SDLK_F5: compile(!(keyMod & KMOD_CTRL)); case SDLK_F7: if (cpu.mode & (CPU_MODE_HALTED | CPU_MODE_ERROR) || (keyMod & KMOD_SHIFT)) { cpu.pc = 0; } cpu.mode &= ~(CPU_MODE_HALTED | CPU_MODE_PAUSED | CPU_MODE_ERROR); updateState(false); break; case SDLK_ESCAPE: cpu.mode |= CPU_MODE_PAUSED; if (keyMod & (KMOD_CTRL | KMOD_SHIFT)) { cpu.mode |= CPU_MODE_HALTED; } updateState(false); break; case SDLK_s: if (keyMod & KMOD_CTRL) { FILE *fptr; char fname[20]; snprintf(fname, sizeof(fname), "riscb%lu.bsm", time(NULL)); fptr = fopen(fname, "w"); generate_string(&editors[0]); fputs(editors[0].outputString, fptr); fclose(fptr); return 1; } break; case SDLK_l: if (keyMod & KMOD_CTRL) { FILE *fptr; char fname[20]; sscanf(editors[0].lines[editors[0].cursor_line].text, "%s", fname); toLowerCase(fname); strcat(fname, ".bsm"); fptr = fopen(fname, "r"); if (fptr) { char *prog = read_file_as_string(fname); toUpperCase(prog); fill_editor_from_string(&editors[0], prog, 0, true, renderer); free(prog); fclose(fptr); } return 1; } break; case SDLK_BACKSPACE: if (!activeEditor.readOnly) { remove_character(&activeEditor, false, renderer); moved = true; } break; case SDLK_DELETE: if (!activeEditor.readOnly) { remove_character(&activeEditor, true, renderer); moved = true; } break; case SDLK_RETURN: case SDLK_RETURN2: if (keyMod & KMOD_CTRL && activeEditorIndex == 0) { compile(!(keyMod & KMOD_SHIFT)); break; } if (!activeEditor.readOnly) { insert_line_rel(&activeEditor, renderer); moved = true; } break; case SDLK_TAB: activeEditorIndex++; if (activeEditorIndex >= editorCount) { activeEditorIndex = 0; } activeEditor = editors[activeEditorIndex]; break; default: break; } if (moved && &activeEditor == &memoryViewer) { updateState(false); } } else if (e.type == SDL_TEXTINPUT) { for (int i = 0; e.text.text[i] != '\0'; i++) { // Iterate over the input string char keySym = e.text.text[i]; if (!activeEditor.readOnly && activeEditor.cursor_pos <= activeEditor.max_line_width) { if (keySym >= 32 && keySym <= 126) { // Printable ASCII range if (keySym > 0x60 && keySym < 0x7b) { // Convert lowercase to uppercase keySym -= 0x20; } if (activeEditor.cursor_pos < activeEditor.max_line_width) { insert_character(&activeEditor, keySym, renderer); } } } } } return 1; } int main(__attribute__((unused)) int argc, __attribute__((unused)) char *args[]) { int status = init(); if (status) { return status; } //Hack to get window to stay up SDL_Event e; bool running = true; Uint64 start; Uint64 end; while (running) { start = SDL_GetTicks64(); while (SDL_PollEvent(&e)) { running = processEvent(e); } status = render(); if (status) { return status; } if (!(cpu.mode & (CPU_MODE_HALTED | CPU_MODE_PAUSED | CPU_MODE_ERROR))) { if (cpu.mode & CPU_MODE_SECOND) { if (!(frames % 60)) { step(&cpu); updateState(false); } } else { step(&cpu); updateState(false); } if (cpu.mode & CPU_MODE_STEP) { cpu.mode |= CPU_MODE_PAUSED; updateState(false); } } end = SDL_GetTicks64(); const unsigned long timeNeeded = end - start; if (timeNeeded < delayNeeded) { SDL_Delay(delayNeeded - timeNeeded); } else { printf("%lu\n", timeNeeded); } } for (uint8_t i = 0; i < editorCount; i++) { destroy_editor(&editors[i]); } for (uint8_t i = 0; i < fontCount; i++) { destroyFont(&fonts[i]); } puts(SDL_GetError()); if (cpuStatsTexture) SDL_DestroyTexture(cpuStatsTexture); if (cpuStateTexture) SDL_DestroyTexture(cpuStateTexture); if (renderer) SDL_DestroyRenderer(renderer); if (window) SDL_DestroyWindow(window); SDL_Quit(); return 0; }