ultimecia

A ps1 emulator in c
Log | Files | Refs

commit c76c3ad493685bc3f6593ce23f00da5f1ea1cae3
parent 79e0ebcdf3c6f14cb2967dadb3c6f4888b786248
Author: noone <vazkats@gmail.com>
Date:   Tue, 18 Mar 2025 00:13:32 +0200

Long time no see.
- I managed to fix the drawing algorithm and now evertything renders pretty much good
- Something is wrong though with the upper text of the logo. It's not shown. I have to figure out why..

Diffstat:
M.gitignore | 1+
Mmakefile | 3++-
Msrc/defs.h | 4++--
Msrc/gpu.c | 126+++++++++++++++++++++++++++++++++++--------------------------------------------
Msrc/main.c | 89+++++++++++++++++++++++++++++++++++++------------------------------------------
Msrc/sr.c | 280+++++++++++++++++++++++++++----------------------------------------------------
Msrc/sr.h | 3+++
Msrc/tags | 39+++++++++++++++++++++++++++++++++++++--
Msrc/util.c | 50++++++++++++++++++++++++++++++--------------------
Msrc/util.h | 7+++++++
10 files changed, 273 insertions(+), 329 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -2,3 +2,4 @@ bin/ a.out test.c +compile_flags.txt diff --git a/makefile b/makefile @@ -1,5 +1,6 @@ +mac: + cc -g src/*.c -o bin/ultimecia -I/opt/homebrew/include/ -L/opt/homebrew/lib/ -lSDL2 bsd: cc -O0 -g src/*.c -o bin/ultimecia -I/usr/local/include -L/usr/local/lib -lSDL2 - win: cc -O3 -g src/*.c -o bin/ultimecia -I/mingw64/include/SDL2 -L/mingw64/lib -lmingw32 -lSDL2main -lSDL2 diff --git a/src/defs.h b/src/defs.h @@ -7,7 +7,7 @@ static const int debug = 0; // Set to 0 to disable debug prints /* #define PANIC(...) do { fprintf(stderr, __VA_ARGS__); exit(EXIT_FAILURE); } while(0) */ /* We should get rid of this */ -#define DEBUG 1; +#define DEBUG 0; #define LOG_NONE 0 #define LOG_DEBUG 1 @@ -16,7 +16,7 @@ static const int debug = 0; // Set to 0 to disable debug prints #define LOG_ERROR 4 // Set the current log level -#define CURRENT_LOG_LEVEL LOG_DEBUG +#define CURRENT_LOG_LEVEL LOG_ERROR #define LOG(level, fmt, ...) \ do { if (level >= CURRENT_LOG_LEVEL) fprintf(stderr, fmt, __VA_ARGS__); } while (0) diff --git a/src/gpu.c b/src/gpu.c @@ -49,6 +49,9 @@ GPU_status(GPU* gpu) /* Bit 14 not supported */ r |= ((u32)gpu->texture_disable) << 15; r |= HOR_RES_into_status(gpu->hres); + /* If we don't emulate bit 31 correctly setting vres to 1 + locks the BIOS. 4.17 on the guide. + */ //r |= ((u32)gpu->vres) << 19; r |= ((u32)gpu->vmode) << 20; r |= ((u32)gpu->display_depth) << 21; @@ -57,14 +60,14 @@ GPU_status(GPU* gpu) r |= ((u32)gpu->interrupt) << 24; /* Always GPU is ready */ - r |= 1 << 26; + r |= 1 << 26; r |= 1 << 27; r |= 1 << 28; r |= ((u32) gpu->dma_direction) << 29; /* Ignore odd lines for now */ - r |= 0 << 31; + r |= 0 << 31; switch(gpu->dma_direction) { @@ -80,7 +83,7 @@ GPU_status(GPU* gpu) return r; } -void +void GPU_gp1(GPU* gpu, u32 val) { @@ -104,7 +107,7 @@ GPU_gp1(GPU* gpu, u32 val) } -void +void GPU_gp1_reset(GPU* gpu, u32 val) { REN* tmp = gpu->ren; @@ -162,7 +165,7 @@ GPU_gp1_display_vram_start(GPU* gpu, u32 val) gpu->display_vram_y_start = (u16)((val >> 10) & 0x1ff); } -void +void GPU_gp1_dma_direction(GPU* gpu, u32 val) { switch (val & 3) { @@ -174,7 +177,7 @@ GPU_gp1_dma_direction(GPU* gpu, u32 val) } } -void +void GPU_gp1_display_mode(GPU* gpu, u32 val) { u8 hr1, hr2; @@ -192,13 +195,13 @@ GPU_gp1_display_mode(GPU* gpu, u32 val) fprintf(stderr, "Unsupported display mode %08X\n", val), exit(EXIT_FAILURE); } -void +void GPU_gp0(GPU* gpu, u32 val) { if (gpu->gp0_words_remaining == 0) { u32 opcode, len; void (*method)(GPU*); - + opcode = (val >> 24) & 0xff; switch (opcode) { @@ -227,12 +230,12 @@ GPU_gp0(GPU* gpu, u32 val) gpu->gp0_words_remaining -= 1; switch (gpu->gp0_mode) { - case GP0_MODE_COMMAND: + case GP0_MODE_COMMAND: GPU_CMD_BUFFER_push_word(&gpu->gp0_command, val); if (gpu->gp0_words_remaining == 0) gpu->gp0_command_method(gpu); break; - case GP0_MODE_IMAGE_LOAD: + case GP0_MODE_IMAGE_LOAD: if (gpu->gp0_words_remaining == 0) gpu->gp0_mode = GP0_MODE_COMMAND; break; @@ -243,8 +246,8 @@ void GPU_gp0_nop(GPU* gpu) {} void GPU_gp0_clear_cache(GPU* gpu) { /* Not implemented */} -void -GPU_gp0_image_load(GPU* gpu) +void +GPU_gp0_image_load(GPU* gpu) { u32 res, width, height, imgsize; @@ -267,9 +270,6 @@ GPU_gp0_draw_mode(GPU* gpu) gpu->page_base_y = (u8)((val >> 4) & 1); gpu->semi_transparency = (u8)((val >> 5) & 3); - /* Thought i would try ternary but I am not sure if this actually works - gpu->texture_depth = (val >> 7 & 3) ? 0 ? TD_T4BIT ? 1 ? TD_T8BIT ? 2 ? TD_T15BIT : 0 : 0 : 0 : 0 : 0 : 0;*/ - switch ((val >> 7) & 3) { case 0: gpu->texture_depth = TD_T4BIT; break; @@ -318,7 +318,7 @@ GPU_gp0_drawing_offset(GPU* gpu) gpu->drawing_x_offset = ((i16)(x << 5)) >> 5; gpu->drawing_y_offset = ((i16)(y << 5)) >> 5; - /* + /* This is a hack because I haven't implemented gpu timings yet. But gp0_drawing_offset is called every frame apparently. */ @@ -347,32 +347,23 @@ GPU_gp0_mask_bit_setting(GPU* gpu) gpu->preserve_masked_pixels = (val & 2) != 0; } -void +void GPU_gp0_quad_mono_opaque(GPU* gpu) { - // LOG(LOG_DEBUG, "Draw quad pls\n", NULL); - // ivec2 positions[4]; - // C colors[4]; - - // positions[0] = POSITION_from_gp0(gpu->gp0_command.buffer[1]); - // positions[1] = POSITION_from_gp0(gpu->gp0_command.buffer[2]); - // positions[2] = POSITION_from_gp0(gpu->gp0_command.buffer[3]); - // positions[3] = POSITION_from_gp0(gpu->gp0_command.buffer[4]); - // LOG(LOG_DEBUG, "Vertex 0, x: %d, y: %d\n", positions[0].x, positions[0].y); - // LOG(LOG_DEBUG, "Vertex 1, x: %d, y: %d\n", positions[1].x, positions[1].y); - // LOG(LOG_DEBUG, "Vertex 2, x: %d, y: %d\n", positions[2].x, positions[2].y); - // LOG(LOG_DEBUG, "Vertex 3, x: %d, y: %d\n", positions[3].x, positions[3].y); - - // colors[0] = colors[1] = colors[2] = colors[3] = COLOR_from_gp0(gpu->gp0_command.buffer[0]); - // LOG(LOG_DEBUG, "Color 0, r: %d, g: %d, b: %d\n", colors[0].r, colors[0].g, colors[0].b); - // //LOG(LOG_DEBUG, "Color 1, x: %d, y: %d\n", positions[1].x, positions[1].y); - // //LOG(LOG_DEBUG, "Color 2, x: %d, y: %d\n", positions[2].x, positions[2].y); - // //LOG(LOG_DEBUG, "Color 3, x: %d, y: %d\n", positions[3].x, positions[3].y); - - // REN_push_quad(gpu->ren, positions, colors); + ivec2 positions[4]; + C colors[4]; + + positions[0] = POSITION_from_gp0(gpu->gp0_command.buffer[1]); + positions[1] = POSITION_from_gp0(gpu->gp0_command.buffer[2]); + positions[2] = POSITION_from_gp0(gpu->gp0_command.buffer[3]); + positions[3] = POSITION_from_gp0(gpu->gp0_command.buffer[4]); + + colors[0] = colors[1] = colors[2] = colors[3] = COLOR_from_gp0(gpu->gp0_command.buffer[0]); + + REN_push_quad(gpu->ren, positions, colors); } -void +void GPU_gp0_triangle_shaded_opaque(GPU* gpu) { ivec2 positions[3]; @@ -391,49 +382,42 @@ GPU_gp0_triangle_shaded_opaque(GPU* gpu) LOG(LOG_DEBUG, "Draw triangle shaded\n", NULL); } -void +void GPU_gp0_quad_texture_blend_opaque(GPU* gpu) { - //ivec2 positions[4]; - //C colors[4]; + ivec2 positions[4]; + C colors[4]; - //positions[0] = POSITION_from_gp0(gpu->gp0_command.buffer[1]); - //positions[1] = POSITION_from_gp0(gpu->gp0_command.buffer[3]); - //positions[2] = POSITION_from_gp0(gpu->gp0_command.buffer[5]); - //positions[3] = POSITION_from_gp0(gpu->gp0_command.buffer[7]); + positions[0] = POSITION_from_gp0(gpu->gp0_command.buffer[1]); + positions[1] = POSITION_from_gp0(gpu->gp0_command.buffer[3]); + positions[2] = POSITION_from_gp0(gpu->gp0_command.buffer[5]); + positions[3] = POSITION_from_gp0(gpu->gp0_command.buffer[7]); - //colors[0] =(C) {0x00, 0x00, 0x80};//COLOR_from_gp0(gpu->gp0_command.buffer[0]); - //colors[1] =(C) {0x00, 0x00, 0x80};//COLOR_from_gp0(gpu->gp0_command.buffer[2]); - //colors[2] =(C) {0x00, 0x00, 0x80};//COLOR_from_gp0(gpu->gp0_command.buffer[4]); - //colors[3] =(C) {0x00, 0x00, 0x80};//COLOR_from_gp0(gpu->gp0_command.buffer[6]); + colors[0] = colors[1] = colors[2] = colors[3] = (C){0x00, 0x00, 0x80}; - //REN_push_quad(gpu->ren, positions, colors); - //LOG(LOG_DEBUG, "Draw quad texture blending\n", NULL); + REN_push_quad(gpu->ren, positions, colors); } -void +void GPU_gp0_quad_shaded_opaque(GPU* gpu) { - //LOG(LOG_DEBUG, "Draw quad pls\n", NULL); - //ivec2 positions[4]; - //C colors[4]; + ivec2 positions[4]; + C colors[4]; - //positions[0] = POSITION_from_gp0(gpu->gp0_command.buffer[1]); - //positions[1] = POSITION_from_gp0(gpu->gp0_command.buffer[3]); - //positions[2] = POSITION_from_gp0(gpu->gp0_command.buffer[5]); - //positions[3] = POSITION_from_gp0(gpu->gp0_command.buffer[7]); - - //colors[0] = COLOR_from_gp0(gpu->gp0_command.buffer[0]); - //colors[1] = COLOR_from_gp0(gpu->gp0_command.buffer[2]); - //colors[2] = COLOR_from_gp0(gpu->gp0_command.buffer[4]); - //colors[3] = COLOR_from_gp0(gpu->gp0_command.buffer[6]); + positions[0] = POSITION_from_gp0(gpu->gp0_command.buffer[1]); + positions[1] = POSITION_from_gp0(gpu->gp0_command.buffer[3]); + positions[2] = POSITION_from_gp0(gpu->gp0_command.buffer[5]); + positions[3] = POSITION_from_gp0(gpu->gp0_command.buffer[7]); - //REN_push_quad(gpu->ren, positions, colors); + colors[0] = COLOR_from_gp0(gpu->gp0_command.buffer[0]); + colors[1] = COLOR_from_gp0(gpu->gp0_command.buffer[2]); + colors[2] = COLOR_from_gp0(gpu->gp0_command.buffer[4]); + colors[3] = COLOR_from_gp0(gpu->gp0_command.buffer[6]); - //LOG(LOG_DEBUG, "Draw quad shaded\n", NULL); + REN_push_quad(gpu->ren, positions, colors); } -void +void GPU_gp0_image_store(GPU* gpu) { u32 res, width, height; @@ -456,11 +440,11 @@ GPU_CMD_BUFFER_new(void) return buffer; } -void -GPU_CMD_BUFFER_clear(GPU_CMD_BUFFER* cmd_buffer) -{ +void +GPU_CMD_BUFFER_clear(GPU_CMD_BUFFER* cmd_buffer) +{ cmd_buffer->len = 0; - /*memset(cmd_buffer->buffer, 413, 12 * sizeof(u32));*/ + memset(cmd_buffer->buffer, 413, 12 * sizeof(u32)); } void diff --git a/src/main.c b/src/main.c @@ -1,14 +1,10 @@ -#include <stdio.h> -#include <string.h> #include <stdlib.h> #include <SDL2/SDL.h> -#include "types.h" #include "cpu.h" #include "interconnect.h" #include "bios.h" #include "mem.h" -#include "defs.h" #include "gpu.h" #include "sr.h" @@ -17,51 +13,50 @@ SDL_Event ev; int main(int argc, char **argv) { - int c; - REN *ren; - CPU *cpu; - Interconnect *inter; - - SDL_Init(SDL_INIT_VIDEO); - inter = new_interconnect(); - cpu = new_cpu(inter); - ren = inter->gpu->ren; - SDL_SetRenderDrawColor(ren->renderer, 0xff, 0xff, 0xff, 0xff); - SDL_RenderClear(ren->renderer); - SDL_RenderPresent(ren->renderer); - - while(1) { - /* Because it's too slow to run events every instr */ - for (c = 0; c < 1e5; c++) - CPU_run_next_instruction(cpu); - - while(SDL_PollEvent(&ev) != 0) { - switch(ev.type) { - case SDL_QUIT: - SDL_Quit(); - exit(1); - case SDL_KEYDOWN: - if (ev.key.keysym.sym == SDLK_q) { - SDL_Quit(); - exit(1); - } else if (ev.key.keysym.sym == SDLK_a) { - SDL_Log("A pressed"); - break; - } - } + int c; + REN *ren; + CPU *cpu; + Interconnect *inter; + + SDL_Init(SDL_INIT_VIDEO); + inter = new_interconnect(); + cpu = new_cpu(inter); + ren = inter->gpu->ren; + SDL_SetRenderDrawColor(ren->renderer, 0xff, 0xff, 0xff, 0xff); + SDL_RenderClear(ren->renderer); + SDL_RenderPresent(ren->renderer); + + while(1) { + /* Because it's too slow to run events every instr */ + for (c = 0; c < 1e5; c++) CPU_run_next_instruction(cpu); + + while(SDL_PollEvent(&ev) != 0) { + switch(ev.type) { + case SDL_QUIT: + SDL_Quit(); + exit(1); + case SDL_KEYDOWN: + if (ev.key.keysym.sym == SDLK_q) { + SDL_Quit(); + exit(1); + } else if (ev.key.keysym.sym == SDLK_a) { + SDL_Log("A pressed"); + break; + } + } + } } - } - free(inter->bios->data); - free(inter->bios); - free(inter->ram->data); - free(inter->ram); - free(inter->dma); - free(inter->gpu); - free(inter); - free(cpu); + free(inter->bios->data); + free(inter->bios); + free(inter->ram->data); + free(inter->ram); + free(inter->dma); + free(inter->gpu); + free(inter); + free(cpu); - SDL_Quit(); + SDL_Quit(); - return 0; + return 0; } diff --git a/src/sr.c b/src/sr.c @@ -1,17 +1,14 @@ /* Based software renderer in one file */ -#include <stdint.h> #include <stdio.h> #include <string.h> #include <stdlib.h> -#include <math.h> #include <SDL2/SDL.h> #include "types.h" #include "sr.h" #include "defs.h" -#include "shaders.h" - +#include "util.h" ivec2 POSITION_from_gp0(u32 val) @@ -53,7 +50,7 @@ FB_flip_vert(u32 *data) } ivec2 -IVEC2_op(ivec2 a, ivec2 b, enum mop mop) +IVEC2_op(ivec2 a, ivec2 b, enum mop mop) { ivec2 c; switch(mop) { @@ -76,7 +73,7 @@ IVEC2_op(ivec2 a, ivec2 b, enum mop mop) } ivec2 -IVEC2_ops(ivec2 a, i32 b, enum mop mop) +IVEC2_ops(ivec2 a, i32 b, enum mop mop) { ivec2 c; switch(mop) { @@ -99,35 +96,35 @@ IVEC2_ops(ivec2 a, i32 b, enum mop mop) } -C -C_new(u32 b) -{ +C +C_new(u32 b) +{ C c; - c.r = (u8)(b & 0xff); - c.g = (u8)((b >> 8) & 0xff); - c.b = (u8)((b >> 16) & 0xff); + c.r = (u8)(b & 0xff); + c.g = (u8)((b >> 8) & 0xff); + c.b = (u8)((b >> 16) & 0xff); return c; }; void REN_FB_set(REN* ren, i32 x, i32 y, C c) { (!ren->fb || x<0 || y<0 || x>=W || y>=H) ? 0 : memcpy(ren->fb + ((x + y * W)), &c, 3); } -//C* -//FB_get(i32 x, i32 y) +//C* +//FB_get(i32 x, i32 y) //{ // void* c = (!fb.data || x<0 || y<0 || x>=W || y>=H) ? (void*)0 : (void*)(C_new(fb.data[(x+y*W)])); // return (C*)c; //} REN* -REN_new() +REN_new() { REN* ren; ren = (REN*)malloc(sizeof(REN)); - ren->window = SDL_CreateWindow("Ultimecia", 2560 / 2 , 1440 / 2, WIN_W, WIN_H, SDL_WINDOW_SHOWN); + ren->window = SDL_CreateWindow("Ultimecia", 400 , 300, WIN_W, WIN_H, SDL_WINDOW_SHOWN); ren->renderer = SDL_CreateRenderer(ren->window, -1, 0); ren->tex = SDL_CreateTexture(ren->renderer, SDL_PIXELFORMAT_RGB888, SDL_TEXTUREACCESS_STREAMING, W, H); - ren->verts = NULL; - ren->colors = NULL; + ren->verts = (ivec2*)malloc(sizeof(ivec2) * VERTEX_BUFFER_LEN); + ren->colors = (C*)malloc(sizeof(C) * VERTEX_BUFFER_LEN); ren->verts = (ivec2*)malloc(sizeof(ivec2) * 10000); ren->colors = (C*)malloc(sizeof(C) * 10000); ren->fb = (u32*)malloc(W*H*sizeof(u32)); @@ -136,173 +133,82 @@ REN_new() return ren; } -void REN_triangle(REN* ren, ivec2 verts[3], C colors[3]) +// Draw a scanline with color interpolation +void +draw_scanline(REN* ren, int y, int x1, C c1, int x2, C c2) { - // Sort vertices by y-coordinate - ivec2 t0, t1, t2; - C c0, c1, c2; - - t0 = verts[0], t1 = verts[1], t2 = verts[2]; - c0 = colors[0], c1 = colors[1], c2 = colors[2]; - - if (t0.y > t1.y) { VEC2I_SWAP(t0, t1); C_SWAP(c0, c1); } - if (t0.y > t2.y) { VEC2I_SWAP(t0, t2); C_SWAP(c0, c2); } - if (t1.y > t2.y) { VEC2I_SWAP(t1, t2); C_SWAP(c1, c2); } - - int total_height = t2.y - t0.y; - if (total_height == 0) return; // Degenerate triangle - - for (int y = t0.y; y <= t2.y; y++) { - int is_upper = (y < t1.y || t1.y == t0.y); - - // Alpha (long edge) - float alpha = (float)(y - t0.y) / (float)(t2.y - t0.y); - int Ax = t0.x + (int)((t2.x - t0.x) * alpha); - C colorA = { - (u8)(c0.r + (c2.r - c0.r) * alpha), - (u8)(c0.g + (c2.g - c0.g) * alpha), - (u8)(c0.b + (c2.b - c0.b) * alpha) - }; - - // Beta (short edge) - float beta = is_upper - ? (float)(y - t0.y) / (float)(t1.y - t0.y) - : (float)(y - t1.y) / (float)(t2.y - t1.y); - int Bx = is_upper - ? t0.x + (int)((t1.x - t0.x) * beta) - : t1.x + (int)((t2.x - t1.x) * beta); - - C colorB = is_upper - ? (C){ - (u8)(c0.r + (c1.r - c0.r) * beta), - (u8)(c0.g + (c1.g - c0.g) * beta), - (u8)(c0.b + (c1.b - c0.b) * beta) - } - : (C){ - (u8)(c1.r + (c2.r - c1.r) * beta), - (u8)(c1.g + (c2.g - c1.g) * beta), - (u8)(c1.b + (c2.b - c1.b) * beta) - }; - - // Ensure left-to-right order - if (Ax > Bx) { - int temp_x = Ax; Ax = Bx; Bx = temp_x; - C temp_c = colorA; colorA = colorB; colorB = temp_c; - } - - // Draw scanline - for (int x = Ax; x <= Bx; x++) { - float t = (float)(x - Ax) / (float)(Bx - Ax + 1e-6f); // Avoid divide by zero - C color = { - (u8)(colorA.r + (colorB.r - colorA.r) * t), - (u8)(colorA.g + (colorB.g - colorA.g) * t), - (u8)(colorA.b + (colorB.b - colorA.b) * t) - }; - - REN_FB_set(ren, x, y, color); - } - } + if (x1 > x2) { + swap_int(&x1, &x2); + swap_color(&c1, &c2); + } + + int dx = x2 - x1; + if (dx == 0) return; + + float dr = (c2.r - c1.r) / (float)dx; + float dg = (c2.g - c1.g) / (float)dx; + float db = (c2.b - c1.b) / (float)dx; + + float r = c1.r, g = c1.g, b = c1.b; + for (int x = x1; x <= x2; x++) { + REN_FB_set(ren, x, y, (C){r,g,b}); + r += dr; g += dg; b += db; + } } -//void REN_triangle(REN* ren, ivec2 verts[3], C colors[3]) -//{ -// // Sort vertices by y-coordinate -// ivec2 t0, t1, t2; -// C c0, c1, c2; -// -// t0 = verts[0], t1 = verts[1], t2 = verts[2]; -// c0 = colors[0], c1 = colors[1], c2 = colors[2]; -// -// if (t0.y > t1.y) { VEC2I_SWAP(t0, t1); C_SWAP(c0, c1); } -// if (t0.y > t2.y) { VEC2I_SWAP(t0, t2); C_SWAP(c0, c2); } -// if (t1.y > t2.y) { VEC2I_SWAP(t1, t2); C_SWAP(c1, c2); } -// -// int inv_total_height = t2.y - t0.y; -// //if (inv_total_height == 0) return; -// -// for (int y = t0.y; y <= t2.y; y++) { -// int is_upper = y < t1.y || t1.y == t0.y; -// -// int alpha = (y - t0.y) * inv_total_height; -// -// // Interpolate along the long edge (t0 → t2) -// int Ax = t0.x + (t2.x - t0.x) * alpha; -// C colorA = { -// (u8)(c0.r + (c2.r - c0.r) * alpha), -// (u8)(c0.g + (c2.g - c0.g) * alpha), -// (u8)(c0.b + (c2.b - c0.b) * alpha) -// }; -// -// -// // Interpolate along the short edge (t0 → t1 or t1 → t2) -// int beta = is_upper -// ? (y - t0.y) / (t1.y - t0.y) -// : (y - t1.y) / (t2.y - t1.y); -// -// int Bx = is_upper -// ? t0.x + (t1.x - t0.x) * beta -// : t1.x + (t2.x - t1.x) * beta; -// -// //if (Bx < 0) Bx = 0; // Clamp to minimum screen boundary -// //if (Bx > W) Bx = W; // Clamp to maximum boundary -// -// -// C colorB = is_upper -// ? (C){ -// (u8)(c0.r + (c1.r - c0.r) * beta), -// (u8)(c0.g + (c1.g - c0.g) * beta), -// (u8)(c0.b + (c1.b - c0.b) * beta) -// } -// : (C){ -// (u8)(c1.r + (c2.r - c1.r) * beta), -// (u8)(c1.g + (c2.g - c1.g) * beta), -// (u8)(c1.b + (c2.b - c1.b) * beta) -// }; -// -// // C colorB = (C){ c0.r + (c1.r - c0.r) * beta, c0.g + (c1.g - c0.g) * beta, c0.b + (c1.b - c0.b) * beta }; -// -// // Ensure left-to-right order -// if (Ax > Bx) { -// int temp_x = Ax; Ax = Bx; Bx = temp_x; -// C temp_c = colorA; colorA = colorB; colorB = temp_c; -// LOG(LOG_DEBUG, "IM IN %d \n", Ax); -// } -// -// // Draw the scanline -// for (int x = Ax; x <= Bx; x++) { -// int t = (x - Ax) / (Bx - Ax + 1e-6f); // Prevent divide-by-zero -// C color = { -// (u8)(colorA.r + (colorB.r - colorA.r) * t), -// (u8)(colorA.g + (colorB.g - colorA.g) * t), -// (u8)(colorA.b + (colorB.b - colorA.b) * t) -// }; -// -// REN_FB_set(ren, x, y, color); -// } -// } -//} +// Rasterize triangle with color interpolation +void +REN_triangle(REN* ren, ivec2 verts[3], C colors[3]) +{ + float dx01, dx02, dx12, dr01, dg01, db01, dr02, dg02, db02, dr12, dg12, db12, xL, xR, rL, rR, gL, gR, bL, bR; + + /* Sort vertices by y-coordinate to ensure we process from top to bottom */ + if (verts[0].y > verts[1].y) { swap_vec2(&verts[0], &verts[1]); swap_color(&colors[0], &colors[1]); } + if (verts[0].y > verts[2].y) { swap_vec2(&verts[0], &verts[2]); swap_color(&colors[0], &colors[2]); } + if (verts[1].y > verts[2].y) { swap_vec2(&verts[1], &verts[2]); swap_color(&colors[1], &colors[2]); } + + // Calculate edge slopes + dx01 = (verts[1].x - verts[0].x) / (float)(verts[1].y - verts[0].y + 1); + dx02 = (verts[2].x - verts[0].x) / (float)(verts[2].y - verts[0].y + 1); + dx12 = (verts[2].x - verts[1].x) / (float)(verts[2].y - verts[1].y + 1); + + dr01 = (colors[1].r - colors[0].r) / (float)(verts[1].y - verts[0].y + 1); + dg01 = (colors[1].g - colors[0].g) / (float)(verts[1].y - verts[0].y + 1); + db01 = (colors[1].b - colors[0].b) / (float)(verts[1].y - verts[0].y + 1); + + dr02 = (colors[2].r - colors[0].r) / (float)(verts[2].y - verts[0].y + 1); + dg02 = (colors[2].g - colors[0].g) / (float)(verts[2].y - verts[0].y + 1); + db02 = (colors[2].b - colors[0].b) / (float)(verts[2].y - verts[0].y + 1); + + dr12 = (colors[2].r - colors[1].r) / (float)(verts[2].y - verts[1].y + 1); + dg12 = (colors[2].g - colors[1].g) / (float)(verts[2].y - verts[1].y + 1); + db12 = (colors[2].b - colors[1].b) / (float)(verts[2].y - verts[1].y + 1); + + // Rasterize top part + xL = verts[0].x, rL = colors[0].r, gL = colors[0].g, bL = colors[0].b; + xR = verts[0].x, rR = colors[0].r, gR = colors[0].g, bR = colors[0].b; + + for (int y = verts[0].y; y < verts[1].y; y++) { + draw_scanline(ren, y, (int)xL, (C){rL, gL, bL}, (int)xR, (C){rR, gR, bR}); + xL += dx01; rL += dr01; gL += dg01; bL += db01; + xR += dx02; rR += dr02; gR += dg02; bR += db02; + } + + // Rasterize bottom part + xL = verts[1].x, rL = colors[1].r, gL = colors[1].g, bL = colors[1].b; + for (int y = verts[1].y; y < verts[2].y; y++) { + draw_scanline(ren, y, (int)xL, (C){rL, gL, bL}, (int)xR, (C){rR, gR, bR}); + xL += dx12; rL += dr12; gL += dg12; bL += db12; + xR += dx02; rR += dr02; gR += dg02; bR += db02; + } +} void REN_push_triangle(REN* ren, ivec2 verts[3], C colors[3]) { u8 i; - - if (ren->nvertices > 9) - { - LOG(LOG_DEBUG, "Vertex attribute buffer full push_triangle, forcing draw... and NVERTICES IS %d\n", ren->nvertices); - for(int i = 0; i < ren->nvertices-3; i+=3) - REN_triangle(ren, ren->verts+i, ren->colors+i); - ren->nvertices=0; - } - - //if (ren->nvertices + 3 >= ren->capacity) { - // ren->capacity *= 2; // Double the capacity - // ren->verts = (ivec2*)realloc(ren->verts, sizeof(ivec2) * ren->capacity); - // ren->colors = (C*)realloc(ren->colors, sizeof(C) * ren->capacity); - //} - //ren->verts = (ivec2*)realloc(ren->verts, sizeof(ivec2) * (ren->nvertices + 3)); - //ren->colors = (C*)realloc(ren->colors, sizeof(C) * (ren->nvertices + 3)); + REN_flush(ren); for (i = 0; i < 3; i++) { ren->verts[ren->nvertices] = verts[i]; @@ -315,17 +221,8 @@ void REN_push_quad(REN* ren, ivec2 verts[4], C colors[4]) { u8 i; - - if (ren->nvertices > 9) - { - LOG(LOG_DEBUG, "Vertex attribute buffer full push_squad, forcing draw... and NVERTICES IS %d\n", ren->nvertices); - for(int i = 0; i < ren->nvertices-3;i+=3) - REN_triangle(ren, ren->verts+i, ren->colors+i); - ren->nvertices=0; - } - //ren->verts = (ivec2*)realloc(ren->verts, sizeof(ivec2) * (ren->nvertices + 4)); - //ren->colors = (C*)realloc(ren->colors, sizeof(C) * (ren->nvertices + 4)); + REN_flush(ren); for (i = 0; i < 3; i++) { ren->verts[ren->nvertices] = verts[i]; @@ -338,6 +235,17 @@ REN_push_quad(REN* ren, ivec2 verts[4], C colors[4]) ren->colors[ren->nvertices] = colors[i]; ren->nvertices++; } + int a; +} + +void +REN_flush(REN* ren) { + u32 i; + if (ren->nvertices > 0) { // Always draw whatever is in the buffer + for (i = 0; i < ren->nvertices; i += 3) + REN_triangle(ren, ren->verts + i, ren->colors + i); + ren->nvertices = 0; // Reset buffer for next frame + } } void diff --git a/src/sr.h b/src/sr.h @@ -1,6 +1,7 @@ #pragma once #include <SDL2/SDL.h> +#include "types.h" #define W 640 #define H 480 @@ -42,3 +43,5 @@ void REN_push_triangle(REN*, ivec2[3], C[3]); void REN_push_quad(REN*, ivec2[4], C[4]); void REN_draw(REN* ren); void REN_display(REN* ren); +void REN_flush(REN* ren); +void draw_scanline(REN*, int, int, C, int, C); diff --git a/src/tags b/src/tags @@ -2,7 +2,8 @@ BIOS bios.h /^struct BIOS {$/ BIOS_load32 bios.c /^BIOS_load32(BIOS* b, u32 offset)$/ BIOS_load8 bios.c /^u8 BIOS_load8(BIOS* b, u32 offset) { return b->dat/ BIOS_new bios.c /^BIOS_new(const char* path)$/ -Bool types.h /^typedef uint8_t Bool;$/ +Bool tags /^Bool types.h \/^typedef uint8_t Bool;$\/$/ +C sr.h /^typedef struct {u8 b;u8 g;u8 r;} C;$/ CHANNEL_active mem.c /^CHANNEL_active(Channel* ch)$/ CHANNEL_base mem.c /^CHANNEL_base(Channel* ch)$/ CHANNEL_block_control mem.c /^CHANNEL_block_control(Channel* ch)$/ @@ -13,6 +14,7 @@ CHANNEL_set_base mem.c /^CHANNEL_set_base(Channel* ch, u32 val)$/ CHANNEL_set_block_control mem.c /^CHANNEL_set_block_control(Channel* ch, u32 val)$/ CHANNEL_set_control mem.c /^CHANNEL_set_control(Channel* ch, u32 val)$/ CHANNEL_transfer_size mem.c /^CHANNEL_transfer_size(Channel* ch, u32 *res)$/ +COLOR_from_gp0 sr.c /^COLOR_from_gp0(u32 val)$/ CPU cpu.h /^} CPU;$/ CPU_decode_and_execute cpu.c /^CPU_decode_and_execute(CPU* cpu, instruction* i)$/ CPU_load16 cpu.c /^u16 CPU_load16(CPU* cpu, u32 addr) { return INTER_/ @@ -22,6 +24,8 @@ CPU_run_next_instruction cpu.c /^CPU_run_next_instruction(CPU* cpu)$/ CPU_store16 cpu.c /^void CPU_store16(CPU* cpu, u32 addr, u16 val) { IN/ CPU_store32 cpu.c /^void CPU_store32(CPU* cpu, u32 addr, u32 val) { IN/ CPU_store8 cpu.c /^void CPU_store8(CPU* cpu, u32 addr, u8 val) { IN/ +C_SWAP sr.h /^#define C_SWAP(x, y) { C temp = x; x = y; y = temp/ +C_new sr.c /^C_new(u32 b)$/ Channel mem.h /^} Channel;$/ DISPLAY_DEPTH gpu.h /^} DISPLAY_DEPTH;$/ DMA mem.h /^} DMA;$/ @@ -34,27 +38,39 @@ DMA_set_control mem.c /^DMA_set_control(DMA* dma, u32 val)$/ DMA_set_interrupt mem.c /^DMA_set_interrupt(DMA* dma, u32 val)$/ Direction mem.h /^} Direction;$/ EXCEPTION cpu.h /^} EXCEPTION;$/ +FB_flip_vert sr.c /^FB_flip_vert(u32 *data)$/ FIELD gpu.h /^} FIELD;$/ +GP0_MODE gpu.h /^} GP0_MODE;$/ GPU gpu.h /^} GPU;$/ GPU_CMD_BUFFER gpu.h /^} GPU_CMD_BUFFER;$/ -GPU_CMD_BUFFER_clear gpu.c /^void GPU_CMD_BUFFER_clear(GPU_CMD_BUFFER* cmd_buff/ +GPU_CMD_BUFFER_clear gpu.c /^GPU_CMD_BUFFER_clear(GPU_CMD_BUFFER* cmd_buffer) $/ GPU_CMD_BUFFER_new gpu.c /^GPU_CMD_BUFFER_new(void)$/ GPU_CMD_BUFFER_push_word gpu.c /^GPU_CMD_BUFFER_push_word(GPU_CMD_BUFFER* cmd_buffe/ GPU_gp0 gpu.c /^GPU_gp0(GPU* gpu, u32 val)$/ +GPU_gp0_clear_cache gpu.c /^void GPU_gp0_clear_cache(GPU* gpu) { \/* Not imple/ GPU_gp0_draw_mode gpu.c /^GPU_gp0_draw_mode(GPU* gpu)$/ GPU_gp0_drawing_area_bottom_right gpu.c /^GPU_gp0_drawing_area_bottom_right(GPU* gpu)$/ GPU_gp0_drawing_area_top_left gpu.c /^GPU_gp0_drawing_area_top_left(GPU* gpu)$/ GPU_gp0_drawing_offset gpu.c /^GPU_gp0_drawing_offset(GPU* gpu)$/ +GPU_gp0_image_load gpu.c /^GPU_gp0_image_load(GPU* gpu) $/ +GPU_gp0_image_store gpu.c /^GPU_gp0_image_store(GPU* gpu)$/ GPU_gp0_mask_bit_setting gpu.c /^GPU_gp0_mask_bit_setting(GPU* gpu)$/ GPU_gp0_nop gpu.c /^void GPU_gp0_nop(GPU* gpu) {}$/ +GPU_gp0_quad_mono_opaque gpu.c /^GPU_gp0_quad_mono_opaque(GPU* gpu)$/ +GPU_gp0_quad_shaded_opaque gpu.c /^GPU_gp0_quad_shaded_opaque(GPU* gpu)$/ +GPU_gp0_quad_texture_blend_opaque gpu.c /^GPU_gp0_quad_texture_blend_opaque(GPU* gpu)$/ GPU_gp0_texture_window gpu.c /^GPU_gp0_texture_window(GPU* gpu)$/ +GPU_gp0_triangle_shaded_opaque gpu.c /^GPU_gp0_triangle_shaded_opaque(GPU* gpu)$/ GPU_gp1 gpu.c /^GPU_gp1(GPU* gpu, u32 val)$/ +GPU_gp1_acknowledge_irq gpu.c /^GPU_gp1_acknowledge_irq(GPU* gpu, u32 val)$/ +GPU_gp1_display_enable gpu.c /^GPU_gp1_display_enable(GPU* gpu, u32 val)$/ GPU_gp1_display_horizontal_range gpu.c /^GPU_gp1_display_horizontal_range(GPU* gpu, u32 val/ GPU_gp1_display_mode gpu.c /^GPU_gp1_display_mode(GPU* gpu, u32 val)$/ GPU_gp1_display_vertical_range gpu.c /^GPU_gp1_display_vertical_range(GPU* gpu, u32 val)$/ GPU_gp1_display_vram_start gpu.c /^GPU_gp1_display_vram_start(GPU* gpu, u32 val)$/ GPU_gp1_dma_direction gpu.c /^GPU_gp1_dma_direction(GPU* gpu, u32 val)$/ GPU_gp1_reset gpu.c /^GPU_gp1_reset(GPU* gpu, u32 val)$/ +GPU_gp1_reset_command_buffer gpu.c /^GPU_gp1_reset_command_buffer(GPU* gpu, u32 val)$/ GPU_new gpu.c /^GPU_new(void)$/ GPU_read gpu.c /^GPU_read(GPU* gpu)$/ GPU_status gpu.c /^GPU_status(GPU* gpu)$/ @@ -71,10 +87,15 @@ INTER_set_dma_reg interconnect.c /^INTER_set_dma_reg(Interconnect* inter, u32 of INTER_store16 interconnect.c /^INTER_store16(Interconnect* inter, u32 addr, u16 v/ INTER_store32 interconnect.c /^INTER_store32(Interconnect* inter, u32 addr, u32 v/ INTER_store8 interconnect.c /^INTER_store8(Interconnect* inter, u32 addr, u8 val/ +IVEC2_op sr.c /^IVEC2_op(ivec2 a, ivec2 b, enum mop mop)$/ +IVEC2_ops sr.c /^IVEC2_ops(ivec2 a, i32 b, enum mop mop)$/ +I_SWAP sr.h /^#define I_SWAP(x, y) { int temp = x; x = y; y = te/ Interconnect interconnect.h /^struct Interconnect {$/ +LOG defs.h /^#define LOG(level, fmt, ...) \\$/ LOG_ERR defs.h /^#define LOG_ERR(x) fprintf(stderr, (x))$/ Load cpu.h /^} Load;$/ Mmain main.c /^main(int argc, char **argv)$/ +POSITION_from_gp0 sr.c /^POSITION_from_gp0(u32 val)$/ Port mem.h /^} Port;$/ RAM mem.h /^} RAM;$/ RAM_load16 mem.c /^RAM_load16(RAM* r, u32 offset)$/ @@ -84,10 +105,20 @@ RAM_new mem.c /^RAM_new(void) $/ RAM_store16 mem.c /^RAM_store16(RAM* r, u32 offset, u16 val)$/ RAM_store32 mem.c /^RAM_store32(RAM* r, u32 offset, u32 val)$/ RAM_store8 mem.c /^RAM_store8(RAM* r, u32 offset, u8 val)$/ +REN sr.h /^} REN;$/ +REN_FB_set sr.c /^void REN_FB_set(REN* ren, i32 x, i32 y, C c) { (!r/ +REN_display sr.c /^REN_display(REN* ren)$/ +REN_draw sr.c /^REN_draw(REN* ren)$/ +REN_new sr.c /^REN_new()$/ +REN_push_quad sr.c /^REN_push_quad(REN* ren, ivec2 verts[4], C colors[4/ +REN_push_triangle sr.c /^REN_push_triangle(REN* ren, ivec2 verts[3], C colo/ +REN_triangle sr.c /^void REN_triangle(REN* ren, ivec2 verts[3], C colo/ +SHADERS_gradient_shader shaders.c /^SHADERS_gradient_shader(float alpha, float beta, f/ Step mem.h /^} Step;$/ Sync mem.h /^} Sync;$/ TEXTURE_DEPTH gpu.h /^} TEXTURE_DEPTH;$/ UTIL_contains util.c /^UTIL_contains(u32 start, u32 length, u32 addr, u32/ +VEC2I_SWAP sr.h /^#define VEC2I_SWAP(x, y) { ivec2 temp = x; x = y; / VMODE gpu.h /^} VMODE;$/ branch cpu.c /^branch(CPU* cpu, u32 offset)$/ checked_addi32 util.c /^checked_addi32(i32 a, i32 b, i32* res)$/ @@ -98,7 +129,9 @@ i32 types.h /^typedef int32_t i32;$/ i64 types.h /^typedef int64_t i64;$/ i8 types.h /^typedef int8_t i8;$/ instruction cpu.h /^} instruction;$/ +ivec2 sr.h /^typedef struct { int x, y; } ivec2;$/ mask_region util.c /^u32 mask_region(u32 addr) { return addr & REGION_M/ +mop sr.h /^enum mop {ADD, SUB, MUL, DIV};$/ new_cpu cpu.c /^new_cpu(Interconnect* inter) {$/ new_instr cpu.c /^new_instr(u32 i)$/ new_interconnect interconnect.c /^new_interconnect(void) {$/ @@ -173,9 +206,11 @@ op_swr cpu.c /^op_swr(CPU* cpu, instruction* i)$/ op_syscall cpu.c /^void op_syscall(CPU* cpu, instruction* i)$/ op_xor cpu.c /^op_xor(CPU* cpu, instruction* i)$/ op_xori cpu.c /^op_xori(CPU* cpu, instruction* i)$/ +point sr.h /^typedef struct { double x, y, z; } point;$/ reg cpu.c /^u32 reg(CPU* cpu, u32 reg) { return cpu->out_regs[/ set_reg cpu.c /^void set_reg(CPU* cpu, u32 reg, u32 val) { cpu->ou/ u16 types.h /^typedef uint16_t u16;$/ u32 types.h /^typedef uint32_t u32;$/ u64 types.h /^typedef uint64_t u64;$/ u8 types.h /^typedef uint8_t u8;$/ +vec3f sr.h /^typedef struct { double x, y, z; } vec3f;$/ diff --git a/src/util.c b/src/util.c @@ -3,42 +3,52 @@ #include <limits.h> const u32 REGION_MASK[8] = { - /* KUSEG: 2048MB */ - 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, - /* KSEG0: 512MB */ - 0x7fffffff, - /* KSEG1: 512MB */ - 0x1fffffff, - /* KSEG2: 1024MB */ - 0xffffffff, 0xffffffff + /* KUSEG: 2048MB */ + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + /* KSEG0: 512MB */ + 0x7fffffff, + /* KSEG1: 512MB */ + 0x1fffffff, + /* KSEG2: 1024MB */ + 0xffffffff, 0xffffffff }; u32 UTIL_contains(u32 start, u32 length, u32 addr, u32 *res) { - *res = addr-start; - return (addr >= start && addr < start + length) ? 1 : 0; + *res = addr-start; + return (addr >= start && addr < start + length) ? 1 : 0; } u8 checked_addi32(i32 a, i32 b, i32* res) { - if ((b > 0 && a > INT_MAX - b) || (b < 0 && a < INT_MIN - b)) { - return 0; - } - *res = a + b; - return 1; + if ((b > 0 && a > INT_MAX - b) || (b < 0 && a < INT_MIN - b)) { + return 0; + } + *res = a + b; + return 1; } u8 checked_subi32(i32 a, i32 b, i32* res) { - if ((b > 0 && a > INT_MAX - b) || (b < 0 && a < INT_MIN - b) || (b == 0 && a == INT_MIN)) { - return 0; /* Overflow occurred */ - } + if ((b > 0 && a > INT_MAX - b) || (b < 0 && a < INT_MIN - b) || (b == 0 && a == INT_MIN)) { + return 0; /* Overflow occurred */ + } - *res = a - b; - return 1; + *res = a - b; + return 1; } u32 mask_region(u32 addr) { return addr & REGION_MASK[addr >> 29]; } + +void +swap_int(int *a, int *b) { int temp = *a; *a = *b; *b = temp; } + +void +swap_color(C *a, C *b) { C temp = *a; *a = *b; *b = temp; } + +void +swap_vec2(ivec2 *a, ivec2 *b) { ivec2 temp = *a; *a = *b; *b = temp; } + diff --git a/src/util.h b/src/util.h @@ -1,6 +1,7 @@ #pragma once #include "types.h" +#include "sr.h" #define BIOS_SIZE 512 * 1024 #define BIOS_START 0x1FC00000 @@ -42,3 +43,9 @@ u32 UTIL_contains(u32, u32, u32, u32*); u8 checked_addi32(i32, i32, i32*); u8 checked_subi32(i32, i32, i32*); u32 mask_region(u32); + +/* Misc */ +void swap_int(int*, int*); +void swap_color(C*, C*); +void swap_vec2(ivec2*, ivec2*); +