mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2025-01-11 08:30:02 +00:00
NES: fixed clipping, update presets, apu.c/h, vrambuf.c
This commit is contained in:
parent
8daa260527
commit
db60c8e380
@ -95,6 +95,7 @@ TODO:
|
|||||||
- show .map file in listings? memory map view?
|
- show .map file in listings? memory map view?
|
||||||
- open ROM from URL?
|
- open ROM from URL?
|
||||||
- NES: disassembly not aligned on PC
|
- NES: disassembly not aligned on PC
|
||||||
|
- NES: vrambuf.c for Solarian
|
||||||
|
|
||||||
|
|
||||||
WEB WORKER FORMAT
|
WEB WORKER FORMAT
|
||||||
|
20
presets/nes/apu.c
Normal file
20
presets/nes/apu.c
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#include "apu.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
77
presets/nes/apu.h
Normal file
77
presets/nes/apu.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
|
||||||
|
#ifndef _APU_H
|
||||||
|
#define _APU_H
|
||||||
|
|
||||||
|
#include <nes.h>
|
||||||
|
|
||||||
|
// 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
|
22
presets/nes/bcd.c
Normal file
22
presets/nes/bcd.c
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
|
2
presets/nes/bcd.h
Normal file
2
presets/nes/bcd.h
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
unsigned int bcd_add(unsigned int a, unsigned int b);
|
@ -8,6 +8,14 @@
|
|||||||
// include CC65 NES Header (PPU)
|
// include CC65 NES Header (PPU)
|
||||||
#include <nes.h>
|
#include <nes.h>
|
||||||
|
|
||||||
|
// 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 the pattern table into CHR ROM
|
||||||
//#link "chr_generic.s"
|
//#link "chr_generic.s"
|
||||||
|
|
||||||
@ -22,12 +30,6 @@ extern char demo_sounds[];
|
|||||||
|
|
||||||
typedef enum { SND_START, SND_HIT, SND_COIN, SND_JUMP } SFXIndex;
|
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
|
///// DEFINES
|
||||||
|
|
||||||
#define COLS 30 // floor width in tiles
|
#define COLS 30 // floor width in tiles
|
||||||
@ -66,7 +68,7 @@ static byte player_screen_y = 0;
|
|||||||
// score (BCD)
|
// score (BCD)
|
||||||
static byte score = 0;
|
static byte score = 0;
|
||||||
|
|
||||||
// flash animation (virtual bright)
|
// screen flash animation (virtual bright)
|
||||||
static byte vbright = 4;
|
static byte vbright = 4;
|
||||||
|
|
||||||
// random byte between (a ... b-1)
|
// random byte between (a ... b-1)
|
||||||
@ -75,15 +77,6 @@ byte rndint(byte a, byte b) {
|
|||||||
return (rand() % (b-a)) + a;
|
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)
|
///// OAM buffer (for sprites)
|
||||||
|
|
||||||
#define OAMBUF ((unsigned char*) 0x200)
|
#define OAMBUF ((unsigned char*) 0x200)
|
||||||
@ -102,46 +95,10 @@ word getntaddr(byte x, byte y) {
|
|||||||
|
|
||||||
// convert nametable address to attribute address
|
// convert nametable address to attribute address
|
||||||
word nt2attraddr(word a) {
|
word nt2attraddr(word a) {
|
||||||
return (a & 0x2c00) | 0x3c0 | (a & 0x0C00) |
|
return (a & 0x2c00) | 0x3c0 |
|
||||||
((a >> 4) & 0x38) | ((a >> 2) & 0x07);
|
((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
|
/// METASPRITES
|
||||||
|
|
||||||
// define a 2x2 metasprite
|
// 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(playerLClimb, 0xec, 0);
|
||||||
DEF_METASPRITE_2x2_FLIP(playerLSad, 0xf0, 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] = {
|
const unsigned char* const playerRunSeq[16] = {
|
||||||
playerLRun1, playerLRun2, playerLRun3,
|
playerLRun1, playerLRun2, playerLRun3,
|
||||||
@ -353,6 +316,8 @@ void draw_entire_stage() {
|
|||||||
byte y;
|
byte y;
|
||||||
for (y=0; y<ROWS; y++) {
|
for (y=0; y<ROWS; y++) {
|
||||||
draw_floor_line(y);
|
draw_floor_line(y);
|
||||||
|
// allow buffer to flush, delaying a frame
|
||||||
|
cflushnow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -649,7 +614,7 @@ void pickup_object(Actor* actor) {
|
|||||||
sfx_play(SND_HIT,0);
|
sfx_play(SND_HIT,0);
|
||||||
vbright = 8; // flash
|
vbright = 8; // flash
|
||||||
} else {
|
} else {
|
||||||
score = bcdadd(score, 1);
|
score = bcd_add(score, 1);
|
||||||
sfx_play(SND_COIN,0);
|
sfx_play(SND_COIN,0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,11 @@
|
|||||||
seg.u RAM
|
seg.u RAM
|
||||||
org $0
|
org $0
|
||||||
|
|
||||||
ScrollX byte ; used during NMI
|
ScrollPos byte ; used during NMI
|
||||||
ScrollY byte ; used during NMI
|
Rand byte
|
||||||
|
Temp1 byte
|
||||||
|
|
||||||
|
SpriteBuf equ $200
|
||||||
|
|
||||||
;;;;; NES CARTRIDGE HEADER
|
;;;;; NES CARTRIDGE HEADER
|
||||||
|
|
||||||
@ -24,6 +27,7 @@ Start:
|
|||||||
jsr SetPalette ;set colors
|
jsr SetPalette ;set colors
|
||||||
jsr FillVRAM ;set PPU RAM
|
jsr FillVRAM ;set PPU RAM
|
||||||
jsr WaitSync ;wait for VSYNC (and PPU warmup)
|
jsr WaitSync ;wait for VSYNC (and PPU warmup)
|
||||||
|
jsr InitSprites
|
||||||
lda #0
|
lda #0
|
||||||
sta PPU_ADDR
|
sta PPU_ADDR
|
||||||
sta PPU_ADDR ;PPU addr = 0
|
sta PPU_ADDR ;PPU addr = 0
|
||||||
@ -51,6 +55,33 @@ FillVRAM: subroutine
|
|||||||
bne .loop
|
bne .loop
|
||||||
rts
|
rts
|
||||||
|
|
||||||
|
;
|
||||||
|
InitSprites: subroutine
|
||||||
|
lda #1
|
||||||
|
ldx #0
|
||||||
|
.loop
|
||||||
|
sta SpriteBuf,x
|
||||||
|
jsr NextRandom
|
||||||
|
inx
|
||||||
|
bne .loop
|
||||||
|
rts
|
||||||
|
|
||||||
|
;
|
||||||
|
MoveSprites: subroutine
|
||||||
|
lda #1
|
||||||
|
ldx #0
|
||||||
|
.loop
|
||||||
|
sta Temp1
|
||||||
|
and #3
|
||||||
|
clc
|
||||||
|
adc SpriteBuf,x
|
||||||
|
sta SpriteBuf,x
|
||||||
|
lda Temp1
|
||||||
|
jsr NextRandom
|
||||||
|
inx
|
||||||
|
bne .loop
|
||||||
|
rts
|
||||||
|
|
||||||
; set palette colors
|
; set palette colors
|
||||||
|
|
||||||
SetPalette: subroutine
|
SetPalette: subroutine
|
||||||
@ -76,34 +107,21 @@ SetPalette: subroutine
|
|||||||
|
|
||||||
NMIHandler:
|
NMIHandler:
|
||||||
SAVE_REGS
|
SAVE_REGS
|
||||||
|
; load sprites
|
||||||
|
lda #$02
|
||||||
|
sta PPU_OAM_DMA
|
||||||
; update scroll position (must be done after VRAM updates)
|
; update scroll position (must be done after VRAM updates)
|
||||||
jsr ReadJoypad
|
inc ScrollPos
|
||||||
pha
|
lda ScrollPos
|
||||||
and #$03
|
|
||||||
tay
|
|
||||||
lda ScrollDirTab,y
|
|
||||||
clc
|
|
||||||
adc ScrollX
|
|
||||||
sta ScrollX
|
|
||||||
sta PPU_SCROLL
|
sta PPU_SCROLL
|
||||||
pla
|
lda #0
|
||||||
lsr
|
|
||||||
lsr
|
|
||||||
and #$03
|
|
||||||
tay
|
|
||||||
lda ScrollDirTab,y
|
|
||||||
clc
|
|
||||||
adc ScrollY
|
|
||||||
sta ScrollY
|
|
||||||
sta PPU_SCROLL
|
sta PPU_SCROLL
|
||||||
|
; move sprites
|
||||||
|
jsr MoveSprites
|
||||||
; reload registers
|
; reload registers
|
||||||
RESTORE_REGS
|
RESTORE_REGS
|
||||||
rti
|
rti
|
||||||
|
|
||||||
; Scroll direction lookup table
|
|
||||||
ScrollDirTab:
|
|
||||||
hex 00 01 ff 00
|
|
||||||
|
|
||||||
;;;;; CONSTANT DATA
|
;;;;; CONSTANT DATA
|
||||||
|
|
||||||
align $100
|
align $100
|
||||||
|
147
presets/nes/horizmask.c
Normal file
147
presets/nes/horizmask.c
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
|
||||||
|
#include "neslib.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
}
|
@ -4,7 +4,8 @@
|
|||||||
|
|
||||||
#include "neslib.h"
|
#include "neslib.h"
|
||||||
|
|
||||||
typedef unsigned char byte;
|
#include "apu.h"
|
||||||
|
//#link "apu.c"
|
||||||
|
|
||||||
//
|
//
|
||||||
// MUSIC ROUTINES
|
// MUSIC ROUTINES
|
||||||
@ -41,13 +42,6 @@ byte next_music_byte() {
|
|||||||
return *music_ptr++;
|
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() {
|
void play_music() {
|
||||||
static byte ch = 0;
|
static byte ch = 0;
|
||||||
if (music_ptr) {
|
if (music_ptr) {
|
||||||
@ -56,25 +50,9 @@ void play_music() {
|
|||||||
if ((note & 0x80) == 0) {
|
if ((note & 0x80) == 0) {
|
||||||
int period = note_table[note & 63];
|
int period = note_table[note & 63];
|
||||||
if (ch == 0) {
|
if (ch == 0) {
|
||||||
APU.pulse[0].period_low = period & 0xff;
|
APU_PULSE_DECAY(0, period, DUTY_25, 2, 10);
|
||||||
APU.pulse[0].len_period_high = period >> 8;
|
|
||||||
APU.pulse[0].control = DUTY;
|
|
||||||
} else {
|
} else {
|
||||||
APU.pulse[1].period_low = period & 0xff;
|
APU_PULSE_DECAY(1, period, DUTY_25, 2, 10);
|
||||||
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;
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
ch = ch^1;
|
ch = ch^1;
|
||||||
} else {
|
} else {
|
||||||
@ -92,24 +70,9 @@ void start_music(const byte* music) {
|
|||||||
cur_duration = 0;
|
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)
|
void main(void)
|
||||||
{
|
{
|
||||||
init_apu();
|
apu_init();
|
||||||
music_ptr = 0;
|
music_ptr = 0;
|
||||||
while (1) {
|
while (1) {
|
||||||
if (!music_ptr) start_music(music1);
|
if (!music_ptr) start_music(music1);
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#ifndef _NESLIB_H
|
||||||
|
#define _NESLIB_H
|
||||||
/*
|
/*
|
||||||
(C) 2015 Alex Semenov (Shiru)
|
(C) 2015 Alex Semenov (Shiru)
|
||||||
(C) 2016 Lauri Kasanen
|
(C) 2016 Lauri Kasanen
|
||||||
@ -30,8 +32,14 @@
|
|||||||
// unrle_vram renamed to vram_unrle, with adr argument removed
|
// unrle_vram renamed to vram_unrle, with adr argument removed
|
||||||
// 060414 - many fixes and improvements, including sequental VRAM updates
|
// 060414 - many fixes and improvements, including sequental VRAM updates
|
||||||
// previous versions were created since mid-2011, there were many 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 MSB(x) (((x)>>8))
|
||||||
#define LSB(x) (((x)&0xff))
|
#define LSB(x) (((x)&0xff))
|
||||||
|
|
||||||
|
#endif /* neslib.h */
|
||||||
|
|
||||||
|
@ -5,17 +5,23 @@
|
|||||||
|
|
||||||
#include "neslib.h"
|
#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 COLS 32
|
||||||
#define ROWS 28
|
#define ROWS 28
|
||||||
|
|
||||||
//#define DEBUG_FRAMERATE
|
//#define DEBUG_FRAMERATE
|
||||||
|
|
||||||
typedef unsigned char byte;
|
|
||||||
typedef signed char sbyte;
|
|
||||||
typedef unsigned short word;
|
|
||||||
|
|
||||||
///
|
|
||||||
|
|
||||||
const char PALETTE[32] = {
|
const char PALETTE[32] = {
|
||||||
0x0f,
|
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 CHAR(x) ((x)-' ')
|
||||||
#define BLANK 0
|
#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() {
|
void clrscr() {
|
||||||
updptr = 0;
|
updptr = 0;
|
||||||
cendbuf();
|
cendbuf();
|
||||||
ppu_off();
|
ppu_off();
|
||||||
vram_adr(0x2000);
|
vram_adr(NAMETABLE_A);
|
||||||
vram_fill(BLANK, 32*28);
|
vram_fill(BLANK, 32*28);
|
||||||
vram_adr(0x0);
|
vram_adr(0x0);
|
||||||
ppu_on_all();
|
ppu_on_all();
|
||||||
@ -203,26 +161,7 @@ void draw_bcd_word(byte col, byte row, word bcd) {
|
|||||||
buf[j] = CHAR('0'+(bcd&0xf));
|
buf[j] = CHAR('0'+(bcd&0xf));
|
||||||
bcd >>= 4;
|
bcd >>= 4;
|
||||||
}
|
}
|
||||||
putbytes(col, row, buf, 5);
|
putbytes(NTADR_A(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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GAME CODE
|
// GAME CODE
|
||||||
@ -261,17 +200,17 @@ typedef struct {
|
|||||||
} AttackingEnemy;
|
} AttackingEnemy;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
signed char dx;
|
|
||||||
byte xpos;
|
byte xpos;
|
||||||
signed char dy;
|
|
||||||
byte ypos;
|
byte ypos;
|
||||||
|
signed char dx;
|
||||||
|
signed char dy;
|
||||||
} Missile;
|
} Missile;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
byte name;
|
|
||||||
byte tag;
|
|
||||||
byte x;
|
byte x;
|
||||||
byte y;
|
byte y;
|
||||||
|
byte name;
|
||||||
|
byte tag;
|
||||||
} Sprite;
|
} Sprite;
|
||||||
|
|
||||||
#define ENEMIES_PER_ROW 8
|
#define ENEMIES_PER_ROW 8
|
||||||
@ -366,7 +305,7 @@ void draw_row(byte row) {
|
|||||||
}
|
}
|
||||||
x += 3;
|
x += 3;
|
||||||
}
|
}
|
||||||
putbytes(0, y, buf, sizeof(buf));
|
putbytes(NTADR_A(0, y), buf, sizeof(buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw_next_row() {
|
void draw_next_row() {
|
||||||
@ -724,37 +663,30 @@ void new_player_ship() {
|
|||||||
|
|
||||||
void set_sounds() {
|
void set_sounds() {
|
||||||
byte i;
|
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
|
// missile fire sound
|
||||||
if (missiles[PLYRMISSILE].ypos != YOFFSCREEN) {
|
if (missiles[PLYRMISSILE].ypos != YOFFSCREEN) {
|
||||||
APU.pulse[0].period_low = missiles[PLYRMISSILE].ypos ^ 0xff;
|
APU_PULSE_SUSTAIN(0, missiles[PLYRMISSILE].ypos ^ 0xff, DUTY_50, 6);
|
||||||
APU.pulse[0].len_period_high = 0;
|
|
||||||
APU.pulse[0].control = 0x80 | 0x30 | 6;
|
|
||||||
} else {
|
} else {
|
||||||
APU.pulse[0].control = 0x30;
|
APU_PULSE_CONTROL(0, DUTY_50, 1);
|
||||||
}
|
}
|
||||||
// enemy explosion sound
|
// enemy explosion sound
|
||||||
if (player_exploding && player_exploding < 8) {
|
if (player_exploding && player_exploding < 8) {
|
||||||
APU.noise.control = 4;
|
APU_NOISE_DECAY(8 + player_exploding, 5, 15);
|
||||||
APU.noise.period = 8 + player_exploding;
|
|
||||||
APU.noise.len = 15;
|
|
||||||
} else if (enemy_exploding) {
|
} else if (enemy_exploding) {
|
||||||
APU.noise.control = 2;
|
APU_NOISE_DECAY(8 + enemy_exploding, 2, 8);
|
||||||
APU.noise.period = 8 + enemy_exploding;
|
|
||||||
APU.noise.len = 8;
|
|
||||||
}
|
}
|
||||||
// set diving sounds for spaceships
|
// set diving sounds for spaceships
|
||||||
for (i=0; i<2; i++) {
|
for (i=0; i<2; i++) {
|
||||||
register AttackingEnemy* a = i ? &attackers[4] : &attackers[0];
|
register AttackingEnemy* a = i ? &attackers[4] : &attackers[0];
|
||||||
if (a->findex && !a->returning) {
|
if (a->findex && !a->returning) {
|
||||||
byte y = a->y >> 8;
|
byte y = a->y >> 8;
|
||||||
APU.triangle.counter = 0xc0;
|
APU_TRIANGLE_SUSTAIN(0x100 | y);
|
||||||
APU.triangle.period_low = y;
|
enable |= ENABLE_TRIANGLE;
|
||||||
APU.triangle.len_period_high = 1;
|
|
||||||
enable |= 0x4;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
APU.status = enable;
|
APU_ENABLE(enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char starx[32];
|
static char starx[32];
|
||||||
@ -783,7 +715,7 @@ void play_round() {
|
|||||||
register byte t0;
|
register byte t0;
|
||||||
byte end_timer = 255;
|
byte end_timer = 255;
|
||||||
add_score(0);
|
add_score(0);
|
||||||
putstring(0, 0, "PLAYER 1");
|
//putbytes(NTADR_A(0, 1), "PLAYER 1", 8);
|
||||||
setup_formation();
|
setup_formation();
|
||||||
clrobjs();
|
clrobjs();
|
||||||
formation_direction = 1;
|
formation_direction = 1;
|
||||||
@ -851,7 +783,7 @@ void set_shifted_pattern(const byte* src, word dest, byte shift) {
|
|||||||
vram_write(buf, sizeof(buf));
|
vram_write(buf, sizeof(buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup_tileset() {
|
void setup_graphics() {
|
||||||
byte i;
|
byte i;
|
||||||
word src;
|
word src;
|
||||||
word dest;
|
word dest;
|
||||||
@ -869,26 +801,14 @@ void setup_tileset() {
|
|||||||
set_shifted_pattern(&TILESET[src], dest, i);
|
set_shifted_pattern(&TILESET[src], dest, i);
|
||||||
dest += 3*16;
|
dest += 3*16;
|
||||||
}
|
}
|
||||||
}
|
// activate vram buffer
|
||||||
|
cendbuf();
|
||||||
const byte APUINIT[0x13] = {
|
set_vram_update(updbuf);
|
||||||
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 main() {
|
||||||
setup_tileset();
|
setup_graphics();
|
||||||
init_apu();
|
apu_init();
|
||||||
init_stars();
|
init_stars();
|
||||||
player_score = 0;
|
player_score = 0;
|
||||||
while (1) {
|
while (1) {
|
||||||
|
@ -12,13 +12,7 @@ extern unsigned char palSprites[16];
|
|||||||
extern unsigned char TILESET[8*256];
|
extern unsigned char TILESET[8*256];
|
||||||
|
|
||||||
#define COLS 32
|
#define COLS 32
|
||||||
#define ROWS 28
|
#define ROWS 27
|
||||||
|
|
||||||
#define NTADR(x,y) ((0x2000|((y)<<5)|(x)))
|
|
||||||
|
|
||||||
typedef unsigned char byte;
|
|
||||||
typedef signed char sbyte;
|
|
||||||
typedef unsigned short word;
|
|
||||||
|
|
||||||
// read a character from VRAM.
|
// read a character from VRAM.
|
||||||
// this is tricky because we have to wait
|
// this is tricky because we have to wait
|
||||||
@ -27,10 +21,10 @@ typedef unsigned short word;
|
|||||||
// back to the start of the frame.
|
// back to the start of the frame.
|
||||||
byte getchar(byte x, byte y) {
|
byte getchar(byte x, byte y) {
|
||||||
// compute VRAM read address
|
// compute VRAM read address
|
||||||
word addr = NTADR(x,y);
|
word addr = NTADR_A(x,y);
|
||||||
byte rd;
|
byte rd;
|
||||||
// wait for VBLANK to start
|
// wait for VBLANK to start
|
||||||
waitvsync();
|
ppu_wait_nmi();
|
||||||
vram_adr(addr);
|
vram_adr(addr);
|
||||||
vram_read(&rd, 1);
|
vram_read(&rd, 1);
|
||||||
vram_adr(0x0);
|
vram_adr(0x0);
|
||||||
@ -60,7 +54,7 @@ void vdelay(byte count) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void cputcxy(byte x, byte y, char ch) {
|
void cputcxy(byte x, byte y, char ch) {
|
||||||
word addr = NTADR(x,y);
|
word addr = NTADR_A(x,y);
|
||||||
if (updptr >= 60) cflushnow();
|
if (updptr >= 60) cflushnow();
|
||||||
updbuf[updptr++] = addr >> 8;
|
updbuf[updptr++] = addr >> 8;
|
||||||
updbuf[updptr++] = addr & 0xff;
|
updbuf[updptr++] = addr & 0xff;
|
||||||
@ -69,7 +63,7 @@ void cputcxy(byte x, byte y, char ch) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void cputsxy(byte x, byte y, char* str) {
|
void cputsxy(byte x, byte y, char* str) {
|
||||||
word addr = NTADR(x,y);
|
word addr = NTADR_A(x,y);
|
||||||
byte len = strlen(str);
|
byte len = strlen(str);
|
||||||
if (updptr >= 60 - len) cflushnow();
|
if (updptr >= 60 - len) cflushnow();
|
||||||
updbuf[updptr++] = (addr >> 8) | NT_UPD_HORZ;
|
updbuf[updptr++] = (addr >> 8) | NT_UPD_HORZ;
|
||||||
|
40
presets/nes/vrambuf.c
Normal file
40
presets/nes/vrambuf.c
Normal file
@ -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();
|
||||||
|
}
|
38
presets/nes/vrambuf.h
Normal file
38
presets/nes/vrambuf.h
Normal file
@ -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
|
@ -17,6 +17,7 @@ const JSNES_PRESETS = [
|
|||||||
{id:'neslib1.c', name:'Text'},
|
{id:'neslib1.c', name:'Text'},
|
||||||
{id:'scroll.c', name:'Scrolling'},
|
{id:'scroll.c', name:'Scrolling'},
|
||||||
{id:'statusbar.c', name:'Status Bar'},
|
{id:'statusbar.c', name:'Status Bar'},
|
||||||
|
{id:'horizmask.c', name:'Horizontal Scrolling'},
|
||||||
{id:'sprites.c', name:'Sprites'},
|
{id:'sprites.c', name:'Sprites'},
|
||||||
{id:'metasprites.c', name:'Metasprites'},
|
{id:'metasprites.c', name:'Metasprites'},
|
||||||
{id:'flicker.c', name:'Flickering Sprites'},
|
{id:'flicker.c', name:'Flickering Sprites'},
|
||||||
@ -118,6 +119,7 @@ const _JSNESPlatform = function(mainElement) {
|
|||||||
},
|
},
|
||||||
//TODO: onBatteryRamWrite
|
//TODO: onBatteryRamWrite
|
||||||
});
|
});
|
||||||
|
//nes.ppu.clipToTvSize = false;
|
||||||
nes.stop = function() {
|
nes.stop = function() {
|
||||||
// TODO: trigger breakpoint
|
// TODO: trigger breakpoint
|
||||||
self.pause();
|
self.pause();
|
||||||
@ -300,10 +302,10 @@ const _JSNESPlatform = function(mainElement) {
|
|||||||
var PPUFLAGS = [
|
var PPUFLAGS = [
|
||||||
["f_nmiOnVblank","NMI_ON_VBLANK"],
|
["f_nmiOnVblank","NMI_ON_VBLANK"],
|
||||||
["f_spVisibility","SPRITES"],
|
["f_spVisibility","SPRITES"],
|
||||||
["f_spClipping","CLIP_SPRITES"],
|
["f_spClipping","NO_CLIP_SPRITES"],
|
||||||
["f_dispType","MONOCHROME"],
|
["f_dispType","MONOCHROME"],
|
||||||
["f_bgVisibility","BACKGROUND"],
|
["f_bgVisibility","BACKGROUND"],
|
||||||
["f_bgClipping","CLIP_BACKGROUND"],
|
["f_bgClipping","NO_CLIP_BACKGROUND"],
|
||||||
];
|
];
|
||||||
for (var i=0; i<PPUFLAGS.length; i++) {
|
for (var i=0; i<PPUFLAGS.length; i++) {
|
||||||
var flag = PPUFLAGS[i];
|
var flag = PPUFLAGS[i];
|
||||||
|
@ -892,7 +892,7 @@ function _recordVideo() {
|
|||||||
};
|
};
|
||||||
f();
|
f();
|
||||||
});
|
});
|
||||||
return true;
|
//TODO? return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setFrameRateUI(fps:number) {
|
function setFrameRateUI(fps:number) {
|
||||||
|
@ -881,13 +881,12 @@ function linkLD65(step:BuildStep) {
|
|||||||
var binpath = "main";
|
var binpath = "main";
|
||||||
if (staleFiles(step, [binpath])) {
|
if (staleFiles(step, [binpath])) {
|
||||||
var errors = [];
|
var errors = [];
|
||||||
var errmsg = '';
|
|
||||||
var LD65 = emglobal.ld65({
|
var LD65 = emglobal.ld65({
|
||||||
instantiateWasm: moduleInstFn('ld65'),
|
instantiateWasm: moduleInstFn('ld65'),
|
||||||
noInitialRun:true,
|
noInitialRun:true,
|
||||||
//logReadFiles:true,
|
//logReadFiles:true,
|
||||||
print:print_fn,
|
print:print_fn,
|
||||||
printErr:function(s) { errmsg += s + '\n'; }
|
printErr:function(s) { errors.push({msg:s,line:0}); }
|
||||||
});
|
});
|
||||||
var FS = LD65['FS'];
|
var FS = LD65['FS'];
|
||||||
var cfgfile = '/' + platform + '.cfg';
|
var cfgfile = '/' + platform + '.cfg';
|
||||||
@ -905,8 +904,6 @@ function linkLD65(step:BuildStep) {
|
|||||||
'-o', 'main', '-m', 'main.map'].concat(step.args, libargs);
|
'-o', 'main', '-m', 'main.map'].concat(step.args, libargs);
|
||||||
//console.log(args);
|
//console.log(args);
|
||||||
execMain(step, LD65, args);
|
execMain(step, LD65, args);
|
||||||
if (errmsg.length)
|
|
||||||
errors.push({line:0, msg:errmsg});
|
|
||||||
if (errors.length)
|
if (errors.length)
|
||||||
return {errors:errors};
|
return {errors:errors};
|
||||||
var aout = FS.readFile("main", {encoding:'binary'});
|
var aout = FS.readFile("main", {encoding:'binary'});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user