aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig4
-rw-r--r--CONTRIBUTING.md20
-rw-r--r--Makefile24
-rw-r--r--README.md4
-rw-r--r--bin/values.rb80
-rw-r--r--config/values.yml30
-rw-r--r--config/values/player.s28
-rw-r--r--src/player.s22
8 files changed, 193 insertions, 19 deletions
diff --git a/.editorconfig b/.editorconfig
index c420a82..bd627cd 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -9,5 +9,9 @@ end_of_line = lf
[*.{s,S}]
indent_style = space
+[*.rb]
+indent_size = 2
+indent_style = space
+
[Makefile]
indent_style = tab
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index c70602d..215034b 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,8 +1,10 @@
## Why?
-Do you want to fix an error you have found? Do you know a way to improve my
-6502-fu or do you know a technique on NES/Famicom development that might help
-here? I am open for discussion and welcome any help!
+- You want to make a suggestion on something that could be improved.
+- You want to report a bug, something that doesn't work as expected.
+- You know of a sick 6502 assembly technique that could be applied.
+
+I am open for discussion and welcome any help!
## How?
@@ -35,6 +37,18 @@ In order to test your changes, I'd go this way:
3. Run the ROM that was produced with an emulator of your choosing. Make sure
that things run as expected.
+### Customizing the build process
+
+You can pass the following arguments to `make`:
+
+- `CC65`: the compiler to use (defaults to `cl65`).
+- `CCOPTS`: the options to use for the compiler (defaults to `--target nes`).
+- `RUBY`: the ruby to use (defaults to `ruby`).
+
+Note that you can also set `DEBUG=1`, and with that you will pass sobre extra
+debugging options, like telling `cl65` to also output a `out/labels.txt` file
+with memory address on the symbols that have been evaluated.
+
## Modifying assets
I am using [NEXXT studio 3](https://frankengraphics.itch.io/nexxt) for managing
diff --git a/Makefile b/Makefile
index 74a3d93..a00dc6b 100644
--- a/Makefile
+++ b/Makefile
@@ -7,12 +7,23 @@ else
Q =
endif
+# NOTE: you can configure `CC65` and `CCOPTS` with the compiler and its options
+# that you might require. Moreover, if you pass `DEBUG` to `make`, then an
+# `out/labels.txt` file will be generated.
CC65 ?= cl65
CCOPTS ?= --target nes
ifeq "$(DEBUG)" "1"
CCOPTS += -g -Ln out/labels.txt
endif
+# Ruby is used to generate the files on `config/values/`. If it can't be found,
+# a warning will be echo'ed.
+#
+# NOTE: you can actually set RUBY as an argument to `make` if you want to pass
+# something special to it.
+RUBY ?= ruby
+HAS_RUBY := $(shell command -v $(RUBY) >/dev/null 2>&1 && echo true || echo false)
+
.PHONY: all
all: clean deps build
@@ -25,10 +36,19 @@ clean:
.PHONY: deps
deps:
- @which $(CC65) >/dev/null 2>/dev/null || (echo "ERROR: $(CC65) not found." && false)
+ @which $(CC65) >/dev/null 2>/dev/null || (echo "ERROR: '$(CC65)' not found." && false)
+
+.PHONY: gen-values
+gen-values:
+ifeq ($(HAS_RUBY),true)
+ $(E) " GEN config/values"
+ $(Q) ruby bin/values.rb
+else
+ @(Q) echo "WARNING: '$(RUBY)' not found; files under 'config/values/' will not be generated."
+endif
.PHONY: build
-build: build-full build-partial build-pal
+build: gen-values build-full build-partial build-pal
.PHONY: build-full
build-full:
diff --git a/README.md b/README.md
index 6a15642..bfda220 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,9 @@
This is a port to the NES/Famicom of the renown
[jetpac](https://en.wikipedia.org/wiki/Jetpac) game from Ultimate Play the Game.
You can find a ROM to play the game in the [releases
-page](https://github.com/mssola/jetpac.nes/releases).
+page](https://github.com/mssola/jetpac.nes/releases). Read the
+[CONTRIBUTING.md](./CONTRIBUTING.md) file if you want to make any changes,
+report an issue or make a suggestion.
## The game
diff --git a/bin/values.rb b/bin/values.rb
new file mode 100644
index 0000000..3ff1578
--- /dev/null
+++ b/bin/values.rb
@@ -0,0 +1,80 @@
+#!/usr/bin/env ruby
+
+##
+# Generate the different values on `config/values/*.s` by parsing the values on
+# `config/values.yml`. The values on that file are agnostic to NTSC or PAL, and
+# it's up to this script to produce constants which make sense for NTSC and PAL.
+# That is, it's a way to ensure that both NTSC and PAL have the same experience
+# (or at least as close as possible).
+
+##
+# Parse the configuration.
+
+require 'yaml'
+
+config_path = File.join("#{File.dirname(__FILE__)}/..", 'config/')
+config = YAML.safe_load_file(File.join(config_path, 'values.yml'))
+
+# Converts the given floating point value into a signed fixed point value in the
+# 4.4 format.
+def to_signed_fixed_point(value)
+ integer = value.to_i
+ raise "bad signed fixed point value" if integer > 7 || integer < -7
+ integer &= 0b00001111
+
+ decimal = (value % 1) * 100
+ decimal = ((decimal * 15) / 100.0).round & 0b00001111
+
+ (integer << 4) | decimal
+end
+
+##
+# Loop through the configuration and fetch values for NTSC and PAL.
+
+res = {}
+config.each do |model, properties|
+ res[model] ||= { ntsc: {}, pal: {} }
+
+ properties.each do |name, ntsc|
+ name = name.upcase
+ pal = (ntsc * 6) / 5.0
+
+ res[model][:ntsc][name] = to_signed_fixed_point(ntsc)
+ res[model][:pal][name] = to_signed_fixed_point(pal)
+ end
+end
+
+##
+# Generate each model as expected.
+
+def to_hex(value)
+ hex = value.to_s(16).upcase
+
+ if hex.size == 1
+ "$0#{hex}"
+ else
+ "$#{hex}"
+ end
+end
+
+def values_to_asm(values)
+ contents = ""
+ values.each { |k, v| contents << " #{k} = #{to_hex(v)}\n" }
+ contents.rstrip
+end
+
+res.each do |model, formats|
+ path = File.join(config_path, "values/#{model}.s")
+ contents = <<HERE
+;; This file has been automatically generated via bin/values.rb.
+;; DO NOT MODIFY this file directly: check config/values.yml instead.
+
+.ifdef PAL
+#{values_to_asm(formats[:pal])}
+.else
+#{values_to_asm(formats[:ntsc])}
+.endif
+HERE
+
+ File.open(path, 'w') { |f| f.write(contents) }
+end
diff --git a/config/values.yml b/config/values.yml
new file mode 100644
index 0000000..257282e
--- /dev/null
+++ b/config/values.yml
@@ -0,0 +1,30 @@
+# Constants used throughout the game in a floating point format. The
+# `bin/values.rb` script will make sure to transform these into 4.4 signed fixed
+# point values that fit into a byte, as expected by the code from this game.
+#
+# Each file will have the name of the key from the first level (e.g. "player"
+# will become "config/values/player.s"), and under the file each constant will
+# be upper cased (e.g. "player.throttle_up" will become "THROTTLE_UP" inside of
+# the "player.s" file).
+
+player:
+ # Gravity and initial velocity when throttling from the ground.
+ gravity: 2.50
+ blast_off: -1.50
+
+ # Throttling.
+ throttle_up: -2.90
+ throttle_left: -2.90
+ throttle_right: 1.90
+
+ # Walking constant velocities.
+ walk_left: -1.80
+ walk_right: 0.80
+
+ # Horizontal bounces
+ bounce_left: -2.53
+ bounce_right: 1.53
+
+ # Vertical bounces
+ reduce_full_speed: 1.00
+ reduce_mid_speed: 0.53
diff --git a/config/values/player.s b/config/values/player.s
new file mode 100644
index 0000000..a27d10a
--- /dev/null
+++ b/config/values/player.s
@@ -0,0 +1,28 @@
+;; This file has been automatically generated via bin/values.rb.
+;; DO NOT MODIFY this file directly: check config/values.yml instead.
+
+.ifdef PAL
+ GRAVITY = $30
+ BLAST_OFF = $F3
+ THROTTLE_UP = $D8
+ THROTTLE_LEFT = $D8
+ THROTTLE_RIGHT = $24
+ WALK_LEFT = $ED
+ WALK_RIGHT = $0E
+ BOUNCE_LEFT = $DE
+ BOUNCE_RIGHT = $1D
+ REDUCE_FULL_SPEED = $13
+ REDUCE_MID_SPEED = $0A
+.else
+ GRAVITY = $28
+ BLAST_OFF = $F8
+ THROTTLE_UP = $E2
+ THROTTLE_LEFT = $E2
+ THROTTLE_RIGHT = $1D
+ WALK_LEFT = $F3
+ WALK_RIGHT = $0C
+ BOUNCE_LEFT = $E7
+ BOUNCE_RIGHT = $18
+ REDUCE_FULL_SPEED = $10
+ REDUCE_MID_SPEED = $08
+.endif
diff --git a/src/player.s b/src/player.s
index 9cb73f3..ebf6dfb 100644
--- a/src/player.s
+++ b/src/player.s
@@ -62,19 +62,15 @@
INIT_Y_POSITION_LO = ((Background::GROUND_Y_COORD - PLAYER_HEIGHT) & $0F) << 4
INIT_Y_POSITION_HI = (Background::GROUND_Y_COORD - PLAYER_HEIGHT) >> 4
- ;; The initial position on the X axis is more or less at the center.
+ ;; The initial position on the X axis is right below the mid platform.
INIT_X_POSITION_LO = $00
- INIT_X_POSITION_HI = $07
+ INIT_X_POSITION_HI = $08
;; Different acceleration/velocity constants.
- GRAVITY = $28
- THROTTLE_UP = $D8
- THROTTLE_LEFT = $D8
- THROTTLE_RIGHT = $28
- BLAST_OFF = $F8 ; Initial velocity from ground.
- WALK_LEFT = $F8
- WALK_RIGHT = $08
- REDUCE_FULL_SPEED = $10 ; Next velocity after hitting a ceiling at full speed.
+ ;;
+ ;; NOTE: automatically generated via `bin/values.rb`. Check the
+ ;; `config/values.yml` to understand the meaning of each constant.
+ .include "../config/values/player.s"
zp_screen_y = $40
zp_position_y = $41 ; NOTE: 16-bit.
@@ -553,7 +549,7 @@
lda #REDUCE_FULL_SPEED
bne @correct_vertical_velocity
@reduced_velocity:
- lda #8
+ lda #REDUCE_MID_SPEED
@correct_vertical_velocity:
sta zp_velocity_y
rts
@@ -639,7 +635,7 @@
;; game did.
sec
sbc #PLAYER_WIDTH
- ldx #$E8
+ ldx #BOUNCE_LEFT
bne @horizontal_eject
@left_collision:
@@ -647,7 +643,7 @@
;; need to add the tile width to it.
clc
adc #8
- ldx #$18
+ ldx #BOUNCE_RIGHT
@horizontal_eject:
;; The screen coordinate has been computed into the `a` register, and