Skip to content

Instantly share code, notes, and snippets.

@Bananattack
Last active April 17, 2018 04:36
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Bananattack/05cd40e570501ffbb495120c7b5b52a9 to your computer and use it in GitHub Desktop.
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.
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;
}
}
}
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
];
}
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;
}
}
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,
];
}
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];
}
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;
}
}
}
}
}
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;
}
}
}
}
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;
}
}
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";
}
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;
}
}
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;
}
}
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];
}
}
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;
}
}
@PypeBros
Copy link

would be nice to have a way to state that caller of .update() should set x to something meaningful to the function.

@Bananattack
Copy link
Author

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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment