Prepare NES machine type

This commit is contained in:
drmortalwombat 2023-03-04 14:07:34 +01:00
parent f34aa2dcbe
commit 6189e0cc49
13 changed files with 1952 additions and 68 deletions

View File

@ -131,6 +131,9 @@ w0:
#elif defined(OSCAR_TARGET_BIN)
#elif defined(OSCAR_TARGET_NES)
sei
cld
#else
byt 0x0b
byt 0x08
@ -153,6 +156,7 @@ w0:
stx spentry
#endif
// Clear BSS Segment
lda #<BSSStart
@ -239,6 +243,7 @@ bcode:
#endif
spexit:
#if !defined(OSCAR_TARGET_NES)
lda #$4c
sta $54
lda #0
@ -248,6 +253,7 @@ spexit:
#if defined(__C128__)
lda #0
sta 0xff00
#endif
#endif
rts
}

2
include/nes/nes.c Normal file
View File

@ -0,0 +1,2 @@
#include "nes.h"

76
include/nes/nes.h Normal file
View File

@ -0,0 +1,76 @@
#ifndef NES_NES_H
#define NES_NES_H
#include <c64/types.h>
#define PPU_CTRL_NT_2000 0b00000000
#define PPU_CTRL_NT_2400 0b00000001
#define PPU_CTRL_NT_2800 0b00000010
#define PPU_CTRL_NT_2C00 0b00000011
#define PPU_CTRL_INC_1 0b00000000
#define PPU_CTRL_INC_32 0b00000100
#define PPU_CTRL_SPR_0000 0b00000000
#define PPU_CTRL_SPR_1000 0b00001000
#define PPU_CTRL_BG_0000 0b00000000
#define PPU_CTRL_BG_1000 0b00010000
#define PPU_CTRL_SPR_8X8 0b00000000
#define PPU_CTRL_SPR_8X16 0b00100000
#define PPU_CTRL_NMI 0b10000000
#define PPU_MASK_GREYSCALE 0b00000001
#define PPU_MASK_BG8 0b00000010
#define PPU_MASK_SPR8 0b00000100
#define PPU_MASK_BG_ON 0b00001000
#define PPU_MASK_SPR_ON 0b00010000
#define PPU_MASK_EM_RED 0b00100000
#define PPU_MASK_EM_GREEN 0b01000000
#define PPU_MASK_EM_BLUE 0b10000000
struct PPU
{
volatile byte control;
volatile byte mask;
volatile byte status;
volatile byte oamaddr;
volatile byte oamdata;
volatile byte scroll;
volatile byte addr;
volatile byte data;
};
#define ppu (*((struct PPU *)0x2000))
struct NESIO
{
volatile byte sq1_volume;
volatile byte sq1_sweep;
volatile word sq1_freq;
volatile byte sq2_volume;
volatile byte sq2_sweep;
volatile word sq2_freq;
volatile byte tri_volume;
volatile byte tri_pad;
volatile word tri_freq;
volatile byte noise_volume;
volatile byte noise_pad;
volatile word noise_freq;
volatile byte dmc_freq;
volatile byte dmc_raw;
volatile byte dmc_start;
volatile byte dmc_length;
volatile byte oamdma;
volatile byte channels;
volatile byte input[2];
};
#define nesio (*((struct NESIO *)0x4000))
#pragma compile("nes.c")
#endif

648
include/nes/neslib.c Normal file
View File

