ultimecia

A ps1 emulator in c
Log | Files | Refs

commit 7485052cf5412b55b91f2fde562b46cad727f7a3
parent 4c129482be98fe54516eff50bb117872ef96a033
Author: root <root@lunarcry>
Date:   Mon, 21 Oct 2024 19:17:18 +0000

SR:
    Moved it to main src/ folder and integrate it into the gpu module.
    Still a little hard to understand how to correctly draw the triangles and
    if it's possible with this implementation of the software rasterizer.. we'll see

Diffstat:
Mbuild.sh | 3++-
Msrc/gpu.c | 17+++++++++--------
Msrc/gpu.h | 4++--
Msrc/main.c | 43+++++++++++++++++++++++++------------------
Dsrc/renderer.c | 67-------------------------------------------------------------------
Dsrc/renderer.h | 33---------------------------------
Asrc/sr.c | 379+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sr.h | 43+++++++++++++++++++++++++++++++++++++++++++
Msrc/sr/sr.c | 17++---------------
Asrc/sr/sr.h | 26++++++++++++++++++++++++++
10 files changed, 488 insertions(+), 144 deletions(-)

diff --git a/build.sh b/build.sh @@ -1 +1,2 @@ -cc -Wall -Wpedantic -Wall -pedantic -g src/*.c -o bin/ultimecia -I/usr/local/include -L/usr/local/lib -lSDL2 +#cc -Wall -Wpedantic -Wall -pedantic -g src/*.c -o bin/ultimecia -I/usr/local/include -L/usr/local/lib -lSDL2 +cc -g src/*.c -o bin/ultimecia -I/usr/local/include -L/usr/local/lib -lSDL2 diff --git a/src/gpu.c b/src/gpu.c @@ -4,7 +4,7 @@ #include "gpu.h" #include "types.h" #include "defs.h" -#include "renderer.h" +#include "sr.h" u8 HOR_RES_from_fields(u8 hr1, u8 hr2) { return (hr2 & 1) | ((hr1 & 3) << 1); } u32 HOR_RES_into_status(u8 hr) {return ((u32)hr) << 16; } @@ -17,7 +17,7 @@ GPU_new(void) gpu = (GPU*)malloc(sizeof(GPU)); memset(gpu, 0, sizeof(GPU)); gpu->display_disabled = 1; - gpu->ren = RENDERER_new(); + gpu->ren = REN_new(); return gpu; } @@ -107,7 +107,8 @@ GPU_gp1(GPU* gpu, u32 val) void GPU_gp1_reset(GPU* gpu, u32 val) { - memset(gpu, 0, sizeof(GPU)); + REN* tmp = gpu->ren; + memset(gpu, 0, sizeof(GPU)); /* Fill out the rest of the fields with no$cash spec reset values*/ gpu->display_disabled = 1; gpu->interlaced = 1; @@ -115,6 +116,7 @@ GPU_gp1_reset(GPU* gpu, u32 val) gpu->display_horiz_end = 0xc00; gpu->display_line_start = 0x10; gpu->display_line_end = 0x100; + gpu->ren = tmp; } void @@ -349,8 +351,8 @@ GPU_gp0_quad_mono_opaque(GPU* gpu) void GPU_gp0_triangle_shaded_opaque(GPU* gpu) { - R_Position positions[3]; - R_Color colors[3]; + ivec2 positions[3]; + C colors[3]; positions[0] = POSITION_from_gp0(gpu->gp0_command.buffer[1]); positions[1] = POSITION_from_gp0(gpu->gp0_command.buffer[3]); @@ -360,7 +362,7 @@ GPU_gp0_triangle_shaded_opaque(GPU* gpu) colors[1] = COLOR_from_gp0(gpu->gp0_command.buffer[2]); colors[2] = COLOR_from_gp0(gpu->gp0_command.buffer[4]); - // gpu->ren->push_triangle(positions, colors); + REN_push_triangle(gpu->ren, positions, colors); fprintf(stdout, "Draw triangle shaded\n"); } @@ -415,4 +417,4 @@ GPU_CMD_BUFFER_push_word(GPU_CMD_BUFFER* cmd_buffer, u32 word) cmd_buffer->buffer[cmd_buffer->len] = word; cmd_buffer->len++; -} -\ No newline at end of file +} diff --git a/src/gpu.h b/src/gpu.h @@ -1,7 +1,7 @@ #pragma once #include "types.h" -#include "renderer.h" +#include "sr.h" typedef enum _GP0_MODE { GP0_MODE_COMMAND, @@ -88,7 +88,7 @@ typedef struct _GPU { u32 gp0_words_remaining; void (*gp0_command_method) (struct _GPU*); - RENDERER* ren; + REN* ren; } GPU; /* GPU */ diff --git a/src/main.c b/src/main.c @@ -10,30 +10,37 @@ #include "mem.h" #include "defs.h" #include "gpu.h" -#include "renderer.h" +#include "sr.h" + +//FB fb; +//SDL_Window* win; +//SDL_Renderer* ren; +//vec3f **vertices; +//i32 **faces; +SDL_Event ev; int main(int argc, char **argv) { - CPU* cpu; - Interconnect* inter; - - inter = new_interconnect(); - cpu = new_cpu(inter); + REN *ren; + CPU *cpu; + Interconnect *inter; - while(1) { - CPU_run_next_instruction(cpu); - } + inter = new_interconnect(); + cpu = new_cpu(inter); + while(1) { + CPU_run_next_instruction(cpu); + } - free(inter->bios->data); - free(inter->bios); - free(inter->ram->data); - free(inter->ram); - free(inter->dma); - free(inter); - free(cpu); + free(inter->bios->data); + free(inter->bios); + free(inter->ram->data); + free(inter->ram); + free(inter->dma); + free(inter); + free(cpu); - SDL_Quit(); + SDL_Quit(); - return 0; + return 0; } diff --git a/src/renderer.c b/src/renderer.c @@ -1,66 +0,0 @@ -/* This is the renderer */ - -#include <SDL2/SDL.h> -#include <stdint.h> -#include "types.h" -#include "renderer.h" - -RENDERER* -RENDERER_new() -{ - RENDERER* ren; - - SDL_Init(SDL_INIT_VIDEO); - - ren = malloc(sizeof(RENDERER)); - ren->window = SDL_CreateWindow("Ultimecia", 0, 0, 1024, 512, SDL_WINDOW_RESIZABLE); - ren->renderer = SDL_CreateRenderer(ren->window, -1, SDL_RENDERER_SOFTWARE); - ren->framebuffer = malloc(sizeof(u32) * 320 * 240); - memset(ren->framebuffer, 0, sizeof(u32)* 320 * 240); - ren->nvertices = 0; - - return ren; -} - -R_Position -POSITION_from_gp0(u32 val) -{ - R_Position pos; - pos.x = (i16)val; - pos.y = (i16)(val >> 16); - - return pos; -} - -R_Color -COLOR_from_gp0(u32 val) -{ - R_Color c; - c.r = (u8)val; - c.g = (u8)(val >> 8); - c.b = (u8)(val >> 16); - - return c; -} - -void -RENDERER_draw() -{ - return; -} - -void -RENDERER_push_triangle(RENDERER* ren, R_Position positions[3], R_Color colors[3]) -{ - u8 i; - - if (ren->nvertices + 3 > 1) { - printf("Vertex attribute buffer full, forcing draw...\n"); - RENDERER_draw(); - } - - for (i = 0; i < 3; i++) { - ren->nvertices++; - } - -} -\ No newline at end of file diff --git a/src/renderer.h b/src/renderer.h @@ -1,33 +0,0 @@ -#pragma once - -#include <SDL2/SDL.h> -#include "types.h" - -typedef struct _R_Position { - i16 x; - i16 y; -} R_Position; - -typedef struct _R_Color { - u8 r; - u8 g; - u8 b; -} R_Color; - -typedef struct _RENDERER { - SDL_Window* window; - SDL_Surface* surface; - SDL_Renderer* renderer; - R_Position* positions; - R_Color* colors; - u32* framebuffer; - u32 nvertices; /* Current number or vertices in the buffers */ -} RENDERER; - -RENDERER* RENDERER_new(); -R_Position POSITION_from_gp0(u32 val); -R_Color COLOR_from_gp0(u32 val); -void RENDERER_push_triangle(RENDERER*, R_Position[3], R_Color[3]); -void RENDERER_draw(); - - diff --git a/src/sr.c b/src/sr.c @@ -0,0 +1,379 @@ +/* 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" + +FB fb; +SDL_Window* win; +SDL_Renderer* ren; +vec3f **vertices; +i32 **faces; +//SDL_Event ev; + + +ivec2 +POSITION_from_gp0(u32 val) +{ + ivec2 pos; + pos.x = (i16)val; + pos.y = (i16)(val >> 16); + + return pos; +} + +C +COLOR_from_gp0(u32 val) +{ + C c; + c.r = (u8)val; + c.g = (u8)(val >> 8); + c.b = (u8)(val >> 16); + c.a = (u8)((val >> 24) & 0xff); + return c; +} + +void +RENDERER_push_triangle(REN* ren, ivec2 positions[3], C colors[3]) +{ + u8 i; + + if (ren->nvertices + 3 > 1) { + printf("Vertex attribute buffer full, forcing draw...\n"); + //RENDERER_draw(); + } + + for (i = 0; i < 3; i++) { + ren->nvertices++; + } + +} + +void +FB_flip_vert(u32 *data) +{ + u64 bytes_per_line; u32 *line; i32 half, j; + + bytes_per_line = W; + line = (u32 *)malloc(bytes_per_line * sizeof(u32)); + half = H>>1; + + for (i32 j=0; j<half; j++) { + u64 l1 = j*bytes_per_line; + u64 l2 = (H-1-j)*bytes_per_line; + memmove((void *)line, (void *)(data+l1), bytes_per_line* sizeof(u32)); + memmove((void *)(data+l1), (void *)(data+l2), bytes_per_line* sizeof(u32)); + memmove((void *)(data+l2), (void *)line, bytes_per_line* sizeof(u32)); + } + free(line); +} + +ivec2 +IVEC2_op(ivec2 a, ivec2 b, enum mop mop) +{ + ivec2 c; + switch(mop) { + case ADD: + c.x = a.x + b.x; + c.y = a.y + b.y; + break; + case SUB: + c.x = a.x - b.x; + c.y = a.y - b.y; + break; + case MUL: + c.x = a.x * b.x; + c.y = a.y * b.y; + break; + default: + break; + } + return c; +} + +ivec2 +IVEC2_ops(ivec2 a, i32 b, enum mop mop) +{ + ivec2 c; + switch(mop) { + case ADD: + c.x = a.x + b; + c.y = a.y + b; + break; + case SUB: + c.x = a.x - b; + c.y = a.y - b; + break; + case MUL: + c.x = a.x * b; + c.y = a.y * b; + break; + default: + break; + } + return c; +} + + +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.a = (u8)((b >> 24) & 0xff); + return c; +}; + +v new_fb() { fb.data = malloc(W*H*sizeof(u32));} +void FB_set(i32 x, i32 y, C c) { (!fb.data || x<0 || y<0 || x>=W || y>=H) ? 0 : memcpy(fb.data + ((x + y * W)), &c, 4); } +void REN_FB_set(REN* ren, i32 x, i32 y, C c) { (!fb.data || x<0 || y<0 || x>=W || y>=H) ? 0 : memcpy(ren->fb + ((x + y * W)), &c, 4); } + +//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; +//} + +void +line(ivec2 v, ivec2 v1, C color) +{ + + i32 x,y,x0,x1,y0,y1,dx,dy,sx,sy,e,e2,p; + x0 = v.x; + y0 = v.y; + x1 = v1.x; + y1 = v1.y; + + dx = abs(x1-x0); + sx = x0 < x1 ? 1 : -1; + dy = -abs(y1-y0); + sy = y0 < y1 ? 1 : -1; + e = dx + dy; + + while (1) { + FB_set(x0, y0, color); + if (x0 == x1 && y0 == y1) break; + e2 = 2 * e; + if (e2 >= dy) { + e += dy; + x0 += sx; + } + if (e2 <= dx) { + e += dx; + y0 += sy; + } + } +} + +REN* +REN_new() +{ + REN* ren; + SDL_Init(SDL_INIT_VIDEO); + ren = (REN*)malloc(sizeof(REN)); + ren->window = SDL_CreateWindow("Ultimecia", 400, 400, WIN_W, WIN_H, SDL_WINDOW_RESIZABLE); + ren->renderer = SDL_CreateRenderer(ren->window, -1, SDL_RENDERER_SOFTWARE); + ren->tex = SDL_CreateTexture(ren->renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, W, H); + ren->verts = NULL; + ren->colors = NULL; + ren->fb = malloc(W*H*sizeof(u32)); + memset(ren->fb, 0, (u32)(W*H)*sizeof(u32)); + ren->nvertices = 0; + return ren; +} + +i32 +parse_obj(i32 *fcount, i32 *ecount) +{ + FILE *f; + char *line; + char *tokens[128], *p; + size_t len; + ssize_t read; + long pos; + i32 edge_count, face_count; + + f = fopen("african.obj", "rb"); + + fseek(f, 0, SEEK_END); + pos = ftell(f); + fseek(f, 0, SEEK_SET); + + line = NULL; + edge_count = 0; + face_count = 0; + + while ((read = getline(&line, &len, f)) != -1) { + i32 i; + i = 0; + if (strstr(line, "v ") != NULL) { + for ((p = strtok(line, " ")); p; (p = strtok(NULL, " "))) + tokens[i++] = p; + + vertices = realloc(vertices, (edge_count + 1) * sizeof(vec3f*)); + vertices[edge_count] = malloc(sizeof(vec3f)); + + vertices[edge_count]->x = atof(tokens[1]); + vertices[edge_count]->y = atof(tokens[2]); + vertices[edge_count++]->z = atof(tokens[3]); + } + + if (strstr(line, "f ") != NULL) { + for ((p = strtok(line, " ")); p; (p = strtok(NULL, " "))) + tokens[i++] = p; + + faces = realloc(faces, (face_count + 1) * sizeof(i32*)); + faces[face_count] = malloc(sizeof(i32)*3); + + faces[face_count][0] = atoi(tokens[1]) - 1; + faces[face_count][1] = atoi(tokens[2]) - 1; + faces[face_count++][2] = atoi(tokens[3]) - 1; + } + } + + fclose(f); + if (line) + free(line); + + *fcount = face_count; + *ecount = edge_count; + + return 1; + +} + +void +draw_obj(i32 fcount) +{ + for (i32 i=0; i<fcount-1; i++) { + i32 *face = faces[i]; + + for (i32 j=0; j<3; j++) { + vec3f *v0 = vertices[face[j]]; + vec3f *v1 = vertices[face[(j+1)%3]]; + i32 x0 = (v0->x+1.)*W/2.; + i32 y0 = W - ((v0->y+1.)*W/2.); + i32 x1 = (v1->x+1.)*W/2.; + i32 y1 = W - ((v1->y+1.)*W/2.); + line((ivec2){x0, y0}, (ivec2){x1, y1}, C_new(0x3333ffff)); + } + } +} + +void +triangle(REN* ren, ivec2 t0, ivec2 t1, ivec2 t2, C color) { + // sort the vertices, t0, t1, t2 lower−to−upper (bubblesort yay!) + ivec2 temp; + + if (t0.y>t1.y) VEC2I_SWAP(t0, t1); + if (t0.y>t2.y) VEC2I_SWAP(t0, t2); + if (t1.y>t2.y) VEC2I_SWAP(t1, t2); + + i32 total_height = t2.y - t0.y; + + for (i32 i = 0; i < total_height; i++) { + i32 second_half = i > (t1.y - t0.y) || t1.y == t0.y; + i32 segment_height = second_half ? t2.y - t1.y : t1.y - t0.y; + float alpha = (float)i / total_height; + float beta = (float)(i - (second_half ? t1.y - t0.y : 0)) / segment_height; // how far down are we + + ivec2 A = { + t0.x + (t2.x - t0.x) * alpha, + t0.y + i + }; + + ivec2 B = second_half ? + (ivec2){ + t1.x + (t2.x - t1.x) * beta, + t1.y + (i - (t1.y - t0.y)) + } : + (ivec2){ + t0.x + (t1.x - t0.x) * beta, + t0.y + i + }; + + if (A.x > B.x) VEC2I_SWAP(A, B); + // Draw horizontal span between A and B + for (i32 x = A.x; x <= B.x; x++) { + REN_FB_set(ren, x, A.y, color); + } + } +} + +void +REN_push_triangle(REN* ren, ivec2 verts[3], C colors[3]) +{ + u8 i; + long VERTEX_BUFFER_LEN = 64*1024; + + if (ren->nvertices + 3 > 100) { + printf("Vertex attribute buffer full, forcing draw...\n"); + for(int i = 0; i < ren->nvertices-2; i++) + triangle(ren, ren->verts[i], ren->verts[i+1], ren->verts[i+2], ren->colors[i]); + } + + ren->verts = realloc(ren->verts, sizeof(ivec2) * (ren->nvertices + 3)); + ren->colors = realloc(ren->colors, sizeof(C) * (ren->nvertices + 3)); + for (i = 0; i < 3; i++) { + ren->verts[ren->nvertices] = verts[i]; + ren->colors[ren->nvertices] = colors[i]; + ren->nvertices++; + } +} + +i32 +haha_main() +{ + //init_sdl(); + + //SDL_Event ev; + + SDL_RenderClear(ren); + + //i32 fcount, ecount; + //i32 ho = parse_obj(&fcount, &ecount); + + //draw_obj(fcount); + + //ivec2 t0[3] = {{10, 70}, {50, 160}, {70, 80}}; + //ivec2 t1[3] = {{180, 50}, {150, 1}, {70, 180}}; + //ivec2 t2[3] = {{180, 150}, {120, 160}, {130, 180}}; + //triangle(t0[0], t0[1], t0[2],C_new(0xff000000)); + //triangle(t1[0], t1[1], t1[2],C_new(0xffff0000)); + //triangle(t2[0], t2[1], t2[2],C_new(0x000ff000)); + //ivec2 v0 = {0, 0}; + //ivec2 v1 = {W, 0}; + //ivec2 v2 = {W/2, H}; + //triangle(v0, v1, v2,C_new(0xff000000)); + + //FB_flip_vert(fb.data); + + //SDL_UpdateTexture(texture, NULL, fb.data, W * sizeof(u32)); + //SDL_RenderCopy(ren->ren, texture, NULL, NULL); + //SDL_RenderPresent(ren); + + //while(1) { + // while(SDL_PollEvent(&ev) != 0) { + // switch(ev.type) { + // case SDL_QUIT: + // SDL_Quit(); + // exit(1); + // } + // switch(ev.key.keysym.sym) { + // case SDLK_q: + // SDL_Quit(); + // exit(1); + // } + // } + //} + return 0; +} diff --git a/src/sr.h b/src/sr.h @@ -0,0 +1,43 @@ +#pragma once + +#include <SDL2/SDL.h> + +#define W 1024 +#define H 1024 +#define WIN_W 1024 +#define WIN_H 1024 + +#define VEC2I_SWAP(x, y) { ivec2 temp = x; x = y; y = temp; } +#define I_SWAP(x, y) { int temp = x; x = y; y = temp; } + +typedef void v; typedef uint8_t u8;typedef uint16_t u16;typedef uint32_t u32;typedef uint64_t u64;typedef int8_t i8;typedef int16_t i16;typedef int32_t i32;typedef int64_t i64;typedef uint8_t Bool; +typedef struct { u32 *data; } FB; +typedef struct {u8 b;u8 g;u8 r; u8 a;} C; +typedef struct { double x, y, z; } point; +typedef struct { int x, y; } ivec2; +typedef struct { double x, y, z; } vec3f; +enum mop {ADD, SUB, MUL, DIV}; + +typedef struct _RENDERER { + SDL_Window* window; + SDL_Texture* tex; + SDL_Renderer* renderer; + ivec2* verts; + C* colors; + u32* fb; + u32 nvertices; /* Current number or vertices in the buffers */ +} REN; + +void FB_flip_vert(u32*); +ivec2 IVEC2_op(ivec2, ivec2, enum mop); +ivec2 IVEC2_ops(ivec2, i32, enum mop); +C C_new(u32); + +v new_fb(); //{ fb.data = malloc(W*H*sizeof(u32));} +void FB_set(i32, i32, C); +void triangle(REN*, ivec2, ivec2, ivec2, C color); +REN* REN_new(); +ivec2 POSITION_from_gp0(u32 val); +C COLOR_from_gp0(u32 val); +void REN_push_triangle(REN*, ivec2[3], C[3]); +void RENDERER_draw(); diff --git a/src/sr/sr.c b/src/sr/sr.c @@ -7,21 +7,8 @@ #include <math.h> #include <SDL2/SDL.h> -#define W 1024 -#define H 1024 -#define WIN_W 1024 -#define WIN_H 1024 - -#define VEC2I_SWAP(x, y) { ivec2 temp = x; x = y; y = temp; } -#define I_SWAP(x, y) { int temp = x; x = y; y = temp; } - -typedef void v; typedef uint8_t u8;typedef uint16_t u16;typedef uint32_t u32;typedef uint64_t u64;typedef int8_t i8;typedef int16_t i16;typedef int32_t i32;typedef int64_t i64;typedef uint8_t Bool; -typedef struct { u32 *data; } FB; -typedef struct {u8 b;u8 g;u8 r; u8 a;} C; -typedef struct { double x, y, z; } point; -typedef struct { int x, y; } ivec2; -typedef struct { double x, y, z; } vec3f; -enum mop {ADD, SUB, MUL, DIV}; +#include "../types.h" +#include "sr.h" FB fb; SDL_Window* win; diff --git a/src/sr/sr.h b/src/sr/sr.h @@ -0,0 +1,26 @@ +#pragma once + +#define W 1024 +#define H 1024 +#define WIN_W 1024 +#define WIN_H 1024 + +#define VEC2I_SWAP(x, y) { ivec2 temp = x; x = y; y = temp; } +#define I_SWAP(x, y) { int temp = x; x = y; y = temp; } + +typedef void v; typedef uint8_t u8;typedef uint16_t u16;typedef uint32_t u32;typedef uint64_t u64;typedef int8_t i8;typedef int16_t i16;typedef int32_t i32;typedef int64_t i64;typedef uint8_t Bool; +typedef struct { u32 *data; } FB; +typedef struct {u8 b;u8 g;u8 r; u8 a;} C; +typedef struct { double x, y, z; } point; +typedef struct { int x, y; } ivec2; +typedef struct { double x, y, z; } vec3f; +enum mop {ADD, SUB, MUL, DIV}; + +void FB_flip_vert(u32*); +ivec2 IVEC2_op(ivec2, ivec2, enum mop); +ivec2 IVEC2_ops(ivec2, i32, enum mop); +C C_new(u32); + +v new_fb(); //{ fb.data = malloc(W*H*sizeof(u32));} +void FB_set(i32, i32, C); +void triangle(ivec2, ivec2, ivec2, C color);