From db60c8e3807964b758110dda052091bed7b3b4e6 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sun, 24 Feb 2019 10:36:38 -0500 Subject: [PATCH] NES: fixed clipping, update presets, apu.c/h, vrambuf.c --- doc/notes.txt | 1 + presets/nes/apu.c | 20 ++++++ presets/nes/apu.h | 77 ++++++++++++++++++++ presets/nes/bcd.c | 22 ++++++ presets/nes/bcd.h | 2 + presets/nes/climber.c | 75 ++++++-------------- presets/nes/ex3.asm | 64 +++++++++++------ presets/nes/horizmask.c | 147 ++++++++++++++++++++++++++++++++++++++ presets/nes/music.c | 47 ++---------- presets/nes/neslib.h | 11 +++ presets/nes/shoot2.c | 150 +++++++++------------------------------ presets/nes/siegegame.c | 16 ++--- presets/nes/vrambuf.c | 40 +++++++++++ presets/nes/vrambuf.h | 38 ++++++++++ src/platform/nes.ts | 6 +- src/ui.ts | 2 +- src/worker/workermain.ts | 5 +- 17 files changed, 470 insertions(+), 253 deletions(-) create mode 100644 presets/nes/apu.c create mode 100644 presets/nes/apu.h create mode 100644 presets/nes/bcd.c create mode 100644 presets/nes/bcd.h create mode 100644 presets/nes/horizmask.c create mode 100644 presets/nes/vrambuf.c create mode 100644 presets/nes/vrambuf.h diff --git a/doc/notes.txt b/doc/notes.txt index 84f3b511..59d61f4f 100644 --- a/doc/notes.txt +++ b/doc/notes.txt @@ -95,6 +95,7 @@ TODO: - show .map file in listings? memory map view? - open ROM from URL? - NES: disassembly not aligned on PC +- NES: vrambuf.c for Solarian WEB WORKER FORMAT diff --git a/presets/nes/apu.c b/presets/nes/apu.c new file mode 100644 index 00000000..3002f9fd --- /dev/null +++ b/presets/nes/apu.c @@ -0,0 +1,20 @@ + + +#include "apu.h" + +#include + +const unsigned char APUINIT[0x13] = { + 0x30,0x08,0x00,0x00, + 0x30,0x08,0x00,0x00, + 0x80,0x00,0x00,0x00, + 0x30,0x00,0x00,0x00, + 0x00,0x00,0x00 +}; + +void apu_init() { + // from https://wiki.nesdev.com/w/index.php/APU_basics + memcpy((void*)0x4000, APUINIT, sizeof(APUINIT)); + APU.fcontrol = 0x40; // frame counter 5-step + APU.status = 0x0f; // turn on all channels except DMC +} diff --git a/presets/nes/apu.h b/presets/nes/apu.h new file mode 100644 index 00000000..319406f4 --- /dev/null +++ b/presets/nes/apu.h @@ -0,0 +1,77 @@ + +#ifndef _APU_H +#define _APU_H + +#include + +// Functions/macros for direct control +// of APU sound generation + +// enable +#define ENABLE_PULSE0 0x1 +#define ENABLE_PULSE1 0x2 +#define ENABLE_TRIANGLE 0x4 +#define ENABLE_NOISE 0x8 +#define ENABLE_DMC 0x10 + +#define APU_ENABLE(enable)\ + APU.status = (enable); + +// pulse channels +#define DUTY_75 0xc0 +#define DUTY_50 0x80 +#define DUTY_25 0x40 +#define DUTY_12 0x00 + +#define PULSE_ENVLOOP 0x20 +#define PULSE_CONSTVOL 0x10 +#define PULSE_VOLENVMASK 0xf + +#define PULSE_CH0 0 +#define PULSE_CH1 1 + +#define APU_PULSE_DECAY(channel,period,duty,decay,len)\ + APU.pulse[channel].period_low = (period)&0xff;\ + APU.pulse[channel].len_period_high = (((period)>>8)&7) | ((len)<<3);\ + APU.pulse[channel].control = (duty) | (decay); + +#define APU_PULSE_SUSTAIN(channel,period,duty,vol)\ + APU.pulse[channel].period_low = (period)&0xff;\ + APU.pulse[channel].len_period_high = (((period)>>8)&7);\ + APU.pulse[channel].control = (duty) | (vol) | (PULSE_CONSTVOL|PULSE_ENVLOOP); + +#define APU_PULSE_CONTROL(channel,duty,decay)\ + APU.pulse[channel].control = (duty) | (decay); + + +// triangle channel +#define TRIANGLE_LC_HALT 0x80 +#define TRIANGLE_LC_MASK 0x7f + +#define APU_TRIANGLE_SUSTAIN(period)\ + APU.triangle.counter = 0xc0;\ + APU.triangle.period_low = (period) & 0xff;\ + APU.triangle.len_period_high = (period) >> 8; + +// noise channel +#define NOISE_ENVLOOP 0x20 +#define NOISE_CONSTVOL 0x10 +#define NOISE_VOLENVMASK 0xf + +#define NOISE_PERIOD_BUZZ 0x80 + +#define APU_NOISE_SUSTAIN(_period,vol)\ + APU.noise.control = (vol) | (NOISE_ENVLOOP|NOISE_CONSTVOL);\ + APU.noise.period = (_period); + +#define APU_NOISE_DECAY(_period,_decay,_len)\ + APU.noise.control = (_decay);\ + APU.noise.period = (_period);\ + APU.noise.len = (_len); + + +// initialize APU with default state +void apu_init(void); + + +#endif diff --git a/presets/nes/bcd.c b/presets/nes/bcd.c new file mode 100644 index 00000000..80bfd79f --- /dev/null +++ b/presets/nes/bcd.c @@ -0,0 +1,22 @@ + +#include "neslib.h" + +word bcd_add(word a, word b) { + word result = 0; + byte c = 0; + byte shift = 0; + while (shift < 16) { + byte d = (a & 0xf) + (b & 0xf) + c; + c = 0; + while (d >= 10) { + c++; + d -= 10; + } + result |= d << shift; + shift += 4; + a >>= 4; + b >>= 4; + } + return result; +} + diff --git a/presets/nes/bcd.h b/presets/nes/bcd.h new file mode 100644 index 00000000..25926d6c --- /dev/null +++ b/presets/nes/bcd.h @@ -0,0 +1,2 @@ + +unsigned int bcd_add(unsigned int a, unsigned int b); diff --git a/presets/nes/climber.c b/presets/nes/climber.c index 0468f2f2..1429c3d5 100644 --- a/presets/nes/climber.c +++ b/presets/nes/climber.c @@ -8,6 +8,14 @@ // include CC65 NES Header (PPU) #include +// BCD arithmetic support +#include "bcd.h" +//#link "bcd.c" + +// VRAM update buffer +#include "vrambuf.h" +//#link "vrambuf.c" + // link the pattern table into CHR ROM //#link "chr_generic.s" @@ -22,12 +30,6 @@ extern char demo_sounds[]; typedef enum { SND_START, SND_HIT, SND_COIN, SND_JUMP } SFXIndex; -// define basic types -typedef unsigned char byte; -typedef signed char sbyte; -typedef unsigned short word; -typedef enum { false, true } bool; - ///// DEFINES #define COLS 30 // floor width in tiles @@ -66,7 +68,7 @@ static byte player_screen_y = 0; // score (BCD) static byte score = 0; -// flash animation (virtual bright) +// screen flash animation (virtual bright) static byte vbright = 4; // random byte between (a ... b-1) @@ -75,15 +77,6 @@ byte rndint(byte a, byte b) { return (rand() % (b-a)) + a; } -byte bcdadd(byte a, byte b) { - byte c = (a & 0xf) + (b & 0xf); - if (c < 10) { - return a + b; - } else { - return (c-10) + 0x10 + (a & 0xf0) + (b & 0xf0); - } -} - ///// OAM buffer (for sprites) #define OAMBUF ((unsigned char*) 0x200) @@ -102,46 +95,10 @@ word getntaddr(byte x, byte y) { // convert nametable address to attribute address word nt2attraddr(word a) { - return (a & 0x2c00) | 0x3c0 | (a & 0x0C00) | + return (a & 0x2c00) | 0x3c0 | ((a >> 4) & 0x38) | ((a >> 2) & 0x07); } -///// VRAM UPDATE BUFFER - -#define VBUFSIZE 64 -byte updbuf[VBUFSIZE]; // update buffer -byte updptr = 0; // index to end of buffer - -// add EOF marker to buffer -void cendbuf() { - updbuf[updptr] = NT_UPD_EOF; -} - -// flush buffer now, waiting for next frame -void cflushnow() { - // make sure buffer has EOF marker - cendbuf(); - // wait for next frame to flush update buffer - // this will also set the scroll registers properly - ppu_wait_frame(); - // clear the buffer - updptr = 0; - cendbuf(); -} - -// add multiple characters to update buffer -// using horizontal increment -void putbytes(word addr, char* str, byte len) { - if (updptr >= VBUFSIZE-4-len) cflushnow(); - updbuf[updptr++] = (addr >> 8) | NT_UPD_HORZ; - updbuf[updptr++] = addr & 0xff; - updbuf[updptr++] = len; - while (len--) { - updbuf[updptr++] = *str++; - } - cendbuf(); -} - /// METASPRITES // define a 2x2 metasprite @@ -178,7 +135,13 @@ DEF_METASPRITE_2x2_FLIP(playerLJump, 0xe8, 0); DEF_METASPRITE_2x2_FLIP(playerLClimb, 0xec, 0); DEF_METASPRITE_2x2_FLIP(playerLSad, 0xf0, 0); -DEF_METASPRITE_2x2(personToSave, 0xba, 1); +//DEF_METASPRITE_2x2(personToSave, 0xba, 1); +const unsigned char personToSave[]={ + 0, 0, (0xba)+0, 3, + 0, 8, (0xba)+2, 0, + 8, 0, (0xba)+1, 3, + 8, 8, (0xba)+3, 0, + 128}; const unsigned char* const playerRunSeq[16] = { playerLRun1, playerLRun2, playerLRun3, @@ -353,6 +316,8 @@ void draw_entire_stage() { byte y; for (y=0; y + +// 0 = horizontal mirroring +// 1 = vertical mirroring +#define NES_MIRRORING 1 + +// VRAM update buffer +#include "vrambuf.h" +//#link "vrambuf.c" + +// link the pattern table into CHR ROM +//#link "chr_generic.s" + +// function to write a string into the name table +// adr = start address in name table +// str = pointer to string +void put_str(unsigned int adr, const char *str) { + vram_adr(adr); // set PPU read/write address + vram_write(str, strlen(str)); // write bytes to PPU +} + +static word x_scroll = 0; +static byte bldg_height = 8; +static byte bldg_width = 8; +static byte bldg_char = 1; +static byte bldg_attr = 0x55; + +#define PLAYROWS 24 + +word nt2attraddr(word a) { + return (a & 0x2c00) | 0x3c0 | + ((a >> 4) & 0x38) | ((a >> 2) & 0x07); +} + +void update_nametable() { + word addr; + char buf[PLAYROWS]; + // divide x_scroll by 8 + // to get nametable X position + byte x = ((x_scroll >> 3)+32) & 63; + if (x < 32) + addr = NTADR_A(x, 4); + else + addr = NTADR_B(x&31, 4); + // create vertical slice + // clear empty space + memset(buf, 0, PLAYROWS-bldg_height); + // draw roof + buf[PLAYROWS-bldg_height-1] = bldg_char & 3; + // draw rest of building + memset(buf+PLAYROWS-bldg_height, bldg_char, bldg_height); + // draw vertical slice in name table + putbytes(addr ^ 0xc000, buf, sizeof(buf)); + // every 4 columns, update attribute table + if ((x & 3) == 1) { + // compute attribute table address + // of upper attribute block + addr = nt2attraddr(addr) + 8*4; + VRAMBUF_PUT(addr, bldg_attr, 0); + // put lower attribute block + addr += 8; + VRAMBUF_PUT(addr, bldg_attr, 0); + cendbuf(); + } + // generate new building? + if (--bldg_width == 0) { + bldg_height = (rand8() & 7) + 2; + bldg_width = (rand8() & 3) * 4 + 4; + bldg_char = (rand8() & 15); + bldg_attr = rand8(); + } +} + +// function to scroll window up and down until end +void scroll_demo() { + // infinite loop + while (1) { + // update nametable every 8 pixels + if ((x_scroll & 7) == 0) { + update_nametable(); + } + // manually force vram update + ppu_wait_nmi(); + flush_vram_update(updbuf); + cclearbuf(); + // reset ppu address + vram_adr(0x0); + // set scroll register + // and increment x_scroll + split(x_scroll++, 0); + } +} + +const char PALETTE[32] = { + 0x03, // background color + + 0x11,0x30,0x27, 0, // background 0 + 0x1c,0x20,0x2c, 0, // background 1 + 0x00,0x10,0x20, 0, // background 2 + 0x06,0x16,0x26, 0, // background 3 + + 0x16,0x35,0x24, 0, // sprite 0 + 0x00,0x37,0x25, 0, // sprite 1 + 0x0d,0x2d,0x3a, 0, // sprite 2 + 0x0d,0x27,0x2a // sprite 3 +}; + +// main function, run after console reset +void main(void) { + // set palette colors + pal_all(PALETTE); + + // write text to name table + put_str(NTADR_A(7,0), "Nametable A, Line 0"); + put_str(NTADR_A(7,1), "Nametable A, Line 1"); + put_str(NTADR_A(7,2), "Nametable A, Line 2"); + vram_adr(NTADR_A(0,3)); + vram_fill(5, 32); + put_str(NTADR_A(2,4), "Nametable A, Line 4"); + put_str(NTADR_A(2,15),"Nametable A, Line 15"); + put_str(NTADR_A(2,27),"Nametable A, Line 27"); + put_str(NTADR_B(2,4), "Nametable B, Line 4"); + put_str(NTADR_B(2,15),"Nametable B, Line 15"); + put_str(NTADR_B(2,27),"Nametable B, Line 27"); + + // set attributes + vram_adr(0x23c0); + vram_fill(0x55, 8); + + // set sprite 0 + oam_clear(); + oam_spr(0, 30, 1, 1, 0); + + // clip left 8 pixels of screen + ppu_mask(MASK_SPR|MASK_BG); + + // clear vram buffer + cclearbuf(); + + // enable PPU rendering (turn on screen) + ppu_on_all(); + + // scroll window back and forth + scroll_demo(); +} diff --git a/presets/nes/music.c b/presets/nes/music.c index 7169fa82..63134dda 100644 --- a/presets/nes/music.c +++ b/presets/nes/music.c @@ -4,7 +4,8 @@ #include "neslib.h" -typedef unsigned char byte; +#include "apu.h" +//#link "apu.c" // // MUSIC ROUTINES @@ -41,13 +42,6 @@ byte next_music_byte() { return *music_ptr++; } -#define DUTY_75 0xc0 -#define DUTY_50 0x80 -#define DUTY_25 0x40 -#define DUTY_12 0x00 - -#define DUTY (DUTY_25 | 0x1) // decay rate == 1 - void play_music() { static byte ch = 0; if (music_ptr) { @@ -56,25 +50,9 @@ void play_music() { if ((note & 0x80) == 0) { int period = note_table[note & 63]; if (ch == 0) { - APU.pulse[0].period_low = period & 0xff; - APU.pulse[0].len_period_high = period >> 8; - APU.pulse[0].control = DUTY; + APU_PULSE_DECAY(0, period, DUTY_25, 2, 10); } else { - APU.pulse[1].period_low = period & 0xff; - APU.pulse[1].len_period_high = period >> 8; - APU.pulse[1].control = DUTY; - /* - //period = note_table_tri[note & 63]; - period >>= 1; - APU.triangle.counter = 0xc0; - APU.triangle.period_low = period & 0xff; - APU.triangle.len_period_high = period >> 8; - */ - /* - APU.noise.control = 0x4; - APU.noise.period = 0xe; - APU.noise.len = 0xf; - */ + APU_PULSE_DECAY(1, period, DUTY_25, 2, 10); } ch = ch^1; } else { @@ -92,24 +70,9 @@ void start_music(const byte* music) { cur_duration = 0; } -const byte APUINIT[0x13] = { - 0x30,0x08,0x00,0x00, - 0x30,0x08,0x00,0x00, - 0x80,0x00,0x00,0x00, - 0x30,0x00,0x00,0x00, - 0x00,0x00,0x00 -}; - -void init_apu() { - // from https://wiki.nesdev.com/w/index.php/APU_basics - memcpy((void*)0x4000, APUINIT, sizeof(APUINIT)); - APU.fcontrol = 0x40; // frame counter 5-step - APU.status = 0x0f; // turn on all channels except DMC -} - void main(void) { - init_apu(); + apu_init(); music_ptr = 0; while (1) { if (!music_ptr) start_music(music1); diff --git a/presets/nes/neslib.h b/presets/nes/neslib.h index cb60d6c0..bca4ff17 100644 --- a/presets/nes/neslib.h +++ b/presets/nes/neslib.h @@ -1,3 +1,5 @@ +#ifndef _NESLIB_H +#define _NESLIB_H /* (C) 2015 Alex Semenov (Shiru) (C) 2016 Lauri Kasanen @@ -30,8 +32,14 @@ // unrle_vram renamed to vram_unrle, with adr argument removed // 060414 - many fixes and improvements, including sequental VRAM updates // previous versions were created since mid-2011, there were many updates +// xxxx19 - updated by sehugg@8bitworkshop +// define basic types for convenience +typedef unsigned char byte; // 8-bit unsigned +typedef signed char sbyte; // 8-bit signed +typedef unsigned short word; // 16-bit signed +typedef enum { false, true } bool; // boolean @@ -300,3 +308,6 @@ void __fastcall__ nmi_set_callback(void (*callback)(void)); #define MSB(x) (((x)>>8)) #define LSB(x) (((x)&0xff)) + +#endif /* neslib.h */ + diff --git a/presets/nes/shoot2.c b/presets/nes/shoot2.c index 499c3cf9..f8ede247 100644 --- a/presets/nes/shoot2.c +++ b/presets/nes/shoot2.c @@ -5,17 +5,23 @@ #include "neslib.h" +// APU (sound) support +#include "apu.h" +//#link "apu.c" + +// BCD arithmetic support +#include "bcd.h" +//#link "bcd.c" + +// VRAM update buffer +#include "vrambuf.h" +//#link "vrambuf.c" + #define COLS 32 #define ROWS 28 //#define DEBUG_FRAMERATE -typedef unsigned char byte; -typedef signed char sbyte; -typedef unsigned short word; - -/// - const char PALETTE[32] = { 0x0f, @@ -131,59 +137,11 @@ const char TILESET[128*8*2] = {/*{w:8,h:8,bpp:1,count:128,brev:1,np:2,pofs:8,rem #define CHAR(x) ((x)-' ') #define BLANK 0 -// VRAM UPDATE BUFFER - -#define VBUFSIZE 96 -byte updbuf[VBUFSIZE]; -byte updptr = 0; - -void cendbuf() { - updbuf[updptr] = NT_UPD_EOF; -} - -void cflushnow() { - cendbuf(); - ppu_wait_nmi(); - flush_vram_update(updbuf); - updptr = 0; - cendbuf(); - vram_adr(0x0); -} - -void vdelay(byte count) { - while (count--) cflushnow(); -} - -void putchar(byte x, byte y, char ch) { - word addr = NTADR_A(x,y); - if (updptr >= VBUFSIZE-4) cflushnow(); - updbuf[updptr++] = addr >> 8; - updbuf[updptr++] = addr & 0xff; - updbuf[updptr++] = ch; - cendbuf(); -} - -void putbytes(byte x, byte y, char* str, byte len) { - word addr = NTADR_A(x,y); - if (updptr >= VBUFSIZE-4-len) cflushnow(); - updbuf[updptr++] = (addr >> 8) | NT_UPD_HORZ; - updbuf[updptr++] = addr & 0xff; - updbuf[updptr++] = len; - while (len--) { - updbuf[updptr++] = *str++; - } - cendbuf(); -} - -void putstring(byte x, byte y, char* str) { - putbytes(x, y, str, strlen(str)); -} - void clrscr() { updptr = 0; cendbuf(); ppu_off(); - vram_adr(0x2000); + vram_adr(NAMETABLE_A); vram_fill(BLANK, 32*28); vram_adr(0x0); ppu_on_all(); @@ -203,26 +161,7 @@ void draw_bcd_word(byte col, byte row, word bcd) { buf[j] = CHAR('0'+(bcd&0xf)); bcd >>= 4; } - putbytes(col, row, buf, 5); -} - -word bcd_add(word a, word b) { - word result = 0; - byte c = 0; - byte shift = 0; - while (shift < 16) { - byte d = (a & 0xf) + (b & 0xf) + c; - c = 0; - while (d >= 10) { - c++; - d -= 10; - } - result |= d << shift; - shift += 4; - a >>= 4; - b >>= 4; - } - return result; + putbytes(NTADR_A(col, row), buf, 5); } // GAME CODE @@ -261,17 +200,17 @@ typedef struct { } AttackingEnemy; typedef struct { - signed char dx; byte xpos; - signed char dy; byte ypos; + signed char dx; + signed char dy; } Missile; typedef struct { - byte name; - byte tag; byte x; byte y; + byte name; + byte tag; } Sprite; #define ENEMIES_PER_ROW 8 @@ -366,7 +305,7 @@ void draw_row(byte row) { } x += 3; } - putbytes(0, y, buf, sizeof(buf)); + putbytes(NTADR_A(0, y), buf, sizeof(buf)); } void draw_next_row() { @@ -724,37 +663,30 @@ void new_player_ship() { void set_sounds() { byte i; - byte enable = 0x1 | 0x2 | 0x8; + // these channels decay, so ok to always enable + byte enable = ENABLE_PULSE0|ENABLE_PULSE1|ENABLE_NOISE; // missile fire sound if (missiles[PLYRMISSILE].ypos != YOFFSCREEN) { - APU.pulse[0].period_low = missiles[PLYRMISSILE].ypos ^ 0xff; - APU.pulse[0].len_period_high = 0; - APU.pulse[0].control = 0x80 | 0x30 | 6; + APU_PULSE_SUSTAIN(0, missiles[PLYRMISSILE].ypos ^ 0xff, DUTY_50, 6); } else { - APU.pulse[0].control = 0x30; + APU_PULSE_CONTROL(0, DUTY_50, 1); } // enemy explosion sound if (player_exploding && player_exploding < 8) { - APU.noise.control = 4; - APU.noise.period = 8 + player_exploding; - APU.noise.len = 15; + APU_NOISE_DECAY(8 + player_exploding, 5, 15); } else if (enemy_exploding) { - APU.noise.control = 2; - APU.noise.period = 8 + enemy_exploding; - APU.noise.len = 8; + APU_NOISE_DECAY(8 + enemy_exploding, 2, 8); } // set diving sounds for spaceships for (i=0; i<2; i++) { register AttackingEnemy* a = i ? &attackers[4] : &attackers[0]; if (a->findex && !a->returning) { byte y = a->y >> 8; - APU.triangle.counter = 0xc0; - APU.triangle.period_low = y; - APU.triangle.len_period_high = 1; - enable |= 0x4; + APU_TRIANGLE_SUSTAIN(0x100 | y); + enable |= ENABLE_TRIANGLE; } } - APU.status = enable; + APU_ENABLE(enable); } static char starx[32]; @@ -783,7 +715,7 @@ void play_round() { register byte t0; byte end_timer = 255; add_score(0); - putstring(0, 0, "PLAYER 1"); + //putbytes(NTADR_A(0, 1), "PLAYER 1", 8); setup_formation(); clrobjs(); formation_direction = 1; @@ -851,7 +783,7 @@ void set_shifted_pattern(const byte* src, word dest, byte shift) { vram_write(buf, sizeof(buf)); } -void setup_tileset() { +void setup_graphics() { byte i; word src; word dest; @@ -869,26 +801,14 @@ void setup_tileset() { set_shifted_pattern(&TILESET[src], dest, i); dest += 3*16; } -} - -const byte APUINIT[0x13] = { - 0x30,0x08,0x00,0x00, - 0x30,0x08,0x00,0x00, - 0x80,0x00,0x00,0x00, - 0x30,0x00,0x00,0x00, - 0x00,0x00,0x00 -}; - -void init_apu() { - // from https://wiki.nesdev.com/w/index.php/APU_basics - memcpy((void*)0x4000, APUINIT, sizeof(APUINIT)); - APU.fcontrol = 0x40; // frame counter 5-step - APU.status = 0x0f; // turn on all channels except DMC + // activate vram buffer + cendbuf(); + set_vram_update(updbuf); } void main() { - setup_tileset(); - init_apu(); + setup_graphics(); + apu_init(); init_stars(); player_score = 0; while (1) { diff --git a/presets/nes/siegegame.c b/presets/nes/siegegame.c index 8ebefc70..7d69493e 100644 --- a/presets/nes/siegegame.c +++ b/presets/nes/siegegame.c @@ -12,13 +12,7 @@ extern unsigned char palSprites[16]; extern unsigned char TILESET[8*256]; #define COLS 32 -#define ROWS 28 - -#define NTADR(x,y) ((0x2000|((y)<<5)|(x))) - -typedef unsigned char byte; -typedef signed char sbyte; -typedef unsigned short word; +#define ROWS 27 // read a character from VRAM. // this is tricky because we have to wait @@ -27,10 +21,10 @@ typedef unsigned short word; // back to the start of the frame. byte getchar(byte x, byte y) { // compute VRAM read address - word addr = NTADR(x,y); + word addr = NTADR_A(x,y); byte rd; // wait for VBLANK to start - waitvsync(); + ppu_wait_nmi(); vram_adr(addr); vram_read(&rd, 1); vram_adr(0x0); @@ -60,7 +54,7 @@ void vdelay(byte count) { } void cputcxy(byte x, byte y, char ch) { - word addr = NTADR(x,y); + word addr = NTADR_A(x,y); if (updptr >= 60) cflushnow(); updbuf[updptr++] = addr >> 8; updbuf[updptr++] = addr & 0xff; @@ -69,7 +63,7 @@ void cputcxy(byte x, byte y, char ch) { } void cputsxy(byte x, byte y, char* str) { - word addr = NTADR(x,y); + word addr = NTADR_A(x,y); byte len = strlen(str); if (updptr >= 60 - len) cflushnow(); updbuf[updptr++] = (addr >> 8) | NT_UPD_HORZ; diff --git a/presets/nes/vrambuf.c b/presets/nes/vrambuf.c new file mode 100644 index 00000000..bffdc316 --- /dev/null +++ b/presets/nes/vrambuf.c @@ -0,0 +1,40 @@ + +#include "neslib.h" +#include "vrambuf.h" + +// index to end of buffer +byte updptr = 0; + +// add EOF marker to buffer +void cendbuf(void) { + updbuf[updptr] = NT_UPD_EOF; +} + +void cclearbuf(void) { + updptr = 0; + cendbuf(); +} + +// flush buffer now, waiting for next frame +void cflushnow(void) { + // make sure buffer has EOF marker + cendbuf(); + // wait for next frame to flush update buffer + // this will also set the scroll registers properly + ppu_wait_frame(); + // clear the buffer + cclearbuf(); +} + +// add multiple characters to update buffer +// using horizontal increment +void putbytes(word addr, char* str, byte len) { + if (updptr >= VBUFSIZE-4-len) cflushnow(); + updbuf[updptr++] = (addr >> 8) ^ NT_UPD_HORZ; + updbuf[updptr++] = addr & 0xff; + updbuf[updptr++] = len; + while (len--) { + updbuf[updptr++] = *str++; + } + cendbuf(); +} diff --git a/presets/nes/vrambuf.h b/presets/nes/vrambuf.h new file mode 100644 index 00000000..04cf6df6 --- /dev/null +++ b/presets/nes/vrambuf.h @@ -0,0 +1,38 @@ + +#ifndef _VRAMBUF_H +#define _VRAMBUF_H + +#include "neslib.h" + +// VBUFSIZE = maximum update buffer bytes +#define VBUFSIZE 128 + +// update buffer starts at $100 (stack page) +#define updbuf ((byte*)0x100) + +// index to end of buffer +extern byte updptr; + +// macros +#define VRAMBUF_PUT(addr,len,flags)\ + updbuf[updptr++] = ((addr) >> 8) | (flags);\ + updbuf[updptr++] = (addr) & 0xff;\ + updbuf[updptr++] = (len); + +#define VRAMBUF_ADD(b)\ + updbuf[updptr++] = (b); + +// add EOF marker to buffer +void cendbuf(void); + +// clear update buffer +void cclearbuf(void); + +// flush buffer now, waiting for next frame +void cflushnow(void); + +// add multiple characters to update buffer +// using horizontal increment +void putbytes(word addr, char* str, byte len); + +#endif // vrambuf.h diff --git a/src/platform/nes.ts b/src/platform/nes.ts index c4e0c1b1..521a76e8 100644 --- a/src/platform/nes.ts +++ b/src/platform/nes.ts @@ -17,6 +17,7 @@ const JSNES_PRESETS = [ {id:'neslib1.c', name:'Text'}, {id:'scroll.c', name:'Scrolling'}, {id:'statusbar.c', name:'Status Bar'}, + {id:'horizmask.c', name:'Horizontal Scrolling'}, {id:'sprites.c', name:'Sprites'}, {id:'metasprites.c', name:'Metasprites'}, {id:'flicker.c', name:'Flickering Sprites'}, @@ -118,6 +119,7 @@ const _JSNESPlatform = function(mainElement) { }, //TODO: onBatteryRamWrite }); + //nes.ppu.clipToTvSize = false; nes.stop = function() { // TODO: trigger breakpoint self.pause(); @@ -300,10 +302,10 @@ const _JSNESPlatform = function(mainElement) { var PPUFLAGS = [ ["f_nmiOnVblank","NMI_ON_VBLANK"], ["f_spVisibility","SPRITES"], - ["f_spClipping","CLIP_SPRITES"], + ["f_spClipping","NO_CLIP_SPRITES"], ["f_dispType","MONOCHROME"], ["f_bgVisibility","BACKGROUND"], - ["f_bgClipping","CLIP_BACKGROUND"], + ["f_bgClipping","NO_CLIP_BACKGROUND"], ]; for (var i=0; i