@ -0,0 +1,648 @@
#include "neslib.h"
// NES hardware-dependent functions by Shiru (shiru@mail.ru)
// with improvements by VEG
// Feel free to do anything you want with this code, consider it Public Domain
const char palBrightTable[] = {
0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0f,0x0f,0x0f,
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x00,0x00,0x00,
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x10,0x10,0x10,
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x20,0x20,0x20,
0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,
0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,
0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,
0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30
};
char OAM_BUF[256];
char PAL_BUF[32];
#pragma align(OAM_BUF, 256)
char NTSC_MODE;
volatile char FRAME_CNT1;
volatile char FRAME_CNT2;
char VRAM_UPDATE;
char * NAME_UPD_ADR;
char NAME_UPD_ENABLE;
char PAL_UPDATE;
const char * PAL_BG_PTR;
const char * PAL_SPR_PTR;
char SCROLL_X;
char SCROLL_Y;
char SCROLL_X1;
char SCROLL_Y1;
char PAD_STATE[2];
char PAD_STATEP[2];
char PAD_STATET[2];
char PPU_CTRL_VAR;
char PPU_CTRL_VAR1;
char PPU_MASK_VAR;
char RAND_SEED[2];
int main(void)
{
ppu.mask = 0;
nesio.dmc_freq = 0;
ppu.control = 0;
char c = ppu.status;
do {} while (!(ppu.status & 0x80));
do {} while (!(ppu.status & 0x80));
nesio.input[1] = 0x40;
ppu.addr = 0x3f;
ppu.addr = 0x00;
for(char i=0; i<32; i++)
ppu.data = 0x0f;
ppu.addr = 0x20;
ppu.addr = 0x00;
for(unsigned i=0; i<0x1000; i++)
ppu.data = 0x00;
char i = 0;
do {
((char *)0x200)[i] = 0;
((char *)0x300)[i] = 0;
((char *)0x400)[i] = 0;
((char *)0x500)[i] = 0;
((char *)0x600)[i] = 0;
((char *)0x700)[i] = 0;
i++;
} while (i);
pal_bright(4);
pal_clear();
oam_clear();
PPU_CTRL_VAR = 0x80;
PPU_MASK_VAR = 0x06;
ppu.control = 0x80;
RAND_SEED[0] = 0xfd;
RAND_SEED[1] = 0xfd;
ppu.scroll = 0x00;
ppu.scroll = 0x00;
ppu.oamaddr = 0x00;
nes_game();
return 0;
}
__hwinterrupt void nmi(void)
{
if (PPU_MASK_VAR & 0x18)
{
nesio.oamdma = (unsigned)(&OAM_BUF[0]) >> 8;
if (PAL_UPDATE)
{
PAL_UPDATE = 0;
ppu.addr = 0x3f;
ppu.addr = 0x00;
char c = PAL_BG_PTR[PAL_BUF[0]];
ppu.data = c;
ppu.data = PAL_BG_PTR[PAL_BUF[1]];
ppu.data = PAL_BG_PTR[PAL_BUF[2]];
ppu.data = PAL_BG_PTR[PAL_BUF[3]];
#pragma unroll(full)
for(char j=0; j<3; j++)
{
ppu.data = c;
ppu.data = PAL_BG_PTR[PAL_BUF[5 + 4 * j + 0]];
ppu.data = PAL_BG_PTR[PAL_BUF[5 + 4 * j + 1]];
ppu.data = PAL_BG_PTR[PAL_BUF[5 + 4 * j + 2]];
}
#pragma unroll(full)
for(char j=0; j<4; j++)
{
ppu.data = c;
ppu.data = PAL_SPR_PTR[PAL_BUF[17 + 4 * j + 0]];
ppu.data = PAL_SPR_PTR[PAL_BUF[17 + 4 * j + 1]];
ppu.data = PAL_SPR_PTR[PAL_BUF[17 + 4 * j + 2]];
}
}
if (VRAM_UPDATE)
{
VRAM_UPDATE = 0;
if (NAME_UPD_ENABLE)
flush_vram_update(NAME_UPD_ADR);
}
ppu.addr = 0x00;
ppu.addr = 0x00;
ppu.scroll = SCROLL_X;
ppu.scroll = SCROLL_Y;
ppu.control = PPU_CTRL_VAR;
}
ppu.mask = PPU_MASK_VAR;
FRAME_CNT1++;
FRAME_CNT2++;
if (FRAME_CNT2 == 6)
FRAME_CNT2 = 0;
// jsr FamiToneUpdate
}
void pal_all(const char *data)
{
for(char i=0; i<32; i++)
PAL_BUF[i] = data[i];
PAL_UPDATE++;
}
void pal_bg(const char *data)
{
for(char i=0; i<16; i++)
PAL_BUF[i] = data[i];
PAL_UPDATE++;
}
void pal_spr(const char *data)
{
for(char i=0; i<16; i++)
PAL_BUF[i + 16] = data[i];
PAL_UPDATE++;
}
void pal_col(unsigned char index,unsigned char color)
{
PAL_BUF[index & 0x1f] = color;
PAL_UPDATE++;
}
void pal_clear(void)
{
for(char i=0; i<32; i++)
PAL_BUF[i] = 0x0f;
PAL_UPDATE++;
}
void pal_spr_bright(unsigned char bright)
{
PAL_SPR_PTR = palBrightTable + 16 * bright;
PAL_UPDATE++;
}
void pal_bg_bright(unsigned char bright)
{
PAL_BG_PTR = palBrightTable + 16 * bright;
PAL_UPDATE++;
}
void pal_bright(unsigned char bright)
{
pal_spr_bright(bright);
pal_bg_bright(bright);
}
void ppu_off(void)
{
PPU_MASK_VAR &= 0b11100111;
ppu_wait_nmi();
}
void ppu_on_all(void)
{
PPU_MASK_VAR|= 0b00011000;
ppu_wait_nmi();
}
void ppu_on_bg(void)
{
PPU_MASK_VAR |= 0b00001000;
ppu_wait_nmi();
}
void ppu_on_spr(void)
{
PPU_MASK_VAR |= 0b00010000;
ppu_wait_nmi();
}
void ppu_mask(unsigned char mask)
{
PPU_MASK_VAR = mask;
}
unsigned char ppu_system(void)
{
return NTSC_MODE;
}
unsigned char get_ppu_ctrl_var(void)
{
return PPU_CTRL_VAR;
}
void set_ppu_ctrl_var(unsigned char var)
{
PPU_CTRL_VAR = var;
}
void oam_clear(void)
{
char i = 0;
do {
OAM_BUF[i] = 0;
i += 4;
} while (i);
}
void oam_size(unsigned char size)
{
if (size & 1)
PPU_CTRL_VAR |= 0x20;
else
PPU_CTRL_VAR &= ~0x20;
}
unsigned char oam_spr(unsigned char x,unsigned char y,unsigned char chrnum,unsigned char attr,unsigned char sprid)
{
OAM_BUF[sprid + 2] = attr;
OAM_BUF[sprid + 1] = chrnum;
OAM_BUF[sprid + 0] = y;
OAM_BUF[sprid + 3] = x;
return attr + 4;
}
unsigned char oam_meta_spr(unsigned char x,unsigned char y,unsigned char sprid,const unsigned char *data)
{
char i = 0;
while (!(data[i] & 0x80))
{
OAM_BUF[sprid + 3] = x + data[i + 0];
OAM_BUF[sprid + 0] = y + data[i + 1];
OAM_BUF[sprid + 1] = data[i + 2];
OAM_BUF[sprid + 2] = data[i + 3];
sprid += 4;
i += 4;
}
return sprid;
}
void oam_hide_rest(unsigned char sprid)
{
do {
OAM_BUF[sprid] = 240;
sprid += 4;
} while (sprid);
}
void ppu_wait_frame(void)
{
VRAM_UPDATE = 1;
char c = FRAME_CNT1;
while (c == FRAME_CNT1) ;
if (NTSC_MODE)
{
while (FRAME_CNT2 == 5) ;
}
}
void ppu_wait_nmi(void)
{
VRAM_UPDATE = 1;
char c = FRAME_CNT1;
while (c == FRAME_CNT1) ;
}
void vram_unrle(const unsigned char *data)
{
char tag = *data++;
char b;
for(;;)
{
char c = *data++;
if (c != tag)
{
ppu.data = c;
b = c;
}
else
{
c = *data++;
if (!c)
return;
while (c)
{
ppu.data = b;
c--;
}
}
}
}
void scroll(unsigned int x,unsigned int y)
{
char b = (PPU_CTRL_VAR & 0xfc) | ((x >> 8) & 1);
if (y >= 240)
{
y -= 240;
b |= 2;
}
SCROLL_Y = y;
SCROLL_X = x;
PPU_CTRL_VAR = b;
}
void split(unsigned int x,unsigned int y)
{
char b = (PPU_CTRL_VAR & 0xfc) | ((x >> 8) & 1);
SCROLL_X1 = x;
PPU_CTRL_VAR1 = b;
while (ppu.status & 0x40) ;
while (!(ppu.status & 0x40)) ;
ppu.scroll = SCROLL_X1;
ppu.scroll = 0;
ppu.control = PPU_CTRL_VAR1;
}
void bank_spr(unsigned char n)
{
if (n & 1)
PPU_CTRL_VAR |= 0x08;
else
PPU_CTRL_VAR &= ~0x08;
}
void bank_bg(unsigned char n)
{
if (n & 1)
PPU_CTRL_VAR |= 0x10;
else
PPU_CTRL_VAR &= ~0x10;
}
void vram_read(unsigned char *dst,unsigned int size)
{
for(unsigned i=size; i!=0; i--)
*dst++ = ppu.data;
}
void vram_write(const unsigned char *src,unsigned int size)
{
for(unsigned i=size; i!=0; i--)
ppu.data = *src++;
}
void music_play(unsigned char song)
{
//_music_play=FamiToneMusicPlay
}
void music_stop(void)
{
//_music_stop=FamiToneMusicStop
}
void music_pause(unsigned char pause)
{
//_music_pause=FamiToneMusicPause
}
void sfx_play(unsigned char sound,unsigned char channel)
{
#if 0
_sfx_play:
.if(FT_SFX_ENABLE)
and #$03
tax
lda @sfxPriority,x
tax
jsr popa
jmp FamiToneSfxPlay
@sfxPriority:
.byte FT_SFX_CH0,FT_SFX_CH1,FT_SFX_CH2,FT_SFX_CH3
.else
rts
.endif
#endif
}
void sample_play(unsigned char sample)
{
#if 0
.if(FT_DPCM_ENABLE)
_sample_play=FamiToneSamplePlay
.else
_sample_play:
rts
.endif
#endif
}
unsigned char pad_poll(unsigned char pad)
{
char buf[3];
for(char j=0; j<3; j++)
{
nesio.input[0] = 1;
nesio.input[0] = 0;
char c = 0;
for(char i=0; i<8; i++)
{
c = (c | (nesio.input[pad] << 8)) >> 1;
}
buf[j] = c;
}
char b = buf[0];
if (b != buf[1] && b != buf[2])
b = buf[1];
PAD_STATE[pad] = b;
PAD_STATET[pad] = (b ^ PAD_STATEP[pad]) & PAD_STATE[pad];
PAD_STATEP[pad] = b;
return b;
}
unsigned char pad_trigger(unsigned char pad)
{
pad_poll(pad);
return PAD_STATET[pad];
}
unsigned char pad_state(unsigned char pad)
{
return PAD_STATE[pad];
}
unsigned char rand1(void)
{
if (RAND_SEED[0] & 0x80)
{
RAND_SEED[0] <<= 1;
RAND_SEED[0] ^= 0xcf;
}
else
RAND_SEED[0] <<= 1;
return RAND_SEED[0];
}
unsigned char rand2(void)
{
if (RAND_SEED[1] & 0x80)
{
RAND_SEED[1] <<= 1;
RAND_SEED[1] ^= 0xd7;
}
else
RAND_SEED[1] <<= 1;
}
unsigned char rand8(void)
{
return rand1() + rand2();
}
unsigned int rand16(void)
{
return (rand1() << 8) | rand2();
}
void set_rand(unsigned seed)
{
RAND_SEED[0] = seed & 0xff;
RAND_SEED[1] = seed >> 8;
}
void set_vram_update(unsigned char *buf)
{
NAME_UPD_ADR = buf;
NAME_UPD_ENABLE = buf != nullptr;
}
void flush_vram_update(unsigned char *buf)
{
char i = 0;
for(;;)
{
char c = buf[i++];
if (c < 0x40)
{
ppu.addr = c;
ppu.addr = buf[i++];
ppu.data = buf[i++];
}
else
{
if (c < 0x80)
ppu.control = PPU_CTRL_VAR | 0x04;
else if (c != 0xff)
ppu.control = PPU_CTRL_VAR & ~0x04;
else
return;
ppu.addr = c & 0x3f;
ppu.addr = buf[i++];
c = buf[i++];
do {
ppu.data = buf[i++];
c--;
} while (c);
ppu.control = PPU_CTRL_VAR;
}
}
}
void vram_adr(unsigned int addr)
{
ppu.addr = addr >> 8;
ppu.addr = addr & 0xff;
}
void vram_put(unsigned char n)
{
ppu.data = n;
}
void vram_fill(unsigned char n,unsigned int size)
{
for(unsigned i=size; i!=0; i--)
ppu.data = n;
}
void vram_inc(unsigned char n)
{
if (n)
PPU_CTRL_VAR |= 0x04;
else
PPU_CTRL_VAR &= ~0x04;
ppu.control = PPU_CTRL_VAR;
}
void memfill(void *dst,unsigned char value,unsigned int size)
{
for(unsigned i=size; i!=0; i--)
*dst++ = value;
}
unsigned char nesclock(void)
{
return FRAME_CNT1;
}
void delay(unsigned char frames)
{
while (frames)
{
ppu_wait_nmi();
frames--;
}
}
#pragma data(boot)
__export struct Boot
{
void * nmi, * reset, * irq;
} boot = {
nmi,
(void *)0x8000,
nullptr
};
#pragma data(data)

