From 5199df364ae8860ee4dbb8fba3b62fddb0ba801f Mon Sep 17 00:00:00 2001 From: Sebastian Riedel Date: Fri, 4 Feb 2022 02:07:03 +0100 Subject: [PATCH 01/10] Switch to makebin --- game/makefile | 3 +- ihx2gb/ihx2gb.js | 257 --------------------------------------- ihx2gb/package-lock.json | 26 ---- ihx2gb/package.json | 10 -- 4 files changed, 2 insertions(+), 294 deletions(-) delete mode 100644 ihx2gb/ihx2gb.js delete mode 100644 ihx2gb/package-lock.json delete mode 100644 ihx2gb/package.json diff --git a/game/makefile b/game/makefile index fe32ea9..27e66c0 100644 --- a/game/makefile +++ b/game/makefile @@ -1,5 +1,6 @@ CC = sdcc ASMC = sdasgb +MKROM = makebin CFLAGS = -c -mgbz80 ASMFLAGS = -plosgffjw LINKFLAGS = -mgbz80 --no-std-crt0 --data-loc 0xc0a0 @@ -36,7 +37,7 @@ obj/dependencies: $(SRCFILES) bin/BubbleFactory.gb: obj/game.ihx @mkdir -p bin - node ../ihx2gb --name BUBBLEFACT --licensee DH --cartridge 0x1B --ram 1 --version 1 $< $@ + $(MKROM) -Z -yn BUBBLEFACT -yk DH -yt 0x1B -ya 1 -yS -yp0x014C=1 $< $@ obj/game.ihx: $(OBJFILES) @mkdir -p obj diff --git a/ihx2gb/ihx2gb.js b/ihx2gb/ihx2gb.js deleted file mode 100644 index 7445dc9..0000000 --- a/ihx2gb/ihx2gb.js +++ /dev/null @@ -1,257 +0,0 @@ -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const commander = require("commander"); -const Overflow = require("overflow-js").Overflow; - -function parseHexPair(string, index) { - return parseInt(string.substr(index, 2), 16); -} - -function writeLogo(buffer) { - const logoBuffer = Buffer.from([ - 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 - ]); - - logoBuffer.copy(buffer, 0x0104); -} - -function writeTitle(buffer, title) { - if(title.length > 11) { - console.warn(`warning: title longer than 11 characters, later characters will be ignored`); - } - - if(/^[A-Z0-9 ]*$/g.test(title) == false) { - console.error("title may only have uppercase letters, digits, and spaces"); - process.exit(1); - } - - const startAddress = 0x0134; - buffer.fill(0x00, startAddress, startAddress + 11); - buffer.write(title, startAddress, Math.min(title.length, 11), "ascii"); -} - -function writeManufacturerCode(buffer, code) { - if(code.length > 4) { - console.warn(`warning: manufacturer code longer than 4 characters, later characters will be ignored`); - } - - if(/^[A-Z0-9 ]*$/g.test(code) == false) { - console.error("manufacturer code may only have uppercase letters, digits, and spaces"); - process.exit(1); - } - - const startAddress = 0x013F; - buffer.fill(0x00, startAddress, startAddress + 4); - buffer.write(code, startAddress, Math.min(code.length, 4), "ascii"); -} - -function writeCGBFlag(buffer, flag) { - buffer.writeUInt8(flag, 0x0143); -} - -function writeLicenseeCode(buffer, code) { - if(code.length > 2) { - console.warn(`warning: licensee code longer than 2 characters, later characters will be ignored`); - } - - if(/^[a-zA-Z0-9 ]*$/g.test(code) == false) { - console.error("licensee code may only have letters, digits, and spaces"); - process.exit(1); - } - - const startAddress = 0x0144; - buffer.fill(0xFF, startAddress, startAddress + 2); - buffer.write(code, startAddress, Math.min(code.length, 2), "ascii"); -} - -function writeSGBFlag(buffer, flag) { - buffer.writeUInt8(flag, 0x0146); -} - -function writeCartridgeType(buffer, type) { - buffer.writeUInt8(type, 0x0147); -} - -function writeROMSize(buffer, size) { - buffer.writeUInt8(size, 0x0148); -} - -function writeRAMSize(buffer, size) { - buffer.writeUInt8(size, 0x0149); -} - -function writeDestinationCode(buffer, code) { - buffer.writeUInt8(code, 0x014A); -} - -function writeOldLicenseeCode(buffer, code) { - buffer.writeUInt8(code, 0x014B); -} - -function writeROMVersion(buffer, version) { - buffer.writeUInt8(version, 0x014C); -} - -function writeHeaderChecksum(buffer) { - let x = Overflow.ubyte(0); - for(let index = 0x0134; index <= 0x014C; index++) { - x = x.minus(buffer[index]).minus(1); - } - buffer.writeUInt8(x.value, 0x014D); -} - -function writeROMChecksum(buffer) { - let x = Overflow.ushort(0); - for(let index = 0x00; index < buffer.length; index++) { - if(index == 0x014E || index == 0x014F) { - continue; - } - x = x.plus(buffer[index]); - } - buffer.writeUInt16BE(x.value, 0x014E); -} - -function processMap(mapFilePath, symOutputPath) { - if(fs.existsSync(mapFilePath) == false) { - return; - } - - const regex = /^[\s]+([0-9A-F]{8}) _([a-zA-Z0-9_]+)/; - - const text = fs.readFileSync(mapFilePath, "utf8"); - const lines = text.split(/[\n\r]+/g); - const addresses = []; - lines.forEach((line) => { - const match = regex.exec(line); - if(match == null) { - return; - } - const address = match[1]; - const symbol = match[2]; - - addresses.push({ - "address" : address, - "symbol" : symbol - }); - }); - - const symbolLines = [ - "; Generated by IHX2GB", - "" - ]; - addresses.forEach((address) => { - symbolLines.push(`00:${address.address.substr(4)} ${address.symbol}`); - }); - - fs.writeFileSync(symOutputFilePath, symbolLines.join("\n")); -} - -commander - .usage("[options] ") - .option("-n, --name [value]", "The name in the header. Default: \"MY GAME\"", "MY GAME") - .option("-m, --manufacturer [code]", "The manufacturer code. Default: \"\"", "") - .option("-c, --cgb [flag]", "The decimal Game Boy Color support flag. 128 for CGB+GB, 192 for CGB-only. Default: 0", "0", parseInt) - .option("-l, --licensee [code]", "The ASCII licensee code. Default: \"00\"", "00") - .option("-s, --sgb [flag]", "The decimal Super Game Boy support flag. 3 to enable. Default: 0", "0", parseInt) - .option("-t, --cartridge [type]", "The decimal cartridge type. Default: 0", "0", parseInt) - .option("-r, --rom [code]", "The decimal ROM size code. File will be 32KB << value, except for 82, 83, and 84. Default: 0", "0", parseInt) - .option("-a, --ram [code]", "The decimal external RAM size code. Default: 0", "0", parseInt) - .option("-d, --destination [code]", "The destination code. 0 for Japan, 1 for international. Default: 1", "1", parseInt) - .option("--oldlicensee [code]", "The decimal old licensee code. 51 to use new licensee code. SGB will not work if != 51. Default: 51", "51", parseInt) - .option("-v, --version [version]", "The ROM version. Default: 0", "0", parseInt) - -commander.on("--help", () => { - console.log("Go to http://gbdev.gg8.se/wiki/articles/The_Cartridge_Header for header details, including values for Cartridge Type, ROM Size, and RAM Size. That site lists codes in hex, but must be provided here in decimal."); - console.log(""); -}); - -commander.parse(process.argv); - -if(commander.rom > 8) { - let n = 32768; - for(let i=0; i < commander.rom; i++) { - n *= 2; - } - console.error("rom size codes > 8 unsupported. Fun fact, you asked me to make a file " + (n) + " bytes big! Wow!"); - process.exit(1); -} - -if(commander.args.length != 2) { - console.error("must provide both an input and output file"); - process.exit(1); -} - -const inputFilePath = path.resolve(commander.args[0]); -const outputFilePath = path.resolve(commander.args[1]); - -const inputFileDirectory = path.dirname(inputFilePath); -const inputFileBasename = path.basename(inputFilePath, path.extname(inputFilePath)); -const mapFilePath = path.join(inputFileDirectory, inputFileBasename + ".map"); - -const outputFileDirectory = path.dirname(outputFilePath); -const outputFileBasename = path.basename(outputFilePath, path.extname(outputFilePath)); -const symOutputFilePath = path.join(outputFileDirectory, outputFileBasename + ".sym"); - -processMap(mapFilePath, symOutputFilePath); - -const ihxText = fs.readFileSync(inputFilePath, { "encoding" : "utf8" }); -const ihxRecords = ihxText.split(/[\n\r]+/g).filter((record) => { return record.length > 0; }); -const gbBuffer = Buffer.alloc(32768 << commander.rom, 0xFF); - -let baseAddress = 0; - -ihxRecords.forEach((record) => { - let head = 0; - - if(record[head] != ":") { - console.error("not an ihx file"); - process.exit(1); - } - - head +=1; - - const dataLength = parseHexPair(record, head); - head += 2; - - const address = baseAddress + (parseHexPair(record, head) << 8) + parseHexPair(record, head + 2); - head += 4; - - const type = parseHexPair(record, head); - head += 2; - - if(type == 0) { - // Data Record, copy bytes - for(let index = 0; index < dataLength; index++) { - const byte = parseHexPair(record, head); - head += 2; - - gbBuffer[address + index] = byte; - } - } else if(type == 1) { - // EOF Record. - } else { - console.error("unsupported record type"); - process.exit(1); - } -}); - -writeLogo(gbBuffer); -writeTitle(gbBuffer, commander.name); -writeManufacturerCode(gbBuffer, commander.manufacturer); -writeCGBFlag(gbBuffer, commander.cgb); -writeLicenseeCode(gbBuffer, commander.licensee); -writeSGBFlag(gbBuffer, commander.sgb); -writeCartridgeType(gbBuffer, commander.cartridge); -writeROMSize(gbBuffer, commander.rom); -writeRAMSize(gbBuffer, commander.ram); -writeDestinationCode(gbBuffer, commander.destination); -writeOldLicenseeCode(gbBuffer, commander.oldlicensee); -writeROMVersion(gbBuffer, commander.version); -writeHeaderChecksum(gbBuffer); -writeROMChecksum(gbBuffer); - -fs.writeFileSync(outputFilePath, gbBuffer); diff --git a/ihx2gb/package-lock.json b/ihx2gb/package-lock.json deleted file mode 100644 index 78345e8..0000000 --- a/ihx2gb/package-lock.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "ihx2gb", - "version": "0.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "commander": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", - "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", - "requires": { - "graceful-readlink": ">= 1.0.0" - } - }, - "graceful-readlink": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" - }, - "overflow-js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/overflow-js/-/overflow-js-1.0.0.tgz", - "integrity": "sha1-G8S//0Qg5Rc5ZuvWd8SASR7phPA=" - } - } -} diff --git a/ihx2gb/package.json b/ihx2gb/package.json deleted file mode 100644 index 2aa81c8..0000000 --- a/ihx2gb/package.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "ihx2gb", - "description": "converts intel ihx files to game boy format", - "version": "0.0.0", - "main": "ihx2gb", - "dependencies": { - "commander": "~2.9.0", - "overflow-js": "~1.0.0" - } -} From ead33fcd4d74b5b716450dd92c0e1ce4604b5e1e Mon Sep 17 00:00:00 2001 From: Sebastian Riedel Date: Fri, 4 Feb 2022 16:25:57 +0100 Subject: [PATCH 02/10] Update README and improve makefile --- README.md | 5 +++-- game/makefile | 37 +++++++++++++++++++++---------------- img2gb/package-lock.json | 2 +- makefile | 19 +++++++++++++++++++ stringc/package-lock.json | 2 +- 5 files changed, 45 insertions(+), 20 deletions(-) create mode 100644 makefile diff --git a/README.md b/README.md index 6c8da4f..2794362 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,12 @@ I built this project on macOS. The instructions should be pretty easy to follow The build pipeline has dependencies on [node.js](http://nodejs.org) and [SDCC](http://sdcc.sourceforge.net). You'll need to install them. ### Dependencies -`cd` into `ihx2gb/` and run `npm install`. - `cd` into `img2gb/` and run `npm install`. `cd` into `stringc/` and run `npm install`. ### Building the Game `cd` into `game/` and run `make` or `make debug`. + +### Building the Game and Dependencies +Just run `make`, `make debug` or `make run` in the root folder. \ No newline at end of file diff --git a/game/makefile b/game/makefile index 27e66c0..2799cdd 100644 --- a/game/makefile +++ b/game/makefile @@ -1,6 +1,8 @@ CC = sdcc ASMC = sdasgb MKROM = makebin +EMU = sameboy +NODE = node CFLAGS = -c -mgbz80 ASMFLAGS = -plosgffjw LINKFLAGS = -mgbz80 --no-std-crt0 --data-loc 0xc0a0 @@ -14,44 +16,47 @@ OBJS = $(SRCS:.c=.o) SRCFILES = $(patsubst %.c,src/%.c,$(SRCS)) OBJFILES = $(patsubst %.s,obj/%.rel,$(ASMS)) $(patsubst %.c,obj/%.rel,$(SRCS)) +.PHONY: build debug run clean + build: obj/dependencies bin/BubbleFactory.gb debug: CFLAGS += -DDEBUG debug: build src/data_strings.c: data/strings.json - node ../stringc data/stringsMap.json $< $@ + $(NODE) ../stringc data/stringsMap.json $< $@ src/data_gfx_%.c: data/gfx/%.png - node ../img2gb -n $(notdir $(basename $<)) $< $@ + $(NODE) ../img2gb -n $(notdir $(basename $<)) $< $@ -obj/dependencies: $(SRCFILES) +obj/dependencies: $(SRCFILES) | obj/ @echo "regenerate dependency file" - @mkdir -p obj - @rm -f obj/dependencies + @$(RM) -f obj/dependencies @for srcFile in $(SRCFILES) ; do \ { printf "obj/"; $(CC) -MM $$srcFile; } >> obj/dependencies ; \ done -include obj/dependencies -bin/BubbleFactory.gb: obj/game.ihx - @mkdir -p bin +bin/BubbleFactory.gb: obj/game.ihx | bin/ $(MKROM) -Z -yn BUBBLEFACT -yk DH -yt 0x1B -ya 1 -yS -yp0x014C=1 $< $@ -obj/game.ihx: $(OBJFILES) - @mkdir -p obj +obj/game.ihx: $(OBJFILES) | obj/ $(CC) $(LINKFLAGS) $^ -o $@ -obj/%.rel: src/%.c - @mkdir -p obj +obj/%.rel: src/%.c | obj/ $(CC) $(CFLAGS) $< -o $@ -obj/%.rel: src/%.s - @mkdir -p obj +obj/%.rel: src/%.s | obj/ $(ASMC) $(ASMFLAGS) $@ $< +%/: + @mkdir -p $@ + +run: build + $(EMU) bin/BubbleFactory.gb + clean: - rm -rf src/data_* - rm -rf obj - rm -rf bin + $(RM) -rf src/data_* + $(RM) -rf obj + $(RM) -rf bin diff --git a/img2gb/package-lock.json b/img2gb/package-lock.json index 1995d27..f1f5bd5 100644 --- a/img2gb/package-lock.json +++ b/img2gb/package-lock.json @@ -1,7 +1,7 @@ { "name": "img2gb", "version": "0.0.0", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, "dependencies": { "commander": { diff --git a/makefile b/makefile new file mode 100644 index 0000000..4648552 --- /dev/null +++ b/makefile @@ -0,0 +1,19 @@ +.PHONY: build debug tools run clean + +build: tools + $(MAKE) -C game/ $@ + +debug: tools + $(MAKE) -C game/ $@ + +run: tools + $(MAKE) -C game/ $@ + +clean: + $(MAKE) -C game/ $@ + $(RM) -rf img2gb/node_modules stringc/node_modules + +tools: img2gb/node_modules stringc/node_modules + +%/node_modules: + cd $* && npm install \ No newline at end of file diff --git a/stringc/package-lock.json b/stringc/package-lock.json index 64f85c0..900a929 100644 --- a/stringc/package-lock.json +++ b/stringc/package-lock.json @@ -1,7 +1,7 @@ { "name": "stringc", "version": "1.0.0", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, "dependencies": { "commander": { From ca4bd2e455d243bf7d3532088a74809cc73d4962 Mon Sep 17 00:00:00 2001 From: Sebastian Riedel Date: Fri, 4 Feb 2022 17:25:01 +0100 Subject: [PATCH 03/10] Allow custom SDCC paths --- README.md | 9 ++++++++- game/makefile | 7 ++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2794362..3cc26e5 100644 --- a/README.md +++ b/README.md @@ -17,4 +17,11 @@ The build pipeline has dependencies on [node.js](http://nodejs.org) and [SDCC](h `cd` into `game/` and run `make` or `make debug`. ### Building the Game and Dependencies -Just run `make`, `make debug` or `make run` in the root folder. \ No newline at end of file +Just run `make`, `make debug` or `make run` in the root folder. + +### Building with an SDCC Snapshot +*This is for versions of SDCC that aren't installed* +``` +SDCC_HOME=/path/to/sdcc/share/sdcc/ +make SDCCBIN=${SDCC_HOME}../../bin/ +``` \ No newline at end of file diff --git a/game/makefile b/game/makefile index 2799cdd..7cf218c 100644 --- a/game/makefile +++ b/game/makefile @@ -1,6 +1,7 @@ -CC = sdcc -ASMC = sdasgb -MKROM = makebin +SDCCBIN = +CC = $(SDCCBIN)sdcc +ASMC = $(SDCCBIN)sdasgb +MKROM = $(SDCCBIN)makebin EMU = sameboy NODE = node CFLAGS = -c -mgbz80 From a8d36235ac88ae454004630573c5ca24b8ad3612 Mon Sep 17 00:00:00 2001 From: Sebastian Riedel Date: Fri, 4 Feb 2022 18:51:09 +0100 Subject: [PATCH 04/10] Adapt to target renaming gbz80->sm83 --- game/makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/game/makefile b/game/makefile index 7cf218c..d322d17 100644 --- a/game/makefile +++ b/game/makefile @@ -4,9 +4,9 @@ ASMC = $(SDCCBIN)sdasgb MKROM = $(SDCCBIN)makebin EMU = sameboy NODE = node -CFLAGS = -c -mgbz80 +CFLAGS = -c -msm83 ASMFLAGS = -plosgffjw -LINKFLAGS = -mgbz80 --no-std-crt0 --data-loc 0xc0a0 +LINKFLAGS = -msm83 --no-std-crt0 --data-loc 0xc0a0 GFXS = $(notdir $(shell find data/gfx -name '*.png')) GFXSRC = $(patsubst %.png,data_gfx_%.c,$(GFXS)) From 5564c334156c81c29b007f3dabd21927f70173db Mon Sep 17 00:00:00 2001 From: Sebastian Riedel Date: Fri, 4 Feb 2022 19:06:40 +0100 Subject: [PATCH 05/10] Force old calling convention for handwritten asm --- game/src/gb.c | 14 +++++++------- game/src/gb.h | 14 +++++++------- game/src/memory.c | 2 +- game/src/memory.h | 2 +- game/src/sprites.c | 2 +- game/src/sprites.h | 2 +- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/game/src/gb.c b/game/src/gb.c index f18892e..a887eaa 100644 --- a/game/src/gb.c +++ b/game/src/gb.c @@ -16,7 +16,7 @@ GBJoypadState gbJoypadReleasedSinceLastUpdate; // === // Public API // === -void gbLCDDisable() { +void gbLCDDisable() __sdcccall(0) { __asm push af @@ -36,28 +36,28 @@ void gbLCDDisable() { __endasm; } -void gbLCDEnable() { +void gbLCDEnable() __sdcccall(0) { __asm ld hl, #0xff40 set #7, (hl) __endasm; } -void gbSpritesDisable() { +void gbSpritesDisable() __sdcccall(0) { __asm ld hl, #0xff40 res #1, (hl) __endasm; } -void gbSpritesEnable() { +void gbSpritesEnable() __sdcccall(0) { __asm ld hl, #0xff40 set #1, (hl) __endasm; } -void gbJoypadStateUpdate() { +void gbJoypadStateUpdate() __sdcccall(0) { GBUInt8 lastValue; lastValue = gbJoypadState; @@ -109,7 +109,7 @@ void gbJoypadStateUpdate() { gbJoypadReleasedSinceLastUpdate = (gbJoypadState ^ lastValue) & lastValue; } -void gbLogUInt8(GBUInt8 value) { +void gbLogUInt8(GBUInt8 value) __sdcccall(0) { (void)(value); // Suppresses unused variable warning __asm @@ -128,7 +128,7 @@ void gbLogUInt8(GBUInt8 value) { __endasm; } -void gbLogUInt16(GBUInt16 value) { +void gbLogUInt16(GBUInt16 value) __sdcccall(0) { (void)(value); // Suppresses unused variable warning __asm diff --git a/game/src/gb.h b/game/src/gb.h index 39cb73a..fe62261 100644 --- a/game/src/gb.h +++ b/game/src/gb.h @@ -79,15 +79,15 @@ extern GBJoypadState gbJoypadState; extern GBJoypadState gbJoypadPressedSinceLastUpdate; extern GBJoypadState gbJoypadReleasedSinceLastUpdate; -void gbLCDDisable(); -void gbLCDEnable(); +void gbLCDDisable() __sdcccall(0); +void gbLCDEnable() __sdcccall(0); -void gbSpritesDisable(); -void gbSpritesEnable(); +void gbSpritesDisable() __sdcccall(0); +void gbSpritesEnable() __sdcccall(0); -void gbJoypadStateUpdate(); +void gbJoypadStateUpdate() __sdcccall(0); -void gbLogUInt8(GBUInt8 value); -void gbLogUInt16(GBUInt16 value); +void gbLogUInt8(GBUInt8 value) __sdcccall(0); +void gbLogUInt16(GBUInt16 value) __sdcccall(0); #endif /* gb_h */ diff --git a/game/src/memory.c b/game/src/memory.c index d8db993..76d6290 100644 --- a/game/src/memory.c +++ b/game/src/memory.c @@ -4,7 +4,7 @@ volatile const void * memoryCopySourceAddress; volatile void * memoryCopyDestinationAddress; volatile GBUInt8 memoryCopyLength; -void memoryCopy() { +void memoryCopy() __sdcccall(0) { __asm push af diff --git a/game/src/memory.h b/game/src/memory.h index db800fc..25b4d67 100644 --- a/game/src/memory.h +++ b/game/src/memory.h @@ -7,6 +7,6 @@ extern volatile const void * memoryCopySourceAddress; extern volatile void * memoryCopyDestinationAddress; extern volatile GBUInt8 memoryCopyLength; -void memoryCopy(); +void memoryCopy() __sdcccall(0); #endif diff --git a/game/src/sprites.c b/game/src/sprites.c index d4c446c..a8b0bd8 100644 --- a/game/src/sprites.c +++ b/game/src/sprites.c @@ -35,7 +35,7 @@ void spritesInit() { tempAnimationPhase = 0; } -void spritesWriteToOAM() { +void spritesWriteToOAM() __sdcccall(0) { __asm call #0xFF80 __endasm; diff --git a/game/src/sprites.h b/game/src/sprites.h index 149ba12..6cac954 100644 --- a/game/src/sprites.h +++ b/game/src/sprites.h @@ -38,7 +38,7 @@ extern volatile const SpriteFrameAttributes * spritesWriteAttributes; #define spritesAttributesMake(priority, flipX, flipY, paletteNumber) (((priority) << 7) | ((flipX) << 5) | ((flipY) << 6) | ((paletteNumber) << 4)) void spritesInit(); -void spritesWriteToOAM(); +void spritesWriteToOAM() __sdcccall(0); void spritesWrite2x2(); void spritesWrite1x1(); From 8b6b5402699e252db09e9d4e4749ae115d41d818 Mon Sep 17 00:00:00 2001 From: Sebastian Riedel Date: Fri, 4 Feb 2022 19:50:21 +0100 Subject: [PATCH 06/10] Add more debug flags and statistic tools --- game/makefile | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/game/makefile b/game/makefile index 7cf218c..2fd6471 100644 --- a/game/makefile +++ b/game/makefile @@ -1,12 +1,15 @@ SDCCBIN = CC = $(SDCCBIN)sdcc -ASMC = $(SDCCBIN)sdasgb +AS = $(SDCCBIN)sdasgb MKROM = $(SDCCBIN)makebin +# https://github.com/LIJI32/SameBoy or any other emulator EMU = sameboy +# https://github.com/bbbbbr/romusage +ROMUSE = romusage NODE = node CFLAGS = -c -mgbz80 -ASMFLAGS = -plosgffjw -LINKFLAGS = -mgbz80 --no-std-crt0 --data-loc 0xc0a0 +ASFLAGS = -plosgffjw +LDFLAGS = -mgbz80 --no-std-crt0 --data-loc 0xc0a0 GFXS = $(notdir $(shell find data/gfx -name '*.png')) GFXSRC = $(patsubst %.png,data_gfx_%.c,$(GFXS)) @@ -17,11 +20,14 @@ OBJS = $(SRCS:.c=.o) SRCFILES = $(patsubst %.c,src/%.c,$(SRCS)) OBJFILES = $(patsubst %.s,obj/%.rel,$(ASMS)) $(patsubst %.c,obj/%.rel,$(SRCS)) -.PHONY: build debug run clean +ROM = BubbleFactory -build: obj/dependencies bin/BubbleFactory.gb +.PHONY: build debug run clean spaceleft statistics -debug: CFLAGS += -DDEBUG +build: obj/dependencies bin/$(ROM).gb + +debug: CFLAGS += -DDEBUG --debug +debug: LDFLAGS += -Wl-y debug: build src/data_strings.c: data/strings.json @@ -39,23 +45,33 @@ obj/dependencies: $(SRCFILES) | obj/ -include obj/dependencies -bin/BubbleFactory.gb: obj/game.ihx | bin/ +bin/$(ROM).gb: obj/game.ihx | bin/ $(MKROM) -Z -yn BUBBLEFACT -yk DH -yt 0x1B -ya 1 -yS -yp0x014C=1 $< $@ + cp obj/game.sym bin/$(ROM).sym + cp obj/game.noi bin/$(ROM).noi + test -e obj/game.cdb && cp obj/game.cdb bin/$(ROM).cdb obj/game.ihx: $(OBJFILES) | obj/ - $(CC) $(LINKFLAGS) $^ -o $@ + $(CC) $(LDFLAGS) $^ -o $@ obj/%.rel: src/%.c | obj/ $(CC) $(CFLAGS) $< -o $@ obj/%.rel: src/%.s | obj/ - $(ASMC) $(ASMFLAGS) $@ $< + $(AS) $(ASFLAGS) $@ $< %/: @mkdir -p $@ +spaceleft: build + $(ROMUSE) bin/$(ROM).noi -g -E + +statistics: build + test -e bin/$(ROM).cdb && $(ROMUSE) bin/$(ROM).cdb -g + $(ROMUSE) bin/$(ROM).noi -G -E -sH -a + run: build - $(EMU) bin/BubbleFactory.gb + $(EMU) $(ROM) clean: $(RM) -rf src/data_* From d26108bf3ab971b05a7618f2f1c89e7ba76e4442 Mon Sep 17 00:00:00 2001 From: Sebastian Riedel Date: Fri, 4 Feb 2022 20:11:17 +0100 Subject: [PATCH 07/10] Invert file existence checks --- game/makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/game/makefile b/game/makefile index 2fd6471..50a3f9d 100644 --- a/game/makefile +++ b/game/makefile @@ -49,7 +49,7 @@ bin/$(ROM).gb: obj/game.ihx | bin/ $(MKROM) -Z -yn BUBBLEFACT -yk DH -yt 0x1B -ya 1 -yS -yp0x014C=1 $< $@ cp obj/game.sym bin/$(ROM).sym cp obj/game.noi bin/$(ROM).noi - test -e obj/game.cdb && cp obj/game.cdb bin/$(ROM).cdb + test \! -s obj/game.cdb || cp obj/game.cdb bin/$(ROM).cdb obj/game.ihx: $(OBJFILES) | obj/ $(CC) $(LDFLAGS) $^ -o $@ @@ -67,11 +67,11 @@ spaceleft: build $(ROMUSE) bin/$(ROM).noi -g -E statistics: build - test -e bin/$(ROM).cdb && $(ROMUSE) bin/$(ROM).cdb -g + test \! -s bin/$(ROM).cdb || $(ROMUSE) bin/$(ROM).cdb -g $(ROMUSE) bin/$(ROM).noi -G -E -sH -a run: build - $(EMU) $(ROM) + $(EMU) bin/$(ROM).gb clean: $(RM) -rf src/data_* From 534e93c02a797c489c5376789ed3d25f123da137 Mon Sep 17 00:00:00 2001 From: Sebastian Riedel Date: Fri, 4 Feb 2022 20:46:05 +0100 Subject: [PATCH 08/10] Remove unnecessary old calling conventions --- game/src/gb.c | 10 +++++----- game/src/gb.h | 10 +++++----- game/src/memory.c | 2 +- game/src/memory.h | 2 +- game/src/sprites.c | 2 +- game/src/sprites.h | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/game/src/gb.c b/game/src/gb.c index a887eaa..f99113a 100644 --- a/game/src/gb.c +++ b/game/src/gb.c @@ -16,7 +16,7 @@ GBJoypadState gbJoypadReleasedSinceLastUpdate; // === // Public API // === -void gbLCDDisable() __sdcccall(0) { +void gbLCDDisable() { __asm push af @@ -36,28 +36,28 @@ void gbLCDDisable() __sdcccall(0) { __endasm; } -void gbLCDEnable() __sdcccall(0) { +void gbLCDEnable() { __asm ld hl, #0xff40 set #7, (hl) __endasm; } -void gbSpritesDisable() __sdcccall(0) { +void gbSpritesDisable() { __asm ld hl, #0xff40 res #1, (hl) __endasm; } -void gbSpritesEnable() __sdcccall(0) { +void gbSpritesEnable() { __asm ld hl, #0xff40 set #1, (hl) __endasm; } -void gbJoypadStateUpdate() __sdcccall(0) { +void gbJoypadStateUpdate() { GBUInt8 lastValue; lastValue = gbJoypadState; diff --git a/game/src/gb.h b/game/src/gb.h index fe62261..fc8c216 100644 --- a/game/src/gb.h +++ b/game/src/gb.h @@ -79,13 +79,13 @@ extern GBJoypadState gbJoypadState; extern GBJoypadState gbJoypadPressedSinceLastUpdate; extern GBJoypadState gbJoypadReleasedSinceLastUpdate; -void gbLCDDisable() __sdcccall(0); -void gbLCDEnable() __sdcccall(0); +void gbLCDDisable(); +void gbLCDEnable(); -void gbSpritesDisable() __sdcccall(0); -void gbSpritesEnable() __sdcccall(0); +void gbSpritesDisable(); +void gbSpritesEnable(); -void gbJoypadStateUpdate() __sdcccall(0); +void gbJoypadStateUpdate(); void gbLogUInt8(GBUInt8 value) __sdcccall(0); void gbLogUInt16(GBUInt16 value) __sdcccall(0); diff --git a/game/src/memory.c b/game/src/memory.c index 76d6290..d8db993 100644 --- a/game/src/memory.c +++ b/game/src/memory.c @@ -4,7 +4,7 @@ volatile const void * memoryCopySourceAddress; volatile void * memoryCopyDestinationAddress; volatile GBUInt8 memoryCopyLength; -void memoryCopy() __sdcccall(0) { +void memoryCopy() { __asm push af diff --git a/game/src/memory.h b/game/src/memory.h index 25b4d67..db800fc 100644 --- a/game/src/memory.h +++ b/game/src/memory.h @@ -7,6 +7,6 @@ extern volatile const void * memoryCopySourceAddress; extern volatile void * memoryCopyDestinationAddress; extern volatile GBUInt8 memoryCopyLength; -void memoryCopy() __sdcccall(0); +void memoryCopy(); #endif diff --git a/game/src/sprites.c b/game/src/sprites.c index a8b0bd8..d4c446c 100644 --- a/game/src/sprites.c +++ b/game/src/sprites.c @@ -35,7 +35,7 @@ void spritesInit() { tempAnimationPhase = 0; } -void spritesWriteToOAM() __sdcccall(0) { +void spritesWriteToOAM() { __asm call #0xFF80 __endasm; diff --git a/game/src/sprites.h b/game/src/sprites.h index 6cac954..149ba12 100644 --- a/game/src/sprites.h +++ b/game/src/sprites.h @@ -38,7 +38,7 @@ extern volatile const SpriteFrameAttributes * spritesWriteAttributes; #define spritesAttributesMake(priority, flipX, flipY, paletteNumber) (((priority) << 7) | ((flipX) << 5) | ((flipY) << 6) | ((paletteNumber) << 4)) void spritesInit(); -void spritesWriteToOAM() __sdcccall(0); +void spritesWriteToOAM(); void spritesWrite2x2(); void spritesWrite1x1(); From d7de030a8ce36016840fb8381d2c728bd5043533 Mon Sep 17 00:00:00 2001 From: Sebastian Riedel Date: Fri, 4 Feb 2022 22:40:29 +0100 Subject: [PATCH 09/10] Rewrite asm for new calling convention --- game/src/gb.c | 24 +++++--------------- game/src/gb.h | 4 ++-- game/src/map.c | 56 ++++++++++++++--------------------------------- game/src/memory.c | 39 ++++++++++++--------------------- game/src/memory.h | 4 +--- 5 files changed, 38 insertions(+), 89 deletions(-) diff --git a/game/src/gb.c b/game/src/gb.c index f99113a..1414322 100644 --- a/game/src/gb.c +++ b/game/src/gb.c @@ -109,44 +109,30 @@ void gbJoypadStateUpdate() { gbJoypadReleasedSinceLastUpdate = (gbJoypadState ^ lastValue) & lastValue; } -void gbLogUInt8(GBUInt8 value) __sdcccall(0) { +// 8bit in a +void gbLogUInt8(GBUInt8 value) { (void)(value); // Suppresses unused variable warning __asm - push af - push hl - ldhl sp, #3 - ld a, (hl) ld d, d jr .gbLogUInt8End .dw 0x6464 .dw 0x0000 .strz "0x%a%" .gbLogUInt8End: - pop hl - pop af __endasm; } -void gbLogUInt16(GBUInt16 value) __sdcccall(0) { +// 16bit in de +void gbLogUInt16(GBUInt16 value) { (void)(value); // Suppresses unused variable warning __asm - push af - push bc - push hl - ldhl sp, #8 - ld a, (hl+) - ld c, a - ld b, (hl) ld d, d jr .gbLogUInt16End .dw 0x6464 .dw 0x0000 - .strz "0x%bc%" + .strz "0x%de%" .gbLogUInt16End: - pop hl - pop bc - pop af __endasm; } diff --git a/game/src/gb.h b/game/src/gb.h index fc8c216..39cb73a 100644 --- a/game/src/gb.h +++ b/game/src/gb.h @@ -87,7 +87,7 @@ void gbSpritesEnable(); void gbJoypadStateUpdate(); -void gbLogUInt8(GBUInt8 value) __sdcccall(0); -void gbLogUInt16(GBUInt16 value) __sdcccall(0); +void gbLogUInt8(GBUInt8 value); +void gbLogUInt16(GBUInt16 value); #endif /* gb_h */ diff --git a/game/src/map.c b/game/src/map.c index e901d71..62b89e7 100644 --- a/game/src/map.c +++ b/game/src/map.c @@ -225,13 +225,9 @@ void mapUpdateGraphics() { _needsToRedrawHighScore = false; memoryCopyLength = 4; - memoryCopyDestinationAddress = gbTileMap0; - memoryCopySourceAddress = _highScoreMap[0]; - memoryCopy(); - - memoryCopyDestinationAddress = gbTileMap0 + 32; - memoryCopySourceAddress = _highScoreMap[1]; - memoryCopy(); + memoryCopy(gbTileMap0, _highScoreMap[0]); + + memoryCopy(gbTileMap0 + 32, _highScoreMap[1]); return; } @@ -240,13 +236,9 @@ void mapUpdateGraphics() { _needsToRedrawScore = false; memoryCopyLength = 4; - memoryCopyDestinationAddress = gbTileMap0 + 16; - memoryCopySourceAddress = _scoreMap[0]; - memoryCopy(); - - memoryCopyDestinationAddress = gbTileMap0 + 48; - memoryCopySourceAddress = _scoreMap[1]; - memoryCopy(); + memoryCopy(gbTileMap0 + 16, _scoreMap[0]); + + memoryCopy(gbTileMap0 + 48, _scoreMap[1]); return; } @@ -303,13 +295,9 @@ void mapUpdateGraphics() { if(_mapAnimationTickTime & 2) { // Top Left memoryCopyLength = 8; - memoryCopyDestinationAddress = gbTileMap0 + 67; - memoryCopySourceAddress = tileAnimations1[_mapAnimationCycle]; - memoryCopy(); - - memoryCopyDestinationAddress = gbTileMap0 + 99; - memoryCopySourceAddress = tileAnimations2[_mapAnimationCycle]; - memoryCopy(); + memoryCopy(gbTileMap0 + 67, tileAnimations1[_mapAnimationCycle]); + + memoryCopy(gbTileMap0 + 99, tileAnimations2[_mapAnimationCycle]); if(bubblesIsTopInDanger()) { if((_mapAnimationCycle & 1) == 0) { @@ -328,35 +316,23 @@ void mapUpdateGraphics() { } else { // Top Right memoryCopyLength = 6; - memoryCopyDestinationAddress = gbTileMap0 + 75; - memoryCopySourceAddress = tileAnimations1[_mapAnimationCycle] + 8; - memoryCopy(); - - memoryCopyDestinationAddress = gbTileMap0 + 107; - memoryCopySourceAddress = tileAnimations2[_mapAnimationCycle] + 8; - memoryCopy(); + memoryCopy(gbTileMap0 + 75, tileAnimations1[_mapAnimationCycle] + 8); + + memoryCopy(gbTileMap0 + 107, tileAnimations2[_mapAnimationCycle] + 8); } } else { if(_mapAnimationTickTime & 2) { // Bottom Left memoryCopyLength = 8; - memoryCopyDestinationAddress = gbTileMap0 + 451; - memoryCopySourceAddress = tileAnimations3[_mapAnimationCycle]; - memoryCopy(); + memoryCopy(gbTileMap0 + 451, tileAnimations3[_mapAnimationCycle]); - memoryCopyDestinationAddress = gbTileMap0 + 483; - memoryCopySourceAddress = tileAnimations4[_mapAnimationCycle]; - memoryCopy(); + memoryCopy(gbTileMap0 + 483, tileAnimations4[_mapAnimationCycle]); } else { // Bottom Right memoryCopyLength = 6; - memoryCopyDestinationAddress = gbTileMap0 + 459; - memoryCopySourceAddress = tileAnimations3[_mapAnimationCycle] + 8; - memoryCopy(); + memoryCopy(gbTileMap0 + 459, tileAnimations3[_mapAnimationCycle] + 8); - memoryCopyDestinationAddress = gbTileMap0 + 491; - memoryCopySourceAddress = tileAnimations4[_mapAnimationCycle] + 8; - memoryCopy(); + memoryCopy(gbTileMap0 + 491, tileAnimations4[_mapAnimationCycle] + 8); if(bubblesIsBottomInDanger()) { if((_mapAnimationCycle & 1) == 0) { diff --git a/game/src/memory.c b/game/src/memory.c index d8db993..cb4b119 100644 --- a/game/src/memory.c +++ b/game/src/memory.c @@ -1,45 +1,34 @@ #include "memory.h" -volatile const void * memoryCopySourceAddress; -volatile void * memoryCopyDestinationAddress; volatile GBUInt8 memoryCopyLength; -void memoryCopy() { +void memoryCopy(void * dest, const void * src) { + // Suppresses unused variable warning + (void)(dest); // in de + (void)(src); // in bc __asm - + push af - push bc - push de - ld hl, #_memoryCopyLength - ld c, (hl) - - ld hl, #_memoryCopyDestinationAddress - ld e, (hl) - inc hl - ld d, (hl) - - ld hl, #_memoryCopySourceAddress ld a, (hl) - inc hl - ld h, (hl) - ld l, a - -.loop: - ld a, c + + ld h, b + ld l, c + + ld c, a + or a jr z, .endLoop +.loop: ld a, (hl+) ld (de), a inc de + dec c - - jr .loop + jr nz, .loop .endLoop: - pop de - pop bc pop af __endasm; diff --git a/game/src/memory.h b/game/src/memory.h index db800fc..94cddfb 100644 --- a/game/src/memory.h +++ b/game/src/memory.h @@ -3,10 +3,8 @@ #include "gb.h" -extern volatile const void * memoryCopySourceAddress; -extern volatile void * memoryCopyDestinationAddress; extern volatile GBUInt8 memoryCopyLength; -void memoryCopy(); +void memoryCopy(void * dest, const void * src); #endif From 819aa441aad1120ea0b08d3f9d8757e421ee8534 Mon Sep 17 00:00:00 2001 From: Sebastian Riedel Date: Fri, 4 Feb 2022 23:45:56 +0100 Subject: [PATCH 10/10] Optimize some asm --- game/src/crt0.s | 10 ++++------ game/src/gb.c | 33 ++++++++++++++++++++++----------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/game/src/crt0.s b/game/src/crt0.s index f77c4af..f8ac85c 100644 --- a/game/src/crt0.s +++ b/game/src/crt0.s @@ -61,24 +61,22 @@ ld sp, #0xE000 ; initialize gamepad to 0 - ld a, #0 + xor a ld (_gbJoypadState), a ; install dma handler ld c, #(.endOAMDMA - .oamDMA) ld de, #0xFF80 ld hl, #.oamDMA + + ; c is never 0 .installOAMDMALoop: - ld a, c - or a - jr z, .endInstallOAMDMALoop - ld a, (hl+) ld (de), a inc de dec c - jr .installOAMDMALoop + jr nz, .installOAMDMALoop .endInstallOAMDMALoop: call _main diff --git a/game/src/gb.c b/game/src/gb.c index 1414322..be7dac8 100644 --- a/game/src/gb.c +++ b/game/src/gb.c @@ -23,7 +23,7 @@ void gbLCDDisable() { ; busy wait for vblank .lcdDisableLoop: - ld a, (#0xff44) + ldh a, (#0xff44) cp a, #144 jr nz, .lcdDisableLoop @@ -69,29 +69,40 @@ void gbJoypadStateUpdate() { push af push bc + ld c, #0 ; Read first part of joypad state ld a, #0x20 - ld (#0xff00), a + ldh (c), a ; Read several times to burn cycles waiting for register to update - ld a, (#0xff00) - ld a, (#0xff00) + ; `ldh a, (c); ldh a, (c)` same amount of clocks + ; as `ld a, (#0xff00)` but one byte less + ldh a, (c) + ldh a, (c) + ldh a, (c) + ldh a, (c) cpl ; Invert so 1=on and 0=off and #0x0f ; Only want 4 least significant bits ld b, a ; Store in b ; Read second part of joypad state ld a, #0x10 - ld (#0xff00), a + ldh (c), a ; Read several times to burn cycles waiting for register to update - ld a, (#0xff00) - ld a, (#0xff00) - ld a, (#0xff00) - ld a, (#0xff00) - ld a, (#0xff00) - ld a, (#0xff00) + ldh a, (c) + ldh a, (c) + ldh a, (c) + ldh a, (c) + ldh a, (c) + ldh a, (c) + ldh a, (c) + ldh a, (c) + ldh a, (c) + ldh a, (c) + ldh a, (c) + ldh a, (c) cpl ; invert and #0x0f ; only 4 least significant bits