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 0000000..716ea75 Binary files /dev/null and b/src/vgasim differ diff --git a/src/vgasim.c b/src/vgasim.c new file mode 100644 index 0000000..d2bfca8 --- /dev/null +++ b/src/vgasim.c @@ -0,0 +1,210 @@ +#include + +#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