import "vcs"; import "banks"; in ram { var t0, t1, t2, t3, t4, t5, t6, t7 : u8; var joy : u8; var fire : [u8; 2]; var unpress : [u8; 2]; var score : [u8; 2]; var water_color : u8; namespace winner { var x, y : u8; } namespace voice { var index : [u8; 2]; var length : [u8; 2]; } namespace game { let SCORE_X = 50; let START_X = 30; let START_Y = 40; let WINNER_X = 45; let WINNER_X2 = 160 - 45 + 5; let LEFT_EDGE = 16; let RIGHT_EDGE = 160 - 16; let COLOR_WATER_START = 0xA2; let COLOR_P1 = 0xFF; let COLOR_P2 = 0x8F; let COLOR_BREAD = 0x28; let DETUNE_P2 = 2; } namespace sprite { let PLAYER1 = 0; let PLAYER2 = 1; let MISSILE1 = 2; let MISSILE2 = 3; let BALL = 4; let COUNT = 5; var x : [u8; COUNT]; var y : [u8; COUNT]; var data : [u8; COUNT]; var gfx_ptr : [*const u8; COUNT]; } namespace player1 { let index = sprite.PLAYER1; var x @ &sprite.x[index] : u8; var y @ &sprite.y[index] : u8; var reflect @ &sprite.data[index] : u8; var gfx_ptr @ &sprite.gfx_ptr[index] : *const u8; } namespace player2 { let index = sprite.PLAYER2; var x @ &sprite.x[index] : u8; var y @ &sprite.y[index] : u8; var reflect @ &sprite.data[index] : u8; var gfx_ptr @ &sprite.gfx_ptr[index] : *const u8; } namespace missile1 { let index = sprite.MISSILE1; var x @ &sprite.x[index] : u8; var y @ &sprite.y[index] : u8; var speed @ &sprite.data[index] : u8; var gfx_ptr @ &sprite.gfx_ptr[index] : *const u8; } namespace missile2 { let index = sprite.MISSILE2; var x @ &sprite.x[index] : u8; var y @ &sprite.y[index] : u8; var speed @ &sprite.data[index] : u8; var gfx_ptr @ &sprite.gfx_ptr[index] : *const u8; } namespace ball { let index = sprite.BALL; var x @ &sprite.x[index] : u8; var y @ &sprite.y[index] : u8; var gfx_ptr @ &sprite.gfx_ptr[index] : *const u8; } } in rom { import "random"; func main() { nointerrupt = true; decimal = false; s = x = 0xFF; a = 0; vcs.pattern.missile1 = a; vcs.pattern.missile2 = a; vcs.pattern.player1 = a; vcs.pattern.player2 = a; vcs.pattern.playfield1 = a; vcs.pattern.playfield2 = a; vcs.pattern.playfield3 = a; vcs.color.bg = a; vcs.color.fg = a; vcs.color.player1 = a = game.COLOR_P1; vcs.color.player2 = a = game.COLOR_P2; vcs.color.fg = a = game.COLOR_BREAD; vcs.color.bg = a = 0x00; vcs.control.player1 = a = vcs.control.PLAYER_SINGLE | vcs.control.MISSILE_4PX; vcs.control.player2 = a = vcs.control.PLAYER_SINGLE | vcs.control.MISSILE_4PX; vcs.control.playfield = a = vcs.control.BALL_4PX; player1.x = a = game.START_X; player1.y = a = game.START_Y; player1.reflect = a = vcs.reflect.ENABLE; player2.x = a = 160 - player1.x; player2.y = a = game.START_Y; player2.reflect = a = player1.reflect ^ vcs.reflect.ENABLE; ball.x = a = 84; ball.y = a = game.START_Y; missile1.x = a = 30; missile1.y = a = 200; missile1.speed = a = 0; missile2.x = a = 160 - 30; missile2.y = a = 200; missile2.speed = a = 0; water_color = a = game.COLOR_WATER_START; vcs.hmove.clear = a; score[0] = a = 0; score[1] = a = 0; voice.length[0] = a = 0; voice.length[1] = a = 0; winner.x = a = game.WINNER_X; winner.y = a = 8; a = vcs.timer.value; if zero { a += 1; } random.lo = a; random.hi = a; voice.index[0] = a = &sfx.intro as u8; voice.length[0] = a = sfx.intro.len; while true { vcs.sync.vsync = a = vcs.sync.VSYNC_START; inline for let i in 1 .. 3 { vcs.sync.wsync = a; } vcs.timer.set64 = a = 43; vcs.sync.vsync = a = 0; vcs.io.ddr_a = a = 0; joy = a = vcs.io.port_a; fire[0] = a = vcs.io.triggers[0] & vcs.io.TRIGGER_JOY_FIRE; fire[1] = a = vcs.io.triggers[1] & vcs.io.TRIGGER_JOY_FIRE; random.next(); a = ball.y; if a >= 100 { spawn_bread(); } else { x = sprite.PLAYER1; move_player_sprite(); joy = a = joy << 4; x = sprite.PLAYER2; move_player_sprite(); x = sprite.MISSILE1; move_missile_sprite(); x = sprite.MISSILE2; move_missile_sprite(); } inline for let i in 0 .. 1 { a = voice.length[i]; if !zero { x = voice.index[i]; vcs.audio.volumes[i] = a = sfx.volume_data[x]; vcs.audio.frequencies[i] = a = ((sfx.data[x] & 0xF0) >>> 4) + (i * game.DETUNE_P2); vcs.audio.tones[i] = a = sfx.data[x] & 0x0F; voice.index[i]++; voice.length[i]--; } else { vcs.audio.volumes[i] = a = 0; } } <:player1.gfx_ptr = a = score[0] << 3; >:player1.gfx_ptr = a = >:&graphic.digits; <:player2.gfx_ptr = a = score[1] << 3; >:player2.gfx_ptr = a = >:&graphic.digits; <:ball.gfx_ptr = a = <:&graphic.dot - winner.y; >:ball.gfx_ptr = a = >:&graphic.dot -# 0; <:missile1.gfx_ptr = a = <:&graphic.dot - missile1.y; >:missile1.gfx_ptr = a = >:&graphic.dot -# 0; <:missile2.gfx_ptr = a = <:&graphic.dot - missile2.y; >:missile2.gfx_ptr = a = >:&graphic.dot -# 0; do { a = vcs.timer.value; } while !zero; vcs.sync.vblank = a = 0; a = game.SCORE_X; x = sprite.PLAYER1; position_sprite(); a = 160 - game.SCORE_X; x = sprite.PLAYER2; position_sprite(); a = winner.x; x = sprite.BALL; position_sprite(); vcs.sync.wsync = a; vcs.reflect.player1 = a = 0; vcs.reflect.player2 = a; x = 0; do { vcs.sync.wsync = a; x++; vcs.sync.wsync = a; y = a = x >>> 1; vcs.pattern.player1 = a = player1.gfx_ptr[y]; vcs.pattern.player2 = a = player2.gfx_ptr[y]; vcs.pattern.ball = a = ball.gfx_ptr[y]; x++; vcs.sync.wsync = a; } while x < 16; push(a = x); x = 0; do { a = sprite.x[x]; position_sprite(); x++; } while x != sprite.COUNT; <:player1.gfx_ptr = a = <:&graphic.duck - player1.y; >:player1.gfx_ptr = a = >:&graphic.duck -# 0; <:player2.gfx_ptr = a = <:&graphic.duck - player2.y; >:player2.gfx_ptr = a = >:&graphic.duck -# 0; <:ball.gfx_ptr = a = <:&graphic.dot - ball.y; >:ball.gfx_ptr = a = >:&graphic.dot -# 0; vcs.sync.wsync = a; vcs.color.bg = a = water_color; vcs.reflect.player1 = a = player1.reflect; vcs.reflect.player2 = a = player2.reflect; x = a = pop(); do { vcs.sync.wsync = a; y = a = x >>> 1; vcs.pattern.player1 = a = player1.gfx_ptr[y]; vcs.pattern.player2 = a = player2.gfx_ptr[y]; vcs.pattern.ball = a = ball.gfx_ptr[y]; vcs.sync.wsync = a; vcs.pattern.missile1 = a = missile1.gfx_ptr[y]; vcs.pattern.missile2 = a = missile2.gfx_ptr[y]; x++; x++; } while x < 187; vcs.sync.wsync = a; a = 0; vcs.pattern.player1 = a; vcs.pattern.player2 = a; vcs.pattern.missile1 = a; vcs.pattern.missile2 = a; vcs.pattern.ball = a; vcs.pattern.playfield1 = a; vcs.pattern.playfield2 = a; vcs.pattern.playfield3 = a; vcs.color.bg = a; vcs.sync.vblank = a = vcs.sync.VBLANK_RESET_TRIGGER | vcs.sync.VBLANK_START; inline for let i in 1 .. 30 { vcs.sync.wsync = a; } } } // Arguments: // a = position func position_sprite() { vcs.hmove.clear = a; vcs.sync.wsync = a; carry = true; do { a -#= 15; } while carry; y = a; vcs.hmove.players[x] = a = ((&fine_adjust as u16 - 0xF1) as *u8)[y]; vcs.reset.players[x] = a; vcs.sync.wsync = a; vcs.hmove.apply = a; inline for let i in 1 .. 6 { nop(); } } // Arguments: // x = sprite index func move_player_sprite() { y = joy; if { a = y & vcs.io.PORT_A_JOY_LEFT; } && zero { a = sprite.x[x] - 1; if a < game.LEFT_EDGE { a = game.LEFT_EDGE; } sprite.x[x] = a; sprite.data[x] = a = 0; } else if { a = y & vcs.io.PORT_A_JOY_RIGHT; } && zero { a = sprite.x[x] + 1; if a >= game.RIGHT_EDGE { a = game.RIGHT_EDGE; } sprite.x[x] = a; sprite.data[x] = a = vcs.reflect.ENABLE; } if { a = y & vcs.io.PORT_A_JOY_UP; } && zero { sprite.y[x] = a = sprite.y[x] - 1; if !carry { sprite.y[x] = a = 94; } } else if { a = y & vcs.io.PORT_A_JOY_DOWN; } && zero { a = sprite.y[x] + 1; if a >= 94 { sprite.y[x] = a = 0; } sprite.y[x] = a; } if { a = fire[x]; } && zero { if { a = unpress[x]; } && zero { unpress[x] = a = 1; check_bread(); if { a = t5; } && zero { shoot_missile(); } } } else { unpress[x] = a = 0; } check_enemy_missile(); } // Arguments: // x = sprite index func move_missile_sprite() { a = sprite.data[x]; if !zero { a = sprite.x[x] + sprite.data[x]; if a < game.LEFT_EDGE { sprite.y[x] = a = 200; sprite.data[x] = a = 0; } else if a >= game.RIGHT_EDGE { sprite.y[x] = a = 200; sprite.data[x] = a = 0; } else { sprite.x[x] = a; } } } func spawn_bread() { random.next(); a = random.lo; goto spawn_failed if a < 32; goto spawn_failed if a >= 160 - 32; ball.x = a; a = random.hi; goto spawn_failed if a < 28; goto spawn_failed if a >= 84; ball.y = a; return; spawn_failed: ball.y = a = 200; } func check_bread() { t0 = a = ball.x + 8; t1 = a = sprite.x[x] + 17; t2 = a = ball.y + 6; t3 = a = sprite.y[x] + 8; t5 = a = 0; if { a = sprite.x[x]; } && a < t0 { if { a = ball.x + 2; } && a < t1 { if { a = sprite.y[x] + 1; } && a < t2 { if { a = ball.y; } && a < t3 { t5 = a = 1; ball.y = a = 200; if { a = score[x]; } && a < 9 { score[x]++; voice.index[x] = a = &sfx.score as u8; voice.length[x] = a = sfx.score.len; } else { score[0] = a = 0; score[1] = a = 0; sprite.x[0] = a = game.START_X; sprite.y[0] = a = game.START_Y; sprite.x[1] = a = 160 - game.START_X; sprite.y[1] = a = game.START_Y; random.next(); a = random.lo & 0xF0 | 0x2; if a == water_color { a = a + 0x10; } water_color = a; winner.y = a = 0; winner.x = a = game.WINNER_X; if x == 1 { winner.x = a = game.WINNER_X2; } voice.index[x] = a = &sfx.win as u8; voice.length[x] = a = sfx.win.len; } } } } } } func shoot_missile() { y = a = x + 2; if { a = sprite.data[y]; } && !zero { return; } voice.index[x] = a = &sfx.shoot as u8; voice.length[x] = a = sfx.shoot.len; sprite.x[y] = a = sprite.x[x]; sprite.y[y] = a = sprite.y[x] + 1; if { a = sprite.data[x]; } && zero { a = (0x4 ^ 0xFF) + 1; } else { a = 0x4; } sprite.data[y] = a; } func check_enemy_missile() { y = a = x ^ 1 + 2; t0 = a = sprite.x[y] + 8; t1 = a = sprite.x[x] + 17; t2 = a = sprite.y[y] + 6; t3 = a = sprite.y[x] + 14; t5 = a = 0; ^if { a = sprite.x[x]; } && a < t0 { if { a = sprite.x[y] + 2; } && a < t1 { if { a = sprite.y[x] + 1; } && a < t2 { if { a = sprite.y[y]; } && a < t3 { t5 = a = 1; a = sprite.x[x] + sprite.data[y] + sprite.data[y] + sprite.data[y] + sprite.data[y]; if a < game.LEFT_EDGE { a = game.LEFT_EDGE; } else if a >= game.RIGHT_EDGE { a = game.RIGHT_EDGE; } sprite.x[x] = a; voice.index[x] = a = &sfx.hurt as u8; voice.length[x] = a = sfx.hurt.len; random.next(); t6 = a = random.lo & 0x7 + 4; if { a = random.hi & 0x1; } && zero { sprite.y[x] = a = sprite.y[x] + t6; } else { sprite.y[x] = a = sprite.y[x] - t6; } sprite.y[y] = a = 200; sprite.data[y] = a = 0; if { a = score[x]; } && a != 0 { if { a = random.hi & 0x6; } && zero { score[x]--; } } } } } } } namespace graphic { let DOT_PATTERN = [ 2, 2, 2, 2 ]; let DUCK_PATTERN = [ 0b00011100_u8, 0b00101010_u8, 0b00101010_u8, 0b11111110_u8, 0b00011100_u8, 0b00111111_u8, 0b00111111_u8, 0b00011110_u8, ]; const @ 0xF600 : [u8] = [0u8; 256]; const dot @ 0xF700 : [u8] = DOT_PATTERN ~ [0u8; 256 - DOT_PATTERN.len]; const @ 0xF800 : [u8] = [0u8; 256]; const duck @ 0xF900 : [u8] = DUCK_PATTERN ~ [0u8; 256 - DUCK_PATTERN.len]; const digits @ 0xFA00 : [u8] = [ 0b00111000, 0b01000100, 0b01000100, 0b01000100, 0b01000100, 0b01000100, 0b00111000, 0b00000000, 0b00010000, 0b00110000, 0b00010000, 0b00010000, 0b00010000, 0b00010000, 0b00111000, 0b00000000, 0b00111000, 0b01000100, 0b00000100, 0b00011000, 0b00100000, 0b01000000, 0b01111100, 0b00000000, 0b00111000, 0b01000100, 0b00000100, 0b00011000, 0b00000100, 0b01000100, 0b00111000, 0b00000000, 0b01001000, 0b01001000, 0b01001000, 0b01111100, 0b00001000, 0b00001000, 0b00001000, 0b00000000, 0b01111100, 0b01000000, 0b01000000, 0b01111000, 0b00000100, 0b00000100, 0b01111000, 0b00000000, 0b00111000, 0b01000000, 0b01000000, 0b01111000, 0b01000100, 0b01000100, 0b00111000, 0b00000000, 0b01111100, 0b00000100, 0b00001000, 0b00001000, 0b00010000, 0b00010000, 0b00010000, 0b00000000, 0b00111000, 0b01000100, 0b01000100, 0b00111000, 0b01000100, 0b01000100, 0b00111000, 0b00000000, 0b00111000, 0b01000100, 0b01000100, 0b00111100, 0b00000100, 0b00001000, 0b00110000, 0b00000000, ]; } } in rom @ 0xFB00 { const fine_adjust : [u8] = [ 0b01110000, 0b01100000, 0b01010000, 0b01000000, 0b00110000, 0b00100000, 0b00010000, 0b00000000, 0b11110000, 0b11100000, 0b11010000, 0b11000000, 0b10110000, 0b10100000, 0b10010000, ]; namespace sfx { const volume_data : [u8] = [ 0xF, 0xF, 0xF, 0xF, 0xF, 0x7, 0x4, 0x0, 0xF, 0xF, 0xF, 0xF, 0xF, 0x7, 0x4, 0x0, 0xF, 0xF, 0xF, 0xF, 0x1, 0x1, 0x1, 0x1, ]; } } in rom @ 0xFC00 { namespace sfx { let Bb8 = 0b0001; let E8 = 0b0010; let Bb7 = 0b0011; let G7 = 0b0100; let E7 = 0b0101; let Bb6 = 0b0111; let A6 = 0b1000; let G6 = 0b1001; let E6 = 0b1011; let C6 = 0b1110; let Bb5 = 0b1111; let NOTE(pitch, instrument, length) = [(pitch << 4) | instrument; length]; let REST(length) = [0; length]; const data : [u8] = []; const shoot : [u8] = [((13 - i) << 4) | 0x7 for let i in 0 .. 7]; const hurt : [u8] = [((((i / 2) >> 2) + ((i / 2) & 0xF) << 4) | 0x3) * (1 - i % 2) for let i in 0 .. 15]; const score : [u8] = [(7 - i) << 4 | 0xC for let i in 1 .. 3] ~ [(i >> 1) << 4 | 0xC for let i in 1 .. 7]; let WIN_INSTRUMENT = 10; let INTRO_INSTRUMENT = 10; const win : [u8] = NOTE(Bb6, WIN_INSTRUMENT, 4) ~ NOTE(G6, WIN_INSTRUMENT, 8) ~ NOTE(A6, WIN_INSTRUMENT, 8) ~ NOTE(G6, WIN_INSTRUMENT, 8) ~ NOTE(E7, WIN_INSTRUMENT, 8); const intro : [u8] = NOTE(E7, INTRO_INSTRUMENT, 8) ~ NOTE(Bb6, INTRO_INSTRUMENT, 8) ~ NOTE(G6, INTRO_INSTRUMENT, 8) ~ NOTE(A6, INTRO_INSTRUMENT, 8) ~ NOTE(Bb6, INTRO_INSTRUMENT, 8) ~ NOTE(E7, INTRO_INSTRUMENT, 8) ~ NOTE(A6, INTRO_INSTRUMENT, 8) ~ NOTE(Bb6, INTRO_INSTRUMENT, 8) ~ NOTE(E6, INTRO_INSTRUMENT, 4) ~ REST(4) ~ NOTE(E6, INTRO_INSTRUMENT, 4) ~ REST(4) ~ NOTE(E7, INTRO_INSTRUMENT, 8); } } in rom @ 0xFFFA { const = [main, main, main]; }