git.y1.nz

gbdk-2020

GameBoy Development Kit
download: https://git.y1.nz/archives/gbdk.tar.gz
README | Files | Log | Refs | LICENSE

commit 4feddab2322451ac5f5623ee5516593c51679e1d
parent 9715970cc68e14a292af738ad595416b494258ef
Author: Toxa <56631470+untoxa@users.noreply.github.com>
Date:   Mon, 14 Mar 2022 17:58:49 +0300

Merge pull request #332 from gbdk-2020/MSX

MSXDOS support
Diffstat:
MMakefile13++++++++++++-
Mdocs/pages/06b_supported_consoles.md14+++++++++++++-
Mgbdk-lib/Makefile.common2+-
Mgbdk-lib/examples/cross-platform/banks_autobank/Makefile13+++++++++----
Mgbdk-lib/examples/cross-platform/banks_autobank/Makefile.targets7++++++-
Mgbdk-lib/examples/cross-platform/rle_map/src/main.c3---
Agbdk-lib/examples/msxdos/Makefile18++++++++++++++++++
Agbdk-lib/examples/msxdos/hello/Makefile30++++++++++++++++++++++++++++++
Agbdk-lib/examples/msxdos/hello/test.c12++++++++++++
Agbdk-lib/examples/msxdos/smoke/Makefile30++++++++++++++++++++++++++++++
Agbdk-lib/examples/msxdos/smoke/assets.c46++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-lib/examples/msxdos/smoke/banked.c8++++++++
Agbdk-lib/examples/msxdos/smoke/ram_bank0.s7+++++++
Agbdk-lib/examples/msxdos/smoke/ram_bank1.s7+++++++
Agbdk-lib/examples/msxdos/smoke/smoketest.c90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-lib/examples/msxdos/test/Makefile30++++++++++++++++++++++++++++++
Agbdk-lib/examples/msxdos/test/test.c9+++++++++
Mgbdk-lib/include/gbdk/bcd.h2+-
Mgbdk-lib/include/gbdk/metasprites.h2++
Mgbdk-lib/include/gbdk/platform.h2++
Agbdk-lib/include/msx/hardware.h201+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-lib/include/msx/metasprites.h118+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-lib/include/msx/msx.h751+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mgbdk-lib/include/stdlib.h4++--
Mgbdk-lib/libc/Makefile2+-
Mgbdk-lib/libc/_divslong.c14++++----------
Mgbdk-lib/libc/_modslong.c14++++----------
Dgbdk-lib/libc/asm/z80/heap.s44--------------------------------------------
Mgbdk-lib/libc/atoi.c36+++++++++++++++++++++---------------
Mgbdk-lib/libc/atol.c42+++++++++++++++++++++++-------------------
Mgbdk-lib/libc/isalpha.c9+++------
Mgbdk-lib/libc/isdigit.c9+++------
Mgbdk-lib/libc/islower.c9+++------
Mgbdk-lib/libc/isspace.c9+++------
Mgbdk-lib/libc/isupper.c9+++------
Mgbdk-lib/libc/puts.c8++++++--
Mgbdk-lib/libc/sprintf.c28++++++++++++++--------------
Mgbdk-lib/libc/strcat.c17++++++-----------
Mgbdk-lib/libc/string.c23+++++++----------------
Mgbdk-lib/libc/strncat.c26+++++++++++---------------
Mgbdk-lib/libc/strncmp.c14++++++--------
Mgbdk-lib/libc/strncpy.c21+++++++++------------
Mgbdk-lib/libc/targets/gbz80/ap/Makefile2+-
Mgbdk-lib/libc/targets/gbz80/ap/crt0.s1+
Mgbdk-lib/libc/targets/gbz80/ap/global.s27+++++++++++++++++++++++++++
Agbdk-lib/libc/targets/gbz80/crlf.c2++
Mgbdk-lib/libc/targets/gbz80/duck/Makefile2+-
Mgbdk-lib/libc/targets/gbz80/duck/crt0.s1+
Mgbdk-lib/libc/targets/gbz80/duck/global.s27+++++++++++++++++++++++++++
Mgbdk-lib/libc/targets/gbz80/gb/Makefile2+-
Mgbdk-lib/libc/targets/gbz80/gb/crt0.s1+
Mgbdk-lib/libc/targets/gbz80/gb/global.s27+++++++++++++++++++++++++++
Mgbdk-lib/libc/targets/gbz80/sgb.s236+++++++++++++++++++++++++++++++++++--------------------------------------------
Dgbdk-lib/libc/targets/z80/fill_rect_compat.s30------------------------------
Dgbdk-lib/libc/targets/z80/fill_rect_xy.s99-------------------------------------------------------------------------------
Dgbdk-lib/libc/targets/z80/fill_rect_xy_compat.s102-------------------------------------------------------------------------------
Mgbdk-lib/libc/targets/z80/gg/Makefile12++++++------
Rgbdk-lib/libc/targets/z80/__sdcc_bcall.s -> gbdk-lib/libc/targets/z80/gg/__sdcc_bcall.s0
Agbdk-lib/libc/targets/z80/gg/crlf.c2++
Mgbdk-lib/libc/targets/z80/gg/crt0.s9+++++----
Mgbdk-lib/libc/targets/z80/gg/global.s4++--
Dgbdk-lib/libc/targets/z80/int.s197-------------------------------------------------------------------------------
Dgbdk-lib/libc/targets/z80/metasprites_hide.s37-------------------------------------
Agbdk-lib/libc/targets/z80/msxdos/Makefile36++++++++++++++++++++++++++++++++++++
Agbdk-lib/libc/targets/z80/msxdos/__sdcc_bcall.s41+++++++++++++++++++++++++++++++++++++++++
Agbdk-lib/libc/targets/z80/msxdos/__sdcc_bcall_abc.s59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-lib/libc/targets/z80/msxdos/bdos_getchar.s11+++++++++++
Agbdk-lib/libc/targets/z80/msxdos/bdos_gets.s35+++++++++++++++++++++++++++++++++++
Agbdk-lib/libc/targets/z80/msxdos/bdos_putchar.s16++++++++++++++++
Agbdk-lib/libc/targets/z80/msxdos/crlf.c2++
Agbdk-lib/libc/targets/z80/msxdos/crt0.s522+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-lib/libc/targets/z80/msxdos/global.s483+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-lib/libc/targets/z80/msxdos/msx_fill_rect.s25+++++++++++++++++++++++++
Agbdk-lib/libc/targets/z80/msxdos/msx_fill_rect_xy.s94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-lib/libc/targets/z80/msxdos/msx_int.s185+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-lib/libc/targets/z80/msxdos/msx_metasprites.s77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-lib/libc/targets/z80/msxdos/msx_metasprites_hide.s37+++++++++++++++++++++++++++++++++++++
Agbdk-lib/libc/targets/z80/msxdos/msx_metasprites_hide_spr.s39+++++++++++++++++++++++++++++++++++++++
Agbdk-lib/libc/targets/z80/msxdos/msx_refresh_oam.s28++++++++++++++++++++++++++++
Agbdk-lib/libc/targets/z80/msxdos/msx_set_1bpp_data.s77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-lib/libc/targets/z80/msxdos/msx_set_1bpp_sprite_data.s52++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-lib/libc/targets/z80/msxdos/msx_set_native_data.s73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-lib/libc/targets/z80/msxdos/msx_set_tile_map.s29+++++++++++++++++++++++++++++
Agbdk-lib/libc/targets/z80/msxdos/msx_set_tile_map_xy.s100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-lib/libc/targets/z80/msxdos/msx_write_vdp.s15+++++++++++++++
Agbdk-lib/libc/targets/z80/msxdos/pad.s66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mgbdk-lib/libc/targets/z80/putchar.s1+
Dgbdk-lib/libc/targets/z80/refresh_oam.s35-----------------------------------
Dgbdk-lib/libc/targets/z80/set_1bpp_data.s108-------------------------------------------------------------------------------
Dgbdk-lib/libc/targets/z80/set_2bpp_data.s144-------------------------------------------------------------------------------
Dgbdk-lib/libc/targets/z80/set_native_data.s51---------------------------------------------------
Mgbdk-lib/libc/targets/z80/set_tile_map_xy.s4++--
Mgbdk-lib/libc/targets/z80/set_tile_map_xy_compat.s4++--
Mgbdk-lib/libc/targets/z80/set_tile_submap.s4++--
Mgbdk-lib/libc/targets/z80/set_tile_submap_compat.s4++--
Mgbdk-lib/libc/targets/z80/sms/Makefile12++++++------
Rgbdk-lib/libc/targets/z80/__sdcc_bcall.s -> gbdk-lib/libc/targets/z80/sms/__sdcc_bcall.s0
Agbdk-lib/libc/targets/z80/sms/crlf.c2++
Mgbdk-lib/libc/targets/z80/sms/crt0.s9+++++----
Mgbdk-lib/libc/targets/z80/sms/global.s4++--
Rgbdk-lib/libc/targets/z80/fill_rect.s -> gbdk-lib/libc/targets/z80/sms_fill_rect.s0
Agbdk-lib/libc/targets/z80/sms_fill_rect_compat.s30++++++++++++++++++++++++++++++
Agbdk-lib/libc/targets/z80/sms_fill_rect_xy.s99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-lib/libc/targets/z80/sms_fill_rect_xy_compat.s102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-lib/libc/targets/z80/sms_int.s198+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rgbdk-lib/libc/targets/z80/metasprites.s -> gbdk-lib/libc/targets/z80/sms_metasprites.s0
Agbdk-lib/libc/targets/z80/sms_metasprites_hide.s37+++++++++++++++++++++++++++++++++++++
Rgbdk-lib/libc/targets/z80/metasprites_hide_spr.s -> gbdk-lib/libc/targets/z80/sms_metasprites_hide_spr.s0
Agbdk-lib/libc/targets/z80/sms_refresh_oam.s35+++++++++++++++++++++++++++++++++++
Agbdk-lib/libc/targets/z80/sms_set_1bpp_data.s108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-lib/libc/targets/z80/sms_set_2bpp_data.s144+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-lib/libc/targets/z80/sms_set_native_data.s51+++++++++++++++++++++++++++++++++++++++++++++++++++
Mgbdk-lib/libc/targets/z80/vmemcpy.s2+-
Mgbdk-lib/libc/time.c9++-------
Mgbdk-lib/libc/tolower.c7+++----
Mgbdk-lib/libc/toupper.c7+++----
Mgbdk-support/bankpack/obj_data.c2++
Mgbdk-support/bankpack/obj_data.h7++++---
Mgbdk-support/lcc/gb.c5++++-
Mgbdk-support/lcc/gb.h12+++++++-----
Mgbdk-support/lcc/lcc.c33+++++++++++++++++++++++++++------
Mgbdk-support/lcc/targets.c28++++++++++++++++++++++++++++
Mgbdk-support/lcc/targets.h1+
Agbdk-support/makebin/Makefile30++++++++++++++++++++++++++++++
Agbdk-support/makebin/makebin.c1022+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-support/makecom/Makefile32++++++++++++++++++++++++++++++++
Agbdk-support/makecom/bin_to_com.c164+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-support/makecom/bin_to_com.h32++++++++++++++++++++++++++++++++
Agbdk-support/makecom/common.h26++++++++++++++++++++++++++
Agbdk-support/makecom/files.c62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-support/makecom/files.h7+++++++
Agbdk-support/makecom/list.c70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-support/makecom/list.h21+++++++++++++++++++++
Agbdk-support/makecom/makecom.c147+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-support/makecom/noi_file.c164+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-support/makecom/noi_file.h41+++++++++++++++++++++++++++++++++++++++++
Agbdk-support/makecom/pascal/Makefile31+++++++++++++++++++++++++++++++
Agbdk-support/makecom/pascal/makecom.dpr185+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-support/makecom/path_ops.c145+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-support/makecom/path_ops.h16++++++++++++++++
140 files changed, 6948 insertions(+), 1221 deletions(-)

