scroll down works on CHIP-8 and High-Res modes

scroll left and right work in stdef and hidef
adds octo test roms
adds schip fonts to memory
This commit is contained in:
2024-10-25 08:19:03 -04:00
parent a978ddc41e
commit e29ac45c84
65 changed files with 13342 additions and 230 deletions
+310
View File
@@ -0,0 +1,310 @@
###########################################
#
# Chipenstein 3D
#
# A work-in-progress experiment
# in creating a raycast 2.5d shooter.
# Uses PWM techniques to simulate extra
# colors and must run at 1000 cycles/frame.
# Epilepsy warning!
#
###########################################
# The largest a map for this approach could be is 16x16.
# Technically we don't have to surround all sides with
# walls due to wraparound, but if there is a ray path
# around the map without hitting a wall our raycast
# routine will get stuck in an infinite loop:
: map-data
0xFF 0x01 0xFF 0x01 0xFF 0x01 0xFF 0x01 0xFF 0x01
0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF
0xFF 0xFF 0x01 0xFF 0x00 0x00 0x00 0x00 0x00 0x01
0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF
0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x01
0x01 0x00 0xFF 0x01 0x00 0x00 0x00 0xFF 0x00 0xFF
0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x01
0x01 0xFF 0x01 0xFF 0x01 0xFF 0x01 0xFF 0x01 0xFF
: collision-test
# take whole x/y in vc/vd, return map color in v0
i := map-data
vF := 0b111
vc &= vF
i += vc # + (x % 8)
vd &= vF
v0 <<= vd
v0 <<= v0
v0 <<= v0
i += v0 # + (y % 8)*8
load v0 # the current map color
;
: player-position
0 0 0 0 # scratchpad
: save-position
v0 := va
v1 := vb
v2 := vc
v3 := vd
i := player-position
save v3
;
: restore-position
i := player-position
load v3
va := v0
vb := v1
vc := v2
vd := v3
;
###########################################
#
# Raycasting
#
###########################################
# This is a table of the deltas and magnitudes for a fixed-point representation
# of a cosine function. 64 entries correspond to ~5.6 degrees each.
# deltas are interleaved with magnitudes- 0 for positive and 1 for negative.
# deltas are normalized to slightly over half a step to make the distance calculations
# come up with a maximum distance of roughly 15. Generated like so:
#
# for(int x = 0; x < 64; x++) {
# double s = Math.cos(Math.PI*2/64*x);
# System.out.format("0x%02X 0x%02X ", (int)(140 * Math.abs(s)), (s>=0) ?0 : 1);
# if (x % 8 == 7) { System.out.println(); }
# }
: cosine-table
0x8C 0x00 0x8B 0x00 0x89 0x00 0x85 0x00 0x81 0x00 0x7B 0x00 0x74 0x00 0x6C 0x00
0x62 0x00 0x58 0x00 0x4D 0x00 0x41 0x00 0x35 0x00 0x28 0x00 0x1B 0x00 0x0D 0x00
0x00 0x00 0x0D 0x01 0x1B 0x01 0x28 0x01 0x35 0x01 0x41 0x01 0x4D 0x01 0x58 0x01
0x62 0x01 0x6C 0x01 0x74 0x01 0x7B 0x01 0x81 0x01 0x85 0x01 0x89 0x01 0x8B 0x01
0x8C 0x01 0x8B 0x01 0x89 0x01 0x85 0x01 0x81 0x01 0x7B 0x01 0x74 0x01 0x6C 0x01
0x62 0x01 0x58 0x01 0x4D 0x01 0x41 0x01 0x35 0x01 0x28 0x01 0x1B 0x01 0x0D 0x01
0x00 0x01 0x0D 0x00 0x1B 0x00 0x28 0x00 0x35 0x00 0x41 0x00 0x4D 0x00 0x58 0x00
0x62 0x00 0x6C 0x00 0x74 0x00 0x7B 0x00 0x81 0x00 0x85 0x00 0x89 0x00 0x8B 0x00
: get-cosine
# takes angle in v0,
# returns delta, magnitude in v0, v1
vf := 0b111111
v0 &= vf # mod 64
v0 <<= v0
i := cosine-table
i += v0
load v1
;
: get-angles
# takes angle in v3,
# unpacks x/y delta/mag into v4-v7
# cos(a) = cosine-table[(a % 64) << 1]
v0 := v3
get-cosine
v4 := v0 # x delta
v5 := v1 # x magnitude
# sin(a) = cosine-table[(a+16 % 64) << 1]
v0 := v3
v0 += 16
get-cosine
v6 := v0 # y delta
v7 := v1 # y magnitude
;
: delta-step
vf := 0
if v5 == 0 then va += v4 # positive x delta
if vf == 1 then vc += 1 # carry in
vf := 0
if v5 == 1 then va -= v4 # negative x delta
if vf == 0 then vc += -1 # borrow out
vf := 0
if v7 == 0 then vb += v6 # positive y delta
if vf == 1 then vd += 1 # carry in
vf := 0
if v7 == 1 then vb -= v6 # negative y delta
if vf == 0 then vd += -1 # borrow out
;
: heights
# {height, color}
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
: raycast
save-position
#v0-v1 are available as scratch.
v8 := 0 # loop index * 2
v3 := v9 # scan angle
v3 += -8
loop
get-angles
v2 := 16 # height+1
loop
delta-step
v2 += -1 # count down height
collision-test # returns result in v0
while v2 != 0
if v0 == 0 then
again
# save height and color in heights table:
i := heights
i += v8
v1 := v0
v0 := v2
save v1
vf := v3
restore-position
v3 := vf
v8 += 2
v3 += 1 # 16 slices * 5.625 = 90 degree FoV
if v8 != 32 then
again
;
###########################################
#
# Rendering
#
###########################################
# this 45-byte table allows me to construct all the necessary vertical
# strips using only immediate i and a height offset:
: top 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
: btm 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
: sync
# take advantage of the fact that vf
# is always a free register if we aren't
# doing any operations that can carry:
loop
vf := delay
if vf != 0 then
again
vf := 1
delay := vf
;
: draw-heights
# registers for draw loop:
# v0 is used to store the current height
# v1 stores the 'color' bitmask
v2 := 0 # heightmap index
v3 := 1 # upward strip Y
v4 := 16 # down strip Y
v5 := 0 # strip X (avoid shifting v1 in loop)
v6 := 15 # constant
sync clear
loop
# fetch height of column
i := heights
i += v2
load v1
# apply PWM duty cycle for 'color'
v1 &= ve
if v1 == 0 then v0 := 0
# draw upward strip
i := top
i += v0
v0 =- v6
sprite v5 v3 15
# draw downward strip
i := btm
i += v0
sprite v5 v4 15
# draw 16 columns
v2 += 2
v5 += 4
if v2 != 32 then
again
;
###########################################
#
# Main Loop
#
###########################################
: gun
0x10 0x18 0x24 0x24 0x24 0x24 0x5A 0xC2 0xE7 0x7F 0x3E 0x21 0x41 0x41
: spin-left
v9 += -1
raycast
;
: spin-right
v9 += 1
raycast
;
: walk-forward
v3 := v9
get-angles
delta-step
raycast
;
: walk-backward
v3 := v9
get-angles
vf := 1
v5 ^= vf
v7 ^= vf
delta-step
raycast
;
: main
# global state:
v9 := 30 # player angle
va := 128 # x position (fractional)
vb := 128 # y position (fractional)
vc := 5 # x position (whole)
vd := 5 # y position (whole)
ve := 0 # rolling frame counter
raycast
loop
draw-heights
i := gun
v0 := 38
v1 := 18
sprite v0 v1 14
ve += 1
v0 := 7
if v0 key then spin-left
v0 := 9
if v0 key then spin-right
v0 := 5
if v0 key then walk-forward
v0 := 8
if v0 key then walk-backward
again
+83
View File
@@ -0,0 +1,83 @@
###########################################
#
# Sprite scrolling demo:
#
# Draw a computer monitor with a scrolling
# image by using two copies of its sprites
# and adjusting an offset into that data
# before each draw.
#
###########################################
: main
# draw the background:
v0 := 16
v1 := 4
i := comp-LT
sprite v0 v1 11
v0 += 8
i := comp-T
sprite v0 v1 3
v0 += 8
sprite v0 v1 3
v0 += 8
i := comp-RT
sprite v0 v1 11
v0 := 16
v1 += 11
i := comp-LB
sprite v0 v1 15
v0 += 8
v1 += 7
i := comp-B
sprite v0 v1 8
v0 += 8
sprite v0 v1 8
v0 += 8
v1 += -7
i := comp-RB
sprite v0 v1 15
# main animation loop:
va := 24 # left x
vb := 32 # right x
vc := 7 # common y
v9 := 0 # scroll offset
v8 := 0b1111 # constant
draw-texture
loop
draw-texture
v9 += 1
v9 &= v8
draw-texture
vF := 4
delay := vF
loop
vF := delay
if vF != 0 then
again
again
: draw-texture
i := grenade-L
i += v9
sprite va vc 15
i := grenade-R
i += v9
sprite vb vc 15
;
: grenade-L 0x0F 0x30 0x7C 0x7C 0xF8 0xF4 0xE0 0xE8 0xF0 0xE8 0xE0 0x68 0x70 0x34 0x08 0x00
0x0F 0x30 0x7C 0x7C 0xF8 0xF4 0xE0 0xE8 0xF0 0xE8 0xE0 0x68 0x70 0x34 0x08 0x00
: grenade-R 0xF0 0x0C 0x46 0x66 0x33 0x13 0x0B 0x0B 0x1F 0x0F 0x0F 0x1E 0x1E 0x1C 0x30 0x00
0xF0 0x0C 0x46 0x66 0x33 0x13 0x0B 0x0B 0x1F 0x0F 0x0F 0x1E 0x1E 0x1C 0x30 0x00
: comp-LT 0x3F 0x3F 0x3F 0x3C 0x3C 0x3C 0x3C 0x3C 0x3C 0x3C 0x3C
: comp-RT 0xFC 0xFC 0xFC 0x3C 0x3C 0x3C 0x3C 0x3C 0x3C 0x3C 0x3C
: comp-T 0xFF 0xFF 0xFF
: comp-LB 0x3C 0x3C 0x3C 0x3C 0x3C 0x3C 0x3C 0x3F 0x3F 0x3F 0x00 0x07 0x1C 0x73 0x7F
: comp-RB 0x3C 0x3C 0x3C 0x3C 0x3C 0x3C 0x3C 0xFC 0xFC 0xFC 0x00 0xE0 0xD8 0x26 0xFE
: comp-B 0xFF 0xFF 0xFF 0xFF 0x33 0xCC 0x33 0xFF
+23
View File
@@ -0,0 +1,23 @@
###########################################
#
# Key input test program.
# Move a dot around the screen in response
# to keypresses.
#
###########################################
: dot
0b10000000
: main
v0 := 10
v1 := 10
i := dot
loop
sprite v0 v1 1
v2 := 7 if v2 key then v0 += -1 # left
v2 := 9 if v2 key then v0 += 1 # right
v2 := 5 if v2 key then v1 += -1 # up
v2 := 8 if v2 key then v1 += 1 # down
again
+30
View File
@@ -0,0 +1,30 @@
###########################################
#
# Smile
#
# Draw smiley faces on the screen randomly
# and periodically clear the display.
#
###########################################
: smile
0b00100100
0b00100100
0b00000000
0b10000001
0b01000010
0b00111100
: main
i := smile
loop
v2 := 0
loop
v0 := random 0b00111111
v1 := random 0b00011111
sprite v0 v1 6
v2 += 1
if v2 != 32 then
again
clear
again
+73
View File
@@ -0,0 +1,73 @@
###########################################
#
# An Octo implementation of an in-place
# selection sort capable of operating on
# arrays of a fixed size up to 256 elements.
#
###########################################
:const SIZE 16
:calc SIZE-1 { SIZE - 1 }
:alias here v1
:alias rest v2
:alias min-index v3
:alias min-value v4
:alias here-value v5
: selection-sort
here := 0
loop
min-index := here
i := data
i += here
load v0
min-value := v0
here-value := v0
rest := here
rest += 1
i := data
i += rest
loop
load v0
if v0 < min-value begin
min-index := rest
min-value := v0
end
rest += 1
if rest != SIZE then
again
if min-index != here begin
v0 := here-value
i := data
i += min-index
save v0
v0 := min-value
i := data
i += here
save v0
end
here += 1
if here != SIZE-1 then
again
;
###########################################
#
# Usage Example
#
###########################################
: data 14 5 15 6 1 3 10 7 0 9 11 4 2 13 8 12
: main
selection-sort
i := data
load vf
:breakpoint sort-complete
;
+116
View File
@@ -0,0 +1,116 @@
###########################################
#
# An Octo implementation of an in-place
# heap sort capable of operating on
# arrays of a fixed size up to 255 elements.
#
###########################################
:const SIZE 16
:calc SIZE-1 { SIZE - 1 }
:calc LIMIT { ( SIZE / 2 ) + 1 }
:alias left-val v0
:alias right-val v1
:alias start v2
:alias root v3
:alias last v4
:alias best v5
:alias left v6
:alias best-val v7
:alias root-val v8
: heap-sort
start := LIMIT
last := SIZE
loop
root := start
sift-down
start += -1
if start != -1 then
again
start := SIZE-1
loop
# swap data[0] with data[start]:
i := data
load v0
vf := v0
i := data
i += start
load v0
i := data
save v0
i := data
i += start
v0 := vf
save v0
start += -1
root := 0
last := start
sift-down
if start != 0 then
again
;
: assign-best
best := left
best-val := left-val
jump found-best
: sift-down
i := data
i += root
load v0
root-val := v0
loop
left <<= root
if left > last then return
i := data
i += left
load v1
best := left
best += 1
best-val := right-val
if left-val > right-val then jump assign-best
if left == last then jump assign-best
: found-best
if root-val >= best-val then return
i := data
i += root
v0 := best-val
save v0
i := data
i += best
v0 := root-val
save v0
root := best
again
###########################################
#
# Usage Example
#
###########################################
: data 14 5 15 6 1 3 10 7 0 9 11 4 2 13 8 12
: main
heap-sort
i := data
load vf
:breakpoint sort-complete
;
+131
View File
@@ -0,0 +1,131 @@
###########################################
#
# An Octo implementation of an unusual
# sorting algorithm which combines an
# optimal sorting network and a merge pass.
# This implementation is designed to sort
# exactly 16 items. The 'sort-8' subroutine
# can be used alone to sort the values
# stored in the bottom 8 registers.
#
###########################################
: sort-8
# compare-and-swap:
:macro cas A B {
if A > B begin
vf := A
A := B
B := vf
end
}
cas v0 v1
cas v2 v3
cas v4 v5
cas v6 v7
cas v0 v2
cas v4 v6
cas v1 v3
cas v0 v4
cas v5 v7
cas v3 v7
cas v1 v5
cas v3 v5
cas v2 v6
cas v2 v4
cas v1 v2
cas v3 v6
cas v2 v4
cas v5 v6
cas v3 v4
;
: heap1 0 0 0 0 0 0 0 0
: heap2 0 0 0 0 0 0 0 0
:alias val1 v8
:alias val2 v9
:alias dest va
:alias index1 vb
:alias index2 vc
: fused-sort
i := data
load v7
sort-8
val1 := v0
i := heap1
save v7
i := data
load v7
load v7 # cheaper than adding an offset of 8
sort-8
val2 := v0
i := heap2
save v7
index1 := 1
index2 := 1
dest := 0
: merge
if val1 > val2 then jump merge-2
append-1
if index1 != 9 then jump merge
loop
append-2
if dest == 16 then return
again
: merge-2
append-2
if index2 != 9 then jump merge
loop
append-1
if dest == 16 then return
again
: append-1
v0 := val1
i := data
i += dest
save v0
dest += 1
i := heap1
i += index1
load v0
val1 := v0
index1 += 1
;
: append-2
v0 := val2
i := data
i += dest
save v0
dest += 1
i := heap2
i += index2
load v0
val2 := v0
index2 += 1
;
###########################################
#
# Usage Example
#
###########################################
: data 14 5 15 6 1 3 10 7 0 9 11 4 2 13 8 12
: main
fused-sort
i := data
load vf
:breakpoint sort-complete
;
+90
View File
@@ -0,0 +1,90 @@
###########################################
#
# An Octo implementation of a
# bucket sort capable of operating on
# arrays of a fixed size up to 256 elements,
# with values ranging 0-15.
# This technique could be generalized
# for full-byte values given a larger
# bucket array.
#
###########################################
:const SIZE 16
:const MAX_VAL 16
: empty
0 0 0 0 0 0 0 0
: buckets
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
: bucket-sort
# zero bucket array
i := empty
load v7
save v7 # unrolled loop to zero bucket array
save v7 # and initialize loop counters for later.
# bucket the elements
# v1 is data index (already 0)
loop
# load data
i := data
i += v1
load v0
vf := v0
# increment bucket
i := buckets
i += vf
load v0
v0 += 1
i := buckets
i += vf
save v0
v1 += 1
if v1 != SIZE then
again
# unpack bucket counts
# v1 is temporary count
# v2 is data index (already 0)
# v3 is bucket index (already 0)
loop
i := buckets
i += v3
load v0
if v0 != 0 begin
v1 := v0
v0 := v3
i := data
i += v2
v2 += v1
loop
save v0
v1 += -1
if v1 != 0 then
again
end
v3 += 1
if v3 != MAX_VAL then
again
;
###########################################
#
# Usage Example
#
###########################################
: data 14 5 15 6 1 3 10 7 0 9 11 4 2 13 8 12
: main
bucket-sort
i := data
load vf
:breakpoint sort-complete
;
+74
View File
@@ -0,0 +1,74 @@
###########################################
#
# A modular stack data structure using
# a v0-based calling convention.
# vf is used as a working temporary
# register because it is "fragile".
# maximum stack size is 256 bytes.
#
###########################################
# the first value is the pointer index
: stack 0 0 0 0 0 0 0 0 0
: push
# modifies vf, v0
# takes argument in v0
# stack[++stack[0] + 1] := v0
i := stack
vf := v0
load v0
i += v0
v0 := vf
save v0
i := stack
load v0
v0 += 1
i := stack
save v0
;
: pop
# modifies vf, v0
# returns result in v0
# v0 := stack[stack[0]-- + 1]
i := stack
load v0
v0 += -1
vf := v0
i := stack
save v0
i += vf
load v0
;
###########################################
#
# Usage Example:
#
###########################################
: print
# takes an arg in v0
i := hex v0
sprite va vb 5
va += 6
;
: main
va := 3
vb := 3
v0 := 5 push
v0 := 3 push
v0 := 1 push
pop print
v0 := 9 push
pop print
pop print
pop print
# should print '1935'
+80
View File
@@ -0,0 +1,80 @@
###########################################
#
# A modular stack data structure using
# a vf-based calling convention.
# This approach can only be extended up to
# a size 16 stack.
#
###########################################
: scratch 0 0 0 0 0 0 0 0
: under 0
: stack 0
: over 0 0 0 0 0 0 0
: push
# here we take our argument from vf.
# and do not corrupt any other registers.
# we can eliminate the scratch saving
# and restoring if low registers don't
# need to be preserved.
i := scratch
save v6
i := stack
load v6
i := over
save v6
i := stack
v0 := vf
save v0
i := scratch
load v6
;
: pop
# result is left in vf.
# again, we don't corrupt any other registers.
i := scratch
save v7
i := stack
load v7
vf := v0
i := under
save v7
i := scratch
load v7
;
###########################################
#
# Usage Example:
#
###########################################
: print
# takes an arg in vf
i := hex vf
sprite va vb 5
va += 6
;
: main
va := 3
vb := 3
vf := 5 push
vf := 3 push
vf := 1 push
pop print
vf := 9 push
pop print
pop print
pop print
# should print '1935'
+55
View File
@@ -0,0 +1,55 @@
###########################################
#
# A modular stack data structure using
# a v0-based calling convention.
# This approach reserves a dedicated
# register for a stack pointer and as
# a result is very simple and fast.
# maximum stack size is 256 bytes.
#
###########################################
:alias stack-ptr vd
: stack 0 0 0 0 0 0 0 0
: push
i := stack
i += stack-ptr
save v0
stack-ptr += 1
;
: pop
stack-ptr += -1
i := stack
i += stack-ptr
load v0
;
###########################################
#
# Usage Example:
#
###########################################
: print
# takes an arg in v0
i := hex v0
sprite va vb 5
va += 6
;
: main
va := 3
vb := 3
v0 := 5 push
v0 := 3 push
v0 := 1 push
pop print
v0 := 9 push
pop print
pop print
pop print
# should print '1935'