diff --git a/Makefile b/Makefile index cc9e49f..76c944f 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,8 @@ SRC = SRC += $(GEN_SRC) SRC += main.cpp SRC += input.cpp +SRC += vram.cpp +SRC += font.cpp DEP = $(patsubst %.cpp,%.cpp.d,$(SRC)) @@ -26,8 +28,9 @@ GFX_TILESETS = $(call res_png,2bpp,pokered/gfx/tilesets/) GFX_BLOCKSETS = $(call res,bst,pokered/gfx/blocksets/) GFX_SPRITES = $(call res_png,2bpp,pokered/gfx/sprites/) MAPS_BLOCKS = $(call res,blk,pokered/maps/) +FONTS = res/font.2bpp.o -GENERATED = $(GFX_TILESETS) $(GFX_BLOCKSETS) $(GFX_SPRITES) $(MAPS_BLOCKS) $(GEN_SRC) +GENERATED = $(GFX_TILESETS) $(GFX_BLOCKSETS) $(GFX_SPRITES) $(MAPS_BLOCKS) $(GEN_SRC) $(FONTS) OBJ = $(patsubst %.cpp,%.o,$(SRC) $(GENERATED)) @@ -52,9 +55,20 @@ gen/%.hpp: $(GEN_PYTHON_SOURCE) gen/%.cpp: gen/%.hpp $(GEN_PYTHON_SOURCE) $(RUN_GENERATE) $@ -res/%.2bpp: pokered/%.png +define PNG_TO_2BPP @mkdir -p $(dir $@) python tools/png_to_nbpp.py 2 $@ $< +endef + +res/%.2bpp: derived/%.png + $(PNG_TO_2BPP) + +res/%.2bpp: pokered/%.png + $(PNG_TO_2BPP) + +res/.1bpp: pokered/%.png + @mkdir -p $(dir $@) + python tools/png_to_nbpp.py 1 $@ $< res/gfx/sprites/%.2bpp: pokered/gfx/sprites/%.png @mkdir -p $(dir $@) diff --git a/derived/font.png b/derived/font.png new file mode 100644 index 0000000..5762a9e Binary files /dev/null and b/derived/font.png differ diff --git a/font.cpp b/font.cpp new file mode 100644 index 0000000..79ed264 --- /dev/null +++ b/font.cpp @@ -0,0 +1,40 @@ +#include "font.hpp" +#include "vram.hpp" +#include "start_size.hpp" + +#include "res/font.2bpp.h" +#include "font.hpp" + +constexpr inline uint8_t ascii_to_font(uint8_t c) +{ + if (c >= 'A' && c <= 'Z') + return (c - 'A') + 0; + if (c >= 'a' && c <= 'z') + return (c - 'a') + 32; + if (c >= '0' && c <= '9') + return (c - '0') + 64; + + switch (c) { + case '!': return 26; + case '(': return 27; + case ')': return 28; + case ',': return 29; + case '-': return 30; + case '.': return 31; + case '/': return 58; + case ':': return 59; + case ';': return 60; + case '?': return 61; + case '[': return 62; + case ']': return 63; + } +} + +uint32_t load_font(uint32_t top) +{ + static start_size_t start_size = { + reinterpret_cast(&_binary_res_font_2bpp_start), + reinterpret_cast(&_binary_res_font_2bpp_size), + }; + return cell_data(start_size, top); +} diff --git a/font.hpp b/font.hpp new file mode 100644 index 0000000..0ce032e --- /dev/null +++ b/font.hpp @@ -0,0 +1,5 @@ +#pragma once + +#include + +uint32_t load_font(uint32_t top); diff --git a/main.cpp b/main.cpp index c1150cd..b18b485 100644 --- a/main.cpp +++ b/main.cpp @@ -11,6 +11,8 @@ #include "common/intback.hpp" #include "input.hpp" +#include "vram.hpp" +#include "font.hpp" #include "gen/maps.hpp" #include "gen/sprites.hpp" @@ -21,67 +23,9 @@ #include "actor.hpp" #include "ledge_tiles.hpp" -constexpr inline uint16_t rgb15(int32_t r, int32_t g, int32_t b) -{ - return ((b & 31) << 10) | ((g & 31) << 5) | ((r & 31) << 0); -} - -void palette_data() -{ - vdp2.cram.u16[3] = rgb15( 0, 0, 0); - vdp2.cram.u16[2] = rgb15(10, 10, 10); - vdp2.cram.u16[1] = rgb15(21, 21, 21); - vdp2.cram.u16[0] = rgb15(31, 31, 31); -} - -static inline void _2bpp_4bpp_vram_copy(uint32_t * vram, const start_size_t& buf) -{ - for (uint32_t ix = 0; ix < buf.size / 4; ix += 1) { - const uint32_t pixels = reinterpret_cast(buf.start)[ix]; - const uint32_t px0 = pixels >> 16 & 0xffff; - const uint32_t px1 = pixels >> 0 & 0xffff; - -#define lshift(n) ((7 - n) * 2) -#define rshift(n) ((7 - n) * 4) -#define px(p, n) (((p >> lshift(n)) & 0b11) << rshift(n)) -#define p0(n) (px(px0, n)) -#define p1(n) (px(px1, n)) - vram[ix * 2 + 0] = p0(7) | p0(6) | p0(5) | p0(4) | p0(3) | p0(2) | p0(1) | p0(0); - vram[ix * 2 + 1] = p1(7) | p1(6) | p1(5) | p1(4) | p1(3) | p1(2) | p1(1) | p1(0); -#undef p1 -#undef p0 -#undef px -#undef lshift -#undef rshift - } -} - -uint32_t character_pattern_table(const start_size_t& buf, const uint32_t top) -{ - // round to nearest multiple of 32 - const uint32_t table_size = ((buf.size * 2) + 0x20 - 1) & (-0x20); - const uint32_t base_address = top - table_size; - - uint32_t * vram = &vdp1.vram.u32[(base_address / 4)]; - _2bpp_4bpp_vram_copy(vram, buf); - - return base_address; -} - -uint32_t cell_data(const start_size_t& buf, const uint32_t top) -{ - // round to nearest multiple of 32 - const uint32_t table_size = ((buf.size * 2) + 0x20 - 1) & (-0x20); - const uint32_t base_address = top - table_size; // in bytes - - uint32_t * vram = &vdp2.vram.u32[(base_address / 4)]; - _2bpp_4bpp_vram_copy(vram, buf); - - return base_address; -} - struct draw_t { struct { + uint16_t font; // div 32 uint16_t tilesets[tileset_t::count]; // div 32 uint16_t spritesheets[spritesheet_t::count]; // div 128 } base_pattern; @@ -121,13 +65,16 @@ void load_vram() vdp2.reg.CYCB1 = 0xeeee'eeee; uint32_t vdp2_top = (sizeof (union vdp2_vram)); + vdp2_top = load_font(vdp2_top); + state.draw.base_pattern.font = vdp2_top / 32; + for (uint32_t i = 0; i < tileset_t::count; i++) vdp2_top = load_tileset(vdp2_top, static_cast(i)); - vdp2.reg.CYCA0 = 0x0fff'ffff; - vdp2.reg.CYCA1 = 0x0fff'ffff; - vdp2.reg.CYCB0 = 0x4fff'ffff; - vdp2.reg.CYCB1 = 0x4fff'ffff; + vdp2.reg.CYCA0 = 0x01ff'ffff; + vdp2.reg.CYCA1 = 0x01ff'ffff; + vdp2.reg.CYCB0 = 0x45ff'ffff; + vdp2.reg.CYCB1 = 0x45ff'ffff; uint32_t vdp1_top = (sizeof (union vdp1_vram)); for (uint32_t i = 0; i < spritesheet_t::count; i++) @@ -164,7 +111,7 @@ void render_sprite(const uint32_t ix, const uint32_t sprite_id, const screen_t& screen, const offset_t& offset, int32_t y_offset) { - constexpr uint32_t color_address = 0; + constexpr uint32_t color_address = 16; const uint32_t sprite_offset = facing_offset(facing) + animation_frame; const uint32_t base_pattern = state.draw.base_pattern.spritesheets[sprite_id]; const uint32_t character_address = ((base_pattern + sprite_offset) * 128) / 8; @@ -257,7 +204,7 @@ void render_map() } } - vdp2.reg.BGON = BGON__N0ON | BGON__N0TPON; + vdp2.reg.BGON = BGON__N0ON | BGON__N0TPON | BGON__N1ON; } constexpr inline world_t direction_offset(const world_t& world, const enum actor_t::direction dir) @@ -592,29 +539,23 @@ void init_vdp2() 1-word: value of bit 6-0 * 0x2000 2-word: value of bit 5-0 * 0x4000 */ - constexpr int nbg0_plane_a = 0; - constexpr int nbg1_plane_a = 1; + constexpr int nbg0_plane = 0; + constexpr int nbg1_plane = 1; //constexpr int plane_a_offset = plane_a * 0x2000; //constexpr int page_size = 64 * 64 * 2; // N0PNB__1WORD (16-bit) //constexpr int plane_size = page_size * 1; // bits 8~6 - vdp2.reg.MPOFN = MPOFN__N0MP(0) - | MPOFN__N1MP(0); - vdp2.reg.MPABN0 = MPABN0__N0MPB(0) | MPABN0__N0MPA(nbg0_plane_a); // bits 5~0 - vdp2.reg.MPCDN0 = MPCDN0__N0MPD(0) | MPCDN0__N0MPC(0); // bits 5~0 + vdp2.reg.MPOFN = MPOFN__N0MP(nbg0_plane >> 6) + | MPOFN__N1MP(nbg1_plane >> 6); + vdp2.reg.MPN0 = MPN0__N0MP(nbg0_plane); + vdp2.reg.MPN1 = MPN1__N1MP(nbg1_plane); - vdp2.reg.MPABN1 = MPABN1__N1MPB(0) | MPABN1__N1MPA(nbg1_plane_a); // bits 5~0 - vdp2.reg.MPCDN1 = MPCDN1__N1MPD(0) | MPCDN1__N1MPC(0); // bits 5~0 - - auto& base_pattern = state.draw.base_pattern.tilesets[tileset_t::overworld]; - - vdp2.reg.PNCN0 = PNCN0__N0PNB__1WORD | PNCN0__N0CNSM | PNCN0__N0SCN((base_pattern >> 10) & 0x1f); + const uint32_t base_pattern = state.draw.base_pattern.font; vdp2.reg.PNCN1 = PNCN1__N1PNB__1WORD | PNCN1__N1CNSM | PNCN1__N1SCN((base_pattern >> 10) & 0x1f); - - vdp2.vram.u16[0x2000 / 2 + 0] = (base_pattern & 0xfff) + 1; - vdp2.vram.u16[0x2000 / 2 + 1] = (base_pattern & 0xfff) + 2; + const uint32_t value = ((base_pattern + 127) & 0xfff) | PATTERN_NAME_TABLE_1WORD__PALETTE(1); + fill(&vdp2.vram.u32[0 / 4], value | value << 16, 0x4000); palette_data(); } @@ -638,13 +579,14 @@ void main() constexpr uint16_t top_y = 48 - 1; constexpr uint16_t bot_x = 239 + 1; constexpr uint16_t bot_y = 191 + 1; - - vdp2.reg.WCTLA = WCTLA__N0W0E | WCTLA__N0W0A__OUTSIDE; vdp2.reg.WPSX0 = top_x << 1; vdp2.reg.WPSY0 = top_y; vdp2.reg.WPEX0 = bot_x << 1; vdp2.reg.WPEY0 = bot_y; + vdp2.reg.WCTLA = WCTLA__N0W0E | WCTLA__N0W0A__OUTSIDE; + vdp2.reg.WCTLC = WCTLC__SPW0E | WCTLC__SPW0A__OUTSIDE; + // free-running timer sh2.reg.TCR = TCR__CKS__INTERNAL_DIV128; sh2.reg.FTCSR = 0; diff --git a/start_size.hpp b/start_size.hpp index 852c736..776059c 100644 --- a/start_size.hpp +++ b/start_size.hpp @@ -1,5 +1,7 @@ #pragma once +#include + struct start_size_t { uint8_t const * const start; uint32_t size; diff --git a/tools/png_to_nbpp.py b/tools/png_to_nbpp.py index 5065f40..eb5399c 100644 --- a/tools/png_to_nbpp.py +++ b/tools/png_to_nbpp.py @@ -1,5 +1,6 @@ import os import sys +import io from PIL import Image @@ -13,19 +14,24 @@ def convert(image, bpp): bytes_per_row = (px_per_row // (bits_per_byte // bpp)) assert image.mode in {'L', '1'}, image.mode + width, height = image.size buf = bytearray(width * height // px_per_byte) + print(len(buf)) for cell_y in range(height//8): for cell_x in range(width//8): for y in range(8): for x in range(8): px = image.getpixel((cell_x * 8 + x, cell_y * 8 + y)) - if image.mode == 'L': - index = intensity_to_index(px) - elif image.mode == '1': + if bpp == 1: + assert px in {255, 0}, px index = int(px != 0) + else: + index = intensity_to_index(px) + assert index < (2 ** bpp), index + buf_ix = x//px_per_byte + (bytes_per_row * (cell_x * 8 + (cell_y * width) + y)) buf[buf_ix] |= (index << bpp * ((px_per_byte - 1) - (x % px_per_byte))) return buf @@ -51,10 +57,15 @@ bpp = int(sys.argv[1]) out_path = sys.argv[2] assert len(sys.argv) >= 4, sys.argv +tmp = io.BytesIO() + +for in_path in sys.argv[3:]: + print(in_path) + im = Image.open(in_path) + buf = convert(im, bpp) + if 'NBPP_DEBUG' in os.environ: + debug(buf, bpp) + tmp.write(buf) + with open(out_path, 'wb') as f: - for in_path in sys.argv[3:]: - im = Image.open(in_path) - buf = convert(im, bpp) - if 'NBPP_DEBUG' in os.environ: - debug(buf, bpp) - f.write(buf) + f.write(tmp.getvalue()) diff --git a/vram.cpp b/vram.cpp new file mode 100644 index 0000000..1b821f8 --- /dev/null +++ b/vram.cpp @@ -0,0 +1,63 @@ +#include "vdp2.h" +#include "vdp1.h" + +#include "vram.hpp" + +void palette_data() +{ + vdp2.cram.u16[3] = rgb15( 0, 0, 0); + vdp2.cram.u16[2] = rgb15(10, 10, 10); + vdp2.cram.u16[1] = rgb15(21, 21, 21); + vdp2.cram.u16[0] = rgb15(31, 31, 31); + + vdp2.cram.u16[16 + 3] = rgb15( 0, 0, 0); + vdp2.cram.u16[16 + 2] = rgb15(21, 21, 21); + vdp2.cram.u16[16 + 1] = rgb15(31, 31, 31); + vdp2.cram.u16[16 + 0] = 0; // transparent +} + +static void _2bpp_4bpp_vram_copy(uint32_t * vram, const start_size_t& buf) +{ + for (uint32_t ix = 0; ix < buf.size / 4; ix += 1) { + const uint32_t pixels = reinterpret_cast(buf.start)[ix]; + const uint32_t px0 = pixels >> 16 & 0xffff; + const uint32_t px1 = pixels >> 0 & 0xffff; + +#define lshift(n) ((7 - n) * 2) +#define rshift(n) ((7 - n) * 4) +#define px(p, n) (((p >> lshift(n)) & 0b11) << rshift(n)) +#define p0(n) (px(px0, n)) +#define p1(n) (px(px1, n)) + vram[ix * 2 + 0] = p0(7) | p0(6) | p0(5) | p0(4) | p0(3) | p0(2) | p0(1) | p0(0); + vram[ix * 2 + 1] = p1(7) | p1(6) | p1(5) | p1(4) | p1(3) | p1(2) | p1(1) | p1(0); +#undef p1 +#undef p0 +#undef px +#undef lshift +#undef rshift + } +} + +uint32_t character_pattern_table(const start_size_t& buf, const uint32_t top) +{ + // round to nearest multiple of 32 + const uint32_t table_size = ((buf.size * 2) + 0x20 - 1) & (-0x20); + const uint32_t base_address = top - table_size; + + uint32_t * vram = &vdp1.vram.u32[(base_address / 4)]; + _2bpp_4bpp_vram_copy(vram, buf); + + return base_address; +} + +uint32_t cell_data(const start_size_t& buf, const uint32_t top) +{ + // round to nearest multiple of 32 + const uint32_t table_size = ((buf.size * 2) + 0x20 - 1) & (-0x20); + const uint32_t base_address = top - table_size; // in bytes + + uint32_t * vram = &vdp2.vram.u32[(base_address / 4)]; + _2bpp_4bpp_vram_copy(vram, buf); + + return base_address; +} diff --git a/vram.hpp b/vram.hpp new file mode 100644 index 0000000..473d496 --- /dev/null +++ b/vram.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include "start_size.hpp" + +constexpr inline uint16_t rgb15(int32_t r, int32_t g, int32_t b) +{ + return ((b & 31) << 10) | ((g & 31) << 5) | ((r & 31) << 0); +} + +void palette_data(); + +uint32_t character_pattern_table(const start_size_t& buf, const uint32_t top); + +uint32_t cell_data(const start_size_t& buf, const uint32_t top);