diff --git a/Makefile b/Makefile @@ -11,7 +11,7 @@ PKG = gbdk VER = 4.0.6 PORTS=gbz80 z80 -PLATFORMS=gb ap duck gg sms +PLATFORMS=gb ap duck gg sms msxdos # Prefix to add to the standard tools. Usefull for a standard gcc # cross-compile. @@ -145,6 +145,8 @@ endif @$(MAKE) -C $(GBDKSUPPORTDIR)/png2asset TOOLSPREFIX=$(TOOLSPREFIX) @echo Building gbcompress @$(MAKE) -C $(GBDKSUPPORTDIR)/gbcompress TOOLSPREFIX=$(TOOLSPREFIX) TARGETDIR=$(TARGETDIR)/ --no-print-directory + @echo Building makecom + @$(MAKE) -C $(GBDKSUPPORTDIR)/makecom TOOLSPREFIX=$(TOOLSPREFIX) TARGETDIR=$(TARGETDIR)/ --no-print-directory @echo gbdk-support-install: gbdk-support-build $(BUILDDIR)/bin @@ -167,6 +169,9 @@ gbdk-support-install: gbdk-support-build $(BUILDDIR)/bin @echo Installing gbcompress @cp $(GBDKSUPPORTDIR)/gbcompress/gbcompress$(EXEEXTENSION) $(BUILDDIR)/bin/gbcompress$(EXEEXTENSION) @$(TARGETSTRIP) $(BUILDDIR)/bin/gbcompress$(EXEEXTENSION) + @echo Installing makecom + @cp $(GBDKSUPPORTDIR)/makecom/makecom$(EXEEXTENSION) $(BUILDDIR)/bin/makecom$(EXEEXTENSION) + @$(TARGETSTRIP) $(BUILDDIR)/bin/makecom$(EXEEXTENSION) @echo gbdk-support-clean: @@ -391,6 +396,12 @@ ifneq (,$(wildcard $(BUILDDIR)/bin/)) echo \`\`\` >> $(TOOLCHAIN_DOCS_FILE); $(BUILDDIR)/bin/makebin -h >> $(TOOLCHAIN_DOCS_FILE) 2>&1 echo \`\`\` >> $(TOOLCHAIN_DOCS_FILE); +# makecom + echo \@anchor makecom-settings >> $(TOOLCHAIN_DOCS_FILE); + echo \# makecom settings >> $(TOOLCHAIN_DOCS_FILE); + echo \`\`\` >> $(TOOLCHAIN_DOCS_FILE); + $(BUILDDIR)/bin/makecom -h >> $(TOOLCHAIN_DOCS_FILE) 2>&1 + echo \`\`\` >> $(TOOLCHAIN_DOCS_FILE); # gbcompress echo \@anchor gbcompress-settings >> $(TOOLCHAIN_DOCS_FILE); echo \# gbcompress settings >> $(TOOLCHAIN_DOCS_FILE); diff --git a/docs/pages/06b_supported_consoles.md b/docs/pages/06b_supported_consoles.md @@ -13,6 +13,8 @@ As of version `4.0.5` GBDK includes support for other consoles in addition to th - Sega Master System (SMS) - Sega Game Gear (GG) + - MSX DOS (MSXDOS) + While the GBDK API has many convenience functions that work the same or similar across different consoles, it's important to keep their different capabilities in mind when writing code intended to run on more than one. Some (but not all) of the differences are screen sizes, color capabilities, memory layouts, processor type (z80 vs gbz80/sm83) and speed. @@ -33,11 +35,14 @@ When compiling with @ref sdcc-settings "sdcc": When assembling with @ref sdasgb-settings "sdasgb" (for GB/AP) and @ref sdasz80-settings "sdasz80" (for SMS/GG): - Select the appropriate include path: `-I<gbdk-path>lib/small/asxxxx/<plat>` -When linking with @ref sdldgb-settings "sdldgb" (for GB/AP) and @ref sdldz80-settings "sdldz80" (for SMS/GG): +When linking with @ref sdldgb-settings "sdldgb" (for GB/AP) and @ref sdldz80-settings "sdldz80" (for SMS/GG or MSXDOS): - Select the appropriate include paths: `-k <gbdk-path>lib/small/asxxxx/<port>`, `-k <gbdk-path>lib/small/asxxxx/<plat>` - Include the appropriate library files `-l <port>.lib`, `-l <plat>.lib` - The crt will be under ` <gbdk-path>lib/small/asxxxx/<plat>/crt0.o` +MSXDOS requires an additional build step after makebin to create the final binary: + - `makecom <image.bin> [<image.noi>] <output.com>` + ## Console Port and Platform Settings - Nintendo Game Boy / Game Boy Color @@ -57,6 +62,10 @@ When linking with @ref sdldgb-settings "sdldgb" (for GB/AP) and @ref sdldz80-set - @ref lcc : `-mz80:gg` - port:`z80`, plat:`gg` + - MSX DOS + - @ref lcc : `-mz80:msxdos` + - port:`z80`, plat:`msxdos` + # Cross-Platform Constants There are several constant \#defines that can be used to help select console specific code during compile time (with `#ifdef`, `#ifndef`) . @@ -82,6 +91,9 @@ There are several constant \#defines that can be used to help select console spe - `SEGA` will be \#defined - `GAMEGEAR` will be \#defined + - When `<msx/msx.h>` is included (either directly or through `<gbdk/platform.h>`) + - `MSXDOS` will be \#defined + ## Console Hardware Properties Constants that describe properties of the console hardware are listed below. Their values will change to reflect the current console target that is being built. diff --git a/gbdk-lib/Makefile.common b/gbdk-lib/Makefile.common @@ -11,7 +11,7 @@ ifndef PORTS PORTS = gbz80 z80 endif ifndef PLATFORMS -PLATFORMS = gb ap duck gg sms +PLATFORMS = gb ap duck gg sms msxdos endif ifndef SDCCLIB diff --git a/gbdk-lib/examples/cross-platform/banks_autobank/Makefile b/gbdk-lib/examples/cross-platform/banks_autobank/Makefile @@ -1,12 +1,13 @@ # If you move this project you can change the directory # to match your GBDK root directory (ex: GBDK_HOME = "C:/GBDK/" -GBDK_HOME = ../../../ +#GBDK_HOME = ../../../ +GBDK_HOME = c:/gb/gbdk-2020/build/gbdk/ LCC = $(GBDK_HOME)bin/lcc # Set platforms to build here, spaced separated. (These are in the separate Makefile.targets) # They can also be built/cleaned individually: "make gg" and "make gg-clean" # Possible are: gb gbc pocket megaduck sms gg -TARGETS=gb pocket megaduck sms gg +TARGETS = gb pocket megaduck sms gg msxdos # Configure platform specific LCC flags here: LCCFLAGS_gb = -Wl-yt0x1B # Set an MBC for banking (1B-ROM+MBC5+RAM+BATT) @@ -15,6 +16,7 @@ LCCFLAGS_duck = -Wl-yt0x1B # Usually the same as required for .gb LCCFLAGS_gbc = -Wl-yt0x1B -Wm-yc # Same as .gb with: -Wm-yc (gb & gbc) or Wm-yC (gbc exclusive) LCCFLAGS_sms = LCCFLAGS_gg = +LCCFLAGS_msxdos = LCCFLAGS += $(LCCFLAGS_$(EXT)) # This adds the current platform specific LCC Flags @@ -22,8 +24,10 @@ LCCFLAGS += -Wl-j -Wm-yoA -Wm-ya4 -autobank -Wb-ext=.rel -Wb-v # MBC + Autobanki # LCCFLAGS += -debug # Uncomment to enable debug output # LCCFLAGS += -v # Uncomment for lcc verbose output +CFLAGS = -Iinclude + # You can set the name of the ROM file here -PROJECTNAME = banks_autobank +PROJECTNAME = test # EXT?=gb # Only sets extension to default (game boy .gb) if not populated SRCDIR = src @@ -35,7 +39,7 @@ MKDIRS = $(OBJDIR) $(BINDIR) # See bottom of Makefile for directory auto-cr BINS = $(OBJDIR)/$(PROJECTNAME).$(EXT) CSOURCES = $(foreach dir,$(SRCDIR),$(notdir $(wildcard $(dir)/*.c))) $(foreach dir,$(RESDIR),$(notdir $(wildcard $(dir)/*.c))) ASMSOURCES = $(foreach dir,$(SRCDIR),$(notdir $(wildcard $(dir)/*.s))) -OBJS = $(CSOURCES:%.c=$(OBJDIR)/%.o) $(ASMSOURCES:%.s=$(OBJDIR)/%.o) +OBJS = $(CSOURCES:%.c=$(OBJDIR)/%.o) $(ASMSOURCES:%.s=$(OBJDIR)/%.o) # Builds all targets sequentially all: $(TARGETS) @@ -60,6 +64,7 @@ $(OBJDIR)/%.s: $(SRCDIR)/%.c # Link the compiled object files into a .gb ROM file $(BINS): $(OBJS) $(LCC) $(LCCFLAGS) $(CFLAGS) -o $(BINDIR)/$(PROJECTNAME).$(EXT) $(OBJS) + rm -f $(BINDIR)/*.map $(BINDIR)/*.ihx $(BINDIR)/*.bin clean: @echo Cleaning diff --git a/gbdk-lib/examples/cross-platform/banks_autobank/Makefile.targets b/gbdk-lib/examples/cross-platform/banks_autobank/Makefile.targets @@ -11,7 +11,12 @@ build-target: $(BINS) clean-target: rm -rf $(OBJDIR) - rm -rf $(BINDIR) + rm -f $(BINDIR)/*.com $(BINDIR)/*.bin $(BINDIR)/*.noi $(BINDIR)/*.sym $(BINDIR)/*.ihx $(BINDIR)/*.map $(BINDIR)/*.0?? + +msxdos-clean: + ${MAKE} clean-target EXT=com +msxdos: + ${MAKE} build-target PORT=z80 PLAT=msxdos EXT=com gb-clean: ${MAKE} clean-target EXT=gb diff --git a/gbdk-lib/examples/cross-platform/rle_map/src/main.c b/gbdk-lib/examples/cross-platform/rle_map/src/main.c @@ -15,9 +15,6 @@ INCBIN_EXTERN(map_tiles) INCBIN(map_compressed, "res/map.bin.rle") INCBIN_EXTERN(map_compressed) -extern uint8_t sample_rle_data[]; - - uint8_t data[32][MAP_DATA_HEIGHT]; // Collision map buffer uint8_t scrollpos = 0; // Scroll position in pixels uint8_t datapos = 0; // x position in tiles inside the collision map diff --git a/gbdk-lib/examples/msxdos/Makefile b/gbdk-lib/examples/msxdos/Makefile @@ -0,0 +1,17 @@ +# Register all subdirectories in the project's root directory. +SUBDIRS := $(wildcard */.) + +# Top-level phony targets. +all clean compile.bat: $(SUBDIRS) FORCE +# Similar to: +# .PHONY: all clean +# all clean: $(SUBDIRS) +# GNU's .PHONY target is more efficient in that it explicitly declares non-files. + +# Recurse `make` into each subdirectory +# Pass along targets specified at command-line (if any). +$(SUBDIRS): FORCE + $(MAKE) -C $@ $(MAKECMDGOALS) + +# Force targets. +FORCE: + diff --git a/gbdk-lib/examples/msxdos/hello/Makefile b/gbdk-lib/examples/msxdos/hello/Makefile @@ -0,0 +1,30 @@ +CC = ../../../bin/lcc -v -mz80:msxdos -Wm-yoA -Wl-j + +BINS = test.com + +all: $(BINS) + +ASRC = $(wildcard *.s) +CSRC = $(wildcard *.c) + +OBJS = $(CSRC:%.c=%.o) $(ASRC:%.s=%.o) + +compile.bat: Makefile + @echo "REM Automatically generated from Makefile" > compile.bat + @make -sn | sed y/\\//\\\\/ | grep -v make >> compile.bat + +# Compile and link single file in one pass + +%.o: %.c + $(CC) -c -o $@ $< + +%.o: %.s + $(CC) -c -o $@ $< + +$(BINS): $(OBJS) + $(CC) -Wm-yS -o $@ $^ + rm -f *.map *.noi *.ihx *.lst + +clean: + rm -f *.o *.lst *.map *.com *~ *.rel *.cdb *.ihx *.lnk *.sym *.asm *.noi + diff --git a/gbdk-lib/examples/msxdos/hello/test.c b/gbdk-lib/examples/msxdos/hello/test.c @@ -0,0 +1,12 @@ +#include <stdio.h> +#include <stdint.h> + +int a = 100, b = 200; +void main(int argc, char * argv[]) { + printf("Hello, world!\r\nargc: %d\r\n", argc); + + for (int i = 0; i != argc; i++) + printf("argv%d = '%s'\r\n", i, argv[i]); + + printf("a = %d b = %d\r\n&a == 0x%x", a, b, &a); +} diff --git a/gbdk-lib/examples/msxdos/smoke/Makefile b/gbdk-lib/examples/msxdos/smoke/Makefile @@ -0,0 +1,30 @@ +CC = ../../../bin/lcc -mz80:msxdos -Wm-yoA -Wl-j + +BINS = smoketest.com + +all: $(BINS) + +ASRC = $(wildcard *.s) +CSRC = $(wildcard *.c) + +OBJS = $(CSRC:%.c=%.o) $(ASRC:%.s=%.o) + +compile.bat: Makefile + @echo "REM Automatically generated from Makefile" > compile.bat + @make -sn | sed y/\\//\\\\/ | grep -v make >> compile.bat + +# Compile and link single file in one pass + +%.o: %.c + $(CC) -c -o $@ $< + +%.o: %.s + $(CC) -c -o $@ $< + +$(BINS): $(OBJS) + $(CC) -Wm-yS -o $@ $^ + rm -f *.map *.noi *.ihx *.lst + +clean: + rm -f *.o *.lst *.map *.com *~ *.rel *.cdb *.ihx *.lnk *.sym *.asm *.noi + diff --git a/gbdk-lib/examples/msxdos/smoke/assets.c b/gbdk-lib/examples/msxdos/smoke/assets.c @@ -0,0 +1,46 @@ +#pragma bank 2 + +#include <sms/sms.h> + +BANKREF(earth_data) +const unsigned char earth_data[] = { + + /* Tile 0x00 */ + 0x07,0x07,0x18,0x1F,0x32,0x2D,0x71,0x4E,0x70,0x4F,0xF8,0x87,0xF8,0x87,0xF8,0x87, + 0xFC,0x83,0xFE,0x81,0x7F,0x40,0x7F,0x40,0x3F,0x20,0x1F,0x18,0x07,0x07,0x00,0x00, + 0xC0,0xC0,0xF0,0x30,0x78,0x88,0x3C,0xC4,0x5C,0xA4,0x9E,0x62,0x3E,0xC2,0x3E,0xC2, + 0x5E,0xA2,0x7E,0x82,0x0C,0xF4,0x0C,0xF4,0x98,0x68,0xB0,0x70,0xC0,0xC0,0x00,0x00, + 0x07,0x07,0x1F,0x18,0x2F,0x30,0x4F,0x70,0x6F,0x50,0x9F,0xE0,0x9F,0xE0,0xBF,0xC0, + 0xFF,0x80,0xB7,0xC8,0x63,0x5C,0x43,0x7C,0x3F,0x20,0x1F,0x18,0x07,0x07,0x00,0x00, + 0xC0,0xC0,0xB0,0x70,0x18,0xE8,0x0C,0xF4,0x0C,0xF4,0x82,0x7E,0x82,0x7E,0x86,0x7A, + 0xC6,0x3A,0xE6,0x1A,0xF4,0x0C,0xFC,0x04,0xF8,0x08,0xF0,0x30,0xC0,0xC0,0x00,0x00, + + /* Tile 0x08 */ + 0x07,0x07,0x1E,0x19,0x20,0x3F,0x40,0x7F,0x42,0x7D,0x81,0xFE,0x81,0xFE,0x83,0xFC, + 0xD7,0xA8,0xBB,0xC4,0x6E,0x51,0x7C,0x43,0x3F,0x20,0x1F,0x18,0x07,0x07,0x00,0x00, + 0xC0,0xC0,0x70,0xB0,0xE8,0x18,0xF4,0x0C,0xF4,0x0C,0xFE,0x02,0xFE,0x02,0xFE,0x02, + 0xFE,0x02,0x7E,0x82,0x3C,0xC4,0x3C,0xC4,0xF8,0x08,0xF0,0x30,0xC0,0xC0,0x00,0x00, + 0x07,0x07,0x1B,0x1C,0x20,0x3F,0x40,0x7F,0x40,0x7F,0xE0,0x9F,0x90,0xEF,0x89,0xF6, + 0x8D,0xF2,0x9F,0xE0,0x5E,0x61,0x6F,0x50,0x3F,0x20,0x1F,0x18,0x07,0x07,0x00,0x00, + 0xC0,0xC0,0xB0,0x70,0x28,0xD8,0x04,0xFC,0x2C,0xD4,0x1E,0xE2,0x1E,0xE2,0x3E,0xC2, + 0x7E,0x82,0xB6,0x4A,0xE4,0x1C,0xC4,0x3C,0xF8,0x08,0xF0,0x30,0xC0,0xC0,0x00,0x00, + + /* Tile 0x10 */ + 0x07,0x07,0x18,0x1F,0x20,0x3F,0x40,0x7F,0x40,0x7F,0xEE,0x91,0xF1,0x8E,0xE0,0x9F, + 0xE0,0x9F,0xF1,0x8E,0x71,0x4E,0x72,0x4D,0x3F,0x20,0x1F,0x18,0x07,0x07,0x00,0x00, + 0xC0,0xC0,0xF0,0x30,0x08,0xF8,0x04,0xFC,0x04,0xFC,0x02,0xFE,0x02,0xFE,0x92,0x6E, + 0xD6,0x2A,0xFE,0x02,0xEC,0x14,0xFC,0x04,0xF8,0x08,0xF0,0x30,0xC0,0xC0,0x00,0x00, + 0x07,0x07,0x1D,0x1A,0x36,0x29,0x5C,0x63,0x6C,0x53,0xCE,0xB1,0x9F,0xE0,0x9E,0xE1, + 0xAE,0xD1,0xBF,0xC0,0x47,0x78,0x47,0x78,0x2F,0x30,0x1F,0x18,0x07,0x07,0x00,0x00, + 0xC0,0xC0,0x70,0xB0,0x08,0xF8,0x04,0xFC,0x04,0xFC,0xE2,0x1E,0x32,0xCE,0x0E,0xF2, + 0x0E,0xF2,0x1E,0xE2,0x1C,0xE4,0x2C,0xD4,0xF8,0x08,0xF0,0x30,0xC0,0xC0,0x00,0x00, + + /* Tile 0x18 */ + 0x07,0x07,0x1E,0x19,0x33,0x2C,0x49,0x76,0x42,0x7D,0xC4,0xBB,0xC1,0xBE,0xC1,0xBE, + 0xE2,0x9D,0xF3,0x8C,0x78,0x47,0x78,0x47,0x3C,0x23,0x1C,0x1B,0x07,0x07,0x00,0x00, + 0xC0,0xC0,0x70,0xB0,0x68,0x98,0xC4,0x3C,0xC4,0x3C,0xEE,0x12,0xF2,0x0E,0xE2,0x1E, + 0xE2,0x1E,0xF2,0x0E,0x7C,0x84,0x7C,0x84,0xF8,0x08,0xF0,0x30,0xC0,0xC0,0x00,0x00 +}; + +BANKREF(earth_data_size) +const unsigned int earth_data_size = sizeof(earth_data); diff --git a/gbdk-lib/examples/msxdos/smoke/banked.c b/gbdk-lib/examples/msxdos/smoke/banked.c @@ -0,0 +1,7 @@ +#pragma bank 3 + +#include <stdint.h> + +uint16_t banked_func(uint8_t be, uint8_t ef) __banked { + return ((be << 8) | ef); +} + diff --git a/gbdk-lib/examples/msxdos/smoke/ram_bank0.s b/gbdk-lib/examples/msxdos/smoke/ram_bank0.s @@ -0,0 +1,6 @@ + .area _DATA_0 + +_array0:: + .ds 0x10 +_array1:: + .ds 0x10 + diff --git a/gbdk-lib/examples/msxdos/smoke/ram_bank1.s b/gbdk-lib/examples/msxdos/smoke/ram_bank1.s @@ -0,0 +1,6 @@ + .area _DATA_1 + +_array2:: + .ds 0x04 +_array3:: + .ds 0x06 + diff --git a/gbdk-lib/examples/msxdos/smoke/smoketest.c b/gbdk-lib/examples/msxdos/smoke/smoketest.c @@ -0,0 +1,90 @@ +#include <sms/sms.h> + +BANKREF_EXTERN(earth_data) +extern const unsigned char earth_data[]; + +BANKREF_EXTERN(earth_data_size) +extern const unsigned int earth_data_size; + +const uint8_t tilemap[] = {2, 4, 6, 8, 3, 5, 7, 9}; +const uint16_t tilemapw[] = {2, 4, 6, 8, 3, 5, 7, 9}; + +uint16_t banked_func(uint8_t be, uint8_t ef) __banked; + +int16_t x = 8 << 4, y = 0; +int8_t xspd = 0, yspd = 0; + +uint8_t anim = 0, tick = 0; + +joypads_t joy; +void main() { + HIDE_LEFT_COLUMN; + SPRITES_8x16; + DISPLAY_ON; + +// vmemcpy(0x4000, earth_data, sizeof(earth_data)); + + SWITCH_ROM(BANK(earth_data)); + if (banked_func(0xBE, 0xEF) == 0xBEEF) { + set_bkg_data(2, earth_data_size >> 4, earth_data); + set_sprite_data(0, earth_data_size >> 4, earth_data); + set_sprite_tile(0, 0); + set_sprite_tile(1, 2); + } + + set_bkg_tiles(4, 10, 4, 2, tilemap); + + set_tile_map(4, 16, 4, 2, tilemapw); + + joypad_init(2, &joy); + + while(TRUE) { + joypad_ex(&joy); + + if (joy.joy0 & J_LEFT) { + if (xspd > -32) xspd -= 2; + } else if (joy.joy0 & J_RIGHT) { + if (xspd < 32) xspd += 2; + } else { + if (xspd < 0) xspd++; else if (xspd > 0) xspd--; + } + + if (joy.joy0 & J_UP) { + if (yspd > -32) yspd -= 2; + } else if (joy.joy0 & J_DOWN) { + if (yspd < 32) yspd += 2; + } else { + if (yspd < 0) yspd++; else if (yspd > 0) yspd--; + } + + x += xspd; + if (x < (8 << 4)) x = 8 << 4; else if (x > (30 * 8) << 4) x = (30 * 8) << 4; + + + y += yspd; + if (y < 0) y = 0; else if (y > (22 * 8) << 4) y = (22 * 8) << 4; + + tick++; tick &= 7; + if (!tick) { + anim++; if (anim == 7) anim = 0; + set_sprite_tile(0, anim << 2); + set_sprite_tile(1, (anim << 2) + 2); + } + + move_sprite(0, x >> 4, y >> 4); + move_sprite(1, (x >> 4) + 8, y >> 4); + + if (joy.joy1 & J_LEFT) { + scroll_bkg(-1, 0); + } else if (joy.joy1 & J_RIGHT) { + scroll_bkg(1, 0); + } + if (joy.joy1 & J_UP) { + scroll_bkg(0, -1); + } else if (joy.joy1 & J_DOWN) { + scroll_bkg(0, 1); + } + + wait_vbl_done(); + } +} diff --git a/gbdk-lib/examples/msxdos/test/Makefile b/gbdk-lib/examples/msxdos/test/Makefile @@ -0,0 +1,30 @@ +CC = ../../../bin/lcc -mz80:msxdos -Wm-yoA -Wl-j + +BINS = test.com + +all: $(BINS) + +ASRC = $(wildcard *.s) +CSRC = $(wildcard *.c) + +OBJS = $(CSRC:%.c=%.o) $(ASRC:%.s=%.o) + +compile.bat: Makefile + @echo "REM Automatically generated from Makefile" > compile.bat + @make -sn | sed y/\\//\\\\/ | grep -v make >> compile.bat + +# Compile and link single file in one pass + +%.o: %.c + $(CC) -c -o $@ $< + +%.o: %.s + $(CC) -c -o $@ $< + +$(BINS): $(OBJS) + $(CC) -Wm-yS -o $@ $^ + rm -f *.map *.noi *.ihx *.lst + +clean: + rm -f *.o *.lst *.map *.com *~ *.rel *.cdb *.ihx *.lnk *.sym *.asm *.noi + diff --git a/gbdk-lib/examples/msxdos/test/test.c b/gbdk-lib/examples/msxdos/test/test.c @@ -0,0 +1,9 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +void main() { + uint8_t data[32]; + uint16_t score = 75; + printf("%d %s", strlen(uitoa(score, data, 10)), data); +} diff --git a/gbdk-lib/include/gbdk/bcd.h b/gbdk-lib/include/gbdk/bcd.h @@ -3,7 +3,7 @@ #if defined(__TARGET_gb) || defined(__TARGET_ap) || defined(__TARGET_duck) #include <gb/bcd.h> -#elif defined(__TARGET_sms) || defined(__TARGET_gg) +#elif defined(__TARGET_sms) || defined(__TARGET_gg) || defined(__TARGET_msxdos) #error Not implemented yet #else #error Unrecognized port diff --git a/gbdk-lib/include/gbdk/metasprites.h b/gbdk-lib/include/gbdk/metasprites.h @@ -5,6 +5,8 @@ #include <gb/metasprites.h> #elif defined(__TARGET_sms) || defined(__TARGET_gg) #include <sms/metasprites.h> +#elif defined(__TARGET_msxdos) + #include <msx/metasprites.h> #else #error Unrecognized port #endif diff --git a/gbdk-lib/include/gbdk/platform.h b/gbdk-lib/include/gbdk/platform.h @@ -7,6 +7,8 @@ #include <gb/sgb.h> #elif defined(__TARGET_sms) || defined(__TARGET_gg) #include <sms/sms.h> +#elif defined(__TARGET_msxdos) + #include <msx/msx.h> #else #error Unrecognized port #endif diff --git a/gbdk-lib/include/msx/hardware.h b/gbdk-lib/include/msx/hardware.h @@ -0,0 +1,201 @@ +/** @file msx/hardware.h + Defines that let the MSX hardware registers be accessed + from C. +*/ +#ifndef _HARDWARE_H +#define _HARDWARE_H + +#include <types.h> + +#define __BYTES extern UBYTE +#define __BYTE_REG extern volatile UBYTE + +static volatile SFR AT(0x3E) MEMORY_CTL; + +#define MEMCTL_JOYON 0b00000000 +#define MEMCTL_JOYOFF 0b00000100 +#define MEMCTL_BASEON 0b00000000 +#define MEMCTL_BASEOFF 0b00001000 +#define MEMCTL_RAMON 0b00000000 +#define MEMCTL_RAMOFF 0b00010000 +#define MEMCTL_CROMON 0b00000000 +#define MEMCTL_CROMOFF 0b00100000 +#define MEMCTL_ROMON 0b00000000 +#define MEMCTL_ROMOFF 0b01000000 +#define MEMCTL_EXTON 0b00000000 +#define MEMCTL_EXTOFF 0b10000000 + +static volatile SFR AT(0x3F) JOY_CTL; + +#define JOY_P1_LATCH 0b00000010 +#define JOY_P2_LATCH 0b00001000 + +static volatile SFR AT(0x7E) VCOUNTER; + +static volatile SFR AT(0x7F) PSG; + +#define PSG_LATCH 0x80 + +#define PSG_CH0 0b00000000 +#define PSG_CH1 0b00100000 +#define PSG_CH2 0b01000000 +#define PSG_CH3 0b01100000 + +#define PSG_VOLUME 0b00010000 + +static volatile SFR AT(0x7F) HCOUNTER; + +static volatile SFR AT(0x98) VDP_DATA; +static volatile SFR AT(0x99) VDP_CMD; +static volatile SFR AT(0x99) VDP_STATUS; + +#define STATF_INT_VBL 0b10000000 +#define STATF_9_SPR 0b01000000 +#define STATF_SPR_COLL 0b00100000 + +#define VDP_REG_MASK 0b10000000 +#define VDP_R0 0b10000000 +extern UBYTE shadow_VDP_R0; + +#define R0_DEFAULT 0b00000000 +#define R0_CB_OUTPUT 0b00000000 +#define R0_CB_INPUT 0b01000000 +#define R0_IE2_OFF 0b00000000 +#define R0_IE2 0b00100000 +#define R0_IE1_OFF 0b00000000 +#define R0_IE1 0b00010000 +#define R0_SCR_MODE1 0b00000000 +#define R0_SCR_MODE2 0b00000010 +#define R0_SCR_MODE3 0b00000100 +#define R0_ES_OFF 0b00000000 +#define R0_ES 0b00000001 + +#define VDP_R1 0b10000001 +extern UBYTE shadow_VDP_R1; + +#define R1_DEFAULT 0b10000000 +#define R1_DISP_OFF 0b00000000 +#define R1_DISP_ON 0b01000000 +#define R1_IE_OFF 0b00000000 +#define R1_IE 0b00100000 +#define R1_SCR_MODE1 0b00010000 +#define R1_SCR_MODE2 0b00000000 +#define R1_SCR_MODE3 0b00000000 +#define R1_SPR_8X8 0b00000000 +#define R1_SPR_16X16 0b00000010 +#define R1_SPR_MAG 0b00000001 +#define R1_SPR_MAG_OFF 0b00000000 + +#define VDP_R2 0b10000010 +extern UBYTE shadow_VDP_R2; + +#define R2_MAP_0x3800 0xFF +#define R2_MAP_0x3000 0xFD +#define R2_MAP_0x2800 0xFB +#define R2_MAP_0x2000 0xF9 +#define R2_MAP_0x1800 0xF7 +#define R2_MAP_0x1000 0xF5 +#define R2_MAP_0x0800 0xF3 +#define R2_MAP_0x0000 0xF1 + +#define VDP_R3 0b10000011 +extern UBYTE shadow_VDP_R3; +#define VDP_R4 0b10000100 +extern UBYTE shadow_VDP_R4; +#define VDP_R5 0b10000101 +extern UBYTE shadow_VDP_R5; + +#define R5_SAT_0x3F00 0xFF +#define R5_SAT_MASK 0b10000001 + +#define VDP_R6 0b10000110 +extern UBYTE shadow_VDP_R6; + +#define R6_BANK0 0xFB +#define R6_DATA_0x0000 0xFB +#define R6_BANK1 0xFF +#define R6_DATA_0x2000 0xFF + +#define VDP_R7 0b10000111 +extern UBYTE shadow_VDP_R7; +#define VDP_RBORDER 0b10000111 +extern UBYTE shadow_VDP_RBORDER; + +#define R7_COLOR_MASK 0b11110000 + +#define VDP_R8 0b10001000 +extern UBYTE shadow_VDP_R8; +#define VDP_RSCX 0b10001000 +extern UBYTE shadow_VDP_RSCX; + +#define VDP_R9 0b10001001 +extern UBYTE shadow_VDP_R9; +#define VDP_RSCY 0b10001001 +extern UBYTE shadow_VDP_RSCY; + +#define VDP_R10 0b10001010 +extern UBYTE shadow_VDP_R10; + +#define R10_INT_OFF 0xFF +#define R10_INT_EVERY 0x00 + +static volatile SFR AT(0xDC) JOY_PORT1; + +#define JOY_P1_UP 0b00000001 +#define JOY_P1_DOWN 0b00000010 +#define JOY_P1_LEFT 0b00000100 +#define JOY_P1_RIGHT 0b00001000 +#define JOY_P1_SW1 0b00010000 +#define JOY_P1_TRIGGER 0b00010000 +#define JOY_P1_SW2 0b00100000 +#define JOY_P2_UP 0b01000000 +#define JOY_P2_DOWN 0b10000000 + +static volatile SFR AT(0xDD) JOY_PORT2; + +#define JOY_P2_LEFT 0b00000001 +#define JOY_P2_RIGHT 0b00000010 +#define JOY_P2_SW1 0b00000100 +#define JOY_P2_TRIGGER 0b00000100 +#define JOY_P2_SW2 0b00001000 +#define JOY_RESET 0b00010000 +#define JOY_P1_LIGHT 0b01000000 +#define JOY_P2_LIGHT 0b10000000 + +static volatile SFR AT(0xF0) FMADDRESS; +static volatile SFR AT(0xF1) FMDATA; +static volatile SFR AT(0xF2) AUDIOCTRL; + +static volatile SFR AT(0xfc) MAP_FRAME0; +static volatile SFR AT(0xfd) MAP_FRAME1; +static volatile SFR AT(0xfe) MAP_FRAME2; +static volatile SFR AT(0xff) MAP_FRAME3; + +extern const UBYTE _BIOS; + +extern const UBYTE _SYSTEM; + +#define SYSTEM_PAL 0x00 +#define SYSTEM_NTSC 0x01 + +extern volatile UBYTE VDP_ATTR_SHIFT; + +#define VDP_SAT_TERM 0xD0 + +#if defined(__TARGET_msxdos) +#define DEVICE_SCREEN_X_OFFSET 0 +#define DEVICE_SCREEN_Y_OFFSET 0 +#define DEVICE_SCREEN_WIDTH 32 +#define DEVICE_SCREEN_HEIGHT 24 +#define DEVICE_SCREEN_BUFFER_WIDTH 32 +#define DEVICE_SCREEN_BUFFER_HEIGHT 28 +#define DEVICE_SCREEN_MAP_ENTRY_SIZE 2 +#define DEVICE_SPRITE_PX_OFFSET_X 0 +#define DEVICE_SPRITE_PX_OFFSET_Y -1 +#else +#error Unrecognized port +#endif +#define DEVICE_SCREEN_PX_WIDTH (DEVICE_SCREEN_WIDTH * 8) +#define DEVICE_SCREEN_PX_HEIGHT (DEVICE_SCREEN_HEIGHT * 8) + +#endif diff --git a/gbdk-lib/include/msx/metasprites.h b/gbdk-lib/include/msx/metasprites.h @@ -0,0 +1,118 @@ +/** @file sms/metasprites.h + + # Metasprite support + + A metasprite is a larger sprite made up from a + collection of smaller individual hardware sprites. + Different frames of the same metasprites can share + tile data. + + The api supports metasprites in both + @ref SPRITES_8x8 and @ref SPRITES_8x16 mode. If + 8x16 mode is used then the height of the metasprite + must be a multiple of 16. + + The origin (pivot) for the metasprite is not required + to be in the upper left-hand corner as with regular + hardware sprites. + + Use the @ref utility_png2asset tool to convert single + or multiple frames of graphics into metasprite + structured data for use with the ...metasprite...() + functions. + + # Metasprites composed of variable numbers of sprites + + When using png2asset, it's common for the output of + different frames to be composed of different numbers + of hardware sprites (since it's trying to create each + frame as efficiently as possible). Due to that, it's + good practice to clear out (hide) unused sprites in the + shadow_OAM that have been set by previous frames. +*/ + +#ifndef _METASPRITES_H_INCLUDE +#define _METASPRITES_H_INCLUDE + +#include <msx/hardware.h> +#include <types.h> +#include <stdint.h> + +/** Metasprite sub-item structure + @param dy (int8_t) Y coordinate of the sprite relative to the metasprite origin (pivot) + @param dx (int8_t) X coordinate of the sprite relative to the metasprite origin (pivot) + @param dtile (uint8_t) Start tile relative to the metasprites own set of tiles + @param props (uint8_t) Property Flags + + Metasprites are built from multiple metasprite_t items (one for each sub-sprite) + and a pool of tiles they reference. If a metasprite has multiple frames then each + frame will be built from some number of metasprite_t items (which may vary based + on how many sprites are required for that particular frame). + + A metasprite frame is terminated with a {metasprite_end} entry. +*/ +typedef struct metasprite_t { + int8_t dy, dx; + uint8_t dtile; + uint8_t props; +} metasprite_t; + +#define metasprite_end -128 +#define METASPR_ITEM(dy,dx,dt,a) {(dy),(dx),(dt),(a)} +#define METASPR_TERM {metasprite_end} + +extern const void * __current_metasprite; +extern uint8_t __current_base_tile; +extern uint8_t __render_shadow_OAM; + + +static uint8_t __move_metasprite(uint8_t id, uint8_t x, uint8_t y) Z88DK_CALLEE PRESERVES_REGS(iyh, iyl); +static void __hide_metasprite(uint8_t id) Z88DK_FASTCALL PRESERVES_REGS(iyh, iyl); + +/** + * Hides all hardware sprites in range from <= X < to + * @param from start OAM index + * @param to finish OAM index + */ +void hide_sprites_range(UINT8 from, UINT8 to) Z88DK_CALLEE PRESERVES_REGS(iyh, iyl); + +/** Moves metasprite to the absolute position x and y + + @param metasprite Pointer to the first struct of the metasprite (for the desired frame) + @param base_tile Number of the first tile where the metasprite's tiles start + @param base_sprite Number of the first hardware sprite to be used by the metasprite + @param x Absolute x coordinate of the sprite + @param y Absolute y coordinate of the sprite + + Moves __metasprite__ to the absolute position __x__ and __y__ + (with __no flip__ on the X or Y axis). Hardware sprites are + allocated starting from __base_sprite__, using tiles + starting from __base_tile__. + + Sets: + \li __current_metasprite = metasprite; + \li __current_base_tile = base_tile; + + @return Number of hardware sprites used to draw this metasprite + */ +inline uint8_t move_metasprite(const metasprite_t * metasprite, uint8_t base_tile, uint8_t base_sprite, uint8_t x, uint8_t y) { + __current_metasprite = metasprite; + __current_base_tile = base_tile; + return __move_metasprite(base_sprite, x, y); +} + +/** Hides a metasprite from the screen + + @param metasprite Pointer to first struct of the desired metasprite frame + @param base_sprite Number of hardware sprite to start with + + Sets: + \li __current_metasprite = metasprite; + + **/ +inline void hide_metasprite(const metasprite_t * metasprite, uint8_t base_sprite) { + __current_metasprite = metasprite; + __hide_metasprite(base_sprite); +} + +#endif diff --git a/gbdk-lib/include/msx/msx.h b/gbdk-lib/include/msx/msx.h @@ -0,0 +1,751 @@ +/** @file msx/msx.h + MSX specific functions. +*/ +#ifndef _MSX_H +#define _MSX_H + +#include <types.h> +#include <stdint.h> +#include <gbdk/version.h> +#include <msx/hardware.h> + +#define MSX +#ifdef NINTENDO +#undef NINTENDO +#endif +#ifdef SEGA +#undef SEGA +#endif +#if defined(__TARGET_msxdos) +#define MSXDOS +#endif + + +#define VBK_REG VDP_ATTR_SHIFT + +/** Joypad bits. + A logical OR of these is used in the wait_pad and joypad + functions. For example, to see if the B button is pressed + try + + uint8_t keys; + keys = joypad(); + if (keys & J_B) { + ... + } + + @see joypad + */ +#define J_UP 0b00100000 +#define J_DOWN 0b01000000 +#define J_LEFT 0b00010000 +#define J_RIGHT 0b10000000 +#define J_A 0b00000001 +#define J_B 0b00000100 +#define J_SELECT 0b00001000 +#define J_START 0b00000010 + +/** Screen modes. + Normally used by internal functions only. + @see mode() + */ +#define M_TEXT_OUT 0x02U +#define M_TEXT_INOUT 0x03U +/** Set this in addition to the others to disable scrolling + + If scrolling is disabled, the cursor returns to (0,0) + @see mode() +*/ +#define M_NO_SCROLL 0x04U +/** Set this to disable interpretation + @see mode() +*/ +#define M_NO_INTERP 0x08U + +/** If set the background tile will be flipped horizontally. + */ +#define S_FLIPX 0x02U +/** If set the background tile will be flipped vertically. + */ +#define S_FLIPY 0x04U +/** If set the background tile palette. + */ +#define S_PALETTE 0x08U +/** If set the background tile priority. + */ +#define S_PRIORITY 0x10U + +// VDP helper macros +#define __WRITE_VDP_REG(REG, v) shadow_##REG=(v);__critical{VDP_CMD=(shadow_##REG),VDP_CMD=REG;} +#define __READ_VDP_REG(REG) shadow_##REG + +void WRITE_VDP_CMD(uint16_t cmd) Z88DK_FASTCALL PRESERVES_REGS(b, c, d, e, iyh, iyl); +void WRITE_VDP_DATA(uint16_t data) Z88DK_FASTCALL PRESERVES_REGS(b, c, d, e, iyh, iyl); + +/** Set the current screen mode - one of M_* modes + + Normally used by internal functions only. + + @see M_TEXT_OUT, M_TEXT_INOUT, M_NO_SCROLL, M_NO_INTERP +*/ +void mode(uint8_t m) OLDCALL; + +/** Returns the current mode + + @see M_TEXT_OUT, M_TEXT_INOUT, M_NO_SCROLL, M_NO_INTERP +*/ +uint8_t get_mode() OLDCALL; + +/* Interrupt flags */ +/** Disable calling of interrupt service routines + */ +#define EMPTY_IFLAG 0x00U +/** VBlank Interrupt occurs at the start of the vertical blank. + + During this period the video ram may be freely accessed. + @see set_interrupts(), @see add_VBL + */ +#define VBL_IFLAG 0x01U +/** LCD Interrupt when triggered by the STAT register. + @see set_interrupts(), @see add_LCD +*/ +#define LCD_IFLAG 0x02U +/** Does nothing on MSX + */ +#define TIM_IFLAG 0x04U +/** Does nothing on MSX + */ +#define SIO_IFLAG 0x08U +/** Does nothing on MSX + */ +#define JOY_IFLAG 0x10U + +void set_interrupts(uint8_t flags) Z88DK_FASTCALL; + +/* Limits */ +/** Width of the visible screen in pixels. + */ +#define SCREENWIDTH DEVICE_SCREEN_PX_WIDTH +/** Height of the visible screen in pixels. + */ +#define SCREENHEIGHT DEVICE_SCREEN_PX_HEIGHT +/** The Minimum X position of the Window Layer (Left edge of screen) @see move_win() + */ +#define MINWNDPOSX 0x00U +/** The Minimum Y position of the Window Layer (Top edge of screen) @see move_win() + */ +#define MINWNDPOSY 0x00U +/** The Maximum X position of the Window Layer (Right edge of screen) @see move_win() + */ +#define MAXWNDPOSX 0x00U +/** The Maximum Y position of the Window Layer (Bottom edge of screen) @see move_win() + */ +#define MAXWNDPOSY 0x00U + + +/** Interrupt handlers + */ +typedef void (*int_handler)(void) NONBANKED; + +/** Removes the VBL interrupt handler. + @see add_VBL() +*/ +void remove_VBL(int_handler h) Z88DK_FASTCALL PRESERVES_REGS(iyh, iyl); + +/** Removes the LCD interrupt handler. + @see add_LCD(), remove_VBL() +*/ +void remove_LCD(int_handler h) Z88DK_FASTCALL PRESERVES_REGS(b, c, iyh, iyl); + +void remove_TIM(int_handler h) Z88DK_FASTCALL; +void remove_SIO(int_handler h) Z88DK_FASTCALL; +void remove_JOY(int_handler h) Z88DK_FASTCALL; + +/** Adds a V-blank interrupt handler. +*/ +void add_VBL(int_handler h) Z88DK_FASTCALL PRESERVES_REGS(d, e, iyh, iyl); + +/** Adds a LCD interrupt handler. +*/ +void add_LCD(int_handler h) Z88DK_FASTCALL PRESERVES_REGS(b, c, iyh, iyl); + +/** Does nothing on MSX + */ +void add_TIM(int_handler h) Z88DK_FASTCALL; + +/** Does nothing on MSX + */ +void add_SIO(int_handler h) Z88DK_FASTCALL; + +/** Does nothing on MSX + */ +void add_JOY(int_handler h) Z88DK_FASTCALL; + +/** Cancel pending interrupts + */ +inline uint8_t cancel_pending_interrupts() { + return VDP_STATUS; +} + +inline void move_bkg(uint8_t x, uint8_t y) { + __WRITE_VDP_REG(VDP_RSCX, -x); + __WRITE_VDP_REG(VDP_RSCY, y); +} + +inline void scroll_bkg(int8_t x, int8_t y) { + __WRITE_VDP_REG(VDP_RSCX, __READ_VDP_REG(VDP_RSCX) - x); + int16_t tmp = __READ_VDP_REG(VDP_RSCY) + y; + __WRITE_VDP_REG(VDP_RSCY, (tmp < 0) ? 224 + tmp : tmp % 224u); +} + +/** HALTs the CPU and waits for the vertical blank interrupt (VBL) to finish. + + This is often used in main loops to idle the CPU at low power + until it's time to start the next frame. It's also useful for + syncing animation with the screen re-draw. + + Warning: If the VBL interrupt is disabled, this function will + never return. If the screen is off this function returns + immediately. +*/ +void wait_vbl_done() PRESERVES_REGS(b, c, d, e, h, l, iyh, iyl); + +/** Turns the display off. + + @see DISPLAY_ON +*/ +inline void display_off() { + __WRITE_VDP_REG(VDP_R1, __READ_VDP_REG(VDP_R1) &= (~R1_DISP_ON)); +} + +/** Turns the display back on. + @see display_off, DISPLAY_OFF +*/ +#define DISPLAY_ON \ + __WRITE_VDP_REG(VDP_R1, __READ_VDP_REG(VDP_R1) |= R1_DISP_ON) + +/** Turns the display off immediately. + @see display_off, DISPLAY_ON +*/ +#define DISPLAY_OFF \ + display_off(); + +/** Copies data from shadow OAM to OAM + */ +void refresh_OAM(); + +/** Blanks leftmost column, so it is not garbaged when you use horizontal scroll + @see SHOW_LEFT_COLUMN +*/ +#define HIDE_LEFT_COLUMN \ + __WRITE_VDP_REG(VDP_R0, __READ_VDP_REG(VDP_R0) |= R0_LCB) + +/** Shows leftmost column + @see HIDE_LEFT_COLUMN +*/ +#define SHOW_LEFT_COLUMN \ + __WRITE_VDP_REG(VDP_R0, __READ_VDP_REG(VDP_R0) &= (~R0_LCB)) + +/** Turns on the background layer. + Not yet implemented +*/ +#define SHOW_BKG + +/** Turns off the background layer. + Not yet implemented +*/ +#define HIDE_BKG + +/** Turns on the window layer + Not yet implemented +*/ +#define SHOW_WIN + +/** Turns off the window layer. + Not yet implemented +*/ +#define HIDE_WIN + +/** Turns on the sprites layer. + Not yet implemented +*/ +#define SHOW_SPRITES + +/** Turns off the sprites layer. + Not yet implemented +*/ +#define HIDE_SPRITES + +/** Sets sprite size to 8x16 pixels, two tiles one above the other. +*/ +#define SPRITES_8x16 \ + __WRITE_VDP_REG(VDP_R1, __READ_VDP_REG(VDP_R1) |= R1_SPR_8X16) + +/** Sets sprite size to 8x8 pixels, one tile. +*/ +#define SPRITES_8x8 \ + __WRITE_VDP_REG(VDP_R1, __READ_VDP_REG(VDP_R1) &= (~R1_SPR_8X16)) + +/** Macro returns TRUE if device supports color + * (it always does on MSX) + */ +#define DEVICE_SUPPORTS_COLOR (TRUE) + +/** Global Time Counter in VBL periods (60Hz) + + Increments once per Frame + + Will wrap around every ~18 minutes (unsigned 16 bits = 65535 / 60 / 60 = 18.2) +*/ +extern volatile uint16_t sys_time; + +/** Tracks current active ROM bank in frame 1 +*/ +extern volatile uint8_t _current_bank; +#define CURRENT_BANK _current_bank + +/** Obtains the __bank number__ of VARNAME + + @param VARNAME Name of the variable which has a __bank_VARNAME companion symbol which is adjusted by bankpack + + Use this to obtain the bank number from a bank reference + created with @ref BANKREF(). + + @see BANKREF_EXTERN(), BANKREF() +*/ +#ifndef BANK +#define BANK(VARNAME) ( (uint8_t) & __bank_ ## VARNAME ) +#endif + +/** Creates a reference for retrieving the bank number of a variable or function + + @param VARNAME Variable name to use, which may be an existing identifier + + @see BANK() for obtaining the bank number of the included data. + + More than one `BANKREF()` may be created per file, but each call should + always use a unique VARNAME. + + Use @ref BANKREF_EXTERN() within another source file + to make the variable and it's data accesible there. +*/ +#define BANKREF(VARNAME) void __func_ ## VARNAME() __banked __naked { \ +__asm \ + .local b___func_ ## VARNAME \ + ___bank_ ## VARNAME = b___func_ ## VARNAME \ + .globl ___bank_ ## VARNAME \ +__endasm; \ +} + +/** Creates extern references for accessing a BANKREF() generated variable. + + @param VARNAME Name of the variable used with @ref BANKREF() + + This makes a @ref BANKREF() reference in another source + file accessible in the current file for use with @ref BANK(). + + @see BANKREF(), BANK() +*/ +#define BANKREF_EXTERN(VARNAME) extern const void __bank_ ## VARNAME; + + +/** Makes switch the active ROM bank in frame 1 + @param b ROM bank to switch to +*/ + +void SWITCH_ROM(uint8_t bank) Z88DK_FASTCALL PRESERVES_REGS(b, c, d, e, iyh, iyl); +#define SWITCH_ROM1 SWITCH_ROM + +/** Makes switch the active ROM bank in frame 2 + @param b ROM bank to switch to +*/ + +#define SWITCH_ROM2(b) MAP_FRAME2=(b) + +/** Switches RAM bank + @param b SRAM bank to switch to +*/ + +#define SWITCH_RAM(b) RAM_CONTROL=((b)&1)?RAM_CONTROL|RAMCTL_BANK:RAM_CONTROL&(~RAMCTL_BANK) + +/** Enables RAM +*/ + +#define ENABLE_RAM RAM_CONTROL|=RAMCTL_RAM + +/** Disables RAM +*/ + +#define DISABLE_RAM RAM_CONTROL&=(~RAMCTL_RAM) + + +/** Delays the given number of milliseconds. + Uses no timers or interrupts, and can be called with + interrupts disabled + */ +void delay(uint16_t d) Z88DK_FASTCALL; + + +/** Reads and returns the current state of the joypad. +*/ +uint8_t joypad() OLDCALL PRESERVES_REGS(b, c, d, e, h, iyh, iyl); + +/** Waits until at least one of the buttons given in mask are pressed. +*/ +uint8_t waitpad(uint8_t mask) Z88DK_FASTCALL PRESERVES_REGS(b, c, d, e, iyh, iyl); + +/** Waits for the directional pad and all buttons to be released. + + Note: Checks in a loop that doesn't HALT at all, so the CPU + will be maxed out until this call returns. +*/ +void waitpadup() PRESERVES_REGS(b, c, d, e, iyh, iyl); + +/** Multiplayer joypad structure. + + Must be initialized with @ref joypad_init() first then it + may be used to poll all avaliable joypads with @ref joypad_ex() +*/ +typedef struct { + uint8_t npads; + union { + struct { + uint8_t joy0, joy1, joy2, joy3; + }; + uint8_t joypads[4]; + }; +} joypads_t; + +/** Initializes joypads_t structure for polling multiple joypads + @param npads number of joypads requested (1, 2 or 4) + @param joypads pointer to joypads_t structure to be initialized + + Only required for @ref joypad_ex, not required for calls to regular @ref joypad() + @returns number of joypads avaliable + @see joypad_ex(), joypads_t +*/ +uint8_t joypad_init(uint8_t npads, joypads_t * joypads) Z88DK_CALLEE; + +/** Polls all avaliable joypads + @param joypads pointer to joypads_t structure to be filled with joypad statuses, + must be previously initialized with joypad_init() + + @see joypad_init(), joypads_t +*/ +void joypad_ex(joypads_t * joypads) Z88DK_FASTCALL PRESERVES_REGS(iyh, iyl); + + +#if defined(__TARGET_msxdos) + +#define RGB(r,g,b) ((r) | ((g) << 2) | ((b) << 4)) +#define RGB8(r,g,b) (((r) >> 6) | (((g) >> 6) << 2) | (((b) >> 6) << 4)) +#define RGBHTML(RGB24bit) (((RGB24bit) >> 22) | ((((RGB24bit) & 0xFFFF) >> 14) << 2) | ((((RGB24bit) & 0xFF) >> 6) << 4)) + +/** Common colors based on the EGA default palette. + */ +#define RGB_RED RGB( 3, 0, 0) +#define RGB_DARKRED RGB( 2, 0, 0) +#define RGB_GREEN RGB( 0, 3, 0) +#define RGB_DARKGREEN RGB( 0, 2, 0) +#define RGB_BLUE RGB( 0, 0, 3) +#define RGB_DARKBLUE RGB( 0, 0, 2) +#define RGB_YELLOW RGB( 3, 3, 0) +#define RGB_DARKYELLOW RGB( 2, 2, 0) +#define RGB_CYAN RGB( 0, 3, 3) +#define RGB_AQUA RGB( 3, 1, 2) +#define RGB_PINK RGB( 3, 0, 3) +#define RGB_PURPLE RGB( 2, 0, 2) +#define RGB_BLACK RGB( 0, 0, 0) +#define RGB_DARKGRAY RGB( 1, 1, 1) +#define RGB_LIGHTGRAY RGB( 2, 2, 2) +#define RGB_WHITE RGB( 3, 3, 3) + +typedef uint8_t palette_color_t; + +#else +#error Unrecognized port +#endif + +void set_default_palette(); +inline void cpu_fast() {} + +void set_palette_entry(uint8_t palette, uint8_t entry, uint16_t rgb_data) Z88DK_CALLEE PRESERVES_REGS(iyh, iyl); +#define set_bkg_palette_entry set_palette_entry +#define set_sprite_palette_entry(palette,entry,rgb_data) set_palette_entry(1,entry,rgb_data) +void set_palette(uint8_t first_palette, uint8_t nb_palettes, palette_color_t *rgb_data) Z88DK_CALLEE; +#define set_bkg_palette set_palette +#define set_sprite_palette(first_palette,nb_palettes,rgb_data) set_palette(1,1,rgb_data) + +void set_native_tile_data(uint16_t start, uint16_t ntiles, const void *src) Z88DK_CALLEE; +inline void set_bkg_4bpp_data(uint16_t start, uint16_t ntiles, const void *src) { + set_native_tile_data(start, ntiles, src); +} +void set_sprite_1bpp_data(uint16_t start, uint16_t ntiles, const void *src) Z88DK_CALLEE; +inline void set_native_sprite_data(uint16_t start, uint16_t ntiles, const void *src) { + set_sprite_1bpp_data(start, ntiles, src); +} + +#define COMPAT_PALETTE(C0,C1,C2,C3) (((uint16_t)(C3) << 12) | ((uint16_t)(C2) << 8) | ((uint16_t)(C1) << 4) | (uint16_t)(C0)) +extern uint16_t _current_2bpp_palette; +inline void set_2bpp_palette(uint16_t palette) { + _current_2bpp_palette = palette; +} +//void set_tile_2bpp_data(uint16_t start, uint16_t ntiles, const void *src, uint16_t palette) Z88DK_CALLEE PRESERVES_REGS(iyh, iyl); +inline void set_bkg_data(uint16_t start, uint16_t ntiles, const void *src) { + set_native_tile_data(start, ntiles, src); +} +inline void set_sprite_data(uint16_t start, uint16_t ntiles, const void *src) { + set_sprite_1bpp_data(start, ntiles, src); +} +//inline void set_bkg_2bpp_data(uint16_t start, uint16_t ntiles, const void *src) { +// set_tile_2bpp_data(start, ntiles, src, _current_2bpp_palette); +//} +//inline void set_sprite_2bpp_data(uint16_t start, uint16_t ntiles, const void *src) { +// set_tile_2bpp_data((uint8_t)(start) + 0x100u, ntiles, src, _current_2bpp_palette); +//} + +extern uint16_t _current_1bpp_colors; +inline void set_1bpp_colors(uint8_t fgcolor, uint8_t bgcolor) { + _current_1bpp_colors = ((uint16_t)bgcolor << 8) | fgcolor; +} +void set_tile_1bpp_data(uint16_t start, uint16_t ntiles, const void *src, uint16_t colors) Z88DK_CALLEE PRESERVES_REGS(iyh, iyl); +inline void set_bkg_1bpp_data(uint16_t start, uint16_t ntiles, const void *src) { + set_tile_1bpp_data(start, ntiles, src, _current_1bpp_colors); +} + + +/** Copies arbitrary data to an address in VRAM + + @param dst destination VRAM Address + @param src Pointer to source buffer + @param size Number of bytes to copy + + Copies __size__ bytes from a buffer at _src__ to VRAM starting at __dst__. +*/ +void set_data(uint16_t dst, const void *src, uint16_t size) Z88DK_CALLEE PRESERVES_REGS(iyh, iyl); +void vmemcpy(uint16_t dst, const void *src, uint16_t size) Z88DK_CALLEE PRESERVES_REGS(iyh, iyl); + +void set_tile_map(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint8_t *tiles) Z88DK_CALLEE PRESERVES_REGS(iyh, iyl); +#define set_bkg_tiles set_tile_map +#define set_win_tiles set_tile_map + +extern uint8_t _map_tile_offset; +inline void set_bkg_based_tiles(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint8_t *tiles, uint8_t base_tile) { + _map_tile_offset = base_tile; + set_tile_map(x, y, w, h, tiles); + _map_tile_offset = 0; +} +inline void set_win_based_tiles(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint8_t *tiles, uint8_t base_tile) { + _map_tile_offset = base_tile; + set_tile_map(x, y, w, h, tiles); + _map_tile_offset = 0; +} + +void set_tile_submap(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t map_w, const uint8_t *map) Z88DK_CALLEE PRESERVES_REGS(iyh, iyl); +void set_tile_submap_compat(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t map_w, const uint8_t *map) Z88DK_CALLEE PRESERVES_REGS(iyh, iyl); +inline void set_bkg_submap(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint8_t *map, uint8_t map_w) { + set_tile_submap_compat(x, y, w, h, map_w, map); +} +inline void set_win_submap(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint8_t *map, uint8_t map_w) { + set_tile_submap_compat(x, y, w, h, map_w, map); +} + +extern uint8_t _submap_tile_offset; +inline void set_bkg_based_submap(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint8_t *map, uint8_t map_w, uint8_t base_tile) { + _submap_tile_offset = base_tile; + set_tile_submap_compat(x, y, w, h, map_w, map); + _submap_tile_offset = 0; +} +inline void set_win_based_submap(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint8_t *map, uint8_t map_w, uint8_t base_tile) { + _submap_tile_offset = base_tile; + set_tile_submap_compat(x, y, w, h, map_w, map); + _submap_tile_offset = 0; +} + +void fill_rect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint16_t tile) Z88DK_CALLEE PRESERVES_REGS(iyh, iyl); +#define fill_bkg_rect fill_rect +#define fill_win_rect fill_rect + +/** Sprite Attributes structure + @param x X Coordinate of the sprite on screen + @param y Y Coordinate of the sprite on screen + @param tile Sprite tile number (see @ref set_sprite_tile) + @param prop OAM Property Flags (see @ref set_sprite_prop) +*/ +typedef struct OAM_item_t { + uint8_t y, x; //< X, Y Coordinates of the sprite on screen + uint8_t tile; //< Sprite tile number + uint8_t prop; //< OAM Property Flags +} OAM_item_t; + + +/** Shadow OAM array in WRAM, that is DMA-transferred into the real OAM each VBlank +*/ +extern volatile struct OAM_item_t shadow_OAM[]; + +/** MSB of shadow_OAM address is used by OAM copying routine +*/ +extern volatile uint8_t _shadow_OAM_base; + +/** Flag for disabling of OAM copying routine + + Values: + \li 1: OAM copy routine is disabled (non-isr VDP operation may be in progress) + \li 0: OAM copy routine is enabled + + This flag is modified by all MSX GBDK API calls that write to the VDP. + It is set to DISABLED when they start and ENABLED when they complete. + + @note It is recommended to avoid writing to the Video Display Processor + (VDP) during an interrupt service routine (ISR) since it can corrupt + the VDP pointer of an VDP operation already in progress. + + If it is necessary, this flag can be used during an ISR to determine + whether a VDP operation is already in progress. If the value is `1` + then avoid writing to the VDP (tiles, map, scrolling, colors, etc). + + \code{.c} + // at the beginning of and ISR that would write to the VDP + if (_shadow_OAM_OFF) return; + \endcode + + @see @ref docs_consoles_safe_display_controller_access +*/ +extern volatile uint8_t _shadow_OAM_OFF; + +/** Disable shadow OAM to VRAM copy on each VBlank +*/ +#define DISABLE_VBL_TRANSFER \ + _shadow_OAM_base = 0 + +/** Enable shadow OAM to VRAM copy on each VBlank +*/ +#define ENABLE_VBL_TRANSFER \ + _shadow_OAM_base = (uint8_t)((uint16_t)&shadow_OAM >> 8) + +/** Amount of hardware sprites in OAM +*/ +#define MAX_HARDWARE_SPRITES 32 + +/** Sets address of 256-byte aligned array of shadow OAM to be transferred on each VBlank +*/ +inline void SET_SHADOW_OAM_ADDRESS(void * address) { + _shadow_OAM_base = (uint8_t)((uint16_t)address >> 8); +} + +/** Sets sprite number __nb__in the OAM to display tile number __tile__. + + @param nb Sprite number, range 0 - 39 + @param tile Selects a tile (0 - 255) from memory at 8000h - 8FFFh + \n In CGB Mode this could be either in VRAM Bank + \n 0 or 1, depending on Bit 3 of the OAM Attribute Flag + \n (see @ref set_sprite_prop) + + In 8x16 mode: + \li The sprite will also display the next tile (__tile__ + 1) + directly below (y + 8) the first tile. + \li The lower bit of the tile number is ignored: + the upper 8x8 tile is (__tile__ & 0xFE), and + the lower 8x8 tile is (__tile__ | 0x01). + \li See: @ref SPRITES_8x16 +*/ +inline void set_sprite_tile(uint8_t nb, uint8_t tile) { + shadow_OAM[nb].tile=tile; +} + + +/** Returns the tile number of sprite number __nb__ in the OAM. + +@param nb Sprite number, range 0 - 39 + +@see set_sprite_tile for more details +*/ +inline uint8_t get_sprite_tile(uint8_t nb) { + return shadow_OAM[nb].tile; +} + +inline void set_sprite_prop(uint8_t nb, uint8_t prop) { + shadow_OAM[nb].prop = prop; +} + +inline uint8_t get_sprite_prop(uint8_t nb) { + return shadow_OAM[nb].prop; +} + +/** Moves sprite number __nb__ to the __x__, __y__ position on the screen. + + @param nb Sprite number, range 0 - 39 + @param x X Position. Specifies the sprites horizontal position on the screen (minus 8). + \n An offscreen value (X=0 or X>=168) hides the sprite, but the sprite + still affects the priority ordering - a better way to hide a sprite is to set + its Y-coordinate offscreen. + @param y Y Position. Specifies the sprites vertical position on the screen (minus 16). + \n An offscreen value (for example, Y=0 or Y>=160) hides the sprite. + + Moving the sprite to 0,0 (or similar off-screen location) will hide it. +*/ +inline void move_sprite(uint8_t nb, uint8_t x, uint8_t y) { + OAM_item_t * itm = &shadow_OAM[nb]; + itm->y=y, itm->x=x; +} + + +/** Moves sprite number __nb__ relative to its current position. + + @param nb Sprite number, range 0 - 39 + @param x Number of pixels to move the sprite on the __X axis__ + \n Range: -128 - 127 + @param y Number of pixels to move the sprite on the __Y axis__ + \n Range: -128 - 127 + + @see move_sprite for more details about the X and Y position + */ +inline void scroll_sprite(uint8_t nb, int8_t x, int8_t y) { + OAM_item_t * itm = &shadow_OAM[nb]; + itm->y+=y, itm->x+=x; +} + + +/** Hides sprite number __nb__ by moving it to zero position by Y. + + @param nb Sprite number, range 0 - 39 + */ +inline void hide_sprite(uint8_t nb) { + shadow_OAM[nb].y = 0xC0; +} + +/** + * Set byte in vram at given memory location + * + * @param addr address to write to + * @param v value + */ +void set_vram_byte(uint8_t * addr, uint8_t v) Z88DK_CALLEE PRESERVES_REGS(iyh, iyl); + +/** + * Set single tile t with attributes on background layer at x,y + * @param x X-coordinate + * @param y Y-coordinate + * @param t tile index + * @return returns the address of tile, so you may use faster set_vram_byte() later + */ +uint8_t * set_attributed_tile_xy(uint8_t x, uint8_t y, uint16_t t) Z88DK_CALLEE PRESERVES_REGS(iyh, iyl); + +/** + * Set single tile t on background layer at x,y + * @param x X-coordinate + * @param y Y-coordinate + * @param t tile index + * @return returns the address of tile, so you may use faster set_vram_byte() later + */ +uint8_t * set_tile_xy(uint8_t x, uint8_t y, uint8_t t) Z88DK_CALLEE PRESERVES_REGS(iyh, iyl); +#define set_bkg_tile_xy set_tile_xy +#define set_win_tile_xy set_tile_xy + +/** + * Get address of X,Y tile of background map + */ +uint8_t * get_bkg_xy_addr(uint8_t x, uint8_t y) Z88DK_CALLEE PRESERVES_REGS(iyh, iyl); +#define get_win_xy_addr get_bkg_xy_addr + +#endif /* _MSX_H */ diff --git a/gbdk-lib/include/stdlib.h b/gbdk-lib/include/stdlib.h @@ -14,12 +14,12 @@ returned to the parent. All open streams are flushed and closed. */ -void exit(int status); +void exit(int status) OLDCALL; #if 0 /** Compatibility function. Not implemented. */ -int getkey(void); +int getkey(void) OLDCALL; #endif /** Returns the absolute value of int __i__ diff --git a/gbdk-lib/libc/Makefile b/gbdk-lib/libc/Makefile @@ -7,7 +7,7 @@ ifeq ($(PORTS),) endif ifeq ($(PLATFORMS),) - PLATFORMS = gb ap duck gg sms + PLATFORMS = gb ap duck gg sms msxdos endif TOPDIR = .. diff --git a/gbdk-lib/libc/_divslong.c b/gbdk-lib/libc/_divslong.c @@ -28,14 +28,8 @@ unsigned long _divulong (unsigned long a, unsigned long b); -long _divslong (long a, long b) -{ - long r; - - r = _divulong((a < 0 ? -a : a), - (b < 0 ? -b : b)); - if ( (a < 0) ^ (b < 0)) - return -r; - else - return r; +long _divslong (long a, long b) { + long r; + r = _divulong((a < 0 ? -a : a), (b < 0 ? -b : b)); + return ((a < 0) ^ (b < 0)) ? -r : r; } diff --git a/gbdk-lib/libc/_modslong.c b/gbdk-lib/libc/_modslong.c @@ -28,14 +28,8 @@ unsigned long _modulong (unsigned long a, unsigned long b); -long _modslong (long a, long b) -{ - long r; - - r = _modulong((a < 0 ? -a : a), - (b < 0 ? -b : b)); - if ( (a < 0) ^ (b < 0)) - return -r; - else - return r; +long _modslong (long a, long b) { + long r; + r = _modulong((a < 0 ? -a : a), (b < 0 ? -b : b)); + return ( (a < 0) ^ (b < 0)) ? -r : r; } diff --git a/gbdk-lib/libc/asm/z80/heap.s b/gbdk-lib/libc/asm/z80/heap.s @@ -1,44 +0,0 @@ -;-------------------------------------------------------------------------- -; heap.s -; -; Copyright (C) 2001, Michael Hope -; -; This library is free software; you can redistribute it and/or modify it -; under the terms of the GNU General Public License as published by the -; Free Software Foundation; either version 2, or (at your option) any -; later version. -; -; This library is distributed in the hope that it will be useful, -; but WITHOUT ANY WARRANTY; without even the implied warranty of -; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -; GNU General Public License for more details. -; -; You should have received a copy of the GNU General Public License -; along with this library; see the file COPYING. If not, write to the -; Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, -; MA 02110-1301, USA. -; -; As a special exception, if you link this library with other files, -; some of which are compiled with SDCC, to produce an executable, -; this library does not by itself cause the resulting executable to -; be covered by the GNU General Public License. This exception does -; not however invalidate any other reasons why the executable file -; might be covered by the GNU General Public License. -;-------------------------------------------------------------------------- - -; Just stubs - not copyrightable - - ;; Stubs that hook the heap in - .globl ___sdcc_heap_init - - .area _GSINIT - call ___sdcc_heap_init - - .area _HEAP -___sdcc_heap:: - ;; For now just allocate 1k of heap. - .ds 1023 - - .area _HEAP_END -___sdcc_heap_end:: - .ds 1 diff --git a/gbdk-lib/libc/atoi.c b/gbdk-lib/libc/atoi.c @@ -3,23 +3,29 @@ #include <types.h> #include <ctype.h> +inline bool _isdigit(char c) { + return ((uint8_t)((uint8_t)c - '0') < 10u) ? true : false; +} + int atoi(const char *s) NONBANKED { - uint8_t i, sign = 0; - int8_t n; + bool sign = false; + int n; - for(i = 0; (s[i] == ' ') || (s[i] == '\n') || (s[i] == '\t'); ++i) - ; - switch(s[i]) - { - case '-': - sign++; - /* and fall through */ - case '+': - ++i; - break; + const uint8_t * pc = (const uint8_t *)s; + + for(; ((*pc == ' ') || (*pc == '\t') || (*pc == '\n')); ++pc); + + switch(*pc) { + case '-': + sign = true; + /* and fall through */ + case '+': + ++pc; + break; } - for(n = 0; isdigit(s[i]); ++i) - n = 10 * n + s[i] - '0'; - return (sign == 0 ? n : -n); + + for(n = 0; _isdigit(*pc); ++pc) n = 10 * n + (*pc - '0'); + + return (sign == 0 ? n : -n); } diff --git a/gbdk-lib/libc/atol.c b/gbdk-lib/libc/atol.c @@ -1,24 +1,28 @@ #include <stdint.h> -#include <stdlib.h> -#include <ctype.h> +#include <stdbool.h> -long atol(const char *s) NONBANKED -{ - uint8_t i, sign = 0; - long n; +inline bool _isdigit(char c) { + return ((uint8_t)((uint8_t)c - '0') < 10u) ? true : false; +} + +long atol(const char *s) { + bool sign = false; + long n; - for(i = 0; (s[i] == ' ') || (s[i] == '\n') || (s[i] == '\t'); ++i) - ; - switch(s[i]) - { - case '-': - sign++; - /* and fall through */ - case '+': - ++i; - break; + const uint8_t * pc = (const uint8_t *)s; + + for(; ((*pc == ' ') || (*pc == '\t') || (*pc == '\n')); ++pc); + + switch(*pc) { + case '-': + sign = true; + /* and fall through */ + case '+': + ++pc; + break; } - for(n = 0; isdigit(s[i]); ++i) - n = 10 * n + s[i] - '0'; - return (sign == 0 ? n : -n); + + for(n = 0; _isdigit(*pc); ++pc) n = 10 * n + (*pc - '0'); + + return (sign == 0 ? n : -n); } diff --git a/gbdk-lib/libc/isalpha.c b/gbdk-lib/libc/isalpha.c @@ -1,10 +1,7 @@ #include <ctype.h> #include <stdbool.h> +#include <stdint.h> -bool isalpha(char c) -{ - if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) - return 1; - else - return 0; +bool isalpha(char c) { + return ((((uint8_t)((uint8_t)c - 'a') < ('z' - 'a' + 1))) || ((uint8_t)((uint8_t)c - 'A') < ('Z' - 'A' + 1))) ? true : false; } diff --git a/gbdk-lib/libc/isdigit.c b/gbdk-lib/libc/isdigit.c @@ -1,10 +1,7 @@ #include <ctype.h> #include <stdbool.h> +#include <stdint.h> -bool isdigit(char c) -{ - if(c >= '0' && c <= '9') - return 1; - else - return 0; +bool isdigit(char c) { + return ((uint8_t)((uint8_t)c - '0') < 10u) ? true : false; } diff --git a/gbdk-lib/libc/islower.c b/gbdk-lib/libc/islower.c @@ -1,10 +1,7 @@ #include <ctype.h> #include <stdbool.h> +#include <stdint.h> -bool islower(char c) -{ - if(c >= 'a' && c <= 'z') - return 1; - else - return 0; +bool islower(char c) { + return ((uint8_t)((uint8_t)c - 'a') < ('z' - 'a' + 1)) ? true : false; } diff --git a/gbdk-lib/libc/isspace.c b/gbdk-lib/libc/isspace.c @@ -1,10 +1,7 @@ #include <ctype.h> #include <stdbool.h> +#include <stdint.h> -bool isspace(char c) -{ - if(c == ' ' || c == '\t' || c == '\n') - return 1; - else - return 0; +bool isspace(char c) { + return (((uint8_t)c == ' ') || ((uint8_t)c == '\t') || ((uint8_t)c == '\n')) ? true : false; } diff --git a/gbdk-lib/libc/isupper.c b/gbdk-lib/libc/isupper.c @@ -1,10 +1,7 @@ #include <ctype.h> #include <stdbool.h> +#include <stdint.h> -bool isupper(char c) -{ - if(c >= 'A' && c <= 'Z') - return 1; - else - return 0; +bool isupper(char c) { + return ((uint8_t)((uint8_t)c - 'A') < ('Z' - 'A' + 1)) ? true : false; } diff --git a/gbdk-lib/libc/puts.c b/gbdk-lib/libc/puts.c @@ -1,8 +1,12 @@ #include <stdio.h> +extern const char *__crlf; + void puts(const char *s) NONBANKED { while (*s) - putchar(*s++); - putchar('\n'); + putchar(*s++); + + for (s = __crlf; (*s); ) + putchar(*s++); } diff --git a/gbdk-lib/libc/sprintf.c b/gbdk-lib/libc/sprintf.c @@ -2,46 +2,46 @@ #include <stdio.h> #include <stdarg.h> #include <stdlib.h> +#include <stdint.h> typedef void (*emitter_t)(char, char **) OLDCALL; static const char _hex[] = "0123456789ABCDEF"; -static inline void _printhex(unsigned int u, emitter_t emitter, char ** pData) +inline void _printhex(uint16_t u, emitter_t emitter, char ** pData) { - for (char i = 0; i < 4; i++) { - (*emitter)(_hex[u >> 12], pData); - u = u << 4; - } + (*emitter)(_hex[(uint8_t)(u >> 8) >> 4], pData); + (*emitter)(_hex[(uint8_t)(u >> 8) & 0x0fu], pData); + (*emitter)(_hex[((uint8_t)u >> 4) & 0x0fu], pData); + (*emitter)(_hex[(uint8_t)u & 0x0fu], pData); } -static inline void _printhexbyte(unsigned char u, emitter_t emitter, char ** pData) +inline void _printhexbyte(uint8_t u, emitter_t emitter, char ** pData) { (*emitter)(_hex[u >> 4], pData); (*emitter)(_hex[u & 0x0fu], pData); } static void _printbuf(char * buf, emitter_t emitter, char ** pData) { - char *s = buf; - while (*s) (*emitter)(*s, pData), s++; + for (char *s = buf; *s; s++) (*emitter)(*s, pData); } void __printf(const char *format, emitter_t emitter, char **pData, va_list va) { char buf[16]; - while (*format) { - if (*format == '%') { + while ((uint8_t)(*format)) { + if ((uint8_t)(*format) == '%') { format++; // 0 Padding is not supported, ignore - if (*format == '0') format++; + if ((uint8_t)(*format) == '0') format++; // Width Specifier is not supported, ignore 1 digit worth - if ((*format >= '1') && (*format <= '9')) format++; + if ((uint8_t)((uint8_t)(*format) - '1') < 9u) format++; - switch (*format) { + switch ((uint8_t)(*format)) { case 'h': { - switch (*++format) { + switch ((uint8_t)(*++format)) { case 'X' : case 'x' : { _printhexbyte(va_arg(va, char), emitter, pData); diff --git a/gbdk-lib/libc/strcat.c b/gbdk-lib/libc/strcat.c @@ -5,15 +5,10 @@ * Return s1. */ -char *strcat(char *s1, const char *s2) NONBANKED -{ - char *os1; - - os1 = s1; - while(*s1++) - ; - --s1; - while(*s1++ = *s2++) - ; - return os1; +char *strcat(char *s1, const char *s2) { + char *os1 = s1; + while (*s1++) ; + --s1; + while (*s1++ = *s2++) ; + return os1; } diff --git a/gbdk-lib/libc/string.c b/gbdk-lib/libc/string.c @@ -8,8 +8,7 @@ char *strcpy(char *dest, const char *source) NONBANKED { char *d = dest; const char *s = source; - while (*d = *s) - d++, s++; + while (*d = *s) d++, s++; return dest; } #endif @@ -20,26 +19,18 @@ void *memcpy(void *dest, const void *source, size_t count) NONBANKED char *d = dest; const char *s = source; while (count--) { - *d = *s; - d++; - s++; + *d = *s; + d++; + s++; } return dest; } #endif #if USE_C_STRCMP -int strcmp(const char *s1, const char *s2) NONBANKED -{ +int strcmp(const char *s1, const char *s2) { char ret = 0; - - while (!(ret = *s1 - *s2) && *s2) - ++s1, ++s2; - - if (ret < 0) - return -1; - else if (ret > 0) - return 1; - return 0; + while (!(ret = *s1 - *s2) && *s2) ++s1, ++s2; + return (ret < 0) ? -1 : ((ret > 0) ? 1 : 0); } #endif diff --git a/gbdk-lib/libc/strncat.c b/gbdk-lib/libc/strncat.c @@ -6,20 +6,16 @@ * Return s1. */ -char *strncat(char *s1, const char *s2, int n) NONBANKED -{ - char *os1; - - os1 = s1; - while(*s1++) - ; - --s1; - while(*s1++ = *s2++) { - if(n == 0) { - *--s1 = '\0'; - break; +char *strncat(char *s1, const char *s2, int n) { + char *os1 = s1; + while (*s1++) ; + --s1; + while (*s1++ = *s2++) { + if (n == 0) { + *--s1 = '\0'; + break; + } + n--; } - n--; - } - return os1; + return os1; } diff --git a/gbdk-lib/libc/strncmp.c b/gbdk-lib/libc/strncmp.c @@ -7,12 +7,10 @@ * s1<s2: <0 */ -int strncmp(const char *s1, const char *s2, int n) NONBANKED -{ - while(n > 0 && *s1 == *s2++) { - if(*s1++ == '\0') - return 0; - n--; - } - return (n == 0 ? 0 : *s1 - *--s2); +int strncmp(const char *s1, const char *s2, int n) { + while ((n > 0) && (*s1 == *s2++)) { + if (*s1++ == '\0') return 0; + n--; + } + return (n == 0 ? 0 : *s1 - *--s2); } diff --git a/gbdk-lib/libc/strncpy.c b/gbdk-lib/libc/strncpy.c @@ -5,17 +5,14 @@ * Return s1. */ -char *strncpy(char *s1, const char *s2, int n) NONBANKED -{ - int i; - char *os1; - - os1 = s1; - for(i = 0; i < n; i++) - if((*s1++ = *s2++) == '\0') { - while(++i < n) - *s1++ = '\0'; - return os1; +char *strncpy(char *s1, const char *s2, int n) { + int i; + char *os1 = s1; + for (i = 0; i < n; i++) { + if ((*s1++ = *s2++) == '\0') { + while (++i < n) *s1++ = '\0'; + return os1; + } } - return os1; + return os1; } diff --git a/gbdk-lib/libc/targets/gbz80/ap/Makefile b/gbdk-lib/libc/targets/gbz80/ap/Makefile @@ -5,7 +5,7 @@ TOPDIR = ../../../.. THIS = ap PORT = gbz80 -CSRC = digits.c gprint.c gprintf.c gprintln.c gprintn.c +CSRC = crlf.c digits.c gprint.c gprintf.c gprintln.c gprintn.c ASSRC = cgb.s cgb_palettes.s cgb_compat.s \ cpy_data.s \ diff --git a/gbdk-lib/libc/targets/gbz80/ap/crt0.s b/gbdk-lib/libc/targets/gbz80/ap/crt0.s @@ -328,6 +328,7 @@ _set_interrupts:: .area _INITIALIZED ;; For malloc .area _HEAP + .area _HEAP_END .area _DATA __cpu:: diff --git a/gbdk-lib/libc/targets/gbz80/ap/global.s b/gbdk-lib/libc/targets/gbz80/ap/global.s @@ -388,6 +388,33 @@ OAMB_PAL1 = 4 ; Palette number; 0,1 (DMG) OAMB_BANK1 = 3 ; Bank number; 0,1 (GBC) + ;; SGB packets + .PAL_01 = 0x00 + .PAL_23 = 0x01 + .PAL_03 = 0x02 + .PAL_12 = 0x03 + .ATTR_BLK = 0x04 + .ATTR_LIN = 0x05 + .ATTR_DIV = 0x06 + .ATTR_CHR = 0x07 + .SOUND = 0x08 + .SOU_TRN = 0x09 + .PAL_SET = 0x0A + .PAL_TRN = 0x0B + .ATRC_EN = 0x0C + .TEST_EN = 0x0D + .ICON_EN = 0x0E + .DATA_SND = 0x0F + .DATA_TRN = 0x10 + .MLT_REQ = 0x11 + .JUMP = 0x12 + .CHR_TRN = 0x13 + .PCT_TRN = 0x14 + .ATTR_TRN = 0x15 + .ATTR_SET = 0x16 + .MASK_EN = 0x17 + .OBJ_TRN = 0x18 + ;; CPU detection .DMG_TYPE = 0x01 ; Original GB or Super GB .MGB_TYPE = 0xFF ; Pocket GB or Super GB 2 diff --git a/gbdk-lib/libc/targets/gbz80/crlf.c b/gbdk-lib/libc/targets/gbz80/crlf.c @@ -0,0 +1 @@ +const char * const __crlf = "\n"; + diff --git a/gbdk-lib/libc/targets/gbz80/duck/Makefile b/gbdk-lib/libc/targets/gbz80/duck/Makefile @@ -5,7 +5,7 @@ TOPDIR = ../../../.. THIS = duck PORT = gbz80 -CSRC = digits.c gprint.c gprintf.c gprintln.c gprintn.c +CSRC = crlf.c digits.c gprint.c gprintf.c gprintln.c gprintn.c ASSRC = cgb.s cgb_palettes.s cgb_compat.s \ cpy_data.s \ diff --git a/gbdk-lib/libc/targets/gbz80/duck/crt0.s b/gbdk-lib/libc/targets/gbz80/duck/crt0.s @@ -267,6 +267,7 @@ _set_interrupts:: .area _INITIALIZED ;; For malloc .area _HEAP + .area _HEAP_END .area _DATA __cpu:: diff --git a/gbdk-lib/libc/targets/gbz80/duck/global.s b/gbdk-lib/libc/targets/gbz80/duck/global.s @@ -388,6 +388,33 @@ OAMB_PAL1 = 4 ; Palette number; 0,1 (DMG) OAMB_BANK1 = 3 ; Bank number; 0,1 (GBC) + ;; SGB packets + .PAL_01 = 0x00 + .PAL_23 = 0x01 + .PAL_03 = 0x02 + .PAL_12 = 0x03 + .ATTR_BLK = 0x04 + .ATTR_LIN = 0x05 + .ATTR_DIV = 0x06 + .ATTR_CHR = 0x07 + .SOUND = 0x08 + .SOU_TRN = 0x09 + .PAL_SET = 0x0A + .PAL_TRN = 0x0B + .ATRC_EN = 0x0C + .TEST_EN = 0x0D + .ICON_EN = 0x0E + .DATA_SND = 0x0F + .DATA_TRN = 0x10 + .MLT_REQ = 0x11 + .JUMP = 0x12 + .CHR_TRN = 0x13 + .PCT_TRN = 0x14 + .ATTR_TRN = 0x15 + .ATTR_SET = 0x16 + .MASK_EN = 0x17 + .OBJ_TRN = 0x18 + ;; CPU detection .DMG_TYPE = 0x01 ; Original GB or Super GB .MGB_TYPE = 0xFF ; Pocket GB or Super GB 2 diff --git a/gbdk-lib/libc/targets/gbz80/gb/Makefile b/gbdk-lib/libc/targets/gbz80/gb/Makefile @@ -5,7 +5,7 @@ TOPDIR = ../../../.. THIS = gb PORT = gbz80 -CSRC = digits.c gprint.c gprintf.c gprintln.c gprintn.c +CSRC = crlf.c digits.c gprint.c gprintf.c gprintln.c gprintn.c ASSRC = cgb.s cgb_palettes.s cgb_compat.s \ cpy_data.s \ diff --git a/gbdk-lib/libc/targets/gbz80/gb/crt0.s b/gbdk-lib/libc/targets/gbz80/gb/crt0.s @@ -333,6 +333,7 @@ _set_interrupts:: .area _INITIALIZED ;; For malloc .area _HEAP + .area _HEAP_END .area _DATA .start_crt_globals: diff --git a/gbdk-lib/libc/targets/gbz80/gb/global.s b/gbdk-lib/libc/targets/gbz80/gb/global.s @@ -388,6 +388,33 @@ OAMB_PAL1 = 4 ; Palette number; 0,1 (DMG) OAMB_BANK1 = 3 ; Bank number; 0,1 (GBC) + ;; SGB packets + .PAL_01 = 0x00 + .PAL_23 = 0x01 + .PAL_03 = 0x02 + .PAL_12 = 0x03 + .ATTR_BLK = 0x04 + .ATTR_LIN = 0x05 + .ATTR_DIV = 0x06 + .ATTR_CHR = 0x07 + .SOUND = 0x08 + .SOU_TRN = 0x09 + .PAL_SET = 0x0A + .PAL_TRN = 0x0B + .ATRC_EN = 0x0C + .TEST_EN = 0x0D + .ICON_EN = 0x0E + .DATA_SND = 0x0F + .DATA_TRN = 0x10 + .MLT_REQ = 0x11 + .JUMP = 0x12 + .CHR_TRN = 0x13 + .PCT_TRN = 0x14 + .ATTR_TRN = 0x15 + .ATTR_SET = 0x16 + .MASK_EN = 0x17 + .OBJ_TRN = 0x18 + ;; CPU detection .DMG_TYPE = 0x01 ; Original GB or Super GB .MGB_TYPE = 0xFF ; Pocket GB or Super GB 2 diff --git a/gbdk-lib/libc/targets/gbz80/sgb.s b/gbdk-lib/libc/targets/gbz80/sgb.s @@ -1,154 +1,128 @@ - .include "global.s" + .include "global.s" - .PAL_01 = 0x00 - .PAL_23 = 0x01 - .PAL_03 = 0x02 - .PAL_12 = 0x03 - .ATTR_BLK = 0x04 - .ATTR_LIN = 0x05 - .ATTR_DIV = 0x06 - .ATTR_CHR = 0x07 - .SOUND = 0x08 - .SOU_TRN = 0x09 - .PAL_SET = 0x0A - .PAL_TRN = 0x0B - .ATRC_EN = 0x0C - .TEST_EN = 0x0D - .ICON_EN = 0x0E - .DATA_SND = 0x0F - .DATA_TRN = 0x10 - .MLT_REQ = 0x11 - .JUMP = 0x12 - .CHR_TRN = 0x13 - .PCT_TRN = 0x14 - .ATTR_TRN = 0x15 - .ATTR_SET = 0x16 - .MASK_EN = 0x17 - .OBJ_TRN = 0x18 + .area _CODE - .area _CODE - - ;; Check if running on SGB - ;; Set A to 0xFF when running on SGB - ;; Clear A when running on DMG + ;; Check if running on SGB + ;; Set A to 0xFF when running on SGB + ;; Clear A when running on DMG .sgb_check:: _sgb_check:: - PUSH BC - ADD SP, #-0x10 - LDHL SP, #0 - LD C, #0x10 - XOR A - RST 0x28 - LDHL SP,#0 - LD A,#((.MLT_REQ << 3) | 1) - LD (HL+),A - LD A,#0x03 - LD (HL-),A - CALL .sgb_transfer - LD C,#.P1 - LD A,#(.P14 | .P15) - LDH (C),A - LDH A,(C) - LDH A,(C) - LD E, #4 - LDH A,(C) ; read delayed + PUSH BC + ADD SP, #-0x10 + LDHL SP, #0 + LD C, #0x10 + XOR A + RST 0x28 + LDHL SP,#0 + LD A,#((.MLT_REQ << 3) | 1) + LD (HL+),A + LD A,#0x03 + LD (HL-),A + CALL .sgb_transfer + LD C,#.P1 + LD A,#(.P14 | .P15) + LDH (C),A + LDH A,(C) + LDH A,(C) + LD E, #4 + LDH A,(C) ; read delayed 3$: - LD B, A + LD B, A - LD A,#.P15 - LDH (C),A - LDH A,(C) - LDH A,(C) + LD A,#.P15 + LDH (C),A + LDH A,(C) + LDH A,(C) - LD A,#.P14 - LDH (C),A - LDH A,(C) - LDH A,(C) + LD A,#.P14 + LDH (C),A + LDH A,(C) + LDH A,(C) - LD A,#(.P14 | .P15) - LDH (C),A - LDH A,(C) - LDH A,(C) - LDH A,(C) - LDH A,(C) ; read delayed - CP B - JR NZ,1$ - - DEC E - JR NZ,3$ + LD A,#(.P14 | .P15) + LDH (C),A + LDH A,(C) + LDH A,(C) + LDH A,(C) + LDH A,(C) ; read delayed + CP B + JR NZ,1$ + + DEC E + JR NZ,3$ 2$: - LD E,#0 - JR 4$ + LD E,#0 + JR 4$ 1$: - LDHL SP,#1 - LD A,#0x00 - LD (HL-),A - CALL .sgb_transfer - LD E,#1 + LDHL SP,#1 + LD A,#0x00 + LD (HL-),A + CALL .sgb_transfer + LD E,#1 4$: - ADD SP,#0x10 - POP BC - RET + ADD SP,#0x10 + POP BC + RET _sgb_transfer:: - LDHL SP,#2 - LD A,(HL+) - LD H,(HL) - LD L,A - + LDHL SP,#2 + LD A,(HL+) + LD H,(HL) + LD L,A + .sgb_transfer:: - PUSH BC - LD A,(HL) ; Top of command data - AND #0x03 - JR Z,6$ + PUSH BC + LD A,(HL) ; Top of command data + AND #0x03 + JR Z,6$ 1$: - PUSH AF - LD C,#.P1 - LDH (C),A ; Send reset - LD A,#(.P14 | .P15) - LDH (C),A - LD B,#0x10 ; Set counter to transfer 16 byte -2$: LD E,#0x08 ; Set counter to transfer 8 bit - LD A,(HL+) - LD D,A + PUSH AF + LD C,#.P1 + LDH (C),A ; Send reset + LD A,#(.P14 | .P15) + LDH (C),A + LD B,#0x10 ; Set counter to transfer 16 byte +2$: LD E,#0x08 ; Set counter to transfer 8 bit + LD A,(HL+) + LD D,A 3$: - SRL D - LD A,#.P14 ; P14 = high, P15 = low (output "1") - JR C,4$ - LD A,#.P15 ; P14 = low, P15 = high (output "0") + SRL D + LD A,#.P14 ; P14 = high, P15 = low (output "1") + JR C,4$ + LD A,#.P15 ; P14 = low, P15 = high (output "0") 4$: - LDH (C),A - LDH A,(C) ; delay - LDH A,(C) - LD A,#(.P14 | .P15); P14 = high, P15 = high - LDH (C),A - LDH A,(C) ; delay - LDH A,(C) - DEC E - JR NZ,3$ + LDH (C),A + LDH A,(C) ; delay + LDH A,(C) + LD A,#(.P14 | .P15); P14 = high, P15 = high + LDH (C),A + LDH A,(C) ; delay + LDH A,(C) + DEC E + JR NZ,3$ - DEC B - JR NZ,2$ - - LD A,#.P15 ; 129th bit "0" output - LDH (C),A - LDH A,(C) ; delay - LDH A,(C) - LD A,#(.P14 | .P15) - LDH (C),A + DEC B + JR NZ,2$ + + LD A,#.P15 ; 129th bit "0" output + LDH (C),A + LDH A,(C) ; delay + LDH A,(C) + LD A,#(.P14 | .P15) + LDH (C),A - LD DE,#7000 + LD DE,#8400 ; was: 7000 5$: - LDH A,(.P1) ; 3 + - DEC DE ; 2 + - LD A,D ; 1 + - OR E ; 1 + - JR NZ,5$ ; 3 = 10 cycles + LDH A,(.P1) ; 3 + + DEC DE ; 2 + + LD A,D ; 1 + + OR E ; 1 + + JR NZ,5$ ; 3 = 10 cycles - POP AF - DEC A - JR NZ, 1$ + POP AF + DEC A + JR NZ, 1$ 6$: - POP BC - RET + POP BC + RET diff --git a/gbdk-lib/libc/targets/z80/fill_rect_compat.s b/gbdk-lib/libc/targets/z80/fill_rect_compat.s @@ -1,30 +0,0 @@ - .include "global.s" - - .globl .set_tile_map_xy - - .area _HOME - -; void set_tile_map(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint16_t tile) __z88dk_callee __preserves_regs(iyh, iyl); -_fill_rect_compat:: - pop hl ; HL = ret - pop bc ; BC = YX - pop de ; DE = WH - ex (sp), hl ; HL = data - ex de, hl ; HL = WH, DE = data - - ld a, b - ld b, d - - add #.SCREEN_Y_OFS - ld d, a - xor a - FAST_MOD8 d #28 - ld d, a - - ld a, c - add #.SCREEN_X_OFS - and #0x1f - ld c, e - ld e, a ; BC = data, DE = YX - - jp .fill_rect_xy_compat diff --git a/gbdk-lib/libc/targets/z80/fill_rect_xy.s b/gbdk-lib/libc/targets/z80/fill_rect_xy.s @@ -1,98 +0,0 @@ - .include "global.s" - - .title "VRAM utilities" - .module VRAMUtils - - .ez80 - - .area _HOME - - ;; Set background tile table from (BC) at XY = DE of size WH = HL -.fill_rect_xy:: - push hl - ld hl, #.VDP_TILEMAP - - ;; Set background tile from (BC) at YX = DE, size WH on stack, to VRAM from address (HL) -.fill_rect_xy_tt:: - push bc ; Store source - - ld a, d - rrca ; rrca(2) == rlca(6) - rrca - ld d, a - and #0x07 - add h - ld b, a - ld a, #0xC0 - and d - sla e - add e - ld c, a ; dest BC = HL + ((0x20 * Y) * 2) + (X * 2) - - ld a, b - cp #>(.VDP_TILEMAP+0x0700) - jr c, 5$ - ld b, #>.VDP_TILEMAP -5$: - pop hl ; HL = source - pop de ; DE = HW - push ix ; save IX - push de ; store HW - - ld ixh, b - ld ixl, c - push ix ; store dest - - DISABLE_VBLANK_COPY ; switch OFF copy shadow SAT - -1$: ; copy H rows - SMS_WRITE_VDP_CMD ixh, ixl - ld c, #.VDP_DATA -2$: ; copy W tiles - out (c), l - VDP_DELAY - out (c), h - - ld a, ixl - and #0x3F - inc a - inc a - bit 6, a - jp z, 3$ - and #0x3F - ld b, a - ld a, ixl - and #0xC0 - or b - ld ixl, a - SMS_WRITE_VDP_CMD ixh, ixl - dec e - jp nz, 2$ - jp 7$ -3$: - inc ixl - inc ixl - dec e - jp nz, 2$ -7$: - pop ix - pop de - - dec d - jr z, 6$ - - push de - - ld bc, #0x40 - add ix, bc - ld a, ixh - cp #>(.VDP_TILEMAP+0x0700) - jp c, 4$ - ld ixh, #>.VDP_TILEMAP -4$: - push ix - jp 1$ -6$: - ENABLE_VBLANK_COPY ; switch ON copy shadow SAT - pop ix ; restore IX - ret - diff --git a/gbdk-lib/libc/targets/z80/fill_rect_xy_compat.s b/gbdk-lib/libc/targets/z80/fill_rect_xy_compat.s @@ -1,101 +0,0 @@ - .include "global.s" - - .globl .vdp_shift - - .title "VRAM utilities" - .module VRAMUtils - - .ez80 - - .area _HOME - - ;; Set background tile table from (BC) at XY = DE of size WH = HL -.fill_rect_xy_compat:: - push hl - ld hl, #.VDP_TILEMAP - - ;; Set background tile from (BC) at YX = DE, size WH on stack, to VRAM from address (HL) -.fill_rect_xy_tt_compat:: - push bc ; Store source - - ld a, d - rrca ; rrca(2) == rlca(6) - rrca - ld d, a - and #0x07 - add h - ld b, a - ld a, #0xC0 - and d - sla e - add e - ld hl, #.vdp_shift - add (hl) - ld c, a ; dest BC = HL + ((0x20 * Y) * 2) + (X * 2) - - ld a, b - cp #>(.VDP_TILEMAP+0x0700) - jr c, 5$ - ld b, #>.VDP_TILEMAP -5$: - pop hl ; HL = source - pop de ; DE = HW - push ix ; save IX - push de ; store HW - ld ixh, b - ld ixl, c - push ix ; store dest - - DISABLE_VBLANK_COPY ; switch OFF copy shadow SAT - -1$: ; copy H rows - SMS_WRITE_VDP_CMD ixh, ixl - ld c, #.VDP_DATA -2$: ; copy W tiles - out (c), l - VDP_DELAY - in a, (c) ; skip next byte - - ld a, ixl - and #0x3F - inc a - inc a - bit 6, a - jp z, 3$ - and #0x3F - ld b, a - ld a, ixl - and #0xC0 - or b - ld ixl, a - SMS_WRITE_VDP_CMD ixh, ixl - dec e - jp nz, 2$ - jp 7$ -3$: - inc ixl - inc ixl - dec e - jp nz, 2$ -7$: - pop ix - pop de - - dec d - jr z, 6$ - - push de - - ld bc, #0x40 - add ix, bc - ld a, ixh - cp #>(.VDP_TILEMAP+0x0700) - jp c, 4$ - ld ixh, #>.VDP_TILEMAP -4$: - push ix - jp 1$ -6$: - ENABLE_VBLANK_COPY ; switch ON copy shadow SAT - pop ix ; restore IX - ret - diff --git a/gbdk-lib/libc/targets/z80/gg/Makefile b/gbdk-lib/libc/targets/z80/gg/Makefile @@ -5,25 +5,25 @@ TOPDIR = ../../../.. THIS = gg PORT = z80 -CSRC = +CSRC = crlf.c ASSRC = set_interrupts.s \ outi.s vmemcpy.s \ - refresh_oam.s \ - set_native_data.s set_1bpp_data.s set_2bpp_data.s \ + sms_refresh_oam.s \ + sms_set_native_data.s sms_set_1bpp_data.s sms_set_2bpp_data.s \ set_tile_map.s set_tile_map_xy.s set_tile_map_compat.s set_tile_map_xy_compat.s \ set_tile_submap.s set_tile_submap_compat.s \ coords_to_address.s \ set_tile.s \ - fill_rect.s fill_rect_xy.s fill_rect_compat.s fill_rect_xy_compat.s \ - metasprites.s metasprites_hide.s metasprites_hide_spr.s \ + sms_fill_rect.s sms_fill_rect_xy.s sms_fill_rect_compat.s sms_fill_rect_xy_compat.s \ + sms_metasprites.s sms_metasprites_hide.s sms_metasprites_hide_spr.s \ f_ibm_full.s f_ibm_sh.s f_italic.s f_min.s f_spect.s \ font.s color.s \ putchar.s \ scroll.s cls.s gotoxy.s \ palette.s set_palette.s \ pad.s pad_ex.s \ - int.s nmi.s \ + sms_int.s nmi.s \ mode.s clock.s \ delay.s \ emu_debug_printf.s \ diff --git a/gbdk-lib/libc/targets/z80/__sdcc_bcall.s b/gbdk-lib/libc/targets/z80/gg/__sdcc_bcall.s diff --git a/gbdk-lib/libc/targets/z80/gg/crlf.c b/gbdk-lib/libc/targets/z80/gg/crlf.c @@ -0,0 +1 @@ +const char * const __crlf = "\n"; + diff --git a/gbdk-lib/libc/targets/z80/gg/crt0.s b/gbdk-lib/libc/targets/z80/gg/crt0.s @@ -13,18 +13,18 @@ ; .org 0x08 ; --profile handler - .org 0x10 ; RST 0x10: SMS_WRITE_VDP_CMD + .org 0x10 ; RST 0x10: VDP_WRITE_CMD _WRITE_VDP_CMD:: - SMS_WRITE_VDP_CMD h, l + VDP_WRITE_CMD h, l ret ; .org 0x18 ; unusable - .org 0x20 ; RST 0x20: SMS_WRITE_VDP_DATA + .org 0x20 ; RST 0x20: VDP_WRITE_DATA _WRITE_VDP_DATA:: - SMS_WRITE_VDP_DATA h, l + VDP_WRITE_DATA h, l ret ; .org 0x28 ; unusable @@ -125,6 +125,7 @@ _WRITE_VDP_DATA:: .area _BSEG .area _BSS .area _HEAP + .area _HEAP_END .area _CODE .area _GSINIT diff --git a/gbdk-lib/libc/targets/z80/gg/global.s b/gbdk-lib/libc/targets/z80/gg/global.s @@ -243,7 +243,7 @@ ;; Macro definitions -.macro SMS_WRITE_VDP_DATA regH regL ?lbl +.macro VDP_WRITE_DATA regH regL ?lbl ld a, i ld a, regL di @@ -255,7 +255,7 @@ lbl: out (#.VDP_DATA), a .endm -.macro SMS_WRITE_VDP_CMD regH regL ?lbl +.macro VDP_WRITE_CMD regH regL ?lbl ld a, i ld a, regL di diff --git a/gbdk-lib/libc/targets/z80/int.s b/gbdk-lib/libc/targets/z80/int.s @@ -1,197 +0,0 @@ - .include "global.s" - - .title "INT Handler" - .module INTHandler - - .globl .sys_time, .vbl_done - .globl .OUTI128, .OUTI64, __shadow_OAM_base - - .area _HOME - -_INT_ISR:: - push af - push bc - push de - push hl - push iy - push ix - - in a, (.VDP_STAT) - and #.STATF_INT_VBL - jp z, 2$ - ;; handle VBlank - - ld hl, (.sys_time) - inc hl - ld (.sys_time), hl - - ld a, #1 - ld (.vbl_done), a - - ;; transfer shadow OAM - ld a, (__shadow_OAM_OFF) ; check transfer is OFF - or a - jp nz, 1$ - ld hl, #__shadow_OAM_base - ld h, (hl) - ld l, a ; a == 0 here - or h - jp z, 1$ - - ld c, #.VDP_CMD - ld a, #<.VDP_SAT - out (c), a - ld a, #>.VDP_SAT - out (c), a - dec c ; c == .VDP_DATA - call .OUTI64 - inc c ; c == .VDP_CMD - ld a, #<(.VDP_SAT + 0x80) - out (c), a - ld a, #>(.VDP_SAT + 0x80) - out (c), a - dec c ; c == .VDP_DATA - call .OUTI128 -1$: - - ;; call user-defined VBlank handlers - ld hl, (.VBLANK_HANDLER0) - ld a, h - or l - jp z, 3$ - CALL_HL - - ld hl, (.VBLANK_HANDLER1) - ld a, h - or l - jp z, 3$ - CALL_HL - - ld hl, (.VBLANK_HANDLER2) - ld a, h - or l - jp z, 3$ - CALL_HL - jp 3$ - - ;; handle HBlank -2$: - ld hl, (.HBLANK_HANDLER0) - CALL_HL - -3$: - pop ix - pop iy - pop hl - pop de - pop bc - pop af - ei - reti - -; void remove_LCD (int_handler h) __z88dk_fastcall __preserves_regs(b, c, iyh, iyl); -_remove_LCD:: -.remove_LCD:: - ld hl, #.empty_function - -; void add_LCD (int_handler h) __z88dk_fastcall __preserves_regs(b, c, iyh, iyl); -_add_LCD:: -.add_LCD:: - ld (.HBLANK_HANDLER0), hl - ret - -; void add_VBL(int_handler h) __z88dk_fastcall __preserves_regs(d, e, iyh, iyl); -_add_VBL:: - ld b, h - ld c, l - -.add_VBL:: - ld hl, #.VBLANK_HANDLER0 - - ;; Add interrupt routine in BC to the interrupt list in HL -.add_int:: -1$: - ld a, (hl) - inc hl - or (hl) - jr z, 2$ - inc hl - jr 1$ -2$: - ld (hl), b - dec hl - ld (hl), c - ret - -; void remove_VBL(int_handler h) __z88dk_fastcall __preserves_regs(iyh, iyl); -_remove_VBL:: - ld b, h - ld c, l - - ;; Remove interrupt routine in BC from the VBL interrupt list - ;; falldown to .remove_int -.remove_VBL:: - ld hl, #.VBLANK_HANDLER0 - - ;; Remove interrupt BC from interrupt list HL if it exists - ;; Abort if a 0000 is found (end of list) -.remove_int:: -1$: - ld e, (hl) - inc hl - ld d, (hl) - inc hl - ld a, e - or d - ret z ; No interrupt found - - ld a, e - cp c - jr nz, 1$ - - ld a, d - cp b - jr nz, 1$ - - ld d, h - ld e, l - dec de - dec de - - ;; Now do a memcpy from here until the end of the list -2$: - ld a, (hl) - ldi - or (hl) - ldi - jr nz, 2$ - ret - -_remove_TIM:: -_remove_SIO:: -_remove_JOY:: -_add_TIM:: -_add_SIO:: -_add_JOY:: -.empty_function: - ret - - .area _INITIALIZED - -.HBLANK_HANDLER0: - .ds 0x02 -.VBLANK_HANDLER0: - .ds 0x02 -.VBLANK_HANDLER1: - .ds 0x02 -.VBLANK_HANDLER2: - .ds 0x02 - .ds 0x02 - - .area _INITIALIZER - - .dw .empty_function - .dw 0x0000 - .dw 0x0000 - .dw 0x0000 - .dw 0x0000 diff --git a/gbdk-lib/libc/targets/z80/metasprites_hide.s b/gbdk-lib/libc/targets/z80/metasprites_hide.s @@ -1,37 +0,0 @@ - .include "global.s" - - .title "Metasprites" - .module Metasprites - - .globl ___current_metasprite, ___render_shadow_OAM - - .area _CODE - - -; void __hide_metasprite(uint8_t id) __z88dk_fastcall __preserves_regs(iyh,iyl); - -___hide_metasprite:: - ld e, l - - ld hl, (___current_metasprite) - - ld bc, #3 - - ld a, (___render_shadow_OAM) - ld d, a -1$: - ld a, (hl) - cp #0x80 - ret z - - ld a, #0x40 - cp e - ret c - - add hl, bc - - ld a, #0xC0 - ld (de), a - - inc e - jp 1$ diff --git a/gbdk-lib/libc/targets/z80/msxdos/Makefile b/gbdk-lib/libc/targets/z80/msxdos/Makefile @@ -0,0 +1,36 @@ +# GB specific Makefile + +TOPDIR = ../../../.. + +THIS = msxdos +PORT = z80 + +CSRC = crlf.c + +ASSRC = set_interrupts.s \ + outi.s \ + bdos_putchar.s bdos_getchar.s bdos_gets.s \ + msx_refresh_oam.s \ + msx_write_vdp.s \ + msx_set_native_data.s msx_set_1bpp_data.s msx_set_1bpp_sprite_data.s \ + msx_set_tile_map.s msx_set_tile_map_xy.s \ + msx_fill_rect.s msx_fill_rect_xy.s \ + msx_metasprites.s msx_metasprites_hide.s msx_metasprites_hide_spr.s \ + pad.s \ + msx_int.s \ + mode.s clock.s \ + delay.s \ + memset_small.s \ + far_ptr.s \ + color.s \ + heap.s \ + __sdcc_bcall.s __sdcc_bcall_abc.s \ + crt0.s + +CRT0 = crt0.s + +include $(TOPDIR)/Makefile.common + +AS = $(AS_Z80) + +include ../../../Makefile.platform diff --git a/gbdk-lib/libc/targets/z80/msxdos/__sdcc_bcall.s b/gbdk-lib/libc/targets/z80/msxdos/__sdcc_bcall.s @@ -0,0 +1,41 @@ + .include "global.s" + + .area _CODE + + .globl ___sdcc_bcall_ehl +; +; default trampoline to call banked functions +; Usage: +; ld e, #<function_bank> +; ld hl, #<function> +; call ___sdcc_bcall_ehl +; +___sdcc_bcall_ehl:: + push hl + ld h, #>__banks_remap_table + ld l, e ; e == new bank + + ld a, (__current_bank) + ld l, (hl) ; l == segment + ld h, a ; h == old bank + + ld a, e + ld (__current_bank), a ; new bank to __current_bank + + ld a, l ; set segment + call .mapper_page_set + ex (sp), hl ; old bank is left on stack + inc sp + + CALL_HL + dec sp + + ex (sp), hl + ld a, h + ld (__current_bank), a + ld h, #>__banks_remap_table + ld l, a + ld a, (hl) + call .mapper_page_set + pop hl + ret diff --git a/gbdk-lib/libc/targets/z80/msxdos/__sdcc_bcall_abc.s b/gbdk-lib/libc/targets/z80/msxdos/__sdcc_bcall_abc.s @@ -0,0 +1,59 @@ + .include "global.s" + + .area _CODE + + .globl ___sdcc_bcall + .globl ___sdcc_bcall_abc + +; +; trampoline to call banked functions +; used when legacy banking is enabled only +; Usage: +; call ___sdcc_bcall +; .dw <function> +; .dw <function_bank> +; +___sdcc_bcall:: + ex (sp), hl + ld c, (hl) + inc hl + ld b, (hl) + inc hl + ld a, (hl) + inc hl + inc hl + ex (sp), hl +; +; trampoline to call banked functions with __z88dk_fastcall calling convention +; Usage: +; ld a, #<function_bank> +; ld bc, #<function> +; call ___sdcc_bcall_abc +; +___sdcc_bcall_abc:: + push hl + ld l, a + ld a, (__current_bank) + ld h, #>__banks_remap_table + ld l, (hl) + ld h, a + call .mapper_page_set + ex (sp), hl + + inc sp + call ___sdcc_bjump_abc + dec sp + + ex (sp), hl + ld a, h + ld (__current_bank), a + ld h, #>__banks_remap_table + ld l, a + ld a, (hl) + call .mapper_page_set + pop hl + ret +; +___sdcc_bjump_abc: + push bc + ret diff --git a/gbdk-lib/libc/targets/z80/msxdos/bdos_getchar.s b/gbdk-lib/libc/targets/z80/msxdos/bdos_getchar.s @@ -0,0 +1,10 @@ + .include "global.s" + + .title "getchar" + .module getchar + + .area _CODE + +_getchar:: + JP_BDOS #_INNOE + + diff --git a/gbdk-lib/libc/targets/z80/msxdos/bdos_gets.s b/gbdk-lib/libc/targets/z80/msxdos/bdos_gets.s @@ -0,0 +1,34 @@ + .include "global.s" + + .title "gets" + .module gets + + .area _CODE + +_gets:: + pop hl + pop de + push de + push hl + push de + ld c, #63 ; limit input to 64 bytes including trailing zero + ; that match with internal buffer in scanf() +1$: + push bc + push de + CALL_BDOS #_CONIN + pop de + pop bc + cp #0x0d ; CR + jp z, 2$ + cp #0x05 ; EOF + jp z, 2$ + ld (de), a + inc de + dec c + jp nz, 1$ +2$: + xor a + ld (de), a + pop hl + ret + diff --git a/gbdk-lib/libc/targets/z80/msxdos/bdos_putchar.s b/gbdk-lib/libc/targets/z80/msxdos/bdos_putchar.s @@ -0,0 +1,15 @@ + .include "global.s" + + .title "putchar" + .module putchar + + .area _CODE + +_setchar:: +_putchar:: + pop hl + pop de + push de ; char in E + push hl + JP_BDOS #_CONOUT + + diff --git a/gbdk-lib/libc/targets/z80/msxdos/crlf.c b/gbdk-lib/libc/targets/z80/msxdos/crlf.c @@ -0,0 +1 @@ +const char * const __crlf = "\r\n"; + diff --git a/gbdk-lib/libc/targets/z80/msxdos/crt0.s b/gbdk-lib/libc/targets/z80/msxdos/crt0.s @@ -0,0 +1,522 @@ + .include "global.s" + + .title "Runtime" + .module Runtime + + .ez80 + + ;; Ordering of segments for the linker. + .area _CODE + .area _CODE_0 + .area _HOME + .area _BASE + .area _LIT + .area _GSINIT + .area _GSFINAL + .area _INITIALIZER + + .area _DATA + .area _INITIALIZED + .area _BSEG + .area _BSS + .area _HEAP + .area _HEAP_END + + ; program startup code + .area _CODE +.start:: + ei + call .save_int_vector + call .gsinit + + ld a, (___overlay_count) + and a + call nz, .load_overlays + jp c, .exit_error + + call .setup_video_mode + + ld hl, #.LS_FILE_BUFFER + xor a + ld b, a + ld c, (hl) + or c + jr z, 1$ + + add hl, bc + inc hl + xor a + ld (hl), a + ld c, a ; param counter + + ld hl, #(.LS_FILE_BUFFER + 1) +3$: + ld a, (hl) + or a + jr z, 6$ + cp #0x20 + jr nz, 6$ + inc hl + jr 3$ +6$: + jr z, 2$ + + inc c + push hl +4$: + inc hl + ld a, (hl) + or a + jr z, 2$ + cp #0x20 + jr nz, 4$ +5$: + ld (hl), #0 + inc hl + jr 3$ +2$: + ld hl, #0 + add hl, sp + push hl ; push pointer to argv array on stack +1$: + ld b, #0 + push bc ; push argc + ; reverse argv order + ld e, c + srl e + jr z, 7$ ; only 1 parameter + + dec c + ld ixh, b + ld ixl, c + add ix, ix + ld b, h + ld c, l + add ix, bc +8$: + ld d, (hl) + ld a, 0 (ix) + ld 0 (ix), d + ld (hl), a + inc hl + ld d, (hl) + ld a, 1 (ix) + ld 1 (ix), d + ld (hl), a + inc hl + dec ix + dec ix + dec e + jr nz, 8$ +7$: + call _main +.exit: + push hl + ld l, #0 + call _SWITCH_ROM + call .restore_int_vector + pop hl + ld b, l + CALL_BDOS #_TERM ; try terminate usind MSX DOS 2 function + JP_BDOS #_TERM0 +_exit:: + pop hl + pop hl + jr .exit + +.exit_error: + ld hl, #1$ + push hl + call _puts + JP_BDOS #_TERM0 +1$: + .asciz "LOAD ERROR!\r\n" + + ;; fills memory at HL of length BC with A, clobbers DE +.memset_simple:: + ld e, a + ld a, c + or b + ret z + ld (hl), e + dec bc + ld d, h + ld e, l + inc de + + ;; copies BC bytes from HL into DE +.memcpy_simple:: + ld a, c + or b + ret z + ldir + ret + + ;; put some internal data here, to save bytes wasted for the alignment of __banks_remap_table + +___overlay_count:: + .db 0 +.overlay_fcb: + .db 0 ; drive +___overlay_name:: + .ascii "OVERLAYS" ; filename +.overlay_fcb_ext:: + .ascii "000" ; extension +.overlay_fcb_extent: + .db 0 ; extent + .db 0 ; attributes +.overlay_fcb_record_size: + .db 0 ; extent ; msx-dos1: record size low byte +.overlay_fbc_record_cont: + .db 0 ; record count ; msx-dos1: record size high byte +.overlay_fcb_file_size: + .db 0, 0, 0, 0 ; file size + .db 0, 0, 0, 0 ; volume id + .ds 8 ; internal +.overlay_fcb_position: + .db 0 ; current record within extent +.overlay_fcb_random_pos: + .db 0, 0, 0, 0 ; Random record number +.overlay_fcb_end: + +__memman_present:: + .db 0 +__rammapper_table:: + .dw 0 + +.rammapper_hiwater: + .db 4 ; 7 segments for 128K config +.rammapper_alloc_hiwater: + .db 1 ; allocation starts from bank 1 + +.mapper_page_alloc:: + ld hl, #.rammapper_hiwater + ld a, (hl) + inc (hl) + or a ; never fail + ret + +_SWITCH_ROM:: ; Z88DK_FASTCALL : uint8_t parameter in l + ld a, l + ld (__current_bank), a + ld h, #>__banks_remap_table + ld l, a + ld a, (hl) +.mapper_page_set:: + out (.MAP_FRAME1), a + ret + +.mapper_page_get:: + in a, (.MAP_FRAME1) +__mapper_page_mask = .+1 +.globl __mapper_page_mask + and #0x00 ; zero ram size, will be patched + ret + +__current_bank:: + .db 0 + +; --- 256 byte boundary --------------------------------------- + + .bndry 0x100 +__banks_remap_table:: + .ds 100 +l__banks_remap_table = .-__banks_remap_table + +__mapper_bank_alloc:: + ld hl, #.rammapper_alloc_hiwater + ld a, (hl) + inc (hl) + ld h, #>__banks_remap_table + ld l, a + ld a, #0xff + cp (hl) + jr nz, 1$ + + xor a + ld b, a + push hl + call .mapper_page_alloc + pop hl + ret c ; return if no memory + ld (hl), a ; set segment number for the bank number +1$: + or a + ret ; allocated bank returns in l + +.macro TRANSFER_POINTER ofs dest + ld hl, #dest + ld (hl), #0xc3 + inc hl + ld a, ofs (ix) + ld (hl), a + inc hl + ld a, ofs+1 (ix) + ld (hl), a +.endm + +.initialize_ram_mapper:: + ; detect mapper capacity + in a, (.MAP_FRAME1) + ld e, a + xor a + out (.MAP_FRAME1), a + in a, (.MAP_FRAME1) + cpl + ld (__mapper_page_mask), a + and e + ld hl, #__banks_remap_table + ld (hl), a + inc hl + ld (hl), a ; bank 0 == bank 1 + + xor a + ld de,#0401 ; get routines info table + call .EXTBIO + ld (__rammapper_table), hl + + xor a + ld de, #0x0402 ; populate ram mapper routines table + call .EXTBIO + or a + jr nz, 1$ + scf + ret +1$: + push hl + pop ix + + TRANSFER_POINTER 0x01, .mapper_page_alloc + TRANSFER_POINTER 0x1f, .mapper_page_set + TRANSFER_POINTER 0x22, .mapper_page_get + + ld a, #1 + ld (__memman_present), a + + or a ; return ok + ret + + ;; Wait for VBL interrupt to be finished +.wait_vbl_done:: +_wait_vbl_done:: + ld a, (_shadow_VDP_R1) + and #.R1_DISP_ON + ret z + + xor a + ld (.vbl_done), a +1$: + halt + ld a, (.vbl_done) + or a + jr z, 1$ + ret + +; --- 256 byte boundary --------------------------------------- + + .bndry 256 +_shadow_OAM:: + .ds 0x80 + + ;; load overlays, count in A +.load_overlays: + ld b, a + ld a, (__memman_present) + or a + jr nz, 3$ + ld a, (__mapper_page_mask) + sub #3 ; ram segments used for DOS + inc a + cp b + ret c ; not sufficient ram to load overlays +3$: + ld c, #1 +1$: + push bc + call .load_overlay + pop bc + ret c ; error loading overlay + + ld a, c + cp b + jr z, 2$ ; all loaded - return + + inc c ; next overlay + ld a, #(l__banks_remap_table - 1) + cp c + ret c ; more than 99 overlays are not allowed + + jr 1$ +2$: + xor a + call _SWITCH_ROM + ret + +.load_overlay: + push bc + call __mapper_bank_alloc + pop bc + ret c ; no free segments + + call _SWITCH_ROM ; switch bank to l + + ld b, #8 + xor a +1$: + rlc c + adc a + daa + djnz 1$ + ; result in a, max 99 overlays + ld c, a + ld b, #0x0f + ld e, #'0' + ld hl, #(.overlay_fcb_ext + 1) + rra + rra + rra + rra + and b + add e + ld (hl), a + inc hl + ld a, c + and b + add e + ld (hl), a + + xor a + ld bc, #(.overlay_fcb_end - .overlay_fcb_extent) + ld hl, #.overlay_fcb_extent + call .memset_simple ; initialize fcb + + ld de, #.overlay_fcb + CALL_BDOS #_FOPEN + rrca + ret c ; file not found + + ld de, #0x4000 +3$: + CALL_BDOS #_SETDTA + + push de + ld de, #.overlay_fcb + CALL_BDOS #_RDSEQ + pop de + + rrca + jr c, 2$ ; EOF reached + + ld a, #128 + ADD_A_REG16 d, e + + ld a, #0xc0 + cp d + jr z, 2$ ; end of page1 reached + + jr 3$ +2$: + ld de, #.overlay_fcb + CALL_BDOS #_FCLOSE + + or a ; return ok + ret + +.restore_int_vector: + ld hl, #__old_int_vector + ld de, #.LS_INT_VECTOR + jr .restore_ldir +.save_int_vector: + ld hl, #.LS_INT_VECTOR + ld de, #__old_int_vector +.restore_ldir: + ld bc, #0x0005 + ld a, i + push af + di + ldir + pop af + jp po, 1$ + ei +1$: + ret + +.setup_video_mode: + ld a, i + push af + di + ;; Initialize VDP + ld c, #.VDP_CMD + ld b, #(.shadow_VDP_end - .shadow_VDP) + ld hl,#(.shadow_VDP_end - 1) +1$: + outd + jr 3$ ; delay +3$: + ld a, b + or #.VDP_REG_MASK + out (c), a + + ld a, b + or a + jr nz, 1$ + + pop af + jp po, 2$ + ei +2$: + ret + +.shadow_VDP: +_shadow_VDP_R0:: + .db #(.R0_DEFAULT | .R0_SCR_MODE2) +_shadow_VDP_R1:: + .db #(.R1_DEFAULT | .R1_DISP_ON | .R1_IE | .R1_SCR_MODE2 | .R1_SPR_8X8) +_shadow_VDP_R2:: + .db .R2_MAP_0x1C00 +_shadow_VDP_R3:: + .db 0xFF ; tiledata attr from 0x2000 +_shadow_VDP_R4:: + .db 0x03 ; tiledata from 0x0000 +_shadow_VDP_R5:: + .db .R5_SAT_0x1B00 +_shadow_VDP_R6:: + .db 0x07 +_shadow_VDP_R7:: +_shadow_VDP_RBORDER:: + .db 0x01 +.shadow_VDP_end:: + +.sys_time:: +_sys_time:: + .dw 0x0000 +.vbl_done:: + .db 0 +__shadow_OAM_base:: + .db #>_shadow_OAM +__shadow_OAM_OFF:: + .db 0 +.mode:: + .ds .T_MODE_INOUT ; Current mode +__old_int_vector:: + .ds 5 + + .area _GSINIT +.gsinit:: + ;; initialize static storage + xor a + ld bc, #l__DATA + ld hl, #s__DATA + call .memset_simple ; initialize variables in RAM with zero + + ;; initialize static storage variables + ld bc, #l__INITIALIZER + ld hl, #s__INITIALIZER + ld de, #s__INITIALIZED + call .memcpy_simple + + ;; initialize ram mapper + call .initialize_ram_mapper + + .area _GSFINAL + ret diff --git a/gbdk-lib/libc/targets/z80/msxdos/global.s b/gbdk-lib/libc/targets/z80/msxdos/global.s @@ -0,0 +1,483 @@ + .OS_RESET = 0x0000 + .LS_IO = 0x0003 + .LS_DSK = 0x0004 + .LS_INT_VECTOR = 0x0038 + .LS_FCB0 = 0x005C + .LS_FCB1 = 0x006C + .LS_FILE_BUFFER = 0x0080 + .BDOS = 0x0005 + + .EXTBIO = 0xFFCA + + ; MSX-DOS 1 + _TERM0 = 0x00 ; Program terminate + _CONIN = 0x01 ; Console input + _CONOUT = 0x02 ; Console output + _AUXIN = 0x03 ; Auxiliary input + _AUXOUT = 0x04 ; Auxiliary output + _LSTOUT = 0x05 ; Printer output + _DIRIO = 0x06 ; Direct console I/O + _DIRIN = 0x07 ; Direct console input + _INNOE = 0x08 ; Console input without echo + _STROUT = 0x09 ; String output + _BUFIN = 0x0A ; Buffered line input + _CONST = 0x0B ; Console status + _CPMVER = 0x0C ; Return version number + _DSKRST = 0x0D ; Disk reset + _SELDSK = 0x0E ; Select disk + _FOPEN = 0x0F ; Open file (FCB) + _FCLOSE = 0x10 ; Close file (FCB) + _SFIRST = 0x11 ; Search for first entry (FCB) + _SNEXT = 0x12 ; Search for next entry (FCB) + _FDEL = 0x13 ; Delete file (FCB) + _RDSEQ = 0x14 ; Sequential read (FCB) + _WRSEQ = 0x15 ; Sequential write (FCB) + _FMAKE = 0x16 ; Create file (FCB) + _FREN = 0x17 ; Rename file (FCB) + _LOGIN = 0x18 ; Get login vector + _CURDRV = 0x19 ; Get current drive + _SETDTA = 0x1A ; Set disk transfer address + _ALLOC = 0x1B ; Get allocation information + _RDRND = 0x21 ; Random read (FCB) + _WRRND = 0x22 ; Random write (FCB) + _FSIZE = 0x23 ; Get file size (FCB) + _SETRND = 0x24 ; Set random record (FCB) + _WRBLK = 0x26 ; Random block write (FCB) + _RDBLK = 0x27 ; Random block read (FCB) + _WRZER = 0x28 ; Random write with zero fill (FCB) + _GDATE = 0x2A ; Get date + _SDATE = 0x2B ; Set date + _GTIME = 0x2C ; Get time + _STIME = 0x2D ; Set time + _VERIFY = 0x2E ; Set/reset verify flag + _RDABS = 0x2F ; Absolute sector read + _WRABS = 0x30 ; Absolute sector write + + ; MSX-DOS2 + _DPARM = 0x31 ; Get disk parameters + _FFIRST = 0x40 ; Find first entry + _FNEXT = 0x41 ; Find next entry + _FNEW = 0x42 ; Find new entry + _OPEN = 0x43 ; Open file handle + _CREATE = 0x44 ; Create file handle + _CLOSE = 0x45 ; Close file handle + _ENSURE = 0x46 ; Ensure file handle + _DUP = 0x47 ; Duplicate file handle + _READ = 0x48 ; Read from file handle + _WRITE = 0x49 ; Write to file handle + _SEEK = 0x4A ; Move file handle pointer + _IOCTL = 0x4B ; I/O control for devices + _HTEST = 0x4C ; Test file handle + _DELETE = 0x4D ; Delete file or subdirectory + _RENAME = 0x4E ; Rename file or subdirectory + _MOVE = 0x4F ; Move file or subdirectory + _ATTR = 0x50 ; Get/set file attributes + _FTIME = 0x51 ; Get/set file date and time + _HDELETE = 0x52 ; Delete file handle + _HRENAME = 0x53 ; Rename file handle + _HMOVE = 0x54 ; Move file handle + _HATTR = 0x55 ; Get/set file handle attributes + _HFTIME = 0x56 ; Get/set file handle date and time + _GETDTA = 0x57 ; Get disk transfer address + _GETVFY = 0x58 ; Get verify flag setting + _GETCD = 0x59 ; Get current directory + _CHDIR = 0x5A ; Change current directory + _PARSE = 0x5B ; Parse pathname + _PFILE = 0x5C ; Parse filename + _CHKCHR = 0x5D ; Check character + _WPATH = 0x5E ; Get whole path string + _FLUSH = 0x5F ; Flush disk buffers + _FORK = 0x60 ; Fork a child process + _JOIN = 0x61 ; Rejoin parent process + _TERM = 0x62 ; Terminate with error code + _DEFAB = 0x63 ; Define abort exit routine + _DEFER = 0x64 ; Define disk error handler routine + _ERROR = 0x65 ; Get previous error code + _EXPLAIN = 0x66 ; Explain error code + _FORMAT = 0x67 ; Format a disk + _RAMD = 0x68 ; Create or destroy RAM disk + _BUFFER = 0x69 ; Allocate sector buffers + _ASSIGN = 0x6A ; Logical drive assignment + _GENV = 0x6B ; Get environment item + _SENV = 0x6C ; Set environment item + _FENV = 0x6D ; Find environment item + _DSKCHK = 0x6E ; Get/set disk check status + _DOSVER = 0x6F ; Get MSX-DOS version number + _REDIR = 0x70 ; Get/set redirection status + + ; MSX_DOS ERROR CODES + .NCOMP = 0xFF ; Incompatible disk + .WRERR = 0xFE ; Write error + .DISK = 0xFD ; Disk error + .NRDY = 0xFC ; Not ready + .VERFY = 0xFB ; Verify error + .DATA = 0xFA ; Data error + .RNF = 0xF9 ; Sector not found + .WPROT = 0xF8 ; Write protected disk + .UFORM = 0xF7 ; Unformatted disk + .NDOS = 0xF6 ; Not a DOS disk + .WDISK = 0xF5 ; Wrong disk + .WFILE = 0xF4 ; Wrong disk for file + .SEEK = 0xF3 ; Seek error + .IFAT = 0xF2 ; Bad file allocation table + .NOUPB = 0xF1 ; -- + .IFORM = 0xF0 ; Cannot format this drive + .INTER = 0xDF ; Internal error + .NORAM = 0xDE ; Not enough memory + .IBDOS = 0xDC ; Invalid MSX-DOS call + .IDRV = 0xDB ; Invalid drive + .IFNM = 0xDA ; Invalid filename + .IPATH = 0xD9 ; Invalid pathname + .PLONG = 0xD8 ; Pathname too long + .NOFIL = 0xD7 ; File not found + .NODIR = 0xD6 ; Directory not found + .DRFUL = 0xD5 ; Root directory full + .DKFUL = 0xD4 ; Disk full + .DUPF = 0xD3 ; Duplicate filename + .DIRE = 0xD2 ; Invalid directory move + .FILRO = 0xD1 ; Read only file + .DIRNE = 0xD0 ; Directory not empty + .IATTR = 0xCF ; Invalid attributes + .DOT = 0xCE ; Invalid . or .. operation + .SYSX = 0xCD ; System file exists + .DIRX = 0xCC ; Directory exists + .FILEX = 0xCB ; File exists + .FOPEN = 0xCA ; File already in use + .OV64K = 0xC9 ; Cannot transfer above 64K + .FILE = 0xC8 ; File allocation error + .EOF = 0xC7 ; End of file + .ACCV = 0xC6 ; File access violation + .IPROC = 0xC5 ; Invalid process id + .NHAND = 0xC4 ; No spare file handles + .IHAND = 0xC3 ; Invalid file handle + .NOPEN = 0xC2 ; File handle not open + .IDEV = 0xC1 ; Invalid device operation + .IENV = 0xC0 ; Invalid environment string + .ELONG = 0xBF ; Environment string too long + .IDATE = 0xBE ; Invalid date + .ITIME = 0xBD ; Invalid time + .RAMDX = 0xBC ; RAM disk (drive H ) already exists + .NRAMD = 0xBB ; RAM disk does not exist + .HDEAD = 0xBA ; File handle has been deleted + .EOL = 0xB9 ; Internal error. Should never occur + .ISBFN = 0xB8 ; Invalid sub-function number + .STOP = 0x9F ; Ctrl-STOP pressed + .CTRLC = 0x9E ; Ctrl-C pressed + .ABORT = 0x9D ; Disk operation aborted + .OUTERR = 0x9C ; Error on standard output + .INERR = 0x9B ; Error on standard input + .BADCOM = 0x8F ; Wrong version of COMMAND + .BADCM = 0x8E ; Unrecognized command + .BUFUL = 0x8D ; Command too long + .OKCMD = 0x8C ; -- + .IPARM = 0x8B ; Invalid parameter + .INP = 0x8A ; Too many parameters + .NOPAR = 0x89 ; Missing parameter + .IOPT = 0x88 ; Invalid option + .BADNO = 0x87 ; Invalid number + .NOHELP = 0x86 ; File for HELP not found + .BADVER = 0x85 ; Wrong version of MSX-DOS + .NOCAT = 0x84 ; Cannot concatenate destination file + .BADEST = 0x83 ; Cannot create destination file + .COPY = 0x82 ; File cannot be copied onto itself + .OVDEST = 0x81 ; Cannot overwrite previous destination file + + .MEMORY_CTL = 0x3E + + .MEMCTL_JOYON = 0b00000000 + .MEMCTL_JOYOFF = 0b00000100 + .MEMCTL_BASEON = 0b00000000 + .MEMCTL_BASEOFF = 0b00001000 + .MEMCTL_RAMON = 0b00000000 + .MEMCTL_RAMOFF = 0b00010000 + .MEMCTL_CROMON = 0b00000000 + .MEMCTL_CROMOFF = 0b00100000 + .MEMCTL_ROMON = 0b00000000 + .MEMCTL_ROMOFF = 0b01000000 + .MEMCTL_EXTON = 0b00000000 + .MEMCTL_EXTOFF = 0b10000000 + + .JOY_CTL = 0x3F + + .JOY_P1_LATCH = 0b00000010 + .JOY_P2_LATCH = 0b00001000 + + .VDP_VRAM = 0x4000 + .VDP_TILEDATA0 = 0x4000 + .VDP_TILEDATA1 = 0x4800 + .VDP_TILEDATA2 = 0x5000 + .VDP_COLORDATA0 = 0x6000 + .VDP_COLORDATA1 = 0x6800 + .VDP_COLORDATA1 = 0x7000 + + .VDP_SPRDATA0 = 0x7800 + + .VDP_TILEMAP = 0x5C00 + .VDP_CRAM = 0xC000 + .VDP_SAT = 0x5B00 + + .VDP_SAT_TERM = 0xD0 + + .VDP_VCOUNTER = 0x7E + .VDP_PSG = 0x7F + .VDP_HCOUNTER = 0x7F + + .VDP_DATA = 0x98 + .VDP_CMD = 0x99 + .VDP_STAT = 0x99 + + .STATF_INT_VBL = 0b10000000 + .STATF_9_SPR = 0b01000000 + .STATF_SPR_COLL = 0b00100000 + + .VDP_REG_MASK = 0b10000000 + .VDP_R0 = 0b10000000 + + .R0_DEFAULT = 0b00000000 + .R0_CB_OUTPUT = 0b00000000 + .R0_CB_INPUT = 0b01000000 + .R0_IE2_OFF = 0b00000000 + .R0_IE2 = 0b00100000 + .R0_IE1_OFF = 0b00000000 + .R0_IE1 = 0b00010000 + .R0_SCR_MODE1 = 0b00000000 + .R0_SCR_MODE2 = 0b00000010 + .R0_ES_OFF = 0b00000000 + .R0_ES = 0b00000001 + + .VDP_R1 = 0b10000001 + + .R1_DEFAULT = 0b10000000 + .R1_DISP_OFF = 0b00000000 + .R1_DISP_ON = 0b01000000 + .R1_IE_OFF = 0b00000000 + .R1_IE = 0b00100000 + .R1_SCR_MODE1 = 0b00010000 + .R1_SCR_MODE2 = 0b00000000 + .R1_SCR_MODE3 = 0b00000000 + .R1_SPR_8X8 = 0b00000000 + .R1_SPR_16X16 = 0b00000010 + .R1_SPR_MAG = 0b00000001 + .R1_SPR_MAG_OFF = 0b00000000 + + .VDP_R2 = 0b10000010 + + .R2_MAP_0x1C00 = 0x07 + + .VDP_R3 = 0b10000011 + .VDP_R4 = 0b10000100 + .VDP_R5 = 0b10000101 + + .R5_SAT_0x1B00 = 0x36 + .R5_SAT_MASK = 0b10000001 + + .VDP_R6 = 0b10000110 + + .VDP_R7 = 0b10000111 + .VDP_RBORDER = 0b10000111 + + .R7_COLOR_MASK = 0b11110000 + + .VDP_R8 = 0b10001000 + + .VDP_R9 = 0b10001001 + + .VDP_R10 = 0b10001010 + + .JOYPAD_COUNT = 1 + + .UP = 0b00100000 + .DOWN = 0b01000000 + .LEFT = 0b00010000 + .RIGHT = 0b10000000 + .A = 0b00000001 + .B = 0b00000100 + .SELECT = 0b00001000 + .START = 0b00000010 + + .KBD_SELECT_ROW = 0xAA + .KBD_INPUT = 0xA9 + + .JOY_PORT1 = 0xDC + + .JOY_P1_UP = 0b00000001 + .JOY_P1_DOWN = 0b00000010 + .JOY_P1_LEFT = 0b00000100 + .JOY_P1_RIGHT = 0b00001000 + .JOY_P1_SW1 = 0b00010000 + .JOY_P1_TRIGGER = 0b00010000 + .JOY_P1_SW2 = 0b00100000 + .JOY_P2_UP = 0b01000000 + .JOY_P2_DOWN = 0b10000000 + + .JOY_PORT2 = 0xDD + + .JOY_P2_LEFT = 0b00000001 + .JOY_P2_RIGHT = 0b00000010 + .JOY_P2_SW1 = 0b00000100 + .JOY_P2_TRIGGER = 0b00000100 + .JOY_P2_SW2 = 0b00001000 + .JOY_RESET = 0b00010000 + .JOY_P1_LIGHT = 0b01000000 + .JOY_P2_LIGHT = 0b10000000 + + .FMADDRESS = 0xF0 + .FMDATA = 0xF1 + .AUDIOCTRL = 0xF2 + + .MAP_FRAME0 = 0xfffc + .MAP_FRAME1 = 0xfffd + .MAP_FRAME2 = 0xfffe + .MAP_FRAME3 = 0xffff + + .BIOS = 0xC000 + + .SYSTEM_PAL = 0x00 + .SYSTEM_NTSC = 0x01 + + .CPU_CLOCK = 3579545 + + ;; GBDK library screen modes + + .T_MODE = 0x02 ; Text mode (bit 2) + .T_MODE_OUT = 0x02 ; Text mode output only + .T_MODE_INOUT = 0x03 ; Text mode with input + .M_NO_SCROLL = 0x04 ; Disables scrolling of the screen in text mode + .M_NO_INTERP = 0x08 ; Disables special character interpretation + + ;; Screen dimentions in tiles + + .SCREEN_X_OFS = 0 + .SCREEN_Y_OFS = 0 + .SCREEN_WIDTH = 32 + .SCREEN_HEIGHT = 24 + .VDP_MAP_HEIGHT = 24 + .VDP_MAP_WIDTH = 32 + + ;; Interrupt flags + + .VBL_IFLAG = 0x01 + .LCD_IFLAG = 0x02 + + ; characters + .CR = 0x0A + .SPACE = 0x00 + + ;; Main user routine + .globl _main + + ;; interrupt handler + .globl _INT_ISR + + ;; Macro definitions + +.macro CALL_BDOS fn + ld c, fn + call .BDOS +.endm + +.macro JP_BDOS fn + ld c, fn + jp .BDOS +.endm + +.macro VDP_WRITE_DATA regH regL ?lbl + ld a, i + ld a, regL + di + out (#.VDP_DATA), a ; 11 + ld a, regH ; 4 + jp po, lbl ; 7/12 + ei ; 4 (total: 26/27) +lbl: + out (#.VDP_DATA), a +.endm + +.macro VDP_WRITE_CMD regH regL ?lbl + ld a, i + ld a, regL + di + out (#.VDP_CMD), a + ld a, regH + jp po, lbl + ei +lbl: + out (#.VDP_CMD), a +.endm + +.macro VDP_DELAY ?lbl + nop + jr lbl +lbl: +.endm + +.macro VDP_CANCEL_INT + in a, (.VDP_STAT) ; cancel pending VDP interrupts +.endm + +.macro WRITE_VDP_CMD_HL + MSX_WRITE_VDP_CMD h, l +.endm + +.macro WRITE_VDP_DATA_HL + MSX_WRITE_VDP_DATA h, l +.endm + +.macro CALL_HL + call ___sdcc_call_hl +.endm + +.macro DISABLE_VBLANK_COPY + ld a, #1 + ld (__shadow_OAM_OFF), a +.endm + +.macro ENABLE_VBLANK_COPY + xor a + ld (__shadow_OAM_OFF), a +.endm + +.macro ADD_A_REG16 regH regL + add regL + ld regL, a + adc regH + sub regL + ld regH, a +.endm + +.macro MUL_DE_BY_A_RET_HL ?lbl1 ?lbl2 + ; Multiply DE by A, return result in HL; preserves: BC + ld hl, #0 +lbl1: + srl a + jp nc, lbl2 + add hl, de +lbl2: + sla e + rl d + or a + jp nz, lbl1 +.endm + +.macro DIV_PART divident divisor ?lbl + rl divident + rla + sub divisor + jr nc, lbl + add divisor +lbl: +.endm +.macro FAST_DIV8 divident divisor + ; returns modulus in A + .rept 8 + DIV_PART divident divisor + .endm + ld a, divident + cpl +.endm +.macro FAST_MOD8 divident divisor + ; returns modulus in A + .rept 8 + DIV_PART divident divisor + .endm +.endm diff --git a/gbdk-lib/libc/targets/z80/msxdos/msx_fill_rect.s b/gbdk-lib/libc/targets/z80/msxdos/msx_fill_rect.s @@ -0,0 +1,25 @@ + .include "global.s" + + .globl .fill_rect_xy + + .area _HOME + +; void fill_rect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint16_t tile) __z88dk_callee __preserves_regs(iyh, iyl); +_fill_rect:: + pop hl ; HL = ret + pop bc ; BC = YX + pop de ; DE = WH + ex (sp), hl ; HL = data + ex de, hl ; HL = WH, DE = data + + xor a + FAST_MOD8 b #.VDP_MAP_HEIGHT + + ld b, d + ld d, a + + ld a, c + ld c, e + ld e, a ; BC = data, DE = YX + + jp .fill_rect_xy diff --git a/gbdk-lib/libc/targets/z80/msxdos/msx_fill_rect_xy.s b/gbdk-lib/libc/targets/z80/msxdos/msx_fill_rect_xy.s @@ -0,0 +1,93 @@ + .include "global.s" + + .title "VRAM utilities" + .module VRAMUtils + + .ez80 + + .area _HOME + + ;; Set background tile table from (BC) at XY = DE of size WH = HL +.fill_rect_xy:: + push hl + ld hl, #.VDP_TILEMAP + + ;; Set background tile from (BC) at YX = DE, size WH on stack, to VRAM from address (HL) +.fill_rect_xy_tt:: + push bc ; Store source + + ld a, d + rrca ; rrca(3) == rlca(5) + rrca + rrca + ld d, a + and #0x07 + add h + ld b, a + ld a, #0xE0 + and d + add e + ld c, a ; dest BC = HL + ((0x20 * Y) + X + + ld a, b + cp #>(.VDP_TILEMAP+0x0300) + jr c, 5$ + ld b, #>.VDP_TILEMAP +5$: + pop hl ; HL = source + pop de ; DE = HW + push ix ; save IX + push de ; store HW + ld ixh, b + ld ixl, c + push ix ; store dest + + DISABLE_VBLANK_COPY ; switch OFF copy shadow SAT + +1$: ; copy H rows + VDP_WRITE_CMD ixh, ixl + ld c, #.VDP_DATA +2$: ; copy W tiles + out (c), l + + ld a, ixl + and #0x1F + inc a + bit 5, a + jp z, 3$ + and #0x1F + ld b, a + ld a, ixl + and #0xE0 + or b + ld ixl, a + VDP_WRITE_CMD ixh, ixl + dec e + jp nz, 2$ + jp 7$ +3$: + inc ixl + dec e + jp nz, 2$ +7$: + pop ix + pop de + + dec d + jr z, 6$ + + push de + + ld bc, #0x20 + add ix, bc + ld a, ixh + cp #>(.VDP_TILEMAP+0x0300) + jp c, 4$ + ld ixh, #>.VDP_TILEMAP +4$: + push ix + jp 1$ +6$: + ENABLE_VBLANK_COPY ; switch ON copy shadow SAT + pop ix ; restore IX + ret + diff --git a/gbdk-lib/libc/targets/z80/msxdos/msx_int.s b/gbdk-lib/libc/targets/z80/msxdos/msx_int.s @@ -0,0 +1,185 @@ + .include "global.s" + + .title "INT Handler" + .module INTHandler + + .globl .sys_time, .vbl_done + .globl .OUTI128, __shadow_OAM_base + + .area _GSINIT + + ld a, i + ld a, #0xc3 + di + ld (.LS_INT_VECTOR), a + ld hl, #_INT_ISR + jp po, 1$ + ei +1$: + ld (.LS_INT_VECTOR + 1), hl + + .area _HOME + +_INT_ISR:: + push af + push bc + push de + push hl + push iy + push ix + + in a, (.VDP_STAT) + and #.STATF_INT_VBL + jp z, 2$ + ;; handle VBlank + + ld hl, (.sys_time) + inc hl + ld (.sys_time), hl + + ld a, #1 + ld (.vbl_done), a + + ;; transfer shadow OAM + ld a, (__shadow_OAM_OFF) ; check transfer is OFF + or a + jp nz, 1$ + ld hl, #__shadow_OAM_base + ld h, (hl) + ld l, a ; a == 0 here + or h + jp z, 1$ + + ld c, #.VDP_CMD + ld a, #<.VDP_SAT + out (c), a + ld a, #>.VDP_SAT + out (c), a + dec c ; c == .VDP_DATA + call .OUTI128 +1$: + + ;; call user-defined VBlank handlers + ld hl, (.VBLANK_HANDLER0) + ld a, h + or l + jp z, 2$ + CALL_HL + + ld hl, (.VBLANK_HANDLER1) + ld a, h + or l + jp z, 2$ + CALL_HL + + ld hl, (.VBLANK_HANDLER2) + ld a, h + or l + jp z, 2$ + CALL_HL + +2$: + pop ix + pop iy + pop hl + pop de + pop bc + pop af + ei + reti + +; void add_VBL(int_handler h) __z88dk_fastcall __preserves_regs(d, e, iyh, iyl); +_add_VBL:: + ld b, h + ld c, l + +.add_VBL:: + ld hl, #.VBLANK_HANDLER0 + + ;; Add interrupt routine in BC to the interrupt list in HL +.add_int:: +1$: + ld a, (hl) + inc hl + or (hl) + jr z, 2$ + inc hl + jr 1$ +2$: + ld (hl), b + dec hl + ld (hl), c + ret + +; void remove_VBL(int_handler h) __z88dk_fastcall __preserves_regs(iyh, iyl); +_remove_VBL:: + ld b, h + ld c, l + + ;; Remove interrupt routine in BC from the VBL interrupt list + ;; falldown to .remove_int +.remove_VBL:: + ld hl, #.VBLANK_HANDLER0 + + ;; Remove interrupt BC from interrupt list HL if it exists + ;; Abort if a 0000 is found (end of list) +.remove_int:: +1$: + ld e, (hl) + inc hl + ld d, (hl) + inc hl + ld a, e + or d + ret z ; No interrupt found + + ld a, e + cp c + jr nz, 1$ + + ld a, d + cp b + jr nz, 1$ + + ld d, h + ld e, l + dec de + dec de + + ;; Now do a memcpy from here until the end of the list +2$: + ld a, (hl) + ldi + or (hl) + ldi + jr nz, 2$ + +_remove_LCD:: +.remove_LCD:: +_add_LCD:: +.add_LCD:: +_remove_TIM:: +_remove_SIO:: +_remove_JOY:: +_add_TIM:: +_add_SIO:: +_add_JOY:: +.empty_function: + ret + + .area _INITIALIZED + +.VBLANK_HANDLER0: + .ds 0x02 +.VBLANK_HANDLER1: + .ds 0x02 +.VBLANK_HANDLER2: + .ds 0x02 + .ds 0x02 + + .area _INITIALIZER + + .dw 0x0000 + .dw 0x0000 + .dw 0x0000 + .dw 0x0000 diff --git a/gbdk-lib/libc/targets/z80/msxdos/msx_metasprites.s b/gbdk-lib/libc/targets/z80/msxdos/msx_metasprites.s @@ -0,0 +1,77 @@ + .include "global.s" + + .title "Metasprites" + .module Metasprites + + .area _DATA + +___current_metasprite:: + .ds 0x02 +___current_base_tile:: + .ds 0x01 + + .area _CODE + +; uint8_t __move_metasprite(uint8_t id, uint8_t x, uint8_t y) __z88dk_callee __preserves_regs(iyh,iyl); + +___move_metasprite:: + ld hl, #4 + add hl, sp + + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld e, (hl) + + ld hl, (___current_metasprite) + + ld a, (___render_shadow_OAM) + ld d, a +1$: + ld a, (hl) ; dy + inc hl + cp #0x80 + jp z, 2$ + add b + ld b, a + cp #0xD0 + jp nz, 3$ + ld a, #0xC0 +3$: + ld (de), a + inc e + + ld a, (hl) ; dx + inc hl + add c + ld c, a + ld (de), a + inc e + + ld a, (___current_base_tile) + add (hl) ; tile + inc hl + ld (de), a + inc e + + ld a, (hl) + inc hl + ld (de), a + inc e + + jp 1$ +2$: + pop hl + pop bc + inc sp + push hl + ld a, e + srl a + srl a + sub c + ld l, a + ret + +___render_shadow_OAM:: + .db #>_shadow_OAM diff --git a/gbdk-lib/libc/targets/z80/msxdos/msx_metasprites_hide.s b/gbdk-lib/libc/targets/z80/msxdos/msx_metasprites_hide.s @@ -0,0 +1,37 @@ + .include "global.s" + + .title "Metasprites" + .module Metasprites + + .globl ___current_metasprite, ___render_shadow_OAM + + .area _CODE + + +; void __hide_metasprite(uint8_t id) __z88dk_fastcall __preserves_regs(iyh,iyl); + +___hide_metasprite:: + ld e, l + + ld hl, (___current_metasprite) + + ld bc, #4 + + ld a, (___render_shadow_OAM) + ld d, a +1$: + ld a, (hl) + cp #0x80 + ret z + + ld a, #0x1F + cp e + ret c + + add hl, bc + + ld a, #0xC0 + ld (de), a + + inc e + jp 1$ diff --git a/gbdk-lib/libc/targets/z80/msxdos/msx_metasprites_hide_spr.s b/gbdk-lib/libc/targets/z80/msxdos/msx_metasprites_hide_spr.s @@ -0,0 +1,39 @@ + .include "global.s" + + .title "Metasprites" + .module Metasprites + + .globl ___render_shadow_OAM + + .area _CODE + +; void hide_sprites_range(UINT8 from, UINT8 to) __z88dk_callee __preserves_regs(iyh,iyl); + +_hide_sprites_range:: + pop hl + pop de + push hl + + ld a, d + cp #(32+1) + ret nc + + sub e + ret c + ret z + ld b, a + + ld a, e + add a + add a + ld l, a + + ld a, (___render_shadow_OAM) + ld h, a + ld de, #4 +1$: + ld (hl), #0xC0 + add hl, de + djnz 1$ + + ret diff --git a/gbdk-lib/libc/targets/z80/msxdos/msx_refresh_oam.s b/gbdk-lib/libc/targets/z80/msxdos/msx_refresh_oam.s @@ -0,0 +1,27 @@ + .include "global.s" + + .title "VRAM utilities" + .module VRAMUtils + + .globl __shadow_OAM_base + + .ez80 + + .area _HOME + +; void refresh_OAM(); +_refresh_OAM:: + DISABLE_VBLANK_COPY ; switch OFF copy shadow SAT + ld de, #.VDP_SAT + VDP_WRITE_CMD d, e + + ld h, #>_shadow_OAM + ld l, #0 + ld c, #.VDP_DATA + ld b, #128 +1$: + outi + jp nz, 1$ + + ENABLE_VBLANK_COPY ; switch OFF copy shadow SAT + ret + diff --git a/gbdk-lib/libc/targets/z80/msxdos/msx_set_1bpp_data.s b/gbdk-lib/libc/targets/z80/msxdos/msx_set_1bpp_data.s @@ -0,0 +1,77 @@ + .include "global.s" + + .title "VRAM utilities" + .module VRAMUtils + + .ez80 + + .area _HOME + +; void set_tile_1bpp_data(uint16_t start, uint16_t ntiles, const void *src, uint16_t colors) __z88dk_callee; +_set_tile_1bpp_data:: + pop af + pop hl + pop iy + pop de + pop bc + push af + + push ix + ld ixl, c + + add hl, hl + add hl, hl + add hl, hl + + push hl + + ld bc, #.VDP_COLORDATA0 + add hl, bc + ex (sp), hl + + ld bc, #.VDP_TILEDATA0 + add hl, bc + + DISABLE_VBLANK_COPY ; switch OFF copy shadow SAT + + ld c, #.VDP_DATA + inc iyh + inc iyl + jr 2$ + +1$: + VDP_WRITE_CMD h, l + ex de, hl + ld b, #8 +3$: + outi + jr nz, 3$ + ex de, hl + + ld a, #8 + ADD_A_REG16 h, l + ex (sp), hl + + VDP_WRITE_CMD h, l + ld b, #8 + ld a, ixl +4$: + out (c), a + inc hl + dec b + jr nz, 4$ + + ex (sp), hl +2$: + dec iyl + jr nz, 1$ + + dec iyh + jr nz, 1$ + + ENABLE_VBLANK_COPY ; switch ON copy shadow SAT + + pop hl + pop ix + + ret diff --git a/gbdk-lib/libc/targets/z80/msxdos/msx_set_1bpp_sprite_data.s b/gbdk-lib/libc/targets/z80/msxdos/msx_set_1bpp_sprite_data.s @@ -0,0 +1,52 @@ + .include "global.s" + + .title "VRAM utilities" + .module VRAMUtils + + .ez80 + + .area _HOME + +; void set_sprite_1bpp_data(uint16_t start, uint16_t ntiles, const void *src) __z88dk_callee; +_set_sprite_1bpp_data:: + pop af + pop hl + pop iy + pop de + push af + + add hl, hl + add hl, hl + add hl, hl + + ld bc, #.VDP_SPRDATA0 + add hl, bc + + DISABLE_VBLANK_COPY ; switch OFF copy shadow SAT + + VDP_WRITE_CMD h, l + + ex de, hl + + ld d, iyh + ld e, iyl + + ld c, #.VDP_DATA + inc d + inc e + jr 2$ +1$: + ld b, #8 +3$: + outi + jr nz, 3$ +2$: + dec e + jr nz, 1$ + + dec d + jr nz, 1$ + + ENABLE_VBLANK_COPY ; switch ON copy shadow SAT + + ret diff --git a/gbdk-lib/libc/targets/z80/msxdos/msx_set_native_data.s b/gbdk-lib/libc/targets/z80/msxdos/msx_set_native_data.s @@ -0,0 +1,73 @@ + .include "global.s" + + .title "VRAM utilities" + .module VRAMUtils + .ez80 + + .area _HOME + +; void set_native_tile_data(uint16_t start, uint16_t ntiles, const void *src) __z88dk_callee +_set_native_tile_data:: + pop bc + pop hl + pop iy + pop de + push bc + + add hl, hl + add hl, hl + add hl, hl + + push hl + + ld bc, #.VDP_COLORDATA0 + add hl, bc + ex (sp), hl + + ld bc, #.VDP_TILEDATA0 + add hl, bc + + DISABLE_VBLANK_COPY ; switch OFF copy shadow SAT + + ld c, #.VDP_DATA + inc iyh + inc iyl + jr 2$ + +1$: + VDP_WRITE_CMD h, l + ex de, hl + ld b, #8 +3$: + outi + jr nz, 3$ + ex de, hl + + ld a, #8 + ADD_A_REG16 h, l + ex (sp), hl + + VDP_WRITE_CMD h, l + ex de, hl + ld b, #8 +4$: + outi + jr nz, 4$ + ex de, hl + + ld a, #8 + ADD_A_REG16 h, l + ex (sp), hl + +2$: + dec iyl + jr nz, 1$ + + dec iyh + jr nz, 1$ + + pop hl + + ENABLE_VBLANK_COPY ; switch ON copy shadow SAT + + ret diff --git a/gbdk-lib/libc/targets/z80/msxdos/msx_set_tile_map.s b/gbdk-lib/libc/targets/z80/msxdos/msx_set_tile_map.s @@ -0,0 +1,29 @@ + .include "global.s" + + .title "VRAM utilities" + .module VRAMUtils + + .globl .set_tile_map_xy + + .area _HOME + +; void set_tile_map(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint8_t *tiles) __z88dk_callee __preserves_regs(iyh, iyl); +_set_tile_map:: + pop hl ; HL = ret + pop bc ; BC = YX + pop de ; DE = WH + ex (sp), hl ; HL = data + ex de, hl ; HL = WH, DE = data + + xor a + FAST_MOD8 b #.VDP_MAP_HEIGHT + + ld b, d + ld d, a + + ld a, c + ld c, e + ld e, a ; BC = data, DE = YX + + jp .set_tile_map_xy + diff --git a/gbdk-lib/libc/targets/z80/msxdos/msx_set_tile_map_xy.s b/gbdk-lib/libc/targets/z80/msxdos/msx_set_tile_map_xy.s @@ -0,0 +1,100 @@ + .include "global.s" + + .title "VRAM utilities" + .module VRAMUtils + + .ez80 + + .area _HOME + + ;; Set background tile table from (BC) at XY = DE of size WH = HL +.set_tile_map_xy:: + push hl + ld hl, #.VDP_TILEMAP + + ;; Set background tile from (BC) at YX = DE, size WH on stack, to VRAM from address (HL) +.set_tile_map_xy_tt:: + push bc ; Store source + + ld a, d + rrca ; rrca(3) == rlca(5) + rrca + rrca + ld d, a + and #0x07 + add h + ld b, a + ld a, #0xE0 + and d + add e + ld c, a ; dest BC = HL + ((0x20 * Y) + X + + ld a, b + cp #>(.VDP_TILEMAP+0x0300) + jr c, 5$ + ld b, #>.VDP_TILEMAP +5$: + pop hl ; HL = source + pop de ; DE = HW + push ix ; save IX + push de ; store HW + ld ixh, b + ld ixl, c + push ix ; store dest + + DISABLE_VBLANK_COPY ; switch OFF copy shadow SAT + +1$: ; copy H rows + VDP_WRITE_CMD ixh, ixl + ld c, #.VDP_DATA +2$: ; copy W tiles + ld a, (__map_tile_offset) + add (hl) + out (c), a + inc hl + dec b + + ld a, ixl + and #0x1F + inc a + bit 5, a + jp z, 3$ + and #0x1F + ld b, a + ld a, ixl + and #0xE0 + or b + ld ixl, a + VDP_WRITE_CMD ixh, ixl + dec e + jp nz, 2$ + jp 7$ +3$: + inc ixl + dec e + jp nz, 2$ +7$: + pop ix + pop de + + dec d + jr z, 6$ + + push de + + ld bc, #0x20 + add ix, bc + ld a, ixh + cp #>(.VDP_TILEMAP+0x0300) + jp c, 4$ + ld ixh, #>.VDP_TILEMAP +4$: + push ix + jp 1$ +6$: + ENABLE_VBLANK_COPY ; switch ON copy shadow SAT + pop ix ; restore IX + ret + +__map_tile_offset:: + .db 0x00 diff --git a/gbdk-lib/libc/targets/z80/msxdos/msx_write_vdp.s b/gbdk-lib/libc/targets/z80/msxdos/msx_write_vdp.s @@ -0,0 +1,14 @@ + .include "global.s" + + .title "write vdp" + .module write_vdp + + .area _CODE + +_WRITE_VDP_CMD:: + VDP_WRITE_CMD h, l + ret +_WRITE_VDP_DATA:: + VDP_WRITE_DATA h, l + ret + + diff --git a/gbdk-lib/libc/targets/z80/msxdos/pad.s b/gbdk-lib/libc/targets/z80/msxdos/pad.s @@ -0,0 +1,66 @@ + .include "global.s" + + .title "JOYPad utilities" + .module JOYPad + .area _HOME + + ;; Get Keypad Button Status +_joypad:: +.jpad:: + ld a, #0x06 ; ... <code> <caps> <graph> <ctrl> <shift> + out (.KBD_SELECT_ROW), a + in a, (.KBD_INPUT) + cpl + + rrca + rl l + rrca + rrca + rl l ; shift graph + + ld a, #0x07 ; <ret>, ... + out (.KBD_SELECT_ROW), a + in a, (.KBD_INPUT) + cpl + + rlca + rl l + rl l + ld a, #0b00001110 + and l + ld l, a + + ld a, #0x08 ; <r><d><u><l><del><ins><home><space> + out (.KBD_SELECT_ROW), a + in a, (.KBD_INPUT) + cpl + and #0b11110001 + + or l + ld l, a ; <r><d><u><l><shift><graph><enter><space> + + ret + + ;; Wait until all buttons have been released +.padup:: +_waitpadup:: +1$: + ld h,#0x7f ; wait for .jpad return zero 127 times in a row +2$: + call .jpad + or a ; have all buttons been released? + jr nz,1$ ; not yet + + dec h + jr nz,2$ + + ret + + ;; Wait for the key to be pressed +_waitpad:: +.wait_pad:: +1$: + call .jpad ; read pad + and l ; compare with mask? + jr z,1$ ; loop if no intersection + ret diff --git a/gbdk-lib/libc/targets/z80/putchar.s b/gbdk-lib/libc/targets/z80/putchar.s @@ -36,6 +36,7 @@ _putchar:: push de push hl + ld a, e cp #.CR jr nz, 0$ diff --git a/gbdk-lib/libc/targets/z80/refresh_oam.s b/gbdk-lib/libc/targets/z80/refresh_oam.s @@ -1,34 +0,0 @@ - .include "global.s" - - .title "VRAM utilities" - .module VRAMUtils - - .globl __shadow_OAM_base - - .ez80 - - .area _HOME - -; void refresh_OAM(); -_refresh_OAM:: - DISABLE_VBLANK_COPY ; switch OFF copy shadow SAT - ld de, #.VDP_SAT - SMS_WRITE_VDP_CMD d, e - - ld h, #>_shadow_OAM - ld l, #0 - ld c, #.VDP_DATA - ld b, #64 -1$: - outi - jp nz, 1$ - - ld de, #(.VDP_SAT + 0x80) - SMS_WRITE_VDP_CMD d, e - ld b, #128 -2$: - outi - jp nz, 2$ - - ENABLE_VBLANK_COPY ; switch OFF copy shadow SAT - ret - diff --git a/gbdk-lib/libc/targets/z80/set_1bpp_data.s b/gbdk-lib/libc/targets/z80/set_1bpp_data.s @@ -1,108 +0,0 @@ - .include "global.s" - - .title "VRAM utilities" - .module VRAMUtils - - .ez80 - - .area _HOME - -; void set_tile_1bpp_data(uint16_t start, uint16_t ntiles, const void *src, uint16_t colors) __z88dk_callee __preserves_regs(iyh,iyl); -_set_tile_1bpp_data:: - pop de ; pop ret address - pop hl - - add hl, hl - add hl, hl - add hl, hl - add hl, hl - add hl, hl - - ld bc, #.VDP_VRAM - add hl, bc - - DISABLE_VBLANK_COPY ; switch OFF copy shadow SAT - - SMS_WRITE_VDP_CMD h, l - - ex de, hl ; hl = ret - - pop bc ; bc = ntiles - pop de ; de = src - ex (sp), hl ; hl = palette - - ex de, hl - - inc b - inc c - push ix - - ld ixh, d - ld ixl, e ; ix == palette - - push iy - ld iy, #-4 - add iy, sp - ld sp, iy - push bc - jr 2$ - -1$: - ex (sp), hl - - ld d, #8 -6$: - ld c, (hl) - inc hl - - ld e, #8 -5$: - srl c - - jr c, 10$ - ld a, ixh - jr 11$ -10$: - ld a, ixl -11$: - rra - rr 0 (iy) - rra - rr 1 (iy) - rra - rr 2 (iy) - rra - rr 3 (iy) - - dec e - jr nz, 5$ - - ld a, 0 (iy) - out (.VDP_DATA), a - ld a, 1 (iy) - out (.VDP_DATA), a - ld a, 2 (iy) - out (.VDP_DATA), a - ld a, 3 (iy) - out (.VDP_DATA), a - - dec d - jr nz, 6$ -2$: - ex (sp), hl - - dec l - jr nz, 1$ - - dec h - jr nz, 1$ - - ld iy, #6 - add iy, sp - ld sp, iy - pop iy - pop ix - - ENABLE_VBLANK_COPY ; switch ON copy shadow SAT - - ret diff --git a/gbdk-lib/libc/targets/z80/set_2bpp_data.s b/gbdk-lib/libc/targets/z80/set_2bpp_data.s @@ -1,144 +0,0 @@ - .include "global.s" - - .title "VRAM utilities" - .module VRAMUtils - - .ez80 - - .area _INITIALIZED -__current_2bpp_palette:: - .ds 0x02 - - .area _INITIALIZER - .dw 0b0011001000010000 - - .area _HOME - -; void set_tile_2bpp_data(uint16_t start, uint16_t ntiles, const void *src, uint16_t palette) __z88dk_callee __preserves_regs(iyh,iyl); -_set_tile_2bpp_data:: - pop de ; pop ret address - pop hl - - add hl, hl - add hl, hl - add hl, hl - add hl, hl - add hl, hl - - ld bc, #.VDP_VRAM - add hl, bc - - DISABLE_VBLANK_COPY ; switch OFF copy shadow SAT - - SMS_WRITE_VDP_CMD h, l - - ex de, hl ; hl = ret - - pop bc ; bc = ntiles - pop de ; de = src - ex (sp), hl ; hl = palette - - ex de, hl - - inc b - inc c - push ix - push iy - ld iy, #-4 - add iy, sp - ld sp, iy - push bc - jr 2$ - -1$: - ex (sp), hl - - ld ixh, #8 -6$: - ld c, (hl) - inc hl - ld b, (hl) - inc hl - - ld ixl, #8 -5$: - xor a - srl b - rla - srl c - rla - - push de ; save palette - - inc a - dec a - jr nz, 10$ - ; zero - ld a, e - jr 13$ -10$: - dec a - jr nz, 11$ - ; one - ld a, e - jr 14$ -11$: - dec a - jr nz, 12$ - ; two - ld a, d - jr 13$ -12$: - ;three - ld a, d -14$: - rra - rra - rra - rra -13$: - and #0x0F - - rra - rr 0 (iy) - rra - rr 1 (iy) - rra - rr 2 (iy) - rra - rr 3 (iy) - - pop de ; restore palette - - dec ixl - jr nz, 5$ - - ld a, 0 (iy) - out (.VDP_DATA), a - ld a, 1 (iy) - out (.VDP_DATA), a - ld a, 2 (iy) - out (.VDP_DATA), a - ld a, 3 (iy) - out (.VDP_DATA), a - - dec ixh - jr nz, 6$ -2$: - ex (sp), hl - - dec l - jr nz, 1$ - - dec h - jr nz, 1$ - - ld iy, #6 - add iy, sp - ld sp, iy - pop iy - pop ix - - ENABLE_VBLANK_COPY ; switch ON copy shadow SAT - - ret diff --git a/gbdk-lib/libc/targets/z80/set_native_data.s b/gbdk-lib/libc/targets/z80/set_native_data.s @@ -1,51 +0,0 @@ - .include "global.s" - - .title "VRAM utilities" - .module VRAMUtils - .area _HOME - -; void set_native_tile_data(uint16_t start, uint16_t ntiles, const void *src) __z88dk_callee __preserves_regs(iyh,iyl); -_set_native_tile_data:: - pop de ; pop ret address - pop hl - - add hl, hl - add hl, hl - add hl, hl - add hl, hl - add hl, hl - - ld bc, #.VDP_VRAM - add hl, bc - - DISABLE_VBLANK_COPY ; switch OFF copy shadow SAT - - SMS_WRITE_VDP_CMD h, l - - pop bc - pop hl - push de - - ld e, c - ld d, b - - ld c, #.VDP_DATA - inc d - inc e - jr 2$ - -1$: - ld b, #32 -3$: - outi - jr nz, 3$ -2$: - dec e - jr nz, 1$ - - dec d - jr nz, 1$ - - ENABLE_VBLANK_COPY ; switch ON copy shadow SAT - - ret diff --git a/gbdk-lib/libc/targets/z80/set_tile_map_xy.s b/gbdk-lib/libc/targets/z80/set_tile_map_xy.s @@ -46,7 +46,7 @@ DISABLE_VBLANK_COPY ; switch OFF copy shadow SAT 1$: ; copy H rows - SMS_WRITE_VDP_CMD ixh, ixl + VDP_WRITE_CMD ixh, ixl ld c, #.VDP_DATA 2$: ; copy W tiles outi @@ -65,7 +65,7 @@ and #0xC0 or b ld ixl, a - SMS_WRITE_VDP_CMD ixh, ixl + VDP_WRITE_CMD ixh, ixl dec e jp nz, 2$ jp 7$ diff --git a/gbdk-lib/libc/targets/z80/set_tile_map_xy_compat.s b/gbdk-lib/libc/targets/z80/set_tile_map_xy_compat.s @@ -58,7 +58,7 @@ __map_tile_offset:: DISABLE_VBLANK_COPY ; switch OFF copy shadow SAT 1$: ; copy H rows - SMS_WRITE_VDP_CMD ixh, ixl + VDP_WRITE_CMD ixh, ixl ld c, #.VDP_DATA 2$: ; copy W tiles ld a, (__map_tile_offset) @@ -82,7 +82,7 @@ __map_tile_offset:: and #0xC0 or b ld ixl, a - SMS_WRITE_VDP_CMD ixh, ixl + VDP_WRITE_CMD ixh, ixl dec e jp nz, 2$ jp 7$ diff --git a/gbdk-lib/libc/targets/z80/set_tile_submap.s b/gbdk-lib/libc/targets/z80/set_tile_submap.s @@ -98,7 +98,7 @@ _set_tile_submap:: DISABLE_VBLANK_COPY ; switch OFF copy shadow SAT 1$: ; copy H rows - SMS_WRITE_VDP_CMD ixh, ixl + VDP_WRITE_CMD ixh, ixl ld c, #.VDP_DATA 2$: ; copy W tiles outi @@ -117,7 +117,7 @@ _set_tile_submap:: and #0xC0 or b ld ixl, a - SMS_WRITE_VDP_CMD ixh, ixl + VDP_WRITE_CMD ixh, ixl dec e jp nz, 2$ jp 7$ diff --git a/gbdk-lib/libc/targets/z80/set_tile_submap_compat.s b/gbdk-lib/libc/targets/z80/set_tile_submap_compat.s @@ -108,7 +108,7 @@ _set_tile_submap_compat:: DISABLE_VBLANK_COPY ; switch OFF copy shadow SAT 1$: ; copy H rows - SMS_WRITE_VDP_CMD ixh, ixl + VDP_WRITE_CMD ixh, ixl ld c, #.VDP_DATA 2$: ; copy W tiles ld a, (__map_tile_offset) @@ -132,7 +132,7 @@ _set_tile_submap_compat:: and #0xC0 or b ld ixl, a - SMS_WRITE_VDP_CMD ixh, ixl + VDP_WRITE_CMD ixh, ixl dec e jp nz, 2$ jp 7$ diff --git a/gbdk-lib/libc/targets/z80/sms/Makefile b/gbdk-lib/libc/targets/z80/sms/Makefile @@ -5,25 +5,25 @@ TOPDIR = ../../../.. THIS = sms PORT = z80 -CSRC = +CSRC = crlf.c ASSRC = set_interrupts.s \ outi.s vmemcpy.s \ - refresh_oam.s \ - set_native_data.s set_1bpp_data.s set_2bpp_data.s \ + sms_refresh_oam.s \ + sms_set_native_data.s sms_set_1bpp_data.s sms_set_2bpp_data.s \ set_tile_map.s set_tile_map_xy.s set_tile_map_compat.s set_tile_map_xy_compat.s \ set_tile_submap.s set_tile_submap_compat.s \ coords_to_address.s \ set_tile.s \ - fill_rect.s fill_rect_xy.s fill_rect_compat.s fill_rect_xy_compat.s \ - metasprites.s metasprites_hide.s metasprites_hide_spr.s \ + sms_fill_rect.s sms_fill_rect_xy.s sms_fill_rect_compat.s sms_fill_rect_xy_compat.s \ + sms_metasprites.s sms_metasprites_hide.s sms_metasprites_hide_spr.s \ f_ibm_full.s f_ibm_sh.s f_italic.s f_min.s f_spect.s \ font.s color.s \ putchar.s \ scroll.s cls.s gotoxy.s \ palette.s set_palette.s \ pad.s pad_ex.s \ - int.s nmi.s \ + sms_int.s nmi.s \ mode.s clock.s \ delay.s \ emu_debug_printf.s \ diff --git a/gbdk-lib/libc/targets/z80/__sdcc_bcall.s b/gbdk-lib/libc/targets/z80/sms/__sdcc_bcall.s diff --git a/gbdk-lib/libc/targets/z80/sms/crlf.c b/gbdk-lib/libc/targets/z80/sms/crlf.c @@ -0,0 +1 @@ +const char * const __crlf = "\n"; + diff --git a/gbdk-lib/libc/targets/z80/sms/crt0.s b/gbdk-lib/libc/targets/z80/sms/crt0.s @@ -13,18 +13,18 @@ ; .org 0x08 ; --profile handler - .org 0x10 ; RST 0x10: SMS_WRITE_VDP_CMD + .org 0x10 ; RST 0x10: VDP_WRITE_CMD _WRITE_VDP_CMD:: - SMS_WRITE_VDP_CMD h, l + VDP_WRITE_CMD h, l ret ; .org 0x18 ; unusable - .org 0x20 ; RST 0x20: SMS_WRITE_VDP_DATA + .org 0x20 ; RST 0x20: VDP_WRITE_DATA _WRITE_VDP_DATA:: - SMS_WRITE_VDP_DATA h, l + VDP_WRITE_DATA h, l ret ; .org 0x28 ; unusable @@ -132,6 +132,7 @@ _WRITE_VDP_DATA:: .area _BSEG .area _BSS .area _HEAP + .area _HEAP_END .area _CODE .area _GSINIT diff --git a/gbdk-lib/libc/targets/z80/sms/global.s b/gbdk-lib/libc/targets/z80/sms/global.s @@ -207,7 +207,7 @@ ;; Macro definitions -.macro SMS_WRITE_VDP_DATA regH regL ?lbl +.macro VDP_WRITE_DATA regH regL ?lbl ld a, i ld a, regL di @@ -219,7 +219,7 @@ lbl: out (#.VDP_DATA), a .endm -.macro SMS_WRITE_VDP_CMD regH regL ?lbl +.macro VDP_WRITE_CMD regH regL ?lbl ld a, i ld a, regL di diff --git a/gbdk-lib/libc/targets/z80/fill_rect.s b/gbdk-lib/libc/targets/z80/sms_fill_rect.s diff --git a/gbdk-lib/libc/targets/z80/sms_fill_rect_compat.s b/gbdk-lib/libc/targets/z80/sms_fill_rect_compat.s @@ -0,0 +1,30 @@ + .include "global.s" + + .globl .set_tile_map_xy + + .area _HOME + +; void fill_rect_compat(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint16_t tile) __z88dk_callee __preserves_regs(iyh, iyl); +_fill_rect_compat:: + pop hl ; HL = ret + pop bc ; BC = YX + pop de ; DE = WH + ex (sp), hl ; HL = data + ex de, hl ; HL = WH, DE = data + + ld a, b + ld b, d + + add #.SCREEN_Y_OFS + ld d, a + xor a + FAST_MOD8 d #28 + ld d, a + + ld a, c + add #.SCREEN_X_OFS + and #0x1f + ld c, e + ld e, a ; BC = data, DE = YX + + jp .fill_rect_xy_compat diff --git a/gbdk-lib/libc/targets/z80/sms_fill_rect_xy.s b/gbdk-lib/libc/targets/z80/sms_fill_rect_xy.s @@ -0,0 +1,98 @@ + .include "global.s" + + .title "VRAM utilities" + .module VRAMUtils + + .ez80 + + .area _HOME + + ;; Set background tile table from (BC) at XY = DE of size WH = HL +.fill_rect_xy:: + push hl + ld hl, #.VDP_TILEMAP + + ;; Set background tile from (BC) at YX = DE, size WH on stack, to VRAM from address (HL) +.fill_rect_xy_tt:: + push bc ; Store source + + ld a, d + rrca ; rrca(2) == rlca(6) + rrca + ld d, a + and #0x07 + add h + ld b, a + ld a, #0xC0 + and d + sla e + add e + ld c, a ; dest BC = HL + ((0x20 * Y) * 2) + (X * 2) + + ld a, b + cp #>(.VDP_TILEMAP+0x0700) + jr c, 5$ + ld b, #>.VDP_TILEMAP +5$: + pop hl ; HL = source + pop de ; DE = HW + push ix ; save IX + push de ; store HW + + ld ixh, b + ld ixl, c + push ix ; store dest + + DISABLE_VBLANK_COPY ; switch OFF copy shadow SAT + +1$: ; copy H rows + VDP_WRITE_CMD ixh, ixl + ld c, #.VDP_DATA +2$: ; copy W tiles + out (c), l + VDP_DELAY + out (c), h + + ld a, ixl + and #0x3F + inc a + inc a + bit 6, a + jp z, 3$ + and #0x3F + ld b, a + ld a, ixl + and #0xC0 + or b + ld ixl, a + VDP_WRITE_CMD ixh, ixl + dec e + jp nz, 2$ + jp 7$ +3$: + inc ixl + inc ixl + dec e + jp nz, 2$ +7$: + pop ix + pop de + + dec d + jr z, 6$ + + push de + + ld bc, #0x40 + add ix, bc + ld a, ixh + cp #>(.VDP_TILEMAP+0x0700) + jp c, 4$ + ld ixh, #>.VDP_TILEMAP +4$: + push ix + jp 1$ +6$: + ENABLE_VBLANK_COPY ; switch ON copy shadow SAT + pop ix ; restore IX + ret + diff --git a/gbdk-lib/libc/targets/z80/sms_fill_rect_xy_compat.s b/gbdk-lib/libc/targets/z80/sms_fill_rect_xy_compat.s @@ -0,0 +1,101 @@ + .include "global.s" + + .globl .vdp_shift + + .title "VRAM utilities" + .module VRAMUtils + + .ez80 + + .area _HOME + + ;; Set background tile table from (BC) at XY = DE of size WH = HL +.fill_rect_xy_compat:: + push hl + ld hl, #.VDP_TILEMAP + + ;; Set background tile from (BC) at YX = DE, size WH on stack, to VRAM from address (HL) +.fill_rect_xy_tt_compat:: + push bc ; Store source + + ld a, d + rrca ; rrca(2) == rlca(6) + rrca + ld d, a + and #0x07 + add h + ld b, a + ld a, #0xC0 + and d + sla e + add e + ld hl, #.vdp_shift + add (hl) + ld c, a ; dest BC = HL + ((0x20 * Y) * 2) + (X * 2) + + ld a, b + cp #>(.VDP_TILEMAP+0x0700) + jr c, 5$ + ld b, #>.VDP_TILEMAP +5$: + pop hl ; HL = source + pop de ; DE = HW + push ix ; save IX + push de ; store HW + ld ixh, b + ld ixl, c + push ix ; store dest + + DISABLE_VBLANK_COPY ; switch OFF copy shadow SAT + +1$: ; copy H rows + VDP_WRITE_CMD ixh, ixl + ld c, #.VDP_DATA +2$: ; copy W tiles + out (c), l + VDP_DELAY + in a, (c) ; skip next byte + + ld a, ixl + and #0x3F + inc a + inc a + bit 6, a + jp z, 3$ + and #0x3F + ld b, a + ld a, ixl + and #0xC0 + or b + ld ixl, a + VDP_WRITE_CMD ixh, ixl + dec e + jp nz, 2$ + jp 7$ +3$: + inc ixl + inc ixl + dec e + jp nz, 2$ +7$: + pop ix + pop de + + dec d + jr z, 6$ + + push de + + ld bc, #0x40 + add ix, bc + ld a, ixh + cp #>(.VDP_TILEMAP+0x0700) + jp c, 4$ + ld ixh, #>.VDP_TILEMAP +4$: + push ix + jp 1$ +6$: + ENABLE_VBLANK_COPY ; switch ON copy shadow SAT + pop ix ; restore IX + ret + diff --git a/gbdk-lib/libc/targets/z80/sms_int.s b/gbdk-lib/libc/targets/z80/sms_int.s @@ -0,0 +1,197 @@ + .include "global.s" + + .title "INT Handler" + .module INTHandler + + .globl .sys_time, .vbl_done + .globl .OUTI128, .OUTI64, __shadow_OAM_base + + .area _HOME + +_INT_ISR:: + push af + push bc + push de + push hl + push iy + push ix + + in a, (.VDP_STAT) + and #.STATF_INT_VBL + jp z, 2$ + ;; handle VBlank + + ld hl, (.sys_time) + inc hl + ld (.sys_time), hl + + ld a, #1 + ld (.vbl_done), a + + ;; transfer shadow OAM + ld a, (__shadow_OAM_OFF) ; check transfer is OFF + or a + jp nz, 1$ + ld hl, #__shadow_OAM_base + ld h, (hl) + ld l, a ; a == 0 here + or h + jp z, 1$ + + ld c, #.VDP_CMD + ld a, #<.VDP_SAT + out (c), a + ld a, #>.VDP_SAT + out (c), a + dec c ; c == .VDP_DATA + call .OUTI64 + inc c ; c == .VDP_CMD + ld a, #<(.VDP_SAT + 0x80) + out (c), a + ld a, #>(.VDP_SAT + 0x80) + out (c), a + dec c ; c == .VDP_DATA + call .OUTI128 +1$: + + ;; call user-defined VBlank handlers + ld hl, (.VBLANK_HANDLER0) + ld a, h + or l + jp z, 3$ + CALL_HL + + ld hl, (.VBLANK_HANDLER1) + ld a, h + or l + jp z, 3$ + CALL_HL + + ld hl, (.VBLANK_HANDLER2) + ld a, h + or l + jp z, 3$ + CALL_HL + jp 3$ + + ;; handle HBlank +2$: + ld hl, (.HBLANK_HANDLER0) + CALL_HL + +3$: + pop ix + pop iy + pop hl + pop de + pop bc + pop af + ei + reti + +; void remove_LCD (int_handler h) __z88dk_fastcall __preserves_regs(b, c, iyh, iyl); +_remove_LCD:: +.remove_LCD:: + ld hl, #.empty_function + +; void add_LCD (int_handler h) __z88dk_fastcall __preserves_regs(b, c, iyh, iyl); +_add_LCD:: +.add_LCD:: + ld (.HBLANK_HANDLER0), hl + ret + +; void add_VBL(int_handler h) __z88dk_fastcall __preserves_regs(d, e, iyh, iyl); +_add_VBL:: + ld b, h + ld c, l + +.add_VBL:: + ld hl, #.VBLANK_HANDLER0 + + ;; Add interrupt routine in BC to the interrupt list in HL +.add_int:: +1$: + ld a, (hl) + inc hl + or (hl) + jr z, 2$ + inc hl + jr 1$ +2$: + ld (hl), b + dec hl + ld (hl), c + ret + +; void remove_VBL(int_handler h) __z88dk_fastcall __preserves_regs(iyh, iyl); +_remove_VBL:: + ld b, h + ld c, l + + ;; Remove interrupt routine in BC from the VBL interrupt list + ;; falldown to .remove_int +.remove_VBL:: + ld hl, #.VBLANK_HANDLER0 + + ;; Remove interrupt BC from interrupt list HL if it exists + ;; Abort if a 0000 is found (end of list) +.remove_int:: +1$: + ld e, (hl) + inc hl + ld d, (hl) + inc hl + ld a, e + or d + ret z ; No interrupt found + + ld a, e + cp c + jr nz, 1$ + + ld a, d + cp b + jr nz, 1$ + + ld d, h + ld e, l + dec de + dec de + + ;; Now do a memcpy from here until the end of the list +2$: + ld a, (hl) + ldi + or (hl) + ldi + jr nz, 2$ + ret + +_remove_TIM:: +_remove_SIO:: +_remove_JOY:: +_add_TIM:: +_add_SIO:: +_add_JOY:: +.empty_function: + ret + + .area _INITIALIZED + +.HBLANK_HANDLER0: + .ds 0x02 +.VBLANK_HANDLER0: + .ds 0x02 +.VBLANK_HANDLER1: + .ds 0x02 +.VBLANK_HANDLER2: + .ds 0x02 + .ds 0x02 + + .area _INITIALIZER + + .dw .empty_function + .dw 0x0000 + .dw 0x0000 + .dw 0x0000 + .dw 0x0000 + diff --git a/gbdk-lib/libc/targets/z80/metasprites.s b/gbdk-lib/libc/targets/z80/sms_metasprites.s diff --git a/gbdk-lib/libc/targets/z80/sms_metasprites_hide.s b/gbdk-lib/libc/targets/z80/sms_metasprites_hide.s @@ -0,0 +1,37 @@ + .include "global.s" + + .title "Metasprites" + .module Metasprites + + .globl ___current_metasprite, ___render_shadow_OAM + + .area _CODE + + +; void __hide_metasprite(uint8_t id) __z88dk_fastcall __preserves_regs(iyh,iyl); + +___hide_metasprite:: + ld e, l + + ld hl, (___current_metasprite) + + ld bc, #3 + + ld a, (___render_shadow_OAM) + ld d, a +1$: + ld a, (hl) + cp #0x80 + ret z + + ld a, #0x3F + cp e + ret c + + add hl, bc + + ld a, #0xC0 + ld (de), a + + inc e + jp 1$ diff --git a/gbdk-lib/libc/targets/z80/metasprites_hide_spr.s b/gbdk-lib/libc/targets/z80/sms_metasprites_hide_spr.s diff --git a/gbdk-lib/libc/targets/z80/sms_refresh_oam.s b/gbdk-lib/libc/targets/z80/sms_refresh_oam.s @@ -0,0 +1,34 @@ + .include "global.s" + + .title "VRAM utilities" + .module VRAMUtils + + .globl __shadow_OAM_base + + .ez80 + + .area _HOME + +; void refresh_OAM(); +_refresh_OAM:: + DISABLE_VBLANK_COPY ; switch OFF copy shadow SAT + ld de, #.VDP_SAT + VDP_WRITE_CMD d, e + + ld h, #>_shadow_OAM + ld l, #0 + ld c, #.VDP_DATA + ld b, #64 +1$: + outi + jp nz, 1$ + + ld de, #(.VDP_SAT + 0x80) + VDP_WRITE_CMD d, e + ld b, #128 +2$: + outi + jp nz, 2$ + + ENABLE_VBLANK_COPY ; switch OFF copy shadow SAT + ret + diff --git a/gbdk-lib/libc/targets/z80/sms_set_1bpp_data.s b/gbdk-lib/libc/targets/z80/sms_set_1bpp_data.s @@ -0,0 +1,108 @@ + .include "global.s" + + .title "VRAM utilities" + .module VRAMUtils + + .ez80 + + .area _HOME + +; void set_tile_1bpp_data(uint16_t start, uint16_t ntiles, const void *src, uint16_t colors) __z88dk_callee __preserves_regs(iyh,iyl); +_set_tile_1bpp_data:: + pop de ; pop ret address + pop hl + + add hl, hl + add hl, hl + add hl, hl + add hl, hl + add hl, hl + + ld bc, #.VDP_VRAM + add hl, bc + + DISABLE_VBLANK_COPY ; switch OFF copy shadow SAT + + VDP_WRITE_CMD h, l + + ex de, hl ; hl = ret + + pop bc ; bc = ntiles + pop de ; de = src + ex (sp), hl ; hl = palette + + ex de, hl + + inc b + inc c + push ix + + ld ixh, d + ld ixl, e ; ix == palette + + push iy + ld iy, #-4 + add iy, sp + ld sp, iy + push bc + jr 2$ + +1$: + ex (sp), hl + + ld d, #8 +6$: + ld c, (hl) + inc hl + + ld e, #8 +5$: + srl c + + jr c, 10$ + ld a, ixh + jr 11$ +10$: + ld a, ixl +11$: + rra + rr 0 (iy) + rra + rr 1 (iy) + rra + rr 2 (iy) + rra + rr 3 (iy) + + dec e + jr nz, 5$ + + ld a, 0 (iy) + out (.VDP_DATA), a + ld a, 1 (iy) + out (.VDP_DATA), a + ld a, 2 (iy) + out (.VDP_DATA), a + ld a, 3 (iy) + out (.VDP_DATA), a + + dec d + jr nz, 6$ +2$: + ex (sp), hl + + dec l + jr nz, 1$ + + dec h + jr nz, 1$ + + ld iy, #6 + add iy, sp + ld sp, iy + pop iy + pop ix + + ENABLE_VBLANK_COPY ; switch ON copy shadow SAT + + ret diff --git a/gbdk-lib/libc/targets/z80/sms_set_2bpp_data.s b/gbdk-lib/libc/targets/z80/sms_set_2bpp_data.s @@ -0,0 +1,144 @@ + .include "global.s" + + .title "VRAM utilities" + .module VRAMUtils + + .ez80 + + .area _INITIALIZED +__current_2bpp_palette:: + .ds 0x02 + + .area _INITIALIZER + .dw 0b0011001000010000 + + .area _HOME + +; void set_tile_2bpp_data(uint16_t start, uint16_t ntiles, const void *src, uint16_t palette) __z88dk_callee __preserves_regs(iyh,iyl); +_set_tile_2bpp_data:: + pop de ; pop ret address + pop hl + + add hl, hl + add hl, hl + add hl, hl + add hl, hl + add hl, hl + + ld bc, #.VDP_VRAM + add hl, bc + + DISABLE_VBLANK_COPY ; switch OFF copy shadow SAT + + VDP_WRITE_CMD h, l + + ex de, hl ; hl = ret + + pop bc ; bc = ntiles + pop de ; de = src + ex (sp), hl ; hl = palette + + ex de, hl + + inc b + inc c + push ix + push iy + ld iy, #-4 + add iy, sp + ld sp, iy + push bc + jr 2$ + +1$: + ex (sp), hl + + ld ixh, #8 +6$: + ld c, (hl) + inc hl + ld b, (hl) + inc hl + + ld ixl, #8 +5$: + xor a + srl b + rla + srl c + rla + + push de ; save palette + + inc a + dec a + jr nz, 10$ + ; zero + ld a, e + jr 13$ +10$: + dec a + jr nz, 11$ + ; one + ld a, e + jr 14$ +11$: + dec a + jr nz, 12$ + ; two + ld a, d + jr 13$ +12$: + ;three + ld a, d +14$: + rra + rra + rra + rra +13$: + and #0x0F + + rra + rr 0 (iy) + rra + rr 1 (iy) + rra + rr 2 (iy) + rra + rr 3 (iy) + + pop de ; restore palette + + dec ixl + jr nz, 5$ + + ld a, 0 (iy) + out (.VDP_DATA), a + ld a, 1 (iy) + out (.VDP_DATA), a + ld a, 2 (iy) + out (.VDP_DATA), a + ld a, 3 (iy) + out (.VDP_DATA), a + + dec ixh + jr nz, 6$ +2$: + ex (sp), hl + + dec l + jr nz, 1$ + + dec h + jr nz, 1$ + + ld iy, #6 + add iy, sp + ld sp, iy + pop iy + pop ix + + ENABLE_VBLANK_COPY ; switch ON copy shadow SAT + + ret diff --git a/gbdk-lib/libc/targets/z80/sms_set_native_data.s b/gbdk-lib/libc/targets/z80/sms_set_native_data.s @@ -0,0 +1,51 @@ + .include "global.s" + + .title "VRAM utilities" + .module VRAMUtils + .area _HOME + +; void set_native_tile_data(uint16_t start, uint16_t ntiles, const void *src) __z88dk_callee __preserves_regs(iyh,iyl); +_set_native_tile_data:: + pop de ; pop ret address + pop hl + + add hl, hl + add hl, hl + add hl, hl + add hl, hl + add hl, hl + + ld bc, #.VDP_VRAM + add hl, bc + + DISABLE_VBLANK_COPY ; switch OFF copy shadow SAT + + VDP_WRITE_CMD h, l + + pop bc + pop hl + push de + + ld e, c + ld d, b + + ld c, #.VDP_DATA + inc d + inc e + jr 2$ + +1$: + ld b, #32 +3$: + outi + jr nz, 3$ +2$: + dec e + jr nz, 1$ + + dec d + jr nz, 1$ + + ENABLE_VBLANK_COPY ; switch ON copy shadow SAT + + ret diff --git a/gbdk-lib/libc/targets/z80/vmemcpy.s b/gbdk-lib/libc/targets/z80/vmemcpy.s @@ -12,7 +12,7 @@ _vmemcpy:: DISABLE_VBLANK_COPY - SMS_WRITE_VDP_CMD h, l + VDP_WRITE_CMD h, l pop hl ; src pop bc ; size diff --git a/gbdk-lib/libc/time.c b/gbdk-lib/libc/time.c @@ -8,15 +8,10 @@ /* clock() is in clock.s */ #include <time.h> -time_t time(time_t *t) -{ +time_t time(time_t *t) { uint16_t ret; - /* Should be relative to 0:00 1970 GMT but hey */ ret = clock() / CLOCKS_PER_SEC; - - if (t) - *t = ret; - + if (t) *t = ret; return ret; } diff --git a/gbdk-lib/libc/tolower.c b/gbdk-lib/libc/tolower.c @@ -1,6 +1,5 @@ -#include <ctype.h> +#include <stdint.h> -char tolower(char c) -{ - return ((c >= 'A' && c <= 'Z') ? c + 32: c); +char tolower (char c) { + return ((uint8_t)((uint8_t)c - 'A') < ('Z' - 'A' + 1)) ? c + 32u : c; } diff --git a/gbdk-lib/libc/toupper.c b/gbdk-lib/libc/toupper.c @@ -1,6 +1,5 @@ -#include <ctype.h> +#include <stdint.h> -char toupper(char c) -{ - return ((c >= 'a' && c <= 'z') ? c - 32: c); +char toupper(char c) { + return ((uint8_t)((uint8_t)c - 'a') < ('z' - 'a' + 1)) ? c - 32u : c; } diff --git a/gbdk-support/bankpack/obj_data.c b/gbdk-support/bankpack/obj_data.c @@ -63,6 +63,8 @@ void banks_set_platform(char * platform_str) { g_platform = PLATFORM_SMS; else if (strcmp(platform_str, PLATFORM_STR_GG) == 0) g_platform = PLATFORM_SMS; // GG uses SMS platform + else if (strcmp(platform_str, PLATFORM_STR_MSXDOS) == 0) + g_platform = PLATFORM_SMS; // MSXDOS uses SMS platform else printf("BankPack: Warning: Invalid platform option %s\n", platform_str); } diff --git a/gbdk-support/bankpack/obj_data.h b/gbdk-support/bankpack/obj_data.h @@ -17,10 +17,11 @@ #define PLATFORM_DEFAULT PLATFORM_GB #define PLATFORM_STR_GB "gb" -#define PLATFORM_STR_AP "ap" // Uses PLATFORM_GB -#define PLATFORM_STR_DUCK "duck" // Uses PLATFORM_GB +#define PLATFORM_STR_AP "ap" // Uses PLATFORM_GB +#define PLATFORM_STR_DUCK "duck" // Uses PLATFORM_GB #define PLATFORM_STR_SMS "sms" -#define PLATFORM_STR_GG "gg" // Uses PLATFORM_SMS +#define PLATFORM_STR_GG "gg" // Uses PLATFORM_SMS +#define PLATFORM_STR_MSXDOS "msxdos" // Uses PLATFORM_SMS #define BANK_TYPE_UNSET 0 #define BANK_TYPE_DEFAULT 1 diff --git a/gbdk-support/lcc/gb.c b/gbdk-support/lcc/gb.c @@ -65,7 +65,8 @@ static struct { { "ihxcheck", "%bindir%ihxcheck" }, { "mkbin", "%sdccdir%makebin" }, { "crt0dir", "%libdir%%plat%/crt0.o"}, - { "libs_include", "-k %libdir%%port%/ -l %port%.lib -k %libdir%%plat%/ -l %plat%.lib"} + { "libs_include", "-k %libdir%%port%/ -l %port%.lib -k %libdir%%plat%/ -l %plat%.lib"}, + { "mkcom", "%sdccdir%makecom"} }; static char *getTokenVal(const char *key) @@ -207,6 +208,7 @@ char *ihxcheck[256]; char *ld[256]; char *bankpack[256]; char *mkbin[256]; +char *postproc[256]; char *rom_extension; arg_entry *llist0_defaults; int llist0_defaults_len = 0; @@ -312,6 +314,7 @@ void finalise(void) buildArgs(ld, _class->ld); buildArgs(ihxcheck, _class->ihxcheck); buildArgs(mkbin, _class->mkbin); + if (strlen(_class->postproc) != 0) buildArgs(postproc, _class->postproc); else postproc[0] = '\0'; rom_extension = strdup(_class->rom_extension); llist0_defaults = _class->llist0_defaults; llist0_defaults_len = _class->llist0_defaults_len; diff --git a/gbdk-support/lcc/gb.h b/gbdk-support/lcc/gb.h @@ -13,13 +13,15 @@ #define EXT_OBJ ".obj" #define EXT_IHX ".ihx" #define EXT_LK ".lk" +#define EXT_ROM ".rom" // ROM file extensions -#define EXT_GB ".gb" -#define EXT_AP ".pocket" -#define EXT_DUCK ".duck" -#define EXT_SMS ".sms" -#define EXT_GG ".gg" +#define EXT_GB ".gb" +#define EXT_AP ".pocket" +#define EXT_DUCK ".duck" +#define EXT_SMS ".sms" +#define EXT_GG ".gg" +#define EXT_MSXDOS ".com" #define ARRAY_LEN(A) (sizeof(A) / sizeof(A[0])) diff --git a/gbdk-support/lcc/lcc.c b/gbdk-support/lcc/lcc.c @@ -33,6 +33,7 @@ static char rcsid[] = "$Id: lcc.c,v 2.0 " BUILDDATE " " BUILDTIME " gbdk-2020 Ex void *alloc(int); extern char *basepath(char *); extern char *path_stripext(char *); +extern char *path_newext(char *, char *); static int callsys(char *[]); extern char *concat(const char *, const char *); static void compose(char *[], List, List, List); @@ -61,7 +62,7 @@ static void handle_autobanking(void); // These get populated from _class using finalise() in gb.c -extern char *cpp[], *include[], *com[], *as[], *bankpack[], *ld[], *ihxcheck[], *mkbin[], inputs[], *suffixes[], *rom_extension; +extern char *cpp[], *include[], *com[], *as[], *bankpack[], *ld[], *ihxcheck[], *mkbin[], *postproc[], inputs[], *suffixes[], *rom_extension; extern arg_entry *llist0_defaults; extern int llist0_defaults_len; @@ -101,6 +102,7 @@ static List lccinputs; /* list of input directories */ char bankpack_newext[1024] = {'\0'}; static int ihx_inputs = 0; // Number of ihx files present in input list static char ihxFile[256] = {'\0'}; +static char binFile[256] = {'\0'}; int main(int argc, char *argv[]) { @@ -286,10 +288,22 @@ int main(int argc, char *argv[]) { { if(errcnt == 0) { - //makebin - compose(mkbin, mkbinlist, append(ihxFile, 0), append(outfile, 0)); - if (callsys(av)) - errcnt++; + // makebin - use output filename unless there is a post-process step + if (strlen(postproc) == 0) + sprintf(binFile, "%s", outfile); + else + sprintf(binFile, "%s", path_newext(outfile, EXT_ROM)); + + compose(mkbin, mkbinlist, append(ihxFile, 0), append(binFile, 0)); + if (callsys(av)) + errcnt++; + + // post-process step (such as makecom), if applicable + if ((strlen(postproc) != 0) && (errcnt == 0)) { + compose(postproc, append(binFile, 0), append(outfile, 0), 0); + if (callsys(av)) + errcnt++; + } } } } @@ -387,7 +401,7 @@ char *basepath(char *name) { return s; } -// path_stripext - return a new string of path [name] with extension removed, +// path_stripext - return a new string of path [name] with extension removed // e.g. /usr/drh/foo.c => /usr/drh/foo char *path_stripext(char *name) { char * copy_str = strsave(name); @@ -406,6 +420,13 @@ char *path_stripext(char *name) { } +// path_newext - return a new string of path [name] with extension replaced +// e.g. /usr/drh/foo.c => /usr/drh/foo +char *path_newext(char *name, char *new_ext) { + return stringf("%s%s", path_stripext(name), new_ext); +} + + // Check if an extension matches the end of a filename int matches_ext(const char * filename, const char * ext) { diff --git a/gbdk-support/lcc/targets.c b/gbdk-support/lcc/targets.c @@ -26,6 +26,11 @@ arg_entry llist0_defaults_sms[] = { {.searchkey= "_CODE=", .addflag= "-b",.addvalue= "_CODE=0x0100", .found= false}, }; +// MSX +arg_entry llist0_defaults_msxdos[] = { + {.searchkey= "_DATA=", .addflag= "-b",.addvalue= "_DATA=0x8000", .found= false}, + {.searchkey= "_CODE=", .addflag= "-b",.addvalue= "_CODE=0x0100", .found= false}, +}; // Port/Platform specific settings // @@ -46,6 +51,7 @@ CLASS classes[] = { "%ld_gb% -n -i $1 %libs_include% $3 %crt0dir% $2", "%ihxcheck% $2 $1", "%mkbin% -yN -Z $1 $2 $3", // -yN: Don't paste in the Nintendo logo bytes for gameboy and clones (-Z) + "", llist0_defaults_gb, ARRAY_LEN(llist0_defaults_gb), }, // Analogue Pocket @@ -61,6 +67,7 @@ CLASS classes[] = { "%ld_gb% -n -i $1 %libs_include% $3 %crt0dir% $2", "%ihxcheck% $2 $1", "%mkbin% -yN -Z $1 $2 $3", // -yN: Don't paste in the Nintendo logo bytes for gameboy and clones (-Z) + "", llist0_defaults_gb, ARRAY_LEN(llist0_defaults_gb), // Use GB linker list defaults }, // Megaduck @@ -76,6 +83,7 @@ CLASS classes[] = { "%ld_gb% -n -i $1 %libs_include% $3 %crt0dir% $2", "%ihxcheck% $2 $1", "%mkbin% -yN -Z $1 $2 $3", // -yN: Don't paste in the Nintendo logo bytes for gameboy and clones (-Z) + "", llist0_defaults_gb, ARRAY_LEN(llist0_defaults_gb), // Use GB linker list defaults }, @@ -92,6 +100,7 @@ CLASS classes[] = { "%ld_z80% -a sms -n -i $1 %libs_include% $3 %crt0dir% $2", "%ihxcheck% $2 $1", "%mkbin% -S -xj 4 $1 $2 $3", + "", llist0_defaults_sms, ARRAY_LEN(llist0_defaults_sms), }, // GG @@ -107,8 +116,27 @@ CLASS classes[] = { "%ld_z80% -a sms -n -i $1 %libs_include% $3 %crt0dir% $2", "%ihxcheck% $2 $1", "%mkbin% -S $1 $2 $3", + "", llist0_defaults_sms, ARRAY_LEN(llist0_defaults_sms), // Use SMS linker list defaults + }, + + // MSX + { "z80", // port + "msxdos", // plat + "msxdos", // default_plat + EXT_MSXDOS, // ROM file extension + "%cpp% %cppdefault% -DINT_16_BITS $1 $2 $3", + "%includedefault%", + "%com% %comdefault% -Wa%asdefault% -DINT_16_BITS $1 %comflag% $2 -o $3", + "%as_z80% %asdefault% $1 $3 $2", + "%bankpack% -plat=sms $1 $2", + "%ld_z80% -a sms -n -i -j $1 %libs_include% $3 %crt0dir% $2", + "%ihxcheck% $2 $1", + "%mkbin% $1 $2 $3", + "%mkcom% $1 $2", + llist0_defaults_msxdos, ARRAY_LEN(llist0_defaults_msxdos), } + }; int classes_count = ARRAY_LEN(classes); diff --git a/gbdk-support/lcc/targets.h b/gbdk-support/lcc/targets.h @@ -27,6 +27,7 @@ typedef struct CLASS { const char *ld; const char *ihxcheck; const char *mkbin; + const char *postproc; arg_entry *llist0_defaults; int llist0_defaults_len; } CLASS; diff --git a/gbdk-support/makebin/Makefile b/gbdk-support/makebin/Makefile @@ -0,0 +1,30 @@ +# bankcheck (auto bank tool) makefile + +ifndef TARGETDIR +TARGETDIR = /opt/gbdk +endif + +ifeq ($(OS),Windows_NT) + BUILD_OS := Windows_NT +else + BUILD_OS := $(shell uname -s) +endif + +# Target older macOS version than whatever build OS is for better compatibility +ifeq ($(BUILD_OS),Darwin) + export MACOSX_DEPLOYMENT_TARGET=10.10 +endif + +CC = $(TOOLSPREFIX)gcc +CFLAGS = -ggdb -O -Wno-incompatible-pointer-types -DGBDKLIBDIR=\"$(TARGETDIR)\" +OBJ = makebin.o +BIN = makebin + +all: $(BIN) + +$(BIN): $(OBJ) + +clean: + rm -f *.o $(BIN) *~ + rm -f *.exe + diff --git a/gbdk-support/makebin/makebin.c b/gbdk-support/makebin/makebin.c @@ -0,0 +1,1022 @@ +/* + makebin - turn a .ihx file into a binary image or GameBoy format binaryimage + + Copyright (c) 2000 Michael Hope + Copyright (c) 2010 Borut Razem + Copyright (c) 2012 Noel Lemouel + Copyright (c) 2020-2021 Sebastian 'basxto' Riedel + Copyright (c) 2020 'bbbbbr' + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <ctype.h> + +#if defined(_WIN32) +#include <fcntl.h> +#include <io.h> +#else +#include <unistd.h> +#endif + + +typedef unsigned char BYTE; + +#define FILL_BYTE 0xff + +int +getnibble (FILE *fin) +{ + int ret; + int c = getc (fin); + + if (feof (fin) || ferror (fin)) + { + fprintf (stderr, "error: unexpected end of file.\n"); + exit (6); + } + + ret = c - '0'; + if (ret > 9) + { + ret -= 'A' - '9' - 1; + } + + if (ret > 0xf) + { + ret -= 'a' - 'A'; + } + + if (ret < 0 || ret > 0xf) + { + fprintf (stderr, "error: character %02x.\n", ret); + exit (7); + } + return ret; +} + +int +getbyte (FILE *fin, int *sum) +{ + int b = (getnibble (fin) << 4) | getnibble (fin); + *sum += b; + return b; +} + +void +usage (void) +{ + fprintf (stderr, + "makebin: convert a Intel IHX file to binary or GameBoy format binary.\n" + "Usage: makebin [options] [<in_file> [<out_file>]]\n" + "Options:\n" + " -p pack mode: the binary file size will be truncated to the last occupied byte\n" + " -s romsize size of the binary file (default: rom banks * 16384)\n" + " -Z generate GameBoy format binary file\n" + " -S generate Sega Master System format binary file\n" + " -t size skip size bytes from the beginning of the rom" + + "SMS format options (applicable only with -S option):\n" + " -xo n rom size (0xa-0x2)\n" + " -xj n set region code (3-7)\n" + //" -xc n product code (0-159999)\n" + " -xv n version number (0-15)\n" + //" -xV n SDSC version number\n" + //" -xd n SDSC date\n" + //" -xA n SDSC author pointer\n" + //" -xn n SDSC program name pointer\n" + //" -xD n SDSC description pointer\n" + + "GameBoy format options (applicable only with -Z option):\n" + " -yo n number of rom banks (default: 2) (autosize: A)\n" + " -ya n number of ram banks (default: 0)\n" + " -yt n MBC type (default: no MBC)\n" + " -yl n old licensee code (default: 0x33)\n" + " -yk cc new licensee string (default: 00)\n" + " -yn name cartridge name (default: none)\n" + " -yc GameBoy Color compatible\n" + " -yC GameBoy Color only\n" + " -ys Super GameBoy\n" + " -yS Convert .noi file named like input file to .sym\n" + " -yj set non-Japanese region flag\n" + " -yN do not copy big N validation logo into ROM header\n" + " -yp addr=value Set address in ROM to given value (address 0x100-0x1FE)\n" + "Arguments:\n" + " <in_file> optional IHX input file, '-' means stdin. (default: stdin)\n" + " <out_file> optional output file, '-' means stdout. (default: stdout)\n"); +} + +#define CART_NAME_LEN 16 + +struct gb_opt_s +{ + char cart_name[CART_NAME_LEN]; /* cartridge name buffer */ + char licensee_str[2]; /* new licensee string */ + BYTE mbc_type; /* MBC type (default: no MBC) */ + short nb_rom_banks; /* Number of rom banks (default: 2) */ + BYTE nb_ram_banks; /* Number of ram banks (default: 0) */ + BYTE licensee_id; /* old licensee code */ + BYTE is_gbc; /* 1 if GBC compatible, 2 if GBC only, false for all other*/ + BYTE is_sgb; /* True if SGB, false for all other*/ + BYTE sym_conversion; /* True if .noi file should be converted to .sym (default false)*/ + BYTE non_jp; /* True if non-Japanese region, false for all other*/ + BYTE rom_banks_autosize; /* True if rom banks should be auto-sized (default false)*/ + bool do_logo_copy; /* True if the nintendo logo should be copied into the ROM (default true) */ + BYTE address_overwrite[16]; /* For limited compatibility with very old versions */ +}; + +struct sms_opt_s +{ + BYTE rom_size; /* Doesn't have to be the real size, needed for checksum */ + BYTE region_code; /* Region code Japan/Export/International and SMS/GG */ + BYTE version; /* Game version */ +}; + +void +gb_postproc (BYTE * rom, int size, int *real_size, struct gb_opt_s *o) +{ + int i, chk; + static const BYTE gb_logo[] = + { + 0xce, 0xed, 0x66, 0x66, 0xcc, 0x0d, 0x00, 0x0b, + 0x03, 0x73, 0x00, 0x83, 0x00, 0x0c, 0x00, 0x0d, + 0x00, 0x08, 0x11, 0x1f, 0x88, 0x89, 0x00, 0x0e, + 0xdc, 0xcc, 0x6e, 0xe6, 0xdd, 0xdd, 0xd9, 0x99, + 0xbb, 0xbb, 0x67, 0x63, 0x6e, 0x0e, 0xec, 0xcc, + 0xdd, 0xdc, 0x99, 0x9f, 0xbb, 0xb9, 0x33, 0x3e + }; + + /* $0104-$0133: Nintendo logo + * If missing, an actual Game Boy won't run the ROM. + */ + + if (o->do_logo_copy) + { + memcpy (&rom[0x104], gb_logo, sizeof (gb_logo)); + } + + rom[0x144] = o->licensee_str[0]; + rom[0x145] = o->licensee_str[1]; + + /* + * 0134-0142: Title of the game in UPPER CASE ASCII. If it + * is less than 16 characters then the + * remaining bytes are filled with 00's. + */ + + /* capitalize cartridge name */ + for (i = 0; i < CART_NAME_LEN; ++i) + { + rom[0x134 + i] = toupper (o->cart_name[i]); + } + + if (o->is_gbc == 1) + { + rom[0x143] = 0x80; + } + + if (o->is_gbc == 2) + { + rom[0x143] = 0xC0; + } + + if (o->is_sgb) + { + rom[0x146] = 0x03; + } + + /* + * 0147: Cartridge type: + * 0-ROM ONLY 12-ROM+MBC3+RAM + * 1-ROM+MBC1 13-ROM+MBC3+RAM+BATT + * 2-ROM+MBC1+RAM 19-ROM+MBC5 + * 3-ROM+MBC1+RAM+BATT 1A-ROM+MBC5+RAM + * 5-ROM+MBC2 1B-ROM+MBC5+RAM+BATT + * 6-ROM+MBC2+BATTERY 1C-ROM+MBC5+RUMBLE + * 8-ROM+RAM 1D-ROM+MBC5+RUMBLE+SRAM + * 9-ROM+RAM+BATTERY 1E-ROM+MBC5+RUMBLE+SRAM+BATT + * B-ROM+MMM01 1F-Pocket Camera + * C-ROM+MMM01+SRAM FD-Bandai TAMA5 + * D-ROM+MMM01+SRAM+BATT FE - Hudson HuC-3 + * F-ROM+MBC3+TIMER+BATT FF - Hudson HuC-1 + * 10-ROM+MBC3+TIMER+RAM+BATT + * 11-ROM+MBC3 + */ + rom[0x147] = o->mbc_type; + + /* + * 0148 ROM size: + * 0 - 256Kbit = 32KByte = 2 banks + * 1 - 512Kbit = 64KByte = 4 banks + * 2 - 1Mbit = 128KByte = 8 banks + * 3 - 2Mbit = 256KByte = 16 banks + * 4 - 4Mbit = 512KByte = 32 banks + * 5 - 8Mbit = 1MByte = 64 banks + * 6 - 16Mbit = 2MByte = 128 banks + * $52 - 9Mbit = 1.1MByte = 72 banks + * $53 - 10Mbit = 1.2MByte = 80 banks + * $54 - 12Mbit = 1.5MByte = 96 banks + */ + switch (o->nb_rom_banks) + { + case 2: + rom[0x148] = 0; + break; + + case 4: + rom[0x148] = 1; + break; + + case 8: + rom[0x148] = 2; + break; + + case 16: + rom[0x148] = 3; + break; + + case 32: + rom[0x148] = 4; + break; + + case 64: + rom[0x148] = 5; + break; + + case 128: + rom[0x148] = 6; + break; + + case 256: + rom[0x148] = 7; + break; + + case 512: + rom[0x148] = 8; + break; + + default: + fprintf (stderr, "warning: unsupported number of ROM banks (%d)\n", o->nb_rom_banks); + rom[0x148] = 0; + break; + } + + /* + * 0149 RAM size: + * 0 - None + * 1 - 16kBit = 2kB = 1 bank + * 2 - 64kBit = 8kB = 1 bank + * 3 - 256kBit = 32kB = 4 banks + * 4 - 1MBit =128kB =16 banks + */ + switch (o->nb_ram_banks) + { + case 0: + rom[0x149] = 0; + break; + + case 1: + rom[0x149] = 2; + break; + + case 4: + rom[0x149] = 3; + break; + + case 16: + rom[0x149] = 4; + break; + + default: + fprintf (stderr, "warning: unsupported number of RAM banks (%d)\n", o->nb_ram_banks); + rom[0x149] = 0; + break; + } + + rom[0x14A] = o->non_jp; + + rom[0x14B] = o->licensee_id; + + for (i = 0; i < 16; i+=2) + { + if(o->address_overwrite[i] != 0xFF) + { + rom[0x0100 & o->address_overwrite[i]] = o->address_overwrite[i+1]; + // warnings for builds ported from ancient GBDK + fprintf (stderr, "caution: -yp0x01%02x=0x%02x is outdated", o->address_overwrite[i], o->address_overwrite[i+1]); + if(o->address_overwrite[i] == 0x43) + switch(o->address_overwrite[i+1]&0xC0) + { + case 0x80: + fprintf (stderr, ", please use -yc instead"); + break; + case 0xC0: + fprintf (stderr, ", please use -yC instead"); + break; + default: + o->address_overwrite[i] = 0xFF; + } + if(o->address_overwrite[i] == 0x44 || o->address_overwrite[i] == 0x45) + fprintf (stderr, ", please use -yk cc instead"); + if(o->address_overwrite[i] == 0x46) + if(o->address_overwrite[i+1] == 0x03) + fprintf (stderr, ", please use -ys instead"); + else + o->address_overwrite[i] = 0xFF; + if(o->address_overwrite[i] == 0x47) + fprintf (stderr, ", please use -yt 0x%02x instead", o->address_overwrite[i+1]); + if(o->address_overwrite[i] == 0x4A) + fprintf (stderr, ", please use -yl 0x%02x instead", o->address_overwrite[i+1]); + if(o->address_overwrite[i] == 0x4B && o->address_overwrite[i+1] == 1) + fprintf (stderr, ", please use -yj instead"); + if(o->address_overwrite[i] == 0xFF) + fprintf (stderr, ", this setting is the default"); + fprintf (stderr, ".\n"); + } + } + + /* Update complement checksum */ + chk = 0; + for (i = 0x134; i < 0x14d; ++i) + chk += rom[i]; + rom[0x014d] = (unsigned char) (0xe7 - (chk & 0xff)); + + /* Update checksum */ + chk = 0; + rom[0x14e] = 0; + rom[0x14f] = 0; + for (i = 0; i < size; ++i) + chk += rom[i]; + rom[0x14e] = (unsigned char) ((chk >> 8) & 0xff); + rom[0x14f] = (unsigned char) (chk & 0xff); + + if (*real_size < 0x150) + *real_size = 0x150; +} + +void +sms_postproc (BYTE * rom, int size, int *real_size, struct sms_opt_s *o) +{ + // based on https://www.smspower.org/Development/ROMHeader + // 0x1ff0 and 0x3ff0 are also possible, but never used + static const char tmr_sega[] = "TMR SEGA "; + short header_base = 0x7ff0; + int chk = 0; + unsigned long i; + // choose earlier positions for smaller roms + if (header_base > size) + header_base = 0x3ff0; + if (header_base > size) + header_base = 0x1ff0; + + memcpy (&rom[header_base], tmr_sega, sizeof (tmr_sega) - 1); + // configure amounts of bytes to check + switch(o->rom_size) + { + case 0xa: + default: + i = 0x1FEF; + break; + case 0xb: + i = 0x3FEF; + break; + case 0xc: + i = 0x7FEF; + break; + case 0xd: + i = 0xBFEF; + break; + case 0xe: + i = 0xFFFF; + break; + case 0xf: + i = 0x1FFFF; + break; + case 0x0: + i = 0x3FFFF; + break; + case 0x1: + i = 0x7FFFF; + break; + case 0x2: + i = 0xFFFFF; + break; + } + // calculate checksum + for(;i > 0; --i) + { + chk += rom[i]; + // 0x7FF0 - 0x7FFF is skipped + if(i == 0x8000) + i = 0x7FF0; + } + // we skipped index 0 + chk += rom[0]; + // little endian + rom[header_base + 0xa] = chk & 0xff; + rom[header_base + 0xb] = (chk>>8) & 0xff; + // game version + rom[header_base + 0xe] &= 0xF0; + rom[header_base + 0xe] |= o->version; + // rom size + rom[header_base + 0xf] = (o->region_code << 4) | o->rom_size; +} + +int +rom_autosize_grow(BYTE **rom, int test_size, int *size, struct gb_opt_s *o) +{ + int last_size = *size; + + while ((test_size > *size) && (o->nb_rom_banks <= 512)) + { + o->nb_rom_banks *= 2; + // banks work differently for mbc6, they have half the size + // but this in general ignored by -yo + *size = o->nb_rom_banks * 0x4000; + } + + if (o->nb_rom_banks > 512) + { + fprintf (stderr, "error: auto-size banks exceeded max of 512 banks.\n"); + return 0; + } + else + { + BYTE * t_rom = *rom; + *rom = realloc (*rom, *size); + if (*rom == NULL) + { + free(t_rom); + fprintf (stderr, "error: couldn't re-allocate size for larger rom image.\n"); + return 0; + } + memset (*rom + last_size, FILL_BYTE, *size - last_size); + } + + return 1; +} + +int +noi2sym (char *filename) +{ + FILE *noi, *sym; + char *nname, *sname; + //ssize_t read; + char read = ' '; + // no$gmb's implementation is limited to 32 character labels + // we can safely throw away the rest + char label[33]; + // 0x + 6 digit hex number + // -> 65536 rom banks is the maximum homebrew cartrideges support (TPP1) + char value[9]; + int name_len = strlen(filename); + int i = 0; + // copy filename's value to nname and sname + nname = malloc((name_len+1) * sizeof(char)); + strcpy (nname, filename); + sname = malloc((name_len+1) * sizeof(char)); + strcpy (sname, filename); + // change the extensions + nname[name_len-1]='i'; + nname[name_len-2]='o'; + nname[name_len-3]='n'; + sname[name_len-1]='m'; + sname[name_len-2]='y'; + sname[name_len-3]='s'; + + if (NULL == (noi = fopen (nname, "r"))) + { + fprintf (stderr, "error: can't open %s: ", nname); + perror(NULL); + return 1; + } + if (NULL == (sym = fopen (sname, "w"))) + { + fprintf (stderr, "error: can't create %s: ", sname); + perror(NULL); + return 1; + } + // write header + fprintf (sym, "; no$gmb compatible .sym file\n; Generated automagically by makebin\n"); + // iterate through .noi file + while (read != EOF && (read = fgetc(noi)) != EOF) + { + // just skip line breaks + if (read == '\r' || read == '\n') + continue; + // read first 4 chars + for (i = 0; i < 4; ++i) + { + value[i] = read; + if ((read = fgetc(noi)) == EOF || read == '\r' || read == '\n') + { + // leave for-loop + break; + } + } + // we left loop early + if (i != 4) + continue; + // only accept if line starts with this + if (strncmp(value, "DEF ", 4) == 0) + { + // read label + for (i = 0; i < 32; ++i) + { + label[i] = read; + if ((read = fgetc(noi)) == EOF || read == '\r' || read == '\n' || read == ' ') + { + // leave for-loop + break; + } + } + // skip rest of the label + while (read != EOF && read != '\r' && read != '\n' && read != ' ') + read = fgetc(noi); + // it has to be end of file or line if it's not space + if (read != ' ') + continue; + // strings have to end with \0 + label[i+1] = '\0'; + // read value + for (i = 0; i < 8; ++i) + { + value[i] = read; + if ((read = fgetc(noi)) == EOF || read == '\r' || read == '\n') + { + // leave for-loop + break; + } + } + // number is too long; ignore + if (read != EOF && read != '\r' && read != '\n') + continue; + value[i+1] = '\0'; + // we successfully read label and value + + // but filter out some invalid symbols + if (strcmp(label, ".__.ABS.") != 0 && strncmp(label, "l__", 3) != 0) + fprintf (sym, "%02X:%04X %s\n", (unsigned int)(strtoul(value, NULL, 0)>>16), (unsigned int)strtoul(value, NULL, 0)&0xFFFF, label); + } + else + // skip until file/line end + while ((read = fgetc(noi))!= EOF && read != '\r' && read != '\n'); + } + + // free close files + fclose (noi); + fclose (sym); + + fprintf (stderr, "Converted %s to %s.\n", nname, sname); + return 0; +} + +int +read_ihx (FILE *fin, BYTE **rom, int *size, int *real_size, struct gb_opt_s *o) +{ + int record_type; + + int extaddr = 0; + do + { + int nbytes; + int addr; + int checksum, sum = 0; + + if (getc (fin) != ':') + { + fprintf (stderr, "error: invalid IHX line.\n"); + return 0; + } + nbytes = getbyte (fin, &sum); + addr = getbyte (fin, &sum) << 8 | getbyte (fin, &sum); + record_type = getbyte (fin, &sum); + if(record_type == 4) + { + extaddr = getbyte (fin, &sum) << 8 | getbyte (fin, &sum); + extaddr <<= 16; // those are the upper 16 bits + checksum = getbyte (fin, &sum); + // move to the next record + if (0 != (sum & 0xff)) + { + fprintf (stderr, "error: bad checksum: %02x.\n", checksum); + return 0; + } + while (isspace (sum = getc (fin))) /* skip all kind of spaces */ + ; + ungetc (sum, fin); + if (getc (fin) != ':') + { + fprintf (stderr, "error: invalid IHX line.\n"); + return 0; + } + // parse real data part + checksum = sum = 0; + nbytes = getbyte (fin, &sum); + // lower 16 bits + addr = getbyte (fin, &sum) << 8 | getbyte (fin, &sum); + record_type = getbyte (fin, &sum); + } + // add linear address extension + addr |= extaddr; + // TODO: warn for unreachable banks according to chosen MBC + if (record_type > 1) + { + fprintf (stderr, "error: unsupported record type: %02x.\n", record_type); + return 0; + } + + if (addr + nbytes > *size) + { + // If auto-size is enabled, grow rom bank size by power of 2 when needed + if (o->rom_banks_autosize) + { + if (rom_autosize_grow(rom, addr + nbytes, size, o) == 0) + return 0; + } + else + { + fprintf (stderr, "error: size of the buffer is too small.\n"); + return 0; + } + } + + while (nbytes--) + { + if (addr < *size) + (*rom)[addr++] = getbyte (fin, &sum); + } + + if (addr > *real_size) + *real_size = addr; + + checksum = getbyte (fin, &sum); + if (0 != (sum & 0xff)) + { + fprintf (stderr, "error: bad checksum: %02x.\n", checksum); + return 0; + } + + while (isspace (sum = getc (fin))) /* skip all kind of spaces */ + ; + ungetc (sum, fin); + } + while (1 != record_type); /* EOF record */ + + return 1; +} + +int +main (int argc, char **argv) +{ + int size = 32768, skipsize = 0, pack = 0, real_size = 0, i = 0; + char *token; + BYTE *rom; + FILE *fin, *fout; + char *filename = NULL; + int ret; + int gb = 0; + int sms = 0; + + struct gb_opt_s gb_opt = {.cart_name="", + .licensee_str={'0', '0'}, + .mbc_type=0, + .nb_rom_banks=2, + .nb_ram_banks=0, + .licensee_id=0x33, + .is_gbc=0, + .is_sgb=0, + .sym_conversion=0, + .non_jp=0, + .rom_banks_autosize=0, + .do_logo_copy=true, + .address_overwrite={0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0} }; + + struct sms_opt_s sms_opt = {.rom_size=0xa, + .region_code=7, + .version=0 }; + +#if defined(_WIN32) + setmode (fileno (stdout), O_BINARY); +#endif + + while (*++argv && '-' == argv[0][0] && '\0' != argv[0][1]) + { + switch (argv[0][1]) + { + case 's': + if (!*++argv) + { + usage (); + return 1; + } + size = strtoul (*argv, NULL, 0); + break; + + case 't': + if (!*++argv) + { + usage (); + return 1; + } + skipsize = strtoul (*argv, NULL, 0); + break; + + case 'h': + usage (); + return 0; + + case 'p': + pack = 1; + break; + + case 'Z': + /* generate GameBoy binary file */ + gb = 1; + break; + + case 'y': + /* GameBoy options: + * -yo Number of rom banks (default: 2) + * -ya Number of ram banks (default: 0) + * -yt MBC type (default: no MBC) + * -yn Name of program (default: name of output file) + */ + switch (argv[0][2]) + { + case 'o': + if (!*++argv) + { + usage (); + return 1; + } + // Use auto-size for rom banks if -yto size param is 'A' + if ((*argv)[0] == 'A' || (*argv)[0] == 'a') + gb_opt.rom_banks_autosize = 1; + else + { + gb_opt.nb_rom_banks = strtoul (*argv, NULL, 0); + size = gb_opt.nb_rom_banks * 0x4000; + } + break; + + case 'a': + if (!++argv) + { + usage (); + return 1; + } + gb_opt.nb_ram_banks = strtoul (*argv, NULL, 0); + break; + + case 't': + if (!*++argv) + { + usage (); + return 1; + } + gb_opt.mbc_type = strtoul (*argv, NULL, 0); + break; + + case 'n': + if (!*++argv) + { + usage (); + return 1; + } + strncpy (gb_opt.cart_name, *argv, CART_NAME_LEN-1); + gb_opt.cart_name[CART_NAME_LEN-1] = '\0'; + break; + + case 'k': + if (!*++argv) + { + usage (); + return 1; + } + strncpy (gb_opt.licensee_str, *argv, 2); + break; + + case 'l': + if (!*++argv) + { + usage (); + return 1; + } + gb_opt.licensee_id = strtoul (*argv, NULL, 0); + break; + + case 'c': + gb_opt.is_gbc = 1; + break; + + case 'C': + gb_opt.is_gbc = 2; + break; + + case 'N': + gb_opt.do_logo_copy = false; // when switch is present, turn off logo copy + break; + + case 's': + gb_opt.is_sgb = 1; + break; + + case 'S': + gb_opt.sym_conversion = 1; + break; + + case 'j': + gb_opt.non_jp = 1; + break; + + // like -yp0x143=0x80 + case 'p': + // remove "-yp" + *argv += 3; + // effectively split string into argv and token + strtok(*argv, "="); + token = strtok(NULL, "="); + for (i = 0; i < 16; i+=2) + { + if(gb_opt.address_overwrite[i] == 0xFF) + { + gb_opt.address_overwrite[i] = strtoul (*argv, NULL, 0); + gb_opt.address_overwrite[i+1] = strtoul (token, NULL, 0); + break; + } + } + break; + + default: + usage (); + return 1; + } + break; + + case 'S': + /* generate SMS binary file */ + sms = 1; + break; + + case 'x': + + switch (argv[0][2]) + { + case 'o': + if (!*++argv) + { + usage (); + return 1; + } + sms_opt.rom_size = strtoul (*argv, NULL, 0); + if ( sms_opt.rom_size > 2 && (sms_opt.rom_size < 0xa || sms_opt.rom_size > 0xf ) ) + { + fprintf (stderr, "error: invalid rom size (0x%X)", sms_opt.rom_size); + perror(NULL); + return 1; + } + if ( sms_opt.rom_size == 0xd || sms_opt.rom_size == 0x2 ) + { + fprintf (stderr, "warning: this rom size (0x%X) is bugged in some BIOSes\n", sms_opt.rom_size); + } + break; + + case 'j': + if (!*++argv) + { + usage (); + return 1; + } + sms_opt.region_code = strtoul (*argv, NULL, 0); + if ( sms_opt.region_code < 3 && sms_opt.region_code > 7 ) + { + fprintf (stderr, "error: invalid region code (0x%X)", sms_opt.region_code); + perror(NULL); + return 1; + } + break; + + case 'v': + if (!*++argv) + { + usage (); + return 1; + } + sms_opt.version = strtoul (*argv, NULL, 0); + if ( sms_opt.version > 0xf ) + { + fprintf (stderr, "error: invalid version (0x%X)", sms_opt.version); + perror(NULL); + return 1; + } + break; + + default: + usage (); + return 1; + } + break; + + default: + usage (); + return 1; + } + } + + fin = stdin; + fout = stdout; + if (*argv) + { + if ('-' != argv[0][0] || '\0' != argv[0][1]) + { + if (NULL == (fin = fopen (*argv, "r"))) + { + fprintf (stderr, "error: can't open %s: ", *argv); + perror(NULL); + return 1; + } + filename = *argv; + } + ++argv; + } + + if (NULL != argv[0] && NULL != argv[1]) + { + usage (); + return 1; + } + + rom = malloc (size); + if (rom == NULL) + { + fclose (fin); + fprintf (stderr, "error: couldn't allocate room for the image.\n"); + return 1; + } + memset (rom, FILL_BYTE, size); + + if (gb_opt.sym_conversion == 1) + { + if (filename) + noi2sym(filename); + else + { + fprintf (stderr, "error: .noi to .sym conversion needs an input file.\n"); + } + } + + ret = read_ihx (fin, &rom, &size, &real_size, &gb_opt); + + fclose (fin); + + if (ret) + { + if (gb) + gb_postproc (rom, size, &real_size, &gb_opt); + else if (sms) + sms_postproc (rom, size, &real_size, &sms_opt); + + if (*argv) + { + if ('-' != argv[0][0] || '\0' != argv[0][1]) + { + if (NULL == (fout = fopen (*argv, "wb"))) + { + fprintf (stderr, "error: can't create %s: ", *argv); + perror(NULL); + return 1; + } + } + } + + int writesize = (pack ? real_size : size) - skipsize; + if (writesize > 0) fwrite (rom + skipsize, 1, writesize, fout); + + fclose (fout); + + return 0; + } + else + return 1; +} + diff --git a/gbdk-support/makecom/Makefile b/gbdk-support/makecom/Makefile @@ -0,0 +1,32 @@ +# makecom makefile + +ifndef TARGETDIR +TARGETDIR = /opt/gbdk +endif + +ifeq ($(OS),Windows_NT) + BUILD_OS := Windows_NT +else + BUILD_OS := $(shell uname -s) +endif + +# Target older macOS version than whatever build OS is for better compatibility +ifeq ($(BUILD_OS),Darwin) + export MACOSX_DEPLOYMENT_TARGET=10.10 +endif + +CC = $(TOOLSPREFIX)gcc +CFLAGS = -ggdb -O -Wno-incompatible-pointer-types -DGBDKLIBDIR=\"$(TARGETDIR)\" +OBJ = makecom.o noi_file.o files.o bin_to_com.o list.o path_ops.o +BIN = makecom + +all: $(BIN) + +$(BIN): $(OBJ) + +clean: + rm -f *.o $(BIN) *~ + rm -f tmp.* + rm -f *.exe + + diff --git a/gbdk-support/makecom/bin_to_com.c b/gbdk-support/makecom/bin_to_com.c @@ -0,0 +1,164 @@ +// This is free and unencumbered software released into the public domain. +// For more information, please refer to <https://unlicense.org> +// bbbbbr 2020 + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdbool.h> +#include <stdint.h> + +#include "common.h" +#include "list.h" +#include "files.h" +#include "noi_file.h" +#include "bin_to_com.h" + + +uint8_t * banks[BANKS_MAX_COUNT] = {NULL}; +uint16_t banks_len[BANKS_MAX_COUNT] = {0}; +uint16_t banks_count = 0; + + +void banks_cleanup(void) { + for (int c = 0; c <= BANKS_MAX_ID; c++) { + if (banks[c] != NULL) { + + free(banks[c]); + banks[c] = NULL; + } + } +} + + +// Allocate memory for banks as banks are requested +// If preceding banks are not yet allocated then do those too +uint8_t * bank_get_ptr(uint16_t bank_num) { + + if (bank_num > BANKS_MAX_ID) { + printf("makecom: ERROR: Requested bank %d is larger than max bank num %d!\n", bank_num, BANKS_MAX_ID); + exit(EXIT_FAILURE); + } + + if (banks[bank_num] == NULL) { + + // Make sure all banks leading up to requested are also allocated + for (int c = 0; c <= bank_num; c++) { + if (banks[c] == NULL) { + + banks[c] = malloc(BANK_SIZE); + + if (!banks[c]) { + printf("makecom: ERROR: Failed to allocate memory for bank %d!\n", c); + exit(EXIT_FAILURE); + } + + // zero out buffer (though unused space will not get written out) + memset(banks[c], 0x00u, BANK_SIZE); + } + } + } + + return banks[bank_num]; +} + + + +// Copy data from source bin file at offset address into requested bank buffer +void copy_to_bank(uint16_t bank_num, uint32_t rom_src_addr, uint32_t bank_out_addr, uint32_t length) { + + // TODO: make sure bin src addr doesn't exceed source file buffer length + if ((rom_src_addr + length) > rom_buf_in_len) { + printf("makecom: ERROR: Can't copy %d bytes from %x for bank %d, would overflow past end of ROM source buffer of size %d!\n", length, rom_src_addr, bank_num, (uint32_t)rom_buf_in_len); + exit(EXIT_FAILURE); + } + + if ((bank_out_addr + length) > BANK_SIZE) { + printf("makecom: ERROR: Can't copy %d bytes to %04x for bank %d, would overflow past end of bank buffer of size %d!\n",length, bank_out_addr, bank_num, BANK_SIZE); + exit(EXIT_FAILURE); + } + + if (bank_num > banks_count) + banks_count = bank_num; + + // Update length of bank data buffers (to allow for truncating output later) + if ((bank_out_addr + length) > banks_len[bank_num]) + banks_len[bank_num] = (bank_out_addr + length); + + // printf("* copying... bank:%d from:%x to:%x len:%d\n", + // bank_num, rom_src_addr, bank_out_addr, length); + + memcpy(bank_get_ptr(bank_num) + bank_out_addr, p_rom_buf_in + rom_src_addr, length); +} + + +void banks_write_out(void) { + + // If any of below fails it will trigger exit() and cleanup() will be called + + // Write first bank out, handled differently than rest + if ((banks[0] != NULL) && (banks_len[0] > 0)) { + // printf("Bank %d: Writing %d bytes to %s\n",0, banks_len[0], filename_out_com); + file_write_from_buffer(filename_out_com, banks[0], banks_len[0]); + } else { + printf("makecom: Error: Bank 0 was empty, no data written\n"); + exit; + } + + // Write out remaining banks if applicable + char bank_fname[MAX_STR_LEN] = ""; + for (int c = 1; c <= BANKS_MAX_ID; c++) { + if ((banks[c] != NULL) && (banks_len[0] > 0)) { + // Format to 8.3 filename with bank num as zero padded extension + sprintf(bank_fname, "%s.%03d", filename_banks_base, c); + // printf("Bank %d: Writing %d bytes to %s\n",c, banks_len[c], bank_fname); + file_write_from_buffer(bank_fname, banks[c], banks_len[c]); + } else + break; // Exit loop on first unpopulated bank + } + +} + + + +int bin2com(void) { + + symbol_item * symbols = (symbol_item *)symbol_list.p_array; + + for(int c = 0; c < symbol_list.count; c++) { + + // Only process completed symbols (start and length both set, lenth not zero) + if ((symbols[c].addr_start != SYM_VAL_UNSET) && + (symbols[c].length != SYM_VAL_UNSET) && + (symbols[c].length > 0)) { + + // If symbol is for a bank > 0 + if ((strncmp(symbols[c].name, "_CODE_", (sizeof("_CODE_") - 1)) == 0) && (symbols[c].bank_num > 0)) { + // Copy data from bank addr in source ROM to start of separate bank buffer + copy_to_bank(symbols[c].bank_num, symbols[c].src_rom_addr, BANK_START_ADDR, symbols[c].length); + } else { + // For remaining symbols (assume bank 0), relocate them by -100 when possible (addr -> addr - 100) + copy_to_bank(BANK_0, symbols[c].addr_start, BANK_0_RELOC_ADDR(symbols[c].addr_start), symbols[c].length); + } + } + } + + // Patch in updated bank / overlay count and bank filename + if ((banks_count > 0) && (overlay_count_addr != SYM_VAL_UNSET) && (overlay_name_addr != SYM_VAL_UNSET)) { + + if (overlay_count_addr > BANK_0_ADDR_OFFSET) + *(banks[0] + (overlay_count_addr - BANK_0_ADDR_OFFSET)) = banks_count; + + if (overlay_name_addr > BANK_0_ADDR_OFFSET) + memcpy(banks[0] + (overlay_name_addr - BANK_0_ADDR_OFFSET), filename_overlay, COM_OVERLAY_NAME_LEN-1); + } + + // No write the data out + banks_write_out(); + + return EXIT_SUCCESS; +} + + + diff --git a/gbdk-support/makecom/bin_to_com.h b/gbdk-support/makecom/bin_to_com.h @@ -0,0 +1,31 @@ +// This is free and unencumbered software released into the public domain. +// For more information, please refer to <https://unlicense.org> +// bbbbbr 2020 + +#ifndef _BIN_TO_COM_H +#define _BIN_TO_COM_H + +#define MAX(a, b) ((a > b) ? a : b) +#define BANKS_MAX_COUNT 256 +#define BANKS_MAX_ID (BANKS_MAX_COUNT - 1) +#define BANK_SIZE (0x4000u) +#define BANK_START_ADDR (0x0000u) +#define BANK_0 0 +#define BANK_0_ADDR_OFFSET 0x100 + +#define BANK_GET_NUM(addr) ((addr & 0xFFFF0000U) >> 16) +#define WITHOUT_BANK(addr) (addr & 0x0000FFFFU) +// Converts a symbol bank address such as 0x34000 to a bin/rom address such as 0x12000 (i.e: ~0x4000 * 3) +// Make sure base bank addess doesn't go negative +#define BANK_GET_ROM_ADDR(addr) ( (MAX(WITHOUT_BANK(addr), BANK_SIZE) - BANK_SIZE) + (BANK_SIZE * BANK_GET_NUM(addr)) ) + +// Apply bank 0 address offset and make sure it doesn't go negative +#define BANK_0_RELOC_ADDR(addr) (MAX((addr), BANK_0_ADDR_OFFSET) - BANK_0_ADDR_OFFSET) + +void copy_data(uint16_t bank_num, uint32_t rom_src_addr, uint32_t bank_out_addr, uint32_t length); +uint8_t * bank_get_ptr(uint16_t bank_num); +void copy_to_bank(uint16_t bank_num, uint32_t rom_src_addr, uint32_t bank_out_addr, uint32_t length); +void banks_cleanup(void); +int bin2com(void); + +#endif // _BIN_TO_COM_H + diff --git a/gbdk-support/makecom/common.h b/gbdk-support/makecom/common.h @@ -0,0 +1,25 @@ +#ifndef _COMMON_H +#define _COMMON_H + +#include <stdint.h> + +#define ARRAY_LEN(A) (sizeof(A) / sizeof(A[0])) + +#define MAX_STR_LEN 4096 +#define MAX_FILE_STR (MAX_STR_LEN) +#define SYM_MAX_STR_LEN 1024 + +#define BANK_FNAME_LEN (8+1+3 +1) // 8.3 filename style + terminator, ex: MYCOMFIL.001 (from "mycomfile.com") +#define COM_OVERLAY_NAME_LEN (8 + 1) // 8 chars for overlay string + terminator "MYCOMFIL" + + +extern char filename_in_bin[]; +extern char filename_in_noi[]; +extern char filename_out_com[]; +extern char filename_banks_base[]; +extern char filename_overlay[]; + +extern uint8_t * p_rom_buf_in; +extern size_t rom_buf_in_len; + +#endif // _COMMON_H + diff --git a/gbdk-support/makecom/files.c b/gbdk-support/makecom/files.c @@ -0,0 +1,62 @@ +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> + + + +// Read from a file into a buffer (will allocate needed memory) +// Returns NULL if reading file didn't succeed +uint8_t * file_read_into_buffer(char * filename, uint32_t *ret_size) { + + long fsize; + FILE * file_in = fopen(filename, "rb"); + uint8_t * filedata = NULL; + + if (file_in) { + // Get file size + fseek(file_in, 0, SEEK_END); + fsize = ftell(file_in); + if (fsize != -1L) { + fseek(file_in, 0, SEEK_SET); + + filedata = malloc(fsize); + if (filedata) { + if (fsize != fread(filedata, 1, fsize, file_in)) { + printf("makecom: Warning: File read size didn't match expected for %s\n", filename); + filedata = NULL; + } + // Read was successful, set return size + *ret_size = fsize; + } else printf("makecom: ERROR: Failed to allocate memory to read file %s\n", filename); + + } else printf("makecom: ERROR: Failed to read size of file %s\n", filename); + + fclose(file_in); + } else printf("makecom: ERROR: Failed to open input file %s\n", filename); + + return filedata; +} + + + +// Writes a buffer to a file +bool file_write_from_buffer(char * filename, uint8_t * p_buf, uint32_t data_len) { + + bool status = false; + size_t wrote_bytes; + FILE * file_out = fopen(filename, "wb"); + if (file_out) { + if (data_len == fwrite(p_buf, 1, data_len, file_out)) + status = true; + else + printf("makecom: Warning: File write size didn't match expected for %s\n", filename); + + fclose(file_out); + } else { + printf("makecom: ERROR: Failed to open output file %s!\n",filename); + exit(EXIT_FAILURE); + } + + return status; +} diff --git a/gbdk-support/makecom/files.h b/gbdk-support/makecom/files.h @@ -0,0 +1,7 @@ +#ifndef _FILES_H +#define _FILES_H + +uint8_t * file_read_into_buffer(char * filename, uint32_t *ret_size); +bool file_write_from_buffer(char * filename, uint8_t * p_buf, uint32_t data_len); + +#endif // _FILES_H diff --git a/gbdk-support/makecom/list.c b/gbdk-support/makecom/list.c @@ -0,0 +1,70 @@ +// This is free and unencumbered software released into the public domain. +// For more information, please refer to <https://unlicense.org> +// bbbbbr 2020 + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +#include "common.h" +#include "list.h" + +#define LIST_GROW_SIZE 100 // 50 // grow array by N entries at a time + +// Initialize the list and it's array +// typesize *must* match the type that will be used with the array +void list_init(list_type * p_list, size_t array_typesize) { + p_list->typesize = array_typesize; + p_list->count = 0; + p_list->size = LIST_GROW_SIZE; + p_list->p_array = (void *)malloc(p_list->size * p_list->typesize); + + if (!p_list->p_array) { + printf("makecom: ERROR: Failed to allocate memory for list!\n"); + exit(EXIT_FAILURE); + } +} + + +// Free the array memory allocated for the list +void list_cleanup(list_type * p_list) { + if (p_list->p_array) { + free (p_list->p_array); + p_list->p_array = NULL; + } +} + + +// Add a new item to the lists array, resize if needed +// p_newitem *must* be the same type the list was initialized with +void list_additem(list_type * p_list, void * p_newitem) { + + void * tmp_list; + + p_list->count++; + + // Grow array if needed + if (p_list->count == p_list->size) { + // Save a copy in case reallocation fails + tmp_list = p_list->p_array; + + p_list->size += p_list->typesize * LIST_GROW_SIZE; + p_list->p_array = (void *)realloc(p_list->p_array, p_list->size * p_list->typesize); + // If realloc failed, free original buffer before quitting + if (!p_list->p_array) { + printf("makecom: ERROR: Failed to reallocate memory for list!\n"); + if (tmp_list) { + free(tmp_list); + tmp_list = NULL; + } + exit(EXIT_FAILURE); + } + } + + // Copy new entry + memcpy((uint_least8_t *)p_list->p_array + ((p_list->count - 1) * p_list->typesize), + p_newitem, + p_list->typesize); +} + diff --git a/gbdk-support/makecom/list.h b/gbdk-support/makecom/list.h @@ -0,0 +1,20 @@ +// This is free and unencumbered software released into the public domain. +// For more information, please refer to <https://unlicense.org> +// bbbbbr 2020 + +#ifndef _LIST_H +#define _LIST_H + + +typedef struct list_type { + void * p_array; + uint32_t size; + uint32_t count; + size_t typesize; +} list_type; + +void list_init(list_type *, size_t); +void list_cleanup(list_type *); +void list_additem(list_type *, void *); + +#endif // _LIST_H + diff --git a/gbdk-support/makecom/makecom.c b/gbdk-support/makecom/makecom.c @@ -0,0 +1,147 @@ +// This is free and unencumbered software released into the public domain. +// For more information, please refer to <https://unlicense.org> +// bbbbbr 2022 + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdbool.h> +#include <stdint.h> +#include <ctype.h> + +#include "path_ops.h" +#include "common.h" +#include "files.h" +#include "noi_file.h" +#include "bin_to_com.h" + +#define EXT_NOI "noi" + +char filename_in_bin[MAX_STR_LEN] = ""; +char filename_in_noi[MAX_STR_LEN] = ""; +char filename_out_com[MAX_STR_LEN] = ""; +char filename_banks_base[MAX_STR_LEN] = ""; +char filename_overlay[COM_OVERLAY_NAME_LEN] = ""; + +uint8_t * p_rom_buf_in = NULL; +size_t rom_buf_in_len = 0; + +static void str_to_upper(char * str); +static void filenames_out_prepare(void); +static void display_help(void); +static int handle_args(int argc, char * argv[]); +void cleanup(void); + + +static void str_to_upper(char * str) { + while (*str) { + *str = toupper(*str); + str++; + } +} + + + +// Generate names for the exported bank/overlay files +// based on the output COM filename and path +static void filenames_out_prepare(void) { + + char str_banks_name[MAX_STR_LEN]; + char str_path_only[MAX_STR_LEN]; + + snprintf(str_banks_name, sizeof(str_banks_name), "%s", filename_out_com); + + if (!get_path_without_filename(filename_out_com, str_path_only, sizeof(str_path_only))) { + printf("makecom: Error: source path %s exceeds max length\n", filename_out_com); + exit; + } + + // Convert filename to uppercase and remove extension + str_to_upper(str_banks_name); + filename_remove_extension(str_banks_name); + + // Then truncate it to 8 characters with no padding for bank filenames + int ret = snprintf(filename_banks_base, sizeof(filename_banks_base), "%s%.8s", str_path_only, get_filename_from_path(str_banks_name)); + if (ret < 0) printf("makecom: Warning, banks output path truncated\n"); + // Then with trailing space char padding for the overlay filename patch + snprintf(filename_overlay, sizeof(filename_overlay), "%-8.8s", get_filename_from_path(str_banks_name)); +} + + +static void display_help(void) { + + fprintf(stdout, + "makecom image.rom image.noi output.com\n" + "Use: convert a binary .rom file to .msxdos com format.\n" + ); +} + + +int handle_args(int argc, char * argv[]) { + + if (argc == 3) { + // Copy input and output filenames from arguments + snprintf(filename_in_bin, sizeof(filename_in_bin), "%s", argv[1]); + snprintf(filename_out_com, sizeof(filename_out_com), "%s", argv[2]); + // Generate .noi file name from input .bin file name + snprintf(filename_in_noi, sizeof(filename_in_noi), "%s", argv[1]); + filename_replace_extension(filename_in_noi, EXT_NOI, MAX_STR_LEN); + } else if (argc == 4) { + // Copy input and output filenames from arguments + snprintf(filename_in_bin, sizeof(filename_in_bin), "%s", argv[1]); + snprintf(filename_in_noi, sizeof(filename_in_noi), "%s", argv[2]); + snprintf(filename_out_com, sizeof(filename_out_com), "%s", argv[3]); + } else { + display_help(); + return false; + } + + filenames_out_prepare(); + + + // printf("bin: %s\n",filename_in_bin); + // printf("noi: %s\n",filename_in_noi); + // printf("com: %s\n",filename_out_com); + + // printf("banks base: _%s_\n",filename_banks_base); + // printf("overlay : _%s_\n",filename_overlay); + + + return true; +} + + +// Registered as atexit() handler +// Free resources during normal shutdown or from a call to exit() +void cleanup(void) { + + banks_cleanup(); + noi_cleanup(); + + if (p_rom_buf_in != NULL) { + free(p_rom_buf_in); + p_rom_buf_in = NULL; + } +} + + +int main( int argc, char *argv[] ) { + + // Exit with failure by default + int ret = EXIT_FAILURE; + + // Register cleanup with exit handler + atexit(cleanup); + + if (handle_args(argc, argv)) { + + p_rom_buf_in = file_read_into_buffer(filename_in_bin, &rom_buf_in_len); + + if (p_rom_buf_in) + if (noi_file_load_symbols(filename_in_noi)) + ret = bin2com(); + } + cleanup(); + + return ret; // Exit with failure by default +} diff --git a/gbdk-support/makecom/noi_file.c b/gbdk-support/makecom/noi_file.c @@ -0,0 +1,164 @@ +// This is free and unencumbered software released into the public domain. +// For more information, please refer to <https://unlicense.org> +// bbbbbr 2020 + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdbool.h> +#include <stdint.h> + +#include "common.h" +#include "noi_file.h" +#include "bin_to_com.h" + +// Example data to parse from a .noi file: +/* +DEF s__CODE 0x200 +DEF l__BASE 0x2A3 +DEF __shadow_OAM_OFF 0x455 +DEF .mode 0x456 +*/ + +const char * known_section_names[] = + {"_CODE","_HOME","_BASE","_CODE_0","_INITIALIZER","_LIT","_GSINIT","_GSFINAL"}; + + +list_type symbol_list; +uint32_t overlay_count_addr = SYM_VAL_UNSET; +uint32_t overlay_name_addr = SYM_VAL_UNSET; + + +// Initialize the symbol list +void noi_init(void) { + + list_init(&symbol_list, sizeof(symbol_item)); +} + + +// Free the symbol list +void noi_cleanup(void) { + + list_cleanup(&symbol_list); +} + + +// Find a matching symbol, if none matches a new one is added and returned +static int symbollist_get_id_by_name(char * symbol_name) { + + symbol_item * symbols = (symbol_item *)symbol_list.p_array; + + // Check for matching symbol name + for(int c = 0;c < symbol_list.count; c++) { + // Return matching symbol index if present + if (strcmp(symbol_name, symbols[c].name) == 0) { + return c; + } + } + + // no match was found, add symbol + symbol_item new_symbol = {.name = "", .addr_start = SYM_VAL_UNSET, .length = SYM_VAL_UNSET, .bank_num = 0x00, .src_rom_addr = SYM_VAL_UNSET}; + snprintf(new_symbol.name, sizeof(new_symbol.name), "%s", symbol_name); + list_additem(&symbol_list, &new_symbol); + + return (symbol_list.count - 1); +} + + +// Add the start address or length for a s_ or l_ symbol record +static void noi_symbollist_add(char rec_type, char * name, char * value) { + + // Only add symbols if they're in the known section name list + for (int c = 0; c < ARRAY_LEN(known_section_names); c++) { + + if ((strcmp(name, known_section_names[c]) == 0) || + (strncmp(name, "_CODE_", (sizeof("_CODE_") - 1)) == 0)) { + + symbol_item * symbols = (symbol_item *)symbol_list.p_array; + + // Check to see if there is an existing record to update + // If one isn't found a new record will have been created automatically + int symbol_id = symbollist_get_id_by_name(name); + if (symbol_id != ERR_NO_SYMBOLS_LEFT) { + + // Handle whether it's a start-of-address or a length record for the given symbol + if (rec_type == NOI_REC_START) { + symbols[symbol_id].addr_start = strtol(value, NULL, 16); + symbols[symbol_id].bank_num = BANK_GET_NUM(symbols[symbol_id].addr_start); + symbols[symbol_id].src_rom_addr = BANK_GET_ROM_ADDR(symbols[symbol_id].addr_start); + } + else if (rec_type == NOI_REC_LENGTH) { + symbols[symbol_id].length = strtol(value, NULL, 16); + } + } + + return; + } + } +} + + +// Load symbols from a .noi file and create paired records for start & length entries +// Plus, look for and store for overlay symbol information +int noi_file_load_symbols(char * filename_in) { + + char cols; + char * p_str; + char * p_words[MAX_SPLIT_WORDS]; + char strline_in[MAX_STR_LEN] = ""; + FILE * noi_file = fopen(filename_in, "r"); + symbol_item symbol; + int symbol_id; + + noi_init(); + + if (noi_file) { + + // Read one line at a time into \0 terminated string + while ( fgets(strline_in, sizeof(strline_in), noi_file) != NULL) { + + // Require minimum length to match + if (strlen(strline_in) >= NOI_REC_START_LEN) { + + // Split string into words separated by spaces + cols = 0; + p_str = strtok(strline_in," "); + while (p_str != NULL) + { + p_words[cols++] = p_str; + // Only split on underscore for the second match + p_str = strtok(NULL, " "); + if (cols >= MAX_SPLIT_WORDS) break; + } + + // [0] = "DEF" + // [1] = symbol name + // [2] = symbol value + if (cols >= NOI_REC_COUNT_MATCH) { + + // If it matches either _s_egment or _l_ength records (first two chars) + // then add a record for it with the first two chars truncated + if ((strncmp(p_words[1], "l_", NOI_REC_S_L_CHK) == 0) || + (strncmp(p_words[1], "s_", NOI_REC_S_L_CHK) == 0)) { + noi_symbollist_add(p_words[1][0], p_words[1] + NOI_REC_S_L_CHK, p_words[2]); + } + // Otherwise check for records of interest and store them + else if (strcmp(p_words[1], "___overlay_count") == 0) { + overlay_count_addr = strtol(p_words[2], NULL, 16); + } + else if (strcmp(p_words[1], "___overlay_name") == 0) { + overlay_name_addr = strtol(p_words[2], NULL, 16); + } + } + } // end: valid min chars to process line + + } // end: while still lines to process + + fclose(noi_file); + + } // end: if valid file + else return (false); + + return true; +} diff --git a/gbdk-support/makecom/noi_file.h b/gbdk-support/makecom/noi_file.h @@ -0,0 +1,40 @@ +// This is free and unencumbered software released into the public domain. +// For more information, please refer to <https://unlicense.org> +// bbbbbr 2020 + +#ifndef _NOI_FILE_H +#define _NOI_FILE_H + +#include "list.h" + +#define MAX_STR_LEN 4096 +#define MAX_SPLIT_WORDS 4 + +#define NOI_REC_COUNT_MATCH 3 // "DEF ...symbol_name... ...symbol_value... + +#define ERR_NO_SYMBOLS_LEFT -1 +#define SYM_VAL_UNSET 0xFFFFFFFF + +#define NOI_REC_START_LEN 4 // length of "DEF " +#define NOI_REC_S_L_CHK 2 // length of "s_" or "l_" +#define NOI_REC_START 's' +#define NOI_REC_LENGTH 'l' + +typedef struct symbol_item { + char name[SYM_MAX_STR_LEN]; + uint32_t addr_start; + uint32_t length; + uint16_t bank_num; + uint32_t src_rom_addr; +} symbol_item; + +extern list_type symbol_list; +extern uint32_t overlay_count_addr; +extern uint32_t overlay_name_addr; + + +int noi_file_load_symbols(char * filename_in); +void noi_init(void); +void noi_cleanup(void); + +#endif // _NOI_FILE_H + diff --git a/gbdk-support/makecom/pascal/Makefile b/gbdk-support/makecom/pascal/Makefile @@ -0,0 +1,31 @@ +# bankcheck (auto bank tool) makefile + +ifndef TARGETDIR +TARGETDIR = /opt/gbdk +endif + +ifeq ($(OS),Windows_NT) + BUILD_OS := Windows_NT +else + BUILD_OS := $(shell uname -s) +endif + +# Target older macOS version than whatever build OS is for better compatibility +ifeq ($(BUILD_OS),Darwin) + export MACOSX_DEPLOYMENT_TARGET=10.10 +endif + +CC = +CFLAGS = +OBJ = +BIN = makecom + +all: $(BIN) + +$(BIN): makecom.dpr + fpc -B $< + +clean: + rm -f *.o $(BIN) *~ *.dof *.cfg *.dsk + rm -f *.exe + diff --git a/gbdk-support/makecom/pascal/makecom.dpr b/gbdk-support/makecom/pascal/makecom.dpr @@ -0,0 +1,184 @@ +{$APPTYPE CONSOLE} +{$IFDEF FPC} + {$MODE DELPHI} +{$ENDIF} +uses classes, sysutils, math; + +procedure ERROR(const msg: ansistring; const params: array of const); +begin + writeln(format(msg, params)); + writeln('USAGE: makecom <image.bin> [<image.noi>] <output.com>'); + halt(1); +end; + +procedure DecodeCommaText(const Value: ansistring; result: tStringList; adelimiter: ansichar); +var P, P1 : PAnsiChar; + S : ansistring; +begin + if assigned(result) then begin + result.BeginUpdate; + try + result.Clear; + P := PChar(Value); + while P^ in [#1..#31] do inc(P); + while P^ <> #0 do + begin + if P^ = '"' then + S := AnsiExtractQuotedStr(P, '"') + else + begin + P1 := P; + while (P^ >= ' ') and (P^ <> adelimiter) do inc(P); + SetString(S, P1, P - P1); + end; + result.Add(S); + while P^ in [#1..#31] do inc(P); + if P^ = adelimiter then + repeat + inc(P); + until not (P^ in [#1..#31]); + end; + finally + result.EndUpdate; + end; + end; +end; + +function load_symbols(const filename: ansistring; symbols: tStringList): boolean; +var s : textfile; + str : ansistring; + row : tStringList; +begin + result:= assigned(symbols); + if result then begin + assignfile(s, filename); reset(s); + try + row := tStringList.create; + try + while not eof(s) do begin + readln(s, str); + DecodeCommaText(str, row, ' '); + if (row[0] = 'DEF') then begin + symbols.Values[row[1]] := row[2]; + end; + end; + result:= true; + finally freeandnil(row); end; + finally closefile(s); end; + end; +end; + +function CopyData(banks: tList; bank: longint; image: tMemoryStream; source_ofs, dest_ofs: longint; len: longint): boolean; +var i : longint; + data : tMemoryStream; +begin + result:= (bank < 255); + if result then begin + if (banks.count <= bank) then + for i:= banks.count to bank do banks.Add(tMemoryStream.Create()); + data:= banks[bank]; + data.Seek(dest_ofs, soFromBeginning); + data.Write(pAnsiChar(image.Memory)[source_ofs], len); + end; +end; +procedure WriteData(banks: tList; const destname: ansistring); +var i : longint; + ovr : ansistring; +begin + if (banks.count > 0) then begin + with tMemoryStream(banks[0]) do try + writeln(format('writing program: %s', [destname])); + SaveToFile(destname); + finally free; end; + for i:= 1 to banks.count - 1 do + with tMemoryStream(banks[i]) do try + ovr:= ChangeFileExt(destname, format('.%.3d', [i])); + writeln(format('writing overlay: %s', [ovr])); + SaveToFile(ovr); + finally free; end; + end; +end; + +function Hex2Int(value: ansistring): longint; +begin + if (copy(value, 1, 2)) = '0x' then begin value[1]:= ' '; value[2]:= '$'; end; + result:= StrToIntDef(value, 0); +end; + +const section_names = '_CODE,_HOME,_BASE,_CODE_0,_INITIALIZER,_LIT,_GSINIT,_GSFINAL'; +var name_bin : ansistring; + name_noi : ansistring; + name_out : ansistring; + symbols : tStringList; + known : tStringList; + banks : tList; + source : tMemoryStream; + i, bank : longint; + name, v, l : ansistring; + addr, len : longint; +begin + if (paramcount() = 2) then begin + name_bin:= paramstr(1); name_noi:= changefileext(name_bin, '.noi'); name_out:= paramstr(2); + end else begin + if (paramcount() < 3) then ERROR('ERROR: Not sufficient parameters', []); + name_bin:= paramstr(1); name_noi:= paramstr(2); name_out:= paramstr(3); + end; + + if not fileexists(name_noi) then ERROR('ERROR: symbol file: "%s" not found', [name_noi]); + if not fileexists(name_bin) then ERROR('ERROR: binary image: "%s" not found', [name_bin]); + + known:= tStringList.create; + symbols := tStringList.create; + try + DecodeCommaText(section_names, known, ','); + if load_symbols(name_noi, symbols) then begin + source:= tMemoryStream.Create; + try + source.LoadFromFile(name_bin); + banks:= tList.Create; + with banks do try + for i:= 0 to symbols.count - 1 do begin + name:= symbols.Names[i]; + v:= symbols.Values[name]; + if (copy(name, 1, 2) = 's_') then begin + name:= copy(name, 3, length(name)); + l:= symbols.Values[format('l_%s',[name])]; + if length(l) > 0 then begin + addr:= Hex2Int(v); + len:= Hex2Int(l); + + if (len > 0) then begin + if (known.IndexOf(name) >= 0) then begin + CopyData(banks, 0, source, addr, math.max(0, addr - $100), len); + end; + if (copy(Name, 1, 6) = '_CODE_') then begin + bank:= addr shr 16; + CopyData(banks, bank, source, math.max(0, (addr and $ffff) - $4000) + $4000 * bank, 0, len); + end; + end; + end; + end; + end; + + if (banks.Count > 0) then begin + addr:= Hex2Int(symbols.Values['___overlay_count']); + if (addr > $100) then pAnsiChar(tMemoryStream(banks[0]).Memory)[addr - $100]:= chr(banks.Count - 1); + addr:= Hex2Int(symbols.Values['___overlay_name']); + if (addr > $100) then begin + name:= format('%-8.8s', [uppercase(changefileext(extractfilename(name_out), ''))]); + system.move(name[1], pAnsiChar(tMemoryStream(banks[0]).Memory)[addr - $100], 8); + end; + end; + + writeln('writing...'); + WriteData(banks, name_out); + writeln('done!'); + finally banks.free; end; + finally freeandnil(source); end; + end; + finally + freeandnil(symbols); + freeandnil(known); + end; + +end. + diff --git a/gbdk-support/makecom/path_ops.c b/gbdk-support/makecom/path_ops.c @@ -0,0 +1,145 @@ +// This is free and unencumbered software released into the public domain. +// For more information, please refer to <https://unlicense.org> +// bbbbbr 2020 + +#include <stdio.h> +#include <string.h> +#include <stdbool.h> +#include "common.h" +#include "path_ops.h" + +const char kExtensionSeparator = '.'; +const char kPathSeparator = + +#ifdef _WIN32 + #ifndef _WIN32 + #define __WIN32__ + #endif +#endif + +#ifdef __WIN32__ + '\\'; +#else + '/'; +#endif + +const char kPathSeparator_unix = '/'; + + +void filename_replace_extension(char * filename, char * new_ext, size_t maxlen) { + + // Use a temp work string in case out and in filename are the same pointer + char temp[MAX_FILE_STR]; + char ext_sep[2] = {'\0'}; // default to empty string + + // Add leading . to path if needed + if (new_ext[0] != kExtensionSeparator) { + ext_sep[0] = kExtensionSeparator; + ext_sep[1] = '\0'; + } + + // Strip extension from filename, append new extension + filename_remove_extension(filename); + snprintf(temp, maxlen, "%s%s%s", filename, ext_sep, new_ext); + snprintf(filename, maxlen, "%s", temp); +} + + +void filename_replace_path(char * filename, char * new_path, size_t maxlen) { + + // Use a temp work string in case out and in filename are the same pointer + char temp[MAX_FILE_STR]; + char path_sep[2] = {'\0'}; // default to empty string + + // Add trailing slash to path if needed (Windows needs both for when running under linix like env) +#ifdef __WIN32__ + if (((new_path[(strlen(new_path)-1)] != kPathSeparator)) && + ((new_path[(strlen(new_path)-1)] != kPathSeparator_unix))) +#else + if ((new_path[(strlen(new_path)-1)] != kPathSeparator)) +#endif + { + path_sep[0] = kPathSeparator; + path_sep[1] = '\0'; + } + + // Strip path from path+filename, pre-pend new path + snprintf(temp, maxlen, "%s%s%s", new_path, path_sep, get_filename_from_path(filename)); + snprintf(filename, maxlen, "%s", temp); +} + + +const char * get_filename_from_path(const char * path) +{ + size_t i; + + // Returns string starting at last occurrance of path separator char + for(i = strlen(path) - 1; i; i--) { + + // Add trailing slash to path if needed (Windows needs both for when running under linix like env) + #ifdef __WIN32__ + if ((path[i] == kPathSeparator) || (path[i] == kPathSeparator_unix)) + #else + if (path[i] == kPathSeparator) + #endif + { + return &path[i+1]; + } + } + return path; +} + + +void filename_remove_extension(char * path) +{ + char * last_ext; + char * last_slash; + + // Find the last path separator if present + // Starting from here ensures that no path ".." characters + // get mistaken as extension delimiters. + last_slash = strrchr (path, kExtensionSeparator); + if (!last_slash) + last_slash = path; + + // Then check to see if there is an extension (starting with the last occurance of '.') + // (tries to remove *all* trailing extensions backward until the last slash) + last_ext = strrchr (last_slash, kExtensionSeparator); + while (last_ext) { + if (last_ext != NULL) { + // If an extension is found then overwrite it with a string terminator + *last_ext = '\0'; + } + last_ext = strrchr (last_slash, kExtensionSeparator); + } +} + + +bool get_path_without_filename(const char * path, char * path_only, uint32_t str_max) +{ + size_t i; + + if (strlen(path) + 1 > str_max) + return false; + + for(i = strlen(path) - 1; i; i--) { + + // Add trailing slash to path if needed (Windows needs both for when running under linix like env) + #ifdef __WIN32__ + if ((path[i] == kPathSeparator) || (path[i] == kPathSeparator_unix)) + #else + if (path[i] == kPathSeparator) + #endif + { + memcpy(path_only, path, i+1 ); + path_only[i+1] = '\0'; + return true; + } + } + + // memcpy(path_only, path, strlen(path)); + // No separater found, so no path + path_only[0] = '\0'; + return true; +} + diff --git a/gbdk-support/makecom/path_ops.h b/gbdk-support/makecom/path_ops.h @@ -0,0 +1,15 @@ +// This is free and unencumbered software released into the public domain. +// For more information, please refer to <https://unlicense.org> +// bbbbbr 2020 + +#ifndef _PATH_OPS_H +#define _PATH_OPS_H + +void filename_replace_extension(char * filename, char * new_ext, size_t maxlen); +void filename_replace_path(char * filename, char * new_path, size_t maxlen); +const char * get_filename_from_path(const char * path); +void filename_remove_extension(char * path); +bool get_path_without_filename(const char * path, char * path_only, uint32_t str_max); + + +#endif // _PATH_OPS_H +

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.