295
include/nes/neslib.h Normal file
View File

@ -0,0 +1,295 @@
#ifndef NES_NESLIB_H
#define NES_NESLIB_H
#include "nes.h"
/*
(C) 2015 Alex Semenov (Shiru)
(C) 2016 Lauri Kasanen
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
// NES hardware-dependent functions by Shiru (shiru@mail.ru)
// Feel free to do anything you want with this code, consider it Public Domain
// Versions history:
// 280215 - fixed palette glitch caused with the active DMC DMA glitch
// 030914 - minor fixes in the vram update system
// 310814 - added vram_flush_update
// 120414 - removed adr argument from vram_write and vram_read,
// 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
void nes_game(void);
// set bg and spr palettes, data is 32 bytes array
void pal_all(const char *data);
// set bg palette only, data is 16 bytes array
void pal_bg(const char *data);
// set spr palette only, data is 16 bytes array
void pal_spr(const char *data);
// set a palette entry, index is 0..31
void pal_col(unsigned char index, unsigned char color);
// reset palette to $0f
void pal_clear(void);
// set virtual bright both for sprites and background, 0 is black, 4 is normal, 8 is white
void pal_bright(unsigned char bright);
// set virtual bright for sprites only
void pal_spr_bright(unsigned char bright);
// set virtual bright for sprites background only
void pal_bg_bright(unsigned char bright);
// wait actual TV frame, 50hz for PAL, 60hz for NTSC
void ppu_wait_nmi(void);
// wait virtual frame, it is always 50hz, frame-to-frame in PAL, frameskip in NTSC
void ppu_wait_frame(void);
// turn off rendering, nmi still enabled when rendering is disabled
void ppu_off(void);
// turn on bg, spr
void ppu_on_all(void);
// turn on bg only
void ppu_on_bg(void);
// turn on spr only
void ppu_on_spr(void);
// set PPU_MASK directly
void ppu_mask(unsigned char mask);
// get current video system, 0 for PAL, not 0 for NTSC
unsigned char ppu_system(void);
// Return an 8-bit counter incremented at each vblank
unsigned char nesclock(void);
// get/set the internal ppu ctrl cache var for manual writing
unsigned char get_ppu_ctrl_var(void);
void set_ppu_ctrl_var(unsigned char var);
// clear OAM buffer, all the sprites are hidden
void oam_clear(void);
// set sprite display mode, 0 for 8x8 sprites, 1 for 8x16 sprites
void oam_size(unsigned char size);
// set sprite in OAM buffer, chrnum is tile, attr is attribute, sprid is offset in OAM in bytes
// returns sprid+4, which is offset for a next sprite
unsigned char oam_spr(unsigned char x, unsigned char y,
unsigned char chrnum, unsigned char attr,
unsigned char sprid);
// set metasprite in OAM buffer
// meta sprite is a const unsigned char array, it contains four bytes per sprite
// in order x offset, y offset, tile, attribute
// x=128 is end of a meta sprite
// returns sprid+4, which is offset for a next sprite
unsigned char oam_meta_spr(unsigned char x, unsigned char y,
unsigned char sprid, const unsigned char *data);
// hide all remaining sprites from given offset
void oam_hide_rest(unsigned char sprid);
// play a music in FamiTone format
void music_play(unsigned char song);
// stop music
void music_stop(void);
// pause and unpause music
void music_pause(unsigned char pause);
// play FamiTone sound effect on channel 0..3
void sfx_play(unsigned char sound, unsigned char channel);
// play a DPCM sample, 1..63
void sample_play(unsigned char sample);
// poll controller and return flags like PAD_LEFT etc, input is pad number (0 or 1)
unsigned char pad_poll(unsigned char pad);
// poll controller in trigger mode, a flag is set only on button down, not hold
// if you need to poll the pad in both normal and trigger mode, poll it in the
// trigger mode for first, then use pad_state
unsigned char pad_trigger(unsigned char pad);
// get previous pad state without polling ports
unsigned char pad_state(unsigned char pad);
// set scroll, including rhe top bits
// it is always applied at beginning of a TV frame, not at the function call
void scroll(unsigned int x, unsigned int y);
// set scroll after screen split invoked by the sprite 0 hit
// warning: all CPU time between the function call and the actual split point will be wasted!
// warning: the program loop has to fit into the frame time, ppu_wait_frame should not be used
// otherwise empty frames without split will be inserted, resulting in jumpy screen
// warning: only X scroll could be changed in this version
void split(unsigned int x, unsigned int y);
// select current chr bank for sprites, 0..1
void bank_spr(unsigned char n);
// select current chr bank for background, 0..1
void bank_bg(unsigned char n);
// get random number 0..255 or 0..65535
unsigned char rand8(void);
unsigned int rand16(void);
// set random seed
void set_rand(unsigned int seed);
// when display is enabled, vram access could only be done with this vram update system
// the function sets a pointer to the update buffer that contains data and addresses
// in a special format. It allows to write non-sequental bytes, as well as horizontal or
// vertical nametable sequences.
// buffer pointer could be changed during rendering, but it only takes effect on a new frame
// number of transferred bytes is limited by vblank time
// to disable updates, call this function with NULL pointer
// the update data format:
// MSB, LSB, byte for a non-sequental write
// MSB|NT_UPD_HORZ, LSB, LEN, [bytes] for a horizontal sequence
// MSB|NT_UPD_VERT, LSB, LEN, [bytes] for a vertical sequence
// NT_UPD_EOF to mark end of the buffer
// length of this data should be under 256 bytes
void set_vram_update(unsigned char *buf);
// all following vram functions only work when display is disabled
// do a series of VRAM writes, the same format as for set_vram_update, but writes done right away
void flush_vram_update(unsigned char *buf);
// set vram pointer to write operations if you need to write some data to vram
void vram_adr(unsigned int adr);
// put a byte at current vram address, works only when rendering is turned off
void vram_put(unsigned char n);
// fill a block with a byte at current vram address, works only when rendering is turned off
void vram_fill(unsigned char n, unsigned int len);
// set vram autoincrement, 0 for +1 and not 0 for +32
void vram_inc(unsigned char n);
// read a block from current address of vram, works only when rendering is turned off
void vram_read(unsigned char *dst, unsigned int size);
// write a block to current address of vram, works only when rendering is turned off
void vram_write(const unsigned char *src, unsigned int size);
// unpack RLE data to current address of vram, mostly used for nametables
void vram_unrle(const unsigned char *data);
// unpack LZ4 data to this address
void vram_unlz4(const unsigned char *in, unsigned char *out,
const unsigned uncompressed_size);
/*
Rough speeds for a full 1024 nametable:
- rle takes 0.5 frames
- uncompressed takes 1.3 frames
- lz4 takes 2.8 frames
*/
// like memset, but does not return anything
void memfill(void *dst, unsigned char value, unsigned int len);
// delay for N frames
void delay(unsigned char frames);
// display.sinc functions
void oam_clear_fast(void);
void oam_meta_spr_pal(unsigned char x,unsigned char y,unsigned char pal,const unsigned char *metasprite);
void oam_meta_spr_clip(signed int x,unsigned char y,const unsigned char *metasprite);
#define PAD_A 0x01
#define PAD_B 0x02
#define PAD_SELECT 0x04
#define PAD_START 0x08
#define PAD_UP 0x10
#define PAD_DOWN 0x20
#define PAD_LEFT 0x40
#define PAD_RIGHT 0x80
#define OAM_FLIP_V 0x80
#define OAM_FLIP_H 0x40
#define OAM_BEHIND 0x20
#define MAX(x1,x2) ((x1)<(x2)?(x2):(x1))
#define MIN(x1,x2) ((x1)<(x2)?(x1):(x2))
#define MASK_SPR 0x10
#define MASK_BG 0x08
#define MASK_EDGE_SPR 0x04
#define MASK_EDGE_BG 0x02
#define NAMETABLE_A 0x2000
#define NAMETABLE_B 0x2400
#define NAMETABLE_C 0x2800
#define NAMETABLE_D 0x2c00
#define NT_UPD_HORZ 0x40
#define NT_UPD_VERT 0x80
#define NT_UPD_EOF 0xff
// macro to calculate nametable address from X,Y in compile time
#define NTADR_A(x,y) (NAMETABLE_A|(((y)<<5)|(x)))
#define NTADR_B(x,y) (NAMETABLE_B|(((y)<<5)|(x)))
#define NTADR_C(x,y) (NAMETABLE_C|(((y)<<5)|(x)))
#define NTADR_D(x,y) (NAMETABLE_D|(((y)<<5)|(x)))
// macro to get MSB and LSB
#define MSB(x) (((x)>>8))
#define LSB(x) (((x)&0xff))
#pragma compile("neslib.c")
#endif

