From f1eecc00f197a97edc4959d8b4bbdd0f59248abb Mon Sep 17 00:00:00 2001 From: Luxferre Date: Sun, 11 Jan 2026 14:27:20 +0200 Subject: [PATCH] Offloaded some NES routines to eznes.h --- nes/eznes.h | 179 +++++++++++++++++++++++++++++++++++++++ nes/nescoundrel.c | 211 +++++++++------------------------------------- 2 files changed, 219 insertions(+), 171 deletions(-) create mode 100644 nes/eznes.h diff --git a/nes/eznes.h b/nes/eznes.h new file mode 100644 index 0000000..eb2f9e5 --- /dev/null +++ b/nes/eznes.h @@ -0,0 +1,179 @@ +#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 char +#define s16 short + +/* Generic constants */ + +#define CPU 1789773 /* NTSC */ + +#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 NES_BLACK 0x0F +#define NES_WHITE 0x30 +#define NES_RED 0x15 +#define NES_GREEN 0x28 +#define NES_YELLOW 0x2A + +/* Graphics section */ + +void ppu_off() { + PPU.mask &= 0xE7; +} + +void ppu_on() { + waitvsync(); + PPU.scroll = 0; + PPU.mask |= 0x1A; +} + +int 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) { + 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 bg, u8 c1, u8 c2, u8 c3, u8 c4) { + u8 i, pal[16] = {0}; + pal[0] = pal[4] = pal[8] = pal[12] = bg; + 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) { + unsigned int 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 */ + +unsigned int get_timer_by_freq(int freq) { + return (unsigned int) ((CPU / 16 / freq) - 1); +} + +void play_pulse(u8 idx, unsigned int freq, u8 len) { + unsigned int 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(unsigned int n1f1, + unsigned int n1f2, + unsigned int n2f1, + unsigned int n2f2, + unsigned int n3f1, + unsigned int n3f2) { + unsigned int i, dl = 2000; + play_pulse(0, n1f1, 2); + play_pulse(1, n1f2, 2); + for(i=0;i -#include -#include #include #include #include +#include "eznes.h" /* Constants section */ -#define SCREEN_WIDTH 32 -#define SCREEN_HEIGHT 28 -#define SCREEN_CENTER_X (SCREEN_WIDTH >> 1) -#define SCREEN_CENTER_Y (SCREEN_HEIGHT >> 1) - -#define KEY_UP 1 -#define KEY_DN 2 -#define KEY_LT 3 -#define KEY_RT 4 -#define KEY_A 5 -#define KEY_B 6 -#define KEY_SL 7 -#define KEY_ST 8 - #define GAME_WHITE 0 #define GAME_RED 1 #define GAME_YELLOW 2 #define GAME_GREEN 3 -/* Graphics section */ - -void ppu_off() { - PPU.mask &= 0xE7; -} - -void ppu_on() { - waitvsync(); - PPU.scroll = 0; - PPU.mask |= 0x1A; -} - -int joy_input_wait() { - unsigned char last_joy = 0, joy, pressed; - /* wait until the previous key is released */ - while(joy_read(JOY_1)); - /* wait for a new press */ - while(1) { - joy = joy_read(JOY_1); - pressed = joy & ~last_joy; - if(pressed & JOY_UP_MASK) return KEY_UP; - if(pressed & JOY_DOWN_MASK) return KEY_DN; - if(pressed & JOY_LEFT_MASK) return KEY_LT; - if(pressed & JOY_RIGHT_MASK) return KEY_RT; - if(pressed & JOY_BTN_1_MASK) return KEY_A; - if(pressed & JOY_BTN_2_MASK) return KEY_B; - if(pressed & JOY_BTN_3_MASK) return KEY_SL; - if(pressed & JOY_BTN_4_MASK) return KEY_ST; - last_joy = joy; - waitvsync(); - } -} - -/* palette loader */ -void init_palette() { - unsigned char i; - const unsigned char my_palette[16] = { - 0x0f, 0, 0, 0x30, /* white */ - 0x0f, 0, 0, 0x15, /* red */ - 0x0f, 0, 0, 0x28, /* yellow */ - 0x0f, 0, 0, 0x2A /* green */ - }; - PPU.vram.address = 0x3F; - PPU.vram.address = 0; - for(i=0; i<16; i++) PPU.vram.data = my_palette[i]; -} - -/* 4x4 char block colorization method (palette is 0 to 3) */ -void colorxy(unsigned char x, unsigned char y, unsigned char palette) { - unsigned int addr; - unsigned char attr_byte; - addr = 0x23C0 + ((y >> 2) << 3) + (x >> 2); - attr_byte = palette | (palette << 2) | (palette << 4) | (palette << 6); - waitvsync(); - PPU.vram.address = (unsigned char) (addr >> 8); - PPU.vram.address = (unsigned char) (addr & 255); - PPU.vram.data = attr_byte; -} - /* Sound section */ -#define CPU 1789773 /* NTSC */ - -unsigned int get_timer(int freq) { - return (unsigned int) ((CPU / 16 / freq) - 1); -} - -void play_pulse(unsigned char idx, unsigned int freq, unsigned char len) { - unsigned int tm = get_timer(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(unsigned int n1f1, - unsigned int n1f2, - unsigned int n2f1, - unsigned int n2f2, - unsigned int n3f1, - unsigned int n3f2) { - unsigned int i, dl = 2000; - play_pulse(0, n1f1, 2); - play_pulse(1, n1f2, 2); - for(i=0;i 1) { switch(joy_input_wait()) { - case KEY_A: res = 1; break; - case KEY_B: res = 0; break; + case EZJOY_A: res = 1; break; + case EZJOY_B: res = 0; break; default: res = 2; } } @@ -431,11 +300,11 @@ void print_msg(char *s) { } /* Array shuffle method */ -void shuffle(char *array, unsigned int n) { - unsigned int i, j, t; +void shuffle(char *array, u16 n) { + u16 i, j, t; if(n > 1) { for(i=n-1;i>0;i--) { - j = (unsigned int) (rand()%(i+1)); + j = (u16) (rand()%(i+1)); t = array[j]; array[j] = array[i]; array[i] = t; @@ -445,12 +314,12 @@ void shuffle(char *array, unsigned int n) { /* Room item handling methods */ -int handle_monster(int val) { - int healthlost = val; +s16 handle_monster(s16 val) { + s16 healthlost = val; char monster_msg[(SCREEN_WIDTH-1)]; beep_enemy(); if(weapon > 0 && val <= durability) { - int weapondiff = val - weapon; + s16 weapondiff = val - weapon; if(weapondiff < 0) weapondiff = 0; snprintf(monster_msg, sizeof(status_msg), "%d-level monster: use weapon?", val); if(confirm(monster_msg)) { @@ -469,14 +338,14 @@ int handle_monster(int val) { return val; } -void handle_weapon(int val) { +void handle_weapon(s16 val) { weapon = val; snprintf(status_msg, sizeof(status_msg), "You equip a %d-level weapon.", weapon); durability = 14; /* reset durability to max limit */ beep_weapon(); } -void handle_potion(unsigned char val) { +void handle_potion(u8 val) { if(potion_drank) { print_msg("Already drank a potion here."); beep_err(); @@ -492,11 +361,11 @@ void handle_potion(unsigned char val) { } /* Main gameplay */ -int game() { - unsigned int i; - int score = -208; /* score storage */ - int item, room_sum; /* true item value */ - int last_room = 0, last_room_clr = 0; /* last room markers */ +s16 game() { + u16 i; + s16 score = -208; /* score storage */ + s16 item, room_sum; /* true item value */ + s16 last_room = 0, last_room_clr = 0; /* last room markers */ char ch; deck_idx = 0; /* reset the deck index */ /* Prefill the deck */ @@ -557,7 +426,7 @@ int game() { } else if(ch == CH_R) { /* run mechanics */ /* Compact and rearrange the deck */ char temp_deck[sizeof(deck)]; - int t_idx = 0; + s16 t_idx = 0; /* 1. Collect all valid remaining cards from deck */ for(i=0; i