diff --git a/build.sh b/build.sh index ca8b9a6..40e143b 100755 --- a/build.sh +++ b/build.sh @@ -15,7 +15,7 @@ clang-format -i emulator.c clang-format -i uxn.h clang-format -i uxn.c rm -f ./uxn -cc -std=c89 -DDEBUG -Wall -Wno-unknown-pragmas -Wpedantic -Wshadow -Wextra -Werror=implicit-int -Werror=incompatible-pointer-types -Werror=int-conversion -Wvla -g -Og -fsanitize=address -fsanitize=undefined uxn.c emulator.c -o bin/emulator +cc -std=c89 -DDEBUG -Wall -Wno-unknown-pragmas -Wpedantic -Wshadow -Wextra -Werror=implicit-int -Werror=incompatible-pointer-types -Werror=int-conversion -Wvla -g -Og -fsanitize=address -fsanitize=undefined uxn.c emulator.c -L/usr/local/lib -lSDL2 -o bin/emulator # run ./bin/emulator bin/boot.rom diff --git a/emulator.c b/emulator.c index 355e19c..653eb12 100644 --- a/emulator.c +++ b/emulator.c @@ -1,5 +1,5 @@ +#include #include -#include "uxn.h" /* Copyright (c) 2021 Devine Lu Linvega @@ -12,6 +12,90 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE. */ +#include "uxn.h" + +#define HOR 32 +#define VER 16 +#define PAD 2 +#define SZ (HOR * VER * 16) + +typedef unsigned char Uint8; + +int WIDTH = 8 * HOR + 8 * PAD * 2; +int HEIGHT = 8 * (VER + 2) + 8 * PAD * 2; +int FPS = 30, GUIDES = 1, BIGPIXEL = 0, ZOOM = 2; + +Uint32 theme[] = { + 0x000000, + 0xFFFFFF, + 0x72DEC2, + 0x666666, + 0x222222}; + +SDL_Window *gWindow; +SDL_Renderer *gRenderer; +SDL_Texture *gTexture; +Uint32 *pixels; + +int +error(char *msg, const char *err) +{ + printf("Error %s: %s\n", msg, err); + return 0; +} + +void +clear(Uint32 *dst) +{ + int v, h; + for(v = 0; v < HEIGHT; v++) + for(h = 0; h < WIDTH; h++) + dst[v * WIDTH + h] = theme[0]; +} + +void +redraw(Uint32 *dst) +{ + SDL_UpdateTexture(gTexture, NULL, dst, WIDTH * sizeof(Uint32)); + SDL_RenderClear(gRenderer); + SDL_RenderCopy(gRenderer, gTexture, NULL, NULL); + SDL_RenderPresent(gRenderer); +} + +void +quit(void) +{ + free(pixels); + SDL_DestroyTexture(gTexture); + gTexture = NULL; + SDL_DestroyRenderer(gRenderer); + gRenderer = NULL; + SDL_DestroyWindow(gWindow); + gWindow = NULL; + SDL_Quit(); + exit(0); +} + +int +init(void) +{ + if(SDL_Init(SDL_INIT_VIDEO) < 0) + return error("Init", SDL_GetError()); + gWindow = SDL_CreateWindow("Uxn", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WIDTH * ZOOM, HEIGHT * ZOOM, SDL_WINDOW_SHOWN); + if(gWindow == NULL) + return error("Window", SDL_GetError()); + gRenderer = SDL_CreateRenderer(gWindow, -1, 0); + if(gRenderer == NULL) + return error("Renderer", SDL_GetError()); + gTexture = SDL_CreateTexture(gRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, WIDTH, HEIGHT); + if(gTexture == NULL) + return error("Texture", SDL_GetError()); + if(!(pixels = (Uint32 *)malloc(WIDTH * HEIGHT * sizeof(Uint32)))) + return error("Pixels", "Failed to allocate memory"); + clear(pixels); + return 1; +} + void echos(Stack8 *s, Uint8 len, char *name) { @@ -49,21 +133,68 @@ echof(Uxn *c) getflag(&c->status, FLAG_COND) != 0); } -int -main(int argc, char *argv[]) +void +domouse(SDL_Event *event) { - Uxn cpu; + (void)event; + printf("mouse\n"); +} + +void +dokey(SDL_Event *event) +{ + (void)event; + printf("key\n"); +} + +int +start(Uxn *u) +{ + int ticknext = 0; + evaluxn(u, u->vreset); + while(1) { + int tick = SDL_GetTicks(); + SDL_Event event; + if(tick < ticknext) + SDL_Delay(ticknext - tick); + ticknext = tick + (1000 / FPS); + evaluxn(u, u->vframe); + while(SDL_PollEvent(&event) != 0) { + switch(event.type) { + case SDL_QUIT: quit(); break; + case SDL_MOUSEBUTTONUP: + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEMOTION: domouse(&event); break; + case SDL_KEYDOWN: dokey(&event); break; + case SDL_WINDOWEVENT: + if(event.window.event == SDL_WINDOWEVENT_EXPOSED) + redraw(pixels); + break; + } + } + } +} + +int +main(int argc, char **argv) +{ + Uxn u; if(argc < 2) - return error(&cpu, "No input.", 0); - if(!load(&cpu, argv[1])) - return error(&cpu, "Load error", 0); - if(!boot(&cpu)) - return error(&cpu, "Boot error", 0); + return error("Input", "Missing"); + if(!bootuxn(&u)) + return error("Boot", "Failed"); + if(!loaduxn(&u, argv[1])) + return error("Load", "Failed"); + if(!init()) + return error("Init", "Failed"); - /* print result */ - echos(&cpu.wst, 0x40, "stack"); - echom(&cpu.ram, 0x40, "ram"); - echof(&cpu); + start(&u); + + echos(&u.wst, 0x40, "stack"); + echom(&u.ram, 0x40, "ram"); + echof(&u); + + quit(); return 0; } diff --git a/uxn.c b/uxn.c index 533a68f..253afcb 100644 --- a/uxn.c +++ b/uxn.c @@ -97,24 +97,44 @@ Uint8 opr[][2] = { /* clang-format on */ int -error(Uxn *u, char *name, int id) +haltuxn(Uxn *u, char *name, int id) { printf("Error: %s#%04x, at 0x%04x\n", name, id, u->counter); return 0; } +void +inituxn(Uxn *u) +{ + size_t i; + char *cptr = (char *)u; + for(i = 0; i < sizeof u; i++) + cptr[i] = 0; +} + int -doliteral(Uxn *u, Uint8 instr) +loaduxn(Uxn *u, char *filepath) +{ + FILE *f; + if(!(f = fopen(filepath, "rb"))) + return haltuxn(u, "Missing input.", 0); + fread(u->ram.dat, sizeof(u->ram.dat), 1, f); + printf("Uxn Loaded: %s\n", filepath); + return 1; +} + +int +lituxn(Uxn *u, Uint8 instr) { if(u->wst.ptr >= 255) - return error(u, "Stack overflow", instr); + return haltuxn(u, "Stack overflow", instr); wspush8(u, instr); u->literal--; return 1; } int -dodevices(Uxn *u) /* experimental */ +devuxn(Uxn *u) /* experimental */ { if(u->ram.dat[0xfff1]) { printf("%c", u->ram.dat[0xfff1]); @@ -124,7 +144,7 @@ dodevices(Uxn *u) /* experimental */ } int -doopcode(Uxn *u, Uint8 instr) +opcuxn(Uxn *u, Uint8 instr) { Uint8 op = instr & 0x1f; setflag(&u->status, FLAG_SHORT, (instr >> 5) & 1); @@ -133,61 +153,40 @@ doopcode(Uxn *u, Uint8 instr) if(getflag(&u->status, FLAG_SHORT)) op += 16; if(u->wst.ptr < opr[op][0]) - return error(u, "Stack underflow", op); + return haltuxn(u, "Stack underflow", op); if(u->wst.ptr + opr[op][1] - opr[op][0] >= 255) - return error(u, "Stack overflow", instr); + return haltuxn(u, "Stack overflow", instr); if(!getflag(&u->status, FLAG_COND) || (getflag(&u->status, FLAG_COND) && wspop8(u))) (*ops[op])(u); - dodevices(u); + devuxn(u); return 1; } int -eval(Uxn *u) +evaluxn(Uxn *u, Uint16 vec) { - Uint8 instr = u->ram.dat[u->ram.ptr++]; - if(u->literal > 0) - return doliteral(u, instr); - else - return doopcode(u, instr); + u->ram.ptr = vec; + setflag(&u->status, FLAG_HALT, 0); + while(!(u->status & FLAG_HALT)) { + Uint8 instr = u->ram.dat[u->ram.ptr++]; + u->counter++; + if(u->literal > 0) + return lituxn(u, instr); + else + return opcuxn(u, instr); + } return 1; } int -load(Uxn *u, char *filepath) +bootuxn(Uxn *u) { - FILE *f; - if(!(f = fopen(filepath, "rb"))) - return error(u, "Missing input.", 0); - fread(u->ram.dat, sizeof(u->ram.dat), 1, f); - return 1; -} - -void -reset(Uxn *u) -{ - size_t i; - char *cptr = (char *)u; - for(i = 0; i < sizeof u; i++) - cptr[i] = 0; -} - -int -boot(Uxn *u) -{ - reset(u); + inituxn(u); u->vreset = mempeek16(u, 0xfffa); u->vframe = mempeek16(u, 0xfffc); u->verror = mempeek16(u, 0xfffe); - /* eval reset */ - u->ram.ptr = u->vreset; - setflag(&u->status, FLAG_HALT, 0); - while(!(u->status & FLAG_HALT) && eval(u)) - u->counter++; - /* eval frame */ - u->ram.ptr = u->vframe; - setflag(&u->status, FLAG_HALT, 0); - while(!(u->status & FLAG_HALT) && eval(u)) - u->counter++; + printf("Uxn Ready.\n"); return 1; } + +/* to start: evaluxn(u, u->vreset); */ diff --git a/uxn.h b/uxn.h index a22468c..916b5a2 100644 --- a/uxn.h +++ b/uxn.h @@ -44,6 +44,6 @@ typedef struct { void setflag(Uint8 *status, char flag, int b); int getflag(Uint8 *status, char flag); -int error(Uxn *c, char *name, int id); -int load(Uxn *c, char *filepath); -int boot(Uxn *c); +int loaduxn(Uxn *c, char *filepath); +int bootuxn(Uxn *c); +int evaluxn(Uxn *u, Uint16 vec); \ No newline at end of file