started on JSNES support

This commit is contained in:
Steven Hugg 2018-07-27 13:39:09 -04:00
parent 0e807690e8
commit aa1d303000
12 changed files with 842 additions and 54 deletions

View File

@ -215,9 +215,9 @@ if (window.location.host.endsWith('8bitworkshop.com')) {
<script src="javatari.js/release/javatari/javatari.js"></script>
<script src="src/cpu/z80fast.js"></script>
<script src="jsnes/jsnes.min.js"></script>
<!--<script src="src/cpu/6809.js"></script>-->
<!--
<script src="jsnes/build/jsnes.min.js"></script>
<script src="jsnes/lib/dynamicaudio-min.js" type="text/javascript" charset="utf-8"></script>
-->
<!--

2
jsnes/jsnes.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1
jsnes/jsnes.min.js.map Normal file

File diff suppressed because one or more lines are too long

54
presets/nes/conio.c Normal file
View File

@ -0,0 +1,54 @@
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <joystick.h>
const char Text [] = "Hello world!";
int main (void)
{
unsigned char width, height;
/* Set screen colors */
(void) bgcolor (COLOR_BLUE);
/* Clear the screen, put cursor in upper left corner */
clrscr ();
/* Ask for the screen size */
screensize (&width, &height);
/* Draw a border around the screen */
/* Top line */
cputc (CH_ULCORNER);
chline (width - 2);
cputc (CH_URCORNER);
/* Vertical line, left side */
cvlinexy (0, 1, height - 2);
/* Bottom line */
cputc (CH_LLCORNER);
chline (width - 2);
cputc (CH_LRCORNER);
/* Vertical line, right side */
cvlinexy (width - 1, 1, height - 2);
/* Write the greeting in the mid of the screen */
gotoxy ((width - strlen (Text)) / 2, height / 2);
cprintf ("%s", Text);
/* Wait for the user to press a button */
joy_install (joy_static_stddrv);
while (!joy_read (JOY_1)) ;
joy_uninstall ();
/* Clear the screen again */
clrscr ();
/* Done */
return EXIT_SUCCESS;
}

103
presets/nes/ex0.asm Normal file
View File

@ -0,0 +1,103 @@
;;;;; CONSTANTS
PPU_CTRL equ $2000
PPU_MASK equ $2001
PPU_STATUS equ $2002
PPU_SCROLL equ $2005
PPU_ADDR equ $2006
PPU_DATA equ $2007
DMC_FREQ equ $4010
;;;;; CARTRIDGE FILE HEADER
processor 6502
seg Header
org $7FF0
NES_MAPPER equ 0 ;mapper number
NES_PRG_BANKS equ 2 ;number of 16K PRG banks, change to 2 for NROM256
NES_CHR_BANKS equ 1 ;number of 8K CHR banks (0 = RAM)
NES_MIRRORING equ 1 ;0 horizontal, 1 vertical, 8 four screen
.byte $4e,$45,$53,$1a ; header
.byte NES_PRG_BANKS
.byte NES_CHR_BANKS
.byte NES_MIRRORING|(NES_MAPPER<<4)
.byte NES_MAPPER&$f0
.byte 0,0,0,0,0,0,0,0 ; reserved, set to zero
;;;;; CODE
seg Code
org $8000
start:
_exit:
sei ;disable IRQs
cld ;decimal mode not supported
ldx #$ff
txs ;set up stack pointer
inx ;increment X to 0
stx PPU_MASK ;disable rendering
stx DMC_FREQ ;disable DMC interrupts
stx PPU_CTRL ;disable NMI interrupts
jsr WaitSyncSafe ;wait for VSYNC
; clear RAM -- not a subroutine because we clear the stack too
lda #0
tax
.clearRAM
sta $0,x
sta $100,x
; skip $200-$2FF, used for OAM display list
sta $300,x
sta $400,x
sta $500,x
sta $600,x
sta $700,x
inx
bne .clearRAM
; wait for PPU warmup
jsr WaitSync
; set palette background
ldy #$0
lda #$3f
sta PPU_ADDR
sty PPU_ADDR
lda #$1c
sta PPU_DATA
; enable PPU rendering
lda #0
sta PPU_ADDR
sta PPU_ADDR ;PPU addr = 0
sta PPU_SCROLL
sta PPU_SCROLL ;scroll = 0
lda #$90
sta PPU_CTRL ;enable NMI
lda #$1e
sta PPU_MASK ;enable rendering
.endless
jmp .endless ;endless loop
;;;;; SUBROUTINES
; wait for VSYNC to start
WaitSyncSafe: subroutine
bit PPU_STATUS
WaitSync:
bit PPU_STATUS
bpl WaitSync
rts
;;;;; INTERRUPT HANDLERS
nmi:
irq:
rti
;;;;; CPU VECTORS
org $fffa
.word nmi ;$fffa vblank nmi
.word start ;$fffc reset
.word irq ;$fffe irq / brk

168
presets/nes/ex1.asm Normal file
View File

@ -0,0 +1,168 @@
;;;;; CONSTANTS
PPU_CTRL equ $2000
PPU_MASK equ $2001
PPU_STATUS equ $2002
OAM_ADDR equ $2003
OAM_DATA equ $2004
PPU_SCROLL equ $2005
PPU_ADDR equ $2006
PPU_DATA equ $2007
PPU_OAM_DMA equ $4014
DMC_FREQ equ $4010
;;;;; ZERO-PAGE VARIABLES
seg.u ZPVars
org $0
ScrollPos byte ; used during NMI
;;;;; CARTRIDGE FILE HEADER
processor 6502
seg Header
org $7FF0
NES_MAPPER equ 0 ;mapper number
NES_PRG_BANKS equ 2 ;number of 16K PRG banks, change to 2 for NROM256
NES_CHR_BANKS equ 1 ;number of 8K CHR banks (0 = RAM)
NES_MIRRORING equ 1 ;0 horizontal, 1 vertical, 8 four screen
.byte $4e,$45,$53,$1a ; header
.byte NES_PRG_BANKS
.byte NES_CHR_BANKS
.byte NES_MIRRORING|(NES_MAPPER<<4)
.byte NES_MAPPER&$f0
.byte 0,0,0,0,0,0,0,0 ; reserved, set to zero
;;;;; CODE
seg Code
org $8000
start:
_exit:
sei ;disable IRQs
cld ;decimal mode not supported
ldx #$ff
txs ;set up stack pointer
inx ;increment X to 0
stx PPU_MASK ;disable rendering
stx DMC_FREQ ;disable DMC interrupts
stx PPU_CTRL ;disable NMI interrupts
jsr WaitSyncSafe ;wait for VSYNC
; clear RAM -- not a subroutine because we clear the stack too
lda #0
tax
.clearRAM
sta $0,x
sta $100,x
; skip $200-$2FF, used for OAM display list
sta $300,x
sta $400,x
sta $500,x
sta $600,x
sta $700,x
inx
bne .clearRAM
; end of clear RAM routine
jsr SetPalette ;set colors
jsr FillVRAM ;set PPU RAM
jsr WaitSync ;wait for VSYNC (and PPU warmup)
lda #0
sta PPU_ADDR
sta PPU_ADDR ;PPU addr = 0
sta PPU_SCROLL
sta PPU_SCROLL ;scroll = 0
lda #$90
sta PPU_CTRL ;enable NMI
lda #$1e
sta PPU_MASK ;enable rendering
.endless
jmp .endless ;endless loop
;;;;; SUBROUTINES
; set palette colors
SetPalette: subroutine
ldy #$0
lda #$3f
sta PPU_ADDR
sty PPU_ADDR
ldx #4
.loop:
lda Palette,y
sta PPU_DATA
iny
dex
bne .loop
rts
; fill video RAM
FillVRAM: subroutine
txa
ldy #$20
sty PPU_ADDR
sta PPU_ADDR
ldy #$10
.loop:
sta PPU_DATA
adc #1
inx
bne .loop
dey
bne .loop
rts
; wait for VSYNC to start
WaitSyncSafe: subroutine
bit PPU_STATUS
WaitSync:
bit PPU_STATUS
bpl WaitSync
rts
;;;;; INTERRUPT HANDLERS
nmi:
irq:
; save registers
pha ; save A
; update scroll position
inc ScrollPos
lda ScrollPos
sta PPU_SCROLL
sta PPU_SCROLL
; reload registers
pla ; reload A
rti
;;;;; CONSTANT DATA
Palette:
hex 1f001020 ; black, gray, lt gray, white
TextString:
byte "HELLO WORLD!"
byte 0
;;;;; CPU VECTORS
org $fffa
.word nmi ;$fffa vblank nmi
.word start ;$fffc reset
.word irq ;$fffe irq / brk
;;;;; TILE SETS
org $10000
REPEAT 64
hex 003c6666766e663c007e181818381818
hex 007e60300c06663c003c66061c06663c
hex 0006067f661e0e06003c6606067c607e
hex 003c66667c60663c00181818180c667e
hex 003c66663c66663c003c66063e66663c
hex 01010101010101010000000000000000
hex ff000000000000000000000000000000
hex 01020408102040800000000000000000
REPEND

48
presets/nes/hello.c Normal file
View File

@ -0,0 +1,48 @@
#include <nes.h>
const unsigned char TEXT[]="Hello PPU!!!!";
const unsigned char PALETTE[]={0x1, 0x00, 0x10, 0x20}; // blue, gray, lt gray, white
void main (void) {
unsigned char index; // used in 'for' loops
// if we've just powered on,
// wait for PPU to warm-up
waitvsync();
waitvsync();
// turn off screen
PPU.control = 0x0; // NMI off
PPU.mask = 0x0; // screen off
// load the palette
// set PPU address to 0x3f00
PPU.vram.address = 0x3f;
PPU.vram.address = 0x00;
for (index = 0; index < sizeof(PALETTE); index++) {
PPU.vram.data = PALETTE[index];
}
// load the text into VRAM
// set PPU address to 0x21c9
PPU.vram.address = 0x21;
PPU.vram.address = 0xc9;
for (index = 0; index < sizeof(TEXT); index++) {
PPU.vram.data = TEXT[index];
}
// reset the scroll position to 0
PPU.scroll = 0;
PPU.scroll = 0;
// reset the PPU address to 0x2000 (frame start)
PPU.vram.address = 0x20;
PPU.vram.address = 0x00;
// turn on the screen
PPU.mask = 0x1e;
// infinite loop
while (1);
}

277
presets/nes/siegegame.c Normal file
View File

@ -0,0 +1,277 @@
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <nes.h>
#include <joystick.h>
#define COLS 32
#define ROWS 28
typedef unsigned char byte;
typedef signed char sbyte;
typedef unsigned short word;
// read a character from VRAM.
// this is tricky because we have to wait
// for VSYNC to start, then set the VRAM
// address to read, then set the VRAM address
// back to the start of the frame.
byte getchar(byte x, byte y) {
// compute VRAM read address
word addr = 0x2020+x+y*32;
byte rd;
// wait for VBLANK to start
waitvsync();
// set VRAM read address in PPU
PPU.vram.address = addr>>8;
PPU.vram.address = addr&0xff;
// read the char from PPUDATA
rd = PPU.vram.data; // discard
rd = PPU.vram.data; // keep this one
// reset the VRAM address to start of frame
PPU.vram.address = 0x00;
PPU.vram.address = 0x00;
// return result
return rd;
}
void delay(byte count) {
while (count--) waitvsync();
}
////////// GAME DATA
typedef struct {
byte x;
byte y;
byte dir;
word score;
char head_attr;
char tail_attr;
int collided:1;
int human:1;
} Player;
Player players[2];
byte credits = 0;
byte frames_per_move;
#define START_SPEED 12
#define MAX_SPEED 5
#define MAX_SCORE 7
///////////
const char BOX_CHARS[8] = { 17, 8, 20, 18, 11, 11, 14, 14 };
void draw_box(byte x, byte y, byte x2, byte y2, const char* chars) {
byte x1 = x;
cputcxy(x, y, chars[2]);
cputcxy(x2, y, chars[3]);
cputcxy(x, y2, chars[0]);
cputcxy(x2, y2, chars[1]);
while (++x < x2) {
cputcxy(x, y, chars[5]);
cputcxy(x, y2, chars[4]);
}
while (++y < y2) {
cputcxy(x1, y, chars[6]);
cputcxy(x2, y, chars[7]);
}
}
void draw_playfield() {
draw_box(0,1,COLS-1,ROWS-1,BOX_CHARS);
cputsxy(0,0,"Plyr1:");
cputsxy(20,0,"Plyr2:");
cputcxy(7,0,players[0].score+'0');
cputcxy(27,0,players[1].score+'0');
}
typedef enum { D_RIGHT, D_DOWN, D_LEFT, D_UP } dir_t;
const char DIR_X[4] = { 1, 0, -1, 0 };
const char DIR_Y[4] = { 0, 1, 0, -1 };
void init_game() {
memset(players, 0, sizeof(players));
players[0].head_attr = '1';
players[1].head_attr = '2';
players[0].tail_attr = 1;
players[1].tail_attr = 9;
frames_per_move = START_SPEED;
}
void reset_players() {
players[0].x = players[0].y = 5;
players[0].dir = D_RIGHT;
players[1].x = COLS-6;
players[1].y = ROWS-6;
players[1].dir = D_LEFT;
players[0].collided = players[1].collided = 0;
}
void draw_player(Player* p) {
cputcxy(p->x, p->y, p->head_attr);
}
void move_player(Player* p) {
cputcxy(p->x, p->y, p->tail_attr);
p->x += DIR_X[p->dir];
p->y += DIR_Y[p->dir];
if (getchar(p->x, p->y) != ' ')
p->collided = 1;
draw_player(p);
}
void human_control(Player* p) {
byte dir = 0xff;
byte joy;
if (!p->human) return;
joy = joy_read (JOY_1);
if (joy & JOY_LEFT_MASK) dir = D_LEFT;
if (joy & JOY_RIGHT_MASK) dir = D_RIGHT;
if (joy & JOY_UP_MASK) dir = D_UP;
if (joy & JOY_DOWN_MASK) dir = D_DOWN;
// don't let the player reverse
if (dir < 0x80 && dir != (p->dir ^ 2)) {
p->dir = dir;
}
}
byte ai_try_dir(Player* p, dir_t dir, byte shift) {
byte x,y;
dir &= 3;
x = p->x + (DIR_X[dir] << shift);
y = p->y + (DIR_Y[dir] << shift);
if (x < COLS && y < ROWS && getchar(x, y) == ' ') {
p->dir = dir;
return 1;
} else {
return 0;
}
}
void ai_control(Player* p) {
dir_t dir;
if (p->human) return;
dir = p->dir;
if (!ai_try_dir(p, dir, 0)) {
ai_try_dir(p, dir+1, 0);
ai_try_dir(p, dir-1, 0);
} else {
ai_try_dir(p, dir-1, 0) && ai_try_dir(p, dir-1, 1+(rand() & 3));
ai_try_dir(p, dir+1, 0) && ai_try_dir(p, dir+1, 1+(rand() & 3));
ai_try_dir(p, dir, rand() & 3);
}
}
byte gameover;
void flash_colliders() {
byte i;
// flash players that collided
for (i=0; i<56; i++) {
//cv_set_frequency(CV_SOUNDCHANNEL_0, 1000+i*8);
//cv_set_attenuation(CV_SOUNDCHANNEL_0, i/2);
if (players[0].collided) players[0].head_attr ^= 0x80;
if (players[1].collided) players[1].head_attr ^= 0x80;
delay(2);
draw_player(&players[0]);
draw_player(&players[1]);
}
//cv_set_attenuation(CV_SOUNDCHANNEL_0, 28);
}
void make_move() {
byte i;
for (i=0; i<frames_per_move; i++) {
human_control(&players[0]);
delay(1);
}
ai_control(&players[0]);
ai_control(&players[1]);
// if players collide, 2nd player gets the point
move_player(&players[1]);
move_player(&players[0]);
}
void declare_winner(byte winner) {
byte i;
clrscr();
for (i=0; i<ROWS/2-3; i++) {
draw_box(i,i,COLS-1-i,ROWS-1-i,BOX_CHARS);
delay(1);
}
cputsxy(12,10,"WINNER:");
cputsxy(12,13,"PLAYER ");
cputcxy(12+7, 13, '1'+winner);
delay(75);
gameover = 1;
}
#define AE(a,b,c,d) (((a)<<0)|((b)<<2)|((c)<<4)|((d)<<6))
// this is attribute table data,
// each 2 bits defines a color palette
// for a 16x16 box
const unsigned char Attrib_Table[0x40]={
AE(3,3,1,1),AE(3,3,1,1),AE(3,3,1,1),AE(3,3,1,1), AE(2,2,1,1),AE(2,2,1,1),AE(2,2,1,1),AE(2,2,1,1),
AE(1,0,1,0),AE(0,0,0,0),AE(0,0,0,0),AE(0,0,0,0), AE(0,0,0,0),AE(0,0,0,0),AE(0,0,0,0),AE(0,1,0,1),
AE(1,0,1,0),AE(0,0,0,0),AE(0,0,0,0),AE(0,0,0,0), AE(0,0,0,0),AE(0,0,0,0),AE(0,0,0,0),AE(0,1,0,1),
AE(1,0,1,0),AE(0,0,0,0),AE(0,0,0,0),AE(0,0,0,0), AE(0,0,0,0),AE(0,0,0,0),AE(0,0,0,0),AE(0,1,0,1),
AE(1,0,1,0),AE(0,0,0,0),AE(0,0,0,0),AE(0,0,0,0), AE(0,0,0,0),AE(0,0,0,0),AE(0,0,0,0),AE(0,1,0,1),
AE(1,0,1,0),AE(0,0,0,0),AE(0,0,0,0),AE(0,0,0,0), AE(0,0,0,0),AE(0,0,0,0),AE(0,0,0,0),AE(0,1,0,1),
AE(1,0,1,0),AE(0,0,0,0),AE(0,0,0,0),AE(0,0,0,0), AE(0,0,0,0),AE(0,0,0,0),AE(0,0,0,0),AE(0,1,0,1),
AE(1,1,1,1),AE(1,1,1,1),AE(1,1,1,1),AE(1,1,1,1), AE(1,1,1,1),AE(1,1,1,1),AE(1,1,1,1),AE(1,1,1,1),
};
// put 8x8 grid of palette entries into the PPU
void setup_attrib_table() {
byte index;
waitvsync(); // wait for VBLANK
PPU.vram.address = 0x23;
PPU.vram.address = 0xc0;
for( index = 0; index < 0x40; ++index ){
PPU.vram.data = Attrib_Table[index];
}
}
void play_round() {
reset_players();
clrscr();
setup_attrib_table();
draw_playfield();
while (1) {
make_move();
if (players[0].collided || players[1].collided) break;
}
flash_colliders();
// add scores to players that didn't collide
if (players[0].collided) players[1].score++;
if (players[1].collided) players[0].score++;
// increase speed
if (frames_per_move > MAX_SPEED) frames_per_move--;
// game over?
if (players[0].score != players[1].score) {
if (players[0].score >= MAX_SCORE)
declare_winner(0);
else if (players[1].score >= MAX_SCORE)
declare_winner(1);
}
}
void play_game() {
gameover = 0;
init_game();
players[0].human = 1;
while (!gameover) {
play_round();
}
}
void main() {
joy_install (joy_static_stddrv);
play_game();
}

View File

@ -109,32 +109,14 @@ abstract class BaseDebugPlatform {
}
}
////// 6502
abstract class BaseFrameBasedPlatform extends BaseDebugPlatform {
debugPCDelta = -1;
function getToolForFilename_6502(fn:string) : string {
if (fn.endsWith(".pla")) return "plasm";
if (fn.endsWith(".c")) return "cc65";
if (fn.endsWith(".s")) return "ca65";
if (fn.endsWith(".acme")) return "acme";
return "dasm"; // .a
}
export abstract class Base6502Platform extends BaseDebugPlatform {
newCPU(membus : MemoryBus) {
var cpu = new jt.M6502();
cpu.connectBus(membus);
return cpu;
evalDebugCondition() {
if (this.debugCondition && !this.debugBreakState) {
this.debugCondition();
}
}
getOpcodeMetadata(opcode, offset) {
return Javatari.getOpcodeMetadata(opcode, offset); // TODO
}
getOriginPC() : number {
return (this.readAddress(0xfffc) | (this.readAddress(0xfffd) << 8)) & 0xffff;
}
restartDebugState() {
if (this.debugCondition && !this.debugBreakState) {
this.debugSavedState = this.saveState();
@ -145,24 +127,38 @@ export abstract class Base6502Platform extends BaseDebugPlatform {
breakpointHit(targetClock : number) {
this.debugTargetClock = targetClock;
this.debugBreakState = this.saveState();
this.debugBreakState.c.PC = (this.debugBreakState.c.PC-1) & 0xffff;
this.debugBreakState.c.PC = (this.debugBreakState.c.PC + this.debugPCDelta) & 0xffff;
console.log("Breakpoint at clk", this.debugClock, "PC", this.debugBreakState.c.PC.toString(16));
this.pause();
if (this.onBreakpointHit) {
this.onBreakpointHit(this.debugBreakState);
}
}
// TODO: lower bound of clock value
runEval(evalfunc : DebugEvalCondition) {
this.setDebugCondition( () => {
if (this.debugClock++ >= this.debugTargetClock) {
var cpuState = this.getCPUState();
cpuState.PC = (cpuState.PC + this.debugPCDelta) & 0xffff;
if (evalfunc(cpuState)) {
this.breakpointHit(this.debugClock-1);
return true;
} else {
return false;
}
}
});
}
step() {
var previousPC = -1;
this.setDebugCondition( () => {
if (this.debugClock++ > this.debugTargetClock) {
//console.log(this.debugClock, this.debugTargetClock, this.getCPUState().PC, this.getCPUState());
if (this.debugClock++ >= this.debugTargetClock) {
var thisState = this.getCPUState();
if (previousPC < 0) {
previousPC = thisState.PC;
} else {
// doesn't work w/ endless loops
if (thisState.PC != previousPC && thisState.T == 0) {
//console.log(previousPC.toString(16), thisPC.toString(16));
this.breakpointHit(this.debugClock-1);
return true;
}
@ -188,20 +184,34 @@ export abstract class Base6502Platform extends BaseDebugPlatform {
return false;
});
}
runEval(evalfunc : DebugEvalCondition) {
this.setDebugCondition( () => {
if (this.debugClock++ > this.debugTargetClock) {
var cpuState = this.getCPUState();
cpuState.PC = (cpuState.PC-1)&0xffff;
if (evalfunc(cpuState)) {
this.breakpointHit(this.debugClock-1);
return true;
} else {
return false;
}
}
});
}
////// 6502
function getToolForFilename_6502(fn:string) : string {
if (fn.endsWith(".pla")) return "plasm";
if (fn.endsWith(".c")) return "cc65";
if (fn.endsWith(".s")) return "ca65";
if (fn.endsWith(".acme")) return "acme";
return "dasm"; // .a
}
export abstract class Base6502Platform extends BaseFrameBasedPlatform {
newCPU(membus : MemoryBus) {
var cpu = new jt.M6502();
cpu.connectBus(membus);
return cpu;
}
getOpcodeMetadata(opcode, offset) {
return Javatari.getOpcodeMetadata(opcode, offset); // TODO
}
getOriginPC() : number {
return (this.readAddress(0xfffc) | (this.readAddress(0xfffd) << 8)) & 0xffff;
}
runUntilReturn() {
var depth = 1;
this.runEval( (c:CpuState) => {
@ -214,6 +224,7 @@ export abstract class Base6502Platform extends BaseDebugPlatform {
return false;
});
}
disassemble(pc:number, read:(addr:number)=>number) : DisasmLine {
return disassemble6502(pc, read(pc), read(pc+1), read(pc+2));
}
@ -336,7 +347,6 @@ export abstract class BaseZ80Platform extends BaseDebugPlatform {
breakpointHit(targetClock : number) {
this.debugTargetClock = targetClock;
this.debugBreakState = this.saveState();
//this.debugBreakState.c.PC = (this.debugBreakState.c.PC-1) & 0xffff;
console.log("Breakpoint at clk", this.debugBreakState.c.T, "PC", this.debugBreakState.c.PC.toString(16));
this.pause();
if (this.onBreakpointHit) {

View File

@ -278,6 +278,7 @@ var Apple2Platform = function(mainElement) {
for (var i=0; i<2000000; i++) {
cpu.clockPulse();
if (this.getCPUState().PC == 0xc602) {
cpu.clockPulse();
cpu.clockPulse();
break;
}

View File

@ -20,33 +20,152 @@ new jt.M6502(); // to get Javatari.getOpcodeMetadata
/// JSNES
var JSNES_KEYCODE_MAP = makeKeycodeMap([
[Keys.VK_Z, 0, 0],
[Keys.VK_X, 0, 1],
[Keys.VK_2, 0, 2],
[Keys.VK_1, 0, 3],
[Keys.VK_UP, 0, 4],
[Keys.VK_DOWN, 0, 5],
[Keys.VK_LEFT, 0, 6],
[Keys.VK_RIGHT, 0, 7],
[Keys.VK_Q, 1, 0],
[Keys.VK_E, 1, 1],
[Keys.VK_4, 1, 2],
[Keys.VK_3, 1, 3],
[Keys.VK_W, 1, 4],
[Keys.VK_S, 1, 5],
[Keys.VK_A, 1, 6],
[Keys.VK_D, 1, 7],
]);
var JSNESPlatform = function(mainElement) {
var self = this;
this.__proto__ = new Base6502Platform();
this.debugPCDelta = 1;
var nes;
var rom;
var video, audio, timer;
var audioFrequency = 44100;
this.getPresets = function() { return NES_PRESETS; }
this.getPresets = function() { return NES_CONIO_PRESETS; }
this.start = function() {
nes = new JSNES({
'ui': $('#emulator').JSNESUI({})
video = new RasterVideo(mainElement,256,224);
audio = new SampleAudio(audioFrequency);
video.create();
var idata = video.getFrameData();
nes = new jsnes.NES({
onFrame: function(frameBuffer) {
for (var i=0; i<frameBuffer.length; i++)
idata[i] = frameBuffer[i] | 0xff000000;
video.updateFrame();
self.restartDebugState();
},
onAudioSample: function(left, right) {
audio.feedSample(left+right, 1);
},
onStatusUpdate: function(s) {
console.log(s);
},
//onBatteryRamWrite
});
nes.stop = function() {
self.pause();// TODO
console.log(nes.cpu.toJSON()); // TODO
throw ("CPU STOPPED");
};
// insert debug hook
nes.cpu._emulate = nes.cpu.emulate;
nes.cpu.emulate = function() {
var cycles = nes.cpu._emulate();
//if (self.debugCondition && !self.debugBreakState && self.debugClock < 100) console.log(self.debugClock, nes.cpu.REG_PC);
self.evalDebugCondition();
return cycles;
}
timer = new AnimationTimer(60, function() {
nes.frame();
});
// set keyboard map
setKeyboardFromMap(video, [], JSNES_KEYCODE_MAP, function(o,key,code,flags) {
if (flags & 1)
nes.buttonDown(o.index+1, o.mask); // controller, button
else
nes.buttonUp(o.index+1, o.mask); // controller, button
});
}
this.loadROM = function(title, data) {
nes.loadRom(data);
var romstr = String.fromCharCode.apply(null, data);
nes.loadROM(romstr);
}
this.getOpcodeMetadata = Javatari.getOpcodeMetadata;
this.getToolForFilename = getToolForFilename_6502;
this.getDefaultExtension = function() { return ".c"; };
this.isRunning = function() { return nes.isRunning; }
this.pause = function() { if (rom) nes.stop(); }
this.resume = function() { if (rom) nes.start(); }
this.clearDebug = function() { // TODO
this.reset = function() {
//nes.cpu.reset(); // doesn't work right, crashes
nes.cpu.requestIrq(nes.cpu.IRQ_RESET);
}
this.isRunning = function() {
return timer.isRunning();
}
this.pause = function() {
timer.stop();
audio.stop();
}
this.resume = function() {
timer.start();
audio.start();
}
this.getCPUState = function() {
var c = nes.cpu.toJSON();
this.copy6502REGvars(c);
return c;
}
this.saveState = function() {
//var s = $.extend(true, {}, nes);
var s = nes.toJSON();
s.c = s.cpu;
this.copy6502REGvars(s.c);
s.cpu.mem = s.cpu.mem.slice(0);
s.ppu.vramMem = s.ppu.vramMem.slice(0);
s.ppu.spriteMem = s.ppu.spriteMem.slice(0);
return s;
}
this.loadState = function(state) {
nes.fromJSON(state);
//nes.cpu.fromJSON(state.cpu);
//nes.mmap.fromJSON(state.mmap);
//nes.ppu.fromJSON(state.ppu);
nes.cpu.mem = state.cpu.mem.slice(0);
nes.ppu.vramMem = state.ppu.vramMem.slice(0);
nes.ppu.spriteMem = state.ppu.spriteMem.slice(0);
//$.extend(nes, state);
}
this.readAddress = function(addr) {
return nes.cpu.mem[addr] & 0xff;
}
this.copy6502REGvars = function(c) {
c.T = 0;
c.PC = c.REG_PC;
c.A = c.REG_ACC;
c.X = c.REG_X;
c.Y = c.REG_Y;
c.SP = c.REG_SP;
c.Z = c.F_ZERO;
c.N = c.F_SIGN;
c.V = c.F_OVERFLOW;
c.D = c.F_DECIMAL;
c.C = c.F_CARRY;
c.R = 1;
c.o = this.readAddress(c.PC+1);
return c;
}
}
/// MAME support
@ -102,7 +221,7 @@ var NESLibPlatform = function(mainElement) {
///
//PLATFORMS['nes'] = JSNESPlatform;
PLATFORMS['nes'] = JSNESPlatform;
PLATFORMS['nes-lib'] = NESLibPlatform;
PLATFORMS['nes-conio'] = NESConIOPlatform;

View File

@ -102,6 +102,11 @@ var PLATFORM_PARAMS = {
//'main.rel'
],
},
'nes': { //TODO
define: '__NES__',
cfgfile: 'neslib.cfg',
libargs: ['neslib.lib', 'nes.lib'],
},
'nes-conio': {
cfgfile: 'nes.cfg',
define: '__NES__',