From 3c3dbd217507655597d86c87825c5a20f82e80f3 Mon Sep 17 00:00:00 2001 From: Koray Yanik Date: Fri, 13 Oct 2023 20:46:05 +0100 Subject: [PATCH] Starting work on PPU -> display -> VGA --- build/tb_top.Makefile | 9 +- dpi/vgasim.c | 113 +++++++++++++++++++++++ rtl/display.sv | 115 +++++++++++++++++++++++ rtl/gb.sv | 19 +++- rtl/ppu/ppu.sv | 18 +++- sim/tb_top.sv | 90 ++++++++++++++++-- sim/vgasim.sv | 44 +++++++++ src/vgasim | Bin 0 -> 16944 bytes src/vgasim.c | 210 ++++++++++++++++++++++++++++++++++++++++++ synthflow | 2 +- 10 files changed, 600 insertions(+), 20 deletions(-) create mode 100644 dpi/vgasim.c create mode 100644 rtl/display.sv create mode 100644 sim/vgasim.sv create mode 100755 src/vgasim create mode 100644 src/vgasim.c diff --git a/build/tb_top.Makefile b/build/tb_top.Makefile index 6e73ff0..a470ac5 100644 --- a/build/tb_top.Makefile +++ b/build/tb_top.Makefile @@ -1,7 +1,12 @@ TB = tb_top -SOURCES = gb.sv cpu.sv ppu.sv idec.sv ctrl.sv alu.sv alu16.sv regbank.sv rom.sv ram.sv cart.sv tb_top.sv clkgen.sv +SOURCES = gb.sv cpu.sv ppu.sv idec.sv ctrl.sv alu.sv alu16.sv regbank.sv rom.sv ram.sv cart.sv display.sv tb_top.sv vgasim.sv clkgen.sv INCLUDES = cpu_pkg.svh sva_common.svh -PATH_SRC = ../rtl:../rtl/cpu:../rtl/ppu:../sim + +DPI_SOURCES = vgasim.c +DPI_CFLAGS = +DPI_LIBS = + +PATH_SRC = ../rtl:../rtl/cpu:../rtl/ppu:../sim:../dpi DEFINES = SVA_ENABLE diff --git a/dpi/vgasim.c b/dpi/vgasim.c new file mode 100644 index 0000000..f229c4b --- /dev/null +++ b/dpi/vgasim.c @@ -0,0 +1,113 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +#define WBUF_SIZE 32 + +static int fifo_fd; + +#define VGASIM_CMD_INIT 0x01 +#define VGASIM_CMD_RESET 0x02 +#define VGASIM_CMD_TICK 0x03 + +int vgasim_init(int screen_h, int screen_w, int screen_bpp) { + int ret; + ssize_t bytes_written, bytes_written_total; + ssize_t pkt_size; + char wbuf[WBUF_SIZE]; + uint16_t *wbuf_ptr; + printf("dpi: vgasim_init: %d x %d x %d\n", screen_h, screen_w, screen_bpp); + + ret = open("/tmp/vgasim", O_WRONLY | O_NONBLOCK); + if (ret == -1) { + printf("dpi: vgasim_init: failed (%d)\n", errno); + return 1; + } + + fifo_fd = ret; + + wbuf_ptr = (uint16_t*)wbuf; + + *wbuf_ptr++ = VGASIM_CMD_INIT; + *wbuf_ptr++ = (uint16_t)screen_h; + *wbuf_ptr++ = (uint16_t)screen_w; + *wbuf_ptr++ = (uint16_t)screen_bpp; + + pkt_size = 4 * sizeof(uint16_t); + + bytes_written_total = 0; + do { + bytes_written = write(fifo_fd, wbuf + bytes_written_total, pkt_size - bytes_written_total); + if (bytes_written < 0) { + printf("dpi: vgasim_init: write error (%d): %s\n", errno, strerror(errno)); + return 2; + } + bytes_written_total += bytes_written; + } while (bytes_written_total < pkt_size); + + return 0; +} + +int vgasim_reset() { + int ret; + ssize_t bytes_written, bytes_written_total; + ssize_t pkt_size; + char wbuf[WBUF_SIZE]; + uint16_t *wbuf_ptr; + + printf("dpi: vgasim_reset\n"); + + wbuf_ptr = (uint16_t*)wbuf; + *wbuf_ptr++ = VGASIM_CMD_RESET; + pkt_size = sizeof(uint16_t); + + bytes_written_total = 0; + do { + bytes_written = write(fifo_fd, wbuf + bytes_written_total, pkt_size - bytes_written_total); + if (bytes_written < 0) { + printf("dpi: vgasim_init: write error (%d): %s\n", errno, strerror(errno)); + return 2; + } + bytes_written_total += bytes_written; + } while (bytes_written_total < pkt_size); + + return 0; +} + +int vgasim_tick(int r, int g, int b, int hsync, int vsync) { + int ret; + ssize_t bytes_written, bytes_written_total; + ssize_t pkt_size; + char wbuf[WBUF_SIZE]; + uint16_t *wbuf_ptr16; + uint8_t *wbuf_ptr8; + + //printf("dpi: vgasim_tick: %X, %X, %X (%s%s)\n", r, g, b, hsync ? "h" : "", vsync ? "v" : ""); + + wbuf_ptr16 = (uint16_t*)wbuf; + *wbuf_ptr16++ = VGASIM_CMD_TICK; + wbuf_ptr8 = (uint8_t*)wbuf_ptr16; + *wbuf_ptr8++ = (uint8_t)r; + *wbuf_ptr8++ = (uint8_t)g; + *wbuf_ptr8++ = (uint8_t)b; + *wbuf_ptr8++ = (uint8_t)((vsync ? 0x02 : 0x00) | (hsync ? 0x01 : 0x00)); + pkt_size = sizeof(uint16_t) + 4 * sizeof(uint8_t); + + bytes_written_total = 0; + do { + bytes_written = write(fifo_fd, wbuf + bytes_written_total, pkt_size - bytes_written_total); + if (bytes_written < 0) { + printf("dpi: vgasim_init: write error (%d): %s\n", errno, strerror(errno)); + return 2; + } + bytes_written_total += bytes_written; + } while (bytes_written_total < pkt_size); + + return 0; +} diff --git a/rtl/display.sv b/rtl/display.sv new file mode 100644 index 0000000..f066fda --- /dev/null +++ b/rtl/display.sv @@ -0,0 +1,115 @@ +module display #( + parameter VGA_DEPTH = 8, + parameter VGA_W = 640, + parameter VGA_H = 480 +) ( + input logic nreset, + + input logic display_enable_i, + + input logic ppu_clk, + input logic [ 1:0] ppu_pixel_i, + input logic ppu_vsync_i, + input logic ppu_hsync_i, + + // RGB ppu_pixel + input logic [ 2:0][VGA_DEPTH-1:0] VGA_PALETTE[3:0], + + input logic vga_clk, + output logic [VGA_DEPTH-1:0] vga_r_o, + output logic [VGA_DEPTH-1:0] vga_g_o, + output logic [VGA_DEPTH-1:0] vga_b_o, + output logic vga_hsync_o, + output logic vga_vsync_o +); + +localparam DISPLAY_W = 160; +localparam DISPLAY_H = 144; +localparam BUFFER_SIZE = DISPLAY_W * DISPLAY_H; + +logic [7:0] ppu_px_r; +logic [7:0] ppu_py_r; +logic [7:0] ppu_px_next; +logic [7:0] ppu_py_next; + +logic [9:0] vga_px_r; +logic [9:0] vga_py_r; +logic [9:0] vga_px_next; +logic [9:0] vga_py_next; + +logic vga_vsync; +logic vga_hsync; + +always_ff @(posedge ppu_clk or negedge nreset) begin + if (!nreset) begin + ppu_px_r <= '0; + ppu_py_r <= '0; + end else begin + ppu_px_r <= ppu_px_next; + ppu_py_r <= ppu_py_next; + end +end +assign ppu_px_next = ppu_hsync_i ? '0 : (ppu_px_r + 8'h01); +assign ppu_py_next = ppu_vsync_i ? '0 : + ppu_hsync_i ? (ppu_py_r + 8'h01) : + (ppu_py_r); + +always_ff @(posedge vga_clk or negedge nreset) begin + if (!nreset) begin + vga_px_r <= '0; + vga_py_r <= '0; + end else begin + vga_px_r <= vga_px_next; + vga_py_r <= vga_py_next; + end +end +assign vga_px_next = vga_hsync ? '0 : (vga_px_r + 8'h01); +assign vga_py_next = vga_vsync ? '0 : + vga_hsync ? (vga_py_r + 8'h01) : + (vga_py_r); + +assign vga_hsync = (vga_px_r == VGA_W); +assign vga_vsync = (vga_py_r == VGA_H); + + +logic [1:0] buffer_r[BUFFER_SIZE-1:0]; +logic [15:0] buffer_raddr; +logic [15:0] buffer_waddr; + +logic buffer_we; + +always_ff @(posedge ppu_clk or negedge nreset) begin + if (!nreset) begin + buffer_r[buffer_waddr] <= '0; + end else if (buffer_we) begin + buffer_r[buffer_waddr] <= ppu_pixel_i; + end +end + +assign buffer_we = ~ppu_vsync_i & ~ppu_hsync_i; + +assign buffer_waddr = DISPLAY_W * ppu_py_r + ppu_px_r; + +logic [1:0] buffer_rval; + +always_ff @(posedge vga_clk or negedge nreset) begin + if (!nreset) begin + buffer_rval <= '0; + end else begin + buffer_rval <= buffer_r[buffer_raddr]; + end +end + +assign buffer_raddr = DISPLAY_W * vga_py_r + vga_py_r; + +logic vga_in_display; +assign vga_in_display = (vga_py_r < DISPLAY_H) & (vga_px_r < DISPLAY_W); + +assign vga_r_o = (display_enable_i & vga_in_display) ? VGA_PALETTE[buffer_rval][0] : '0; +assign vga_g_o = (display_enable_i & vga_in_display) ? VGA_PALETTE[buffer_rval][1] : '0; +assign vga_b_o = (display_enable_i & vga_in_display) ? VGA_PALETTE[buffer_rval][2] : '0; + +assign vga_vsync_o = vga_vsync; +assign vga_hsync_o = vga_hsync; + +endmodule : display diff --git a/rtl/gb.sv b/rtl/gb.sv index d1a3542..55d8595 100644 --- a/rtl/gb.sv +++ b/rtl/gb.sv @@ -10,7 +10,13 @@ module gb ( output wire cart_nwr_o, output wire cart_ncs_o, output wire [15:0] cart_addr_o, - inout wire [ 7:0] cart_data_io + inout wire [ 7:0] cart_data_io, + + // PPU output to display + output logic ppu_display_enable_o, + output logic [ 1:0] ppu_pixel_o, + output logic ppu_vsync_o, + output logic ppu_hsync_o ); @@ -24,13 +30,10 @@ logic [ 7:0] cpu_ppu_rdata; logic rom_enable_r; logic rom_sel; logic [ 7:0] rom_rdata; - logic hiram_sel; logic [ 7:0] hiram_rdata; - logic vram_sel; logic [ 7:0] vram_rdata; - logic cart_sel; logic [ 7:0] cart_rdata; @@ -48,10 +51,16 @@ ppu ppu_inst ( .clk (clk), .nreset (nreset), + .display_enable_o(ppu_display_enable_o), + .cpu_addr_i (cpu_addr), .cpu_rdata_o(cpu_ppu_rdata), .cpu_we_i (cpu_we), - .cpu_wdata_i(cpu_wdata) + .cpu_wdata_i(cpu_wdata), + + .pixel_o (ppu_pixel_o), + .vsync_o (ppu_vsync_o), + .hsync_o (ppu_hsync_o) ); assign rom_enable_r = '1; diff --git a/rtl/ppu/ppu.sv b/rtl/ppu/ppu.sv index 8fd0083..57dcd10 100644 --- a/rtl/ppu/ppu.sv +++ b/rtl/ppu/ppu.sv @@ -4,10 +4,16 @@ module ppu ( input logic clk, input logic nreset, + output logic display_enable_o, + input logic [15:0] cpu_addr_i, output logic [ 7:0] cpu_rdata_o, input logic cpu_we_i, - input logic [ 7:0] cpu_wdata_i + input logic [ 7:0] cpu_wdata_i, + + output logic [ 1:0] pixel_o, + output logic vsync_o, + output logic hsync_o ); `define IOREG_DECL(name) \ @@ -24,7 +30,7 @@ module ppu ( name``_r <= name``_next; \ end \ assign name``_sel = (cpu_addr_i == addr); \ - assign name``_we = name``_we & cpu_we_i; + assign name``_we = name``_sel & cpu_we_i; typedef struct packed { logic lcd_en; @@ -93,6 +99,14 @@ assign cpu_rdata_o = `endif {8{ sy_sel}} & sy_r; +assign pixel_o = ((lx_r < 8'd160) & (ly_r < 8'd144)) ? ly_r[1:0] : // should create a nice line-pattern for now + 2'h00; + +assign vsync_o = ly_r >= 8'd144; +assign hsync_o = lx_r >= 8'd160; + +assign display_enable_o = lcdc_r[7]; + `undef IOREG_DEF `undef IOREG_DECL diff --git a/sim/tb_top.sv b/sim/tb_top.sv index 1740881..3afbd8c 100644 --- a/sim/tb_top.sv +++ b/sim/tb_top.sv @@ -1,6 +1,10 @@ module tb_top; -logic clk; +logic gb_clk; +logic vga_clk; + +logic gb_nreset; +logic vga_nreset; logic nreset; wire gb_cart_clk; @@ -11,13 +15,39 @@ wire gb_cart_ncs; wire [15:0] gb_cart_addr; wire [ 7:0] gb_cart_data; -clkgen clkgen_inst ( - .clk (clk), - .nreset(nreset) +wire display_enable; +wire [ 1:0] ppu_pixel; +wire ppu_hsync; +wire ppu_vsync; + +logic [7:0] vga_r; +logic [7:0] vga_g; +logic [7:0] vga_b; +logic vga_hsync; +logic vga_vsync; + +// 4.19MHz +clkgen #( + .PERIOD_NS (238), + .RESET_DELAY_NS (400) +) gb_clkgen_inst ( + .clk (gb_clk), + .nreset(gb_nreset) ); +// 25MHz +clkgen #( + .PERIOD_NS (40), + .RESET_DELAY_NS (70) +) vga_clkgen_inst ( + .clk (vga_clk), + .nreset(vga_nreset) +); + +assign nreset = gb_nreset & vga_nreset; + gb gb_inst ( - .clk (clk), + .clk (gb_clk), .nreset(nreset), .cart_clk_o (gb_cart_clk), @@ -26,7 +56,12 @@ gb gb_inst ( .cart_nwr_o (gb_cart_nwr), .cart_ncs_o (gb_cart_ncs), .cart_addr_o (gb_cart_addr), - .cart_data_io (gb_cart_data) + .cart_data_io (gb_cart_data), + + .ppu_display_enable_o(display_enable), + .ppu_pixel_o (ppu_pixel), + .ppu_vsync_o (ppu_vsync), + .ppu_hsync_o (ppu_hsync) ); cart cart_inst ( @@ -39,10 +74,45 @@ cart cart_inst ( .data_io (gb_cart_data) ); +display display_inst ( + .nreset (nreset), + .VGA_PALETTE( + // RR GG BB + '{24'h00_00_00, // colour 00 + 24'h60_60_60, // colour 01 + 24'hA0_A0_A0, // colour 10 + 24'hFF_FF_FF} // colour 11 + ), + + .display_enable_i(display_enable), + + .ppu_clk (gb_clk), + .ppu_pixel_i(ppu_pixel), + .ppu_vsync_i(ppu_vsync), + .ppu_hsync_i(ppu_hsync), + + .vga_clk (vga_clk), + .vga_r_o (vga_r), + .vga_g_o (vga_g), + .vga_b_o (vga_b), + .vga_hsync_o(vga_hsync), + .vga_vsync_o(vga_vsync) +); + +vgasim vgasim_inst ( + .clk (vga_clk), + .nreset(nreset), + + .r_i (vga_r), + .g_i (vga_g), + .b_i (vga_b), + .hsync_i (vga_hsync), + .vsync_i (vga_vsync) +); `ifdef SVA_ENABLE `include "sva_common.svh" -`SVA_DEF_CLK(clk); +`SVA_DEF_CLK(gb_clk); `SVA_DEF_NRESET(nreset); logic instr_valid; @@ -70,14 +140,14 @@ assign vram_sel = gb_inst.vram_sel; assign hiram_sel = gb_inst.hiram_sel; assign ppu_sel = gb_inst.ppu_inst.sva_ppu_sel; -always_ff @(posedge clk or negedge nreset) begin +always_ff @(posedge gb_clk or negedge nreset) begin if (!nreset) current_pc <= '0; else if (instr_valid) current_pc <= gb_inst.cpu_inst.pc_r; end -always_ff @(posedge clk or negedge nreset) begin +always_ff @(posedge gb_clk or negedge nreset) begin if (!nreset) begin last_write_address <= '0; last_write_value <= '0; @@ -108,7 +178,7 @@ localparam MAX_COUNT = 10000; logic [15:0] pc_history [$]; int unsigned pc_history_count [$]; -always @(posedge clk iff instr_valid) begin +always @(posedge gb_clk iff instr_valid) begin automatic int unsigned current_pc_count = 0; foreach (pc_history[i]) begin diff --git a/sim/vgasim.sv b/sim/vgasim.sv new file mode 100644 index 0000000..be29bcd --- /dev/null +++ b/sim/vgasim.sv @@ -0,0 +1,44 @@ +module vgasim #( + parameter SCREEN_H = 640, + parameter SCREEN_W = 480, + parameter DEPTH = 8 +) ( + input logic clk, + input logic nreset, + + input logic [DEPTH-1:0] r_i, + input logic [DEPTH-1:0] g_i, + input logic [DEPTH-1:0] b_i, + input logic hsync_i, + input logic vsync_i +); + +import "DPI-C" function int vgasim_init(input int screen_h, input int screen_w, input int screen_bpp); +import "DPI-C" function int vgasim_reset(); +import "DPI-C" function int vgasim_tick(input int r, input int g, input int b, input int hsync, input int vsync); + +initial begin + automatic int ret = vgasim_init(SCREEN_W, SCREEN_H, DEPTH); + assert (ret == 0) else begin + $display($sformatf("[%0t] vgasim init failed: %d", $time, ret)); + $fatal(); + end +end + +always @(negedge nreset) begin + automatic int ret = vgasim_reset(); + assert (ret == 0) else begin + $display($sformatf("[%0t] vgasim reset failed: %d", $time, ret)); + $fatal(); + end +end + +always @(posedge clk iff nreset) begin + automatic int ret = vgasim_tick(r_i, g_i, b_i, hsync_i, vsync_i); + assert (ret == 0) else begin + $display($sformatf("[%0t] vgasim tick failed: %d", $time, ret)); + $fatal(); + end +end + +endmodule : vgasim diff --git a/src/vgasim b/src/vgasim new file mode 100755 index 0000000000000000000000000000000000000000..716ea75a273700c35a7ec36e4af7e137c655b5f2 GIT binary patch literal 16944 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 h%2Hih^^!)VO~~c4E={9sDpK^9w + +#include +#include +#include + +#include +#include +#include +#include + + +#define RBUF_SIZE 256 + +#define DEF_SCREEN_W 640 +#define DEF_SCREEN_H 480 + +unsigned int pixel_x; +unsigned int pixel_y; +unsigned int screen_w; +unsigned int screen_h; +SDL_Window* sdl_window; +SDL_Renderer* sdl_renderer; +SDL_Texture* sdl_texture; + +Uint32* screen; + +#define VGASIM_CMD_INIT 0x01 +#define VGASIM_CMD_RESET 0x02 +#define VGASIM_CMD_TICK 0x03 + +void vgasim_init(int new_screen_h, int new_screen_w, int bpp) { + printf("vgasim_init: %d x %d x %d\n", new_screen_w, new_screen_h, bpp); + pixel_x = 0; + pixel_y = 0; + + if (screen_h != new_screen_h || screen_w != new_screen_w) { + printf("Need to resize\n"); + } + + screen_h = new_screen_h; + screen_w = new_screen_w; +} + +void vgasim_reset() { + printf("vgasim_reset\n"); + pixel_x = 0; + pixel_y = 0; + SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255); + SDL_RenderClear(sdl_renderer); + SDL_RenderPresent(sdl_renderer); +} + +void vgasim_tick(int r, int g, int b, int hsync, int vsync) { + //printf("vgasim_tick: %X, %X, %X (%s%s)\n", r, g, b, hsync ? "h" : "", vsync ? "v" : ""); + + screen[screen_w * pixel_y + pixel_x] = ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | ((b & 0xFF) << 0); + pixel_x++; + if (hsync || pixel_x > screen_w) { + pixel_x = 0; + pixel_y++; + //printf("vgasim_tick: vsync\n"); + SDL_UpdateTexture(sdl_texture, NULL, screen, screen_w * sizeof (Uint32)); + SDL_RenderClear(sdl_renderer); + SDL_RenderCopy(sdl_renderer, sdl_texture, NULL, NULL); + SDL_RenderPresent(sdl_renderer); + } + if (vsync || pixel_y > screen_h) { + pixel_y = 0; + // printf("vgasim_tick: hsync\n"); + } +} + +ssize_t cmd_parse(char *buf, ssize_t bufsize) { + uint8_t *p8; + uint16_t *p16; + uint16_t cmd; + uint8_t r, g, b, hvsync; + uint16_t h, w, bpp; + + if (bufsize < sizeof(uint16_t)) + return 0; + + p16 = (uint16_t*)buf; + cmd = *p16++; + p8 = (uint8_t*)p16; + switch (cmd) { + case VGASIM_CMD_INIT: + if (bufsize < 4 * sizeof(uint16_t)) + return 0; + h = *p16++; + w = *p16++; + bpp = *p16++; + vgasim_init(h, w, bpp); + return 4 * sizeof(uint16_t); + + case VGASIM_CMD_RESET: + vgasim_reset(); + return sizeof(uint16_t); + + case VGASIM_CMD_TICK: + if (bufsize < sizeof(uint16_t) + 4 * sizeof(uint8_t)) + return 0; + r = *p8++; + g = *p8++; + b = *p8++; + hvsync = *p8++; + vgasim_tick(r, g, b, (hvsync & 0x01), ((hvsync & 0x02) >> 1)); + + return sizeof(uint16_t) + 4 * sizeof(uint8_t); + + default: + printf("Unsupported command packet received: %X\n", cmd); + return 0; + } + return 0; +} + +int main (int argc, char **argv) { + int fifo_fd; + char rbuf[RBUF_SIZE]; + ssize_t bytes_read, bytes_read_total; + ssize_t bytes_parsed, bytes_parsed_total; + int ret = mkfifo("/tmp/vgasim", S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + + if (ret == -1 && errno != EEXIST) { + perror(argv[0]); + return 1; + } + + ret = SDL_Init(SDL_INIT_VIDEO); + if (ret < 0) { + printf("SDL_Init failed: %d\n", ret); + return 2; + } + + sdl_window = SDL_CreateWindow("vgasim", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, DEF_SCREEN_W, DEF_SCREEN_H, 0); + if (sdl_window == NULL) { + printf("SDL_CreateWindow failed\n"); + return 3; + } + sdl_renderer = SDL_CreateRenderer(sdl_window, -1, 0); + if (sdl_renderer == NULL) { + printf("SDL_CreateRenderer failed\n"); + return 4; + } + sdl_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, DEF_SCREEN_W, DEF_SCREEN_H); + if (sdl_texture == NULL) { + printf("SDL_CreateTexture failed\n"); + return 5; + } + SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255); + SDL_RenderClear(sdl_renderer); + SDL_RenderPresent(sdl_renderer); + + screen = malloc(DEF_SCREEN_H * DEF_SCREEN_W * sizeof(Uint32)); + if (screen == NULL) { + return 6; + } + + printf("Listening for incoming connections...\n"); + ret = open("/tmp/vgasim", O_RDONLY); + if (ret == -1) { + perror(argv[0]); + return 2; + } + + fifo_fd = ret; + + bytes_read_total = 0; + do { + + // Read as much bytes as we can into our buffer + bytes_read = read(fifo_fd, rbuf + bytes_read_total, RBUF_SIZE - bytes_read_total); + bytes_read_total += bytes_read; + + bytes_parsed_total = 0; + if (bytes_read > 0) { + do { + + // Parse as many bytes as we can + bytes_parsed = cmd_parse(rbuf + bytes_parsed_total, bytes_read_total - bytes_parsed_total); + bytes_parsed_total += bytes_parsed; + } while (bytes_parsed > 0); + } + + // If there's any data left unparsed, it's the start of a new incomplete packet. + // Copy it to the front of the buffer and restart from there. + // Otherwise we cleared our entire buffer and can start again from the beginning. + if (bytes_read_total > bytes_parsed_total) { + memcpy(rbuf, rbuf + bytes_parsed_total, (bytes_read_total - bytes_parsed_total)); + bytes_read_total = bytes_read_total - bytes_parsed_total; + } else { + bytes_read_total = 0; + } + } while (bytes_read > 0); + + if (bytes_read < 0) { + perror(argv[0]); + return 3; + } + + free(screen); + SDL_DestroyTexture(sdl_texture); + SDL_DestroyRenderer(sdl_renderer); + SDL_DestroyWindow(sdl_window); + SDL_Quit(); + + return 0; +} diff --git a/synthflow b/synthflow index 1486728..cc61998 160000 --- a/synthflow +++ b/synthflow @@ -1 +1 @@ -Subproject commit 1486728ced51acd63f20caf41cc47b44f2efa436 +Subproject commit cc619981f0804ce1da54a12d67ff4009d2df56b9