View File

@ -28,7 +28,7 @@ public:
DeclarationScope* mRuntimeScope;
LinkerSection* mSectionCode, * mSectionData, * mSectionBSS, * mSectionHeap, * mSectionStack, * mSectionZeroPage, * mSectionLowCode;
LinkerSection* mSectionCode, * mSectionData, * mSectionBSS, * mSectionHeap, * mSectionStack, * mSectionZeroPage, * mSectionLowCode, * mSectionBoot;
Linker* mLinker;
bool AddUnit(Location & location, const char* name, const char * from);

View File

@ -23,6 +23,7 @@ Compiler::Compiler(void)
mCompilationUnits->mSectionStack = mLinker->AddSection(Ident::Unique("stack"), LST_STACK);
mCompilationUnits->mSectionZeroPage = mLinker->AddSection(Ident::Unique("zeropage"), LST_ZEROPAGE);
mCompilationUnits->mSectionLowCode = nullptr;
mCompilationUnits->mSectionBoot = nullptr;
mCompilationUnits->mSectionStack->mSize = 4096;
mCompilationUnits->mSectionHeap->mSize = 1024;
@ -82,6 +83,11 @@ bool Compiler::ParseSource(void)
case TMACH_C128:
mCompilationUnits->mSectionLowCode = mLinker->AddSection(Ident::Unique("lowcode"), LST_DATA);
break;
case TMACH_NES:
mCompilationUnits->mSectionStack->mSize = 256;
mCompilationUnits->mSectionHeap->mSize = 256;
mCompilationUnits->mSectionBoot = mLinker->AddSection(Ident::Unique("boot"), LST_DATA);
break;
}
mPreprocessor->mCompilerOptions = mCompilerOptions;
@ -194,6 +200,8 @@ bool Compiler::GenerateCode(void)
const Ident* identStartup = Ident::Unique("startup");
const Ident* identBytecode = Ident::Unique("bytecode");
const Ident* identMain = Ident::Unique("main");
const Ident* identRom = Ident::Unique("rom");
const Ident* identBoot = Ident::Unique("boot");
const Ident* identCode = Ident::Unique("code");
const Ident* identZeroPage = Ident::Unique("zeropage");
const Ident* identLowcode = Ident::Unique("lowcode");
@ -205,7 +213,7 @@ bool Compiler::GenerateCode(void)
}
LinkerRegion* regionStartup = mLinker->FindRegion(identStartup);
LinkerRegion* regionLowcode = nullptr;
LinkerRegion* regionLowcode = nullptr, * regionBoot = nullptr;
if (!regionStartup)
{
@ -260,6 +268,8 @@ bool Compiler::GenerateCode(void)
break;
}
}
else if (mTargetMachine == TMACH_NES)
regionStartup = mLinker->AddRegion(identStartup, 0x8000, 0x8080);
else
regionStartup = mLinker->AddRegion(identStartup, 0x0800, 0x0900);
}
@ -296,6 +306,7 @@ bool Compiler::GenerateCode(void)
}
LinkerRegion* regionMain = mLinker->FindRegion(identMain);
LinkerRegion* regionRom = mLinker->FindRegion(identRom);
LinkerSection * sectionStartup = mLinker->AddSection(identStartup, LST_DATA);
LinkerSection* sectionBytecode = nullptr;
@ -315,7 +326,14 @@ bool Compiler::GenerateCode(void)
{
if (!regionMain)
{
if (!(mCompilerOptions & COPT_TARGET_PRG))
if (mTargetMachine == TMACH_NES)
{
regionBoot = mLinker->AddRegion(identBoot, 0xfffa, 0x10000);
regionBoot->mSections.Push(mCompilationUnits->mSectionBoot);
regionRom = mLinker->AddRegion(identRom, 0x8080, 0xfffa);
regionMain = mLinker->AddRegion(identMain, 0x0200, 0x0800);
}
else if (!(mCompilerOptions & COPT_TARGET_PRG))
regionMain = mLinker->AddRegion(identMain, 0x0900, 0x4700);
else if (regionBytecode)
{
@ -379,8 +397,17 @@ bool Compiler::GenerateCode(void)
}
}
regionMain->mSections.Push(mCompilationUnits->mSectionCode);
regionMain->mSections.Push(mCompilationUnits->mSectionData);
if (regionRom)
{
regionRom->mSections.Push(mCompilationUnits->mSectionCode);
regionRom->mSections.Push(mCompilationUnits->mSectionData);
}
else
{
regionMain->mSections.Push(mCompilationUnits->mSectionCode);
regionMain->mSections.Push(mCompilationUnits->mSectionData);
}
regionMain->mSections.Push(mCompilationUnits->mSectionBSS);
regionMain->mSections.Push(mCompilationUnits->mSectionHeap);
regionMain->mSections.Push(mCompilationUnits->mSectionStack);
@ -701,6 +728,13 @@ bool Compiler::WriteOutputFile(const char* targetPath, DiskImage * d64)
printf("Writing <%s>\n", prgPath);
mLinker->WriteBinFile(prgPath);
}
else if (mCompilerOptions & COPT_TARGET_NES)
{
strcat_s(prgPath, "nes");
if (mCompilerOptions & COPT_VERBOSE)
printf("Writing <%s>\n", prgPath);
mLinker->WriteNesFile(prgPath);
}
if (d64)

