diff options
author | Juan J. Martinez <jjm@usebox.net> | 2023-05-01 21:44:35 +0100 |
---|---|---|
committer | Juan J. Martinez <jjm@usebox.net> | 2023-05-01 21:44:35 +0100 |
commit | ef84fb6fcb45a86fce97acda58606a76a937a1da (patch) | |
tree | a3e049b858724837cc9ce0106e30c9e1c028ddc6 /tr8vm.c | |
parent | c081dfe226c6e92865cbb97d8e9a2ef86d8c6acb (diff) | |
download | tr8vm-ef84fb6fcb45a86fce97acda58606a76a937a1da.tar.gz tr8vm-ef84fb6fcb45a86fce97acda58606a76a937a1da.zip |
Added the VM player using SDL
- Wired basic functionality (fram-buffer, frame interrupt)
- Bug fixes in the assembler
Diffstat (limited to 'tr8vm.c')
-rw-r--r-- | tr8vm.c | 255 |
1 files changed, 255 insertions, 0 deletions
@@ -0,0 +1,255 @@ +/* + * TR8 VM Player + * Copyright (C) 2023 by Juan J. Martinez <jjm@usebox.net> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> + +#include "SDL.h" + +#include "vm.h" + +#define ARGB(r, g, b) ((uint32_t)(((r)<<16)|((g)<<8)|(b))) + +#define APP_NAME "TR8 VM Player" +#define APP_VERSION "0.1-alpha" + +#define TR8_W 128 +#define TR8_H 128 + +#define WINDOW_SCALE 4 +#define WINDOW_W (TR8_W * WINDOW_SCALE) +#define WINDOW_H (TR8_H * WINDOW_SCALE) + +static void resize_full_screen(SDL_Window *win, SDL_Renderer *renderer, int fullscreen, SDL_Rect *s) +{ + if (!fullscreen) + { + SDL_SetWindowFullscreen(win, 0); + s->x = 0; + s->y = 0; + s->w = WINDOW_W; + s->h = WINDOW_H; + } + else + { + SDL_SetWindowFullscreen(win, SDL_WINDOW_FULLSCREEN_DESKTOP); + + int sw, sh, w, h, scale; + + SDL_GetRendererOutputSize(renderer, &w, &h); + + sw = w / TR8_W; + sh = h / TR8_H; + scale = sw > sh ? sh : sw; + + s->x = (w - (TR8_W * scale)) / 2; + s->y = (h - (TR8_H * scale)) / 2; + s->w = TR8_W * scale; + s->h = TR8_H * scale; + } +} + +static uint32_t palette[] = +{ + ARGB(0x00, 0x00, 0x00), + ARGB(0x00, 0x00, 0xaa), + ARGB(0x00, 0xaa, 0x00), + ARGB(0x00, 0xaa, 0xaa), + ARGB(0xaa, 0x00, 0x00), + ARGB(0xaa, 0x00, 0xaa), + ARGB(0xaa, 0x55, 0x00), + ARGB(0xaa, 0xaa, 0xaa), + ARGB(0x55, 0x55, 0x55), + ARGB(0x55, 0x55, 0xff), + ARGB(0x55, 0xff, 0x55), + ARGB(0x55, 0xff, 0xff), + ARGB(0xff, 0x55, 0x55), + ARGB(0xff, 0x55, 0xff), + ARGB(0xff, 0xff, 0x55), + ARGB(0xff, 0xff, 0xff), +}; + +uint8_t ram[UINT16_MAX + 1] = { 0 }; +uint32_t *fb_data = NULL; + +void write_m(uint16_t addr, uint8_t b) +{ + if (addr >= VIDEO_RAM && addr < (VIDEO_RAM + 16384)) + fb_data[addr - VIDEO_RAM] = palette[b & 15]; + + ram[addr] = b; +} + +uint8_t read_m(uint16_t addr) +{ + return ram[addr]; +} + +int main(int argc, char *argv[]) +{ + FILE *fd; + size_t size; + + if (argc < 2) + { + fprintf(stderr, "Usage: input.prg\n"); + return 1; + } + fd = fopen(argv[1], "rb"); + if (!fd) + { + fclose(fd); + fprintf(stderr, "Error opening input\n"); + return 1; + } + fseek(fd, 0, SEEK_END); + size = ftell(fd); + if (size > UINT16_MAX + 1) + { + fclose(fd); + fprintf(stderr, "Input is too large\n"); + return 1; + } + fseek(fd, 0, SEEK_SET); + if (fread(ram, 1, size, fd) != size) + { + fclose(fd); + fprintf(stderr, "Error reading input\n"); + return 1; + } + fclose(fd); + + SDL_Window *screen = SDL_CreateWindow(APP_NAME " " APP_VERSION, + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + WINDOW_W, WINDOW_H, + SDL_WINDOW_OPENGL); + if (!screen) + { + fprintf(stderr, "Failed to create a window: %s\n", SDL_GetError()); + return 1; + } + + SDL_Renderer *renderer = SDL_CreateRenderer(screen, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED); + if (!renderer) + { + fprintf(stderr, "Failed to create the renderer: %s\n", SDL_GetError()); + return 1; + } + SDL_SetHint("SDL_HINT_RENDER_SCALE_QUALITY", "0"); + + SDL_Texture *canvas = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, TR8_W, TR8_H); + if (!canvas) + { + fprintf(stderr, "Failed to create the canvas: %s\n", SDL_GetError()); + return 1; + } + + SDL_Texture *fb = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, TR8_W, TR8_H); + if (!fb) + { + fprintf(stderr, "Failed to create the frame-buffer: %s\n", SDL_GetError()); + return 1; + } + SDL_SetTextureBlendMode(fb, SDL_BLENDMODE_NONE); + + int fullscreen = 0; + SDL_Rect dst; + resize_full_screen(screen, renderer, fullscreen, &dst); + + SDL_RenderClear(renderer); + SDL_RenderPresent(renderer); + + Tr8 vm; + tr8_init(&vm, write_m, read_m); + int pitch = 0; + uint8_t rc; + + SDL_Event ev; + while (1) + { + if (SDL_PollEvent(&ev)) + { + if (ev.type == SDL_QUIT) + break; + + if (ev.type == SDL_KEYDOWN) + { + if (ev.key.keysym.sym == SDLK_ESCAPE) + { + if (fullscreen) + resize_full_screen(screen, renderer, 0, &dst); + break; + } + if (ev.key.keysym.sym == SDLK_RETURN && (SDL_GetModState() & KMOD_LALT)) + { + fullscreen ^= 1; + resize_full_screen(screen, renderer, fullscreen, &dst); + continue; + } + } + } + + pitch = 0; + fb_data = NULL; + SDL_LockTexture(fb, NULL, (void **)&fb_data, &pitch); + rc = tr8_eval(&vm); + SDL_UnlockTexture(fb); + + if (!rc) + break; + + /* render to the canvas */ + SDL_SetRenderTarget(renderer, canvas); + SDL_RenderClear(renderer); + + SDL_RenderCopy(renderer, fb, NULL, NULL); + + /* now target the screen */ + SDL_SetRenderTarget(renderer, NULL); + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, canvas, NULL, &dst); + + SDL_RenderPresent(renderer); + + pitch = 0; + fb_data = NULL; + SDL_LockTexture(fb, NULL, (void **)&fb_data, &pitch); + rc = tr8_frame_int(&vm); + SDL_UnlockTexture(fb); + + if (!rc) + break; + } + + if (canvas) + SDL_DestroyTexture(canvas); + if (fb) + SDL_DestroyTexture(fb); + + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(screen); + + return 0; +} |