Upload TIM files into VRAM
This commit is contained in:
parent
468468633e
commit
8ea8da50d1
|
@ -6,20 +6,36 @@ endif()
|
|||
|
||||
set(CMAKE_C_COMPILER psx-gcc)
|
||||
set(CMAKE_CXX_COMPILER psx-g++)
|
||||
set(CMAKE_AR mipsel-unknown-elf-ar)
|
||||
set(CMAKE_SYSTEM_NAME Generic)
|
||||
set(CMAKE_CROSSCOMPILING 1)
|
||||
|
||||
project(pinboid C)
|
||||
|
||||
add_executable(${PROJECT_NAME}
|
||||
"src/main.c"
|
||||
add_executable(${PROJECT_NAME} "src/main.c")
|
||||
|
||||
set(modules
|
||||
"res"
|
||||
"src/game"
|
||||
"src/gfx"
|
||||
"src/system"
|
||||
)
|
||||
|
||||
set(mode pal) # pal or ntsc
|
||||
|
||||
foreach(c ${modules})
|
||||
add_subdirectory(${c})
|
||||
endforeach()
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC init game)
|
||||
|
||||
target_link_directories(${PROJECT_NAME} PUBLIC $ENV{PSXSDK_PATH}/lib)
|
||||
target_compile_options(${PROJECT_NAME} PUBLIC -DFIXMATH_FAST_SIN -D_PAL_MODE_
|
||||
-DPSXSDK_DEBUG -DNO_CDDA -DNO_INTRO -Wall -g3 -Og)
|
||||
-DPSXSDK_DEBUG -DNO_CDDA -DNO_INTRO -Wall -g3 -Og -ffunction-sections
|
||||
-fdata-sections)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC -lpsx -lfixmath)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE . $ENV{PSXSDK_PATH}/include)
|
||||
set(cdroot cdimg)
|
||||
set(cdroot ${CMAKE_SOURCE_DIR}/cdimg)
|
||||
add_custom_target(exe ALL elf2exe ${PROJECT_NAME}
|
||||
${cdroot}/${PROJECT_NAME}.exe -mark="A homebrew game created with PSXSDK"
|
||||
DEPENDS ${PROJECT_NAME})
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
add_library(game "src/game.c")
|
||||
target_include_directories(game PUBLIC "inc")
|
||||
target_link_libraries(game PRIVATE gfx)
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef GAME_H
|
||||
#define GAME_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
int game(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GAME_H */
|
|
@ -0,0 +1,24 @@
|
|||
#include <game.h>
|
||||
#include <gfx.h>
|
||||
|
||||
static int init(void)
|
||||
{
|
||||
struct gfx_sprite s, s2;
|
||||
|
||||
if (gfx_sprite_from_file("cdrom:\\block.TIM;1", &s))
|
||||
return -1;
|
||||
|
||||
gfx_sprite_sort(&s);
|
||||
s2 = s;
|
||||
gfx_sprite_sort(&s2);
|
||||
gfx_draw();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int game(void)
|
||||
{
|
||||
if (init())
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
add_library(gfx
|
||||
"src/init.c"
|
||||
"src/sort.c"
|
||||
"src/sprite.c"
|
||||
)
|
||||
target_include_directories(gfx PUBLIC "inc")
|
||||
|
||||
if(mode STREQUAL "pal")
|
||||
target_compile_definitions(gfx PRIVATE VIDEO_MODE=VMODE_PAL)
|
||||
elseif(mode STREQUAL "ntsc")
|
||||
target_compile_definitions(gfx PRIVATE VIDEO_MODE=VMODE_NTSC)
|
||||
else()
|
||||
message(FATAL_ERROR "unknown video mode")
|
||||
endif()
|
|
@ -0,0 +1,82 @@
|
|||
#ifndef GFX_H
|
||||
#define GFX_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
SCREEN_X = 368,
|
||||
SCREEN_Y = 240
|
||||
};
|
||||
|
||||
/* 0-3 Texture page X Base (N*64) (ie. in 64-halfword steps) ;GPUSTAT.0-3
|
||||
4 Texture page Y Base (N*256) (ie. 0 or 256) ;GPUSTAT.4
|
||||
5-6 Semi Transparency (0=B/2+F/2, 1=B+F, 2=B-F, 3=B+F/4) ;GPUSTAT.5-6
|
||||
7-8 Texture page colors (0=4bit, 1=8bit, 2=15bit, 3=Reserved);GPUSTAT.7-8
|
||||
9 Dither 24bit to 15bit (0=Off/strip LSBs, 1=Dither Enabled) ;GPUSTAT.9
|
||||
10 Drawing to display area (0=Prohibited, 1=Allowed) ;GPUSTAT.10
|
||||
11 Texture Disable (0=Normal, 1=Disable if GP1(09h).Bit0=1) ;GPUSTAT.15
|
||||
(Above might be chipselect for (absent) second VRAM chip?)
|
||||
12 Textured Rectangle X-Flip (BIOS does set this bit on power-up...?)
|
||||
13 Textured Rectangle Y-Flip (BIOS does set it equal to GPUSTAT.13...?)
|
||||
14-23 Not used (should be 0)
|
||||
24-31 Command (E1h)*/
|
||||
union gfx_common
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint32_t tpagex :4;
|
||||
uint32_t tpagey :1;
|
||||
uint32_t stp :2;
|
||||
uint32_t bpp :2;
|
||||
uint32_t dither :1;
|
||||
uint32_t draw_to_disp :1;
|
||||
uint32_t disable :1;
|
||||
uint32_t xflip :1;
|
||||
uint32_t yflip :1;
|
||||
uint32_t :10;
|
||||
uint8_t cmd;
|
||||
};
|
||||
|
||||
uint32_t mask;
|
||||
};
|
||||
|
||||
union gfx_sznext
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint32_t next :24;
|
||||
uint32_t sz :8;
|
||||
};
|
||||
|
||||
uint32_t cmd_next;
|
||||
};
|
||||
|
||||
struct gfx_sprite
|
||||
{
|
||||
union gfx_sznext sznext;
|
||||
union gfx_common common;
|
||||
uint8_t r, g, b;
|
||||
uint8_t cmd;
|
||||
uint16_t x, y;
|
||||
uint16_t clutid;
|
||||
uint8_t u, v;
|
||||
uint16_t w, h;
|
||||
};
|
||||
|
||||
int gfx_init(void);
|
||||
void gfx_draw(void);
|
||||
void gfx_sprite_init(struct gfx_sprite *s);
|
||||
void gfx_sprite_sort(struct gfx_sprite *s);
|
||||
int gfx_sprite_from_file(const char *path, struct gfx_sprite *s);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GFX_H */
|
|
@ -0,0 +1,12 @@
|
|||
#include <gfx.h>
|
||||
#include <psxgpu.h>
|
||||
|
||||
int gfx_init(void)
|
||||
{
|
||||
GsInit();
|
||||
GsClearMem();
|
||||
GsSetVideoMode(SCREEN_X, SCREEN_Y, VMODE_PAL);
|
||||
GsSetDrawEnv(&(GsDrawEnv){.w = SCREEN_X, .h = SCREEN_Y});
|
||||
GsSetDispEnv(&(GsDispEnv){});
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
#include <gfx.h>
|
||||
#include <psxgpu.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static union gfx_sznext *first, *last;
|
||||
|
||||
static void add_to_list(union gfx_sznext *const p)
|
||||
{
|
||||
p->next = 0;
|
||||
|
||||
if (!first)
|
||||
first = p;
|
||||
else if (last)
|
||||
last->next = (uint32_t)p;
|
||||
|
||||
last = p;
|
||||
}
|
||||
|
||||
void gfx_sprite_sort(struct gfx_sprite *const s)
|
||||
{
|
||||
add_to_list(&s->sznext);
|
||||
}
|
||||
|
||||
void gfx_draw(void)
|
||||
{
|
||||
/* Wait for the GPU to finish drawing primitives. */
|
||||
while (!(GPU_CONTROL_PORT & (1 << 0x1a)))
|
||||
;
|
||||
|
||||
/* Wait for the GPU to be free. */
|
||||
while (!(GPU_CONTROL_PORT & (1 << 0x1c)))
|
||||
;
|
||||
|
||||
static uint32_t term = 0xffffff;
|
||||
|
||||
add_to_list((union gfx_sznext *)&term);
|
||||
|
||||
void gpu_ctrl(unsigned int command, unsigned int param);
|
||||
|
||||
gpu_ctrl(4, 2);
|
||||
D2_MADR = (uint32_t)first;
|
||||
D2_BCR = 0;
|
||||
D2_CHCR = (1 << 0xa) | 1 | (1 << 0x18);
|
||||
first = NULL;
|
||||
}
|
|
@ -0,0 +1,232 @@
|
|||
#include <gfx.h>
|
||||
#include <psxgpu.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
void gfx_sprite_init(struct gfx_sprite *const s)
|
||||
{
|
||||
extern unsigned int draw_mode_packet;
|
||||
|
||||
memset(s, 0, sizeof *s);
|
||||
s->sznext.sz = (sizeof (*s) - sizeof s->sznext) / sizeof (uint32_t);
|
||||
s->common.mask = draw_mode_packet;
|
||||
s->cmd = 0x64;
|
||||
s->r = s->g = s->b = NORMAL_LUMINANCE;
|
||||
}
|
||||
|
||||
static void transfer_init(const uint16_t x, const uint16_t y,
|
||||
const uint16_t w, const uint16_t h)
|
||||
{
|
||||
while (!(GPU_CONTROL_PORT & (1<<0x1c)))
|
||||
;
|
||||
|
||||
GPU_CONTROL_PORT = 0x04000000;
|
||||
GPU_DATA_PORT = 0x01000000;
|
||||
GPU_DATA_PORT = 0xE6000000;
|
||||
GPU_DATA_PORT = 0xA0000000;
|
||||
GPU_DATA_PORT = (y << 16) | x;
|
||||
GPU_DATA_PORT = (h << 16) | w;
|
||||
}
|
||||
|
||||
struct tim_pos
|
||||
{
|
||||
uint16_t x, y, w, h;
|
||||
};
|
||||
|
||||
static int transfer(const char *const path, const size_t sz, FILE *const f)
|
||||
{
|
||||
const size_t rem = sz % sizeof (uint32_t);
|
||||
|
||||
if (sz >= sizeof (uint32_t))
|
||||
{
|
||||
for (size_t i = 0; i < sz / sizeof (uint32_t); i++)
|
||||
{
|
||||
uint32_t pix;
|
||||
|
||||
if (!fread(&pix, sizeof pix, 1, f))
|
||||
{
|
||||
printf("%s: could not read CLUT word %zu/%zu\n",
|
||||
path, i, sz / sizeof pix);
|
||||
return -1;
|
||||
}
|
||||
|
||||
GPU_DATA_PORT = pix;
|
||||
}
|
||||
}
|
||||
|
||||
if (rem)
|
||||
{
|
||||
uint32_t pix = 0;
|
||||
|
||||
if (!fread(&pix, rem, 1, f))
|
||||
{
|
||||
printf("%s: failed reading remaining %zu bytes\n", path, rem);
|
||||
return -1;
|
||||
}
|
||||
|
||||
GPU_DATA_PORT = pix;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int upload_clut(const char *const path, struct gfx_sprite *const s,
|
||||
FILE *const f)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32_t sz;
|
||||
struct tim_pos pos;
|
||||
uint16_t colors;
|
||||
uint16_t num;
|
||||
} clut;
|
||||
|
||||
if (!fread(&clut, sizeof clut, 1, f))
|
||||
{
|
||||
printf("%s: No CLUT pos data found\n", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
transfer_init(clut.pos.x, clut.pos.y, clut.pos.w, clut.pos.h);
|
||||
|
||||
const size_t sz = clut.sz - sizeof clut + sizeof clut.sz;
|
||||
|
||||
if (transfer(path, sz, f))
|
||||
return -1;
|
||||
|
||||
s->clutid = get_clutid(clut.pos.x, clut.pos.y);
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum bpp
|
||||
{
|
||||
BPP_4 = 0,
|
||||
BPP_8 = 1,
|
||||
BPP_16 = 2,
|
||||
BPP_24 = 4
|
||||
};
|
||||
|
||||
static int upload_img(const char const *path, struct gfx_sprite *const s,
|
||||
const enum bpp bpp, FILE *const f)
|
||||
{
|
||||
struct tim_pos imgpos;
|
||||
|
||||
if (!fread(&imgpos, sizeof imgpos, 1, f))
|
||||
{
|
||||
printf("%s: No CLUT pos data found\n", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
transfer_init(imgpos.x, imgpos.y, imgpos.w, imgpos.h);
|
||||
|
||||
/* ftell and fseek are limited to 2 GiB when sizeof (long) == 4,
|
||||
* but that's already fine for PSX games. */
|
||||
const long off = ftell(f);
|
||||
fseek(f, 0, SEEK_END);
|
||||
const long sz = ftell(f) - off;
|
||||
fseek(f, off, SEEK_SET);
|
||||
|
||||
if (transfer(path, sz, f))
|
||||
return -1;
|
||||
|
||||
switch (bpp)
|
||||
{
|
||||
case BPP_4:
|
||||
s->w = imgpos.w * 4;
|
||||
break;
|
||||
|
||||
case BPP_8:
|
||||
s->w = imgpos.w * 2;
|
||||
break;
|
||||
|
||||
case BPP_16:
|
||||
s->w = imgpos.w;
|
||||
break;
|
||||
|
||||
case BPP_24:
|
||||
s->w = imgpos.w + (imgpos.w / 2);
|
||||
break;
|
||||
}
|
||||
|
||||
s->h = imgpos.h;
|
||||
|
||||
enum
|
||||
{
|
||||
VRAM_X = 1024,
|
||||
VRAM_Y = 512,
|
||||
TPAGE_WIDTH = 64
|
||||
};
|
||||
|
||||
s->common.tpagex = imgpos.x / TPAGE_WIDTH;
|
||||
s->common.tpagey = imgpos.y / (VRAM_Y / 2);
|
||||
s->u = imgpos.x % TPAGE_WIDTH;
|
||||
s->v = imgpos.y % (VRAM_Y / 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gfx_sprite_from_file(const char *const path, struct gfx_sprite *const s)
|
||||
{
|
||||
int ret = -1;
|
||||
FILE *f = NULL;
|
||||
|
||||
if (!path)
|
||||
{
|
||||
errno = EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
f = fopen(path, "rb");
|
||||
|
||||
if (!f)
|
||||
{
|
||||
printf("could not open %s: %s\n", path, strerror(errno));
|
||||
goto end;
|
||||
}
|
||||
|
||||
gfx_sprite_init(s);
|
||||
|
||||
struct
|
||||
{
|
||||
uint32_t version;
|
||||
uint32_t bpp :3;
|
||||
uint32_t has_clut :1;
|
||||
uint32_t :28;
|
||||
} h;
|
||||
|
||||
enum {VERSION_ID = 0x10};
|
||||
|
||||
if (!fread(&h, sizeof h, 1, f))
|
||||
{
|
||||
printf("%s: TIM header not found\n", path);
|
||||
goto end;
|
||||
}
|
||||
else if (h.version != VERSION_ID)
|
||||
{
|
||||
printf("%s: invalid TIM header 0x%08X\n", h.version);
|
||||
goto end;
|
||||
}
|
||||
else if (h.bpp == BPP_24)
|
||||
{
|
||||
printf("%s: 24-bit mode unsupported\n", path);
|
||||
goto end;
|
||||
}
|
||||
else if (h.has_clut && upload_clut(path, s, f))
|
||||
goto end;
|
||||
else if (upload_img(path, s, h.bpp, f))
|
||||
goto end;
|
||||
|
||||
s->common.bpp = h.bpp ? __builtin_ctz(h.bpp) + 1 : 0;
|
||||
ret = 0;
|
||||
end:
|
||||
if (f)
|
||||
{
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -1,6 +1,11 @@
|
|||
#include <init.h>
|
||||
#include <game.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
if (system_init() || game())
|
||||
return EXIT_FAILURE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
add_library(init "src/init.c")
|
||||
target_include_directories(init PUBLIC "inc")
|
||||
target_link_libraries(init PUBLIC gfx)
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef INIT_H
|
||||
#define INIT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
int system_init(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* INIT_H */
|
|
@ -0,0 +1,17 @@
|
|||
#include <gfx.h>
|
||||
#include <psx.h>
|
||||
|
||||
static void vblank(void *const arg)
|
||||
{
|
||||
}
|
||||
|
||||
int system_init(void)
|
||||
{
|
||||
PSX_InitEx(0);
|
||||
SetVBlankHandler(vblank);
|
||||
|
||||
if (gfx_init())
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue