Last active
April 17, 2018 04:36
-
-
Save Bananattack/05cd40e570501ffbb495120c7b5b52a9 to your computer and use it in GitHub Desktop.
Top-down NES demo in Wiz, using the current work-in-progress of the language. There are some weird things in here, but hopefully some revisions will iron out some of the strange stuff.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "ram"; | |
import "entity"; | |
import "directions"; | |
import "random"; | |
namespace ai_wander { | |
const direction_wait_base : [u8] = [60]; | |
const direction_wait_random : [u8] = [60]; | |
const wander_speed_lo : [u8] = [128]; | |
const wander_speed_hi : [u8] = [0]; | |
let direction_timer = entity.v1; | |
let wander_settting = entity.v2; | |
func init() { | |
random.next(); | |
entity.direction[x] = a = a & 0x3; | |
} | |
func update() { | |
a = direction_timer[x]; | |
if zero { | |
entity.direction[x] = a = (entity.direction[x] & 0x2) ^ 0x2; | |
random.next(); | |
entity.direction[x] = a = a & 1 | entity.direction[x]; | |
random.next(); | |
y = wander_settting[x]; | |
while a >= direction_wait_random[y] { | |
a -= direction_wait_random[y]; | |
} | |
direction_timer[x] = a = a + direction_wait_base[y]; | |
} else { | |
direction_timer[x]--; | |
} | |
t0 = a = 0; | |
t1 = a; | |
t2 = a; | |
t3 = a; | |
y = entity.direction[x]; | |
a = directions.x_offset[y]; | |
if !zero { | |
if negative { | |
y = wander_settting[x]; | |
t0 = a = -wander_speed_lo[y]; | |
t1 = a = (wander_speed_hi[y] ^ 0xFF) +# 0; | |
} else { | |
y = wander_settting[x]; | |
t0 = a = wander_speed_lo[y]; | |
t1 = a = wander_speed_hi[y]; | |
} | |
} | |
y = entity.direction[x]; | |
a = directions.y_offset[y]; | |
if !zero { | |
if negative { | |
y = wander_settting[x]; | |
t2 = a = -wander_speed_lo[y]; | |
t3 = a = (wander_speed_hi[y] ^ 0xFF) +# 0; | |
} else { | |
y = wander_settting[x]; | |
t2 = a = wander_speed_lo[y]; | |
t3 = a = wander_speed_hi[y]; | |
} | |
} | |
entity.move(); | |
a = t15; | |
if zero { | |
direction_timer[x] = a = 0; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "nes"; | |
bank zeropage @ 0x00 : [wiz.ram; 256]; | |
bank stack @ 0x100 : [wiz.ram; 256]; | |
bank ram @ 0x200 : [wiz.ram; 1536]; | |
bank header : [wiz.prg; 16]; | |
bank prg @ 0x8000 : [wiz.prg; 32768]; | |
bank chr : [wiz.chr; 8192]; | |
in header { | |
let HEADER_MIRRORING = 0; | |
let HEADER_BATTERY = 0; | |
let HEADER_FOURSCREEN = 0; | |
let HEADER_MAPPER = 0; | |
// 0..3: "NES" followed by MS-DOS end-of-file marker. | |
const = "NES\x1A" ~ [ | |
// 4: Number of 16K PRG ROM banks | |
2, | |
// 5: Number of 8K CHR ROM banks | |
1, | |
// 6: The "Flags 6" byte, skip the 'trainer' flag for now. | |
(HEADER_MIRRORING) | (HEADER_BATTERY << 1) | (HEADER_FOURSCREEN << 3) | ((HEADER_MAPPER & 0xF) << 4), | |
// 7: The "Flags 7" byte, just the mapper part though. | |
(HEADER_MAPPER >> 4), | |
// 8: Number of 8K PRG RAM banks -- for now just write a 0, which implies 8KB PRG RAM at most. | |
0, | |
// 9..15: Ignore other flag fields. Zero-pad this header to 16 bytes. | |
0, 0, 0, 0, 0, 0, 0 | |
]; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "ram"; | |
import "nes"; | |
import "data"; | |
#[noreturn] func call_t0() { | |
goto t0; | |
} | |
func clear_nametable_vram() { | |
a = nes.ppu.status; | |
nes.ppu.address = a = (&nes.vram.nametable0 as u16 >> 8) as u8; | |
nes.ppu.address = a = &nes.vram.nametable0 as u8; | |
x = 0; | |
y = 8; | |
a = 0; | |
do { | |
do { | |
nes.ppu.data = a; | |
x++; | |
} while !zero; | |
y--; | |
} while !zero; | |
} | |
func load_tilemap() { | |
a = nes.ppu.status; | |
nes.ppu.address = a = (&nes.vram.nametable0 as u16 >> 8) as u8; | |
nes.ppu.address = a = &nes.vram.nametable0 as u8; | |
y = 0; | |
do { | |
a = tilemap_ptr[y]; | |
// 0 | |
a <<= 1; | |
if carry { | |
x = tilemap_fg_tile; | |
} else { | |
x = tilemap_bg_tile; | |
} | |
nes.ppu.data = x; | |
// 1 | |
a <<= 1; | |
if carry { | |
x = tilemap_fg_tile; | |
} else { | |
x = tilemap_bg_tile; | |
} | |
nes.ppu.data = x; | |
// 2 | |
a <<= 1; | |
if carry { | |
x = tilemap_fg_tile; | |
} else { | |
x = tilemap_bg_tile; | |
} | |
nes.ppu.data = x; | |
// 3 | |
a <<= 1; | |
if carry { | |
x = tilemap_fg_tile; | |
} else { | |
x = tilemap_bg_tile; | |
} | |
nes.ppu.data = x; | |
// 4 | |
a <<= 1; | |
if carry { | |
x = tilemap_fg_tile; | |
} else { | |
x = tilemap_bg_tile; | |
} | |
nes.ppu.data = x; | |
// 5 | |
a <<= 1; | |
if carry { | |
x = tilemap_fg_tile; | |
} else { | |
x = tilemap_bg_tile; | |
} | |
nes.ppu.data = x; | |
// 6 | |
a <<= 1; | |
if carry { | |
x = tilemap_fg_tile; | |
} else { | |
x = tilemap_bg_tile; | |
} | |
nes.ppu.data = x; | |
// 7 | |
a <<= 1; | |
if carry { | |
x = tilemap_fg_tile; | |
} else { | |
x = tilemap_bg_tile; | |
} | |
nes.ppu.data = x; | |
y++; | |
} while y != 120; | |
} | |
func load_palette() { | |
a = nes.ppu.status; | |
nes.ppu.address = a = (&nes.vram.palette as u16 >> 8) as u8; | |
nes.ppu.address = a = &nes.vram.palette as u8; | |
x = 0; | |
do { | |
nes.ppu.data = a = data.palette[x]; | |
x++; | |
} while x != nes.vram.PALETTE_SIZE * nes.vram.PALETTE_TOTAL; | |
} | |
func print_text() { | |
let START_X = 10; | |
let START_Y = 3; | |
let TILE_ADDRESS = &nes.vram.nametable0 as u16 + START_Y * 32 + START_X; | |
// Read PPU status to reset its state. | |
a = nes.ppu.status; | |
// Now setup the PPU for copying tiles. | |
nes.ppu.address = a = (TILE_ADDRESS >> 8) as u8; | |
nes.ppu.address = a = TILE_ADDRESS as u8; | |
x = 0; | |
while true { | |
a = data.message[x]; | |
break if zero; | |
x++; | |
nes.ppu.data = a; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace data { | |
const message = "WOW NICE\0"; | |
const palette : [u8] = [ | |
// Tiles | |
0x0F, 0x05, 0x26, 0x27, | |
0x0F, 0x00, 0x10, 0x30, | |
0x0F, 0x00, 0x10, 0x30, | |
0x0F, 0x00, 0x10, 0x30, | |
// Sprites | |
0x0F, 0x03, 0x21, 0x26, | |
0x0F, 0x00, 0x10, 0x30, | |
0x0F, 0x00, 0x10, 0x30, | |
0x0F, 0x00, 0x10, 0x30 | |
]; | |
const level1 : [u8] = [ | |
0b00000000, 0b00000000, 0b00000000, 0b00000000, | |
0b00000000, 0b00000000, 0b00000000, 0b00000000, | |
0b00000000, 0b00000000, 0b00000000, 0b00000000, | |
0b00000000, 0b00000000, 0b00000000, 0b00000000, | |
0b11111111, 0b11111111, 0b11111111, 0b11111111, | |
0b11111111, 0b11111111, 0b11111111, 0b11111111, | |
0b11000000, 0b01100000, 0b00000000, 0b00000011, | |
0b11000000, 0b01100000, 0b00000000, 0b00000011, | |
0b11000000, 0b00000000, 0b00000000, 0b00110011, | |
0b11000000, 0b00000000, 0b00000000, 0b00110011, | |
0b11110000, 0b00000110, 0b00000000, 0b00000011, | |
0b11110000, 0b00000110, 0b00000000, 0b00000011, | |
0b11000000, 0b00000000, 0b01111110, 0b00000011, | |
0b11000000, 0b00000000, 0b01111110, 0b00000011, | |
0b11000110, 0b00000000, 0b01111110, 0b00000011, | |
0b11000110, 0b00000000, 0b01111110, 0b00000011, | |
0b11000000, 0b00000000, 0b00000000, 0b00000011, | |
0b11000000, 0b00000000, 0b00000000, 0b00000011, | |
0b11000000, 0b00000000, 0b00000000, 0b00001111, | |
0b11000000, 0b01111110, 0b00000000, 0b00001111, | |
0b11000000, 0b01111110, 0b00000000, 0b00000011, | |
0b11000000, 0b01111110, 0b00000000, 0b01100011, | |
0b11000000, 0b01111110, 0b00000000, 0b01100011, | |
0b11000000, 0b00000000, 0b00000000, 0b00000011, | |
0b11000000, 0b00000000, 0b00000110, 0b00000011, | |
0b11000000, 0b00000000, 0b00000110, 0b00000011, | |
0b11111111, 0b11111111, 0b11111111, 0b11111111, | |
0b11111111, 0b11111111, 0b11111111, 0b11111111, | |
0b00000000, 0b00000000, 0b00000000, 0b00000000, | |
0b00000000, 0b00000000, 0b00000000, 0b00000000, | |
]; | |
const spawns : [u8] = [ | |
5, 8, | |
12, 12, | |
22, 9, | |
6, 20, | |
20, 20, | |
6, 9, | |
12, 12, | |
22, 9, | |
6, 20, | |
20, 20, | |
5, 8, | |
12, 12, | |
22, 9, | |
6, 20, | |
20, 20, | |
5, 8, | |
12, 12, | |
22, 9, | |
6, 20, | |
20, 20, | |
5, 8, | |
12, 12, | |
22, 9, | |
6, 20, | |
20, 20, | |
5, 8, | |
12, 12, | |
22, 9, | |
6, 20, | |
20, 20, | |
5, 8, | |
12, 12, | |
22, 9, | |
6, 20, | |
20, 20, | |
0xFF, | |
]; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace directions { | |
let LEFT = 0; | |
let RIGHT = 1; | |
let UP = 2; | |
let DOWN = 3; | |
const x_offset : [u8] = [-1 as u8 as iexpr, 1, 0, 0]; | |
const y_offset : [u8] = [0, 0, -1 as u8 as iexpr, 1]; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "ram"; | |
import "nes"; | |
import "common"; | |
namespace entity { | |
func init_system() { | |
x = 0; | |
do { | |
init_ent(); | |
x++; | |
} while x < COUNT; | |
flicker_frame = a = 0; | |
} | |
func init_ent() { | |
a = 0; | |
flags[x] = a; | |
subpixel_x[x] = a; | |
subpixel_y[x] = a; | |
pixel_x[x] = a; | |
pixel_y[x] = a; | |
hp[x] = a; | |
max_hp[x] = a; | |
anim_timer[x] = a; | |
frame[x] = a; | |
attributes[x] = a; | |
update_lo[x] = a; | |
update_hi[x] = a; | |
direction[x] = a; | |
v1[x] = a; | |
v2[x] = a; | |
v3[x] = a; | |
v4[x] = a; | |
} | |
func spawn() { | |
x = 0; | |
do { | |
a = flags[x] & FLAGS_ACTIVE; | |
if zero { | |
break; | |
} | |
x++; | |
} while x < COUNT; | |
init_ent(); | |
flags[x] = a = FLAGS_ACTIVE; | |
} | |
func draw_all() { | |
x = 0; | |
y = flicker_frame; | |
do { | |
oam_buffer[y] = a = pixel_y[x]; y++; | |
oam_buffer[y] = a = frame[x]; y++; | |
oam_buffer[y] = a = attributes[x]; y++; | |
oam_buffer[y] = a = pixel_x[x]; y++; | |
y = a = y + (18 * 4); | |
x++; | |
} while x < COUNT; | |
flicker_frame = a = flicker_frame + 19 * 4; | |
} | |
func update_all() { | |
current = x = 0; | |
do { | |
a = flags[x] & FLAGS_ACTIVE; | |
if !zero { | |
t0 = a = update_lo[x]; | |
t1 = a = update_hi[x]; | |
a = t0 | t1; | |
if !zero { | |
call_t0(); | |
} | |
} | |
current++; | |
x = current; | |
} while x < COUNT; | |
} | |
func move() { | |
t15 = a = 0; | |
a = t0 | t1; | |
if zero { | |
} else { | |
let temp_x = t4; | |
let temp_y = t5; | |
let temp_y2 = t6; | |
temp_x = a = entity.pixel_x[x]; | |
a = t1; | |
if negative { | |
a = temp_x - 1; | |
} else { | |
a = temp_x + 8; | |
} | |
temp_x = a = a >>> 3; | |
temp_y = a = entity.pixel_y[x]; | |
temp_y2 = a = a + 7; | |
temp_y = a = temp_y >>> 3; | |
temp_y2 = a = temp_y2 >>> 3; | |
let index = t7; | |
index = a = temp_y << 2; | |
y = a = (temp_x >>> 3) + index; | |
a = tilemap_ptr[y]; | |
push(a); | |
y = a = temp_x & 0x7; | |
a = pop(); | |
while y != 0 { | |
a = a <<<< 1; | |
y--; | |
} | |
a &= 0x80; | |
if zero { | |
index = a = temp_y2 << 2; | |
y = a = (temp_x >>> 3) + index; | |
a = tilemap_ptr[y]; | |
push(a); | |
y = a = temp_x & 0x7; | |
a = pop(); | |
while y != 0 { | |
a = a <<<< 1; | |
y--; | |
} | |
a &= 0x80; | |
if zero { | |
t15++; | |
entity.subpixel_x[x] = a = entity.subpixel_x[x] + t0; | |
entity.pixel_x[x] = a = entity.pixel_x[x] +# t1; | |
} | |
} | |
} | |
a = t2 | t3; | |
if zero { | |
} else { | |
let temp_y = t4; | |
let temp_x = t5; | |
let temp_x2 = t6; | |
temp_y = a = entity.pixel_y[x]; | |
a = t3; | |
if negative { | |
a = temp_y - 1; | |
} else { | |
a = temp_y + 8; | |
} | |
temp_y = a = a >>> 3; | |
temp_x = a = entity.pixel_x[x]; | |
temp_x2 = a = a + 7; | |
temp_x = a = temp_x >>> 3; | |
temp_x2 = a = temp_x2 >>> 3; | |
let index = t7; | |
index = a = temp_y << 2; | |
y = a = (temp_x >>> 3) + index; | |
a = tilemap_ptr[y]; | |
push(a); | |
y = a = temp_x & 0x7; | |
a = pop(); | |
while y != 0 { | |
a = a <<<< 1; | |
y--; | |
} | |
a &= 0x80; | |
if zero { | |
y = a = (temp_x2 >>> 3) + index; | |
a = tilemap_ptr[y]; | |
push(a); | |
y = a = temp_x2 & 0x7; | |
a = pop(); | |
while y != 0 { | |
a = a <<<< 1; | |
y--; | |
} | |
a &= 0x80; | |
if zero { | |
t15++; | |
entity.subpixel_y[x] = a = entity.subpixel_y[x] + t2; | |
entity.pixel_y[x] = a = entity.pixel_y[x] +# t3; | |
} | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "ram"; | |
import "entity"; | |
import "directions"; | |
import "joy"; | |
import "nes"; | |
namespace hero { | |
func spawn() { | |
entity.spawn(); | |
entity.max_hp[x] = a = 3; | |
entity.hp[x] = a; | |
entity.pixel_x[x] = a = START_X * 8; | |
entity.pixel_y[x] = a = START_Y * 8; | |
entity.update_lo[x] = a = update as u8; | |
entity.update_hi[x] = a = (update as u16 >> 8) as u8; | |
entity.frame[x] = a = 19; | |
entity.direction[x] = a = directions.DOWN; | |
} | |
func update() { | |
let SPEED = 192; | |
let DIAGONAL_SPEED = 132; | |
let x_speed_lo = t0; | |
let x_speed_hi = t1; | |
let y_speed_lo = t2; | |
let y_speed_hi = t3; | |
let frame = t4; | |
x_speed_lo = a = 0; | |
x_speed_hi = a; | |
y_speed_lo = a; | |
y_speed_hi = a; | |
a = joy.controls & nes.joy.LEFT; | |
if !zero { | |
x_speed_lo = a = -SPEED as u8; | |
x_speed_hi = a = (-SPEED >> 8) as u8; | |
entity.direction[x] = a = directions.LEFT; | |
} | |
a = joy.controls & nes.joy.RIGHT; | |
if !zero { | |
x_speed_lo = a = SPEED as u8; | |
x_speed_hi = a = (SPEED >> 8) as u8; | |
entity.direction[x] = a = directions.RIGHT; | |
} | |
a = joy.controls & nes.joy.UP; | |
if !zero { | |
y_speed_lo = a = -SPEED as u8; | |
y_speed_hi = a = (-SPEED >> 8) as u8; | |
entity.direction[x] = a = directions.UP; | |
} | |
a = joy.controls & nes.joy.DOWN; | |
if !zero { | |
y_speed_lo = a = SPEED as u8; | |
y_speed_hi = a = (SPEED >> 8) as u8; | |
entity.direction[x] = a = directions.DOWN; | |
} | |
a = x_speed_lo | x_speed_hi; | |
if !zero { | |
a = y_speed_lo | y_speed_hi; | |
if !zero { | |
a = x_speed_hi; | |
if negative { | |
x_speed_lo = a = (-DIAGONAL_SPEED) as u8; | |
x_speed_hi = a = (-DIAGONAL_SPEED >> 8) as u8; | |
} else { | |
x_speed_lo = a = DIAGONAL_SPEED as u8; | |
x_speed_hi = a = (DIAGONAL_SPEED >> 8) as u8; | |
} | |
a = y_speed_hi; | |
if negative { | |
y_speed_lo = a = (-DIAGONAL_SPEED) as u8; | |
y_speed_hi = a = (-DIAGONAL_SPEED >> 8) as u8; | |
} else { | |
y_speed_lo = a = DIAGONAL_SPEED as u8; | |
y_speed_hi = a = (DIAGONAL_SPEED >> 8) as u8; | |
} | |
} | |
} | |
entity.move(); | |
entity.anim_timer[x]++; | |
frame = a = 0; | |
a = entity.anim_timer[x]; | |
if a >= 20 { | |
if a >= 40 { | |
entity.anim_timer[x] = a = 0; | |
} | |
frame++; | |
} | |
entity.attributes[x] = a = entity.attributes[x] & ~nes.ppu.oam.ATTRIBUTE_HFLIP; | |
a = entity.direction[x]; | |
if a < 2 { | |
if a == 1 { | |
entity.attributes[x] = a = entity.attributes[x] | nes.ppu.oam.ATTRIBUTE_HFLIP; | |
} | |
entity.frame[x] = a = frame + 16; | |
} else { | |
entity.frame[x] = a = a + 16; | |
a = frame; | |
if !zero { | |
entity.attributes[x] = a = entity.attributes[x] | nes.ppu.oam.ATTRIBUTE_HFLIP; | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "ram"; | |
import "nes"; | |
namespace joy { | |
func init() { | |
unpress = a = 0; | |
controls = a = 0; | |
} | |
// Reads controller and stores the result in controls | |
// Derived from code posted by blargg on nesdevwiki, added unpress mask. | |
func update() { | |
// Strobe controller | |
nes.joy.out = a = 1; | |
nes.joy.out = a = 0; | |
// Read all 8 buttons | |
x = 8; | |
do { | |
// Read next button state and mask off low 2 bits. | |
// Compare with $01, which will set carry flag if | |
// either or both bits are set. | |
a = nes.joy.in1 & 0x3; | |
cmp(a, 1); | |
// Now, rotate the carry flag into the top of A, | |
// land shift all the other buttons to the right | |
controls = a = controls >>>> 1; | |
x--; | |
} while !zero; | |
// Remove unpress flag for controls no longer being held. | |
unpress = a = unpress & controls; | |
// Remove controls that have the unpress flag set. | |
// (Keep controls that don't have the unpress flag set) | |
controls = a = ~unpress & controls; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "banks"; | |
import "ram"; | |
import "nes"; | |
in prg { | |
import "entity"; | |
import "common"; | |
import "slime"; | |
import "hero"; | |
import "data"; | |
import "joy"; | |
import "random"; | |
import "data"; | |
#[noreturn] func main() { | |
// Disable decimal arithmetic (though not actually supported on 2A03 anyway) | |
decimal = false; | |
// Disable interrutps. | |
nointerrupt = true; | |
// Prepare stack. | |
s = x = 0xFF; | |
// Turn off rendering. | |
nes.ppu.ctrl = x = 0; | |
nes.ppu.mask = x; | |
// Disable DMC interrupts. | |
nes.apu.sequencer = a = nes.apu.SEQUENCER_DISABLE_IRQ; | |
// Wait for the PPU to be ready to use, which takes 2 vertical blanks. | |
do { | |
do {} while !nes.ppu.status$7; | |
x++; | |
} while x != 2; | |
x = 0; | |
do { | |
(0 as *u8)[x] = a = 0x00; | |
(0x200 as *u8)[x] = a = 0xFE; | |
x++; | |
} while !zero; | |
load_palette(); | |
clear_nametable_vram(); | |
let BG_TILE = 0; | |
let FG_TILE = 8; | |
tilemap_bg_tile = a = BG_TILE; | |
tilemap_fg_tile = a = FG_TILE; | |
*((&tilemap_ptr as u16) as *u8) = a = (&data.level1[0] as u16) as u8; | |
*((&tilemap_ptr as u16 + 1) as *u8) = a = (&data.level1[0] as u16 >> 8) as u8; | |
load_tilemap(); | |
print_text(); | |
joy.init(); | |
entity.init_system(); | |
random.init(); | |
hero.spawn(); | |
y = 0; | |
while true { | |
a = data.spawns[y]; | |
break if a == 0xFF; | |
t0 = a << 3; | |
y++; | |
t1 = a = data.spawns[y] << 3; | |
y++; | |
a = y; push(a); | |
slime.spawn(); | |
y = a = pop(); | |
} | |
a = nes.ppu.status; | |
nes.ppu.scroll = a = 0; | |
nes.ppu.scroll = a; | |
// We're finally ready to show the screen! | |
nes.ppu.ctrl = a = nes.ppu.CTRL_NMI | nes.ppu.CTRL_SPRITE_PATTERN_1; | |
nes.ppu.mask = a = nes.ppu.MASK_LEFTMOST_BG | nes.ppu.MASK_RENDER_BG | nes.ppu.MASK_LEFTMOST_SPRITES | nes.ppu.MASK_RENDER_SPRITES; | |
// Enable interrupts. | |
nointerrupt = false; | |
while true { | |
joy.update(); | |
entity.update_all(); | |
x = 0; | |
do { | |
(0x200 as *u8)[x] = a = 0xFE; | |
x++; | |
} while !zero; | |
entity.draw_all(); | |
random.next(); | |
draw_request = a = DRAW_REQUEST_WAIT_FRAME | DRAW_REQUEST_UPDATE_SPRITES; | |
do { | |
a = draw_request; | |
} while !zero; | |
} | |
} | |
#[nmi] func draw() { | |
push(a); | |
a = x; push(a); | |
a = y; push(a); | |
a = nes.ppu.status; | |
nes.ppu.scroll = a = 0; | |
nes.ppu.scroll = a = 0; | |
a = draw_request & DRAW_REQUEST_UPDATE_SPRITES; | |
if !zero { | |
nes.ppu.oam.address = a = &oam_buffer as u8; | |
nes.ppu.oam.dma = a = (&oam_buffer as u16 >> 8) as u8; | |
} | |
draw_request = a = 0; | |
y = a = pop(); | |
x = a = pop(); | |
a = pop(); | |
} | |
#[irq] func scanline() { | |
push(a); | |
a = x; push(a); | |
a = y; push(a); | |
y = a = pop(); | |
x = a = pop(); | |
a = pop(); | |
} | |
const @ 0xFFFA = [draw, main, scanline]; | |
} | |
in chr { | |
const = embed "../common/bobble_tiles.chr"; | |
const = embed "../common/minirpg_sprites.chr"; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace nes { | |
namespace ppu { | |
namespace oam { | |
var address @ 0x2003 : u8; | |
var data @ 0x2004 : u8; | |
var dma @ 0x4014 : u8; | |
let ATTRIBUTE_PAL_0 = 0x0u8; | |
let ATTRIBUTE_PAL_1 = 0x1u8; | |
let ATTRIBUTE_PAL_2 = 0x2u8; | |
let ATTRIBUTE_PAL_3 = 0x3u8; | |
// 0x04, 0x8, 0x10 are unused | |
let ATTRIBUTE_BEHIND = 0x20u8; | |
let ATTRIBUTE_HFLIP = 0x40u8; | |
let ATTRIBUTE_VFLIP = 0x80u8; | |
} | |
var ctrl @ 0x2000 : u8; | |
var mask @ 0x2001 : u8; | |
var status @ 0x2002 : u8; | |
// oam stuff in separate namespace. | |
var scroll @ 0x2005 : u8; | |
var address @ 0x2006 : u8; | |
var data @ 0x2007 : u8; | |
let CTRL_NAMETABLE_0 = 0x00u8; | |
let CTRL_NAMETABLE_1 = 0x01u8; | |
let CTRL_NAMETABLE_2 = 0x02u8; | |
let CTRL_NAMETABLE_3 = 0x03u8; | |
let CTRL_VRAM_STEP_X = 0x00u8; | |
let CTRL_VRAM_STEP_Y = 0x04u8; | |
let CTRL_SPRITE_PATTERN_0 = 0x00u8; | |
let CTRL_SPRITE_PATTERN_1 = 0x08u8; | |
let CTRL_BG_PATTERN_0 = 0x00u8; | |
let CTRL_BG_PATTERN_1 = 0x10u8; | |
let CTRL_SPRITE_8x8 = 0x00u8; | |
let CTRL_SPRITE_8x16 = 0x20u8; | |
// ppu ctrl flag 0x40 is unused | |
let CTRL_DISABLE = 0x00u8; | |
let CTRL_NMI = 0x80u8; | |
let MASK_GREYSCALE = 0x01u8; | |
let MASK_LEFTMOST_BG = 0x02u8; | |
let MASK_LEFTMOST_SPRITES = 0x04u8; | |
let MASK_RENDER_BG = 0x08u8; | |
let MASK_RENDER_SPRITES = 0x10u8; | |
let MASK_INTENSIFY_R = 0x20u8; | |
let MASK_INTENSIFY_G = 0x40u8; | |
let MASK_INTENSIFY_B = 0x80u8; | |
// ppu status flags 0x00..0x10 are unused | |
let STATUS_SPRITE_OVERFLOW = 0x20u8; | |
let STATUS_SPRITE_ZERO_HIT = 0x40u8; | |
let STATUS_VBLANK = 0x80u8; | |
} | |
namespace vram { | |
// Size of each pattern table in bytes. | |
let PATTERN_SIZE = 0x1000; | |
// Total number of pattern tables. | |
let PATTERN_TOTAL = 2; | |
var pattern0 @ 0x0000 : u8; | |
var pattern1 @ 0x1000 : u8; | |
// Offset to the attribute section of a nametable. | |
let NAMETABLE_ATTRIBUTE_OFFSET = 0x3C0; | |
// Size of each nametable in bytes. | |
let NAMETABLE_SIZE = 0x400; | |
// Total number of nametables. | |
let NAMETABLE_TOTAL = 4; | |
// Base palette address. | |
var nametable0 @ 0x2000 : u8; | |
var nametable1 @ 0x2400 : u8; | |
var nametable2 @ 0x2800 : u8; | |
var nametable3 @ 0x2C00 : u8; | |
var attribute0 @ 0x23C0 : u8; | |
var attribute1 @ 0x27C0 : u8; | |
var attribute2 @ 0x2BC0 : u8; | |
var attribute3 @ 0x2FC0 : u8; | |
// Size of each palette in bytes. | |
let PALETTE_SIZE = 4; | |
// Total number of palettes. | |
let PALETTE_TOTAL = 8; | |
// Base palette address. | |
var palette @ 0x3F00 : u8; | |
} | |
namespace apu { | |
namespace square1 { | |
var ctrl @ 0x4000 : u8; | |
var sweep @ 0x4001 : u8; | |
var low @ 0x4002 : u8; | |
var high @ 0x4003 : u8; | |
} | |
namespace square2 { | |
var ctrl @ 0x4004 : u8; | |
var sweep @ 0x4005 : u8; | |
var low @ 0x4006 : u8; | |
var high @ 0x4007 : u8; | |
} | |
namespace triangle { | |
var ctrl @ 0x4008 : u8; | |
// triangle register 0x4009 is unused | |
var low @ 0x400A : u8; | |
var high @ 0x400B : u8; | |
} | |
namespace noise { | |
var ctrl @ 0x400C : u8; | |
// noise register 0x400D is unused | |
var random @ 0x400E : u8; | |
var length @ 0x400F : u8; | |
} | |
var flag @ 0x4015 : u8; | |
var sequencer @ 0x4017 : u8; | |
let FLAG_SQUARE1 = 0x01u8; | |
let FLAG_SQUARE2 = 0x02u8; | |
let FLAG_TRIANGLE = 0x04u8; | |
let FLAG_NOISE = 0x08u8; | |
let FLAG_DMC_RESTART = 0x10u8; | |
// apu flag 0x20 is unused. | |
let FLAG_FRAME_IRQ = 0x40u8; | |
let FLAG_DMC_IRQ = 0x80u8; | |
let SEQUENCER_FOUR_STEP = 0x00u8; | |
let SEQUENCER_FIVE_STEP = 0x40u8; | |
let SEQUENCER_DISABLE_IRQ = 0x80u8; | |
} | |
namespace joy { | |
var out @ 0x4016 : u8; | |
var in1 @ 0x4016 : u8; | |
var in2 @ 0x4017 : u8; | |
let A = 0x01u8; | |
let B = 0x02u8; | |
let SELECT = 0x04u8; | |
let START = 0x08u8; | |
let UP = 0x10u8; | |
let DOWN = 0x20u8; | |
let LEFT = 0x40u8; | |
let RIGHT = 0x80u8; | |
} | |
namespace mmc3 { | |
namespace irq { | |
// This register specifies the IRQ counter reload value. | |
// When the IRQ counter is zero (or a reload is requested through 0xC001), | |
// this value will be copied into the MMC3 IRQ counter at | |
// the end of the current scanline | |
var latch @ 0xC000 : u8; | |
// Writing any value to this register clears the MMC3 IRQ counter | |
// so that it will be reloaded at the end of the current scanline. | |
var reload @ 0xC001 : u8; | |
// Writing any value to this register will disable MMC3 interrupts | |
// AND acknowledge any pending interrupts. | |
var disable @ 0xE000 : u8; | |
// Writing any value to this register will enable MMC3 interrupts. | |
var enable @ 0xE001 : u8; | |
} | |
var select @ 0x8000 : u8; | |
var data @ 0x8001 : u8; | |
var mirror @ 0xA000 : u8; | |
var wram @ 0xA001 : u8; | |
let SELECT_2K_CHR_00 = 0x00u8; | |
let SELECT_2K_CHR_08 = 0x01u8; | |
let SELECT_1K_CHR_10 = 0x02u8; | |
let SELECT_1K_CHR_14 = 0x03u8; | |
let SELECT_1K_CHR_18 = 0x04u8; | |
let SELECT_1K_CHR_1C = 0x05u8; | |
let SELECT_8K_PRG_80 = 0x06u8; | |
let SELECT_8K_PRG_A0 = 0x07u8; | |
let SELECT_PRG_SWAP = 0x40u8; | |
let SELECT_CHR_SWAP = 0x80u8; | |
let MIRROR_VERTICAL = 0x00u8; | |
let MIRROR_HORIZONTAL = 0x1u8; | |
let RAM_READONLY = 0x40u8; | |
let WRAM_ENABLE = 0x80u8; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "banks"; | |
in zeropage { | |
var t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15 : u8; | |
var tilemap_ptr : *u8; | |
var tilemap_bg_tile : u8; | |
var tilemap_fg_tile : u8; | |
let DRAW_REQUEST_WAIT_FRAME = 0x01; | |
let DRAW_REQUEST_UPDATE_SPRITES = 0x02; | |
var draw_request : u8; | |
namespace joy { | |
var unpress : u8; | |
var controls : u8; | |
} | |
} | |
in ram @ 0x200 { | |
var oam_buffer : [u8; 256]; | |
} | |
in ram @ 0x300 { | |
namespace entity { | |
let COUNT = 32; | |
let FLAGS_ACTIVE = 0x01; | |
let FLAGS_HOSTILE = 0x02; | |
var flags : [u8; COUNT + 1]; | |
var subpixel_x : [u8; COUNT + 1]; | |
var subpixel_y : [u8; COUNT + 1]; | |
var pixel_x : [u8; COUNT + 1]; | |
var pixel_y : [u8; COUNT + 1]; | |
var hp : [u8; COUNT + 1]; | |
var max_hp : [u8; COUNT + 1]; | |
var anim_timer : [u8; COUNT + 1]; | |
var frame : [u8; COUNT + 1]; | |
var attributes : [u8; COUNT + 1]; | |
var update_lo : [u8; COUNT + 1]; | |
var update_hi : [u8; COUNT + 1]; | |
var direction : [u8; COUNT + 1]; | |
var v1 : [u8; COUNT + 1]; | |
var v2 : [u8; COUNT + 1]; | |
var v3 : [u8; COUNT + 1]; | |
var v4 : [u8; COUNT + 1]; | |
var current : u8; | |
var flicker_frame : u8; | |
} | |
namespace hero { | |
let START_X = 8; | |
let START_Y = 8; | |
} | |
namespace random { | |
var index : u8; | |
var value : u8; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "ram"; | |
namespace random { | |
const table : [u8] = [92, 172, 23, 203, 56, 84, 161, 225, 57, 62, 142, 135, 74, 16, 88, 12, 228, 227, 91, 2, 27, 99, 100, 116, 107, 58, 110, 15, 18, 162, 247, 7, 219, 255, 108, 175, 52, 151, 130, 94, 69, 60, 133, 177, 115, 95, 173, 231, 51, 204, 199, 68, 33, 47, 214, 13, 83, 89, 181, 242, 96, 112, 104, 25, 102, 132, 190, 179, 10, 166, 222, 226, 8, 24, 165, 238, 244, 240, 4, 220, 152, 131, 141, 1, 106, 55, 117, 139, 49, 170, 50, 93, 75, 233, 66, 194, 212, 103, 129, 198, 64, 163, 224, 5, 241, 178, 76, 234, 79, 28, 195, 114, 144, 207, 218, 101, 41, 128, 37, 137, 158, 171, 211, 70, 85, 188, 217, 249, 97, 118, 138, 59, 184, 143, 63, 202, 6, 126, 245, 200, 42, 159, 136, 90, 148, 48, 180, 185, 210, 19, 235, 209, 169, 32, 205, 121, 221, 164, 71, 150, 153, 156, 11, 223, 155, 81, 3, 254, 17, 206, 67, 167, 29, 54, 140, 174, 239, 186, 236, 191, 43, 87, 61, 73, 160, 82, 230, 168, 111, 248, 113, 157, 44, 38, 77, 125, 176, 193, 30, 40, 105, 243, 127, 182, 145, 21, 122, 45, 250, 183, 208, 36, 192, 14, 201, 246, 146, 197, 124, 53, 252, 253, 78, 232, 215, 39, 134, 35, 123, 20, 9, 26, 22, 109, 213, 120, 147, 34, 229, 86, 72, 149, 98, 154, 80, 237, 189, 0, 216, 196, 119, 65, 46, 251, 187, 31]; | |
func init() { | |
index = a = 0; | |
value = a; | |
} | |
func next() { | |
y = index; | |
index++; | |
value = a = table[y]; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "ram"; | |
import "entity"; | |
import "ai_wander"; | |
namespace slime { | |
func spawn() { | |
entity.spawn(); | |
entity.max_hp[x] = a = 10; | |
entity.hp[x] = a; | |
entity.pixel_x[x] = a = t0; | |
entity.pixel_y[x] = a = t1; | |
entity.update_lo[x] = a = update as u8; | |
entity.update_hi[x] = a = (update as u16 >> 8) as u8; | |
entity.flags[x] = a = entity.flags[x] | entity.FLAGS_HOSTILE; | |
ai_wander.init(); | |
ai_wander.wander_settting[x] = a = 0; | |
} | |
func update() { | |
ai_wander.update(); | |
let frame = t0; | |
entity.anim_timer[x]++; | |
frame = a = 0; | |
a = entity.anim_timer[x]; | |
if a >= 20 { | |
if a >= 40 { | |
entity.anim_timer[x] = a = 0; | |
} | |
frame++; | |
} | |
entity.frame[x] = a = 20 + frame; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Yeah, agreed, that would be nice. Right now, only void + no-argument functions are supported, but once more basic features are figured out, that would be a great addition. Eventually I want to have arguments/locals/return values for functions, which can designate registers/global variables to use. This way platforms where stack-handling is limited like 6502/Z80 can still have somewhat nice calling conventions (for their platform).