├── src ├── linux │ ├── dma_tags.h │ ├── include │ │ ├── debug.h │ │ ├── dma_tags.h │ │ ├── gs_psm.h │ │ ├── loadfile.h │ │ ├── sifrpc.h │ │ ├── kernel.h │ │ ├── tamtypes.h │ │ ├── dma.h │ │ ├── graph.h │ │ ├── libpad.h │ │ ├── draw_tests.h │ │ ├── draw_blending.h │ │ ├── draw_primitives.h │ │ ├── gs_privileged.h │ │ ├── draw_sampling.h │ │ ├── draw.h │ │ └── gif_tags.h │ ├── vkernel.c │ ├── vdraw.c │ ├── vpad.c │ ├── vgraphics.c │ ├── vdma.c │ └── draw_clear.c ├── vu1prog │ ├── identity.vuobj │ └── identity.asm ├── include │ └── p2g │ │ ├── script.h │ │ ├── utils.h │ │ ├── ps2luaprog.h │ │ ├── core.h │ │ ├── gs.h │ │ ├── bench.h │ │ ├── pad.h │ │ ├── ps2math.h │ │ ├── log.h │ │ └── ps2draw.h ├── core │ ├── api.h │ ├── io.c │ ├── bindings.c │ ├── pad.c │ ├── log.c │ ├── dma.c │ ├── gslua.c │ ├── tga.c │ └── vu.c ├── gs_state.h ├── core.c ├── math │ ├── float.c │ ├── mat4lua.c │ ├── mat3lua.c │ ├── vec3lua.c │ ├── vec4lua.c │ └── vec2lua.c ├── draw │ ├── vu.h │ ├── internal.h │ ├── buffer.h │ ├── vu.c │ ├── draw.h │ └── draw3d.c ├── utils.c ├── examples │ └── rect.c ├── ps2luaprog.c ├── Makefile ├── gs.c ├── pad.c └── ps2math.c ├── asset ├── font.txt ├── half.tga ├── test.tga ├── bigfont.tga ├── bigfont.xcf ├── bigpal.tga ├── picotiles4.tga ├── Makefile ├── pipeline │ ├── palletize.py │ ├── dumpgif.py │ ├── strip_pallet.py │ ├── obj2gif.py │ ├── extract_pallet.py │ ├── extract_vu1_prog.py │ ├── obj.py │ ├── join_pallets.py │ └── tga.py └── cube.obj ├── distfiles ├── SYSTEM.CNF ├── README.md └── spleen.font.LICENSE ├── .lintvars ├── script ├── p2g │ ├── slotlist.lua │ ├── const.lua │ ├── math.lua │ ├── min.lua │ ├── test.lua │ ├── tga.lua │ ├── hot.lua │ ├── font.lua │ ├── vram.lua │ ├── init.lua │ ├── camera.lua │ └── gif.lua ├── eg │ ├── hotreload_draw.lua │ ├── rect.lua │ ├── hotreload.lua │ ├── control.lua │ ├── font.lua │ ├── math.lua │ ├── stress.lua │ ├── texture.lua │ ├── clut.lua │ ├── io.lua │ ├── cube_instance.lua │ ├── tests.lua │ └── cube.lua ├── test │ ├── test_mat4x4.lua │ ├── test_vec2.lua │ ├── test_slotlist.lua │ ├── test_mat3x3.lua │ └── test_vec3.lua ├── main.lua └── math │ ├── mat3.lua │ ├── mat4.lua │ ├── vec2.lua │ ├── vec3.lua │ └── vec4.lua ├── .github └── workflows │ ├── lint.yaml │ └── build.yaml ├── lua.patch ├── Dockerfile ├── .gitignore ├── quality.makefile ├── LICENSE ├── README.md └── Makefile /src/linux/dma_tags.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/linux/include/debug.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/linux/include/dma_tags.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/linux/include/gs_psm.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/linux/include/loadfile.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/linux/include/sifrpc.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /asset/font.txt: -------------------------------------------------------------------------------- 1 | SPLEEN 2 | https://github.com/fcambus/spleen 3 | -------------------------------------------------------------------------------- /distfiles/SYSTEM.CNF: -------------------------------------------------------------------------------- 1 | BOOT2 = cdrom0:\test.elf;1 2 | VER = 0.0 3 | VMODE = NTSC 4 | -------------------------------------------------------------------------------- /asset/half.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phy1um/ps2-homebrew-livestreams/HEAD/asset/half.tga -------------------------------------------------------------------------------- /asset/test.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phy1um/ps2-homebrew-livestreams/HEAD/asset/test.tga -------------------------------------------------------------------------------- /asset/bigfont.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phy1um/ps2-homebrew-livestreams/HEAD/asset/bigfont.tga -------------------------------------------------------------------------------- /asset/bigfont.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phy1um/ps2-homebrew-livestreams/HEAD/asset/bigfont.xcf -------------------------------------------------------------------------------- /asset/bigpal.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phy1um/ps2-homebrew-livestreams/HEAD/asset/bigpal.tga -------------------------------------------------------------------------------- /asset/picotiles4.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phy1um/ps2-homebrew-livestreams/HEAD/asset/picotiles4.tga -------------------------------------------------------------------------------- /src/vu1prog/identity.vuobj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phy1um/ps2-homebrew-livestreams/HEAD/src/vu1prog/identity.vuobj -------------------------------------------------------------------------------- /.lintvars: -------------------------------------------------------------------------------- 1 | CPPLINT_FILTERS=-readability/casting,-build/include_subdir,-legal,-build/header_guard,-whitespace/blank_line 2 | CPPLINT_LINE_LENGTH=80 3 | 4 | -------------------------------------------------------------------------------- /src/include/p2g/script.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef SRC_LUA_H 3 | #define SRC_LUA_H 4 | 5 | #include 6 | 7 | int bind_core_libs(lua_State *l); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /asset/Makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | %.vuprog: ../src/vu1prog/%.vuobj 4 | python pipeline/extract_vu1_prog.py $< $@ 5 | 6 | all: identity.vuprog 7 | 8 | clean: 9 | 10 | -------------------------------------------------------------------------------- /src/core/api.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef P2G_CORE_API_H 3 | #define P2G_CORE_API_H 4 | 5 | #define LIB_PREFIX "P2Garage." 6 | #define MAKE_LUA_LIB_NAME(n) (LIB_PREFIX n) 7 | #endif 8 | -------------------------------------------------------------------------------- /src/linux/vkernel.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | void FlushCache(int i) { return; } 5 | 6 | int SifLoadModule(const char *mod, int i, int j) { 7 | trace("SifLoadModule: %s", mod); 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /script/p2g/slotlist.lua: -------------------------------------------------------------------------------- 1 | 2 | local LOG = require"p2g.log" 3 | 4 | 5 | local list = { 6 | new = function(n) 7 | LOG.info("new list") 8 | return P2GCORE.slotlist.new(n) 9 | end 10 | } 11 | 12 | return list 13 | -------------------------------------------------------------------------------- /src/include/p2g/utils.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PS2UTILS_H 3 | #define PS2UTILS_H 4 | 5 | #include 6 | 7 | int print_buffer(qword_t *b, int len); 8 | int last_index_of(const char *str, int str_len, char c); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/gs_state.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef P2G_SRC_GS_STATE_H 3 | #define P2G_SRC_GS_STATE_H 4 | 5 | struct gs_state { 6 | framebuffer_t fb[2]; 7 | zbuffer_t zb; 8 | int ctx; 9 | }; 10 | 11 | extern struct gs_state *GS_STATE; 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/include/p2g/ps2luaprog.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PS2LUAPROG_H 3 | #define PS2LUAPROG_H 4 | 5 | #include 6 | 7 | int ps2luaprog_init(lua_State *l); 8 | 9 | int ps2luaprog_onframe(lua_State *l); 10 | int ps2luaprog_onstart(lua_State *l); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /src/linux/include/kernel.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef P2SIM_KERNEL_H 3 | #define P2SIM_KERNEL_H 4 | 5 | #include 6 | #define scr_printf(m, ...) printf(m, ##__VA_ARGS__) 7 | 8 | void FlushCache(int i); 9 | int SifLoadModule(const char *mod, int i, int j); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /.github/workflows/lint.yaml: -------------------------------------------------------------------------------- 1 | name: Run Linter 2 | on: [push] 3 | jobs: 4 | lint: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - uses: actions/setup-python@v1 9 | - run: pip install cpplint 10 | - run: make lint 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/include/p2g/core.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef P2G_CORE_H 3 | #define P2G_CORE_H 4 | 5 | typedef void(*fatal_handler_t)(void); 6 | void p2g_fatal(const char *s, ...); 7 | 8 | extern fatal_handler_t p2g_fatal_handler; 9 | void p2g_set_fatal_handler(fatal_handler_t fn); 10 | 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /distfiles/README.md: -------------------------------------------------------------------------------- 1 | # PS2 Engine 2 | 3 | This is a miscelaneous PS2 executable engine by Tom Marks. 4 | 5 | It contains an ELF file, which can be loaded into the PCSX2 or real hardware 6 | using a Homebrew launcher such as uLaunchELF, wLaunchELF or ps2link. 7 | 8 | Visit coding.tommarks.xyz 9 | 10 | -------------------------------------------------------------------------------- /src/vu1prog/identity.asm: -------------------------------------------------------------------------------- 1 | .vu 2 | .align 4 3 | NOP xgkick VI00 4 | NOP[E] NOP 5 | NOP NOP 6 | 7 | -------------------------------------------------------------------------------- /lua.patch: -------------------------------------------------------------------------------- 1 | diff --git a/makefile b/makefile 2 | index 2e7fc40..1ed2b29 100644 3 | --- a/makefile 4 | +++ b/makefile 5 | @@ -124,8 +124,6 @@ $(LUA_T): $(LUA_O) $(CORE_T) 6 | 7 | clean: 8 | $(RM) $(ALL_T) $(ALL_O) 9 | - #PS2 sample clean 10 | - $(MAKE) -C sample clean 11 | 12 | install: all 13 | ifeq ($(platform),PS2) 14 | -------------------------------------------------------------------------------- /asset/pipeline/palletize.py: -------------------------------------------------------------------------------- 1 | from tga import parse, write 2 | import sys 3 | 4 | if __name__ == "__main__": 5 | with open(sys.argv[1], "rb") as f: 6 | bb = f.read() 7 | tga = parse(bb) 8 | print(str(tga.header)) 9 | nh = write(tga) 10 | with open("out.tga", "wb") as r: 11 | r.write(nh) 12 | 13 | -------------------------------------------------------------------------------- /src/linux/vdraw.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | void draw_wait_finish() {} 7 | 8 | qword_t *draw_finish(qword_t *q) { 9 | PACK_GIFTAG(q, GIF_SET_TAG(1, 1, 0, 0, GIF_FLG_PACKED, 1), GIF_REG_AD); 10 | q++; 11 | PACK_GIFTAG(q, 1, GS_REG_FINISH); 12 | q++; 13 | 14 | return q; 15 | } 16 | -------------------------------------------------------------------------------- /src/linux/include/tamtypes.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef P2SIM_TAMTYPES_H 3 | #define P2SIM_TAMTYPES_H 4 | 5 | #include 6 | 7 | typedef union { 8 | uint8_t b[16]; 9 | uint16_t hw[8]; 10 | uint32_t sw[4]; 11 | uint64_t dw[2]; 12 | unsigned __int128 qw; 13 | } qword_t; 14 | 15 | typedef uint64_t u64; 16 | typedef uint32_t u32; 17 | typedef uint8_t u8; 18 | 19 | 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /script/eg/hotreload_draw.lua: -------------------------------------------------------------------------------- 1 | 2 | local D2D = require "p2g.draw2d" 3 | 4 | local hld = { 5 | x = 0, 6 | y = 0, 7 | w = 40, 8 | h = 40, 9 | } 10 | 11 | function hld.new(x, y) 12 | return setmetatable({ 13 | x = x, 14 | y = y, 15 | }, { __index = hld }) 16 | end 17 | 18 | function hld:draw() 19 | D2D:setColour(0xff, 0xff, 0xff, 0x80) 20 | D2D:rect(self.x, self.y, self.w, self.h) 21 | end 22 | 23 | return hld 24 | -------------------------------------------------------------------------------- /src/include/p2g/gs.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef GS_H 3 | #define GS_H 4 | 5 | int gs_init(); 6 | int gs_flip(); 7 | int gs_set_fields(int width, int height, int fmt, int zfmt, int fb1_addr, 8 | int fb2_addr, int zbuf_addr); 9 | int gs_set_output(int width, int height, int interlace, int mode, int ffmd, 10 | int filter_flicker); 11 | int gs_framebuffer_size(int width, int height, int psm); 12 | 13 | int gs_set_ztest(int mode); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/linux/vpad.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | void padInit(int i) {} 5 | 6 | void padPortOpen(int port, int slot, void *p) {} 7 | 8 | int32_t padGetState(int port, int slot) { return 0; } 9 | 10 | int padInfoMode(int port, int slot, int mode, int i) { return 0; } 11 | 12 | void padSetMainMode(int port, int slot, int a, int b) {} 13 | 14 | int padGetReqState(int port, int slot) { return 0; } 15 | 16 | int padRead(int port, int slot, void *p) { return 0; } 17 | -------------------------------------------------------------------------------- /asset/pipeline/dumpgif.py: -------------------------------------------------------------------------------- 1 | 2 | import sys 3 | import struct 4 | 5 | VERTEX_SIZE = 8*4 6 | 7 | with open(sys.argv[1], "rb") as f: 8 | content = f.read() 9 | vertex_count = len(content) / VERTEX_SIZE 10 | 11 | for i in range(int(vertex_count)): 12 | col = struct.unpack_from(" 5 | 6 | #define BENCH_START(vname) clock_t vname = clock() 7 | #define BENCH_INFO(vname, m) \ 8 | do { \ 9 | clock_t __time_now = clock(); \ 10 | info(m, ((float)__time_now - vname) / (float)CLOCKS_PER_SEC); \ 11 | } while (0) 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/linux/include/dma.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef P2SIM_DMA_H 3 | #define P2SIM_DMA_H 4 | 5 | enum dma_channels { 6 | DMA_CHANNEL_GIF, 7 | DMA_CHANNEL_VIF0, 8 | DMA_CHANNEL_VIF1, 9 | }; 10 | 11 | int dma_channel_initialize(int chan, void *handler, int flags); 12 | void dma_channel_fast_waits(int chan); 13 | int dma_channel_send_normal(int chan, void *data, int qwc, int flags, int spr); 14 | int dma_channel_send_chain(int chan, void *data, int data_size, int flags, 15 | int spr); 16 | void dma_wait_fast(); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /script/p2g/const.lua: -------------------------------------------------------------------------------- 1 | 2 | return { 3 | PRIM = { 4 | POINT=0x0, 5 | LINE=0x1, 6 | LINE_STRIP=0x2, 7 | TRI=0x3, 8 | TRI_FAN=0x4, 9 | TRI_STRIP=0x5, 10 | SPRITE=0x6, 11 | }, 12 | REG = { 13 | PRIM=0x0, 14 | BITBLTBUF=0x50, 15 | TRXPOS=0x51, 16 | TRXREG=0x52, 17 | TRXDIR=0x53, 18 | TEXFLUSH=0x3F, 19 | TEX0 = 0x06, 20 | TEX1 = 0x14, 21 | TEX2 = 0x16, 22 | TEXA = 0x3B, 23 | MIPTBP1 = 0x34, 24 | MIPTBP2 = 0x36, 25 | }, 26 | ENABLE=0x1, 27 | DISABLE=0x0, 28 | } 29 | -------------------------------------------------------------------------------- /src/core.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #ifndef LOG_LEVEL_DEFAULT 6 | #define LOG_LEVEL_DEFAULT LOG_LEVEL_DEBUG 7 | #endif 8 | 9 | int log_output_level = LOG_LEVEL_DEFAULT; 10 | 11 | void p2g_set_fatal_handler(fatal_handler_t fn) { 12 | p2g_fatal_handler = fn; 13 | } 14 | 15 | void p2g_fatal(const char *msg, ...) { 16 | logerr("FATAL: %s", msg); 17 | p2g_fatal_handler(); 18 | } 19 | 20 | void p2g_fatal_busyloop() { 21 | while (1) {} 22 | } 23 | 24 | fatal_handler_t p2g_fatal_handler = p2g_fatal_busyloop; 25 | -------------------------------------------------------------------------------- /src/math/float.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #define EPSILON 0.000001 7 | 8 | static int float_compare(lua_State *l) { 9 | float a = lua_tonumber(l, 1); 10 | float b = lua_tonumber(l, 2); 11 | int res = fabs(a - b) < EPSILON; 12 | lua_pushboolean(l, res); 13 | return 1; 14 | } 15 | 16 | int floatmath_lua_init(lua_State *l) { 17 | trace("init core lib math.misc"); 18 | lua_createtable(l, 0, 1); 19 | lua_pushcfunction(l, float_compare); 20 | lua_setfield(l, -2, "floatCmp"); 21 | return 1; 22 | } 23 | -------------------------------------------------------------------------------- /script/p2g/math.lua: -------------------------------------------------------------------------------- 1 | 2 | local types = { 3 | vec2_mt = require("math.vec2"), 4 | vec3_mt = require("math.vec3"), 5 | vec4_mt = require("math.vec4"), 6 | mat3_mt = require("math.mat3"), 7 | mat4_mt = require("math.mat4"), 8 | } 9 | 10 | types.vec2 = types.vec2_mt.new 11 | types.vec2From = types.vec2_mt.from 12 | types.vec3 = types.vec3_mt.new 13 | types.vec3From = types.vec3_mt.from 14 | types.vec4 = types.vec4_mt.new 15 | types.vec4From = types.vec4_mt.from 16 | 17 | types.mat3 = types.mat3_mt.new 18 | types.mat4 = types.mat4_mt.new 19 | 20 | local EPSILON = 0.0001 21 | 22 | function types.floatCmp(a, b) 23 | return math.abs(a-b) < EPSILON 24 | end 25 | 26 | return types 27 | 28 | -------------------------------------------------------------------------------- /src/draw/vu.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef P2G_DRWA_VU_H 3 | #define P2G_DRWA_VU_H 4 | 5 | // TODO(phy1um): move to some other folder 6 | #define VIF_CODE_NOP 0x0 7 | #define VIF_CODE_DIRECT 0x50 8 | #define VIF_CODE_MPG 0b1001010 9 | #define VIF_CODE_MSCAL 0b0010100 10 | // TODO(phy1um): mask bit? 11 | #define VIF_CODE_UNPACK_V432 0b1101100 12 | 13 | #define VIF_CODE_NO_STALL 0x0 14 | #define VIF_CODE_STALL 0x1 15 | 16 | #define TARGET_VU0 1 17 | #define TARGET_VU1 2 18 | 19 | 20 | int draw_vu_upload_program(void *buf, size_t buf_size, int vu_uprog_addr, 21 | int vu_target); 22 | int draw_vu_call_program(int vu_uprog_addr); 23 | int draw_vu_begin_unpack_inline(uint32_t target_addr); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /src/draw/internal.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef P2G_DRAW_INTERNAL 3 | #define P2G_DRAW_INTERNAL 4 | 5 | int draw_clear_buffer(); 6 | int draw_update_last_tag_loops(); 7 | int draw_start_cnt(); 8 | int dmatag_raw(struct commandbuffer *c, int qwc, int type, int addr); 9 | int draw_end_cnt(); 10 | int draw_dma_end(); 11 | int draw_vifcode_direct_start(struct commandbuffer *c); 12 | int draw_vifcode_end(struct commandbuffer *c); 13 | // transfer qwc quad-words from addr, then read next tag 14 | int draw_dma_ref(struct commandbuffer *c, uint32_t addr, int qwc); 15 | int draw_kick(); 16 | int draw_kick_vif(struct commandbuffer *c); 17 | int draw_kick_gif(struct commandbuffer *c); 18 | int commandbuffer_update_last_tag_loop(struct commandbuffer *c); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | test.iso 2 | 3 | # Prerequisites 4 | *.d 5 | 6 | # Object files 7 | *.o 8 | *.ko 9 | *.elf 10 | 11 | # Linker output 12 | *.ilk 13 | *.map 14 | *.exp 15 | 16 | # Precompiled Headers 17 | *.gch 18 | *.pch 19 | 20 | # Libraries 21 | *.lib 22 | *.a 23 | *.la 24 | *.lo 25 | 26 | # Shared objects (inc. Windows DLLs) 27 | *.dll 28 | *.so 29 | *.so.* 30 | *.dylib 31 | 32 | # Executables 33 | *.exe 34 | *.out 35 | *.app 36 | *.i*86 37 | *.x86_64 38 | *.hex 39 | 40 | # Debug files 41 | *.dSYM/ 42 | *.su 43 | *.idb 44 | *.pdb 45 | 46 | # Kernel Module Compile Results 47 | *.mod* 48 | *.cmd 49 | .tmp_versions/ 50 | modules.order 51 | Module.symvers 52 | Mkfile.old 53 | dkms.conf 54 | 55 | # python stuff 56 | **/__pycache__/ 57 | **/*.bin 58 | 59 | dist/* 60 | 61 | tags 62 | -------------------------------------------------------------------------------- /src/linux/include/graph.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef P2SIM_GRAPH_H 3 | #define P2SIM_GRAPH_H 4 | 5 | #include 6 | 7 | enum graph_modes { 8 | GRAPH_MODE_AUTO, 9 | GRAPH_MODE_PAL, 10 | GRAPH_MODE_NTSC, 11 | GRAPH_MODE_HDTV_480P, 12 | GRAPH_MODE_HDTV_576P, 13 | GRAPH_MODE_HDTV_720P, 14 | }; 15 | 16 | #define GRAPH_MODE_FIELD 0 17 | #define GRAPH_ENABLE 1 18 | #define GRAPH_DISABLE 0 19 | 20 | void init_scr(); 21 | void graph_wait_vsync(); 22 | void graph_enable_output(); 23 | void graph_disable_output(); 24 | int graph_set_mode(int interlace, int mode, int ffmd, int flicker_filter); 25 | int graph_set_screen(int xo, int yo, int width, int height); 26 | int graph_set_bgcolor(int r, int g, int b); 27 | void graph_set_framebuffer_filtered(int addr, int width, enum draw_psm psm, 28 | int x, int y); 29 | #endif 30 | -------------------------------------------------------------------------------- /script/p2g/min.lua: -------------------------------------------------------------------------------- 1 | PS2PROG.slow2d = false 2 | 3 | local gif = require("gif") 4 | local VRAM = require("vram") 5 | local D2D = require("draw2d") 6 | 7 | local gs = nil 8 | 9 | function writeToBuffer(b, ints) 10 | print("writing " .. #ints .. " ints") 11 | for i=1,#ints,1 do 12 | b:pushint(ints[i]) 13 | end 14 | end 15 | 16 | function PS2PROG.start() 17 | DMA.init(DMA.GIF) 18 | GS.setOutput(640, 448, GS.INTERLACED, GS.NTSC) 19 | local fb1 = VRAM.buffer(640, 448, GS.PSM24, 256) 20 | local fb2 = VRAM.buffer(640, 448, GS.PSM24, 256) 21 | local zb = VRAM.buffer(640, 448, GS.PSMZ24, 256) 22 | GS.setBuffers(fb1, fb2, zb) 23 | D2D:clearColour(0x2b, 0x2b, 0x2b) 24 | D2D:screenDimensions(640, 448) 25 | 26 | end 27 | 28 | function PS2PROG.frame() 29 | D2D:frameStart(gs) 30 | D2D:frameEnd(gs) 31 | end 32 | 33 | 34 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | push: 4 | branches: 5 | - main 6 | tags: 7 | - v*.*.* 8 | pull_request: 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | container: 13 | image: ps2dev/ps2dev 14 | steps: 15 | - uses: actions/checkout@v2 16 | - run: apk add python3 make gmp-dev mpfr-dev mpc1-dev zip git 17 | - run: make assets 18 | - run: echo "using PS2 SDK @ $PS2SDK" 19 | # we need PLATFORM and platform defined because consistency is hard 20 | - run: VERSION=dev PLATFORM=ps2 platform=PS2 IN_PIPELINE=true make release 21 | - uses: actions/upload-artifact@v3 22 | with: 23 | name: engine package 24 | path: ./*.zip 25 | if: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags') }} 26 | 27 | 28 | -------------------------------------------------------------------------------- /quality.makefile: -------------------------------------------------------------------------------- 1 | 2 | .PHONY: lualint 3 | lualint: 4 | luac5.3 -p $(LUA_FILES) 5 | 6 | .PHONY: lint 7 | lint: 8 | cpplint --filter=$(CPPLINT_FILTERS) --counting=total --linelength=$(CPPLINT_LINE_LENGTH) --extensions=c,h --recursive src 9 | 10 | .PHONY: format 11 | format: 12 | $(DOCKER) run $(DOCKERFLAGS) -v $(shell pwd):/workdir unibeautify/clang-format -i -sort-includes src/**/*.c **/*.h 13 | 14 | .PHONY: cppcheck 15 | cppcheck: 16 | $(DOCKER) run $(DOCKERFLAGS) --rm -v $(shell pwd):/src $(CPPCHECK_IMG) cppcheck -v --xml --enable=all ./src/ 2> $(CPPCHECK_REPORT) 17 | $(DOCKER) run $(DOCKERFLAGS) --rm -v $(shell pwd):/src $(CPPCHECK_IMG) cppcheck-htmlreport --source-dir=. --title="Engine Quality - $(VERSION)" --file=$(CPPCHECK_REPORT) --report-dir=$(CPPCHECK_OUT) 18 | 19 | .PHONY: memcheck 20 | memcheck: 21 | cd dist && valgrind --log-file="./vg-log" ./sim 22 | -------------------------------------------------------------------------------- /src/include/p2g/pad.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PAD_H 3 | #define PAD_H 4 | 5 | #include 6 | 7 | #define BUTTON_X 0 8 | #define BUTTON_SQUARE 1 9 | #define BUTTON_TRIANGLE 2 10 | #define BUTTON_CIRCLE 3 11 | #define BUTTON_L1 4 12 | #define BUTTON_R1 5 13 | #define BUTTON_L2 6 14 | #define BUTTON_R2 7 15 | #define DPAD_DOWN 8 16 | #define DPAD_LEFT 9 17 | #define DPAD_RIGHT 10 18 | #define DPAD_UP 11 19 | #define BUTTON_SELECT 12 20 | #define BUTTON_START 13 21 | #define BTN_MAX 14 22 | 23 | #define AXIS_LEFT_X 0 24 | #define AXIS_LEFT_Y 1 25 | #define AXIS_RIGHT_X 2 26 | #define AXIS_RIGHT_Y 3 27 | #define JOY_AXIS_COUNT 4 28 | 29 | #define R_SIO2MAN "rom0:SIO2MAN" 30 | #define R_PADMAN "rom0:PADMAN" 31 | 32 | int button_pressed(int b); 33 | int button_held(int b); 34 | int button_released(int b); 35 | int joy_axis_value(int a); 36 | 37 | int pad_init(); 38 | void pad_poll(); 39 | void pad_frame_start(); 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /src/linux/vgraphics.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | void graph_wait_vsync() {} 6 | 7 | void graph_enable_output() {} 8 | void graph_disable_output() {} 9 | 10 | int graph_set_mode(int interlace, int mode, int ffmd, int flicker_filter) { 11 | trace("graph set mode: interlace=%d, mode=%d, ffmd=%d, flicker=%d", interlace, 12 | mode, ffmd, flicker_filter); 13 | return 0; 14 | } 15 | 16 | int graph_set_screen(int xo, int yo, int width, int height) { 17 | trace("graph set screen: %d, %d, %d, %d", xo, yo, width, height); 18 | return 0; 19 | } 20 | 21 | int graph_set_bgcolor(int r, int g, int b) { 22 | trace("graph bgcol = (%x, %x, %x)", r, g, b); 23 | return 0; 24 | } 25 | 26 | void graph_set_framebuffer_filtered(int addr, int width, enum draw_psm psm, 27 | int x, int y) { 28 | trace("set FB = %X (width=%d, psm=%d, x=%d, y=%d)", addr, width, psm, x, y); 29 | } 30 | 31 | void init_scr() {} 32 | -------------------------------------------------------------------------------- /script/eg/rect.lua: -------------------------------------------------------------------------------- 1 | 2 | local GIF = require"p2g.gif" 3 | local P = require"p2g.const" 4 | local D2D = require"p2g.draw2d" 5 | local VRAM = require"p2g.vram" 6 | local DMA = require"p2g.dma" 7 | local GS = require"p2g.gs" 8 | local RM = require"p2g.buffer" 9 | local LOG = require"p2g.log" 10 | 11 | function PS2PROG.start() 12 | PS2PROG.logLevel(LOG.traceLevel) 13 | DMA.init(DMA.GIF) 14 | D2D.set_buffer_target(0) 15 | GS.setOutput(640, 448, GS.INTERLACED, GS.NTSC) 16 | local fb1 = VRAM.mem:framebuffer(640, 448, GS.PSM24, 2048) 17 | local fb2 = VRAM.mem:framebuffer(640, 448, GS.PSM24, 2048) 18 | local zb = VRAM.mem:framebuffer(640, 448, GS.PSMZ24, 2048) 19 | GS.setBuffers(fb1, fb2, zb) 20 | D2D:screenDimensions(640, 448) 21 | D2D:clearColour(0x2b, 0x2b, 0x2b) 22 | local db = RM.alloc(200 * 1024) 23 | D2D:bindBuffer(db) 24 | 25 | end 26 | 27 | function PS2PROG.frame() 28 | D2D:frameStart() 29 | D2D:setColour(255,0,0,0x80) 30 | D2D:rect(20, 20, 220, 220) 31 | D2D:frameEnd() 32 | end 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/include/p2g/ps2math.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PS2_MATH_H 3 | #define PS2_MATH_H 4 | 5 | float p2m_vec2_length(const float *v2); 6 | float p2m_vec2_dot(const float *a, const float *b); 7 | void p2m_vec2_rotate(float *v2, float theta); 8 | void p2m_vec2_scale(float *v, float s); 9 | 10 | float p2m_vec3_length(const float *v3); 11 | float p2m_vec3_dot(const float *a, const float *b); 12 | void p2m_vec3_scale(float *v, float s); 13 | 14 | float p2m_vec4_length(const float *v3); 15 | float p2m_vec4_dot(const float *a, const float *b); 16 | void p2m_vec4_scale(float *v, float s); 17 | 18 | void p2m_m3_copy(float *a, const float *b); 19 | void p2m_m3_apply(const float *m3, float *v); 20 | void p2m_m3_add(float *a, const float *b); 21 | void p2m_m3_identity(float *m); 22 | void p2m_m3_multiply(const float *a, const float *b, float *to); 23 | 24 | void p2m_m4_copy(float *a, const float *b); 25 | void p2m_m4_apply(const float *m3, float *v); 26 | void p2m_m4_add(float *a, const float *b); 27 | void p2m_m4_identity(float *m); 28 | void p2m_m4_multiply(const float *a, const float *b, float *to); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /script/test/test_mat4x4.lua: -------------------------------------------------------------------------------- 1 | 2 | local LOG = require"p2g.log" 3 | local M = require"p2g.math" 4 | local test = require"p2g.test" 5 | 6 | local t = {name = "Matrix 4x4"} 7 | 8 | function t.mat4_init() 9 | local a = M.mat4() 10 | for i=0,3 do 11 | for j=0,3 do 12 | v = a[j*4 + i] 13 | if i == j then 14 | if v ~= 1 then 15 | test.fail(string.format("expected 1 @ [%d,%d], got %d", i, j, v)) 16 | end 17 | elseif v ~= 0 then 18 | test.fail(string.format("expected 0 @ [%d,%d], got %d", i, j, v)) 19 | end 20 | end 21 | end 22 | end 23 | 24 | function t.mat4_add() 25 | local a = M.mat4() 26 | local b = M.mat4({ {1,2,3,4},{0,0,0,0},{0,0,0,0},{0,0,0,0} }) 27 | a:add(b) 28 | local c = M.mat4({ {2,2,3,4},{0,1,0,0},{0,0,1,0},{0,0,0,1} }) 29 | test.equal(a, c) 30 | end 31 | 32 | function t.mat4_multiply_identity() 33 | local ii = M.mat4() 34 | local a = M.mat4({ {1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16} }) 35 | local x = M.mat4() 36 | x:copy(a) 37 | x:mul(ii) 38 | test.equal(a, x) 39 | end 40 | 41 | return t 42 | 43 | 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Tom Marks 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /src/linux/include/libpad.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef P2SIM_LIBPAD_H 3 | #define P2SIM_LIBPAD_H 4 | 5 | enum pad { 6 | PAD_STATE_STABLE, 7 | PAD_STATE_FINDCTP1, 8 | PAD_STATE_DISCONN, 9 | PAD_MODETABLE, 10 | PAD_TYPE_DUALSHOCK, 11 | PAD_RSTAT_COMPLETE, 12 | }; 13 | 14 | enum pad_buttons { 15 | PAD_LEFT, 16 | PAD_RIGHT, 17 | PAD_UP, 18 | PAD_DOWN, 19 | PAD_SQUARE, 20 | PAD_TRIANGLE, 21 | PAD_CIRCLE, 22 | PAD_CROSS, 23 | PAD_L1, 24 | PAD_L2, 25 | PAD_R1, 26 | PAD_R2, 27 | PAD_START, 28 | PAD_SELECT, 29 | }; 30 | 31 | enum pad_axis { 32 | AXIS_LEFT_X, 33 | AXIS_LEFT_Y, 34 | AXIS_RIGHT_X, 35 | AXIS_RIGHT_Y, 36 | }; 37 | 38 | struct padButtonStatus { 39 | int btns; 40 | int ljoy_h; 41 | int ljoy_v; 42 | int rjoy_h; 43 | int rjoy_v; 44 | }; 45 | 46 | 47 | void padInit(int i); 48 | void padPortOpen(int port, int slot, void *p); 49 | int32_t padGetState(int port, int slot); 50 | int padInfoMode(int port, int slot, int mode, int i); 51 | void padSetMainMode(int port, int slot, int a, int b); 52 | int padGetReqState(int port, int slot); 53 | 54 | int padRead(int port, int slot, void *p); 55 | 56 | 57 | 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /asset/pipeline/strip_pallet.py: -------------------------------------------------------------------------------- 1 | from tga import parse, write 2 | import sys 3 | import argparse 4 | 5 | parser = argparse.ArgumentParser(description="extract palette from TGA file") 6 | parser.add_argument("file", type=str, help="input colour mapped TGA file") 7 | parser.add_argument("output", type=str, help="name of output file for pallet") 8 | parser.add_argument("-t", "--tiny", type=bool, default=False, help="compress indexes to 4 bits") 9 | 10 | if __name__ == "__main__": 11 | args = parser.parse_args() 12 | with open(args.file, "rb") as f: 13 | bb = f.read() 14 | tga = parse(bb) 15 | tga.col = [] 16 | 17 | if args.tiny: 18 | tga.header.img_spec.bits_per_pixel = 4 19 | else: 20 | tga.header.img_spec.bits_per_pixel = 8 21 | tga.header.img_type = 2 22 | tga.header.img_spec.xorigin = 0 23 | tga.header.img_spec.yorigin = 0 24 | tga.header.col_map_type = 0 25 | tga.header.col_spec.map_len = 0 26 | tga.header.col_spec.entry_bits = 1 27 | updated = write(tga) 28 | with open(args.output, "wb") as out: 29 | out.write(updated) 30 | 31 | -------------------------------------------------------------------------------- /script/p2g/test.lua: -------------------------------------------------------------------------------- 1 | 2 | local LOG = require"p2g.log" 3 | 4 | local test = {} 5 | 6 | local default_handlers = { 7 | failed = function(suite_name, test_name, err) 8 | LOG.error(" % Failed Test (" .. test_name .. ") :: " .. err) 9 | end, 10 | passed = function(suite_name, test_name) end, 11 | } 12 | 13 | 14 | function test.handler(testName) 15 | return function(err) 16 | LOG.error(" % Failed Test (" .. testName .. ") :: " .. err) 17 | end 18 | end 19 | 20 | function test.run_suite(name, t, handlers) 21 | local handlers = handlers or default_handlers 22 | local ran = 0 23 | local pass = 0 24 | for test_name,fn in pairs(t) do 25 | if type(fn) == "function" then 26 | ran = ran + 1 27 | local rv = xpcall(fn, function(err) handlers.failed(name, test_name, err) end) 28 | if rv then 29 | handlers.passed(name, test_name) 30 | end 31 | end 32 | end 33 | end 34 | 35 | function test.equal(exp, actual) 36 | if (exp == actual) == false then 37 | error("expected: " .. tostring(exp) .. ", actual: " .. tostring(actual)) 38 | end 39 | end 40 | 41 | function test.fail(msg) 42 | error(msg) 43 | end 44 | 45 | return test 46 | -------------------------------------------------------------------------------- /script/p2g/tga.lua: -------------------------------------------------------------------------------- 1 | 2 | local IO = require"p2g.io" 3 | local LOG = require"p2g.log" 4 | local TGA = P2GCORE["tga"] 5 | 6 | function TGA.from_file(file_name, alloc) 7 | LOG.trace("loading texture " .. file_name) 8 | local tga_header = alloc(TGA.HEADER_SIZE) 9 | IO.read_file(file_name, 0, TGA.HEADER_SIZE, tga_header) 10 | local w = TGA.get_header_field(tga_header, "width") 11 | local h = TGA.get_header_field(tga_header, "height") 12 | local bps = TGA.get_header_field(tga_header, "bps") 13 | local bpp = math.floor(bps/8) 14 | local size = w*h*bpp 15 | if bps == 4 then size = w*h*0.5 end 16 | LOG.debug(string.format("TGA header: %d x %d @ %d (%d bytes)", w, h, bps, size)) 17 | local tga_body = alloc(size) 18 | IO.read_file(file_name, TGA.HEADER_SIZE, size, tga_body) 19 | local texture = { 20 | width = w, 21 | height = h, 22 | data = tga_body, 23 | format = TGA.BPS_TO_PSM[bps], 24 | fname = file_name, 25 | } 26 | if bps == 32 then 27 | TGA.swizzle32(texture) 28 | elseif bps == 24 then 29 | TGA.swizzle24(texture) 30 | elseif bps == 16 then 31 | TGA.swizzle16(texture) 32 | end 33 | return texture, tga_header 34 | end 35 | 36 | return TGA 37 | -------------------------------------------------------------------------------- /src/linux/include/draw_tests.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * Draw library testing functions 4 | */ 5 | 6 | #ifndef __DRAW_TESTS_H__ 7 | #define __DRAW_TESTS_H__ 8 | 9 | #include 10 | 11 | /*& Alpha Testing */ 12 | #define ATEST_METHOD_ALLFAIL 0 13 | #define ATEST_METHOD_ALLPASS 1 14 | #define ATEST_METHOD_LESS 2 15 | #define ATEST_METHOD_LESS_EQUAL 3 16 | #define ATEST_METHOD_EQUAL 4 17 | #define ATEST_METHOD_GREATER_EQUAL 5 18 | #define ATEST_METHOD_GREATER 6 19 | #define ATEST_METHOD_NOTEQUAL 7 20 | 21 | #define ATEST_KEEP_ALL 0 22 | #define ATEST_KEEP_ZBUFFER 1 23 | #define ATEST_KEEP_FRAMEBUFFER 2 24 | #define ATEST_KEEP_ALPHA 3 25 | 26 | /** Destination Alpha Testing */ 27 | #define DTEST_METHOD_PASS_ZERO 0 28 | #define DTEST_METHOD_PASS_ONE 1 29 | 30 | /** Depth Test */ 31 | #define ZTEST_METHOD_ALLFAIL 0 32 | #define ZTEST_METHOD_ALLPASS 1 33 | #define ZTEST_METHOD_GREATER_EQUAL 2 34 | #define ZTEST_METHOD_GREATER 3 35 | 36 | #endif /* __DRAW_TESTS_H__ */ 37 | -------------------------------------------------------------------------------- /script/p2g/hot.lua: -------------------------------------------------------------------------------- 1 | 2 | return { init = function() 3 | local H = { 4 | truerequire = require, 5 | cache = {}, 6 | -- '_' == 95 7 | ignoreChar = 95, 8 | } 9 | 10 | 11 | function H.require(p) 12 | local m = H.cache[p] 13 | if m == nil then 14 | m = H.truerequire(p) 15 | H.cache[p] = m 16 | end 17 | return m 18 | end 19 | 20 | function H.reload(p) 21 | local m = H.cache[p] 22 | if m == nil then return end 23 | package.loaded[p] = nil 24 | local newmod = H.truerequire(p) 25 | 26 | local pre = newmod["_reload_before"] 27 | if type(pre) == "function" then 28 | LOG.trace("calling reload_before on " .. p) 29 | pre(m) 30 | end 31 | 32 | for k, v in pairs(newmod) do 33 | -- do not copy keys that start with some character 34 | if string.byte(k, 1) ~= H.ignoreChar then 35 | LOG.trace("patching module " .. p .. ": " .. k) 36 | m[k] = v 37 | end 38 | end 39 | 40 | local post = newmod["_reload_after"] 41 | if type(post) == "function" then 42 | LOG.trace("calling reload_after on " .. p) 43 | post(m) 44 | end 45 | end 46 | 47 | return H.require, H.reload 48 | end} 49 | -------------------------------------------------------------------------------- /asset/cube.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.76 (sub 0) OBJ File: '' 2 | # www.blender.org 3 | mtllib cube.mtl 4 | o Cube 5 | v 1.000000 -1.000000 -1.000000 6 | v 1.000000 -1.000000 1.000000 7 | v -1.000000 -1.000000 1.000000 8 | v -1.000000 -1.000000 -1.000000 9 | v 1.000000 1.000000 -0.999999 10 | v 0.999999 1.000000 1.000001 11 | v -1.000000 1.000000 1.000000 12 | v -1.000000 1.000000 -1.000000 13 | vt 1.000000 0.333333 14 | vt 1.000000 0.666667 15 | vt 0.666667 0.666667 16 | vt 0.666667 0.333333 17 | vt 0.666667 0.000000 18 | vt 0.000000 0.333333 19 | vt 0.000000 0.000000 20 | vt 0.333333 0.000000 21 | vt 0.333333 1.000000 22 | vt 0.000000 1.000000 23 | vt 0.000000 0.666667 24 | vt 0.333333 0.333333 25 | vt 0.333333 0.666667 26 | vt 1.000000 0.000000 27 | vn 0.000000 -1.000000 0.000000 28 | vn 0.000000 1.000000 0.000000 29 | vn 1.000000 0.000000 0.000000 30 | vn -0.000000 0.000000 1.000000 31 | vn -1.000000 -0.000000 -0.000000 32 | vn 0.000000 0.000000 -1.000000 33 | usemtl Material 34 | s off 35 | f 2/1/1 3/2/1 4/3/1 36 | f 8/1/2 7/4/2 6/5/2 37 | f 5/6/3 6/7/3 2/8/3 38 | f 6/8/4 7/5/4 3/4/4 39 | f 3/9/5 7/10/5 8/11/5 40 | f 1/12/6 4/13/6 8/11/6 41 | f 1/4/1 2/1/1 4/3/1 42 | f 5/14/2 8/1/2 6/5/2 43 | f 1/12/3 5/6/3 2/8/3 44 | f 2/12/4 6/8/4 3/4/4 45 | f 4/13/5 3/9/5 8/11/5 46 | f 5/6/6 1/12/6 8/11/6 47 | 48 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | /** 7 | * print a QWORD buffer as trace level log 8 | */ 9 | int print_buffer(qword_t *b, int len) { 10 | #ifdef LOG_TRACE 11 | if (log_output_level >= LOG_LEVEL_TRACE) { 12 | trace("-- buffer %p\n", b); 13 | for (int i = 0; i < len; i++) { 14 | printf( 15 | "%02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X\n", 16 | /* 17 | b->b[0], b->b[1], b->b[2], b->b[3], 18 | b->b[4], b->b[5], b->b[6], b->b[7], 19 | b->b[8], b->b[9], b->b[10], b->b[11], 20 | b->b[12], b->b[13], b->b[14], b->b[15] 21 | */ 22 | b->b[15], b->b[14], b->b[13], b->b[12], 23 | b->b[11], b->b[10], b->b[9], b->b[8], 24 | b->b[7], b->b[6], b->b[5], b->b[4], 25 | b->b[3], b->b[2], b->b[1], b->b[0]); 26 | b++; 27 | } 28 | trace("-- /buffer\n"); 29 | } 30 | #endif 31 | return 0; 32 | } 33 | 34 | /** 35 | * get the last index of a character in a string 36 | */ 37 | int last_index_of(const char *str, int str_len, char c) { 38 | int ind = -1; 39 | for (int i = 0; i < str_len; i++) { 40 | if (str[i] == c) { 41 | ind = i; 42 | } 43 | } 44 | return ind; 45 | } 46 | -------------------------------------------------------------------------------- /script/eg/hotreload.lua: -------------------------------------------------------------------------------- 1 | 2 | local PAD = require"p2g.pad" 3 | local LOG = require"p2g.log" 4 | local GIF = require "p2g.gif" 5 | local D2D = require "p2g.draw2d" 6 | local VRAM = require "p2g.vram" 7 | local DMA = require"p2g.dma" 8 | local GS = require"p2g.gs" 9 | local RM = require"p2g.buffer" 10 | 11 | local hotreloadDraw = require "eg.hotreload_draw" 12 | local H = hotreloadDraw.new(40, 40) 13 | 14 | local db = 0 15 | 16 | function PS2PROG.start() 17 | PS2PROG.logLevel(LOG.infoLevel) 18 | DMA.init(DMA.GIF) 19 | GS.setOutput(640, 448, GS.INTERLACED, GS.NTSC) 20 | local fb1 = VRAM.mem:framebuffer(640, 448, GS.PSM24, 2048) 21 | local fb2 = VRAM.mem:framebuffer(640, 448, GS.PSM24, 2048) 22 | local zb = VRAM.mem:framebuffer(640, 448, GS.PSMZ24, 2048) 23 | GS.setBuffers(fb1, fb2, zb) 24 | D2D:screenDimensions(640, 448) 25 | D2D:clearColour(0x2b, 0x2b, 0x2b) 26 | local db = RM.alloc(200 * 1024) 27 | D2D:bindBuffer(db) 28 | end 29 | 30 | function PS2PROG.frame() 31 | D2D:frameStart() 32 | 33 | H:draw() 34 | 35 | D2D:frameEnd() 36 | 37 | if PAD.held(PAD.CIRCLE) and db < 0 then 38 | PS2PROG.logLevel(LOG.traceLevel) 39 | reload"eg.hotreload_draw" 40 | PS2PROG.logLevel(LOG.infoLevel) 41 | db = 30 42 | end 43 | 44 | db = db - 1 45 | end 46 | 47 | 48 | -------------------------------------------------------------------------------- /script/eg/control.lua: -------------------------------------------------------------------------------- 1 | 2 | local GIF = require"p2g.gif" 3 | local P = require"p2g.const" 4 | local D2D = require"p2g.draw2d" 5 | local VRAM = require"p2g.vram" 6 | local LOG = require"p2g.log" 7 | local DMA = require"p2g.dma" 8 | local GS = require"p2g.gs" 9 | local RM = require"p2g.buffer" 10 | local PAD = require "p2g.pad" 11 | 12 | function PS2PROG.start() 13 | PS2PROG.logLevel(LOG.debugLevel) 14 | DMA.init(DMA.GIF) 15 | GS.setOutput(640, 448, GS.INTERLACED, GS.NTSC) 16 | local fb1 = VRAM.mem:framebuffer(640, 448, GS.PSM24, 2048) 17 | local fb2 = VRAM.mem:framebuffer(640, 448, GS.PSM24, 2048) 18 | local zb = VRAM.mem:framebuffer(640, 448, GS.PSMZ24, 2048) 19 | GS.setBuffers(fb1, fb2, zb) 20 | D2D:screenDimensions(640, 448) 21 | D2D:clearColour(0x2b, 0x2b, 0x2b) 22 | local db = RM.alloc(200 * 1024) 23 | D2D:bindBuffer(db) 24 | end 25 | 26 | local pp = { 27 | xx = 100, 28 | yy = 100, 29 | } 30 | 31 | function PS2PROG.frame() 32 | 33 | local dx = PAD.axis(PAD.axisLeftX) 34 | local dy = PAD.axis(PAD.axisLeftY) 35 | LOG.debug("dx = " .. dx .. ", dy = " .. dy) 36 | 37 | pp.xx = pp.xx + 5.2*dx 38 | pp.yy = pp.yy + 5.2*dy 39 | 40 | D2D:frameStart() 41 | D2D:setColour(255,0,0,0x80) 42 | D2D:rect(pp.xx, pp.yy, 24, 24) 43 | D2D:frameEnd() 44 | end 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/include/p2g/log.h: -------------------------------------------------------------------------------- 1 | #ifndef LOG_H 2 | #define LOG_H 3 | 4 | #include 5 | #include 6 | 7 | extern int log_output_level; 8 | 9 | #define LOG_LEVEL_ERROR 0 10 | #define LOG_LEVEL_WARN 1 11 | #define LOG_LEVEL_INFO 5 12 | #define LOG_LEVEL_DEBUG 9 13 | #define LOG_LEVEL_TRACE 15 14 | 15 | #ifndef LOG_NO_OUTPUT 16 | #define logmsg(lvl, i, msg, ...) \ 17 | do { \ 18 | if (log_output_level >= i) { \ 19 | printf(lvl " (%s:%d) " msg "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ 20 | } \ 21 | } while (0) 22 | #else 23 | #define logmsg(lvl, msg, ...) ((void)0) 24 | #endif 25 | 26 | #ifdef LOG_TRACE 27 | #define trace(msg, ...) logmsg("[TRCE]", LOG_LEVEL_TRACE, msg, ##__VA_ARGS__) 28 | #else 29 | #define trace(msg, ...) ((void)0) 30 | #endif 31 | 32 | #define info(msg, ...) logmsg("[INFO]", LOG_LEVEL_INFO, msg, ##__VA_ARGS__) 33 | #define logerr(msg, ...) logmsg("[ERRO]", LOG_LEVEL_ERROR, msg, ##__VA_ARGS__) 34 | #define logdbg(msg, ...) logmsg("[DEBG]", LOG_LEVEL_DEBUG, msg, ##__VA_ARGS__) 35 | 36 | int print_buffer(qword_t *b, int len); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /src/linux/include/draw_blending.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * Draw library blending functions 4 | */ 5 | 6 | #ifndef __DRAW_BLENDING_H__ 7 | #define __DRAW_BLENDING_H__ 8 | 9 | #include 10 | 11 | // color = (c1-c2)*a>>7 + c3 12 | 13 | /** Alpha Blending */ 14 | #define BLEND_COLOR_SOURCE 0 15 | #define BLEND_COLOR_DEST 1 16 | #define BLEND_COLOR_ZERO 2 17 | 18 | #define BLEND_ALPHA_SOURCE 0 19 | #define BLEND_ALPHA_DEST 1 20 | #define BLEND_ALPHA_FIXED 2 21 | 22 | /** Alpha Correction */ 23 | #define ALPHA_CORRECT_RGBA32 0 24 | #define ALPHA_CORRECT_RGBA16 1 25 | 26 | typedef struct { 27 | char color1; 28 | char color2; 29 | char alpha; 30 | char color3; 31 | unsigned char fixed_alpha; 32 | } blend_t; 33 | 34 | #ifdef __cplusplus 35 | extern "C" { 36 | #endif 37 | 38 | /** Alpha Blending Per-Pixel MSB Control */ 39 | qword_t *draw_pixel_alpha_control(qword_t *q, int enable); 40 | 41 | /** Alpha Blending */ 42 | qword_t *draw_alpha_blending(qword_t *q, int context, blend_t *blend); 43 | 44 | /** Alpha Correction */ 45 | qword_t *draw_alpha_correction(qword_t *q, int context, int alpha); 46 | 47 | #ifdef __cplusplus 48 | } 49 | #endif 50 | 51 | #endif /* __DRAW_BLENDING_H__ */ 52 | -------------------------------------------------------------------------------- /distfiles/spleen.font.LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018-2021, Frederic Cambus 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 18 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | POSSIBILITY OF SUCH DAMAGE. 25 | 26 | -------------------------------------------------------------------------------- /script/test/test_vec2.lua: -------------------------------------------------------------------------------- 1 | 2 | local M = require"p2g.math" 3 | local test = require"p2g.test" 4 | 5 | local suite = {name = "Vector 2D"} 6 | 7 | function suite.vec2_init() 8 | local v = M.vec2(5, 17) 9 | test.equal(v.buf:getFloat(0), 5) 10 | test.equal(v.buf:getFloat(1), 17) 11 | test.equal(v.x, 5) 12 | test.equal(v.y, 17) 13 | end 14 | 15 | function suite.vec2_copy() 16 | local v = M.vec2(111, 222) 17 | local other = M.vec2From(v) 18 | test.equal(v.x, other.x) 19 | test.equal(v.y, other.y) 20 | end 21 | 22 | function suite.vec2_add() 23 | local v1 = M.vec2(0, 77) 24 | local v2 = M.vec2(10, 20) 25 | local v3 = v1 + v2 26 | test.equal(v3, M.vec2(10, 97)) 27 | end 28 | 29 | function suite.vec2_sub() 30 | local v1 = M.vec2(10, 32) 31 | local v2 = M.vec2(7, 12) 32 | local v3 = v1 - v2 33 | test.equal(v3, M.vec2(3, 20)) 34 | end 35 | 36 | function suite.vec2_length() 37 | test.equal(M.vec2(0,0):length(), 0) 38 | test.equal(M.vec2(1, 0):length(), 1) 39 | test.equal(M.vec2(3, 4):length(), 5) 40 | end 41 | 42 | function suite.vec2_dot() 43 | local d1 = M.vec2(0,0):dot(M.vec2(7, 19)) 44 | test.equal(d1, 0) 45 | end 46 | 47 | function suite.vec2_scale() 48 | local v = M.vec2(4, 7) 49 | local n = v * 5 50 | test.equal(n, M.vec2(20, 35)) 51 | end 52 | 53 | function suite.vec2_normalize() 54 | local v = M.vec2(10, 7) 55 | v:normalize() 56 | test.equal(v, M.vec2(0.819232, 0.5734623)) 57 | end 58 | 59 | return suite 60 | -------------------------------------------------------------------------------- /script/test/test_slotlist.lua: -------------------------------------------------------------------------------- 1 | 2 | local SL = require"p2g.slotlist" 3 | local test = require"p2g.test" 4 | 5 | local suite = {name = "Slot List"} 6 | 7 | function suite.sl_init() 8 | local sl = SL.new(10) 9 | end 10 | 11 | function suite.sl_push() 12 | local sl = SL.new(10) 13 | local x = 5 14 | sl:push(5) 15 | end 16 | 17 | function suite.sl_each() 18 | local sl = SL.new(10) 19 | sl:push(5, 1) 20 | sl:push(6, 2) 21 | local c = 0 22 | sl:each(function(v, state, i) 23 | if i == 0 then 24 | test.equal(v, 5) 25 | test.equal(state, 1) 26 | elseif i == 1 then 27 | test.equal(v, 6) 28 | test.equal(state, 2) 29 | else 30 | test.fail("unexpected index " .. i) 31 | end 32 | c = c + 1 33 | end) 34 | test.equal(c, 2) 35 | end 36 | 37 | function suite.sl_each_state() 38 | local sl = SL.new(10) 39 | sl:push(5, 1) 40 | sl:push(6, 2) 41 | sl:eachState(2, function(v, state, i) 42 | if i == 1 then 43 | test.equal(v, 6) 44 | test.equal(state, 2) 45 | else 46 | test.fail("unexpected index " .. i) 47 | end 48 | end) 49 | end 50 | 51 | function suite.sl_clear() 52 | local sl = SL.new(10) 53 | for i=1,6,1 do 54 | sl:push(i, 3) 55 | end 56 | sl:each(function(v, state, i) 57 | test.equal(v, i+1) 58 | sl:setState(i, 1) 59 | end) 60 | local c = 0 61 | sl:eachState(1, function() 62 | c = c + 1 63 | end) 64 | test.equal(c, 6) 65 | end 66 | 67 | return suite 68 | -------------------------------------------------------------------------------- /asset/pipeline/obj2gif.py: -------------------------------------------------------------------------------- 1 | 2 | import struct 3 | import sys 4 | import logging 5 | import argparse 6 | 7 | import obj 8 | 9 | from random import random 10 | import math 11 | 12 | log = logging.Logger("obj2gif") 13 | log.setLevel(logging.DEBUG) 14 | ch = logging.StreamHandler() 15 | log.addHandler(ch) 16 | 17 | def get_colour(s): 18 | if s == None: 19 | return get_colour("0x70707080") 20 | n = int(s, 16) 21 | return ((n>>24)&0xff, (n>>16)&0xff, (n>>8)&0xff, n&0xff) 22 | 23 | parse = argparse.ArgumentParser(prog="obj2gif", description="converts a .obj to a PS2 GIF-ready binary file") 24 | parse.add_argument("file") 25 | parse.add_argument("-o", "--output") 26 | parse.add_argument("-c", "--colour") 27 | 28 | args = parse.parse_args() 29 | 30 | with open (args.file, "r") as f: 31 | o = obj.parse(f) 32 | count = 0 33 | rows = [] 34 | (r, g, blu, alpha) = get_colour(args.colour) 35 | for f in o.face_coords(): 36 | [a, b, c] = f 37 | rows.append(struct.pack(" 2 | #include 3 | 4 | #include 5 | 6 | // io.read_file(name, offset, size, target_buffer) 7 | static int io_read_file(lua_State *l) { 8 | const char *file_name = lua_tostring(l, 1); 9 | int read_offset = lua_tointeger(l, 2); 10 | int read_size = lua_tointeger(l, 3); 11 | lua_getfield(l, 4, "ptr"); 12 | void *ptr = lua_touserdata(l, -1); 13 | lua_getfield(l, 4, "size"); 14 | int buffer_size = lua_tointeger(l, -1); 15 | FILE *f = fopen(file_name, "rb"); 16 | if (!f) { 17 | return luaL_error(l, "open %s", file_name); 18 | } 19 | 20 | if (read_size > buffer_size) { 21 | fclose(f); 22 | return luaL_error(l, "buffer too small: got %d need %d", buffer_size, 23 | read_size); 24 | } 25 | fseek(f, read_offset, SEEK_SET); 26 | size_t rc = fread(ptr, 1, read_size, f); 27 | if (rc != read_size) { 28 | fclose(f); 29 | return luaL_error(l, "read %s", file_name); 30 | } 31 | 32 | fclose(f); 33 | return 0; 34 | } 35 | 36 | // io.file_size(name) 37 | static int io_file_size(lua_State *l) { 38 | const char *file_name = lua_tostring(l, 1); 39 | FILE *f = fopen(file_name, "rb"); 40 | if (!f) { 41 | return luaL_error(l, "open %s", file_name); 42 | } 43 | 44 | fseek(f, 0L, SEEK_END); 45 | size_t file_size = ftell(f); 46 | fseek(f, 0L, SEEK_SET); 47 | 48 | fclose(f); 49 | 50 | lua_pushinteger(l, file_size); 51 | return 1; 52 | } 53 | 54 | int io_lua_init(lua_State *l) { 55 | lua_createtable(l, 0, 2); 56 | lua_pushcfunction(l, io_file_size); 57 | lua_setfield(l, -2, "file_size"); 58 | lua_pushcfunction(l, io_read_file); 59 | lua_setfield(l, -2, "read_file"); 60 | return 1; 61 | } 62 | -------------------------------------------------------------------------------- /script/main.lua: -------------------------------------------------------------------------------- 1 | 2 | local LOG = require"p2g.log" 3 | local TEST = require"p2g.test" 4 | 5 | local entrypoint = "eg.cube" 6 | 7 | test_record = {} 8 | 9 | local test_files = { 10 | "test_vec2", 11 | "test_vec3", 12 | "test_mat3x3", 13 | "test_mat4x4", 14 | "test_slotlist", 15 | } 16 | function run_unit_tests() 17 | local ok = true 18 | local any_fail = false 19 | for _, name in ipairs(test_files) do 20 | local suite = require("test." .. name) 21 | local fail = false 22 | local record = {} 23 | LOG.info("== Running suite " .. suite.name) 24 | TEST.run_suite(suite.name, suite, { 25 | passed = function(s, case) 26 | table.insert(record, { 27 | ok = true, 28 | test_name = case, 29 | }) 30 | end, 31 | failed = function(s, case, err) 32 | fail = true 33 | any_fail = true 34 | LOG.error(" !! " .. case .. ": " .. tostring(err)) 35 | table.insert(record, { 36 | ok = false, 37 | test_name = case, 38 | err = err, 39 | }) 40 | end, 41 | }) 42 | test_record[suite.name] = record 43 | if fail then 44 | LOG.error("!! Tests failed !!") 45 | end 46 | end 47 | if any_fail then 48 | -- error("test failures") 49 | -- currently having segfault issues with pcall? 50 | -- so do this instead 51 | return true 52 | end 53 | end 54 | 55 | --[[ 56 | if run_unit_tests() == true then 57 | LOG.error("invalid startup: unit tests failed") 58 | entrypoint = "eg.tests" 59 | end 60 | --]] 61 | 62 | LOG.info("loading entrypoint: " .. entrypoint) 63 | 64 | require(entrypoint) 65 | return {} 66 | 67 | -- default entrypoint - do nothing and hang! 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/draw/buffer.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PS2_HOMEBREW_DRAW_BUFFER_H 3 | #define PS2_HOMEBREW_DRAW_BUFFER_H 4 | 5 | #include "draw.h" 6 | 7 | int giftag_new(struct commandbuffer *s, int flag, int nloop, int eop, int nregs, 8 | uint64_t regs); 9 | int giftag_ad_prim(struct commandbuffer *s, int, int, int, int); 10 | int giftag_ad_texflush(struct commandbuffer *s); 11 | int giftag_ad_bitbltbuf(struct commandbuffer *s, int dba, int dbw, 12 | uint64_t psm); 13 | int giftag_ad_trxpos(struct commandbuffer *s, int sx, uint64_t sy, uint64_t dx, 14 | uint64_t dy, uint64_t dir); 15 | 16 | int gif_ad(struct commandbuffer *s, uint64_t reg, uint64_t value); 17 | int giftag_ad_trxdir(struct commandbuffer *s, int dir); 18 | int giftag_ad_trxreg(struct commandbuffer *s, int rrw, int rrh); 19 | int giftag_ad_texa(struct commandbuffer *s, int ta0, int ta1); 20 | int giftag_ad_tex0(struct commandbuffer *s, 21 | int reg, int tbp, int tbw, int psm, int tw, int th, int tcc, int tfx); 22 | int giftag_ad_tex1(struct commandbuffer *s, int lcm, int mxl, int mtba, 23 | int l, int k); 24 | int giftag_ad_tex2(struct commandbuffer *s, int psm, int cbp, int cpsm, 25 | int csm, int csa, int cld); 26 | int giftag_ad_alpha(struct commandbuffer *s, int a, int b, int c, int d, 27 | int fix); 28 | 29 | 30 | int push_rgbaq(struct commandbuffer *s, const unsigned char cols[4]); 31 | int push_xyz2(struct commandbuffer *s, uint16_t x, uint16_t y, uint32_t z); 32 | int push_st(struct commandbuffer *state, float s, float t); 33 | 34 | int dma_tag(uint32_t *t, int qwc, int type, uint32_t addr); 35 | int vifcode(uint32_t *t, int op, int stall, int num, uint16_t imm); 36 | int vifcode_update_imm(uint16_t *t, uint16_t imm); 37 | int vifcode_update_num(uint8_t *t, uint8_t num); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /script/eg/math.lua: -------------------------------------------------------------------------------- 1 | 2 | local GIF = require"p2g.gif" 3 | local P = require"p2g.const" 4 | local D2D = require"p2g.draw2d" 5 | local VRAM = require"p2g.vram" 6 | local M = require"p2g.math" 7 | local LOG = require"p2g.log" 8 | local DMA = require"p2g.dma" 9 | local GS = require"p2g.gs" 10 | local RM = require"p2g.buffer" 11 | local PAD = require"p2g.pad" 12 | 13 | function PS2PROG.start() 14 | PS2PROG.logLevel(LOG.debugLevel) 15 | DMA.init(DMA.GIF) 16 | GS.setOutput(640, 448, GS.INTERLACED, GS.NTSC) 17 | local fb1 = VRAM.mem:framebuffer(640, 448, GS.PSM24, 2048) 18 | local fb2 = VRAM.mem:framebuffer(640, 448, GS.PSM24, 2048) 19 | local zb = VRAM.mem:framebuffer(640, 448, GS.PSMZ24, 2048) 20 | GS.setBuffers(fb1, fb2, zb) 21 | D2D:screenDimensions(640, 448) 22 | D2D:clearColour(0x2b, 0x2b, 0x2b) 23 | local db = RM.alloc(200 * 1024) 24 | D2D:bindBuffer(db) 25 | end 26 | 27 | local pos = M.vec2(100, 100) 28 | local angle = 0 29 | local fwd = M.vec2(1, 0) 30 | local rv = 0.07 31 | local v = 1.6 32 | 33 | local TAU = 2 * math.pi 34 | 35 | 36 | function stepPlayer() 37 | if PAD.held(PAD.LEFT) then 38 | angle = angle - rv 39 | elseif PAD.held(PAD.RIGHT) then 40 | angle = angle + rv 41 | end 42 | local velocity = 0 43 | if PAD.held(PAD.UP) then velocity = v end 44 | if angle > TAU then angle = angle - TAU end 45 | if angle < 0 then angle = angle + TAU end 46 | local work = M.vec2From(fwd) 47 | work:rotate(angle) 48 | work:scale(velocity) 49 | pos:add(work) 50 | end 51 | 52 | function PS2PROG.frame() 53 | D2D:frameStart() 54 | stepPlayer() 55 | D2D:setColour(255,0,0,0x80) 56 | D2D:rect(pos.x, pos.y, 20, 20) 57 | local w2 = M.vec2From(fwd) 58 | w2:rotate(angle) 59 | w2:scale(60) 60 | D2D:setColour(0, 255, 255, 0x80) 61 | D2D:rect(pos.x + w2.x + 10, pos.y + w2.y + 10, 4, 4) 62 | D2D:frameEnd() 63 | end 64 | 65 | 66 | -------------------------------------------------------------------------------- /script/eg/stress.lua: -------------------------------------------------------------------------------- 1 | local GIF = require"p2g.gif" 2 | local P = require"p2g.const" 3 | local D2D = require"p2g.draw2d" 4 | local VRAM = require"p2g.vram" 5 | local LOG = require"p2g.log" 6 | local DMA = require"p2g.dma" 7 | local GS = require"p2g.gs" 8 | local RM = require"p2g.buffer" 9 | 10 | local emt = { 11 | x=0,y=0,w=1,h=1, 12 | } 13 | function emt:draw() 14 | --D2D:rect(self.x, self.y, self.w, self.h) 15 | D2D:triangle(self.x, self.y, self.x+self.w, self.y, self.x, self.y+self.h) 16 | end 17 | 18 | function emt.new(x,y,w,h) 19 | return setmetatable({x=x,y=y,w=w,h=h}, {__index = emt}) 20 | end 21 | 22 | local scene = {} 23 | 24 | function PS2PROG.start() 25 | PS2PROG.logLevel(LOG.debugLevel) 26 | DMA.init(DMA.GIF) 27 | GS.setOutput(640, 448, GS.INTERLACED, GS.NTSC) 28 | local fb1 = VRAM.mem:framebuffer(640, 448, GS.PSM24, 256) 29 | local fb2 = VRAM.mem:framebuffer(640, 448, GS.PSM24, 256) 30 | local zb = VRAM.mem:framebuffer(640, 448, GS.PSM24, 256) 31 | GS.setBuffers(fb1, fb2, zb) 32 | D2D:clearColour(0x2b, 0x2b, 0x2b) 33 | D2D:screenDimensions(640, 448) 34 | 35 | local vr = VRAM:slice(VRAM.mem.head) 36 | 37 | local dd = 100 38 | local dx = math.floor(640/dd) 39 | local dy = math.floor(448/dd) 40 | for x=0,620,dx do 41 | for y=0,420,dy do 42 | tt = emt.new(x,y,dx,dy) 43 | table.insert(scene, tt) 44 | end 45 | end 46 | 47 | local db = RM.alloc(200*1024) 48 | D2D:bindBuffer(db) 49 | end 50 | 51 | local r = 0xff 52 | local g = 0xff 53 | local ffps = 0 54 | local fpsr = 10 55 | local cc = 0 56 | 57 | function PS2PROG.frame() 58 | D2D:frameStart() 59 | D2D:setColour(r,g,0,0x80) 60 | for i,s in ipairs(scene) do 61 | s:draw() 62 | end 63 | D2D:frameEnd() 64 | if r > 0 then 65 | g = 0xff 66 | r = 0xff 67 | else 68 | g = 0 69 | r = 0xff 70 | end 71 | LOG.info("FPS: " .. FPS) 72 | end 73 | 74 | 75 | -------------------------------------------------------------------------------- /script/p2g/font.lua: -------------------------------------------------------------------------------- 1 | 2 | local D2D = require"p2g.draw2d" 3 | 4 | local font = { 5 | texture = nil, 6 | charWidth = 8, 7 | charHeight = 14, 8 | charS = 0.03125, 9 | charT = 0.25 10 | } 11 | 12 | local function getCharacterIndex(i) 13 | if i >= 0 and i <= 32 then 14 | return i+96 15 | elseif i >= 33 and i <= 126 then 16 | return i-32 17 | else 18 | error("bad character index in string " .. i) 19 | end 20 | end 21 | 22 | function font:drawString(line, x, y) 23 | if line == nil then return end 24 | for i=1,#line,1 do 25 | local ci = getCharacterIndex(string.byte(line, i)) 26 | local ts = (ci % self.charsPerLine) * self.charS 27 | local tt = math.floor(ci/self.charsPerLine) * self.charT 28 | D2D:sprite(self.texture, x+(self.charWidth*i), y, self.charWidth, self.charHeight, ts, tt, 29 | ts + self.charS, tt + self.charT) 30 | end 31 | end 32 | 33 | function font:printLines(x, y, ...) 34 | for i, l in ipairs({...}) do 35 | self:drawString(l, x, y + ((i-1)*self.charHeight)) 36 | end 37 | end 38 | 39 | function font:centerPrint(x, y, width, str) 40 | local strWidth = string.len(str) * self.charWidth 41 | local sx = math.floor((width - strWidth) / 2) 42 | self:drawString(str, x + sx, y) 43 | end 44 | 45 | function font:loadToVram(vr) 46 | if self.resident == false then 47 | vr:texture(self.texture) 48 | D2D:uploadTexture(self.texture) 49 | self.resident = true 50 | end 51 | end 52 | 53 | function font:markUnloaded() 54 | self.resident = false 55 | end 56 | 57 | 58 | function font.new(texture, charWidth, charHeight) 59 | return setmetatable({ 60 | texture = texture, 61 | charWidth = charWidth, 62 | charHeight = charHeight, 63 | charS = charWidth / texture.width, 64 | charT = charHeight / texture.height, 65 | charsPerLine = 32, 66 | resident = false, 67 | }, { __index = font }) 68 | end 69 | 70 | return font 71 | 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # P2Garage Library 2 | 3 | Playstation 2 Homebrew for Humans 4 | 5 | ## What It Is 6 | 7 | This is a framework for creating interactive media on the Playstation 2. 8 | 9 | ## What This Is Not 10 | 11 | P2Garage is _NOT_ a game engine. This library does a lot to simplify building PS2 software. 12 | It has a lot of useful abstractions, makes graphics nice and provides a Lua scripting interface. 13 | to Playstation 2 hardware. The library does not have strong opinions on how to build a game. 14 | There are still lots of problems to solve. 15 | 16 | ## How To Use 17 | 18 | This framework is built on PS2SDK, a community SDK that can be installed from [here.](https://github.com/ps2dev/ps2dev). 19 | The Makefiles in this project assume Docker is installed and will use a docker image to compile everything. 20 | Building without Docker is supported, but not supported as a first-class way of compiling the project. 21 | 22 | Running `make dist` will compile the code and assemble some key assets. The `p2g.elf` file in the `/dist` folder 23 | can be executed on a PS2 using a homebrew launcher such as [wLaunchELF](https://github.com/ps2homebrew/wLaunchELF) 24 | or [ps2link](https://github.com/ps2dev/ps2link). It can also be run in the [PCSX2](https://pcsx2.net/) emulator. 25 | 26 | When `p2g.elf` is run, it initializes the Playstation 2 and calls the file in `script/main.lua`. This Lua file 27 | should be the entrypoint to your game/app/etc. Replace the default one with something. For an example of how I 28 | manage a repository for a game using this framework, see [this repo](https://github.com/phy1um/playstation2-minis). 29 | 30 | ## Current Status 31 | 32 | - [x] Lua Scripting 33 | - [x] 2D Renderer 34 | - [ ] Documentation 35 | - [ ] Asset Pipeline 36 | - [ ] 3D Renderer 37 | - [ ] Using Vector Units :D 38 | 39 | ## Contributors 40 | 41 | - Tom Marks: [website](https://coding.tommarks.xyz), [livestream](https://twitch.tv/phylum919) 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/linux/include/draw_primitives.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * Draw library primitive functions 4 | */ 5 | 6 | #ifndef __DRAW_PRIMITIVES_H__ 7 | #define __DRAW_PRIMITIVES_H__ 8 | 9 | #include 10 | 11 | /** Types */ 12 | #define PRIM_POINT 0x00 13 | #define PRIM_LINE 0x01 14 | #define PRIM_LINE_STRIP 0x02 15 | #define PRIM_TRIANGLE 0x03 16 | #define PRIM_TRIANGLE_STRIP 0x04 17 | #define PRIM_TRIANGLE_FAN 0x05 18 | #define PRIM_SPRITE 0x06 19 | 20 | /** Shading */ 21 | #define PRIM_SHADE_FLAT 0 22 | #define PRIM_SHADE_GOURAUD 1 23 | 24 | /** Texture Mapping Coordinates */ 25 | #define PRIM_MAP_ST 0 26 | #define PRIM_MAP_UV 1 27 | 28 | /** Fixed Color Value */ 29 | #define PRIM_UNFIXED 0 30 | #define PRIM_FIXED 1 31 | 32 | /** Primitive Override Control */ 33 | #define PRIM_OVERRIDE_ENABLE 0 34 | #define PRIM_OVERRIDE_DISABLE 1 35 | 36 | typedef struct { 37 | unsigned char type; 38 | unsigned char shading; 39 | unsigned char mapping; 40 | unsigned char fogging; 41 | unsigned char blending; 42 | unsigned char antialiasing; 43 | unsigned char mapping_type; 44 | unsigned char colorfix; 45 | } prim_t; 46 | 47 | #ifdef __cplusplus 48 | extern "C" { 49 | #endif 50 | 51 | /** Primitive Coordinate System offset */ 52 | qword_t *draw_primitive_xyoffset(qword_t *q, int context, float x, float y); 53 | 54 | /** Primitive Control */ 55 | qword_t *draw_primitive_override(qword_t *q, int mode); 56 | 57 | /** Overridden Primitive Attributes */ 58 | qword_t *draw_primitive_override_setting(qword_t *q, int context, prim_t *prim); 59 | 60 | #ifdef __cplusplus 61 | } 62 | #endif 63 | 64 | #endif /* __DRAW_PRIMITIVES_H__ */ 65 | -------------------------------------------------------------------------------- /src/core/bindings.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | typedef struct { 6 | const char *name; 7 | lua_CFunction open; 8 | } script_binding; 9 | 10 | int gs_lua_init(lua_State *l); 11 | int dma_lua_init(lua_State *l); 12 | int draw_lua_init(lua_State *l); 13 | int log_lua_init(lua_State *l); 14 | int tga_lua_init(lua_State *l); 15 | int io_lua_init(lua_State *l); 16 | int vu_lua_init(lua_State *l); 17 | int draw2d_lua_init(lua_State *l); 18 | int pad_lua_init(lua_State *l); 19 | int slotlist_lua_init(lua_State *l); 20 | int vec2_lua_init(lua_State *l); 21 | int vec3_lua_init(lua_State *l); 22 | int vec4_lua_init(lua_State *l); 23 | int mat3_lua_init(lua_State *l); 24 | int mat4_lua_init(lua_State *l); 25 | int floatmath_lua_init(lua_State *l); 26 | 27 | static script_binding SCRIPT_CORE_LIBS[] = { 28 | {"gs", gs_lua_init}, 29 | {"dma", dma_lua_init}, 30 | {"pad", pad_lua_init}, 31 | {"buffer", draw_lua_init}, 32 | {"log", log_lua_init}, 33 | {"tga", tga_lua_init}, 34 | {"io", io_lua_init}, 35 | {"vu", vu_lua_init}, 36 | {"slotlist", slotlist_lua_init}, 37 | {"draw2d", draw2d_lua_init}, 38 | {"math_vec2", vec2_lua_init}, 39 | {"math_vec3", vec3_lua_init}, 40 | {"math_vec4", vec4_lua_init}, 41 | {"math_mat3", mat3_lua_init}, 42 | {"math_mat4", mat4_lua_init}, 43 | {"math_misc", floatmath_lua_init}, 44 | }; 45 | 46 | int bind_core_libs(lua_State *l) { 47 | int num_libs = sizeof(SCRIPT_CORE_LIBS) / sizeof(script_binding); 48 | info("initializing %d core libraries", num_libs); 49 | lua_createtable(l, 0, num_libs); 50 | for (int i = 0; i < num_libs; i++) { 51 | script_binding *b = &SCRIPT_CORE_LIBS[i]; 52 | trace("init core lib %s", b->name); 53 | if (b->open(l) != 1) { 54 | logerr("failed to open library: %s", b->name); 55 | return 0; 56 | } 57 | lua_setfield(l, -2, b->name); 58 | trace("core set field: %s", b->name); 59 | } 60 | return 1; 61 | } 62 | -------------------------------------------------------------------------------- /asset/pipeline/extract_vu1_prog.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | ELF_SECTION_HEADER_COUNT_OFFSET = 0x30 4 | ELF_SECTION_HEADER_OFFSET = 0x20 5 | ELF_SECTION_STRING_TABLE_INDEX_OFFSET = 0x32 6 | SECTION_HEADER_SIZE = 10*4 7 | 8 | def get_cstring(b, offset): 9 | build = [] 10 | while b[offset] != 0: 11 | build.append(b[offset]) 12 | offset += 1 13 | return bytes(build).decode() 14 | 15 | if __name__ == '__main__': 16 | import sys 17 | if len(sys.argv) != 3: 18 | print(f"usage: {sys.argv[0]} ") 19 | sys.exit(1) 20 | with open(sys.argv[1], "rb") as f: 21 | elf_bin = f.read() 22 | section_header_offset = struct.unpack_from("= 0 and n < 9 then 13 | local buf = rawget(o, "buf") 14 | return buf:getFloat(n) 15 | else return mat3[k] 16 | end 17 | end 18 | 19 | function mat3.__newindex(o, k, v) 20 | local n = tonumber(k) 21 | if n ~= nil and n >= 0 and n < 9 then 22 | local buf = rawget(o, "buf") 23 | return buf:setFloat(n, v) 24 | else return rawset(o, k, v) 25 | end 26 | end 27 | 28 | function mat3:__tostring() 29 | base = "[" 30 | for i=0,8,1 do 31 | if i > 0 and i % 3 == 0 32 | then base = base .. "|" .. self[i] .. " " 33 | else base = base .. self[i] .. " " 34 | end 35 | end 36 | return base .. "]" 37 | end 38 | 39 | function mat3:__eq(other) 40 | for i=0,8,1 do 41 | if floatCmp(self[i], other[i]) == false then 42 | LOG.info("mismatch @ (" .. i .. ") " .. self[i] .. " -> " .. other[i]) 43 | return false 44 | end 45 | end 46 | return true 47 | end 48 | 49 | function mat3.new(x) 50 | local buf = RM.gcAlloc(4 * 9) 51 | if x == nil then 52 | -- if no args initialize to identity matrix 53 | M.identityMat3(buf) 54 | else 55 | -- otherwise copy from nested lists, ew 56 | for row=1,3,1 do 57 | for col=1,3,1 do 58 | buf:setFloat((row-1)*3 + (col-1), x[row][col]) 59 | end 60 | end 61 | end 62 | return setmetatable({buf = buf}, mat3) 63 | end 64 | 65 | function mat3:add(other) 66 | M.addMat3(self.buf, other.buf) 67 | end 68 | 69 | function mat3:mul(other) 70 | M.mulMat3(self.buf, other.buf) 71 | end 72 | 73 | function mat3:apply(v3) 74 | M.applyMat3(self.buf, v3.buf) 75 | end 76 | 77 | function mat3:get(col, row) 78 | return self[(row * 3) + col] 79 | end 80 | 81 | function mat3:copy(from) 82 | M.copyMat3(self.buf, from.buf) 83 | end 84 | 85 | return mat3 86 | -------------------------------------------------------------------------------- /script/eg/clut.lua: -------------------------------------------------------------------------------- 1 | local GIF = require"p2g.gif" 2 | local P = require"p2g.const" 3 | local D2D = require"p2g.draw2d" 4 | local VRAM = require"p2g.vram" 5 | local LOG = require"p2g.log" 6 | local DMA = require"p2g.dma" 7 | local GS = require"p2g.gs" 8 | local RM = require"p2g.buffer" 9 | local TGA = require"p2g.tga" 10 | 11 | local testTex = nil 12 | local pal = nil 13 | local texturesInVram = false 14 | local vr = nil 15 | 16 | function PS2PROG.start() 17 | PS2PROG.logLevel(LOG.debugLevel) 18 | testTex = TGA.from_file("host:picotiles4.tga", RM.alloc) 19 | --testTex.data:print() 20 | pal = TGA.from_file("host:bigpal.tga", RM.alloc) 21 | DMA.init(DMA.GIF) 22 | GS.setOutput(640, 448, GS.INTERLACED, GS.NTSC) 23 | local fb1 = VRAM.mem:framebuffer(640, 448, GS.PSM32, 2048) 24 | local fb2 = VRAM.mem:framebuffer(640, 448, GS.PSM32, 2048) 25 | local zb = VRAM.mem:framebuffer(640, 448, GS.PSMZ24, 2048) 26 | GS.setBuffers(fb1, fb2, zb) 27 | D2D:screenDimensions(640, 448) 28 | D2D:clearColour(0x2b, 0x2b, 0x2b) 29 | 30 | img = D2D.newTexture(64, 64, GS.PSM4) 31 | 32 | vr = VRAM.slice(VRAM.mem.head) 33 | vr:texture(pal) 34 | vr:texture(testTex) 35 | 36 | local drawbuffer = RM.alloc(200 * 1024) 37 | D2D:bindBuffer(drawbuffer) 38 | end 39 | 40 | function uploadTextures() 41 | if not texturesInVram then 42 | D2D:uploadTexture(pal) 43 | D2D:uploadTexture(testTex) 44 | texturesInVram = true 45 | end 46 | end 47 | 48 | function drawTile(x, y, i) 49 | local ix = i % 4 50 | local iy = math.floor(i/4) 51 | D2D:sprite(testTex, x*32, y*32, 32, 32, 0.25*ix, 0.25*iy, 0.25*ix + 0.25, 0.25*iy + 0.25) 52 | end 53 | 54 | function PS2PROG.frame() 55 | D2D:frameStart() 56 | uploadTextures() 57 | D2D:setColour(0x80,0x80,0x80,0x80) 58 | D2D:setClut(pal) 59 | for i=0,19,1 do 60 | for j=0,15,1 do 61 | drawTile(i, j, 0) 62 | end 63 | end 64 | for i=0,10,1 do 65 | drawTile(i, 0, 4) 66 | end 67 | drawTile(4, 9, 8) 68 | drawTile(5, 9, 9) 69 | drawTile(6, 9, 9) 70 | drawTile(7, 9, 10) 71 | D2D:frameEnd() 72 | end 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/core/pad.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define DEADZONE 0.2 5 | 6 | static int pad_lua_button_held(lua_State *l) { 7 | int button_id = lua_tointeger(l, 1); 8 | // TODO(Tom Marks): bounds check 9 | int v = button_held(button_id); 10 | // info("TEST BTN %d = %d", button_id, v); 11 | lua_pushboolean(l, v); 12 | return 1; 13 | } 14 | 15 | static int pad_lua_joy_value(lua_State *l) { 16 | int axis_id = lua_tointeger(l, 1); 17 | // logdbg("axis %d = %d", axis_id, joysticks[axis_id]); 18 | float joy_value = ((joy_axis_value(axis_id) - 127) * 1.0) / 127.0; 19 | if ((joy_value < 0 && joy_value > -DEADZONE) || 20 | (joy_value > 0 && joy_value < DEADZONE)) { 21 | lua_pushinteger(l, 0); 22 | } else { 23 | lua_pushnumber(l, joy_value); 24 | } 25 | return 1; 26 | } 27 | 28 | #define bind_int(v, name) \ 29 | lua_pushinteger(l, v); \ 30 | lua_setfield(l, -2, name) 31 | int pad_lua_init(lua_State *l) { 32 | if (pad_init() == -1) { 33 | lua_pushstring(l, "failed to init pad"); 34 | lua_error(l); 35 | return 1; 36 | } 37 | lua_createtable(l, 0, 5); 38 | lua_pushcfunction(l, pad_lua_button_held); 39 | lua_setfield(l, -2, "held"); 40 | lua_pushcfunction(l, pad_lua_joy_value); 41 | lua_setfield(l, -2, "axis"); 42 | bind_int(BUTTON_X, "X"); 43 | bind_int(BUTTON_SQUARE, "SQUARE"); 44 | bind_int(BUTTON_TRIANGLE, "TRIANGLE"); 45 | bind_int(BUTTON_CIRCLE, "CIRCLE"); 46 | bind_int(DPAD_LEFT, "LEFT"); 47 | bind_int(DPAD_RIGHT, "RIGHT"); 48 | bind_int(DPAD_UP, "UP"); 49 | bind_int(DPAD_DOWN, "DOWN"); 50 | bind_int(BUTTON_SELECT, "SELECT"); 51 | bind_int(BUTTON_START, "START"); 52 | bind_int(BUTTON_L1, "L1"); 53 | bind_int(BUTTON_L2, "L2"); 54 | bind_int(BUTTON_R1, "R1"); 55 | bind_int(BUTTON_R2, "R2"); 56 | bind_int(AXIS_LEFT_X, "axisLeftX"); 57 | bind_int(AXIS_LEFT_Y, "axisLeftY"); 58 | bind_int(AXIS_RIGHT_X, "axisRightX"); 59 | bind_int(AXIS_RIGHT_Y, "axisRightY"); 60 | return 1; 61 | } 62 | -------------------------------------------------------------------------------- /src/linux/vdma.c: -------------------------------------------------------------------------------- 1 | #define DMA_CNT 0x1 2 | #define DMA_REF 0x3 3 | #define DMA_END 0x7 4 | 5 | #include 6 | #include 7 | 8 | char *CHANNEL_NAMES[] = { 9 | "VIF0", "VIF1", "GIF", "??", "??", "??", "??", "??", "??", "??", "!!OOB!!", 10 | }; 11 | 12 | void channel_process(int chan_id, void *data, int len) { 13 | info("channel %s(%d): sink @ %p, size=%d", CHANNEL_NAMES[chan_id], chan_id, 14 | data, len); 15 | return; 16 | } 17 | 18 | int dma_channel_initialize(int chan, void *handler, int flags) { 19 | info("DMA chan init: %s(%d), handler=%p, flags=%x", CHANNEL_NAMES[chan], chan, 20 | handler, flags); 21 | return 0; 22 | } 23 | 24 | void dma_channel_fast_waits(int chan) { 25 | info("DMA chan %s(%d): fast waits enabled", CHANNEL_NAMES[chan], chan); 26 | } 27 | 28 | int dma_channel_send_normal(int chan, void *data, int qwc, int flags, int spr) { 29 | channel_process(chan, data, qwc * 16); 30 | return 0; 31 | } 32 | 33 | int dma_channel_send_chain(int chan, void *data, int data_size, int flags, 34 | int spr) { 35 | trace("dma send chain: %p (%d) -> channel %s(%d)", data, data_size * 16, 36 | CHANNEL_NAMES[chan], chan); 37 | int hb = 0; 38 | while (hb < data_size * 16) { 39 | uint32_t t0 = *((uint32_t *)(data + hb)); 40 | uint32_t addr = *((uint32_t *)(data + hb + 4)); 41 | unsigned int qwc = t0 & 0xffff; 42 | unsigned int type = (t0 >> 28) & 0x7; 43 | trace("parse dma tag: %08X (addr = %04X qwc = %04X type = %02X", t0, addr, 44 | qwc, type); 45 | switch (type) { 46 | case DMA_CNT: 47 | channel_process(chan, data + hb + 8, qwc * 16); 48 | hb += (qwc + 1) * 16; 49 | break; 50 | case DMA_REF: 51 | channel_process(chan, ((void *)&addr), qwc * 16); 52 | hb += 8; 53 | break; 54 | case DMA_END: 55 | return 0; 56 | default: 57 | logerr("unsupported DMA type: %d", type); 58 | hb += 8; 59 | break; 60 | } 61 | } 62 | logerr("ERROR: DMA chain overrun"); 63 | return 1; 64 | } 65 | 66 | void dma_wait_fast() { info("DMA wait (fast)..."); } 67 | -------------------------------------------------------------------------------- /src/core/log.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | void lua_log_message(lua_State *l, int level, const char *pre) { 9 | if (log_output_level >= level) { 10 | lua_Debug ar; 11 | lua_getstack(l, 1, &ar); 12 | lua_getinfo(l, "Sl", &ar); 13 | size_t srclen = strlen(ar.short_src); 14 | int last_sep = last_index_of(ar.short_src, srclen, '/'); 15 | const char *lua_msg = lua_tostring(l, 1); 16 | printf("%s (%s:%d) %s\n", pre, (ar.short_src + last_sep + 1), 17 | ar.currentline, lua_msg); 18 | } 19 | } 20 | 21 | static int lua_log_trace(lua_State *l) { 22 | #ifdef LOG_TRACE 23 | lua_log_message(l, LOG_LEVEL_TRACE, "[TRCE]"); 24 | #endif 25 | return 0; 26 | } 27 | 28 | static int lua_log_debug(lua_State *l) { 29 | lua_log_message(l, LOG_LEVEL_DEBUG, "[DEBG]"); 30 | return 0; 31 | } 32 | static int lua_log_info(lua_State *l) { 33 | lua_log_message(l, LOG_LEVEL_INFO, "[INFO]"); 34 | return 0; 35 | } 36 | 37 | static int lua_log_warn(lua_State *l) { 38 | lua_log_message(l, LOG_LEVEL_WARN, "[WARN]"); 39 | return 0; 40 | } 41 | 42 | static int lua_log_error(lua_State *l) { 43 | lua_log_message(l, LOG_LEVEL_ERROR, "[ERRO]"); 44 | return 0; 45 | } 46 | 47 | int log_lua_init(lua_State *l) { 48 | lua_createtable(l, 0, 10); 49 | lua_pushcfunction(l, lua_log_error); 50 | lua_setfield(l, -2, "error"); 51 | lua_pushcfunction(l, lua_log_warn); 52 | lua_setfield(l, -2, "warn"); 53 | lua_pushcfunction(l, lua_log_info); 54 | lua_setfield(l, -2, "info"); 55 | lua_pushcfunction(l, lua_log_debug); 56 | lua_setfield(l, -2, "debug"); 57 | lua_pushcfunction(l, lua_log_trace); 58 | lua_setfield(l, -2, "trace"); 59 | 60 | lua_pushinteger(l, LOG_LEVEL_ERROR); 61 | lua_setfield(l, -2, "errorLevel"); 62 | lua_pushinteger(l, LOG_LEVEL_WARN); 63 | lua_setfield(l, -2, "warnLevel"); 64 | lua_pushinteger(l, LOG_LEVEL_INFO); 65 | lua_setfield(l, -2, "infoLevel"); 66 | lua_pushinteger(l, LOG_LEVEL_DEBUG); 67 | lua_setfield(l, -2, "debugLevel"); 68 | lua_pushinteger(l, LOG_LEVEL_TRACE); 69 | lua_setfield(l, -2, "traceLevel"); 70 | 71 | return 1; 72 | } 73 | -------------------------------------------------------------------------------- /script/math/mat4.lua: -------------------------------------------------------------------------------- 1 | local M = P2GCORE.math_mat4 2 | local floatCmp = P2GCORE.math_misc.floatCmp 3 | local RM = require"p2g.buffer" 4 | local LOG = require"p2g.log" 5 | 6 | local mat4 = {} 7 | 8 | function mat4.__index(o, k) 9 | local n = tonumber(k) 10 | if n ~= nil and n >= 0 and n < 16 then 11 | local buf = rawget(o, "buf") 12 | return buf:getFloat(n) 13 | else return mat4[k] 14 | end 15 | end 16 | 17 | function mat4.__newindex(o, k, v) 18 | local n = tonumber(k) 19 | if n ~= nil and n >= 0 and n < 16 then 20 | local buf = rawget(o, "buf") 21 | return buf:setFloat(n, v) 22 | else return rawset(o, k, v) 23 | end 24 | end 25 | 26 | function mat4:__tostring() 27 | base = "[" 28 | for i=0,15,1 do 29 | if i > 0 and i % 4 == 0 30 | then base = base .. "|" .. self[i] .. " " 31 | else base = base .. self[i] .. " " 32 | end 33 | end 34 | return base .. "]" 35 | end 36 | 37 | function mat4:__eq(other) 38 | for i=0,15,1 do 39 | if floatCmp(self[i], other[i]) == false then 40 | LOG.info("mismatch @ (" .. i .. ") " .. self[i] .. " -> " .. other[i]) 41 | return false 42 | end 43 | end 44 | return true 45 | end 46 | 47 | function mat4.new(x) 48 | local buf = RM.gcAlloc(4 * 16) 49 | if x == nil then 50 | -- if no args initialize to identity matrix 51 | M.identityMat4(buf) 52 | else 53 | -- otherwise copy from nested lists, ew 54 | for row=1,4,1 do 55 | for col=1,4,1 do 56 | buf:setFloat((row-1)*4 + (col-1), x[row][col]) 57 | end 58 | end 59 | end 60 | return setmetatable({buf = buf}, mat4) 61 | end 62 | 63 | function mat4:set(x,y,v) 64 | self.buf:setFloat(x + (4*y), v) 65 | end 66 | 67 | function mat4:add(other) 68 | M.addMat4(self.buf, other.buf) 69 | end 70 | 71 | function mat4:mul(other) 72 | M.mulMat4(self.buf, other.buf) 73 | end 74 | 75 | function mat4:apply(v4) 76 | M.applyMat4(self.buf, v4.buf) 77 | end 78 | 79 | function mat4:get(col, row) 80 | return self[(row * 4) + col] 81 | end 82 | 83 | function mat4:copy(from) 84 | M.copyMat4(self.buf, from.buf) 85 | end 86 | 87 | function mat4:identity() 88 | M.identityMat4(self.buf) 89 | end 90 | 91 | return mat4 92 | 93 | -------------------------------------------------------------------------------- /script/p2g/vram.lua: -------------------------------------------------------------------------------- 1 | local LOG = require"p2g.log" 2 | local GS = require"p2g.gs" 3 | 4 | local vram = {} 5 | local basePtr = 0 6 | -- maximum is 4mb 7 | local max = math.floor(4 * 1024 * 1024) 8 | vram.max = max 9 | 10 | local vramslice = { 11 | start= 0, 12 | head = 0, 13 | tail = max, 14 | } 15 | 16 | function vramslice:alloc(b, align) 17 | local out = self.head 18 | --out = out + align - (out%align) 19 | while out % align ~= 0 do out = out + 1 end 20 | if out + b >= max then 21 | LOG.error("vram buffer overflow") 22 | error("vram buffer overflow") 23 | end 24 | self.head = out + b 25 | LOG.trace("vram alloc: " .. out .. " base -> " .. basePtr) 26 | return out 27 | end 28 | 29 | function vramslice:framebuffer(w, h, psm, align) 30 | -- NOTE: this size has to be in words rather than bytes for some reason 31 | local sz = vram.size(w, h, psm, align)/4 32 | local fb = { 33 | address = self:alloc(sz, align), 34 | width = w, 35 | height = h, 36 | format = psm, 37 | } 38 | LOG.debug("VRAM framebuffer -- @ " .. fb.address .. ", size = " .. sz) 39 | return fb 40 | end 41 | 42 | function vramslice:texture(tex) 43 | --local size = vram.size(tex.width, tex.height, tex.psm, 256) 44 | local size = tex.width*tex.height*4 45 | tex.basePtr = self:alloc(size, 256) 46 | LOG.debug("allocated texture in VRAM: " .. tex.fname .. " @ " .. tex.basePtr) 47 | return tex 48 | end 49 | 50 | function vramslice:reset() 51 | self.head = self.start 52 | end 53 | 54 | function vramslice:copy() 55 | return vram.slice(self.start, self.tail) 56 | end 57 | 58 | function vram.slice(start, tail) 59 | if tail == nil then tail = max end 60 | return setmetatable({ 61 | start=start, 62 | head=start, 63 | tail=tail 64 | }, 65 | { __index=vramslice } 66 | ) 67 | end 68 | 69 | function vram.size(w, h, psm, align) 70 | local size = w*h 71 | if psm == GS.PSM32 or psm == GS.PSM24 then 72 | size = w*h*4 73 | elseif psm == GS.PSM16 or psm == GS.PSM16S or psm == GS.PSMZ16 or psm == GS.PSMZ16S then 74 | size = w*h*2 75 | elseif psm == GS.PSM4 then 76 | size = math.floor(w*h*0.5) 77 | end 78 | 79 | LOG.trace("VRAM sizeof " .. w .. ", " .. h .. " @" .. psm .. " :: " .. size) 80 | 81 | return size 82 | end 83 | 84 | vram.mem = vram.slice(0, max) 85 | 86 | return vram 87 | -------------------------------------------------------------------------------- /script/p2g/init.lua: -------------------------------------------------------------------------------- 1 | 2 | local LOG = P2GCORE.log 3 | 4 | dbgPrint("p2g.init begin") 5 | 6 | -- Setup search path scripts in PS2 environment 7 | local SEARCH_PATH = P2G_ROOT .. "script/?.lua" 8 | dbgPrint("search path = " .. SEARCH_PATH) 9 | package.path = SEARCH_PATH 10 | 11 | -- Define a function to block the EE Core, sometimes 12 | -- useful for debugging. This will break ps2link reset 13 | function PS2PROG.spinForever() 14 | while true do local c = 1+1 end 15 | end 16 | 17 | -- Setup hot-reload 18 | dbgPrint("setup hot reloading") 19 | local hotReload = require"p2g.hot" 20 | require, reload = hotReload.init() 21 | 22 | -- Allow for requiring core C code 23 | local function coreLoad(as) 24 | dbgPrint("hard loading P2G core: " .. as) 25 | package.loaded["p2g." .. as] = P2GCORE[as] 26 | end 27 | 28 | coreLoad("log") 29 | coreLoad("dma") 30 | coreLoad("gs") 31 | coreLoad("io") 32 | coreLoad("vu") 33 | coreLoad("buffer") 34 | coreLoad("pad") 35 | 36 | -- Get a reference to the Lua scripted draw2d renderer before we override require 37 | local d2d = require("p2g.draw2d") 38 | 39 | -- Create hacked logging require with P2G library hooks 40 | local trueRequire = require 41 | 42 | function require(p) 43 | LOG.trace("-> require " .. p) 44 | local out = trueRequire(p) 45 | LOG.trace("<- require " .. p) 46 | return out 47 | end 48 | 49 | local wrappedRequire = require 50 | 51 | dbgPrint("setup hacked require") 52 | function require(p) 53 | -- Return scripted draw2d if the flag is set 54 | if p == "p2g.draw2d" then 55 | if PS2PROG.slow2d == true then 56 | return d2d 57 | else 58 | return P2GCORE.draw2d 59 | end 60 | end 61 | return wrappedRequire(p) 62 | end 63 | 64 | -- Do some initial draw2d setup 65 | dbgPrint("setup fast draw2d") 66 | P2GCORE.draw2d.loadTexture = d2d.loadTexture 67 | P2GCORE.draw2d.newTexture = d2d.newTexture 68 | P2GCORE.draw2d.vramAllocTexture = d2d.vramAllocTexture 69 | 70 | -- Initialize TGA library constants from GS constants 71 | dbgPrint("setup TGA constants") 72 | local TGA = P2GCORE.tga 73 | local GS = P2GCORE.gs 74 | TGA.BPS_TO_PSM = {} 75 | TGA.BPS_TO_PSM[4] = GS.PSM4 76 | TGA.BPS_TO_PSM[8] = GS.PSM8 77 | TGA.BPS_TO_PSM[16] = GS.PSM16 78 | TGA.BPS_TO_PSM[24] = GS.PSM24 79 | TGA.BPS_TO_PSM[32] = GS.PSM32 80 | 81 | dbgPrint("p2g.init end") 82 | 83 | return function() end 84 | 85 | -------------------------------------------------------------------------------- /src/examples/rect.c: -------------------------------------------------------------------------------- 1 | #define _EE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | // PS2SDK deps 9 | #include 10 | #include 11 | #include 12 | 13 | // normal deps 14 | #include 15 | 16 | #define SCR_WIDTH 640 17 | #define SCR_HEIGHT 448 18 | 19 | static uint32_t VRAM_HEAD = 0; 20 | static uint32_t VRAM_SIZE = 4 * 1024 * 1024; 21 | uint32_t vram_alloc(uint32_t size, uint32_t align) { 22 | trace("vram alloc: %d", size); 23 | while (VRAM_HEAD % align != 0) { 24 | VRAM_HEAD += 1; 25 | } 26 | uint32_t out = VRAM_HEAD; 27 | if (out > VRAM_SIZE) { 28 | p2g_fatal("VRAM overflow"); 29 | } 30 | VRAM_HEAD += size; 31 | return out; 32 | } 33 | 34 | int main(int argc, char *argv[]) { 35 | log_output_level = LOG_LEVEL_TRACE; 36 | 37 | gs_init(); 38 | dma_channel_initialize(DMA_CHANNEL_GIF, 0, 0); 39 | dma_channel_fast_waits(DMA_CHANNEL_GIF); 40 | 41 | gs_set_output(SCR_WIDTH, SCR_HEIGHT, GRAPH_MODE_INTERLACED, GRAPH_MODE_NTSC, 42 | GRAPH_MODE_FIELD, GRAPH_DISABLE); 43 | 44 | int fb1 = 45 | vram_alloc(gs_framebuffer_size(SCR_WIDTH, SCR_HEIGHT, GS_PSM_32), 2048); 46 | int fb2 = 47 | vram_alloc(gs_framebuffer_size(SCR_WIDTH, SCR_HEIGHT, GS_PSM_32), 2048); 48 | 49 | int zbuf = 50 | vram_alloc(gs_framebuffer_size(SCR_WIDTH, SCR_HEIGHT, GS_PSMZ_16), 2048); 51 | trace("allocated buffers: FB1=%x, FB2=%x, ZB=%x", fb1, fb2, zbuf); 52 | gs_set_fields(SCR_WIDTH, SCR_HEIGHT, GS_PSM_32, GS_PSMZ_16, fb1 / 4, fb2 / 4, 53 | zbuf / 4); 54 | 55 | void *draw_buffer_static = calloc(1, 200 * 1024); 56 | // setup drawbuffer 57 | draw_bind_buffer(draw_buffer_static, 200 * 1024); 58 | 59 | draw2d_screen_dimensions(SCR_WIDTH, SCR_HEIGHT); 60 | 61 | int frame_count = 0; 62 | 63 | while (1) { 64 | trace("START FRAME: %d", frame_count); 65 | dma_wait_fast(); 66 | trace("FRAME START"); 67 | draw_frame_start(); 68 | draw2d_set_colour(255, 0, 0, 0x80); 69 | draw2d_rect(100., 80., 100., 80.); 70 | trace("FRAME END"); 71 | draw_frame_end(); 72 | trace("WAIT FINISH"); 73 | draw_wait_finish(); 74 | trace("WAIT VSYNC: (%d)", frame_count); 75 | graph_wait_vsync(); 76 | trace("FLIP"); 77 | gs_flip(); 78 | frame_count += 1; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /script/math/vec2.lua: -------------------------------------------------------------------------------- 1 | local RM = require"p2g.buffer" 2 | local M = P2GCORE.math_vec2 3 | local floatCmp = P2GCORE.math_misc.floatCmp 4 | 5 | local vec2 = {} 6 | 7 | function vec2.__index(o, k) 8 | if k == "x" then 9 | local buf = rawget(o, "buf") 10 | return buf:getFloat(0) 11 | elseif k == "y" then 12 | local buf = rawget(o, "buf") 13 | return buf:getFloat(1) 14 | else 15 | return vec2[k] 16 | end 17 | end 18 | 19 | function vec2.__newindex(o, k, v) 20 | if k == "x" then 21 | local b = rawget(o, "buf") 22 | b:setFloat(0, v) 23 | elseif k == "y" then 24 | local b = rawget(o, "buf") 25 | b:setFloat(1, v) 26 | else 27 | rawset(o, k, v) 28 | end 29 | end 30 | 31 | function vec2:__eq(other) 32 | return floatCmp(self.x, other.x) and floatCmp(self.y, other.y) 33 | end 34 | 35 | function vec2:__tostring() 36 | return "[" .. self.x .. "," .. self.y .. "]" 37 | end 38 | 39 | function vec2.__add(a, b) 40 | local n = vec2.from(a) 41 | n:add(b) 42 | return n 43 | end 44 | 45 | function vec2.__sub(a, b) 46 | local n = vec2.from(a) 47 | n:sub(b) 48 | return n 49 | end 50 | 51 | function vec2.__mul(a, s) 52 | local n = vec2.from(a) 53 | n:scale(s) 54 | return n 55 | end 56 | 57 | function vec2.new(x,y) 58 | local buf = RM.gcAlloc(4 * 2) 59 | local o = {buf = buf} 60 | setmetatable(o, vec2) 61 | o.x = x 62 | o.y = y 63 | return o 64 | end 65 | 66 | function vec2.from(other) 67 | local n = vec2.new(0, 0) 68 | n:copy(other) 69 | return n 70 | end 71 | 72 | function vec2:add(other) 73 | M.addVec2(self.buf, other.buf) 74 | end 75 | 76 | function vec2:sub(other) 77 | M.subVec2(self.buf, other.buf) 78 | end 79 | 80 | function vec2:copy(from) 81 | M.copyVec2(self.buf, from.buf) 82 | end 83 | 84 | function vec2:length() 85 | return M.lenVec2(self.buf) 86 | end 87 | 88 | function vec2:dot(other) 89 | return M.dotVec2(self.buf, other.buf) 90 | end 91 | 92 | function vec2:rotate(theta) 93 | M.rotateVec2(self.buf, theta) 94 | end 95 | 96 | function vec2:copyRotate(theta) 97 | local n = vec2.from(self) 98 | n:rotate(theta) 99 | return n 100 | end 101 | 102 | function vec2:scale(s) 103 | M.scaleVec2(self.buf, s) 104 | end 105 | 106 | function vec2:normalize() 107 | M.normalizeVec2(self.buf) 108 | end 109 | 110 | return vec2 111 | -------------------------------------------------------------------------------- /script/p2g/camera.lua: -------------------------------------------------------------------------------- 1 | local LOG = require"p2g.log" 2 | local M = require"p2g.math" 3 | 4 | local C = { 5 | pos = M.vec3(), 6 | facing = M.vec3(), 7 | aspect = 1, 8 | near = 0.1, 9 | far = 100, 10 | fov = 0.2, 11 | _view_matrix = M.mat4(), 12 | _proj_matrix = M.mat4(), 13 | view_dirty = false, 14 | proj_dirty = false, 15 | } 16 | 17 | function C.new(x,y,z,opts) 18 | local camera = { 19 | pos = M.vec3(x,y,z), 20 | facing = M.vec3(0,0,-1), 21 | aspect = opts.aspect or 4/3, 22 | fov = opts.fov or 0.7, 23 | near = opts.near or 0.1, 24 | far = opts.far or 100, 25 | _view_matrix = M.mat4(), 26 | _proj_matrix = M.mat4(), 27 | -- true when these matricies require recalculation 28 | view_dirty = true, 29 | proj_dirty = true, 30 | } 31 | return setmetatable(camera, { __index = C }) 32 | end 33 | 34 | function C:set_fov(fov) 35 | self.fov = fov 36 | self.proj_dirty = true 37 | end 38 | 39 | function C:set_aspect(ar) 40 | self.aspect = ar 41 | self.proj_dirty = true 42 | end 43 | 44 | function C:move_to(x,y,z) 45 | self.pos:set(x,y,z) 46 | self.view_dirty = true 47 | end 48 | 49 | function C:step(dx,dy,dz) 50 | self.pos.x = self.pos.x + dx 51 | self.pos.y = self.pos.y + dy 52 | self.pos.z = self.pos.z + dz 53 | self.view_dirty = true 54 | end 55 | 56 | function C:look_at(x,y,z) 57 | self.facing:set(x - self.pos.x, y-self.pos.y, z-self.pos.z) 58 | self.facing:normalize() 59 | self.view_dirty = true 60 | end 61 | 62 | function C:view_matrix() 63 | if self.view_dirty == true then 64 | -- TODO: lookat 65 | self._view_matrix:identity() 66 | self._view_matrix:set(3,0,-self.pos.x) 67 | self._view_matrix:set(3,1,-self.pos.y) 68 | self._view_matrix:set(3,2,-self.pos.z) 69 | self.view_dirty = false 70 | end 71 | return self._view_matrix 72 | end 73 | 74 | function C:projection_matrix() 75 | if self.proj_dirty then 76 | self._proj_matrix:identity() 77 | local near = self.near 78 | local far = self.far 79 | local top = near*math.tan(self.fov/2) 80 | local right = top*self.aspect 81 | self._proj_matrix:set(0, 0, near/right) 82 | self._proj_matrix:set(1, 1, near/top) 83 | self._proj_matrix:set(2, 2, (far+near)/(far-near)) 84 | self._proj_matrix:set(2, 3, (-2*far*near)/(far - near)) 85 | self._proj_matrix:set(3, 2, 1) 86 | end 87 | return self._proj_matrix 88 | end 89 | 90 | return C 91 | -------------------------------------------------------------------------------- /src/linux/include/gs_privileged.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * GS Privileged Registers 4 | */ 5 | 6 | #ifndef __GS_PRIVILEGED_H__ 7 | #define __GS_PRIVILEGED_H__ 8 | 9 | #include 10 | 11 | // These are the privileged GS registers mapped to main ram 12 | // These are modified directly without the use of dma 13 | 14 | /** PCRTC Mode Setting */ 15 | #define GS_REG_PMODE (volatile u64 *)0x12000000 16 | /** VHP,VCKSEL,SLCK2,NVCK,CLKSEL,PEVS,PEHS,PVS,PHS,GCONT,SPML,PCK2,XPCK,SINT, 17 | * PRST,EX,CMOD,SLCK,T1248,LC,RC */ 18 | #define GS_REG_SMODE1 (volatile u64 *)0x12000010 19 | /** Setting For Modes Related to Video Synchronization */ 20 | #define GS_REG_SMODE2 (volatile u64 *)0x12000020 21 | /** DRAM Refresh Settings */ 22 | #define GS_REG_SRFSH (volatile u64 *)0x12000030 23 | /** HS,HSVS,HSEQ,HBP,HFP */ 24 | #define GS_REG_SYNCH1 (volatile u64 *)0x12000040 25 | /** HB,HF */ 26 | #define GS_REG_SYNCH2 (volatile u64 *)0x12000050 27 | /** VS,VDP,VBPE,VBP,VFPE,VFP */ 28 | #define GS_REG_SYNCHV (volatile u64 *)0x12000060 29 | /** Setting For Rectangular Area Read Output Circuit 1 */ 30 | #define GS_REG_DISPFB1 (volatile u64 *)0x12000070 31 | /** Setting For Rectangular Area Read Output Circuit 1 */ 32 | #define GS_REG_DISPLAY1 (volatile u64 *)0x12000080 33 | /** Setting For Rectangular Area Read Output Circuit 2 */ 34 | #define GS_REG_DISPFB2 (volatile u64 *)0x12000090 35 | /** Setting For Rectangular Area Read Output Circuit 2 */ 36 | #define GS_REG_DISPLAY2 (volatile u64 *)0x120000A0 37 | /** Setting For Feedback Buffer Write Buffer */ 38 | #define GS_REG_EXTBUF (volatile u64 *)0x120000B0 39 | /** Feedback Write Setting */ 40 | #define GS_REG_EXTDATA (volatile u64 *)0x120000C0 41 | /** Feedback Write Function Control */ 42 | #define GS_REG_EXTWRITE (volatile u64 *)0x120000D0 43 | /** Background Color Setting */ 44 | #define GS_REG_BGCOLOR (volatile u64 *)0x120000E0 45 | /** System Status */ 46 | #define GS_REG_CSR (volatile u64 *)0x12001000 47 | /** Interrupt Mask Control */ 48 | #define GS_REG_IMR (volatile u64 *)0x12001010 49 | /** Host I/F Bus Switching */ 50 | #define GS_REG_BUSDIR (volatile u64 *)0x12001040 51 | /** Signal ID Value Read */ 52 | #define GS_REG_SIGLBLID (volatile u64 *)0x12001080 53 | 54 | 55 | #define GS_SET_CSR(a, b, c, d, e, f, g, h, i, j, k, l) 0 56 | 57 | #endif /* __GS_PRIVILEGED_H__ */ 58 | -------------------------------------------------------------------------------- /src/ps2luaprog.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | int ps2luaprog_is_running = 0; 11 | 12 | static int ps2luaprog_start_nil(lua_State *l) { 13 | info("default start..."); 14 | return 0; 15 | } 16 | 17 | static int ps2luaprog_frame_nil(lua_State *l) { return 0; } 18 | 19 | static int ps2lua_log2(lua_State *l) { 20 | int n = lua_tointeger(l, 1); 21 | float res = log2f(n); 22 | lua_pushnumber(l, res); 23 | return 1; 24 | } 25 | 26 | int ps2luaprog_set_log_level(lua_State *l) { 27 | log_output_level = lua_tointeger(l, 1); 28 | logdbg("updated log level = %d", log_output_level); 29 | return 0; 30 | } 31 | 32 | int ps2luaprog_get_log_level(lua_State *l) { 33 | lua_pushinteger(l, log_output_level); 34 | return 1; 35 | } 36 | 37 | int ps2luaprog_init(lua_State *l) { 38 | lua_createtable(l, 0, 2); 39 | lua_pushcfunction(l, ps2luaprog_start_nil); 40 | lua_setfield(l, -2, "start"); 41 | lua_pushcfunction(l, ps2luaprog_frame_nil); 42 | lua_setfield(l, -2, "frame"); 43 | lua_pushcfunction(l, ps2luaprog_set_log_level); 44 | lua_setfield(l, -2, "logLevel"); 45 | lua_pushcfunction(l, ps2luaprog_get_log_level); 46 | lua_setfield(l, -2, "get_log_level"); 47 | lua_setglobal(l, "PS2PROG"); 48 | lua_pushcfunction(l, ps2lua_log2); 49 | lua_setglobal(l, "log2"); 50 | return 0; 51 | } 52 | 53 | int ps2luaprog_onstart(lua_State *l) { 54 | BENCH_START(timer); 55 | lua_getglobal(l, "PS2PROG"); 56 | lua_pushstring(l, "start"); 57 | lua_gettable(l, -2); 58 | 59 | int rc = lua_pcall(l, 0, 0, 0); 60 | if (rc) { 61 | const char *err = lua_tostring(l, -1); 62 | luaL_traceback(l, l, err, 1); 63 | const char *traceback = lua_tostring(l, -1); 64 | logerr("lua execution error (start event)\n%s", traceback); 65 | return rc; 66 | } 67 | BENCH_INFO(timer, " PS2PROG onstart in %f"); 68 | 69 | return 0; 70 | } 71 | 72 | int ps2luaprog_onframe(lua_State *l) { 73 | trace("PS2LUAPROG.frame called from C"); 74 | lua_getglobal(l, "PS2PROG"); 75 | lua_pushstring(l, "frame"); 76 | lua_gettable(l, -2); 77 | 78 | int rc = lua_pcall(l, 0, 0, 0); 79 | 80 | if (rc) { 81 | const char *err = lua_tostring(l, -1); 82 | luaL_traceback(l, l, err, 1); 83 | const char *traceback = lua_tostring(l, -1); 84 | logerr("lua execution error (frame event)\n%s", traceback); 85 | return rc; 86 | } 87 | 88 | return 0; 89 | } 90 | -------------------------------------------------------------------------------- /script/eg/io.lua: -------------------------------------------------------------------------------- 1 | -- hello world 2 | 3 | local GIF = require"p2g.gif" 4 | local P = require"p2g.const" 5 | local D2D = require"p2g.draw2d" 6 | local VRAM = require"p2g.vram" 7 | local DMA = require"p2g.dma" 8 | local GS = require"p2g.gs" 9 | local RM = require"p2g.buffer" 10 | local LOG = require"p2g.log" 11 | local FONT = require"p2g.font" 12 | local IO = require"p2g.io" 13 | local TGA = require"p2g.tga" 14 | 15 | -- https://stackoverflow.com/questions/1426954/split-string-in-lua 16 | local function mysplit (inputstr, sep) 17 | if sep == nil then 18 | sep = "%s" 19 | end 20 | local t={} 21 | for str in string.gmatch(inputstr, "([^"..sep.."]+)") do 22 | table.insert(t, str) 23 | end 24 | return t 25 | end 26 | 27 | 28 | function read_chars(buf, offset) 29 | if offset > buf.size then 30 | error("out of bounds: " .. offset) 31 | end 32 | local v = buf:read(offset) 33 | local b0 = v & 0xff 34 | local b1 = (v >> 8)&0xff 35 | local b2 = (v >> 16)&0xff 36 | local b3 = (v >> 24)&0xff 37 | return string.char(b0,b1,b2,b3) 38 | end 39 | 40 | function read_n(buf, offset, n) 41 | local b = {} 42 | for i=0,n do 43 | local c4 = read_chars(buf, (i*4)+offset) 44 | table.insert(b, c4) 45 | end 46 | return table.concat(b, "") 47 | end 48 | 49 | function PS2PROG.start() 50 | PS2PROG.logLevel(LOG.debugLevel) 51 | DMA.init(DMA.GIF) 52 | GS.setOutput(640, 448, GS.INTERLACED, GS.NTSC) 53 | local fb1 = VRAM.mem:framebuffer(640, 448, GS.PSM24, 2048) 54 | local fb2 = VRAM.mem:framebuffer(640, 448, GS.PSM24, 2048) 55 | local zb = VRAM.mem:framebuffer(640, 448, GS.PSMZ24, 2048) 56 | GS.setBuffers(fb1, fb2, zb) 57 | D2D:screenDimensions(640, 448) 58 | D2D:clearColour(0x2b, 0x2b, 0x2b) 59 | local db = RM.alloc(200 * 1024) 60 | D2D:bindBuffer(db) 61 | 62 | local fontTexture = TGA.from_file("bigfont.tga", RM.alloc) 63 | font = FONT.new(fontTexture, 8, 16) 64 | 65 | vram = VRAM.slice(VRAM.mem.head) 66 | 67 | size = IO.file_size("script/eg/io.lua") 68 | LOG.info("file size = " .. size) 69 | local file_contents = RM.alloc(size) 70 | LOG.info("allocated buffer, reading file") 71 | IO.read_file("script/eg/io.lua", 0, size, file_contents) 72 | result = mysplit(read_n(file_contents, 0, math.floor(size/4)), "\n") 73 | end 74 | 75 | function PS2PROG.frame() 76 | D2D:frameStart() 77 | font:loadToVram(vram) 78 | D2D:setColour(255,255,255,0x80) 79 | font:printLines(10, 10, table.unpack(result)) 80 | D2D:frameEnd() 81 | end 82 | 83 | 84 | -------------------------------------------------------------------------------- /asset/pipeline/obj.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | log = logging.Logger("obj") 4 | log.setLevel(logging.DEBUG) 5 | ch = logging.StreamHandler() 6 | log.addHandler(ch) 7 | 8 | 9 | def get_vert_index(e): 10 | p = e.split("/") 11 | return int(p[0]) - 1 12 | 13 | def face_from_line(parts): 14 | if len(parts) == 3: 15 | a = get_vert_index(parts[0]) 16 | b = get_vert_index(parts[1]) 17 | c = get_vert_index(parts[2]) 18 | return [Face(a,b,c)] 19 | elif len(parts) == 4: 20 | a = get_vert_index(parts[0]) 21 | b = get_vert_index(parts[1]) 22 | c = get_vert_index(parts[2]) 23 | d = get_vert_index(parts[3]) 24 | return [Face(a,b,c), Face(d,b,c)] 25 | else: 26 | raise Exception(f"face must be tri or quad, got {len(parts)}") 27 | 28 | 29 | class Face(object): 30 | def __init__(self, a, b, c): 31 | self.a = a 32 | self.b = b 33 | self.c = c 34 | 35 | def vert_from_line(parts): 36 | if len(parts) != 3: 37 | raise Exception("Vertices must be 3D") 38 | x = float(parts[0]) 39 | y = float(parts[1]) 40 | z = float(parts[2]) 41 | return Vert(x,y,z) 42 | 43 | class Vert(object): 44 | def __init__(self, x, y, z): 45 | self.x = x 46 | self.y = y 47 | self.z = z 48 | def __str__(self): 49 | return f"({self.x}, {self.y}, {self.z})" 50 | def __repr__(self): 51 | return self.__str__() 52 | 53 | def test_bounds(l, i): 54 | if i < 0 or i >= len(l): 55 | raise Exception(f"index {i} out of bounds [0, {len(l)}]") 56 | 57 | 58 | class ParsedObj(object): 59 | def __init__(self): 60 | self._v = [] 61 | self._f = [] 62 | def add_vert(self, v): 63 | self._v.append(v) 64 | def add_faces(self, f): 65 | self._f += f 66 | def faces(self): 67 | return self._f.__iter__() 68 | def face_coords(self): 69 | for f in self.faces(): 70 | test_bounds(self._v, f.a) 71 | test_bounds(self._v, f.b) 72 | test_bounds(self._v, f.c) 73 | yield [self._v[f.a], self._v[f.b], self._v[f.c]] 74 | 75 | 76 | def parse(f): 77 | res = ParsedObj() 78 | for line in f: 79 | parts = line.split(" ") 80 | if parts[0] == "v": 81 | res.add_vert( vert_from_line(parts[1:]) ) 82 | elif parts[0] == "f": 83 | res.add_faces( face_from_line(parts[1:]) ) 84 | else: 85 | log.info(f"unsupported line type: {parts[0]}") 86 | return res 87 | 88 | -------------------------------------------------------------------------------- /script/eg/cube_instance.lua: -------------------------------------------------------------------------------- 1 | local LOG = require"p2g.log" 2 | local M = require"p2g.math" 3 | local DRAW = require"p2g.draw2d" 4 | 5 | local function scale(m, n) 6 | m:set(0,0, n) 7 | m:set(1,1, n) 8 | m:set(2,2, n) 9 | m:set(3,3, 1) 10 | return m 11 | end 12 | 13 | local function translate(m, ox, oy, oz) 14 | m:set(3,0,ox*10) 15 | m:set(3,1,oy*10) 16 | m:set(3,2,oz*10) 17 | return m 18 | end 19 | 20 | local function rotate_x(m, rx) 21 | m:set(1,1, math.cos(-rx)) 22 | m:set(1,2, -math.sin(-rx)) 23 | m:set(2,1, math.sin(-rx)) 24 | m:set(2,2, math.cos(-rx)) 25 | return m 26 | end 27 | 28 | local function rotate_y(m, ry) 29 | m:set(0,0, math.cos(-ry)) 30 | m:set(0,2, math.sin(-ry)) 31 | m:set(2,0, -math.sin(-ry)) 32 | m:set(2,2, math.cos(-ry)) 33 | return m 34 | end 35 | 36 | local Instance = { 37 | pos = nil, 38 | rot_x = nil, 39 | rot_y = nil, 40 | scale = 1, 41 | mesh = nil, 42 | verts = 0, 43 | vert_size = 0, 44 | } 45 | 46 | function Instance.new(mesh, verts, vert_size) 47 | return setmetatable({ 48 | pos = M.vec3(0,0,0), 49 | rot_x = 0, 50 | rot_y = 0, 51 | scale = 1, 52 | mesh = mesh, 53 | verts = verts, 54 | vert_size = vert_size 55 | }, {__index=Instance}) 56 | end 57 | 58 | function Instance:move_to(x,y,z) 59 | self.pos:set(x,y,z) 60 | end 61 | 62 | function Instance:step(dx,dy,dz) 63 | self.pos.x = self.pos.x + dx 64 | self.pos.y = self.pos.y + dy 65 | self.pos.z = self.pos.z + dz 66 | end 67 | 68 | function Instance:rotate(rx, ry) 69 | self.rot_x = self.rot_x + rx 70 | self.rot_y = self.rot_y + ry 71 | end 72 | 73 | function Instance:set_scale(s) 74 | self.scale = s 75 | end 76 | 77 | function Instance:model_matrix() 78 | local m = M.mat4() 79 | m:mul(translate(M.mat4(), self.pos.x, self.pos.y, self.pos.z)) 80 | m:mul(rotate_x(M.mat4(), self.rot_x)) 81 | m:mul(rotate_y(M.mat4(), self.rot_y)) 82 | m:mul(scale(M.mat4(), self.scale)) 83 | return m 84 | end 85 | 86 | function Instance.draw_all(instances, cam) 87 | for i, inst in ipairs(instances) do 88 | local mvp = M.mat4() 89 | mvp:mul(cam:projection_matrix()) 90 | mvp:mul(cam:view_matrix()) 91 | mvp:mul(inst:model_matrix()) 92 | LOG.trace(":: " .. tostring(mvp)) 93 | --LOG.trace("@ " .. tostring(cam.pos)) 94 | local mo = DRAW:mesh_cnt(inst.mesh, inst.verts, inst.vert_size) 95 | DRAW:ee_transform(mvp.buf, mo, inst.verts, inst.vert_size, 4) 96 | end 97 | end 98 | 99 | return Instance 100 | 101 | -------------------------------------------------------------------------------- /src/math/mat4lua.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #define buffer_null_check(p, s) \ 7 | do { \ 8 | if (!p) { \ 9 | lua_pushstring(l, "got null buffer for " s); \ 10 | lua_error(l); \ 11 | return 1; \ 12 | } \ 13 | } while (0) 14 | 15 | #define get_buf(i, to, msg) \ 16 | lua_pushstring(l, "ptr"); \ 17 | lua_gettable(l, i); \ 18 | float *to = lua_touserdata(l, -1); \ 19 | buffer_null_check(to, msg " [" #i "]"); \ 20 | ((void)0) 21 | 22 | static int lua_add_mat4(lua_State *l) { 23 | get_buf(1, buf1, "add m3"); 24 | get_buf(2, buf2, "add m3"); 25 | 26 | p2m_m4_add(buf1, buf2); 27 | 28 | return 0; 29 | } 30 | 31 | static int lua_copy_mat4(lua_State *l) { 32 | get_buf(1, buf1, "copy m4"); 33 | get_buf(2, buf2, "copy m4"); 34 | 35 | p2m_m4_copy(buf1, buf2); 36 | 37 | return 0; 38 | } 39 | 40 | static int lua_identity_mat4(lua_State *l) { 41 | get_buf(1, buf1, "idetity m4"); 42 | p2m_m4_identity(buf1); 43 | return 0; 44 | } 45 | 46 | static int lua_multiply_mat4(lua_State *l) { 47 | get_buf(1, buf1, "mul m4"); 48 | get_buf(2, buf2, "mul m4"); 49 | p2m_m4_multiply(buf1, buf2, buf1); 50 | return 0; 51 | } 52 | 53 | static int lua_apply_mat4(lua_State *l) { 54 | get_buf(1, buf1, "apply m4"); 55 | get_buf(2, buf2, "apply m4 (v4)"); 56 | p2m_m4_apply(buf1, buf2); 57 | return 0; 58 | } 59 | 60 | int mat4_lua_init(lua_State *l) { 61 | trace("init core lib math.mat4"); 62 | lua_createtable(l, 0, 8); 63 | lua_pushcfunction(l, lua_add_mat4); 64 | lua_setfield(l, -2, "addMat4"); 65 | lua_pushcfunction(l, lua_copy_mat4); 66 | lua_setfield(l, -2, "copyMat4"); 67 | lua_pushcfunction(l, lua_identity_mat4); 68 | lua_setfield(l, -2, "identityMat4"); 69 | lua_pushcfunction(l, lua_multiply_mat4); 70 | lua_setfield(l, -2, "mulMat4"); 71 | lua_pushcfunction(l, lua_apply_mat4); 72 | lua_setfield(l, -2, "applyMat4"); 73 | return 1; 74 | } 75 | -------------------------------------------------------------------------------- /script/eg/tests.lua: -------------------------------------------------------------------------------- 1 | local GIF = require"p2g.gif" 2 | local P = require"p2g.const" 3 | local D2D = require"p2g.draw2d" 4 | local VRAM = require"p2g.vram" 5 | local DMA = require"p2g.dma" 6 | local GS = require"p2g.gs" 7 | local RM = require"p2g.buffer" 8 | local LOG = require"p2g.log" 9 | local FONT = require"p2g.font" 10 | local PAD = require"p2g.pad" 11 | local TGA = require"p2g.tga" 12 | 13 | local camera = { 14 | x = 0, 15 | y = 0, 16 | } 17 | 18 | function PS2PROG.start() 19 | PS2PROG.logLevel(LOG.debugLevel) 20 | DMA.init(DMA.GIF) 21 | GS.setOutput(640, 448, GS.INTERLACED, GS.NTSC) 22 | local fb1 = VRAM.mem:framebuffer(640, 448, GS.PSM24, 2048) 23 | local fb2 = VRAM.mem:framebuffer(640, 448, GS.PSM24, 2048) 24 | local zb = VRAM.mem:framebuffer(640, 448, GS.PSMZ24, 2048) 25 | vram = VRAM.slice(VRAM.mem.head) 26 | GS.setBuffers(fb1, fb2, zb) 27 | D2D:screenDimensions(640, 448) 28 | D2D:clearColour(0x2b, 0x2b, 0x2b) 29 | local db = RM.alloc(200 * 1024) 30 | D2D:bindBuffer(db) 31 | 32 | local fontTexture = TGA.from_file("bigfont.tga", RM.alloc) 33 | font = FONT.new(fontTexture, 8, 16) 34 | end 35 | 36 | local function clip_print(x, y, ...) 37 | local screen_x = x - camera.x 38 | local screen_y = y - camera.y 39 | if screen_x < -16 or screen_y < -16 or screen_x > 640 or screen_y > 448 then 40 | return 41 | end 42 | font:printLines(screen_x, screen_y, ...) 43 | end 44 | 45 | local function printTestCase(offset_x, offset_y, suite_name, record) 46 | D2D:setColour(255,255,255,0x80) 47 | clip_print(offset_x, offset_y, "Tests for " .. suite_name) 48 | local offset_y = offset_y + 16 49 | for case, result in pairs(record) do 50 | local sx = offset_x + 10 51 | if result.ok then 52 | D2D:setColour(0, 255, 0, 0x80) 53 | clip_print(sx, offset_y, "PASS") 54 | sx = sx + font.charWidth * 5 55 | else 56 | D2D:setColour(255, 0, 0, 0x80) 57 | clip_print(sx, offset_y, "FAIL") 58 | sx = sx + font.charWidth * 5 59 | end 60 | D2D:setColour(110,110,110,0x80) 61 | clip_print(sx, offset_y, "case " .. result.test_name) 62 | offset_y = offset_y + 16 63 | end 64 | return offset_y 65 | end 66 | 67 | 68 | function PS2PROG.frame() 69 | D2D:frameStart() 70 | font:loadToVram(vram) 71 | 72 | local yy = 10 73 | for name,record in pairs(test_record) do 74 | yy = printTestCase(10, yy, name, record) 75 | end 76 | 77 | if PAD.held(PAD.DOWN) then 78 | camera.y = camera.y + 2 79 | end 80 | if PAD.held(PAD.UP) then 81 | camera.y = camera.y - 2 82 | end 83 | 84 | 85 | D2D:frameEnd() 86 | end 87 | 88 | 89 | -------------------------------------------------------------------------------- /src/math/mat3lua.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #define buffer_null_check(p, s) \ 7 | do { \ 8 | if (!p) { \ 9 | lua_pushstring(l, "got null buffer for " s); \ 10 | lua_error(l); \ 11 | return 1; \ 12 | } \ 13 | } while (0) 14 | 15 | #define get_buf(i, to, msg) \ 16 | lua_pushstring(l, "ptr"); \ 17 | lua_gettable(l, i); \ 18 | float *to = lua_touserdata(l, -1); \ 19 | buffer_null_check(to, msg " [" #i "]"); \ 20 | ((void)0) 21 | 22 | static int lua_add_mat3(lua_State *l) { 23 | get_buf(1, buf1, "add m3"); 24 | get_buf(2, buf2, "add m3"); 25 | 26 | p2m_m3_add(buf1, buf2); 27 | 28 | return 0; 29 | } 30 | 31 | // local c = copy(to, from) 32 | static int lua_copy_mat3(lua_State *l) { 33 | get_buf(1, buf1, "copy m3"); 34 | get_buf(2, buf2, "copy m3"); 35 | 36 | p2m_m3_copy(buf1, buf2); 37 | 38 | return 0; 39 | } 40 | 41 | static int lua_identity_mat3(lua_State *l) { 42 | get_buf(1, buf1, "idetity m3"); 43 | p2m_m3_identity(buf1); 44 | return 0; 45 | } 46 | 47 | static int lua_multiply_mat3(lua_State *l) { 48 | get_buf(1, buf1, "mul m3"); 49 | get_buf(2, buf2, "mul m3"); 50 | p2m_m3_multiply(buf1, buf2, buf1); 51 | return 0; 52 | } 53 | 54 | static int lua_apply_mat3(lua_State *l) { 55 | get_buf(1, buf1, "apply m3"); 56 | get_buf(2, buf2, "apply m3 (v3)"); 57 | p2m_m3_apply(buf1, buf2); 58 | return 0; 59 | } 60 | 61 | int mat3_lua_init(lua_State *l) { 62 | trace("init core lib math.mat3"); 63 | lua_createtable(l, 0, 8); 64 | lua_pushcfunction(l, lua_add_mat3); 65 | lua_setfield(l, -2, "addMat3"); 66 | lua_pushcfunction(l, lua_copy_mat3); 67 | lua_setfield(l, -2, "copyMat3"); 68 | lua_pushcfunction(l, lua_identity_mat3); 69 | lua_setfield(l, -2, "identityMat3"); 70 | lua_pushcfunction(l, lua_multiply_mat3); 71 | lua_setfield(l, -2, "mulMat3"); 72 | lua_pushcfunction(l, lua_apply_mat3); 73 | lua_setfield(l, -2, "applyMat3"); 74 | return 1; 75 | } 76 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | PS2SDK=/usr/local/ps2dev/ps2sdk 2 | DVP-AS:=dvp-as 3 | HOST_OBJS=$(CORE_OBJ) main.o ps2luaprog.o liblua.a 4 | MAIN_OBJ=utils.o gs.o pad.o core.o 5 | CORE_OBJ=core/bindings.o core/buffer.o core/dma.o core/draw.o core/gslua.o core/log.o core/pad.o core/slotlist.o core/tga.o core/io.o core/vu.o math/vec2lua.o math/vec3lua.o math/mat3lua.o math/float.o math/mat4lua.o math/vec4lua.o 6 | DRAW_OBJ=draw/draw2d.o draw/common.o draw/buffer.o draw/draw3d.o draw/vu.o 7 | MATH_OBJ=ps2math.o 8 | EXAMPLES=examples/rect.c 9 | 10 | # EE_OBJS=$(MAIN_OBJ) $(CORE_OBJ) $(DRAW_OBJ) $(MATH_OBJ) liblua.a 11 | EE_OBJS=$(MAIN_OBJ) $(DRAW_OBJ) $(MATH_OBJ) 12 | EE_LIBS=-ldma -lgraph -ldraw -lkernel -ldebug -lmath3d -lm -lpad 13 | EE_CFLAGS+=-O0 -Wall --std=c99 -Wno-sign-compare -g -fno-strict-aliasing -fno-exceptions -DLUA_USE_PS2 -DLOG_TRACE 14 | 15 | EE_OBJS:=$(addprefix $(PREFIX), $(EE_OBJS)) 16 | HOST_OBJS:=$(addprefix $(PREFIX), $(HOST_OBJS)) 17 | 18 | EE_INCS+=-I$(PS2SDK)/ports/include -I./$(PREFIX)include 19 | EE_LDFLAGS=-L$(PSDSDK)/ee/common/lib -L$(PS2SDK)/ee/lib -L$(PS2SDK)/ports/lib 20 | 21 | SIM_OBJS=vdma.o vkernel.o vdraw.o vgraphics.o vpad.o draw_environment.o draw_clear.o 22 | SIM_OBJS:=$(addprefix $(PREFIX)linux/, $(SIM_OBJS)) 23 | 24 | SIM_BIN=$(PREFIX)test.exe 25 | 26 | DEBUG_OPTS=-DLOG_TRACE 27 | P2G_LIB=$(PREFIX)libp2g.a 28 | 29 | EXAMPLES_SRC=$(wildcard $(PREFIX)examples/*.c) 30 | EXAMPLES_BIN=$(patsubst %.c,%.elf,$(EXAMPLES_SRC)) 31 | 32 | ifeq ($(platform), PS2) 33 | $(info platform=PS2 detected) 34 | include $(PS2SDK)/samples/Makefile.eeglobal 35 | include $(PS2SDK)/samples/Makefile.pref 36 | else 37 | INCS=-I $(PREFIX)linux/include -I./$(PREFIX)include 38 | %.o: %.c 39 | $(CC) $(EE_CFLAGS) $(INCS) -c -o $@ $< 40 | endif 41 | 42 | vu1prog/%.vuobj: vu1prog/%.asm 43 | $(DVP-AS) $(DVP_FLAGS) -o $@ $< 44 | 45 | # TODO(Tom Marks): update ISO building to include everything in dist/ 46 | $(ISO_TGT): $(EE_BIN) 47 | mkisofs $(ISO_FLAGS) -o $(ISO_TGT) ./dist/* 48 | 49 | $(SIM_BIN): $(EE_OBJS) $(SIM_OBJS) 50 | $(CC) $(EE_CFLAGS) -llua -lm -o $@ $(EE_OBJS) $(SIM_OBJS) 51 | 52 | 53 | .PHONY: clean 54 | clean: 55 | rm -rf $(EE_BIN) $(EE_OBJS) $(HOST_OBJS) $(SIM_BIN) $(SIM_OBJS) 56 | 57 | $(P2G_LIB): $(EE_OBJS) 58 | $(EE_AR) rcs $@ $(EE_OBJS) 59 | 60 | $(PREFIX)p2g.elf: $(PREFIX)libp2g.a $(EE_OBJS) $(HOST_OBJS) 61 | $(EE_CC) -T$(EE_LINKFILE) $(EE_OPTFLAGS) -o $@ $(EE_OBJS) $(HOST_OBJS) $(EE_LDFLAGS) $(EXTRA_LDFLAGS) $(EE_LIBS) 62 | 63 | $(PREFIX)examples/%.elf: $(PREFIX)/examples/%.c $(P2G_LIB) 64 | $(EE_CC) -T$(EE_LINKFILE) $(DEBUG_OPTS) $(EE_INCS) $(EE_OPTFLAGS) -o $@ $< $(P2G_LIB) $(EE_LDFLAGS) $(EXTRA_LDFLAGS) $(EE_LIBS) 65 | 66 | examples: $(EXAMPLES_BIN) 67 | -------------------------------------------------------------------------------- /script/math/vec3.lua: -------------------------------------------------------------------------------- 1 | local vec3 = {} 2 | local RM = require"p2g.buffer" 3 | local M = P2GCORE.math_vec3 4 | local floatCmp = P2GCORE.math_misc.floatCmp 5 | 6 | 7 | function vec3.__index(o, k) 8 | if k == "x" then 9 | local buf = rawget(o, "buf") 10 | return buf:getFloat(0) 11 | elseif k == "y" then 12 | local buf = rawget(o, "buf") 13 | return buf:getFloat(1) 14 | elseif k == "z" then 15 | local buf = rawget(o, "buf") 16 | return buf:getFloat(2) 17 | else 18 | return vec3[k] 19 | end 20 | end 21 | 22 | function vec3.__newindex(o, k, v) 23 | if k == "x" then 24 | local b = rawget(o, "buf") 25 | b:setFloat(0, v) 26 | elseif k == "y" then 27 | local b = rawget(o, "buf") 28 | b:setFloat(1, v) 29 | elseif k == "z" then 30 | local b = rawget(o, "buf") 31 | b:setFloat(2, v) 32 | else 33 | rawset(o, k, v) 34 | end 35 | end 36 | 37 | function vec3:__eq(other) 38 | return floatCmp(self.x, other.x) and floatCmp(self.y, other.y) and floatCmp(self.z, other.z) 39 | end 40 | 41 | 42 | function vec3:__tostring() 43 | return "[" .. self.x .. "," .. self.y .. "," .. self.z .. "]" 44 | end 45 | 46 | function vec3.__add(a, b) 47 | local n = vec3.from(a) 48 | n:add(b) 49 | return n 50 | end 51 | 52 | function vec3.__sub(a, b) 53 | local n = vec3.from(a) 54 | n:sub(b) 55 | return n 56 | end 57 | 58 | function vec3.__mul(a, s) 59 | local n = vec3.from(a) 60 | n:scale(s) 61 | return n 62 | end 63 | 64 | function vec3.new(x,y, z) 65 | local buf = RM.gcAlloc(4 * 3) 66 | local o = {buf = buf} 67 | setmetatable(o, vec3) 68 | o.x = x 69 | o.y = y 70 | o.z = z 71 | return o 72 | end 73 | 74 | function vec3.from(other) 75 | local n = vec3.new(0, 0) 76 | n:copy(other) 77 | return n 78 | end 79 | 80 | function vec3:set(x, y, z) 81 | -- avoid slower index func that checks for .x .y and .z 82 | local buf = rawget(self, "buf") 83 | buf:setFloat(0,x) 84 | buf:setFloat(1,y) 85 | buf:setFloat(2,z) 86 | end 87 | 88 | function vec3:add(other) 89 | M.addVec3(self.buf, other.buf) 90 | end 91 | 92 | function vec3:sub(other) 93 | M.subVec3(self.buf, other.buf) 94 | end 95 | 96 | function vec3:copy(from) 97 | M.copyVec3(self.buf, from.buf) 98 | end 99 | 100 | function vec3:length() 101 | return M.lenVec3(self.buf) 102 | end 103 | 104 | function vec3:dot(other) 105 | return M.dotVec3(self.buf, other.buf) 106 | end 107 | 108 | function vec3:scale(s) 109 | M.scaleVec3(self.buf, s) 110 | end 111 | 112 | function vec3:normalize() 113 | M.normalizeVec3(self.buf) 114 | end 115 | 116 | return vec3 117 | 118 | -------------------------------------------------------------------------------- /script/math/vec4.lua: -------------------------------------------------------------------------------- 1 | local vec4 = {} 2 | local RM = require"p2g.buffer" 3 | local M = P2GCORE.math_vec4 4 | local floatCmp = P2GCORE.math_misc.floatCmp 5 | 6 | 7 | function vec4.__index(o, k) 8 | if k == "x" then 9 | local buf = rawget(o, "buf") 10 | return buf:getFloat(0) 11 | elseif k == "y" then 12 | local buf = rawget(o, "buf") 13 | return buf:getFloat(1) 14 | elseif k == "z" then 15 | local buf = rawget(o, "buf") 16 | return buf:getFloat(2) 17 | elseif k == "w" then 18 | local buf = rawget(o, "buf") 19 | return buf:getFloat(3) 20 | else 21 | return vec4[k] 22 | end 23 | end 24 | 25 | function vec4.__newindex(o, k, v) 26 | if k == "x" then 27 | local b = rawget(o, "buf") 28 | b:setFloat(0, v) 29 | elseif k == "y" then 30 | local b = rawget(o, "buf") 31 | b:setFloat(1, v) 32 | elseif k == "z" then 33 | local b = rawget(o, "buf") 34 | b:setFloat(2, v) 35 | elseif k == "w" then 36 | local b = rawget(o, "buf") 37 | b:setFloat(3, v) 38 | else 39 | rawset(o, k, v) 40 | end 41 | end 42 | 43 | function vec4:__eq(other) 44 | return floatCmp(self.x, other.x) and floatCmp(self.y, other.y) and floatCmp(self.z, other.z) and floatCmp(self.w, other.w) 45 | end 46 | 47 | 48 | function vec4:__tostring() 49 | return "[" .. self.x .. "," .. self.y .. "," .. self.z .. "," .. self.w .. "]" 50 | end 51 | 52 | function vec4.__add(a, b) 53 | local n = vec4.from(a) 54 | n:add(b) 55 | return n 56 | end 57 | 58 | function vec4.__sub(a, b) 59 | local n = vec4.from(a) 60 | n:sub(b) 61 | return n 62 | end 63 | 64 | function vec4.__mul(a, s) 65 | local n = vec4.from(a) 66 | n:scale(s) 67 | return n 68 | end 69 | 70 | function vec4.new(x,y,z,w) 71 | local buf = RM.gcAlloc(4 * 4) 72 | local o = {buf = buf} 73 | setmetatable(o, vec4) 74 | o.x = x 75 | o.y = y 76 | o.z = z 77 | o.w = w 78 | return o 79 | end 80 | 81 | function vec4.from(other) 82 | local n = vec4.new(0,0,0,0) 83 | n:copy(other) 84 | return n 85 | end 86 | 87 | function vec4:add(other) 88 | M.addVec4(self.buf, other.buf) 89 | end 90 | 91 | function vec4:sub(other) 92 | M.subVec4(self.buf, other.buf) 93 | end 94 | 95 | function vec4:copy(from) 96 | M.copyVec4(self.buf, from.buf) 97 | end 98 | 99 | function vec4:length() 100 | return M.lenVec4(self.buf) 101 | end 102 | 103 | function vec4:dot(other) 104 | return M.dotVec4(self.buf, other.buf) 105 | end 106 | 107 | function vec4:scale(s) 108 | M.scaleVec4(self.buf, s) 109 | end 110 | 111 | function vec4:normalize() 112 | M.normalizeVec4(self.buf) 113 | end 114 | 115 | return vec4 116 | 117 | -------------------------------------------------------------------------------- /src/linux/include/draw_sampling.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * Draw library sampling functions 4 | */ 5 | 6 | #ifndef __DRAW_SAMPLING_H__ 7 | #define __DRAW_SAMPLING_H__ 8 | 9 | #include 10 | 11 | /** Level of Detail */ 12 | #define LOD_FORMULAIC 0 13 | #define LOD_USE_K 1 14 | 15 | /** Texture Scaling */ 16 | #define LOD_MAG_NEAREST 0 17 | #define LOD_MAG_LINEAR 1 18 | #define LOD_MIN_NEAREST 0 19 | #define LOD_MIN_LINEAR 1 20 | #define LOD_MIN_NEAR_MIPMAP_NEAR 2 21 | #define LOD_MIN_NEAR_MIPMAP_LINE 3 22 | #define LOD_MIN_LINE_MIPMAP_NEAR 4 23 | #define LOD_MIN_LINE_MIPMAP_LINE 5 24 | 25 | /** Mipmaps */ 26 | #define LOD_MIPMAP_REGISTER 0 27 | #define LOD_MIPMAP_CALCULATE 1 28 | 29 | /** Texture wrapping */ 30 | #define WRAP_REPEAT 0 31 | #define WRAP_CLAMP 1 32 | #define WRAP_REGION_CLAMP 2 33 | #define WRAP_REGION_REPEAT 3 34 | 35 | /** Texture Alpha Expansion */ 36 | #define ALPHA_EXPAND_NORMAL 0 37 | #define ALPHA_EXPAND_TRANSPARENT 1 38 | 39 | typedef struct { 40 | unsigned char calculation; 41 | unsigned char max_level; 42 | unsigned char mag_filter; 43 | unsigned char min_filter; 44 | unsigned char mipmap_select; 45 | unsigned char l; 46 | float k; 47 | } lod_t; 48 | 49 | typedef struct { 50 | int address1; 51 | int address2; 52 | int address3; 53 | char width1; 54 | char width2; 55 | char width3; 56 | } mipmap_t; 57 | 58 | typedef struct { 59 | unsigned char horizontal; 60 | unsigned char vertical; 61 | int minu, maxu; 62 | int minv, maxv; 63 | } texwrap_t; 64 | 65 | #ifdef __cplusplus 66 | extern "C" { 67 | #endif 68 | 69 | /** Texture Sampling, Level-of-Detail, and Filtering */ 70 | qword_t *draw_texture_sampling(qword_t *q, int context, lod_t *lod); 71 | 72 | /** Mipmap levels 1-3 */ 73 | qword_t *draw_mipmap1(qword_t *q, int context, mipmap_t *mipmap); 74 | 75 | /** Mipmap levels 4-6 */ 76 | qword_t *draw_mipmap2(qword_t *q, int context, mipmap_t *mipmap); 77 | 78 | /** Texture Clamping */ 79 | qword_t *draw_texture_wrapping(qword_t *q, int context, texwrap_t *wrap); 80 | 81 | /** Alpha Expansion Values */ 82 | qword_t *draw_texture_expand_alpha(qword_t *q, unsigned char zero_value, 83 | int expand, unsigned char one_value); 84 | 85 | #ifdef __cplusplus 86 | } 87 | #endif 88 | 89 | #endif /* __DRAW_SAMPLING_H__ */ 90 | -------------------------------------------------------------------------------- /src/core/dma.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | 11 | static int dma_init(lua_State *l) { 12 | int channel = lua_tointeger(l, 1); 13 | info("doing dma init, channel = %d", channel); 14 | dma_channel_initialize(channel, 0, 0); 15 | dma_channel_fast_waits(channel); 16 | return 0; 17 | } 18 | 19 | static int dma_send_normal_buffer(lua_State *l) { 20 | // buffer is arg 1 21 | lua_pushstring(l, "ptr"); 22 | lua_gettable(l, 1); 23 | void *ptr = lua_touserdata(l, -1); 24 | lua_pushstring(l, "head"); 25 | lua_gettable(l, 1); 26 | int head = lua_tointeger(l, -1); 27 | 28 | // channel is arg 2 29 | int channel = lua_tointeger(l, 2); 30 | 31 | // print buffer for debugging 32 | print_buffer(ptr, head / 16); 33 | 34 | // info("DMA send :: sending %d qwords on channel %d", head/16, channel); 35 | dma_channel_send_normal(channel, ptr, head / 16, 0, 0); 36 | return 0; 37 | } 38 | 39 | static int dma_send_chain_buffer(lua_State *l) { 40 | // buffer is arg 1 41 | lua_pushstring(l, "ptr"); 42 | lua_gettable(l, 1); 43 | void *ptr = lua_touserdata(l, -1); 44 | lua_pushstring(l, "head"); 45 | lua_gettable(l, 1); 46 | int head = lua_tointeger(l, -1); 47 | 48 | // channel is arg 2 49 | int channel = lua_tointeger(l, 2); 50 | 51 | // print buffer for debugging 52 | print_buffer(ptr, head / 16); 53 | 54 | // info("DMA send :: sending %d qwords on channel %d", head/16, channel); 55 | dma_channel_send_chain(channel, ptr, head / 16, 0, 0); 56 | FlushCache(0); 57 | return 0; 58 | } 59 | 60 | static int dma_wait(lua_State *l) { 61 | dma_wait_fast(); 62 | 63 | return 0; 64 | } 65 | 66 | int dma_lua_init(lua_State *l) { 67 | lua_createtable(l, 0, 5); 68 | lua_pushcfunction(l, dma_wait); 69 | lua_setfield(l, -2, "waitFast"); 70 | lua_pushcfunction(l, dma_send_normal_buffer); 71 | lua_setfield(l, -2, "sendNormal"); 72 | lua_pushcfunction(l, dma_send_chain_buffer); 73 | lua_setfield(l, -2, "sendChain"); 74 | lua_pushcfunction(l, dma_init); 75 | lua_setfield(l, -2, "init"); 76 | 77 | lua_pushinteger(l, DMA_CHANNEL_GIF); 78 | lua_setfield(l, -2, "GIF"); 79 | lua_pushinteger(l, DMA_CHANNEL_VIF0); 80 | lua_setfield(l, -2, "VIF0"); 81 | lua_pushinteger(l, DMA_CHANNEL_VIF1); 82 | lua_setfield(l, -2, "VIF1"); 83 | 84 | lua_pushinteger(l, 1 << 28); 85 | lua_setfield(l, -2, "CNT"); 86 | lua_pushinteger(l, 7 << 28); 87 | lua_setfield(l, -2, "END"); 88 | lua_pushinteger(l, 3 << 28); 89 | lua_setfield(l, -2, "REF"); 90 | lua_pushinteger(l, 0); 91 | lua_setfield(l, -2, "REFE"); 92 | lua_pushinteger(l, 2 << 28); 93 | lua_setfield(l, -2, "NEXT"); 94 | 95 | return 1; 96 | } 97 | -------------------------------------------------------------------------------- /src/linux/include/draw.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef P2SIM_DRAW_H 3 | #define P2SIM_DRAW_H 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define ftoi4(f) ((int)f) 14 | 15 | #define DRAW_XYZ_REGLIST \ 16 | ((u64)GIF_REG_XYZ2) << 0 | \ 17 | ((u64)GIF_REG_XYZ2) << 4 18 | 19 | typedef union { 20 | u64 rgbaq; 21 | struct { 22 | u8 r; 23 | u8 g; 24 | u8 b; 25 | u8 a; 26 | float q; 27 | }; 28 | } __attribute__((packed, aligned(8))) color_t; 29 | 30 | typedef struct { 31 | float x; float y; unsigned int z; 32 | } vertex_t; 33 | 34 | typedef struct { 35 | vertex_t v0; 36 | vertex_t v1; 37 | color_t color; 38 | } rect_t; 39 | 40 | 41 | #define DRAW_DISABLE 0 42 | #define DRAW_ENABLE 1 43 | 44 | enum draw_psm { 45 | GS_PSM_4, 46 | GS_PSM_4HL, 47 | GS_PSM_4HH, 48 | GS_PSM_8, 49 | GS_PSM_8H, 50 | GS_PSM_16, 51 | GS_PSM_16S, 52 | GS_PSM_24, 53 | GS_PSM_PS24, 54 | GS_PSM_32, 55 | GS_PSMZ_16, 56 | GS_PSMZ_16S, 57 | GS_PSMZ_24, 58 | GS_PSMZ_32, 59 | }; 60 | 61 | typedef struct { 62 | int address; 63 | int width; 64 | int height; 65 | int mask; 66 | enum draw_psm psm; 67 | } framebuffer_t; 68 | 69 | typedef struct { 70 | int enable; 71 | int address; 72 | int width; 73 | int height; 74 | int method; 75 | int mask; 76 | enum draw_psm zsm; 77 | } zbuffer_t; 78 | 79 | typedef struct { 80 | int enable; 81 | int method; 82 | int compval; 83 | int keep; 84 | } atest_t; 85 | 86 | typedef struct { 87 | int enable; 88 | int pass; 89 | } dtest_t; 90 | 91 | typedef struct { 92 | int enable; 93 | int method; 94 | } ztest_t; 95 | 96 | qword_t * draw_finish(qword_t *q); 97 | void draw_wait_finish(); 98 | 99 | qword_t *draw_framebuffer(qword_t *q, int context, framebuffer_t *frame); 100 | qword_t *draw_zbuffer(qword_t *q, int context, zbuffer_t *zbuffer); 101 | qword_t *draw_dithering(qword_t *q, int enable); 102 | qword_t *draw_dither_matrix(qword_t *q, char *dm); 103 | qword_t *draw_fog_color(qword_t *q, unsigned char r, unsigned char g, 104 | unsigned char b); 105 | qword_t *draw_scan_masking(qword_t *q, int mask); 106 | qword_t *draw_color_clamping(qword_t *q, int enable); 107 | qword_t *draw_alpha_correction(qword_t *q, int context, int alpha); 108 | qword_t *draw_primitive_xyoffset(qword_t *q, int context, float x, float y); 109 | qword_t *draw_primitive_override(qword_t *q, int mode); 110 | qword_t *draw_scissor_area(qword_t *q, int context, int x0, int x1, int y0, 111 | int y1); 112 | 113 | qword_t *draw_setup_environment(qword_t *q, int context, framebuffer_t *frame, 114 | zbuffer_t *z); 115 | 116 | qword_t *draw_disable_tests(qword_t *q, int i, zbuffer_t *zb); 117 | qword_t *draw_clear(qword_t *q, int context, float x, float y, float width, 118 | float height, int r, int g, int b); 119 | qword_t *draw_rect_filled_strips(qword_t *q, int context, rect_t *rect); 120 | 121 | #endif 122 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ISO_TGT=test.iso 2 | BIN=src/p2g.elf 3 | 4 | PS2HOST?=192.168.20.99 5 | #PLATFORM=ps2 6 | #platform=PS2 7 | 8 | VERSION?=$(shell git rev-parse --short HEAD) 9 | ISO_FLAGS?=-l --allow-lowercase -A "P2Garage Engine by Tom Marks -- coding.tommarks.xyz" -V "P2GARAGE:$(VERSION)" 10 | 11 | LUA_FILES=$(shell find script -type f -name "*.lua") 12 | 13 | LUA_LIB=src/liblua.a 14 | 15 | CPPCHECK_REPORT=cppcheck_report.xml 16 | # facthunder/cppcheck:latest (as of 2.12.1) is broken pending this PR: 17 | # https://github.com/Facthunder/cppcheck/pull/35 18 | CPPCHECK_IMG=ghcr.io/facthunder/cppcheck:2.7 19 | CPPCHECK_OUT=html/ 20 | 21 | PREFIX=src/ 22 | 23 | DIST_BIN_NAME=p2g.elf 24 | PCSX2=pcsx2-qt 25 | 26 | DOCKER_IMG=ps2build 27 | DOCKERFLAGS=--user "$(shell id -u):$(shell id -g)" 28 | DOCKER?=docker 29 | 30 | include .lintvars 31 | 32 | ifeq ($(USE_DOCKER), true) 33 | .PHONY: dist 34 | dist: docker-lua docker-elf assets 35 | cp $(BIN) dist/$(DIST_BIN_NAME) 36 | else 37 | .PHONY: dist 38 | dist: $(LUA_LIB) $(BIN) assets 39 | cp $(BIN) dist/$(DIST_BIN_NAME) 40 | endif 41 | 42 | .PHONY: assets 43 | assets: scripts 44 | if ! [ -d dist ]; then mkdir dist; fi 45 | $(MAKE) -C asset 46 | cp asset/*.tga dist/ 47 | cp asset/*.bin dist/ 48 | cp asset/*.vuprog dist/ 49 | cp distfiles/* dist/ 50 | cp LICENSE dist/ 51 | 52 | .PHONY: scripts 53 | scripts: 54 | if ! [ -d dist/script ]; then mkdir -p dist/script; fi 55 | cp -r script/* dist/script 56 | 57 | lua: 58 | git clone --depth 1 https://github.com/ps2dev/lua -b ee-v5.4.4 59 | cd lua && git apply ../lua.patch 60 | 61 | $(LUA_LIB): lua 62 | make -C lua -f makefile 63 | cp lua/liblua.a src/ 64 | 65 | .PHONY: release 66 | release: clean-all dist 67 | zip -r ps2-engine-$(VERSION).zip dist 68 | 69 | 70 | # Run the engine 71 | .PHONY: run 72 | run: scripts 73 | $(PCSX2) $(shell pwd)/dist/$(DIST_BIN_NAME) 74 | 75 | .PHONY: runps2 76 | runps2: scripts 77 | cd dist && ps2client -h $(PS2HOST) -t 10 execee host:$(DIST_BIN_NAME) 78 | 79 | .PHONY: resetps2 80 | resetps2: 81 | ps2client -h $(PS2HOST) -t 5 reset 82 | 83 | .PHONY: runsim 84 | runsim: scripts 85 | cd dist && ./sim 86 | 87 | # Cleanup etc 88 | .PHONY: clean-all 89 | clean-all: 90 | $(MAKE) -C src clean 91 | $(MAKE) -C asset clean 92 | $(MAKE) -C lua -f makefile clean || true 93 | rm -f $(CPPCHECK_REPORT) 94 | rm -rf $(CPPCHECK_OUT) 95 | rm -rf dist/ 96 | 97 | 98 | include src/Makefile 99 | include quality.makefile 100 | 101 | .PHONY: sim 102 | sim: clean-all $(SIM_BIN) assets 103 | cp $(SIM_BIN) dist/sim 104 | 105 | # Docker rules 106 | .PHONY: docker-image 107 | docker-image: 108 | $(DOCKER) build -t $(DOCKER_IMG) . 109 | 110 | .PHONY: docker-elf 111 | docker-elf: 112 | $(DOCKER) run --rm $(DOCKERFLAGS) -v $(shell pwd):/src $(DOCKER_IMG) make $(BIN) 113 | 114 | .PHONY: docker-iso 115 | docker-iso: 116 | $(DOCKER) run --rm $(DOCKERFLAGS) -v $(shell pwd):/src $(DOCKER_IMG) make $(ISO_TGT) 117 | 118 | .PHONY: docker-lua 119 | docker-lua: lua 120 | $(DOCKER) run --rm $(DOCKERFLAGS) -v $(shell pwd):/src $(DOCKER_IMG) bash -c "platform=PS2 make $(LUA_LIB)" 121 | 122 | -------------------------------------------------------------------------------- /src/draw/vu.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "buffer.h" 5 | #include "draw.h" 6 | #include "vu.h" 7 | 8 | extern struct render_state state; 9 | 10 | static int command_buffer_align_head(struct commandbuffer *c, size_t b) { 11 | while (c->offset % b != 0) { 12 | c->head += 1; 13 | c->offset += 1; 14 | } 15 | return 0; 16 | } 17 | 18 | int draw_vu_upload_program(void *buf, size_t buf_size, int vu_uprog_addr, 19 | int vu_target) { 20 | trace("vu program upload begin (uprog addr=%d), buffer@=%d", vu_uprog_addr, 21 | state.buffer.offset); 22 | struct commandbuffer *c = &state.buffer; 23 | if (c->vif.is_active) { 24 | draw_vifcode_end(c); 25 | } 26 | size_t dwc = buf_size / 8; 27 | if (dwc > 256) { 28 | logerr("vu microprog is too large"); 29 | return 1; 30 | } else if (dwc == 256) { 31 | dwc = 0; 32 | } 33 | c->vif.is_active = 0; 34 | command_buffer_align_head(c, 8); 35 | c->head += sizeof(uint32_t); 36 | c->offset += sizeof(uint32_t); 37 | vifcode((uint32_t *)c->head, VIF_CODE_MPG, VIF_CODE_NO_STALL, dwc, 38 | vu_uprog_addr / 8); 39 | c->head += sizeof(uint32_t); 40 | c->offset += sizeof(uint32_t); 41 | // do actual transfer 42 | size_t n_bytes = (buf_size / 8) * 8; 43 | memcpy(c->head, buf, n_bytes); 44 | c->head += n_bytes; 45 | c->offset += n_bytes; 46 | trace("vu program upload end, buffer@=%d", state.buffer.offset); 47 | return 0; 48 | } 49 | 50 | int draw_vu_call_program(int vu_uprog_addr) { 51 | trace("vu program call (uprog addr=%d), buffer@=%d", vu_uprog_addr, 52 | state.buffer.offset); 53 | struct commandbuffer *c = &state.buffer; 54 | if (c->vif.is_active) { 55 | draw_vifcode_end(c); 56 | } 57 | vifcode((uint32_t *)c->head, VIF_CODE_MSCAL, VIF_CODE_NO_STALL, 0, 58 | vu_uprog_addr / 8); 59 | return 0; 60 | } 61 | 62 | // TODO(phy1um): support extra flags 63 | int draw_vu_unpack_v4_32(void *buffer, size_t buffer_size, int vu_addr) { 64 | struct commandbuffer *c = &state.buffer; 65 | draw_vifcode_end(c); 66 | size_t qword_size = buffer_size / 16; 67 | vifcode((uint32_t *)c->head, VIF_CODE_UNPACK_V432, VIF_CODE_NO_STALL, 68 | qword_size, vu_addr); 69 | 70 | size_t n_bytes = (qword_size)*16; 71 | memcpy(c->head, buffer, n_bytes); 72 | c->head += n_bytes; 73 | c->offset += n_bytes; 74 | 75 | return 0; 76 | } 77 | 78 | int draw_vu_begin_unpack_inline(uint32_t target_addr) { 79 | struct commandbuffer *c = &state.buffer; 80 | draw_vifcode_end(c); 81 | int qw_base = (c->offset / 16) * 16; 82 | if (c->offset > 96) { 83 | trace("TODO"); 84 | return 0; 85 | } 86 | while (c->offset - qw_base != 96) { 87 | c->head += 1; 88 | c->offset += 1; 89 | } 90 | trace("vu begin inline unpack (vu addr=%lu) @buffer=%d", target_addr, 91 | c->offset); 92 | vifcode((uint32_t *)c->head, VIF_CODE_UNPACK_V432, VIF_CODE_NO_STALL, 0, 93 | target_addr); 94 | c->vif.head = c->head; 95 | c->vif.is_direct_gif = 0; 96 | c->vif.is_inline_unpack = 1; 97 | c->vif.is_active = 1; 98 | c->head += sizeof(uint32_t); 99 | c->offset += sizeof(uint32_t); 100 | return 0; 101 | } 102 | -------------------------------------------------------------------------------- /asset/pipeline/join_pallets.py: -------------------------------------------------------------------------------- 1 | from tga import * 2 | import sys 3 | import argparse 4 | 5 | ZERO=0xff000000 6 | 7 | class Rect: 8 | def __init__(self, w, h): 9 | self.w = w 10 | self.h = h 11 | self.grid = [ZERO]*w*h 12 | 13 | def get(self, x, y): 14 | return self.grid[y*self.w + x] 15 | 16 | def set(self, x, y, a): 17 | self.grid[y*self.w + x] = a 18 | 19 | def copy_from(self, other, x0, y0): 20 | for i in range(other.w): 21 | for j in range(other.h): 22 | self.set(x0+i, y0+j, other.get(i, j)) 23 | 24 | def each(self): 25 | for j in range(self.h): 26 | for i in range(self.w): 27 | yield self.get(i, j) 28 | 29 | parser = argparse.ArgumentParser(description="extract palette from TGA file") 30 | parser.add_argument("files", type=str, nargs="+", help="input colour mapped TGA file") 31 | 32 | if __name__ == "__main__": 33 | args = parser.parse_args() 34 | ppr = 64 // 16 35 | if len(args.files) > ppr * ppr: 36 | raise Exception(f"too many inputs, must be {ppr*ppr} at most") 37 | big_pals = [] 38 | small_pals = [] 39 | for f in args.files: 40 | with open(f, "rb") as ff: 41 | print(f"trying {f}") 42 | bb = ff.read() 43 | tga = parse(bb) 44 | if tga.header.img_spec.width <= 16: 45 | small_pals.append(tga.img) 46 | elif tga.header.img_spec.width <= 256: 47 | big_pals.append(tga.img) 48 | else: 49 | raise Exception(f"cannot merge pallet with more than 256 colours: {f} = {tga.header.img_spec.width}") 50 | 51 | 52 | data_blocks = [] 53 | for p in small_pals: 54 | r = Rect(16, 16) 55 | for i in range(8): 56 | if i < len(p): 57 | r.set(i, 0, p[i]) 58 | else: 59 | r.set(i, 0, ZERO) 60 | for i in range(8): 61 | if 8 + i < len(p): 62 | r.set(i, 1, p[i+8]) 63 | else: 64 | r.set(i, 1, ZERO) 65 | data_blocks.append(r) 66 | for p in big_pals: 67 | r = Rect(16, 16) 68 | for j in range(16): 69 | for i in range(16): 70 | ind = j*16 + i 71 | if ind < len(p): 72 | r.set(i, j, p[i]) 73 | else: 74 | r.set(i, j, ZERO) 75 | 76 | if len(data_blocks) < ppr*ppr: 77 | for i in range(len(data_blocks), ppr*ppr): 78 | r = Rect(16, 16) 79 | data_blocks.append(r) 80 | 81 | out_image = Rect(64, 64) 82 | for xx in range(ppr): 83 | for yy in range(ppr): 84 | d = data_blocks[yy * ppr + xx] 85 | out_image.copy_from(d, xx*16, yy*16) 86 | 87 | img_data = list(out_image.each()) 88 | 89 | col_spec = TgaColSpec(0, 0, 0) 90 | img_spec = TgaImgSpec(0, 0, 64, 64, 32, 0x20) 91 | out_header = TgaHeader(0, 0, 2, col_spec, img_spec) 92 | out = TGA(out_header, "", [], img_data) 93 | 94 | with open("pallet_glob.tga", "wb") as f: 95 | print(out.header) 96 | data = write(out) 97 | f.write(data) 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /src/core/gslua.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #include "../gs_state.h" 17 | 18 | static int gslua_set_buffers(lua_State *l) { 19 | struct gs_state *st = GS_STATE; 20 | if (!st) { 21 | lua_pushstring(l, "GS state was NULL"); 22 | lua_error(l); 23 | return 1; 24 | } 25 | 26 | // get fields from FB argument (#1, #2) 27 | lua_pushstring(l, "width"); 28 | lua_gettable(l, 1); 29 | int fb_width = lua_tointeger(l, -1); 30 | lua_pop(l, 1); 31 | lua_pushstring(l, "height"); 32 | lua_gettable(l, 1); 33 | int fb_height = lua_tointeger(l, -1); 34 | lua_pop(l, 1); 35 | lua_pushstring(l, "address"); 36 | lua_gettable(l, 1); 37 | int fb1_addr = lua_tointeger(l, -1); 38 | lua_pop(l, 1); 39 | lua_pushstring(l, "format"); 40 | lua_gettable(l, 1); 41 | int fb_fmt = lua_tointeger(l, -1); 42 | lua_pop(l, 1); 43 | lua_pushstring(l, "address"); 44 | lua_gettable(l, 2); 45 | int fb2_addr = lua_tointeger(l, -1); 46 | lua_pop(l, 1); 47 | 48 | // get fields from ZB argument (#4) 49 | lua_pushstring(l, "address"); 50 | lua_gettable(l, 3); 51 | int zb_addr = lua_tointeger(l, -1); 52 | lua_pushstring(l, "format"); 53 | lua_gettable(l, 3); 54 | int zb_fmt = lua_tointeger(l, -1); 55 | lua_pop(l, 1); 56 | 57 | gs_set_fields(fb_width, fb_height, fb_fmt, zb_fmt, fb1_addr, fb2_addr, 58 | zb_addr); 59 | return 0; 60 | } 61 | 62 | static int gslua_set_output(lua_State *l) { 63 | int width = lua_tointeger(l, 1); 64 | int height = lua_tointeger(l, 2); 65 | int interlace = lua_tointeger(l, 3); 66 | int mode = lua_tointeger(l, 4); 67 | gs_set_output(width, height, interlace, mode, GRAPH_MODE_FIELD, 68 | GRAPH_DISABLE); 69 | return 0; 70 | } 71 | 72 | #define bind(n, b) \ 73 | lua_pushinteger(l, b); \ 74 | lua_setfield(l, -2, n) 75 | 76 | int gs_lua_init(lua_State *l) { 77 | lua_createtable(l, 0, 16); 78 | lua_pushcfunction(l, gslua_set_output); 79 | lua_setfield(l, -2, "setOutput"); 80 | lua_pushcfunction(l, gslua_set_buffers); 81 | lua_setfield(l, -2, "setBuffers"); 82 | 83 | bind("PSM4", GS_PSM_4); 84 | bind("PSM4HL", GS_PSM_4HL); 85 | bind("PSM4HH", GS_PSM_4HH); 86 | bind("PSM8", GS_PSM_8); 87 | bind("PSM8H", GS_PSM_8H); 88 | bind("PSM16", GS_PSM_16); 89 | bind("PSM16S", GS_PSM_16S); 90 | bind("PSM24", GS_PSM_24); 91 | bind("PSMPS24", GS_PSM_PS24); 92 | bind("PSM32", GS_PSM_32); 93 | 94 | bind("PSMZ16", GS_PSMZ_16); 95 | bind("PSMZ16S", GS_PSMZ_16S); 96 | bind("PSMZ24", GS_PSMZ_24); 97 | bind("PSMZ32", GS_PSMZ_32); 98 | 99 | bind("NONINTERLACED", 1); 100 | bind("INTERLACED", 1); 101 | 102 | bind("AUTO", GRAPH_MODE_AUTO); 103 | bind("NTSC", GRAPH_MODE_NTSC); 104 | bind("PAL", GRAPH_MODE_PAL); 105 | bind("HDTV_480P", GRAPH_MODE_HDTV_480P); 106 | bind("HDTV_576P", GRAPH_MODE_HDTV_576P); 107 | bind("HDTV_720P", GRAPH_MODE_HDTV_720P); 108 | 109 | return 1; 110 | } 111 | -------------------------------------------------------------------------------- /src/math/vec3lua.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #define buffer_null_check(p, s) \ 7 | do { \ 8 | if (!p) { \ 9 | lua_pushstring(l, "got null buffer for " s); \ 10 | lua_error(l); \ 11 | return 1; \ 12 | } \ 13 | } while (0) 14 | 15 | #define get_buf(i, to, msg) \ 16 | lua_pushstring(l, "ptr"); \ 17 | lua_gettable(l, i); \ 18 | float *to = lua_touserdata(l, -1); \ 19 | buffer_null_check(to, msg " [" #i "]"); \ 20 | ((void)0) 21 | 22 | static int lua_add_vec3(lua_State *l) { 23 | get_buf(1, buf1, "copy vec3"); 24 | get_buf(2, buf2, "copy vec3"); 25 | 26 | buf1[0] += buf2[0]; 27 | buf1[1] += buf2[1]; 28 | buf1[2] += buf2[2]; 29 | 30 | return 0; 31 | } 32 | 33 | static int lua_sub_vec3(lua_State *l) { 34 | get_buf(1, buf1, "sub vec3"); 35 | get_buf(2, buf2, "sub vec3"); 36 | 37 | buf1[0] -= buf2[0]; 38 | buf1[1] -= buf2[1]; 39 | buf1[2] -= buf2[2]; 40 | 41 | return 0; 42 | } 43 | 44 | // local c = copy(to, from) 45 | static int lua_copy_vec3(lua_State *l) { 46 | get_buf(1, buf1, "copy vec3"); 47 | get_buf(2, buf2, "copy vec3"); 48 | 49 | buf1[0] = buf2[0]; 50 | buf1[1] = buf2[1]; 51 | buf1[2] = buf2[2]; 52 | 53 | return 0; 54 | } 55 | 56 | static int lua_length_vec3(lua_State *l) { 57 | get_buf(1, buf1, "length vec3"); 58 | float result = p2m_vec3_length(buf1); 59 | lua_pushnumber(l, result); 60 | return 1; 61 | } 62 | 63 | static int lua_normalize_vec3(lua_State *l) { 64 | get_buf(1, buf1, "normalize vec3"); 65 | float len = p2m_vec3_length(buf1); 66 | buf1[0] /= len; 67 | buf1[1] /= len; 68 | buf1[2] /= len; 69 | return 0; 70 | } 71 | 72 | static int lua_dot_vec3(lua_State *l) { 73 | get_buf(1, buf1, "dot vec3"); 74 | get_buf(2, buf2, "dot vec3"); 75 | float dot = p2m_vec3_dot(buf1, buf2); 76 | lua_pushnumber(l, dot); 77 | return 1; 78 | } 79 | 80 | static int lua_scale_vec3(lua_State *l) { 81 | get_buf(1, buf1, "scale vec3"); 82 | float scalar = lua_tonumber(l, 2); 83 | p2m_vec3_scale(buf1, scalar); 84 | return 0; 85 | } 86 | 87 | int vec3_lua_init(lua_State *l) { 88 | trace("init core lib math.vec3"); 89 | lua_createtable(l, 0, 8); 90 | lua_pushcfunction(l, lua_add_vec3); 91 | lua_setfield(l, -2, "addVec3"); 92 | lua_pushcfunction(l, lua_sub_vec3); 93 | lua_setfield(l, -2, "subVec3"); 94 | lua_pushcfunction(l, lua_copy_vec3); 95 | lua_setfield(l, -2, "copyVec3"); 96 | lua_pushcfunction(l, lua_length_vec3); 97 | lua_setfield(l, -2, "lenVec3"); 98 | lua_pushcfunction(l, lua_normalize_vec3); 99 | lua_setfield(l, -2, "normalizeVec3"); 100 | lua_pushcfunction(l, lua_dot_vec3); 101 | lua_setfield(l, -2, "dotVec3"); 102 | lua_pushcfunction(l, lua_scale_vec3); 103 | lua_setfield(l, -2, "scaleVec3"); 104 | return 1; 105 | } 106 | -------------------------------------------------------------------------------- /src/gs.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "gs_state.h" 16 | 17 | struct gs_state *GS_STATE; 18 | static qword_t *flip_buffer; 19 | 20 | int gs_init() { 21 | info("init GS -- no framebuffer"); 22 | GS_STATE = calloc(1, sizeof(struct gs_state)); 23 | flip_buffer = memalign(64, 10 * 16); 24 | return 0; 25 | } 26 | 27 | int gs_flip() { 28 | trace("GS FLIP START"); 29 | memset(flip_buffer, 0, 10 * 16); 30 | framebuffer_t *fb = &GS_STATE->fb[GS_STATE->ctx]; 31 | graph_set_framebuffer_filtered(fb->address, fb->width, fb->psm, 0, 0); 32 | GS_STATE->ctx ^= 1; 33 | trace("GS BUILD FLIP BUFFER"); 34 | qword_t *q = flip_buffer; 35 | q = draw_framebuffer(q, 0, &GS_STATE->fb[GS_STATE->ctx]); 36 | q = draw_finish(q); 37 | dma_wait_fast(); 38 | trace("GS SEND FLIP BUFFER"); 39 | dma_channel_send_normal(2, flip_buffer, q - flip_buffer, 0, 0); 40 | trace("GS WAIT FLIP BUFFER DRAW FINISH"); 41 | draw_wait_finish(); 42 | trace("GS FLIP DONE"); 43 | return 0; 44 | } 45 | 46 | int gs_set_fields(int width, int height, int fmt, int zfmt, int fb1_addr, 47 | int fb2_addr, int zbuf_addr) { 48 | trace("setup GS fields (words): fb1 @ %X, fb2 @ %X, zbuffer @ %X", fb1_addr, 49 | fb2_addr, zbuf_addr); 50 | struct gs_state *st = GS_STATE; 51 | st->fb[0].address = fb1_addr; 52 | st->fb[0].width = width; 53 | st->fb[0].height = height; 54 | st->fb[0].psm = fmt; 55 | st->fb[0].mask = 0; 56 | st->fb[1].address = fb2_addr; 57 | st->fb[1].width = width; 58 | st->fb[1].height = height; 59 | st->fb[1].psm = fmt; 60 | st->fb[1].mask = 0; 61 | 62 | st->zb.enable = 1; 63 | st->zb.address = zbuf_addr; 64 | st->zb.zsm = zfmt; 65 | st->zb.method = ZTEST_METHOD_ALLPASS; 66 | st->zb.mask = 0; 67 | graph_set_framebuffer_filtered(fb2_addr, width, fmt, 0, 0); 68 | graph_enable_output(); 69 | 70 | // init draw state 71 | qword_t *head = malloc(20 * 16); 72 | memset(head, 0, 20 * 16); 73 | qword_t *q = head; 74 | q = draw_setup_environment(q, 0, st->fb, &st->zb); 75 | q = draw_primitive_xyoffset(q, 0, 2048 - (width / 2), 76 | 2048 - (height / 2)); 77 | q = draw_finish(q); 78 | dma_channel_send_normal(DMA_CHANNEL_GIF, head, q - head, 0, 0); 79 | draw_wait_finish(); 80 | free(head); 81 | 82 | return 0; 83 | } 84 | 85 | int gs_set_output(int width, int height, int interlace, int mode, int ffmd, 86 | int filter_flicker) { 87 | graph_disable_output(); 88 | graph_set_mode(interlace, mode, ffmd, filter_flicker); 89 | graph_set_screen(0, 0, width, height); 90 | graph_set_bgcolor(0, 0, 0); 91 | return 1; 92 | } 93 | 94 | int gs_framebuffer_size(int width, int height, int psm) { 95 | int size = width*height; 96 | switch (psm) { 97 | case GS_PSM_32: 98 | case GS_PSMZ_32: 99 | size *= 4; 100 | break; 101 | case GS_PSM_24: 102 | size *= 3; 103 | break; 104 | case GS_PSM_16: 105 | case GS_PSMZ_16: 106 | case GS_PSM_16S: 107 | case GS_PSMZ_16S: 108 | size *= 2; 109 | break; 110 | case GS_PSM_4: 111 | size = (int)((float)size*0.5); 112 | break; 113 | } 114 | return size; 115 | } 116 | 117 | int gs_set_ztest(int method) { 118 | struct gs_state *st = GS_STATE; 119 | st->zb.method = method; 120 | return 1; 121 | } 122 | -------------------------------------------------------------------------------- /src/math/vec4lua.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #define buffer_null_check(p, s) \ 7 | do { \ 8 | if (!p) { \ 9 | lua_pushstring(l, "got null buffer for " s); \ 10 | lua_error(l); \ 11 | return 1; \ 12 | } \ 13 | } while (0) 14 | 15 | #define get_buf(i, to, msg) \ 16 | lua_pushstring(l, "ptr"); \ 17 | lua_gettable(l, i); \ 18 | float *to = lua_touserdata(l, -1); \ 19 | buffer_null_check(to, msg " [" #i "]"); \ 20 | ((void)0) 21 | 22 | static int lua_add_vec4(lua_State *l) { 23 | get_buf(1, buf1, "copy vec4"); 24 | get_buf(2, buf2, "copy vec4"); 25 | 26 | buf1[0] += buf2[0]; 27 | buf1[1] += buf2[1]; 28 | buf1[2] += buf2[2]; 29 | buf1[3] += buf2[3]; 30 | 31 | return 0; 32 | } 33 | 34 | static int lua_sub_vec4(lua_State *l) { 35 | get_buf(1, buf1, "sub vec4"); 36 | get_buf(2, buf2, "sub vec4"); 37 | 38 | buf1[0] -= buf2[0]; 39 | buf1[1] -= buf2[1]; 40 | buf1[2] -= buf2[2]; 41 | buf1[3] -= buf2[3]; 42 | 43 | return 0; 44 | } 45 | 46 | // local c = copy(to, from) 47 | static int lua_copy_vec4(lua_State *l) { 48 | get_buf(1, buf1, "copy vec4"); 49 | get_buf(2, buf2, "copy vec4"); 50 | 51 | buf1[0] = buf2[0]; 52 | buf1[1] = buf2[1]; 53 | buf1[2] = buf2[2]; 54 | buf1[3] = buf2[3]; 55 | 56 | return 0; 57 | } 58 | 59 | static int lua_length_vec4(lua_State *l) { 60 | get_buf(1, buf1, "length vec4"); 61 | float result = p2m_vec4_length(buf1); 62 | lua_pushnumber(l, result); 63 | return 1; 64 | } 65 | 66 | static int lua_normalize_vec4(lua_State *l) { 67 | get_buf(1, buf1, "normalize vec4"); 68 | float len = p2m_vec4_length(buf1); 69 | buf1[0] /= len; 70 | buf1[1] /= len; 71 | buf1[2] /= len; 72 | buf1[3] /= len; 73 | return 0; 74 | } 75 | 76 | static int lua_dot_vec4(lua_State *l) { 77 | get_buf(1, buf1, "dot vec4"); 78 | get_buf(2, buf2, "dot vec4"); 79 | float dot = p2m_vec4_dot(buf1, buf2); 80 | lua_pushnumber(l, dot); 81 | return 1; 82 | } 83 | 84 | static int lua_scale_vec4(lua_State *l) { 85 | get_buf(1, buf1, "scale vec4"); 86 | float scalar = lua_tonumber(l, 2); 87 | p2m_vec4_scale(buf1, scalar); 88 | return 0; 89 | } 90 | 91 | int vec4_lua_init(lua_State *l) { 92 | trace("init core lib math.vec4"); 93 | lua_createtable(l, 0, 8); 94 | lua_pushcfunction(l, lua_add_vec4); 95 | lua_setfield(l, -2, "addVec4"); 96 | lua_pushcfunction(l, lua_sub_vec4); 97 | lua_setfield(l, -2, "subVec4"); 98 | lua_pushcfunction(l, lua_copy_vec4); 99 | lua_setfield(l, -2, "copyVec4"); 100 | lua_pushcfunction(l, lua_length_vec4); 101 | lua_setfield(l, -2, "lenVec4"); 102 | lua_pushcfunction(l, lua_normalize_vec4); 103 | lua_setfield(l, -2, "normalizeVec4"); 104 | lua_pushcfunction(l, lua_dot_vec4); 105 | lua_setfield(l, -2, "dotVec4"); 106 | lua_pushcfunction(l, lua_scale_vec4); 107 | lua_setfield(l, -2, "scaleVec4"); 108 | return 1; 109 | } 110 | -------------------------------------------------------------------------------- /src/math/vec2lua.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #define buffer_null_check(p, s) \ 7 | do { \ 8 | if (!p) { \ 9 | lua_pushstring(l, "got null buffer for " s); \ 10 | lua_error(l); \ 11 | return 1; \ 12 | } \ 13 | } while (0) 14 | 15 | #define get_buf(i, to, msg) \ 16 | lua_pushstring(l, "ptr"); \ 17 | lua_gettable(l, i); \ 18 | float *to = lua_touserdata(l, -1); \ 19 | buffer_null_check(to, msg " [" #i "]"); \ 20 | ((void)0) 21 | 22 | static int lua_add_vec2(lua_State *l) { 23 | get_buf(1, buf1, "copy vec"); 24 | get_buf(2, buf2, "copy vec"); 25 | 26 | buf1[0] += buf2[0]; 27 | buf1[1] += buf2[1]; 28 | 29 | return 0; 30 | } 31 | 32 | static int lua_sub_vec2(lua_State *l) { 33 | get_buf(1, buf1, "sub vec"); 34 | get_buf(2, buf2, "sub vec"); 35 | 36 | buf1[0] -= buf2[0]; 37 | buf1[1] -= buf2[1]; 38 | 39 | return 0; 40 | } 41 | 42 | // local c = copy(to, from) 43 | static int lua_copy_vec2(lua_State *l) { 44 | get_buf(1, buf1, "copy vec"); 45 | get_buf(2, buf2, "copy vec"); 46 | 47 | buf1[0] = buf2[0]; 48 | buf1[1] = buf2[1]; 49 | 50 | return 0; 51 | } 52 | 53 | static int lua_length_vec2(lua_State *l) { 54 | get_buf(1, buf1, "length vec2"); 55 | float result = p2m_vec2_length(buf1); 56 | lua_pushnumber(l, result); 57 | return 1; 58 | } 59 | 60 | static int lua_normalize_vec2(lua_State *l) { 61 | get_buf(1, buf1, "normalize vec2"); 62 | float len = p2m_vec2_length(buf1); 63 | buf1[0] /= len; 64 | buf1[1] /= len; 65 | return 0; 66 | } 67 | 68 | static int lua_dot_vec2(lua_State *l) { 69 | get_buf(1, buf1, "dot vec2"); 70 | get_buf(2, buf2, "dot vec2"); 71 | float dot = p2m_vec2_dot(buf1, buf2); 72 | lua_pushnumber(l, dot); 73 | return 1; 74 | } 75 | 76 | static int lua_rotate_vec2(lua_State *l) { 77 | get_buf(1, v, "rotate vec2"); 78 | float angle = lua_tonumber(l, 2); 79 | p2m_vec2_rotate(v, angle); 80 | return 0; 81 | } 82 | 83 | static int lua_scale_vec2(lua_State *l) { 84 | get_buf(1, buf1, "scale vec2"); 85 | float scalar = lua_tonumber(l, 2); 86 | p2m_vec2_scale(buf1, scalar); 87 | return 0; 88 | } 89 | 90 | int vec2_lua_init(lua_State *l) { 91 | trace("init core lib math.vec2"); 92 | lua_createtable(l, 0, 8); 93 | lua_pushcfunction(l, lua_add_vec2); 94 | lua_setfield(l, -2, "addVec2"); 95 | lua_pushcfunction(l, lua_sub_vec2); 96 | lua_setfield(l, -2, "subVec2"); 97 | lua_pushcfunction(l, lua_copy_vec2); 98 | lua_setfield(l, -2, "copyVec2"); 99 | lua_pushcfunction(l, lua_length_vec2); 100 | lua_setfield(l, -2, "lenVec2"); 101 | lua_pushcfunction(l, lua_normalize_vec2); 102 | lua_setfield(l, -2, "normalizeVec2"); 103 | lua_pushcfunction(l, lua_dot_vec2); 104 | lua_setfield(l, -2, "dotVec2"); 105 | lua_pushcfunction(l, lua_rotate_vec2); 106 | lua_setfield(l, -2, "rotateVec2"); 107 | lua_pushcfunction(l, lua_scale_vec2); 108 | lua_setfield(l, -2, "scaleVec2"); 109 | return 1; 110 | } 111 | -------------------------------------------------------------------------------- /src/linux/draw_clear.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Normal offset 4 | #define OFFSET 2048.0f 5 | 6 | // Leftmost/Topmost offset (2048.0f - 0.4375f + 1) 7 | #define START_OFFSET 2047.5625f 8 | 9 | // Bottommost/Rightmost offset (2048.0f + 0.5625f + 1) 10 | #define END_OFFSET 2048.5625f 11 | 12 | static char blending = 0; 13 | 14 | qword_t *draw_clear(qword_t *q, int context, float x, float y, float width, 15 | float height, int r, int g, int b) { 16 | 17 | rect_t rect; 18 | 19 | union { 20 | float fvalue; 21 | u32 ivalue; 22 | } q0 = {1.0f}; 23 | 24 | rect.v0.x = x; 25 | rect.v0.y = y; 26 | rect.v0.z = 0x00000000; 27 | 28 | rect.color.rgbaq = GS_SET_RGBAQ(r, g, b, 0x80, q0.ivalue); 29 | 30 | rect.v1.x = x + width - 0.9375f; 31 | rect.v1.y = y + height - 0.9375f; 32 | rect.v1.z = 0x00000000; 33 | 34 | PACK_GIFTAG(q, GIF_SET_TAG(2, 0, 0, 0, 0, 1), GIF_REG_AD); 35 | q++; 36 | PACK_GIFTAG(q, GS_SET_PRMODECONT(PRIM_OVERRIDE_ENABLE), GS_REG_PRMODECONT); 37 | q++; 38 | PACK_GIFTAG(q, GS_SET_PRMODE(0, 0, 0, 0, 0, 0, context, 1), GS_REG_PRMODE); 39 | q++; 40 | 41 | q = draw_rect_filled_strips(q, context, &rect); 42 | 43 | PACK_GIFTAG(q, GIF_SET_TAG(1, 0, 0, 0, 0, 1), GIF_REG_AD); 44 | q++; 45 | PACK_GIFTAG(q, GS_SET_PRMODECONT(PRIM_OVERRIDE_DISABLE), GS_REG_PRMODECONT); 46 | q++; 47 | 48 | return q; 49 | } 50 | 51 | qword_t *draw_rect_filled_strips(qword_t *q, int context, rect_t *rect) { 52 | 53 | qword_t *giftag; 54 | 55 | int __xi0 = ftoi4(rect->v0.x); 56 | int __yi0 = ftoi4(rect->v0.y + START_OFFSET); 57 | 58 | int __xi1 = ftoi4(rect->v1.x); 59 | int __yi1 = ftoi4(rect->v1.y + END_OFFSET); 60 | 61 | // Start primitive 62 | PACK_GIFTAG(q, GIF_SET_TAG(2, 0, 0, 0, GIF_FLG_PACKED, 1), GIF_REG_AD); 63 | q++; 64 | 65 | PACK_GIFTAG(q, GIF_SET_PRIM(PRIM_SPRITE, 0, 0, 0, blending, 0, 0, context, 0), 66 | GIF_REG_PRIM); 67 | q++; 68 | 69 | PACK_GIFTAG(q, rect->color.rgbaq, GIF_REG_RGBAQ); 70 | q++; 71 | 72 | giftag = q; 73 | q++; 74 | 75 | // Fill vertex information in 32 pixel wide strips 76 | while (__xi0 < __xi1) { 77 | 78 | // q->dw[0] = GIF_SET_XYZ(ftoi4(__xf0 + START_OFFSET),__yi0,rect->v0.z); 79 | q->dw[0] = GIF_SET_XYZ(__xi0 + ftoi4(START_OFFSET), __yi0, rect->v0.z); 80 | 81 | // 31<<4 82 | __xi0 += 496; 83 | 84 | // Uneven... 85 | if (__xi0 >= __xi1) { 86 | __xi0 = __xi1; 87 | } 88 | 89 | q->dw[1] = GIF_SET_XYZ(__xi0 + ftoi4(END_OFFSET), __yi1, rect->v0.z); 90 | 91 | // 1<<4 92 | __xi0 += 16; 93 | 94 | q++; 95 | } 96 | 97 | PACK_GIFTAG(giftag, GIF_SET_TAG(q - giftag - 1, 0, 0, 0, GIF_FLG_REGLIST, 2), 98 | DRAW_XYZ_REGLIST); 99 | 100 | return q; 101 | } 102 | 103 | qword_t *draw_disable_tests(qword_t *q, int context, zbuffer_t *z) { 104 | 105 | PACK_GIFTAG(q, GIF_SET_TAG(1, 0, 0, 0, GIF_FLG_PACKED, 1), GIF_REG_AD); 106 | q++; 107 | PACK_GIFTAG(q, 108 | GS_SET_TEST(DRAW_ENABLE, ATEST_METHOD_NOTEQUAL, 0x00, 109 | ATEST_KEEP_FRAMEBUFFER, DRAW_DISABLE, DRAW_DISABLE, 110 | DRAW_ENABLE, ZTEST_METHOD_ALLPASS), 111 | GS_REG_TEST + context); 112 | q++; 113 | 114 | return q; 115 | } 116 | 117 | qword_t *draw_enable_tests(qword_t *q, int context, zbuffer_t *z) { 118 | 119 | PACK_GIFTAG(q, GIF_SET_TAG(1, 0, 0, 0, GIF_FLG_PACKED, 1), GIF_REG_AD); 120 | q++; 121 | PACK_GIFTAG(q, 122 | GS_SET_TEST(DRAW_ENABLE, ATEST_METHOD_NOTEQUAL, 0x00, 123 | ATEST_KEEP_FRAMEBUFFER, DRAW_DISABLE, DRAW_DISABLE, 124 | DRAW_ENABLE, z->method), 125 | GS_REG_TEST + context); 126 | q++; 127 | 128 | return q; 129 | } 130 | -------------------------------------------------------------------------------- /script/eg/cube.lua: -------------------------------------------------------------------------------- 1 | 2 | local GIF = require"p2g.gif" 3 | local P = require"p2g.const" 4 | local DRAW = require"p2g.draw2d" 5 | local VRAM = require"p2g.vram" 6 | local DMA = require"p2g.dma" 7 | local GS = require"p2g.gs" 8 | local RM = require"p2g.buffer" 9 | local LOG = require"p2g.log" 10 | local M = require"p2g.math" 11 | local IO = require"p2g.io" 12 | local VU = require"p2g.vu" 13 | local PAD = require"p2g.pad" 14 | local CAMERA = require"p2g.camera" 15 | local I = require"eg.cube_instance" 16 | 17 | local rx = 0 18 | local ry = 0 19 | local cam = CAMERA.new(0,0,18, { 20 | fov = 0.02, 21 | aspect = 3/4, 22 | near = 0.1, 23 | far = 10, 24 | }) 25 | 26 | local instances = {} 27 | local vuprog = {} 28 | 29 | function PS2PROG.start() 30 | PS2PROG.logLevel(LOG.debugLevel) 31 | DMA.init(DMA.GIF) 32 | DMA.init(DMA.VIF1) 33 | GS.setOutput(640, 448, GS.INTERLACED, GS.NTSC) 34 | DRAW.set_buffer_target(1) 35 | local fb1 = VRAM.mem:framebuffer(640, 448, GS.PSM32, 2048) 36 | local fb2 = VRAM.mem:framebuffer(640, 448, GS.PSM32, 2048) 37 | local zb = VRAM.mem:framebuffer(640, 448, GS.PSMZ16, 2048) 38 | GS.setBuffers(fb1, fb2, zb) 39 | DRAW:screenDimensions(640, 448) 40 | DRAW:clearColour(0x2b, 0x2b, 0x2b) 41 | local db = RM.alloc(200 * 1024) 42 | DRAW:bindBuffer(db) 43 | 44 | local file_target = "cube.bin" 45 | local sz = IO.file_size(file_target) 46 | local verts = sz / (8*4) 47 | cube_model = RM.alloc(sz) 48 | IO.read_file(file_target, 0, sz, cube_model) 49 | 50 | local vuprog_size = IO.file_size("identity.vuprog") 51 | LOG.debug("try load thing " .. vuprog_size) 52 | local vuprog_bytes = RM.alloc(vuprog_size) 53 | IO.read_file("identity.vuprog", 0, vuprog_size, vuprog_bytes) 54 | LOG.debug("trying to create VU program") 55 | vuprog = VU.new_program("identity", vuprog_bytes) 56 | 57 | local putcube = function(x,y,z,s,rx,ry) 58 | local i = I.new(cube_model, verts, 8*4) 59 | i:move_to(x,y,z) 60 | i:rotate(rx, ry) 61 | i:set_scale(s) 62 | table.insert(instances, i) 63 | end 64 | 65 | putcube(0, 0, -3, 20, 0.1, 0.2) 66 | 67 | end 68 | 69 | local reload_debounce = 0 70 | local first_frame = true 71 | 72 | function PS2PROG.frame() 73 | local dt = 1/30 74 | local cam_dx = PAD.axis(PAD.axisLeftX) 75 | local cam_dy = PAD.axis(PAD.axisLeftY) 76 | if PAD.held(PAD.L1) then 77 | instances[1]:step(cam_dx*30*dt, cam_dy*30*dt, 0) 78 | else 79 | cam:step(cam_dx*10*dt, 0, cam_dy*10*dt) 80 | end 81 | 82 | if reload_debounce > 0 then reload_debounce = reload_debounce - 1 end 83 | 84 | if PAD.held(PAD.SELECT) and reload_debounce <= 0 then 85 | local lv = PS2PROG.get_log_level() 86 | PS2PROG.logLevel(LOG.traceLevel) 87 | reload("p2g.camera") 88 | reload("eg.cube_instance") 89 | reload_debounce = 6 90 | PS2PROG.logLevel(lv) 91 | end 92 | 93 | if PAD.held(PAD.X) and reload_debounce <= 0 then 94 | LOG.info(string.format("camera @ {%f, %f, %f}", cam.pos.x, cam.pos.y, cam.pos.z)) 95 | LOG.info(string.format("cube R {%f, %f, 0}", instances[1].rot_x, instances[1].rot_y)) 96 | PS2PROG.logLevel(LOG.traceLevel) 97 | reload_rebounce = 6 98 | end 99 | 100 | local drot_x = 0 101 | local drot_y = 0 102 | if PAD.held(PAD.LEFT) then 103 | drot_y = 0.09 104 | elseif PAD.held(PAD.RIGHT) then 105 | drot_y = -0.09 106 | end 107 | if PAD.held(PAD.UP) then 108 | drot_x = 0.09 109 | elseif PAD.held(PAD.DOWN) then 110 | drot_x = -0.09 111 | end 112 | 113 | instances[1]:rotate(drot_x, drot_y) 114 | 115 | DRAW:frameStart() 116 | if first_frame then 117 | LOG.debug("try upload") 118 | print(vuprog.buffer) 119 | vuprog:upload_to(VU.VU1, 0xe00) 120 | first_frame = false 121 | end 122 | DRAW:vu_begin_unpack_inline(0x100) 123 | I.draw_all(instances, cam) 124 | LOG.debug("try call") 125 | vuprog:call() 126 | DRAW:frameEnd() 127 | 128 | end 129 | 130 | 131 | -------------------------------------------------------------------------------- /script/p2g/gif.lua: -------------------------------------------------------------------------------- 1 | local P = require"p2g.const" 2 | local LOG = require"p2g.log" 3 | 4 | local gif = { 5 | PACKED = 0x0, 6 | REGLIST = 0x01 * 2^26, 7 | IMAGE = math.floor(2^27), 8 | BLOCKSIZE = 0x7FF, 9 | } 10 | 11 | function gif.tag(b, flg, nloop, eop, regs) 12 | local lpp = b.head 13 | LOG.trace("GIFTAG: pushing " .. string.format("0x%x", nloop)) 14 | if eop then 15 | b:pushint(nloop + 0x8000) 16 | else 17 | b:pushint(nloop) 18 | end 19 | local nreg = #regs 20 | if nreg > 16 then error("invalid gif tag: nreg = " .. #regs) end 21 | if nreg == 16 then nreg = 0 end 22 | local w2 = (math.floor(nreg * 2^28) + flg) 23 | LOG.trace("GIFTAG: pushing " .. string.format("0x%x", w2) .. " :: " .. w2) 24 | LOG.trace("GIFTAG: flag = " .. flg) 25 | b:pushint(w2) 26 | local reg = 0 27 | local regc = 0 28 | local max = 1 29 | 30 | -- write the registers 31 | for i=1,#regs,1 do 32 | reg = reg | regs[i] << (4*regc) 33 | regc = regc + 1 34 | if regc >=8 then 35 | b:pushint(reg) 36 | LOG.trace("GIFTAG: pushing regword " .. string.format("0x%x", reg)) 37 | regc = 0 38 | max = max - 1 39 | end 40 | end 41 | 42 | -- pad out the rest 43 | for i=max,0,-1 do 44 | b:pushint(reg) 45 | LOG.trace("GIFTAG: pushing regword " .. string.format("0x%x", reg)) 46 | reg = 0 47 | end 48 | 49 | return lpp 50 | end 51 | 52 | function gif.setAd(b, reg, v1, v2) 53 | b:pushint(v1) 54 | b:pushint(v2) 55 | b:pushint(reg) 56 | b:pushint(0) 57 | end 58 | 59 | function gif.bitBltBuf(b, dba, dbw, psm) 60 | if dbw == 0 then dbw = 1 end 61 | gif.setAd(b, P.REG.BITBLTBUF, 0, dba + (dbw*2^16) + (psm*2^24)) 62 | end 63 | 64 | function gif.trxPos(b, sx, sy, dx, dy, dir) 65 | gif.setAd(b, P.REG.TRXPOS, sx + (sy*2^16), dx + (dy*2^16) + (dir*2^27)) 66 | end 67 | 68 | function gif.trxReg(b, w, h) 69 | gif.setAd(b, P.REG.TRXREG, w, h) 70 | end 71 | 72 | function gif.trxDir(b, dir) 73 | gif.setAd(b, P.REG.TRXDIR, dir, 0) 74 | end 75 | 76 | function gif.tex1(b, lcm, mxl, mtba, l, k) 77 | local v1 = mxl * 4 78 | if lcm then v1 = v1 + 1 end 79 | if mtba then v1 = v1 + 2^8 end 80 | v1 = v1 + math.floor(l * (2^18)) 81 | gif.setAd(b, P.REG.TEX1, v1, k) 82 | end 83 | 84 | function gif.texA(b, a0, a1) 85 | gif.setAd(b, P.REG.TEXA, a0, a1) 86 | end 87 | 88 | function gif.tex2(b, cbp, psm, cpsm, csm) 89 | local v = cbp * 2^5 90 | v = v + 0x40000000 91 | gif.setAd(b, P.REG.TEX2, math.floor(psm * 2^20), v) 92 | end 93 | 94 | function gif.mipTbp1(b, ctx, p1, w1, p2, w2, p3, w3) 95 | b:setMipTbp(p1, w1, p2, w2, p3, w3) 96 | b:pushint(P.REG.MIPTBP1 + ctx) 97 | b:pushint(0) 98 | end 99 | 100 | function gif.mipTbp2(b, ctx, p1, w1, p2, w2, p3, w3) 101 | b:setMipTbp(p1, w1, p2, w2, p3, w3) 102 | b:pushint(P.REG.MIPTBP2 + ctx) 103 | b:pushint(0) 104 | end 105 | 106 | function gif.primAd(b, primType, shaded, textured, aa) 107 | local bits = primType 108 | if shaded then bits = bits + 0x8 end 109 | if textured then bits = bits + 0x10 end 110 | if aa then bits = bits + 0x80 end 111 | bits = bits | 64 112 | gif.setAd(b, P.REG.PRIM, bits, 0) 113 | end 114 | 115 | function gif.packedRGBAQ(bu, r, g, b, a) 116 | bu:pushint(r) 117 | bu:pushint(g) 118 | bu:pushint(b) 119 | bu:pushint(a) 120 | end 121 | 122 | function gif.packedXYZ2(b, x, y, z) 123 | b:pushint(x) 124 | b:pushint(y) 125 | b:pushint(z) 126 | b:pushint(0) 127 | end 128 | 129 | function gif.packedUV(b, u, v) 130 | local vv = u + math.floor(v * 2^16) 131 | b:pushint(vv) 132 | b:pushint(0) 133 | b:pushint(0) 134 | b:pushint(0) 135 | end 136 | 137 | function gif.packedST(b, s, t) 138 | b:pushfloat(s) 139 | b:pushfloat(t) 140 | b:pushfloat(1.0) 141 | b:pushint(0) 142 | end 143 | 144 | function gif.texflush(b) 145 | gif.tag(b, gif.PACKED, 1, false, {0xe}) 146 | gif.setAd(b, P.REG.TEXFLUSH, 0, 0) 147 | end 148 | 149 | return gif 150 | -------------------------------------------------------------------------------- /src/include/p2g/ps2draw.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef DRAW_H 3 | #define DRAW_H 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /** 11 | * CONSTANTS 12 | */ 13 | 14 | #define GIF_MAX_LOOPS 0x7fff 15 | #define QW_SIZE 4*sizeof(uint32_t) 16 | 17 | #define error(msg) p2g_fatal(msg) 18 | 19 | /** 20 | * DRAW 2D 21 | */ 22 | enum d2d_type { 23 | DRAW_FMT_NONE, 24 | DRAW_FMT_GEOM, 25 | DRAW_FMT_GEOM3D, 26 | DRAW_FMT_RECT, 27 | DRAW_FMT_SPRITE, 28 | DRAW_FMT_TEXTRI, 29 | }; 30 | 31 | struct draw_gif { 32 | uint32_t *head; 33 | int loop_count; 34 | }; 35 | 36 | struct draw_dma { 37 | char *head; 38 | int in_cnt; 39 | }; 40 | 41 | struct draw_stats { 42 | int kick_count; 43 | int tris; 44 | int ctx_2d_count; 45 | int ctx_3d_count; 46 | }; 47 | 48 | /* 49 | struct d2d_state { 50 | unsigned char col[4]; 51 | char clear[4]; 52 | int screen_w; 53 | int screen_h; 54 | enum draw_type draw_type; 55 | int vram_texture_ptr; 56 | char *drawbuffer; 57 | char *drawbuffer_head; 58 | size_t drawbuffer_head_offset; 59 | size_t drawbuffer_len; 60 | void *zbuffer_ref; 61 | 62 | int tex_vram_addr; 63 | int tex_width; 64 | int tex_height; 65 | int tex_psm; 66 | int active_tex; 67 | 68 | int clut_tex; 69 | 70 | struct d2d_gif gif; 71 | struct draw_dma dma; 72 | struct d2d_stats this_frame; 73 | struct d2d_stats last_frame; 74 | }; 75 | */ 76 | 77 | #define RENDER_CTX_NULL 0 78 | #define RENDER_CTX_2D 1 79 | #define RENDER_CTX_3D 2 80 | 81 | struct d2d_state { 82 | unsigned char col[4]; 83 | enum d2d_type draw_type; 84 | }; 85 | 86 | struct render_state { 87 | int has_ctx; 88 | char clear[4]; 89 | int screen_w; 90 | int screen_h; 91 | char *cmdbuffer; 92 | char *cmdbuffer_head; 93 | size_t cmdbuffer_head_offset; 94 | size_t cmdbuffer_len; 95 | void *zbuffer; 96 | 97 | int tex_vram_addr; 98 | int tex_width; 99 | int tex_height; 100 | int tex_psm; 101 | int active_tex; 102 | int clut_tex; 103 | 104 | struct draw_dma dma; 105 | struct draw_gif gif; 106 | struct draw_stats this_frame; 107 | struct draw_stats last_frame; 108 | 109 | struct d2d_state d2d; 110 | }; 111 | 112 | // shared draw functions 113 | 114 | int draw_frame_start(); 115 | int draw_frame_end(); 116 | 117 | int draw_get_context(int ctx); 118 | int draw_end_context(); 119 | 120 | int draw_upload_texture(void *texture, size_t bytes, int width, int height, 121 | int format, int vram_addr); 122 | 123 | int draw_bind_buffer(void *buf, size_t buf_len); 124 | 125 | // 2d renderer API 126 | int draw2d_clear_colour(char r, char g, char b); 127 | 128 | int draw2d_triangle(float x1, float y1, float x2, float y2, float x3, float y3); 129 | int draw2d_textri(float x1, float y1, float u1, float v1, 130 | float x2, float y2, float u2, float v2, 131 | float x3, float y3, float u3, float v3); 132 | int draw2d_rect(float x1, float y1, float w, float h); 133 | 134 | int draw2d_screen_dimensions(int w, int h); 135 | int draw2d_set_colour(unsigned char r, unsigned char g, unsigned char b, 136 | unsigned char a); 137 | 138 | int draw2d_sprite(float x, float y, float w, float h, float u1, float v1, 139 | float u2, float v2); 140 | 141 | int draw2d_bind_texture(int tex_vram_addr, int width, int height, int psm); 142 | 143 | int draw2d_set_clut_state(int texture_base); 144 | 145 | // 3d renderer API 146 | // int draw3d_set_property(int property_id, float value); 147 | // int draw3d_mesh(float *verts, size_t vert_len, uint32_t gif_fmt, 148 | // int gif_fmt_len, int floats_per_vert); 149 | // 150 | int draw3d_ee_transform_verts(float *mvp, size_t offset_start, 151 | int vertex_count, int vertex_size, int pos_offset); 152 | size_t draw3d_mesh_triangles_cnt(void *buffer, int vertex_count, 153 | size_t vertex_size); 154 | int draw3d_mesh_triangles_ref(void *buffer, int vertex_count, 155 | size_t vertex_size); 156 | 157 | 158 | 159 | #endif 160 | -------------------------------------------------------------------------------- /src/core/tga.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | struct __attribute__((__packed__)) tga_header { 14 | uint8_t idlen; 15 | uint8_t colMapType; 16 | uint8_t imgType; 17 | 18 | uint16_t firstColEntryIndex; 19 | uint16_t colMapLength; 20 | uint8_t colMapBps; 21 | 22 | uint16_t xorigin; 23 | uint16_t yorigin; 24 | uint16_t width; 25 | uint16_t height; 26 | uint8_t bps; 27 | uint8_t descriptor; 28 | }; 29 | 30 | // TGA.header_get(buf, field_name) 31 | static int get_tga_header_from_buffer(lua_State *l) { 32 | lua_getfield(l, 1, "ptr"); 33 | struct tga_header *header = (struct tga_header *)lua_touserdata(l, -1); 34 | const char *field = lua_tostring(l, 2); 35 | if (strcmp(field, "id_length") == 0) { 36 | lua_pushnumber(l, header->idlen); 37 | return 1; 38 | } else if (strcmp(field, "x_origin") == 0) { 39 | lua_pushnumber(l, header->xorigin); 40 | return 1; 41 | } else if (strcmp(field, "y_origin") == 0) { 42 | lua_pushnumber(l, header->yorigin); 43 | return 1; 44 | } else if (strcmp(field, "width") == 0) { 45 | lua_pushnumber(l, header->width); 46 | return 1; 47 | } else if (strcmp(field, "height") == 0) { 48 | lua_pushnumber(l, header->height); 49 | return 1; 50 | } else if (strcmp(field, "bps") == 0) { 51 | lua_pushnumber(l, header->bps); 52 | return 1; 53 | } 54 | 55 | return luaL_error(l, "unknown field \"%s\"", field); 56 | } 57 | 58 | // TGA.swizzle16(img) 59 | static int swizzle16(lua_State *l) { 60 | const int bpp = 2; 61 | lua_getfield(l, 1, "width"); 62 | int width = lua_tointeger(l, -1); 63 | lua_getfield(l, 1, "height"); 64 | int height = lua_tointeger(l, -1); 65 | lua_getfield(l, 1, "data"); 66 | lua_getfield(l, -1, "ptr"); 67 | char *buffer = lua_touserdata(l, -1); 68 | for (int i = 0; i < width; i++) { 69 | for (int j = 0; j < height; j++) { 70 | unsigned char tmp = buffer[(j * width + i) * bpp + 1]; 71 | buffer[(j * width + i) * bpp + 1] = buffer[(j * width + i) * bpp]; 72 | buffer[(j * width + i) * bpp] = tmp; 73 | } 74 | } 75 | return 0; 76 | } 77 | 78 | static int swizzle24(lua_State *l) { 79 | const int bpp = 3; 80 | lua_getfield(l, 1, "width"); 81 | int width = lua_tointeger(l, -1); 82 | lua_getfield(l, 1, "height"); 83 | int height = lua_tointeger(l, -1); 84 | lua_getfield(l, 1, "data"); 85 | lua_getfield(l, -1, "ptr"); 86 | char *buffer = lua_touserdata(l, -1); 87 | for (int i = 0; i < width; i++) { 88 | for (int j = 0; j < height; j++) { 89 | unsigned char tmp = buffer[(j * width + i) * bpp + 2]; 90 | buffer[(j * width + i) * bpp + 2] = buffer[(j * width + i) * bpp]; 91 | buffer[(j * width + i) * bpp] = tmp; 92 | } 93 | } 94 | return 0; 95 | } 96 | 97 | static int swizzle32(lua_State *l) { 98 | const int bpp = 4; 99 | lua_getfield(l, 1, "width"); 100 | int width = lua_tointeger(l, -1); 101 | lua_getfield(l, 1, "height"); 102 | int height = lua_tointeger(l, -1); 103 | lua_getfield(l, 1, "data"); 104 | lua_getfield(l, -1, "ptr"); 105 | unsigned char *buffer = lua_touserdata(l, -1); 106 | for (int i = 0; i < width; i++) { 107 | for (int j = 0; j < height; j++) { 108 | unsigned char tmp = buffer[(j * width + i) * bpp + 2]; 109 | buffer[((j * width + i) * bpp) + 2] = buffer[(j * width + i) * bpp]; 110 | buffer[(j * width + i) * bpp] = tmp; 111 | // PS2 alpha maps [0,0x80] to TGA's [0,0xFF] 112 | buffer[((j * width + i) * bpp) + 3] /= 2; 113 | } 114 | } 115 | return 0; 116 | } 117 | 118 | int tga_lua_init(lua_State *l) { 119 | lua_createtable(l, 0, 5); 120 | lua_pushcfunction(l, get_tga_header_from_buffer); 121 | lua_setfield(l, -2, "get_header_field"); 122 | lua_pushcfunction(l, swizzle16); 123 | lua_setfield(l, -2, "swizzle16"); 124 | lua_pushcfunction(l, swizzle24); 125 | lua_setfield(l, -2, "swizzle24"); 126 | lua_pushcfunction(l, swizzle32); 127 | lua_setfield(l, -2, "swizzle32"); 128 | lua_pushnumber(l, sizeof(struct tga_header)); 129 | lua_setfield(l, -2, "HEADER_SIZE"); 130 | return 1; 131 | } 132 | -------------------------------------------------------------------------------- /src/pad.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #define pad_test(b, v) \ 12 | if (c&b) { \ 13 | if (btn_held[v]) { \ 14 | btn_release[v] = 1;\ 15 | btn_held[v] = 0;\ 16 | } else {\ 17 | btn_press[v] = 1;\ 18 | btn_held[v] = 1;\ 19 | }\ 20 | } 21 | 22 | char *pad_buffer; 23 | struct padButtonStatus *pad_read_space; 24 | 25 | static int *btn_held; 26 | static int *btn_press; 27 | static int *btn_release; 28 | 29 | int joysticks[JOY_AXIS_COUNT]; 30 | 31 | int prev_inputs; 32 | 33 | int button_held(int b) { return btn_held[b]; } 34 | int button_pressed(int b) { return btn_press[b]; } 35 | int button_released(int b) { return btn_release[b]; } 36 | 37 | int joy_axis_value(int a) { return joysticks[a]; } 38 | 39 | static void wait_vblank() { 40 | // Enable the vsync interrupt. 41 | *GS_REG_CSR |= GS_SET_CSR(0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0); 42 | 43 | // Wait for the vsync interrupt. 44 | while (!(*GS_REG_CSR & (GS_SET_CSR(0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0)))) { 45 | } 46 | 47 | // Disable the vsync interrupt. 48 | *GS_REG_CSR &= ~GS_SET_CSR(0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0); 49 | } 50 | 51 | int pad_init() { 52 | btn_held = memalign(BTN_MAX * sizeof(int), 256); 53 | btn_press = memalign(BTN_MAX * sizeof(int), 256); 54 | btn_release = memalign(BTN_MAX * sizeof(int), 256); 55 | pad_buffer = memalign(256, 256); 56 | if ((u32)pad_buffer & 0xf) { 57 | info("pad buffer was not 16byte aligned: %x", (int)pad_buffer); 58 | return -1; 59 | } 60 | pad_read_space = memalign(128, sizeof(struct padButtonStatus)); 61 | 62 | info("loading SIF modules for PAD"); 63 | int rc = SifLoadModule(R_SIO2MAN, 0, 0); 64 | if (!rc) { 65 | logerr("failed to load SIF %s", R_SIO2MAN); 66 | return 0; 67 | } 68 | rc = SifLoadModule(R_PADMAN, 0, 0); 69 | if (!rc) { 70 | logerr("failed to load SIF %s", R_PADMAN); 71 | return 0; 72 | } 73 | padInit(0); 74 | padPortOpen(0, 0, pad_buffer); 75 | // TODO(phy1um): check for dualshock controller 76 | int busy_loops = 100; 77 | while (busy_loops > 0) { 78 | int32_t state = padGetState(0, 0); 79 | if (state == PAD_STATE_STABLE || state == PAD_STATE_FINDCTP1) { 80 | int modes = padInfoMode(0, 0, PAD_MODETABLE, -1); 81 | for (int i = 0; i < modes; i++) { 82 | if (padInfoMode(0, 0, PAD_MODETABLE, i) == PAD_TYPE_DUALSHOCK) { 83 | info("found dualshock controller in 0:0"); 84 | } 85 | } 86 | padSetMainMode(0, 0, 1, 3); 87 | while (padGetReqState(0, 0) != PAD_RSTAT_COMPLETE) { 88 | } 89 | while (padGetState(0, 0) != PAD_STATE_STABLE) { 90 | } 91 | return 1; 92 | } 93 | busy_loops -= 1; 94 | wait_vblank(); 95 | } 96 | 97 | logerr("failed to set pad mode, state never stable"); 98 | return 0; 99 | } 100 | 101 | static int pad_wait(int port, int slot, int tries) { 102 | int now; 103 | now = padGetState(port, slot); 104 | if (now == PAD_STATE_DISCONN) { 105 | return -1; 106 | } 107 | while ((now != PAD_STATE_STABLE) && (now != PAD_STATE_FINDCTP1)) { 108 | now = padGetState(port, slot); 109 | tries--; 110 | if (tries == 0) { 111 | break; 112 | } 113 | } 114 | return 0; 115 | } 116 | void pad_poll() { 117 | if (pad_wait(0, 0, 10) < 0) { 118 | return; 119 | } 120 | 121 | if (padRead(0, 0, pad_read_space) != 0) { 122 | int pad = 0xffff ^ pad_read_space->btns; 123 | int c = pad ^ prev_inputs; 124 | prev_inputs = pad; 125 | pad_test(PAD_LEFT, DPAD_LEFT); 126 | pad_test(PAD_RIGHT, DPAD_RIGHT); 127 | pad_test(PAD_UP, DPAD_UP); 128 | pad_test(PAD_DOWN, DPAD_DOWN); 129 | pad_test(PAD_CROSS, BUTTON_X); 130 | pad_test(PAD_SQUARE, BUTTON_SQUARE); 131 | pad_test(PAD_TRIANGLE, BUTTON_TRIANGLE); 132 | pad_test(PAD_CIRCLE, BUTTON_CIRCLE); 133 | pad_test(PAD_L1, BUTTON_L1); 134 | pad_test(PAD_L2, BUTTON_L2); 135 | pad_test(PAD_R1, BUTTON_R1); 136 | pad_test(PAD_R2, BUTTON_R2); 137 | pad_test(PAD_SELECT, BUTTON_SELECT); 138 | pad_test(PAD_START, BUTTON_START); 139 | joysticks[AXIS_LEFT_X] = (int)pad_read_space->ljoy_h; 140 | joysticks[AXIS_LEFT_Y] = (int)pad_read_space->ljoy_v; 141 | joysticks[AXIS_RIGHT_X] = (int)pad_read_space->rjoy_h; 142 | joysticks[AXIS_RIGHT_Y] = (int)pad_read_space->rjoy_v; 143 | } 144 | } 145 | 146 | void pad_frame_start() { 147 | for (int i = 0; i < BTN_MAX; i++) { 148 | btn_press[i] = 0; 149 | btn_release[i] = 0; 150 | } 151 | } 152 | 153 | 154 | -------------------------------------------------------------------------------- /src/draw/draw.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PS2_HOMEBREW_DRAW_DRAW_H 3 | #define PS2_HOMEBREW_DRAW_DRAW_H 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "vu.h" 12 | 13 | 14 | /** 15 | * CONSTANTS 16 | */ 17 | 18 | #define GIF_MAX_LOOPS 0x7fff 19 | #define QW_SIZE 4*sizeof(uint32_t) 20 | 21 | #define error(msg) p2g_fatal(msg) 22 | 23 | 24 | 25 | /** 26 | * DRAW 2D 27 | */ 28 | enum d2d_type { 29 | DRAW_FMT_NONE, 30 | DRAW_FMT_GEOM, 31 | DRAW_FMT_GEOM3D, 32 | DRAW_FMT_RECT, 33 | DRAW_FMT_SPRITE, 34 | DRAW_FMT_TEXTRI, 35 | }; 36 | 37 | struct draw_gif { 38 | uint32_t *head; 39 | int loop_count; 40 | }; 41 | 42 | struct draw_dma { 43 | char *head; 44 | int in_cnt; 45 | }; 46 | 47 | struct draw_vif { 48 | char *head; 49 | int is_direct_gif; 50 | int is_inline_unpack; 51 | int is_active; 52 | }; 53 | 54 | struct draw_stats { 55 | int kick_count; 56 | int tris; 57 | int ctx_2d_count; 58 | int ctx_3d_count; 59 | }; 60 | 61 | /* 62 | struct d2d_state { 63 | unsigned char col[4]; 64 | char clear[4]; 65 | int screen_w; 66 | int screen_h; 67 | enum draw_type draw_type; 68 | int vram_texture_ptr; 69 | char *drawbuffer; 70 | char *drawbuffer_head; 71 | size_t drawbuffer_head_offset; 72 | size_t drawbuffer_len; 73 | void *zbuffer_ref; 74 | 75 | int tex_vram_addr; 76 | int tex_width; 77 | int tex_height; 78 | int tex_psm; 79 | int active_tex; 80 | 81 | int clut_tex; 82 | 83 | struct d2d_gif gif; 84 | struct draw_dma dma; 85 | struct d2d_stats this_frame; 86 | struct d2d_stats last_frame; 87 | }; 88 | */ 89 | 90 | #define RENDER_CTX_NULL 0 91 | #define RENDER_CTX_2D 1 92 | #define RENDER_CTX_3D 2 93 | 94 | struct d2d_state { 95 | unsigned char col[4]; 96 | enum d2d_type draw_type; 97 | }; 98 | 99 | struct commandbuffer { 100 | int target_vif; 101 | int in_frame; 102 | char *ptr; 103 | char *head; 104 | size_t offset; 105 | size_t length; 106 | struct draw_dma dma; 107 | struct draw_gif gif; 108 | struct draw_vif vif; 109 | }; 110 | 111 | struct render_state { 112 | int has_ctx; 113 | char clear[4]; 114 | int screen_w; 115 | int screen_h; 116 | struct commandbuffer buffer; 117 | void *zbuffer; 118 | 119 | int tex_vram_addr; 120 | int tex_width; 121 | int tex_height; 122 | int tex_psm; 123 | int active_tex; 124 | int clut_tex; 125 | 126 | struct draw_stats this_frame; 127 | struct draw_stats last_frame; 128 | 129 | struct d2d_state d2d; 130 | 131 | int target_vif; 132 | }; 133 | 134 | // shared draw functions 135 | 136 | int draw_frame_start(); 137 | int draw_frame_end(); 138 | int draw_set_target(int target_vif); 139 | 140 | int draw_get_context(int ctx); 141 | int draw_end_context(); 142 | 143 | int draw_giftags_begin(struct commandbuffer *c); 144 | 145 | int draw_upload_texture(void *texture, size_t bytes, int width, int height, 146 | int format, int vram_addr); 147 | 148 | int draw_bind_buffer(void *buf, size_t buf_len); 149 | 150 | // 2d renderer API 151 | int draw2d_clear_colour(char r, char g, char b); 152 | 153 | int draw2d_triangle(float x1, float y1, float x2, float y2, float x3, float y3); 154 | int draw2d_textri(float x1, float y1, float u1, float v1, 155 | float x2, float y2, float u2, float v2, 156 | float x3, float y3, float u3, float v3); 157 | int draw2d_rect(float x1, float y1, float w, float h); 158 | 159 | int draw2d_screen_dimensions(int w, int h); 160 | int draw2d_set_colour(unsigned char r, unsigned char g, unsigned char b, 161 | unsigned char a); 162 | 163 | int draw2d_sprite(float x, float y, float w, float h, float u1, float v1, 164 | float u2, float v2); 165 | 166 | int draw2d_bind_texture(int tex_vram_addr, int width, int height, int psm); 167 | 168 | int draw2d_set_clut_state(int texture_base); 169 | 170 | // 3d renderer API 171 | // int draw3d_set_property(int property_id, float value); 172 | // int draw3d_mesh(float *verts, size_t vert_len, uint32_t gif_fmt, 173 | // int gif_fmt_len, int floats_per_vert); 174 | // 175 | int draw3d_ee_transform_verts(float *mvp, size_t offset_start, 176 | int vertex_count, int vertex_size, int pos_offset); 177 | size_t draw3d_mesh_triangles_cnt(void *buffer, int vertex_count, 178 | size_t vertex_size); 179 | int draw3d_mesh_triangles_ref(void *buffer, int vertex_count, 180 | size_t vertex_size); 181 | 182 | // TODO(phy1um): circulare dependency thing 183 | int draw_vifcode_end(struct commandbuffer *c); 184 | int draw_vifcode_direct_start(struct commandbuffer *c); 185 | int draw_vu_end_unpack_inline(struct commandbuffer *c, size_t packet_size); 186 | 187 | 188 | #endif 189 | -------------------------------------------------------------------------------- /src/draw/draw3d.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #include "buffer.h" 17 | #include "draw.h" 18 | #include "internal.h" 19 | 20 | #define GIF_REGS_AD 0xe 21 | #define GIF_REGS_AD_LEN 1 22 | #define GIF_REGS_FMT_GEOM3D 0x515151 23 | #define GIF_REGS_FMT_GEOM3D_LEN 6 24 | 25 | extern struct render_state state; 26 | 27 | #define fp_just_decimal(x) ((x) - ((int)(x))) 28 | 29 | #define to_coord(f) \ 30 | 0x8000 + (0xfff0 & (((int)(f) << 4))) + (fp_just_decimal(f) * 0xf) 31 | 32 | // prepend instance data for batch drawing 33 | int draw3d_instance_xyz(float x, float y, float z) { return 0; } 34 | 35 | int draw3d_instance_rgba(char r, char g, char b, char a) { return 0; } 36 | 37 | int draw3d_instance_header_uint32(uint32_t value) { return 0; } 38 | 39 | // put a mesh into drawbuffer by reference (DMA copies without CPU copy) 40 | int draw3d_mesh_triangles_ref(void *buffer, int vertex_count, 41 | size_t vertex_size) { 42 | trace("triangle mesh ref @ %u", state.buffer.offset); 43 | if (state.buffer.gif.loop_count >= GIF_MAX_LOOPS - 1) { 44 | // wtf do we do here if we have to split 45 | draw_kick(); 46 | } 47 | draw_end_cnt(&state.buffer); 48 | int buffer_size = vertex_count * vertex_size; 49 | int qwc = buffer_size / 4 + (buffer_size % 4 == 0 ? 0 : 1); 50 | draw_dma_ref(&state.buffer, (uint32_t)buffer, qwc); 51 | state.this_frame.tris += vertex_count / 3; 52 | return 0; 53 | } 54 | 55 | // put a mesh into drawbuffer by copy (needed to do CPU transforms) 56 | size_t draw3d_mesh_triangles_cnt(void *buffer, int vertex_count, 57 | size_t vertex_size) { 58 | if (state.buffer.gif.loop_count >= GIF_MAX_LOOPS - 1) { 59 | draw_kick(); 60 | } 61 | 62 | int buffer_size = vertex_count * vertex_size; 63 | 64 | trace("triangle mesh cnt @ %u [#vert = %d, |vert| = %d]", state.buffer.offset, 65 | vertex_count, vertex_size); 66 | 67 | if (state.buffer.offset >= state.buffer.length - buffer_size) { 68 | logerr("no room to CNT draw model"); 69 | return -1; 70 | } 71 | 72 | if (state.d2d.draw_type != DRAW_FMT_GEOM3D) { 73 | if (state.d2d.draw_type != DRAW_FMT_NONE) { 74 | commandbuffer_update_last_tag_loop(&state.buffer); 75 | } 76 | draw_giftags_begin(&state.buffer); 77 | giftag_new(&state.buffer, 0, 1, 0, GIF_REGS_AD_LEN, GIF_REGS_AD); 78 | giftag_ad_prim(&state.buffer, GS_PRIM_TRIANGLE, 0, 0, 0); 79 | giftag_new(&state.buffer, 0, 1, 0, GIF_REGS_FMT_GEOM3D_LEN, 80 | GIF_REGS_FMT_GEOM3D); 81 | state.d2d.draw_type = DRAW_FMT_GEOM; 82 | } 83 | 84 | size_t out = state.buffer.offset; 85 | memcpy(state.buffer.head, buffer, buffer_size); 86 | state.buffer.head += buffer_size; 87 | state.buffer.offset += buffer_size; 88 | state.this_frame.tris += vertex_count / 3; 89 | state.buffer.gif.loop_count += vertex_count / 3; 90 | return out; 91 | } 92 | 93 | // apply 4x4 matrix transform to a mesh copied into the drawbuffer 94 | int draw3d_ee_transform_verts(float *mvp, size_t offset_start, int vertex_count, 95 | int vertex_size, int pos_offset) { 96 | 97 | trace("EE transform @ %u [#vert = %d, |vert| = %d]", offset_start, 98 | vertex_count, vertex_size); 99 | size_t vertex_base = offset_start; 100 | for (int i = 0; i < vertex_count; i++) { 101 | float *vertex = (float *)(state.buffer.ptr + vertex_base); 102 | float *position = vertex + pos_offset; 103 | position[3] = 1.f; 104 | trace(" read verts @ %p(%d) [+%d => %p]", vertex, i, pos_offset, position); 105 | trace(" @ {%f, %f, %f, %f}", position[0], position[1], position[2], 106 | position[3]); 107 | p2m_m4_apply(mvp, position); 108 | trace(" => {%f, %f, %f, %f}", position[0], position[1], position[2], 109 | position[3]); 110 | 111 | // convert to fixed point from float 112 | int *position_fp = (int *)position; 113 | position_fp[0] = to_coord((position[0] / position[3])); 114 | position_fp[1] = to_coord((position[1] / position[3])); 115 | position_fp[2] = (2 << 15) - (int)((-2000 * position[2] / position[3])); 116 | position_fp[3] = 0; 117 | trace(" => {%x, %x, %x, %x}", position_fp[0], position_fp[1], 118 | position_fp[2], position_fp[3]); 119 | 120 | vertex_base += vertex_size; 121 | } 122 | return vertex_count; 123 | } 124 | -------------------------------------------------------------------------------- /src/linux/include/gif_tags.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * GIF Tags 4 | */ 5 | 6 | #ifndef __GIF_TAGS_H__ 7 | #define __GIF_TAGS_H__ 8 | 9 | #include 10 | 11 | /** Not sure if this is correct... */ 12 | #define GIF_BLOCK_SIZE 0x7FFF 13 | 14 | /** Enable PRIM field output */ 15 | #define GIF_PRE_DISABLE 0x00 16 | /** Disable PRIM field output */ 17 | #define GIF_PRE_ENABLE 0x01 18 | 19 | /** Point primitive */ 20 | #define GIF_PRIM_POINT 0x00 21 | /** Line primitive */ 22 | #define GIF_PRIM_LINE 0x01 23 | /** Line strip primitive */ 24 | #define GIF_PRIM_LINE_STRIP 0x02 25 | /** Triangle primitive */ 26 | #define GIF_PRIM_TRIANGLE 0x03 27 | /** Triangle strip primitive */ 28 | #define GIF_PRIM_TRIANGLE_STRIP 0x04 29 | /** Triangle fan primitive */ 30 | #define GIF_PRIM_TRIANGLE_FAN 0x05 31 | /** Sprite primitive */ 32 | #define GIF_PRIM_SPRITE 0x06 33 | 34 | /** Packed GIF packet */ 35 | #define GIF_FLG_PACKED 0x00 36 | /** Reglist GIF packet */ 37 | #define GIF_FLG_REGLIST 0x01 38 | /** Image GIF packet */ 39 | #define GIF_FLG_IMAGE 0x02 40 | 41 | /** Drawing primitive setting. */ 42 | #define GIF_REG_PRIM 0x00 43 | /** Vertex color setting. */ 44 | #define GIF_REG_RGBAQ 0x01 45 | /** Specification of vertex texture coordinates. */ 46 | #define GIF_REG_ST 0x02 47 | /** Specification of vertex texture coordinates. */ 48 | #define GIF_REG_UV 0x03 49 | /** Setting for vertex coordinate values. */ 50 | #define GIF_REG_XYZF2 0x04 51 | /** Setting for vertex coordinate values. */ 52 | #define GIF_REG_XYZ2 0x05 53 | /** Texture information setting. */ 54 | #define GIF_REG_TEX0 0x06 55 | /** Texture information setting. (Context 1) */ 56 | #define GIF_REG_TEX0_1 0x06 57 | /** Texture information setting. (Context 2) */ 58 | #define GIF_REG_TEX0_2 0x07 59 | /** Texture wrap mode. */ 60 | #define GIF_REG_CLAMP 0x08 61 | /** Texture wrap mode. (Context 1) */ 62 | #define GIF_REG_CLAMP_1 0x08 63 | /** Texture wrap mode. (Context 2) */ 64 | #define GIF_REG_CLAMP_2 0x09 65 | /** Vertex fog value setting. */ 66 | #define GIF_REG_FOG 0x0A 67 | /** Setting for vertex coordinate values. (Without Drawing Kick) */ 68 | #define GIF_REG_XYZF3 0x0C 69 | /** Setting for vertex coordinate values. (Without Drawing Kick) */ 70 | #define GIF_REG_XYZ3 0x0D 71 | /** GIFtag Address+Data */ 72 | #define GIF_REG_AD 0x0E 73 | /** GIFtag No Operation */ 74 | #define GIF_REG_NOP 0x0F 75 | 76 | #define PACK_GIFTAG(Q, D0, D1) \ 77 | Q->dw[0] = (u64)(D0), \ 78 | Q->dw[1] = (u64)(D1) 79 | 80 | #define GIF_SET_TAG(NLOOP, EOP, PRE, PRIM, FLG, NREG) \ 81 | (u64)((NLOOP) & 0x00007FFF) << 0 | (u64)((EOP) & 0x00000001) << 15 | \ 82 | (u64)((PRE) & 0x00000001) << 46 | (u64)((PRIM) & 0x000007FF) << 47 | \ 83 | (u64)((FLG) & 0x00000003) << 58 | (u64)((NREG) & 0x0000000F) << 60 84 | 85 | #define GIF_SET_PRIM(PRIM, IIP, TME, FGE, ABE, AA1, FST, CTXT, FIX) \ 86 | (u64)((PRIM) & 0x00000007) << 0 | (u64)((IIP) & 0x00000001) << 3 | \ 87 | (u64)((TME) & 0x00000001) << 4 | (u64)((FGE) & 0x00000001) << 5 | \ 88 | (u64)((ABE) & 0x00000001) << 6 | (u64)((AA1) & 0x00000001) << 7 | \ 89 | (u64)((FST) & 0x00000001) << 8 | (u64)((CTXT) & 0x00000001) << 9 | \ 90 | (u64)((FIX) & 0x00000001) << 10 91 | 92 | #define GIF_SET_RGBAQ(R, G, B, A, Q) \ 93 | (u64)((R) & 0x000000FF) << 0 | (u64)((G) & 0x000000FF) << 8 | \ 94 | (u64)((B) & 0x000000FF) << 16 | (u64)((A) & 0x000000FF) << 24 | \ 95 | (u64)((Q) & 0xFFFFFFFF) << 32 96 | 97 | #define GIF_SET_ST(S, T) \ 98 | (u64)((S) & 0xFFFFFFFF) << 0 | (u64)((T) & 0xFFFFFFFF) << 32 99 | 100 | #define GIF_SET_UV(U, V) \ 101 | (u64)((U) & 0x00003FFF) << 0 | (u64)((V) & 0x00003FFF) << 16 102 | 103 | #define GIF_SET_XYZ(X, Y, Z) \ 104 | (u64)((X) & 0x0000FFFF) << 0 | (u64)((Y) & 0x0000FFFF) << 16 | \ 105 | (u64)((Z) & 0xFFFFFFFF) << 32 106 | 107 | #define GIF_SET_XYZF(X, Y, Z, F) \ 108 | (u64)((X) & 0x0000FFFF) << 0 | (u64)((Y) & 0x0000FFFF) << 16 | \ 109 | (u64)((Z) & 0x00FFFFFF) << 32 | (u64)((F) & 0x000000FF) << 56 110 | 111 | #define GIF_SET_TEX0(TBA, TBW, PSM, TW, TH, TCC, TFNCT, CBA, CPSM, CSM, \ 112 | CSA, CLD) \ 113 | (u64)((TBA) & 0x00003FFF) << 0 | (u64)((TBW) & 0x0000003F) << 14 | \ 114 | (u64)((PSM) & 0x0000003F) << 20 | (u64)((TW) & 0x0000000F) << 26 | \ 115 | (u64)((TH) & 0x0000000F) << 30 | (u64)((TCC) & 0x00000001) << 34 | \ 116 | (u64)((TFNCT) & 0x00000003) << 35 | (u64)((CBA) & 0x00003FFF) << 37 | \ 117 | (u64)((CPSM) & 0x0000000F) << 51 | (u64)((CSM) & 0x00000001) << 55 | \ 118 | (u64)((CSA) & 0x0000001F) << 56 | (u64)((CLD) & 0x00000007) << 61 119 | 120 | #define GIF_SET_CLAMP(WMS, WMT, MINU, MAXU, MINV, MAXV) \ 121 | (u64)((WMS) & 0x00000003) << 0 | (u64)((WMT) & 0x00000003) << 2 | \ 122 | (u64)((MINU) & 0x000003FF) << 4 | (u64)((MAXU) & 0x000003FF) << 14 | \ 123 | (u64)((MINV) & 0x000003FF) << 24 | (u64)((MAXV) & 0x000003FF) << 34 124 | 125 | #define GIF_SET_FOG(FOG) (u64)((FOG) & 0x000000FF) << 56 126 | 127 | #endif /* __GIFTAGS_H__ */ 128 | -------------------------------------------------------------------------------- /src/core/vu.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "../draw/draw.h" 6 | 7 | static const char *VU_PROG_METATABLE = "ps2.vuprog"; 8 | #define PROG_STATE_UNBOUND 0 9 | #define PROG_STATE_BOUND_VU0 1 10 | #define PROG_STATE_BOUND_VU1 2 11 | 12 | static int vu1_alloc_head = 0; 13 | 14 | static int vu_state(lua_State *l) { 15 | lua_getfield(l, 1, "_state"); 16 | return 1; 17 | } 18 | 19 | static int vu_upload(lua_State *l) { 20 | int target = lua_tointeger(l, 2); 21 | if (target != TARGET_VU1) { 22 | luaL_error(l, "only VU1 supported"); 23 | return 1; 24 | } 25 | if (!lua_istable(l, 1)) { 26 | logerr("vu upload: first argument must be table"); 27 | luaL_error(l, "first argument must be table"); 28 | return 1; 29 | } 30 | lua_getfield(l, 1, "buffer"); 31 | if (!lua_istable(l, -1)) { 32 | int type = lua_type(l, -1); 33 | const char *type_name = lua_typename(l, type); 34 | logerr("vu upload: first argument must be a buffer obj"); 35 | luaL_error(l, "first argument must be a buffer object, got: %s", type_name); 36 | return 1; 37 | } 38 | 39 | lua_getfield(l, -1, "size"); 40 | int size = lua_tointeger(l, -1); 41 | lua_pop(l, 1); 42 | lua_getfield(l, -1, "ptr"); 43 | void *ptr = lua_touserdata(l, -1); 44 | lua_pop(l, 1); 45 | int rc = draw_vu_upload_program(ptr, size, vu1_alloc_head, target); 46 | if (rc != 0) { 47 | luaL_error(l, "vu program upload failed"); 48 | return 1; 49 | } 50 | lua_pushinteger(l, vu1_alloc_head); 51 | lua_setfield(l, 1, "address"); 52 | vu1_alloc_head += size; 53 | while (vu1_alloc_head % 16 != 0) { 54 | vu1_alloc_head += 1; 55 | } 56 | lua_pushinteger(l, PROG_STATE_BOUND_VU1); 57 | lua_setfield(l, 1, "_state"); 58 | return 0; 59 | } 60 | 61 | static int vu_upload_to(lua_State *l) { 62 | int target = lua_tointeger(l, 2); 63 | if (target != TARGET_VU1) { 64 | luaL_error(l, "only VU1 supported"); 65 | return 1; 66 | } 67 | if (!lua_istable(l, 1)) { 68 | logerr("vu upload: first argument must be table"); 69 | luaL_error(l, "first argument must be table"); 70 | return 1; 71 | } 72 | lua_getfield(l, 1, "buffer"); 73 | if (!lua_istable(l, -1)) { 74 | int type = lua_type(l, -1); 75 | const char *type_name = lua_typename(l, type); 76 | logerr("vu upload: first argument must be a buffer obj"); 77 | luaL_error(l, "first argument must be a buffer object, got: %s", type_name); 78 | return 1; 79 | } 80 | 81 | int addr = lua_tointeger(l, 3); 82 | trace("lua upload to: vu addr=%d", addr); 83 | 84 | lua_getfield(l, -1, "size"); 85 | int size = lua_tointeger(l, -1); 86 | lua_pop(l, 1); 87 | lua_getfield(l, -1, "ptr"); 88 | void *ptr = lua_touserdata(l, -1); 89 | lua_pop(l, 1); 90 | int rc = draw_vu_upload_program(ptr, size, addr, target); 91 | if (rc != 0) { 92 | luaL_error(l, "vu program upload failed"); 93 | return 1; 94 | } 95 | lua_pushinteger(l, addr); 96 | lua_setfield(l, 1, "address"); 97 | lua_pushinteger(l, PROG_STATE_BOUND_VU1); 98 | lua_setfield(l, 1, "_state"); 99 | return 0; 100 | } 101 | 102 | static int vu_call(lua_State *l) { 103 | trace("get state of VU prog"); 104 | lua_getfield(l, 1, "_state"); 105 | int state = lua_tointeger(l, -1); 106 | lua_pop(l, 1); 107 | if (state == PROG_STATE_UNBOUND) { 108 | lua_getfield(l, 1, "name"); 109 | const char *name = lua_tostring(l, -1); 110 | luaL_error(l, "unbound program call \"%s\"", name); 111 | return 1; 112 | } 113 | trace("get addr of VU prog"); 114 | lua_getfield(l, 1, "address"); 115 | int addr = lua_tointeger(l, -1); 116 | lua_pop(l, 1); 117 | trace("calling VU prog"); 118 | int rc = draw_vu_call_program(addr); 119 | if (rc != 0) { 120 | lua_getfield(l, 1, "name"); 121 | const char *name = lua_tostring(l, -1); 122 | luaL_error(l, "call program \"%s\"", name); 123 | return 1; 124 | } 125 | return 0; 126 | } 127 | 128 | static int vu_free(lua_State *l) { return 0; } 129 | 130 | static int vu_new(lua_State *l) { 131 | const char *name = lua_tostring(l, 1); 132 | lua_createtable(l, 0, 4); 133 | lua_pushinteger(l, PROG_STATE_UNBOUND); 134 | lua_setfield(l, -2, "_state"); 135 | lua_pushstring(l, name); 136 | lua_setfield(l, -2, "name"); 137 | lua_pushinteger(l, 0); 138 | lua_setfield(l, -2, "address"); 139 | lua_pushvalue(l, 2); 140 | if (!lua_istable(l, -1)) { 141 | logerr("(%s): 2nd arg must be a buffer object (table)", name); 142 | luaL_error(l, "(%s): 2nd arg must be a buffer object (table)", name); 143 | return 1; 144 | } 145 | lua_setfield(l, -2, "buffer"); 146 | luaL_getmetatable(l, VU_PROG_METATABLE); 147 | lua_setmetatable(l, -2); 148 | 149 | return 1; 150 | } 151 | 152 | static int vu_clear(lua_State *l) { 153 | vu1_alloc_head = 0; 154 | return 0; 155 | } 156 | 157 | int vu_lua_init(lua_State *l) { 158 | luaL_newmetatable(l, VU_PROG_METATABLE); 159 | lua_createtable(l, 0, 3); 160 | lua_pushcfunction(l, vu_state); 161 | lua_setfield(l, -2, "state"); 162 | lua_pushcfunction(l, vu_upload); 163 | lua_setfield(l, -2, "upload"); 164 | lua_pushcfunction(l, vu_upload_to); 165 | lua_setfield(l, -2, "upload_to"); 166 | lua_pushcfunction(l, vu_call); 167 | lua_setfield(l, -2, "call"); 168 | lua_pushcfunction(l, vu_free); 169 | lua_setfield(l, -2, "free"); 170 | 171 | lua_setfield(l, -2, "__index"); 172 | lua_pop(l, 1); 173 | 174 | lua_createtable(l, 0, 4); 175 | lua_pushcfunction(l, vu_new); 176 | lua_setfield(l, -2, "new_program"); 177 | lua_pushcfunction(l, vu_clear); 178 | lua_setfield(l, -2, "clear_programs"); 179 | lua_pushinteger(l, TARGET_VU0); 180 | lua_setfield(l, -2, "VU0"); 181 | lua_pushinteger(l, TARGET_VU1); 182 | lua_setfield(l, -2, "VU1"); 183 | 184 | return 1; 185 | } 186 | -------------------------------------------------------------------------------- /src/ps2math.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | float p2m_vec2_length(const float *v2) { 7 | float xs = v2[0] * v2[0]; 8 | float ys = v2[1] * v2[1]; 9 | return sqrtf(xs + ys); 10 | } 11 | 12 | float p2m_vec2_dot(const float *a, const float *b) { 13 | return (a[0] * b[0]) + (a[1] * b[1]); 14 | } 15 | 16 | void p2m_vec2_rotate(float *v, float theta) { 17 | float x = v[0]; 18 | float y = v[1]; 19 | v[0] = x * cosf(theta) - y * sinf(theta); 20 | v[1] = x * sinf(theta) + y * cosf(theta); 21 | } 22 | 23 | void p2m_vec2_scale(float *v, float s) { 24 | v[0] *= s; 25 | v[1] *= s; 26 | } 27 | 28 | float p2m_vec3_length(const float *v3) { 29 | float xs = v3[0] * v3[0]; 30 | float ys = v3[1] * v3[1]; 31 | float zs = v3[2] * v3[2]; 32 | return sqrtf(xs + ys + zs); 33 | } 34 | 35 | float p2m_vec3_dot(const float *a, const float *b) { 36 | return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]); 37 | } 38 | 39 | void p2m_vec3_scale(float *v, float s) { 40 | v[0] *= s; 41 | v[1] *= s; 42 | v[2] *= s; 43 | } 44 | 45 | float p2m_vec4_length(const float *v4) { 46 | float xs = v4[0] * v4[0]; 47 | float ys = v4[1] * v4[1]; 48 | float zs = v4[2] * v4[2]; 49 | float ws = v4[3] * v4[3]; 50 | return sqrtf(xs + ys + zs + ws); 51 | } 52 | 53 | float p2m_vec4_dot(const float *a, const float *b) { 54 | return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]) + (a[3] + b[3]); 55 | } 56 | 57 | void p2m_vec4_scale(float *v, float s) { 58 | v[0] *= s; 59 | v[1] *= s; 60 | v[2] *= s; 61 | v[3] *= s; 62 | } 63 | 64 | void p2m_m3_copy(float *a, const float *b) { 65 | for (int i = 0; i < 9; i++) { 66 | a[i] = b[i]; 67 | } 68 | } 69 | 70 | void p2m_m3_apply(const float *m3, float *v) { 71 | float nx = m3[0] * v[0] + m3[1] * v[1] + m3[2] * v[2]; 72 | float ny = m3[3] * v[0] + m3[4] * v[1] + m3[5] * v[2]; 73 | float nz = m3[6] * v[0] + m3[7] * v[1] + m3[8] * v[2]; 74 | v[0] = nx; 75 | v[1] = ny; 76 | v[2] = nz; 77 | } 78 | 79 | void p2m_m3_add(float *a, const float *b) { 80 | for (int i = 0; i < 9; i++) { 81 | a[i] += b[i]; 82 | } 83 | } 84 | 85 | void p2m_m3_identity(float *m) { 86 | m[0] = 1; 87 | m[1] = 0; 88 | m[2] = 0; 89 | m[3] = 0; 90 | m[4] = 1; 91 | m[5] = 0; 92 | m[6] = 0; 93 | m[7] = 0; 94 | m[8] = 1; 95 | } 96 | 97 | void p2m_m3_multiply(const float *a, const float *b, float *to) { 98 | float a00 = a[0]; 99 | float a10 = a[1]; 100 | float a20 = a[2]; 101 | float a01 = a[3]; 102 | float a11 = a[4]; 103 | float a21 = a[5]; 104 | float a02 = a[6]; 105 | float a12 = a[7]; 106 | float a22 = a[8]; 107 | float b00 = b[0]; 108 | float b10 = b[1]; 109 | float b20 = b[2]; 110 | float b01 = b[3]; 111 | float b11 = b[4]; 112 | float b21 = b[5]; 113 | float b02 = b[6]; 114 | float b12 = b[7]; 115 | float b22 = b[8]; 116 | 117 | to[0] = a00 * b00 + a10 * b01 + a20 * b02; 118 | to[3] = a01 * b00 + a11 * b01 + a21 * b02; 119 | to[6] = a02 * b00 + a12 * b01 + a22 * b02; 120 | to[1] = a00 * b10 + a10 * b11 + a20 * b12; 121 | to[4] = a01 * b10 + a11 * b11 + a21 * b12; 122 | to[7] = a02 * b10 + a12 * b11 + a22 * b12; 123 | to[2] = a00 * b20 + a10 * b21 + a20 * b22; 124 | to[5] = a01 * b20 + a11 * b21 + a21 * b22; 125 | to[8] = a02 * b20 + a12 * b21 + a22 * b22; 126 | } 127 | 128 | void p2m_m4_identity(float *m) { 129 | for (int i = 1; i < 16; i++) { 130 | m[i] = 0; 131 | } 132 | m[0] = 1; 133 | m[5] = 1; 134 | m[10] = 1; 135 | m[15] = 1; 136 | } 137 | 138 | void p2m_m4_apply(const float *m, float *v) { 139 | float nx = m[0]*v[0] + m[1]*v[1] + m[2]*v[2] + m[3]*v[3]; 140 | float ny = m[4]*v[0] + m[5]*v[1] + m[6]*v[2] + m[7]*v[3]; 141 | float nz = m[8]*v[0] + m[9]*v[1] + m[10]*v[2] + m[11]*v[3]; 142 | float nw = m[12]*v[0] + m[13]*v[1] + m[14]*v[2] + m[15]*v[3]; 143 | v[0] = nx; 144 | v[1] = ny; 145 | v[2] = nz; 146 | v[3] = nw; 147 | } 148 | 149 | void p2m_m4_copy(float *a, const float *b) { 150 | for (int i = 0; i < 16; i++) { 151 | a[i] = b[i]; 152 | } 153 | } 154 | 155 | void p2m_m4_add(float *a, const float *b) { 156 | for (int i = 0; i < 16; i++) { 157 | a[i] += b[i]; 158 | } 159 | } 160 | 161 | void p2m_m4_multiply(const float *a, const float *b, float *to) { 162 | float a00 = a[0]; 163 | float a10 = a[1]; 164 | float a20 = a[2]; 165 | float a30 = a[3]; 166 | float a01 = a[4]; 167 | float a11 = a[5]; 168 | float a21 = a[6]; 169 | float a31 = a[7]; 170 | float a02 = a[8]; 171 | float a12 = a[9]; 172 | float a22 = a[10]; 173 | float a32 = a[11]; 174 | float a03 = a[12]; 175 | float a13 = a[13]; 176 | float a23 = a[14]; 177 | float a33 = a[15]; 178 | 179 | float b00 = b[0]; 180 | float b10 = b[1]; 181 | float b20 = b[2]; 182 | float b30 = b[3]; 183 | float b01 = b[4]; 184 | float b11 = b[5]; 185 | float b21 = b[6]; 186 | float b31 = b[7]; 187 | float b02 = b[8]; 188 | float b12 = b[9]; 189 | float b22 = b[10]; 190 | float b32 = b[11]; 191 | float b03 = b[12]; 192 | float b13 = b[13]; 193 | float b23 = b[14]; 194 | float b33 = b[15]; 195 | 196 | to[0] = a00*b00 + a10*b01 + a20*b02 + a30*b03; 197 | to[1] = a00*b10 + a10*b11 + a20*b12 + a30*b13; 198 | to[2] = a00*b20 + a10*b21 + a20*b22 + a30*b23; 199 | to[3] = a00*b30 + a10*b31 + a20*b32 + a30*b33; 200 | 201 | to[4] = a01*b00 + a11*b01 + a21*b02 + a31*b03; 202 | to[5] = a01*b10 + a11*b11 + a21*b12 + a31*b13; 203 | to[6] = a01*b20 + a11*b21 + a21*b22 + a31*b23; 204 | to[7] = a01*b30 + a11*b31 + a21*b32 + a31*b33; 205 | 206 | to[8] = a02*b00 + a12*b01 + a22*b02 + a32*b03; 207 | to[9] = a02*b10 + a12*b11 + a22*b12 + a32*b13; 208 | to[10]= a02*b20 + a12*b21 + a22*b22 + a32*b23; 209 | to[11]= a02*b30 + a12*b31 + a22*b32 + a32*b33; 210 | 211 | to[12]= a03*b00 + a13*b01 + a23*b02 + a33*b03; 212 | to[13]= a03*b10 + a13*b11 + a23*b12 + a33*b13; 213 | to[14]= a03*b20 + a13*b21 + a23*b22 + a33*b23; 214 | to[15]= a03*b30 + a13*b31 + a23*b32 + a33*b33; 215 | } 216 | -------------------------------------------------------------------------------- /asset/pipeline/tga.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | HEADER_FMT = "\nCM SPEC={self.col_spec}\nIMG SPEC={self.img_spec}" 24 | 25 | class TgaImgSpec: 26 | def __init__(self, xo, yo, w, h, bits_per_pix, desc): 27 | self.xorigin = xo 28 | self.yorigin = yo 29 | self.width = w 30 | self.height = h 31 | self.bits_per_pixel = bits_per_pix 32 | self.desc = desc 33 | 34 | def __str__(self): 35 | return f"[ {self.xorigin} {self.yorigin} {self.width} {self.height} {self.bits_per_pixel} {self.desc} ]" 36 | 37 | class TgaColSpec: 38 | def __init__(self, map_origin, map_len, entry_bits): 39 | self.map_origin = map_origin 40 | self.map_len = map_len 41 | self.entry_bits = entry_bits 42 | 43 | def __str__(self): 44 | return f"[ {self.map_origin} {self.map_len} {self.entry_bits} ]" 45 | 46 | def parse(b): 47 | header_size = struct.calcsize(HEADER_FMT) 48 | header_bytes = b[:header_size] 49 | v = struct.unpack(HEADER_FMT, header_bytes) 50 | col_spec = TgaColSpec(v[3], v[4], v[5]) 51 | img_spec = TgaImgSpec(v[6], v[7], v[8], v[9], v[10], v[11]) 52 | header = TgaHeader(v[0], v[1], v[2], col_spec, img_spec) 53 | print(header) 54 | head = header_size 55 | ident = b[head:head+header.id_len] 56 | head = head+header.id_len 57 | col_map_size = header.col_spec.map_len * (header.col_spec.entry_bits // 8) 58 | col_data = b[head:head+col_map_size] 59 | head = head+col_map_size 60 | img_size = header.img_spec.width * header.img_spec.height * (header.img_spec.bits_per_pixel // 8) 61 | img_data = b[head:head+img_size] 62 | return TGA(header, 63 | ident.decode("ascii"), 64 | handle_pixels(header.col_spec.entry_bits, col_data), 65 | handle_pixels(header.img_spec.bits_per_pixel, img_data)) 66 | 67 | 68 | def handle_pixels(bits_per_pix, img_data): 69 | if len(img_data) == 0: 70 | return b"" 71 | print(f"bits per pixel = {bits_per_pix}, BPP={bits_per_pix // 8}") 72 | bpp = bits_per_pix // 8 73 | if bpp == 1: 74 | return list(map(lambda x: x[0], struct.iter_unpack("B", img_data))) 75 | elif bpp == 2: 76 | return list(map(lambda x: x[0], struct.iter_unpack(">8)&0xff 121 | r = (p>>16)&0xff 122 | out.append(b) 123 | out.append(g) 124 | out.append(r) 125 | return out 126 | 127 | def packed_nibbles(pixels): 128 | out = bytearray() 129 | ln = pixels[::2] 130 | un = pixels[1::2] 131 | for i in range(len(un)): 132 | bb = (ln[i]&0xf | ((un[i]&0xf) << 4)) 133 | out.append(bb) 134 | if len(ln) > len(un): 135 | out.append(ln[-1]&0xf) 136 | return out 137 | 138 | 139 | def write(tga): 140 | header_bytes = struct.pack(HEADER_FMT, 141 | tga.header.id_len, 142 | tga.header.col_map_type, 143 | tga.header.img_type, 144 | tga.header.col_spec.map_origin, 145 | tga.header.col_spec.map_len, 146 | tga.header.col_spec.entry_bits, 147 | tga.header.img_spec.xorigin, 148 | tga.header.img_spec.yorigin, 149 | tga.header.img_spec.width, 150 | tga.header.img_spec.height, 151 | tga.header.img_spec.bits_per_pixel, 152 | tga.header.img_spec.desc) 153 | 154 | ident_bytes = tga.ident.encode("ascii") 155 | col_bytes = encode_pixels(tga.header.col_spec.entry_bits, tga.col) 156 | img_bytes = encode_pixels(tga.header.img_spec.bits_per_pixel, tga.img) 157 | return header_bytes + ident_bytes + col_bytes + img_bytes + FOOTER 158 | 159 | if __name__ == "__main__": 160 | """ test script - read tga in, output "out.tga" which should be identical 161 | """ 162 | import sys 163 | with open(sys.argv[1], "rb") as f: 164 | bb = f.read() 165 | tga = parse(bb) 166 | print("READ TGA WITH HEADER:") 167 | print(str(tga.header)) 168 | nh = write(tga) 169 | with open("out.tga", "wb") as r: 170 | r.write(nh) 171 | 172 | --------------------------------------------------------------------------------