vars /ecavars U[64] buf // A buffer that can store the current and next row U curr_gen = 0 // Swap these offsets back and forth per generation U next_gen = 32 UU drawing_row_offset = 0 // Use this to track which row we're drawing U current_rule = %01001001 fn init_eca_state() {PPUMASK}(~PPUMASK_ON) // Clear display and set attributes ppu_reset_addr($2000) for UU i = 0; i < 1024; i += 1 {PPUDATA}(%00000000) // Reset drawing row offset drawing_row_offset = 0 {PPUMASK}(PPUMASK_ON) fn assemble_code(U left, U middle, U right) U U out = %00000000 out |= left << 2 out |= middle << 1 out |= right return out fn calc_next_cell(U left, U middle, U right) U U code = assemble_code(left, middle, right) ct U mask7 = %10000000 ct U mask6 = %01000000 ct U mask5 = %00100000 ct U mask4 = %00010000 ct U mask3 = %00001000 ct U mask2 = %00000100 ct U mask1 = %00000010 ct U mask0 = %00000001 switch code case %00000111 return (current_rule & mask7) >> 7 case %00000110 return (current_rule & mask6) >> 6 case %00000101 return (current_rule & mask5) >> 5 case %00000100 return (current_rule & mask4) >> 4 case %00000011 return (current_rule & mask3) >> 3 case %00000010 return (current_rule & mask2) >> 2 case %00000001 return (current_rule & mask1) >> 1 case %00000000 return (current_rule & mask0) >> 0 fn calc_and_advance_gen() // Calculate the next row's values U left_ind = 0 U right_ind = 0 U left_cell = 0 U mid_cell = 0 U right_cell = 0 U next_cell = 0 for U i = 0; i < 32; i += 1 // Calculate indices for left and right of cell if i == 0 left_ind = 31 else left_ind = i - 1 if i == 31 right_ind = 0 else right_ind = i + 1 // Get left and right of cell left_cell = buf[curr_gen + left_ind] mid_cell = buf[curr_gen + i] right_cell = buf[curr_gen + right_ind] // Calc next cell next_cell = calc_next_cell(left_cell, mid_cell, right_cell) // Set next cell buf[next_gen + i] = next_cell // Advance next generation if curr_gen == 0 curr_gen = 32 next_gen = 0 else curr_gen = 0 next_gen = 32 // advance drawing row offset, looping back to top drawing_row_offset += 1 if drawing_row_offset == 30 drawing_row_offset = 0 fn upload_curr_gen() // "scroll" to the row we're at UU row_start = $2000 row_start += (drawing_row_offset * 32) ppu_reset_addr(row_start) for U i = 0; i < 32; i += 1 //32 columns //next_cell = 0 //get_next_gen(i) {PPUDATA}(buf[i+curr_gen]) nmi eca_nmi() // Turn on rendering sprites and bg ppu_upload_oam_poll_pads(0) upload_curr_gen() {PPUMASK}(PPUMASK_BG_ON | PPUMASK_NO_CLIP) ppu_reset_scroll(0, 0) // Main mode for running the ECA mode eca_main() : nmi eca_nmi : vars /ecavars // Initialize the screen init_eca_state() // Wait 60 frames before beginning for U i = 0; i < 60; i += 1 nmi // Loop forever: while true nmi update_pads() if pads[0].released & BUTTON_START goto mode settings() : preserves /ecavars calc_and_advance_gen() charmap settingsmap(".E!@#I$T%R^&*L()_i{}|ta:01", 47) //Back up to 47 to grab a blank space data /settingsscreen // Initing settings to Initial Rule which is 73 %01001001 [] settingschars ("................................" "................................" "................................" "....E!I$.R^L(..................." "....@#T%.&*)_..................." "................................" "................................" "....111....110....101....100...." ".....0......1......0......0....." "................................" "....011....010....001....000...." ".....1......0......0......1....." "................................" "................................" "................................" "................................" "....E!I$.i{t...................." "....@#T%.}|:a..................." "................................" "................................" "00000000000000000000000000000000" "................................" "................................" "................................" "................................" "................................" "................................" "................................" "................................" "................................"settingsmap) vars /settings_vars U slow_counter = 0 U editing_rule = 1 // Start off editing the rule U rule_pos = 0 // Start off on highest bit U init_pos = 0 U cursor_x = 0 U cursor_y = 0 U cursor_frame = 0 U loaded = 0 U should_update_tile = 0 U tile_to_update = 0 U zero = 71 U one = 72 fn upload_cursor() // Our stack index into OAM: U i = 0 // Push a sprite: set_oam_x(i, cursor_x) // x-position set_oam_y(i, cursor_y - 1) // y-position set_oam_p(i, $49 + cursor_frame) // tile set_oam_a(i, 0) // options i += 4 // Clear the remainder of OAM hide_oam(i) fn update_cursor() if slow_counter == 0 cursor_frame += 1 if cursor_frame == 8 cursor_frame = 0 if pads[0].pressed & BUTTON_UP editing_rule = 1 else if pads[0].pressed & BUTTON_DOWN editing_rule = 0 if editing_rule if pads[0].pressed & BUTTON_LEFT if rule_pos == 0 rule_pos = 8 // loop back around rule_pos -= 1 else if pads[0].pressed & BUTTON_RIGHT rule_pos += 1 if rule_pos == 8 rule_pos = 0 // loop back around if pads[0].pressed & BUTTON_A U mask = U(1) << (7 - rule_pos) current_rule ^= mask if current_rule & mask tile_to_update = one else tile_to_update = zero should_update_tile = 1 // Find the cursor position cursor_x = 5 cursor_y = 8 if rule_pos > 3 cursor_x += 7 * (rule_pos - 4) cursor_y += 3 else cursor_x += 7 * rule_pos // Scale it cursor_x <<= 3 cursor_y <<= 3 else if pads[0].pressed & BUTTON_LEFT if init_pos == 0 init_pos = 32 // loop back around init_pos -= 1 else if pads[0].pressed & BUTTON_RIGHT init_pos += 1 if init_pos == 32 init_pos = 0 // loop back around if pads[0].pressed & BUTTON_A U tile = buf[curr_gen + init_pos] if tile // Not sure if I can just do buf[curr_gen + init_pos] = !tile buf[curr_gen + init_pos] = 0 tile_to_update = zero else buf[curr_gen + init_pos] = 1 tile_to_update = one should_update_tile = 1 cursor_x = init_pos << 3 // Uses the whole row cursor_y = 160 nmi settings_nmi() // Turn on rendering sprites and bg ppu_upload_oam_poll_pads(0) if should_update_tile ppu_reset_addr($2000 + (UU(cursor_x) >> 3) + ((UU(cursor_y) & %11111000) << 2)) {PPUDATA}(tile_to_update) should_update_tile = 0 if loaded {PPUMASK}(PPUMASK_ON | PPUMASK_NO_CLIP) slow_counter += 1 if slow_counter == 4 // Slow counter "ticks" every 4 frames slow_counter = 0 ppu_reset_scroll(0, 0) mode settings() : nmi settings_nmi : vars /ecavars : vars /settings_vars // Turn rendering off while we load the screen {PPUMASK}(~PPUMASK_ON) ppu_reset_addr($2000) // Load the settings screen CCC/settingsscreen settingsp = @settingschars for UU i = 0; i < 960; i += 1 {PPUDATA}(settingsp{i}) for UU i = 0; i < 64; i += 1 {PPUDATA}(%00000000) loaded = 1 {PPUMASK}(PPUMASK_ON) buf = U[64]() // Reset the buffer to all 0s while true nmi update_pads() update_cursor() upload_cursor() if pads[0].released & BUTTON_START goto mode eca_main() : preserves /ecavars charmap titlemap(".#-=N!@~E$%^S&*(C)_+A{}|12345678qwertyuiop") data /titlescreen [] title ("................................" "................................" "................................" "................................" "..........############.........." ".........##############........." ".........##..........##........." ".........##..........##........." ".........##..N!E$S&..##........." ".........##..@~%^*(..##........." ".........##....C)....##........." ".........##...._+....##........." ".........##....A{....##........." ".........##....}|....##........." ".........##..........##........." ".........##..........##........." ".........##############........." "..........############.........." "................................" "................................" "...........###########.........." "..........#..........#.........." "..........#.12345678.#.........." "..........#...<...>..#.........." "..........#..........#.........." "..........###########..........." "................................" "................................" "................................" "................................" "................................"titlemap) vars /titlescreen UU wipeline = 0 // nmi for entrypoint for the program nmi main_nmi() // Turn on rendering sprites and bg {PPUMASK}(PPUMASK_ON | PPUMASK_NO_CLIP) ppu_reset_scroll(0, 0) if wipeline UU row_start = $2000 row_start += ((wipeline - 1) * 32) ppu_reset_addr(row_start) for U i = 0; i < 32; i += 1 //32 columns //next_cell = 0 //get_next_gen(i) {PPUDATA}(0) // The main entry point for the program mode main() : nmi main_nmi : vars /titlescreen /ecavars // Set the palette: palette = example_palette ppu_upload_palette() ppu_reset_addr($2000) // Load the title screen CCC/titlescreen titlep = @title for UU i = 0; i < 960; i += 1 {PPUDATA}(titlep{i}) for UU i = 0; i < 64; i += 1 {PPUDATA}(%00000000) // Turn rendering on {PPUMASK}(PPUMASK_BG_ON) // Turn the NMI on {PPUCTRL}(PPUCTRL_NMI_ON) // Display title screen for 120 frames for U i = 0; i < 120; i += 1 nmi for U i = 1; i <= 30; i += 1 // use this to wipe the screen wipeline = i nmi // Go to the main eca mode goto mode settings() : preserves /ecavars // Define the tileset (commonly called CHR): chrrom file(fmt, "spritesheet.chr")