View File

@ -21,6 +21,7 @@ static const uint64 COPT_TARGET_CRT512 = 0x400000000ULL;
static const uint64 COPT_TARGET_COPY = 0x800000000ULL;
static const uint64 COPT_TARGET_BIN = 0x1000000000ULL;
static const uint64 COPT_TARGET_LZO = 0x2000000000ULL;
static const uint64 COPT_TARGET_NES = 0x4000000000ULL;
static const uint64 COPT_VERBOSE = 0x10000000000ULL;
static const uint64 COPT_VERBOSE2 = 0x20000000000ULL;
@ -46,7 +47,8 @@ enum TargetMachine
TMACH_VIC20_16K,
TMACH_VIC20_24K,
TMACH_C128,
TMACH_C128B
TMACH_C128B,
TMACH_NES
};

View File

@ -611,6 +611,25 @@ bool Linker::WriteBinFile(const char* filename)
return false;
}
bool Linker::WriteNesFile(const char* filename)
{
FILE* file;
fopen_s(&file, filename, "wb");
if (file)
{
char header[16] = { 0x4e, 0x45, 0x53, 0x1a, 0x02, 0x01, 0x01, 0x00, 0x02, 0x00, 0x00 };
fwrite(header, 1, 16, file);
int done = fwrite(mMemory + 0x8000, 1, 0x8000, file);
done += fwrite(mCartridge[0], 1, 0x2000, file);
fclose(file);
return done == 0x8000 + 0x2000;
}
else
return false;
}
bool Linker::WritePrgFile(DiskImage* image, const char* filename)
{
if (image->OpenFile(filename))

View File

@ -228,6 +228,7 @@ public:
bool WriteLblFile(const char* filename);
bool WriteCrtFile(const char* filename);
bool WriteBinFile(const char* filename);
bool WriteNesFile(const char* filename);
uint64 mCompilerOptions;

File diff suppressed because it is too large Load Diff

View File

@ -186,6 +186,7 @@ public:
NativeCodeBasicBlock* mLoopHeadBlock, * mLoopTailBlock;
NativeRegisterDataSet mDataSet, mNDataSet, mFDataSet;
int mYAlias[256], mYOffset;
NativeCodeInstruction DecodeNative(LinkerObject * lobj, int& offset) const;
@ -428,6 +429,8 @@ public:
bool CrossBlockXYShortcut(void);
bool CrossBlockYAliasProgpagation(const int * yalias, int yoffset);
bool BypassRegisterConditionBlock(void);
bool Check16BitSum(int at, NativeRegisterSum16Info& info);
@ -515,6 +518,12 @@ public:
bool CheckForwardSumYPointer(const NativeCodeBasicBlock* block, int reg, int base, const NativeCodeInstruction & iins, int at, int yval);
bool PatchForwardSumYPointer(const NativeCodeBasicBlock* block, int reg, int base, const NativeCodeInstruction & iins, int at, int yval);
bool CrossBlock16BitFlood(NativeCodeProcedure* proc);
bool CheckCrossBlock16BitFlood(const NativeCodeBasicBlock* block, int sreg, int dreg, int at, bool rvalid);
bool CheckCrossBlock16BitFloodExit(const NativeCodeBasicBlock* block, int sreg, int dreg, bool rvalid);
bool PatchCrossBlock16BitFlood(const NativeCodeBasicBlock* block, int sreg, int dreg, int at);
bool PatchCrossBlock16BitFloodExit(const NativeCodeBasicBlock* block, int sreg, int dreg);
bool CrossBlockXYFlood(NativeCodeProcedure * proc);
bool CheckCrossBlockXFlood(const NativeCodeBasicBlock* block, int reg, int at, bool rvalid);

View File

@ -282,11 +282,22 @@ int main2(int argc, const char** argv)
compiler->mTargetMachine = TMACH_VIC20_24K;
compiler->AddDefine(Ident::Unique("__VIC20__"), "1");
}
else if (!strcmp(targetMachine, "nes"))
{
compiler->mTargetMachine = TMACH_NES;
compiler->mCompilerOptions |= COPT_EXTENDED_ZERO_PAGE;
compiler->AddDefine(Ident::Unique("__NES__"), "1");
}
else
compiler->mErrors->Error(loc, EERR_COMMAND_LINE, "Invalid target machine option", targetMachine);
if (!strcmp(targetFormat, "prg"))
if (compiler->mTargetMachine == TMACH_NES)
{
compiler->mCompilerOptions |= COPT_TARGET_NES;
compiler->AddDefine(Ident::Unique("OSCAR_TARGET_NES"), "1");
}
else if (!strcmp(targetFormat, "prg"))
{
compiler->mCompilerOptions |= COPT_TARGET_PRG;
compiler->AddDefine(Ident::Unique("OSCAR_TARGET_PRG"), "1");