#ifndef EZNES_H #define EZNES_H /** EZnes: a set of helpers for easy NES indie development in pure C for CC65 (in text mode) Created by Luxferre in 2026, released into public domain */ #include #include #include #define u8 unsigned char #define u16 unsigned short #define s8 signed char #define s16 signed short /* Generic constants */ #ifdef PAL #define CPU 1662607 /* PAL */ #else #define CPU 1789773 /* NTSC */ #endif #define SCREEN_WIDTH 32 #define SCREEN_HEIGHT 28 #define SCREEN_CENTER_X (SCREEN_WIDTH >> 1) #define SCREEN_CENTER_Y (SCREEN_HEIGHT >> 1) #define EZJOY_UP 1 #define EZJOY_DN 2 #define EZJOY_LT 3 #define EZJOY_RT 4 #define EZJOY_A 5 #define EZJOY_B 6 #define EZJOY_SL 7 #define EZJOY_ST 8 #define EZJOY_COMBO 9 #define EZ_CORNER_UL (char) 20 #define EZ_CORNER_UR (char) 18 #define EZ_CORNER_DL (char) 17 #define EZ_CORNER_DR (char) 8 #define NES_BLACK 0x0F #define NES_WHITE 0x30 #define NES_RED 0x15 #define NES_GREEN 0x28 #define NES_YELLOW 0x2A /* RNG stuff */ static u16 _ez_entropy = 0; u16 ezrand() { _ez_entropy = 1 + ((_ez_entropy >> 15) | (_ez_entropy << 1)); return ((u16) rand()) ^ _ez_entropy; } /* Graphics section */ void ppu_off() { PPU.mask &= 0xE7; } void ppu_on() { waitvsync(); PPU.scroll = 0; PPU.mask |= 0x1A; } u8 joy_input_wait() { u8 last_joy = 0, joy, pressed; /* wait until the previous key is released */ while(joy_read(JOY_1)); /* wait for a new press */ while(1) { ++_ez_entropy; joy = joy_read(JOY_1); pressed = joy & ~last_joy; if(pressed & JOY_UP_MASK) return EZJOY_UP; if(pressed & JOY_DOWN_MASK) return EZJOY_DN; if(pressed & JOY_LEFT_MASK) return EZJOY_LT; if(pressed & JOY_RIGHT_MASK) return EZJOY_RT; if(pressed & JOY_BTN_1_MASK) return EZJOY_A; if(pressed & JOY_BTN_2_MASK) return EZJOY_B; if(pressed & JOY_BTN_3_MASK) return EZJOY_SL; if(pressed & JOY_BTN_4_MASK) return EZJOY_ST; last_joy = joy; waitvsync(); } } /* palette loader */ void init_palette(u8 bg1, u8 bg2, u8 bg3, u8 bg4, u8 c1, u8 c2, u8 c3, u8 c4) { u8 i, pal[16] = {0}; pal[0] = bg1; pal[4] = bg2; pal[8] = bg3; pal[12] = bg4; pal[3] = c1; pal[7] = c2; pal[11] = c3; pal[15] = c4; PPU.vram.address = 0x3F; PPU.vram.address = 0; for(i=0; i<16; i++) PPU.vram.data = pal[i]; } /* 4x4 char block colorization method (palette is 0 to 3) */ void colorxy(u8 x, u8 y, u8 palette) { u16 addr; u8 attr_byte; addr = 0x23C0 + ((y >> 2) << 3) + (x >> 2); attr_byte = palette | (palette << 2) | (palette << 4) | (palette << 6); waitvsync(); PPU.vram.address = (u8) (addr >> 8); PPU.vram.address = (u8) (addr & 255); PPU.vram.data = attr_byte; } /* Sound section */ u16 get_timer_by_freq(u16 freq) { return (u16) ((CPU / 16 / freq) - 1); } void play_pulse(u8 idx, u16 freq, u8 len) { u16 tm = get_timer_by_freq(freq); if(len > 31) len = 31; if(idx > 1) idx = 0; APU.pulse[idx].period_low = tm & 255; APU.pulse[idx].len_period_high = (tm >> 8) | (len << 3); } void play_jingle(u16 n1f1, u16 n1f2, u16 n2f1, u16 n2f2, u16 n3f1, u16 n3f2) { u16 i, dl = 2000; play_pulse(0, n1f1, 2); play_pulse(1, n1f2, 2); for(i=0;i 1) { for(i=n-1;i>0;--i) { j = (u16) (ezrand()%(i+1)); t = array[j]; array[j] = array[i]; array[i] = t; } } } /* Delay by n cycles */ void delay_cycles(u16 n) { u16 i; for(i=0;i