From aa82984472bc9b5ea331580c9f5b71b02e807556 Mon Sep 17 00:00:00 2001 From: Mateusz Faderewski Date: Sun, 22 Jan 2023 18:06:17 +0100 Subject: [PATCH] [SC64][SW][DOCS] Reduced bootloader binary size / updated readme --- README.md | 3 +- sw/bootloader/Makefile | 190 ++++++++++++------------- sw/bootloader/src/display.c | 38 ++++- sw/bootloader/tools/asset_converter.py | 39 ++++- 4 files changed, 163 insertions(+), 107 deletions(-) diff --git a/README.md b/README.md index 09f4a10..f88240e 100644 --- a/README.md +++ b/README.md @@ -15,11 +15,12 @@ - Status LED and button for general use - 64DD add-on emulation - IS-Viewer 64 debug interface - - Software and firmware update via USB interface - N64 bootloader with support for IPL3 registers spoofing and loading menu from SD card - Enhanced [UltraCIC_C](https://github.com/jago85/UltraCIC_C) emulation with automatic region switching and programmable seed/checksum values - PC app for communicating with flashcart (game/save data upload/download, feature enable control and debug terminal) - [UNFLoader](https://github.com/buu342/N64-UNFLoader) support + - Initial board programming via UART header on board or dedicated JTAG/SWD interfaces + - Software and firmware update via USB interface - 3D printable plastic shell --- diff --git a/sw/bootloader/Makefile b/sw/bootloader/Makefile index 18001b4..2e8257b 100644 --- a/sw/bootloader/Makefile +++ b/sw/bootloader/Makefile @@ -1,95 +1,95 @@ -TOOLCHAIN = mips64-elf- -CC = $(TOOLCHAIN)gcc -CXX = $(TOOLCHAIN)g++ -OBJCOPY = $(TOOLCHAIN)objcopy -OBJDUMP = $(TOOLCHAIN)objdump -SIZE = $(TOOLCHAIN)size -PYTHON = python3 - -FLAGS = -march=vr4300 -mtune=vr4300 $(USER_FLAGS) -CFLAGS = -Os -Wall -ffunction-sections -fdata-sections -ffreestanding -MMD -MP -ASFLAGS = -Wa,-I$(N64_INST)/mips64-elf/lib -LDFLAGS = -lc -nostartfiles -Wl,--gc-sections - -SRC_DIR = src -ASSET_DIR = assets -BUILD_DIR = build - -SRC_FILES = \ - startup.S \ - boot.c \ - crc32.c \ - display.c \ - error.c \ - exception.c \ - exception.S \ - font.c \ - init.c \ - interrupt.c \ - io.c \ - ipl2.S \ - main.c \ - menu.c \ - sc64.c \ - syscalls.c \ - test.c \ - version.c \ - fatfs/diskio.c \ - fatfs/ff.c \ - fatfs/ffsystem.c \ - fatfs/ffunicode.c - -ASSET_FILES = \ - sc64_logo_640_240_dimmed.png - -SRCS = $(SRC_FILES) $(ASSET_FILES) -SRC_OBJS = $(patsubst %,%.o,$(SRC_FILES)) -ASSET_OBJS = $(patsubst %,%.asset.o,$(basename $(ASSET_FILES))) -OBJS = $(addprefix $(BUILD_DIR)/,$(notdir $(SRC_OBJS) $(ASSET_OBJS))) -DEPS = $(OBJS:.o=.d) -VPATH = $(SRC_DIR) $(SRC_DIR)/fatfs $(ASSET_DIR) - -$(@info $(shell mkdir -p ./$(BUILD_DIR) &> /dev/null)) - -$(BUILD_DIR)/%.S.o: %.S - $(CC) -x assembler-with-cpp $(FLAGS) $(ASFLAGS) $(CFLAGS) -c $< -o $@ - -$(BUILD_DIR)/%.c.o: %.c - $(CC) $(FLAGS) $(CFLAGS) -c $< -o $@ - -$(BUILD_DIR)/%.asset: $(ASSET_DIR)/%.png - $(PYTHON) tools/asset_converter.py $< $@ - -$(BUILD_DIR)/%.asset.o: $(BUILD_DIR)/%.asset $(ASSET_DIR)/assets.S - @sed -e "s,@sym@,$*,g" -e "s,@file@,$<," < $(ASSET_DIR)/assets.S | $(CC) -x assembler-with-cpp $(FLAGS) $(ASFLAGS) $(CFLAGS) -c - -o $@ - -$(BUILD_DIR)/bootloader.elf: $(OBJS) N64.ld - $(CXX) $(FLAGS) $(LDFLAGS) -TN64.ld $(OBJS) -o $@ - @$(OBJDUMP) -S $@ > $(BUILD_DIR)/bootloader.lst - -$(BUILD_DIR)/bootloader.bin: $(BUILD_DIR)/bootloader.elf - @$(OBJCOPY) -O binary $< $@ - @chksum64 $@ > /dev/null - @$(PYTHON) tools/strip.py $@ - -$(BUILD_DIR)/bootloader.hex: $(BUILD_DIR)/bootloader.bin - @$(OBJCOPY) -I binary -O ihex $< $@ - -print_size: $(BUILD_DIR)/bootloader.elf - @echo 'Size of modules:' - @$(SIZE) -B -d -t --common $(OBJS) - @echo 'Size of bootloader:' - @$(SIZE) -B -d $< - -all: $(BUILD_DIR)/bootloader.hex print_size - -clean: - @rm -rf ./$(BUILD_DIR)/* - -$(BUILD_DIR)/version.c.o: .FORCE - -.FORCE: - -.PHONY: .FORCE all clean print_size - --include $(DEPS) +TOOLCHAIN = mips64-elf- +CC = $(TOOLCHAIN)gcc +CXX = $(TOOLCHAIN)g++ +OBJCOPY = $(TOOLCHAIN)objcopy +OBJDUMP = $(TOOLCHAIN)objdump +SIZE = $(TOOLCHAIN)size +PYTHON = python3 + +FLAGS = -march=vr4300 -mtune=vr4300 $(USER_FLAGS) +CFLAGS = -Os -Wall -ffunction-sections -fdata-sections -ffreestanding -MMD -MP +ASFLAGS = -Wa,-I$(N64_INST)/mips64-elf/lib +LDFLAGS = -lc -nostartfiles -Wl,--gc-sections + +SRC_DIR = src +ASSET_DIR = assets +BUILD_DIR = build + +SRC_FILES = \ + startup.S \ + boot.c \ + crc32.c \ + display.c \ + error.c \ + exception.c \ + exception.S \ + font.c \ + init.c \ + interrupt.c \ + io.c \ + ipl2.S \ + main.c \ + menu.c \ + sc64.c \ + syscalls.c \ + test.c \ + version.c \ + fatfs/diskio.c \ + fatfs/ff.c \ + fatfs/ffsystem.c \ + fatfs/ffunicode.c + +ASSET_FILES = \ + sc64_logo_640_240_dimmed.png + +SRCS = $(SRC_FILES) $(ASSET_FILES) +SRC_OBJS = $(patsubst %,%.o,$(SRC_FILES)) +ASSET_OBJS = $(patsubst %,%.asset.o,$(basename $(ASSET_FILES))) +OBJS = $(addprefix $(BUILD_DIR)/,$(notdir $(SRC_OBJS) $(ASSET_OBJS))) +DEPS = $(OBJS:.o=.d) +VPATH = $(SRC_DIR) $(SRC_DIR)/fatfs $(ASSET_DIR) + +$(@info $(shell mkdir -p ./$(BUILD_DIR) &> /dev/null)) + +$(BUILD_DIR)/%.S.o: %.S + $(CC) -x assembler-with-cpp $(FLAGS) $(ASFLAGS) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/%.c.o: %.c + $(CC) $(FLAGS) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/%.asset: $(ASSET_DIR)/%.png tools/asset_converter.py + $(PYTHON) tools/asset_converter.py $< $@ --compress + +$(BUILD_DIR)/%.asset.o: $(BUILD_DIR)/%.asset $(ASSET_DIR)/assets.S + @sed -e "s,@sym@,$*,g" -e "s,@file@,$<," < $(ASSET_DIR)/assets.S | $(CC) -x assembler-with-cpp $(FLAGS) $(ASFLAGS) $(CFLAGS) -c - -o $@ + +$(BUILD_DIR)/bootloader.elf: $(OBJS) N64.ld + $(CXX) $(FLAGS) $(LDFLAGS) -TN64.ld $(OBJS) -o $@ + @$(OBJDUMP) -S $@ > $(BUILD_DIR)/bootloader.lst + +$(BUILD_DIR)/bootloader.bin: $(BUILD_DIR)/bootloader.elf tools/strip.py + @$(OBJCOPY) -O binary $< $@ + @chksum64 $@ > /dev/null + @$(PYTHON) tools/strip.py $@ + +$(BUILD_DIR)/bootloader.hex: $(BUILD_DIR)/bootloader.bin + @$(OBJCOPY) -I binary -O ihex $< $@ + +print_size: $(BUILD_DIR)/bootloader.elf + @echo 'Size of modules:' + @$(SIZE) -B -d -t --common $(OBJS) + @echo 'Size of bootloader:' + @$(SIZE) -B -d $< + +all: $(BUILD_DIR)/bootloader.hex print_size + +clean: + @rm -rf ./$(BUILD_DIR)/* + +$(BUILD_DIR)/version.c.o: .FORCE + +.FORCE: + +.PHONY: .FORCE all clean print_size + +-include $(DEPS) diff --git a/sw/bootloader/src/display.c b/sw/bootloader/src/display.c index 2288d58..4023f60 100644 --- a/sw/bootloader/src/display.c +++ b/sw/bootloader/src/display.c @@ -60,6 +60,34 @@ static const vi_regs_t vi_config[] = {{ }}; +static void display_decompress_background (uint32_t *background) { + uint32_t pixel_count = ((*background++) / sizeof(uint32_t)); + uint32_t pixels_painted = 0; + uint8_t *background_data = (uint8_t *) (background); + uint32_t *framebuffer = (uint32_t *) (display_framebuffer); + + while (pixels_painted < pixel_count) { + int pixel_repeat = ((background_data[0]) + 1); + uint32_t pixel_value = ( + ((background_data[1]) << 24) | + ((background_data[2]) << 16) | + ((background_data[3]) << 8) | + (background_data[4]) + ); + for (int i = 0; i < pixel_repeat; i++) { + io_write(framebuffer++, pixel_value); + } + pixels_painted += pixel_repeat; + background_data += 5; + } +} + +static void display_clear_background (void) { + for (int i = 0; i < (SCREEN_WIDTH * SCREEN_HEIGHT); i++) { + io_write(&display_framebuffer[i], BACKGROUND_COLOR); + } +} + static void display_draw_character (char c) { if (c == '\n') { char_x = BORDER_WIDTH; @@ -106,14 +134,10 @@ void display_init (uint32_t *background) { char_x = BORDER_WIDTH; char_y = BORDER_HEIGHT; - if (background == NULL) { - for (int i = 0; i < (SCREEN_WIDTH * SCREEN_HEIGHT); i++) { - io_write(&display_framebuffer[i], BACKGROUND_COLOR); - } + if (background != NULL) { + display_decompress_background(background); } else { - for (int i = 0; i < (SCREEN_WIDTH * SCREEN_HEIGHT); i++) { - io_write(&display_framebuffer[i], *background++); - } + display_clear_background(); } io_write(&VI->MADDR, (uint32_t) (display_framebuffer)); diff --git a/sw/bootloader/tools/asset_converter.py b/sw/bootloader/tools/asset_converter.py index 7341eb2..b0f7da3 100644 --- a/sw/bootloader/tools/asset_converter.py +++ b/sw/bootloader/tools/asset_converter.py @@ -5,22 +5,53 @@ from PIL import Image +def compress(data: bytes, step_size: int=4) -> bytes: + uncompressed_length = len(data) + + if ((uncompressed_length % step_size) != 0): + raise ValueError(f'Data length not aligned to {step_size}') + + compressed_data = bytes() + compressed_data += uncompressed_length.to_bytes(4, byteorder='big') + + count = 0 + last_value = b'' + + for offset in range(0, uncompressed_length + step_size, step_size): + next_value = data[offset:(offset + step_size)] + + if (offset != 0): + if ((next_value == last_value) and (count < 255)): + count += 1 + else: + compressed_data += count.to_bytes(1, byteorder='big') + compressed_data += last_value + count = 0 + + last_value = next_value + + return compressed_data + + if __name__ == "__main__": - if (len(sys.argv) != 3): - print(f"Usage: python {sys.argv[0]} input_path output_path") + if (len(sys.argv) < 3): + print(f"Usage: python {sys.argv[0]} input_path output_path [--compress]") sys.exit(1) asset_input = sys.argv[1] asset_output = sys.argv[2] + asset_compress = len(sys.argv) > 3 and (sys.argv[3] == '--compress') source_asset = None final_asset = None try: source_asset = Image.open(asset_input) - converted_asset = source_asset.convert("RGBA") + converted_asset = source_asset.convert("RGBA").tobytes() + if (asset_compress): + converted_asset = compress(converted_asset) final_asset = open(asset_output, "wb") - final_asset.write(converted_asset.tobytes()) + final_asset.write(converted_asset) except FileNotFoundError: print(f"Couldn't open file \"{asset_input}\"") sys.exit(2)