Compare commits

..

3 Commits
v010 ... v013

Author SHA1 Message Date
byuu
c6c5f4669c Update to bsnes v013 release.
- Greatly improved HDMA timing and accuracy with help from anomie and DMV27 -- fixes bugs in Energy Breaker and Street Fighter Alpha 2
    - Fixed a problem with color add/sub code -- fixes opening battle in Tales of Phantasia and clouds in Energy Breaker
    - Temporarily added DMV27's bugfix for the DSP KON register -- fixes sound in Der Langrisser, but this is not a hardware-accurate fix
    - Disabled VRAM writes outside of vblank -- fixes Hook, but breaks many PD ROMs and fan translations (Roto's BS Zelda hack, Gideon Zhi's Ys 4 translation, etc). I might add an option in the future to toggle this behavior, but for now these games will no longer work. Please keep in mind these games will not run properly on real SNES hardware, either.
    - Improved frameskipping code thanks to a suggestion from Richard Bannister
    - Misc. other code cleanups and improvements (notably in the color table generation code)
    - bsnes is now endian-safe and runs on Mac OS X
    - Added caching support for window clipping tables resulting in a slight speedup. Please let me know if you spot any errors as a result of this change.
2005-10-23 23:32:30 +00:00
byuu
397b9c4505 Update to bsnes v012 release.
Changelog:
    - Added S-DSP emulation
    - Added sound output support via DirectSound -- no sound buffering though, so sound is muted by default
    - Added option to record raw sound output to WAV files
    - Added multiple color adjustment filters to the video output
    - Added mode3/4 direct color support
    - Added mode7 direct color and mosaic support
    - Greatly improved mode7 rendering algorithm thanks to anomie
    - Fixed mode7 screen repitition and EXTBG effects
    - Greatly increased accuracy of NMI and IRQ timing, and emulated many newly discovered hardware quirks involving the two
    - A few speed improvements courtesy of Nach for profiling the code for me
I'm now looking for assistance with sound buffering. Specifically, I need help modifying the DirectSound code to allow the emulator to be ran between 50%-400% normal speed, while keeping the sound output relatively good. If you have experience with this and can help, please get in touch with me (setsunakun0 at hotmail dot com).
2005-10-02 00:38:34 +00:00
byuu
7e2cfb6d40 Update to bsnes v011 release.
- Fixed Mode 0 color palette index problem. Fixes ToP, DQ5, etc.
    - Improved LoROM memory mapper to support 32mbit images. Fixes Tokimeki Memorial, etc.
    - Added full S-DD1 support, SFA2 and Star Ocean are now playable. Special thanks to Andreas Naive
    - Updated BGnxOFS / Mode7 registers with anomie's latest findings
    - Added basic ROM mirroring support. Fixes copy protection issues in MMX, etc.
    - Rewrote string library to work better on gcc/linux
    - Cleaned up S-RTC/S-DD1 emulation to make way for future add-on chip emulation
    - Rewrote DMA code, now runs cycle-by-cycle
    - Rewrote HDMA code, now allows HDMA to be enabled mid-frame, fixes many games
    - Fixed a bug in Mode7 vertical screen flip mode. Fixes FF5 title screen, etc.
    - Greatly improved IRQ triggering. Fixes Der Langrisser, etc.
    - Added full support for open bus. This includes PPU1 and PPU2 open bus support
    - Modified CPU core back to cycle-based system. Slower, but improves debugger
    - Implemented temporary fix for debugger to handle new cycle-based cores
    - Modified CGRAM to ignore highest bit, since it is not used at all by the SNES, and is impossible to read on real hardware. Lowers memory usage by ~1.2mb
    - Added mostly accurate PAL timing support. This should increase compatibility by ~30% or so
    - More stuff I'm forgetting at the moment...
2005-08-26 20:38:00 +00:00
136 changed files with 12378 additions and 6343 deletions

154
bsnes.cfg
View File

@@ -1,57 +1,135 @@
#[bsnes v0.009 configuration file]
# Applies contrast adjust filter to video output when enabled
# Works by lowering the brightness of darker colors,
# while leaving brighter colors alone; thus reducing saturation
# (default = true)
snes.video_color_curve = true
#[apu enable]
apu.enabled = true
# Selects color adjustment filter for video output
# 0 = Normal (no filter, rgb555)
# 1 = Grayscale mode (l5)
# 2 = VGA mode (rgb332)
# 3 = Genesis mode (rgb333)
# (default = 0)
snes.video_color_adjust_mode = 0
#[video mode]
# 0: 256x224w
# 1: 512x448w
# 2: 960x720w
# 3: 640x480f
# 4: 1024x768f
# Mutes SNES audio output when enabled
# (default = true)
snes.mute = true
# Video mode
# 0 = 256x224w
# 1 = 512x448w
# 2 = 960x720w
# 3 = 640x480f
# 4 = 1024x768f
# (default = 1)
video.mode = 1
#[video memory type]
# true: video ram (VRAM)
# false: system ram (SRAM)
#
# VRAM results in the image being stretched in hardware,
# which is generally much faster, and automatically adds
# bilinear filtering (if the card supports it).
#
# However, some video cards end up taking a major speed
# loss when this option is enabled. It is also the only
# way to guarantee that the output image will not be
# filtered.
# Use Video RAM instead of System RAM
# (default = true)
video.use_vram = true
#[color curve]
# gives a more NTSC TV-style feel to the color palette
# by darkening the image contrast.
video.color_curve = enabled
#[show fps]
# true: show fps in titlebar
# false: do not show fps in titlebar
gui.show_fps = true
#[wait for vertical retrace]
# Wait for vertical retrace when updating screen
# (default = false)
video.vblank = false
#[joypad 1 configuration]
# Key numbers are standard windows VK_* keys.
# Unfortunately, I don't have a table of common
# key mappings to list here... use GUI joypad
# configuration utility to edit these.
# Show framerate in window title
# (default = true)
gui.show_fps = true
# Joypad1 up
# (default = 0x26)
input.joypad1.up = 0x26
# Joypad1 down
# (default = 0x28)
input.joypad1.down = 0x28
# Joypad1 left
# (default = 0x25)
input.joypad1.left = 0x25
# Joypad1 right
# (default = 0x27)
input.joypad1.right = 0x27
# Joypad1 A
# (default = 0x58)
input.joypad1.a = 0x58
# Joypad1 B
# (default = 0x5a)
input.joypad1.b = 0x5a
# Joypad1 X
# (default = 0x53)
input.joypad1.x = 0x53
# Joypad1 Y
# (default = 0x41)
input.joypad1.y = 0x41
# Joypad1 L
# (default = 0x44)
input.joypad1.l = 0x44
# Joypad1 R
# (default = 0x43)
input.joypad1.r = 0x43
# Joypad1 select
# (default = 0x10)
input.joypad1.select = 0x10
input.joypad1.start = 0x0d
# Joypad1 start
# (default = 0xd)
input.joypad1.start = 0xd
# Joypad2 up
# (default = 0x54)
input.joypad2.up = 0x54
# Joypad2 down
# (default = 0x47)
input.joypad2.down = 0x47
# Joypad2 left
# (default = 0x46)
input.joypad2.left = 0x46
# Joypad2 right
# (default = 0x48)
input.joypad2.right = 0x48
# Joypad2 A
# (default = 0x4b)
input.joypad2.a = 0x4b
# Joypad2 B
# (default = 0x4a)
input.joypad2.b = 0x4a
# Joypad2 X
# (default = 0x49)
input.joypad2.x = 0x49
# Joypad2 Y
# (default = 0x55)
input.joypad2.y = 0x55
# Joypad2 L
# (default = 0x4f)
input.joypad2.l = 0x4f
# Joypad2 R
# (default = 0x4c)
input.joypad2.r = 0x4c
# Joypad2 select
# (default = 0x5b)
input.joypad2.select = 0x5b
# Joypad2 start
# (default = 0x5d)
input.joypad2.start = 0x5d

BIN
bsnes.exe

Binary file not shown.

View File

@@ -17,5 +17,10 @@ emulators.
The Simple DirectMedia Layer library source code is available from:
http://www.libsdl.org/
This library is distributed under the terms of the GNU LGPL license:
This library is distributed under the terms of the GNU LGPL:
http://www.gnu.org/copyleft/lesser.html
Licensing Exemptions:
---------------------
Richard Bannister has asked for and received my permission to distribute
a binary-only port of bsnes on the Mac OS X platform.

View File

@@ -1,2 +1,3 @@
#include "../base.h"
#include "iplrom.h"
#include "dapu.cpp"

View File

@@ -1,10 +1,9 @@
#define _APU_IPLROM
#include "iplrom.h"
#include "apuregs.h"
class APU {
public:
APURegs regs;
static const uint8 iplrom[64];
enum {
FLAG_N = 0x80, FLAG_V = 0x40,
FLAG_P = 0x20, FLAG_B = 0x10,
@@ -18,11 +17,14 @@ APURegs regs;
virtual uint8 port_read (uint8 port) = 0;
virtual void port_write(uint8 port, uint8 value) = 0;
virtual uint8 *get_spcram_handle() = 0;
virtual void run() = 0;
virtual uint32 cycles_executed() = 0;
virtual void power() = 0;
virtual void reset() = 0;
//debugging functions
virtual bool in_opcode();
void disassemble_opcode(char *output);
inline uint16 __relb(int8 offset, int op_len);
};

View File

@@ -13,7 +13,7 @@ private:
inline bool operator ^= (bool i) { if(i)_b ^= B; return (_b & B); }
};
public:
union {
union {
uint8 _b;
bit<0x80> n;
bit<0x40> v;
@@ -23,7 +23,7 @@ public:
bit<0x04> i;
bit<0x02> z;
bit<0x01> c;
};
};
APURegFlags() { _b = 0; }
inline operator uint8() { return _b; }
@@ -38,10 +38,11 @@ public:
uint16 pc;
union {
uint16 ya;
//not endian-safe
struct {
uint8 a, y;
};
#ifdef ARCH_LSB
struct { uint8 a, y; };
#else
struct { uint8 y, a; };
#endif
};
uint8 x, sp;
APURegFlags p;

View File

@@ -24,7 +24,7 @@ uint8 r;
break;
case 0xf3: //DSPDATA
//0x80-0xff is a read-only mirror of 0x00-0x7f
r = dsp_regs[status.dsp_addr & 0x7f];
r = dsp->read(status.dsp_addr & 0x7f);
break;
case 0xf4: //CPUIO0
case 0xf5: //CPUIO1
@@ -111,7 +111,7 @@ void bAPU::spcram_write(uint16 addr, uint8 value) {
case 0xf3: //DSPDATA
//0x80-0xff is a read-only mirror of 0x00-0x7f
if(status.dsp_addr < 0x80) {
dsp_regs[status.dsp_addr & 0x7f] = value;
dsp->write(status.dsp_addr & 0x7f, value);
}
break;
case 0xf4: //CPUIO0
@@ -209,6 +209,14 @@ void bAPU::stack_write(uint8 value) {
regs.sp--;
}
uint8 *bAPU::get_spcram_handle() {
if(!spcram) {
alert("bAPU::get_spcram_handle() -- spcram uninitialized");
}
return spcram;
}
void bAPU::run() {
exec_cycle();
}
@@ -226,7 +234,8 @@ void bAPU::reset() {
regs.sp = 0xef;
regs.p = 0x02;
status.cycle_pos = 0;
status.cycle_pos = 0;
status.cycles_executed = 0;
//$f1
status.iplrom_enabled = true;
@@ -246,15 +255,12 @@ void bAPU::reset() {
t0.stage3_ticks = 0;
t1.stage3_ticks = 0;
t2.stage3_ticks = 0;
memset(dsp_regs, 0, 128);
}
bAPU::bAPU() {
init_op_table();
spcram = (uint8*)malloc(65536);
memcpy(iplrom, spc700_iplrom, 64);
t0.cycle_frequency = 128; //1.024mhz / 8khz = 128
t1.cycle_frequency = 128; //1.024mhz / 8khz = 128

View File

@@ -29,12 +29,13 @@ struct {
bAPUTimer t0, t1, t2;
uint8 *spcram, iplrom[64], dsp_regs[128];
uint8 *spcram;
inline uint8 spcram_read (uint16 addr);
inline void spcram_write(uint16 addr, uint8 value);
inline uint8 port_read (uint8 port);
inline void port_write(uint8 port, uint8 value);
inline uint8 *get_spcram_handle();
inline void run();
inline uint32 cycles_executed();
inline void power();
@@ -53,6 +54,7 @@ enum {
inline void stack_write(uint8 value);
inline void exec_cycle();
inline bool in_opcode();
inline void init_op_table();
inline uint8 op_adc (uint8 x, uint8 y);

View File

@@ -15,6 +15,11 @@ uint8 op;
}
}
//only return true when we are on an opcode edge
bool bAPU::in_opcode() {
return (status.cycle_pos != 0);
}
void bAPU::init_op_table() {
#include "bapu_optable.cpp"
}

View File

@@ -510,7 +510,7 @@ void bAPU::op_cbne_dp() {
rd = op_read();
break;
case 3:
sp = op_read(OPMODE_DP, dp + 0);
sp = op_read(OPMODE_DP, dp);
break;
case 4:
if(regs.a == sp)status.cycle_pos = 0;
@@ -533,14 +533,16 @@ void bAPU::op_cbne_dpx() {
rd = op_read();
break;
case 3:
sp = op_read(OPMODE_DP, dp + regs.x);
break;
case 4:
if(regs.a == sp)status.cycle_pos = 0;
sp = op_read(OPMODE_DP, dp + regs.x);
break;
case 5:
if(regs.a == sp)status.cycle_pos = 0;
break;
case 6:
break;
case 7:
regs.pc += (int8)rd;
status.cycle_pos = 0;
break;

View File

@@ -107,16 +107,16 @@ int i, l;
replace(t, "$5", op_list[i].arg[5]);
replace(t, "$6", op_list[i].arg[6]);
replace(t, "$7", op_list[i].arg[7]);
fprintf(fp, "%s\r\n\r\n", *t);
fprintf(fp, "%s\r\n\r\n", strptr(t));
strcpy(t, output_header);
replace(t, "$$", op_list[i].name);
fprintf(fph, "%s", *t);
fprintf(fph, "%s", strptr(t));
strcpy(t, output_table);
replace(t, "$$", op_list[i].name);
replace(t, "$0", op_list[i].arg[0]);
fprintf(fpt, "%s", *t);
fprintf(fpt, "%s", strptr(t));
}
}

Binary file not shown.

View File

@@ -37,16 +37,25 @@ bbc7(0xf3, 0x80, ==) {
6:regs.pc += (int8)rd;
}
cbne_dp(0x2e, 0),
cbne_dpx(0xde, regs.x) {
cbne_dp(0x2e) {
1:dp = op_read();
2:rd = op_read();
3:sp = op_read(OPMODE_DP, dp + $1);
3:sp = op_read(OPMODE_DP, dp);
4:if(regs.a == sp)end;
5:
6:regs.pc += (int8)rd;
}
cbne_dpx(0xde) {
1:dp = op_read();
2:rd = op_read();
3:
4:sp = op_read(OPMODE_DP, dp + regs.x);
5:if(regs.a == sp)end;
6:
7:regs.pc += (int8)rd;
}
dbnz_dp(0x6e) {
1:dp = op_read();
2:rd = op_read();

View File

@@ -1,4 +1,7 @@
class bAPUSkip : public APU {
private:
uint8 spcram[65536];
public:
struct _apu_port {
@@ -26,6 +29,7 @@ enum {
uint8 port_read (uint8 port);
void port_write (uint8 port, uint8 value);
uint8 *get_spcram_handle() { return spcram; }
void run();
uint32 cycles_executed() { return 12 * 24; }
void power();

View File

@@ -1,12 +1,23 @@
//virtual function, see src/cpu/dcpu.cpp
//for explanation of this function
bool APU::in_opcode() { return false; }
uint16 APU::__relb(int8 offset, int op_len) {
uint16 pc = regs.pc + op_len;
return pc + offset;
}
void APU::disassemble_opcode(char *output) {
char *s = output, t[512];
char *s, t[512];
uint8 op, op0, op1;
uint16 opw, opdp0, opdp1;
s = output;
if(in_opcode() == true) {
strcpy(s, "..???? <APU within opcode>");
return;
}
sprintf(s, "..%0.4x ", regs.pc);
op = spcram_read(regs.pc);

View File

@@ -3,12 +3,8 @@
//allow writing to the IPLROM, all writes are
//instead mapped to the extended SPC700 RAM region,
//accessible when $f1 bit 7 is clear.
//If you use this buffer directly, make sure not
//to write to it, as this will break other APU
//implementations that attempt to use this buffer.
#ifdef _APU_IPLROM
const uint8 spc700_iplrom[64] = {
const uint8 APU::iplrom[64] = {
/*ffc0*/ 0xcd, 0xef, //mov x,#$ef
/*ffc2*/ 0xbd, //mov sp,x
/*ffc3*/ 0xe8, 0x00, //mov a,#$00
@@ -43,6 +39,3 @@ const uint8 spc700_iplrom[64] = {
/*fffb*/ 0x1f, 0x00, 0x00, //jmp ($0000+x)
/*fffe*/ 0xc0, 0xff //---reset vector location ($ffc0)
};
#else
extern const uint8 spc700_iplrom[64];
#endif

View File

@@ -1,11 +1,36 @@
//this should be declared in the port-specific makefiles
//#define ARCH_LSB
//#define ARCH_MSB
#ifndef ARCH_LSB
#ifndef ARCH_MSB
#define ARCH_LSB
#endif
#endif
#include <time.h>
#include "lib/libbase.h"
#include "lib/libvector.h"
#include "lib/libstring.h"
#include "lib/libconfig.h"
//structs
typedef struct {
uint8 *data;
uint32 size;
}lfile;
inline uint16 read16(uint8 *addr, uint pos) {
#ifdef ARCH_LSB
return *((uint16*)(addr + pos));
#else
return (addr[pos]) | (addr[pos + 1] << 8);
#endif
}
#if defined(_WIN32)
#define _WIN32_
#undef _UNIX_
#elif defined(__GNUC__)
#define _UNIX_
#undef _WIN32_
#else
#error "unknown architecture"
#endif
//platform-specific global functions
void *memalloc(uint32 size, char *name = 0, ...);

103
src/chip/sdd1/sdd1.cpp Normal file
View File

@@ -0,0 +1,103 @@
#include "../../base.h"
#include "sdd1emu.cpp"
void SDD1::init() {
}
void SDD1::enable() {
for(int i=0x4800;i<=0x4807;i++) {
mem_bus->set_mmio_mapper(i, mmio);
}
}
void SDD1::power() {
reset();
}
void SDD1::reset() {
sdd1.index[0] = 0x000000;
sdd1.index[1] = 0x100000;
sdd1.index[2] = 0x200000;
sdd1.index[3] = 0x300000;
for(int i=0;i<8;i++) {
sdd1.active[i] = false;
}
sdd1.dma_active = false;
}
uint32 SDD1::offset(uint32 addr) {
uint8 b = (addr >> 16) & 0xff;
if(b <= 0xbf)return 0;
b -= 0xc0; //b = 0x00-0x3f
b >>= 4; //b = 0-3
b &= 3; //bitmask
return sdd1.index[b] + (addr & 0x0fffff);
}
uint8 SDD1::mmio_read(uint16 addr) {
switch(addr) {
//>>20 == 0x100000 == 1mb
case 0x4804:return (sdd1.index[0] >> 20) & 7;
case 0x4805:return (sdd1.index[1] >> 20) & 7;
case 0x4806:return (sdd1.index[2] >> 20) & 7;
case 0x4807:return (sdd1.index[3] >> 20) & 7;
}
return cpu->regs.mdr;
}
void SDD1::mmio_write(uint16 addr, uint8 data) {
int i;
switch(addr) {
case 0x4801:
for(i=0;i<8;i++) {
sdd1.active[i] = !!(data & (1 << i));
}
break;
//<<20 == 0x100000 == 1mb
case 0x4804:sdd1.index[0] = (data & 7) << 20;break;
case 0x4805:sdd1.index[1] = (data & 7) << 20;break;
case 0x4806:sdd1.index[2] = (data & 7) << 20;break;
case 0x4807:sdd1.index[3] = (data & 7) << 20;break;
}
}
void SDD1::dma_begin(uint8 channel, uint32 addr, uint16 length) {
if(sdd1.active[channel] == true) {
sdd1.active[channel] = false;
sdd1.dma_active = true;
sdd1.buffer_index = 0;
sdd1.buffer_size = length;
sdd1emu.decompress(addr, (length) ? length : 65536, sdd1.buffer);
}
}
bool SDD1::dma_active() {
return sdd1.dma_active;
}
uint8 SDD1::dma_read() {
if(--sdd1.buffer_size == 0) {
sdd1.dma_active = false;
}
//sdd1.buffer[] is 65536 bytes, and sdd1.buffer_index
//is of type uint16, so no buffer overflow is possible
return sdd1.buffer[sdd1.buffer_index++];
}
SDD1::SDD1() {
mmio = new SDD1MMIO();
}
uint8 SDD1MMIO::read(uint32 addr) {
return sdd1->mmio_read(addr);
}
void SDD1MMIO::write(uint32 addr, uint8 value) {
sdd1->mmio_write(addr, value);
}

37
src/chip/sdd1/sdd1.h Normal file
View File

@@ -0,0 +1,37 @@
#include "sdd1emu.h"
class SDD1MMIO : public MMIO {
public:
inline uint8 read (uint32 addr);
inline void write(uint32 addr, uint8 value);
};
class SDD1 {
public:
SDD1emu sdd1emu;
SDD1MMIO *mmio;
struct {
uint32 index[4]; //memory mapping registers
uint8 buffer[65536]; //pointer to decompressed S-DD1 data,
//max. DMA length is 65536
uint16 buffer_index; //DMA read index into S-DD1 decompression buffer
uint16 buffer_size;
bool active[8]; //true when DMA channel should pass through S-DD1
bool dma_active;
}sdd1;
void init();
void enable();
void power();
void reset();
uint32 offset(uint32 addr);
void dma_begin(uint8 channel, uint32 addr, uint16 length);
bool dma_active();
uint8 dma_read();
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);
SDD1();
};

447
src/chip/sdd1/sdd1emu.cpp Normal file
View File

@@ -0,0 +1,447 @@
/************************************************************************
S-DD1'algorithm emulation code
------------------------------
Author: Andreas Naive
Date: August 2003
Last update: October 2004
This code is Public Domain. There is no copyright holded by the author.
Said this, the author wish to explicitly emphasize his inalienable moral rights
over this piece of intelectual work and the previous research that made it
possible, as recognized by most of the copyright laws around the world.
This code is provided 'as-is', with no warranty, expressed or implied.
No responsability is assumed by the author in connection with it.
The author is greatly indebted with The Dumper, without whose help and
patience providing him with real S-DD1 data the research would have never been
possible. He also wish to note that in the very beggining of his research,
Neviksti had done some steps in the right direction. By last, the author is
indirectly indebted to all the people that worked and contributed in the
S-DD1 issue in the past.
An algorithm's documentation is available as a separate document.
The implementation is obvious when the algorithm is
understood.
************************************************************************/
#define SDD1_read(__addr) (mem_bus->read(__addr))
////////////////////////////////////////////////////
void SDD1_IM::prepareDecomp(uint32 in_buf) {
byte_ptr=in_buf;
bit_count=4;
}
////////////////////////////////////////////////////
uint8 SDD1_IM::getCodeword(uint8 code_len) {
uint8 codeword;
uint8 comp_count;
codeword = (SDD1_read(byte_ptr))<<bit_count;
++bit_count;
if (codeword & 0x80) {
codeword |= SDD1_read(byte_ptr+1)>>(9-bit_count);
bit_count+=code_len;
}
if (bit_count & 0x08) {
byte_ptr++;
bit_count&=0x07;
}
return codeword;
}
//////////////////////////////////////////////////////
SDD1_GCD::SDD1_GCD(SDD1_IM *associatedIM) :
IM(associatedIM)
{
}
//////////////////////////////////////////////////////
void SDD1_GCD::getRunCount(uint8 code_num, uint8 *MPScount, bool8 *LPSind) {
const uint8 run_count[] = {
0x00, 0x00, 0x01, 0x00, 0x03, 0x01, 0x02, 0x00,
0x07, 0x03, 0x05, 0x01, 0x06, 0x02, 0x04, 0x00,
0x0f, 0x07, 0x0b, 0x03, 0x0d, 0x05, 0x09, 0x01,
0x0e, 0x06, 0x0a, 0x02, 0x0c, 0x04, 0x08, 0x00,
0x1f, 0x0f, 0x17, 0x07, 0x1b, 0x0b, 0x13, 0x03,
0x1d, 0x0d, 0x15, 0x05, 0x19, 0x09, 0x11, 0x01,
0x1e, 0x0e, 0x16, 0x06, 0x1a, 0x0a, 0x12, 0x02,
0x1c, 0x0c, 0x14, 0x04, 0x18, 0x08, 0x10, 0x00,
0x3f, 0x1f, 0x2f, 0x0f, 0x37, 0x17, 0x27, 0x07,
0x3b, 0x1b, 0x2b, 0x0b, 0x33, 0x13, 0x23, 0x03,
0x3d, 0x1d, 0x2d, 0x0d, 0x35, 0x15, 0x25, 0x05,
0x39, 0x19, 0x29, 0x09, 0x31, 0x11, 0x21, 0x01,
0x3e, 0x1e, 0x2e, 0x0e, 0x36, 0x16, 0x26, 0x06,
0x3a, 0x1a, 0x2a, 0x0a, 0x32, 0x12, 0x22, 0x02,
0x3c, 0x1c, 0x2c, 0x0c, 0x34, 0x14, 0x24, 0x04,
0x38, 0x18, 0x28, 0x08, 0x30, 0x10, 0x20, 0x00,
0x7f, 0x3f, 0x5f, 0x1f, 0x6f, 0x2f, 0x4f, 0x0f,
0x77, 0x37, 0x57, 0x17, 0x67, 0x27, 0x47, 0x07,
0x7b, 0x3b, 0x5b, 0x1b, 0x6b, 0x2b, 0x4b, 0x0b,
0x73, 0x33, 0x53, 0x13, 0x63, 0x23, 0x43, 0x03,
0x7d, 0x3d, 0x5d, 0x1d, 0x6d, 0x2d, 0x4d, 0x0d,
0x75, 0x35, 0x55, 0x15, 0x65, 0x25, 0x45, 0x05,
0x79, 0x39, 0x59, 0x19, 0x69, 0x29, 0x49, 0x09,
0x71, 0x31, 0x51, 0x11, 0x61, 0x21, 0x41, 0x01,
0x7e, 0x3e, 0x5e, 0x1e, 0x6e, 0x2e, 0x4e, 0x0e,
0x76, 0x36, 0x56, 0x16, 0x66, 0x26, 0x46, 0x06,
0x7a, 0x3a, 0x5a, 0x1a, 0x6a, 0x2a, 0x4a, 0x0a,
0x72, 0x32, 0x52, 0x12, 0x62, 0x22, 0x42, 0x02,
0x7c, 0x3c, 0x5c, 0x1c, 0x6c, 0x2c, 0x4c, 0x0c,
0x74, 0x34, 0x54, 0x14, 0x64, 0x24, 0x44, 0x04,
0x78, 0x38, 0x58, 0x18, 0x68, 0x28, 0x48, 0x08,
0x70, 0x30, 0x50, 0x10, 0x60, 0x20, 0x40, 0x00,
};
uint8 codeword=IM->getCodeword(code_num);
if (codeword & 0x80) {
*LPSind=1;
*MPScount=run_count[codeword>>(code_num^0x07)];
}
else {
*MPScount=(1<<code_num);
}
}
///////////////////////////////////////////////////////
SDD1_BG::SDD1_BG(SDD1_GCD *associatedGCD, uint8 code) :
GCD(associatedGCD), code_num(code)
{
}
///////////////////////////////////////////////
void SDD1_BG::prepareDecomp(void) {
MPScount=0;
LPSind=0;
}
//////////////////////////////////////////////
uint8 SDD1_BG::getBit(bool8 *endOfRun) {
uint8 bit;
if (!(MPScount || LPSind)) GCD->getRunCount(code_num, &MPScount, &LPSind);
if (MPScount) {
bit=0;
MPScount--;
}
else {
bit=1;
LPSind=0;
}
if (MPScount || LPSind) (*endOfRun)=0;
else (*endOfRun)=1;
return bit;
}
/////////////////////////////////////////////////
SDD1_PEM::SDD1_PEM(SDD1_BG *associatedBG0, SDD1_BG *associatedBG1,
SDD1_BG *associatedBG2, SDD1_BG *associatedBG3,
SDD1_BG *associatedBG4, SDD1_BG *associatedBG5,
SDD1_BG *associatedBG6, SDD1_BG *associatedBG7) {
BG[0]=associatedBG0;
BG[1]=associatedBG1;
BG[2]=associatedBG2;
BG[3]=associatedBG3;
BG[4]=associatedBG4;
BG[5]=associatedBG5;
BG[6]=associatedBG6;
BG[7]=associatedBG7;
}
/////////////////////////////////////////////////////////
const SDD1_PEM::state SDD1_PEM::evolution_table[]={
{ 0,25,25},
{ 0, 2, 1},
{ 0, 3, 1},
{ 0, 4, 2},
{ 0, 5, 3},
{ 1, 6, 4},
{ 1, 7, 5},
{ 1, 8, 6},
{ 1, 9, 7},
{ 2,10, 8},
{ 2,11, 9},
{ 2,12,10},
{ 2,13,11},
{ 3,14,12},
{ 3,15,13},
{ 3,16,14},
{ 3,17,15},
{ 4,18,16},
{ 4,19,17},
{ 5,20,18},
{ 5,21,19},
{ 6,22,20},
{ 6,23,21},
{ 7,24,22},
{ 7,24,23},
{ 0,26, 1},
{ 1,27, 2},
{ 2,28, 4},
{ 3,29, 8},
{ 4,30,12},
{ 5,31,16},
{ 6,32,18},
{ 7,24,22}
};
//////////////////////////////////////////////////////
void SDD1_PEM::prepareDecomp(void) {
for (uint8 i=0; i<32; i++) {
contextInfo[i].status=0;
contextInfo[i].MPS=0;
}
}
/////////////////////////////////////////////////////////
uint8 SDD1_PEM::getBit(uint8 context) {
bool8 endOfRun;
uint8 bit;
SDD1_ContextInfo *pContInfo=&contextInfo[context];
uint8 currStatus = pContInfo->status;
const state *pState=&SDD1_PEM::evolution_table[currStatus];
uint8 currentMPS=pContInfo->MPS;
bit=(BG[pState->code_num])->getBit(&endOfRun);
if (endOfRun)
if (bit) {
if (!(currStatus & 0xfe)) (pContInfo->MPS)^=0x01;
(pContInfo->status)=pState->nextIfLPS;
}
else
(pContInfo->status)=pState->nextIfMPS;
return bit^currentMPS;
}
//////////////////////////////////////////////////////////////
SDD1_CM::SDD1_CM(SDD1_PEM *associatedPEM) :
PEM(associatedPEM)
{
}
//////////////////////////////////////////////////////////////
void SDD1_CM::prepareDecomp(uint32 first_byte) {
bitplanesInfo = SDD1_read(first_byte) & 0xc0;
contextBitsInfo = SDD1_read(first_byte) & 0x30;
bit_number=0;
for (int i=0; i<8; i++) prevBitplaneBits[i]=0;
switch (bitplanesInfo) {
case 0x00:
currBitplane = 1;
break;
case 0x40:
currBitplane = 7;
break;
case 0x80:
currBitplane = 3;
}
}
/////////////////////////////////////////////////////////////
uint8 SDD1_CM::getBit(void) {
uint8 currContext;
uint16 *context_bits;
switch (bitplanesInfo) {
case 0x00:
currBitplane ^= 0x01;
break;
case 0x40:
currBitplane ^= 0x01;
if (!(bit_number & 0x7f)) currBitplane = ((currBitplane+2) & 0x07);
break;
case 0x80:
currBitplane ^= 0x01;
if (!(bit_number & 0x7f)) currBitplane ^= 0x02;
break;
case 0xc0:
currBitplane = bit_number & 0x07;
}
context_bits = &prevBitplaneBits[currBitplane];
currContext=(currBitplane & 0x01)<<4;
switch (contextBitsInfo) {
case 0x00:
currContext|=((*context_bits & 0x01c0)>>5)|(*context_bits & 0x0001);
break;
case 0x10:
currContext|=((*context_bits & 0x0180)>>5)|(*context_bits & 0x0001);
break;
case 0x20:
currContext|=((*context_bits & 0x00c0)>>5)|(*context_bits & 0x0001);
break;
case 0x30:
currContext|=((*context_bits & 0x0180)>>5)|(*context_bits & 0x0003);
}
uint8 bit=PEM->getBit(currContext);
*context_bits <<= 1;
*context_bits |= bit;
bit_number++;
return bit;
}
//////////////////////////////////////////////////
SDD1_OL::SDD1_OL(SDD1_CM *associatedCM) :
CM(associatedCM)
{
}
///////////////////////////////////////////////////
void SDD1_OL::prepareDecomp(uint32 first_byte, uint16 out_len, uint8 *out_buf) {
bitplanesInfo = SDD1_read(first_byte) & 0xc0;
length=out_len;
buffer=out_buf;
}
///////////////////////////////////////////////////
void SDD1_OL::launch(void) {
uint8 i;
uint8 register1, register2;
switch (bitplanesInfo) {
case 0x00:
case 0x40:
case 0x80:
i=1;
do { //if length==0, we output 2^16 bytes
if (!i) {
*(buffer++)=register2;
i=~i;
}
else {
for (register1=register2=0, i=0x80; i; i>>=1) {
if (CM->getBit()) register1 |= i;
if (CM->getBit()) register2 |= i;
}
*(buffer++)=register1;
}
} while (--length);
break;
case 0xc0:
do {
for (register1=0, i=0x01; i; i<<=1) {
if (CM->getBit()) register1 |= i;
}
*(buffer++)=register1;
} while (--length);
}
}
///////////////////////////////////////////////////////
void SDD1emu::decompress(uint32 in_buf, uint16 out_len, uint8 *out_buf) {
IM.prepareDecomp(in_buf);
BG0.prepareDecomp();
BG1.prepareDecomp();
BG2.prepareDecomp();
BG3.prepareDecomp();
BG4.prepareDecomp();
BG5.prepareDecomp();
BG6.prepareDecomp();
BG7.prepareDecomp();
PEM.prepareDecomp();
CM.prepareDecomp(in_buf);
OL.prepareDecomp(in_buf, out_len, out_buf);
OL.launch();
}
////////////////////////////////////////////////////////////
SDD1emu::SDD1emu() :
GCD(&IM),
BG0(&GCD, 0), BG1(&GCD, 1), BG2(&GCD, 2), BG3(&GCD, 3),
BG4(&GCD, 4), BG5(&GCD, 5), BG6(&GCD, 6), BG7(&GCD, 7),
PEM(&BG0, &BG1, &BG2, &BG3, &BG4, &BG5, &BG6, &BG7),
CM(&PEM),
OL(&CM)
{
}
///////////////////////////////////////////////////////////

161
src/chip/sdd1/sdd1emu.h Normal file
View File

@@ -0,0 +1,161 @@
/************************************************************************
S-DD1'algorithm emulation code
------------------------------
Author: Andreas Naive
Date: August 2003
Last update: October 2004
This code is Public Domain. There is no copyright holded by the author.
Said this, the author wish to explicitly emphasize his inalienable moral rights
over this piece of intelectual work and the previous research that made it
possible, as recognized by most of the copyright laws around the world.
This code is provided 'as-is', with no warranty, expressed or implied.
No responsability is assumed by the author in connection with it.
The author is greatly indebted with The Dumper, without whose help and
patience providing him with real S-DD1 data the research would have never been
possible. He also wish to note that in the very beggining of his research,
Neviksti had done some steps in the right direction. By last, the author is
indirectly indebted to all the people that worked and contributed in the
S-DD1 issue in the past.
An algorithm's documentation is available as a separate document.
The implementation is obvious when the algorithm is
understood.
************************************************************************/
class SDD1_IM { //Input Manager
public:
SDD1_IM(void) {}
void prepareDecomp(uint32 in_buf);
uint8 getCodeword(const uint8 code_len);
private:
uint32 byte_ptr;
uint8 bit_count;
};
////////////////////////////////////////////////////
class SDD1_GCD { //Golomb-Code Decoder
public:
SDD1_GCD(SDD1_IM *associatedIM);
void getRunCount(uint8 code_num, uint8 *MPScount, bool8 *LPSind);
private:
SDD1_IM *const IM;
};
//////////////////////////////////////////////////////
class SDD1_BG { // Bits Generator
public:
SDD1_BG(SDD1_GCD *associatedGCD, uint8 code);
void prepareDecomp(void);
uint8 getBit(bool8 *endOfRun);
private:
const uint8 code_num;
uint8 MPScount;
bool8 LPSind;
SDD1_GCD *const GCD;
};
////////////////////////////////////////////////
class SDD1_PEM { //Probability Estimation Module
public:
SDD1_PEM(SDD1_BG *associatedBG0, SDD1_BG *associatedBG1,
SDD1_BG *associatedBG2, SDD1_BG *associatedBG3,
SDD1_BG *associatedBG4, SDD1_BG *associatedBG5,
SDD1_BG *associatedBG6, SDD1_BG *associatedBG7);
void prepareDecomp(void);
uint8 getBit(uint8 context);
private:
struct state {
uint8 code_num;
uint8 nextIfMPS;
uint8 nextIfLPS;
};
static const state evolution_table[];
struct SDD1_ContextInfo {
uint8 status;
uint8 MPS;
} contextInfo[32];
SDD1_BG * BG[8];
};
///////////////////////////////////////////////////
class SDD1_CM { //Context Model
public:
SDD1_CM(SDD1_PEM *associatedPEM);
void prepareDecomp(uint32 first_byte);
uint8 getBit(void);
private:
uint8 bitplanesInfo;
uint8 contextBitsInfo;
uint8 bit_number;
uint8 currBitplane;
uint16 prevBitplaneBits[8];
SDD1_PEM *const PEM;
};
///////////////////////////////////////////////////
class SDD1_OL { //Output Logic
public:
SDD1_OL(SDD1_CM *associatedCM);
void prepareDecomp(uint32 first_byte, uint16 out_len, uint8 *out_buf);
void launch(void);
private:
uint8 bitplanesInfo;
uint16 length;
uint8 *buffer;
SDD1_CM *const CM;
};
/////////////////////////////////////////////////////////
class SDD1emu {
public:
SDD1emu(void);
void decompress(uint32 in_buf, uint16 out_len, uint8 *out_buf);
private:
SDD1_IM IM;
SDD1_GCD GCD;
SDD1_BG BG0; SDD1_BG BG1; SDD1_BG BG2; SDD1_BG BG3;
SDD1_BG BG4; SDD1_BG BG5; SDD1_BG BG6; SDD1_BG BG7;
SDD1_PEM PEM;
SDD1_CM CM;
SDD1_OL OL;
};

View File

@@ -50,7 +50,9 @@
whenever the RTC is set.
*/
void bCPU::srtc_set_time() {
#include "../../base.h"
void SRTC::set_time() {
time_t rawtime;
tm *t;
::time(&rawtime);
@@ -72,12 +74,20 @@ tm *t;
srtc.data[12] = t->tm_wday;
}
void bCPU::srtc_power() {
void SRTC::init() {
}
void SRTC::enable() {
mem_bus->set_mmio_mapper(0x2800, mmio);
mem_bus->set_mmio_mapper(0x2801, mmio);
}
void SRTC::power() {
memset(&srtc, 0, sizeof(srtc));
reset();
}
void bCPU::srtc_reset() {
void SRTC::reset() {
srtc.index = -1;
srtc.mode = SRTC_READ;
}
@@ -87,7 +97,7 @@ void bCPU::srtc_reset() {
//as reads will refresh the data array with the current system
//time. The write method is only here for the sake of faux
//emulation of the real hardware.
void bCPU::srtc_write(uint8 data) {
void SRTC::write(uint8 data) {
data &= 0x0f; //only the low four bits are used
if(data >= 0x0d) {
@@ -140,10 +150,10 @@ void bCPU::srtc_write(uint8 data) {
}
}
uint8 bCPU::srtc_read() {
uint8 SRTC::read() {
if(srtc.mode == SRTC_READ) {
if(srtc.index < 0) {
srtc_set_time();
set_time();
srtc.index++;
return 0x0f; //send start message
} else if(srtc.index > MAX_SRTC_INDEX) {
@@ -156,3 +166,21 @@ uint8 bCPU::srtc_read() {
return 0x00;
}
}
SRTC::SRTC() {
mmio = new SRTCMMIO();
}
uint8 SRTCMMIO::read(uint32 addr) {
switch(addr) {
case 0x2800:return srtc->read();
}
return cpu->regs.mdr;
}
void SRTCMMIO::write(uint32 addr, uint8 value) {
switch(addr) {
case 0x2801:srtc->write(value);break;
}
}

View File

@@ -1,10 +1,12 @@
void srtc_set_time();
void srtc_power();
void srtc_reset();
void srtc_write(uint8 data);
uint8 srtc_read();
class SRTCMMIO : public MMIO {
public:
inline uint8 read (uint32 addr);
inline void write(uint32 addr, uint8 value);
};
#define MAX_SRTC_INDEX 0x0c
class SRTC {
public:
enum { MAX_SRTC_INDEX = 0x0c };
enum {
SRTC_READ = 0,
@@ -18,9 +20,7 @@ enum {
SRTC_COMMAND_CLEAR = 4
};
#define DAYTICKS (60*60*24)
#define HOURTICKS (60*60)
#define MINUTETICKS (60)
SRTCMMIO *mmio;
/******************************
[srtc.data structure]
@@ -38,11 +38,20 @@ Index Description Range
9 Year ones 0-9
10 Year tens 0-9
11 Year hundreds 9-11 (9=19xx, 10=20xx, 11=21xx)
12 Day of week 0-6 (0=Sunday, ...)
12 Day of week 0-6 (0=Sunday, ...)
******************************/
struct {
int8 index;
uint8 mode;
uint8 data[MAX_SRTC_INDEX + 1];
}srtc;
int8 index;
uint8 mode;
uint8 data[MAX_SRTC_INDEX + 1];
} srtc;
void set_time();
void init();
void enable();
void power();
void reset();
void write(uint8 data);
uint8 read();
SRTC();
};

28
src/config/config.cpp Normal file
View File

@@ -0,0 +1,28 @@
Config config_file;
namespace config {
SNES::VideoColorAdjust SNES::video_color_curve(&config_file, "snes.video_color_curve",
"Applies contrast adjust filter to video output when enabled\n"
"Works by lowering the brightness of darker colors,\n"
"while leaving brighter colors alone; thus reducing saturation",
true, Setting::TRUE_FALSE);
SNES::VideoColorAdjust SNES::video_color_adjust_mode(&config_file, "snes.video_color_adjust_mode",
"Selects color adjustment filter for video output\n"
" 0 = Normal (no filter, rgb555)\n"
" 1 = Grayscale mode (l5)\n"
" 2 = VGA mode (rgb332)\n"
" 3 = Genesis mode (rgb333)",
0, Setting::DEC);
void SNES::VideoColorAdjust::set(uint32 _data) {
data = _data;
::snes->update_color_lookup_table();
}
Setting SNES::mute(&config_file, "snes.mute",
"Mutes SNES audio output when enabled",
true, Setting::TRUE_FALSE);
};

15
src/config/config.h Normal file
View File

@@ -0,0 +1,15 @@
extern Config config_file;
namespace config {
extern struct SNES {
static class VideoColorAdjust : public Setting {
public:
void set(uint32 _data);
SettingOperators(VideoColorAdjust);
} video_color_curve, video_color_adjust_mode;
static Setting mute;
} snes;
};

View File

@@ -1,7 +1,5 @@
#include "../../base.h"
#include "srtc.cpp"
#include "bcpu_opfn.cpp"
#include "bcpu_op_read.cpp"
#include "bcpu_op_rmw.cpp"
@@ -15,172 +13,81 @@
#include "bcpu_timing.cpp"
uint8 bCPU::pio_status() {
return status.pio;
}
#include "bcpu_int.cpp"
/***********
*** IRQ ***
***********
cycles:
[1] pbr,pc ; io/opcode
[2] pbr,pc ; io
[3] 0,s ; pbr
[4] 0,s-1 ; pch
[5] 0,s-2 ; pcl
[6] 0,s-3 ; p
[7] 0,va ; aavl
[8] 0,va+1 ; aavh
*/
void bCPU::irq(uint16 addr) {
if(status.cpu_state == CPUSTATE_WAI) {
status.cpu_state = CPUSTATE_RUN;
regs.pc.w++;
}
//GTE documentation is incorrect, first cycle
//is a memory read fetch from PBR:PC
add_cycles(mem_bus->speed(regs.pc.d));
add_cycles(6);
stack_write(regs.pc.b);
stack_write(regs.pc.h);
stack_write(regs.pc.l);
stack_write(regs.p);
rd.l = op_read(OPMODE_ADDR, addr);
rd.h = op_read(OPMODE_ADDR, addr + 1);
regs.pc.b = 0x00;
regs.pc.w = rd.w;
regs.p.i = 1;
regs.p.d = 0;
//let debugger know the new IRQ opcode address
snes->notify(SNES::CPU_EXEC_OPCODE_END);
}
bool bCPU::hdma_test() {
if(status.hdma_triggered == false) {
if(vcounter() < (overscan()?239:224) && hcounter() >= 278) {
status.hdma_triggered = true;
return true;
}
}
return false;
}
bool bCPU::nmi_test() {
if(vcounter() >= ((overscan()?239:224) + 1) && status.nmi_triggered == false) {
if((vcounter() == ((overscan()?239:224) + 1) && hcycles() >= 12) || (vcounter() > ((overscan()?239:224) + 1))) {
status.nmi_triggered = true;
status.nmi_pin = 0;
if(status.nmi_enabled == true) {
return true;
}
}
}
return false;
}
bool bCPU::irq_test() {
int vpos, hpos;
if(regs.p.i)return false; //no interrupt can occur with I flag set
if(status.irq_pin == 0)return false; //same as above
if(status.virq_enabled == false && status.hirq_enabled == false)return false;
//calculate V/H positions required for IRQ to trigger
vpos = status.virq_pos;
hpos = (status.hirq_enabled) ? status.hirq_pos : 0;
//positions that can never be latched
if(vpos == 261 && hpos == 339 && interlace() == false)return false;
if(vpos == 262 && interlace() == false)return false;
if(vpos == 262 && hpos == 339)return false;
if(vpos > 262)return false;
if(hpos > 339)return false;
if(hpos == 0) {
hpos = 24;
} else {
hpos <<= 2;
hpos += 24; //30 - status.cycle_count;
//it should be OK to use the current line cycles/frame lines,
//as the IRQ will only trigger on the correct scanline anyway...
if(hpos >= time.line_cycles) {
hpos -= time.line_cycles;
vpos++;
if(vpos >= time.frame_lines) {
vpos = 0;
}
}
}
if(status.virq_enabled == true && vcounter() != vpos)return false;
if(hcycles() >= hpos && status.irq_pin == 1) {
//dprintf("* vpos=%3d,hpos=%4d; v=%3d,h=%4d; lc=%d,virq=%3d,hirq=%3d",
// vpos, hpos, vcounter(), hcycles(), status.cycle_count, status.virq_pos, status.hirq_pos);
status.irq_triggered = true;
status.irq_pin = 0;
return true;
}
return false;
}
uint8 bCPU::pio_status() { return status.pio; }
void bCPU::run() {
switch(status.cpu_state) {
case CPUSTATE_DMA:
dma_run();
break;
case CPUSTATE_RUN:
case CPUSTATE_WAI:
if(nmi_test() == true) {
irq(0xffea);
break;
}
if(irq_test() == true) {
irq(0xffee);
break;
}
exec_opcode();
break;
case CPUSTATE_STP:
exec_opcode();
break;
if(run_state.hdma) {
exec_hdma();
return;
}
if(run_state.dma) {
exec_dma();
return;
}
if(run_state.irq) {
irq_run();
return;
}
if(run_state.stp) {
exec_cycle();
return;
}
if(status.cycle_pos == 0) {
//interrupts only trigger on opcode edges
if(time.nmi_pending == true) {
time.nmi_pending = false;
aa.w = 0xffea;
run_state.irq = true;
return;
}
if(time.irq_pending == true) {
time.irq_pending = false;
aa.w = 0xffee;
run_state.irq = true;
return;
}
}
exec_cycle();
}
void bCPU::scanline() {
status.hdma_triggered = false;
time.hdma_triggered = false;
if(vcounter() == 225 && status.auto_joypad_poll == true) {
snes->poll_input();
snes->poll_input(SNES::DEV_JOYPAD1);
snes->poll_input(SNES::DEV_JOYPAD2);
//When the SNES auto-polls the joypads, it writes 1, then 0 to
//$4016, then reads from each 16 times to get the joypad state
//information. As a result, the joypad read positions are set
//to 16 after such a poll. Position 16 is the controller
//connected status bit.
status.joypad1_read_pos = 16;
}
if(status.virq_enabled == false) {
status.irq_pin = 1;
status.joypad2_read_pos = 16;
}
}
void bCPU::frame() {
hdma_initialize();
time.nmi_read = 1;
time.nmi_line = 1;
time.nmi_transition = 0;
status.nmi_triggered = false;
status.nmi_pin = 1;
status.r4210_read = false;
status.irq_triggered = false;
status.irq_pin = 1;
if(cpu_version == 2) {
time.hdmainit_trigger_pos = 12 + dma_counter();
} else {
time.hdmainit_trigger_pos = 12 + 8 - dma_counter();
}
time.hdmainit_triggered = false;
}
void bCPU::power() {
srtc_power();
region = snes->region();
regs.a = regs.x = regs.y = 0x0000;
regs.s = 0x01ff;
@@ -188,10 +95,6 @@ void bCPU::power() {
}
void bCPU::reset() {
srtc_reset();
frame();
//reset vector location
regs.pc = mem_bus->read(0xfffc) | (mem_bus->read(0xfffd) << 8);
@@ -203,26 +106,33 @@ void bCPU::reset() {
regs.db = 0x00;
regs.p = 0x34;
regs.e = 1;
regs.mdr = 0x00;
time_reset();
mmio_reset();
dma_reset();
status.cpu_state = CPUSTATE_RUN;
run_state.hdma = false;
run_state.dma = false;
run_state.irq = false;
run_state.wai = false;
run_state.stp = false;
status.hdma_triggered = false;
status.nmi_triggered = false;
status.nmi_pin = 1;
status.r4210_read = false;
status.irq_triggered = false;
status.irq_pin = 1;
status.cycle_pos = 0;
status.cycle_count = 0;
status.cycles_executed = 0;
apu_port[0] = 0x00;
apu_port[1] = 0x00;
apu_port[2] = 0x00;
apu_port[3] = 0x00;
frame();
//initial latch values for $213c/$213d
//[x]0035 : [y]0000 (53.0 -> 212) [lda $2137]
//[x]0038 : [y]0000 (56.5 -> 226) [nop : lda $2137]
add_cycles(186);
}
uint8 bCPU::port_read(uint8 port) {
@@ -235,50 +145,49 @@ void bCPU::port_write(uint8 port, uint8 value) {
void bCPU::cpu_c2() {
if(regs.d.l != 0x00) {
status.cycle_count = 6;
cycle_edge();
add_cycles(6);
cpu_io();
}
}
void bCPU::cpu_c4(uint16 x, uint16 y) {
if(!regs.p.x && (x & 0xff00) != (y & 0xff00)) {
status.cycle_count = 6;
cycle_edge();
add_cycles(6);
cpu_io();
}
}
void bCPU::cpu_c6(uint16 addr) {
if(regs.e && (regs.pc.w & 0xff00) != (addr & 0xff00)) {
status.cycle_count = 6;
cycle_edge();
add_cycles(6);
cpu_io();
}
}
/* The next 3 functions control bus timing for the CPU.
* cpu_io is an I/O cycle, and always 6 clock cycles long.
* mem_read / mem_write indicate memory access bus cycle,
* they are either 6, 8, or 12 bus cycles long, depending
* both on location and the $420d.d0 FastROM enable bit.
*/
void bCPU::cpu_io() {
status.cycle_count = 6;
cycle_edge();
pre_exec_cycle();
add_cycles(6);
}
uint8 bCPU::mem_read(uint32 addr) {
uint8 r;
status.cycle_count = mem_bus->speed(addr);
cycle_edge();
add_cycles(2);
r = mem_bus->read(addr);
add_cycles(status.cycle_count - 2);
return r;
pre_exec_cycle();
add_cycles(status.cycle_count - 4);
regs.mdr = mem_bus->read(addr);
add_cycles(4);
return regs.mdr;
}
void bCPU::mem_write(uint32 addr, uint8 value) {
status.cycle_count = mem_bus->speed(addr);
cycle_edge();
add_cycles(6);
pre_exec_cycle();
add_cycles(status.cycle_count);
mem_bus->write(addr, value);
add_cycles(status.cycle_count - 6);
}
uint32 bCPU::op_addr(uint8 mode, uint32 addr) {
@@ -290,12 +199,11 @@ uint32 bCPU::op_addr(uint8 mode, uint32 addr) {
addr &= 0xffffff;
break;
case OPMODE_DBR:
addr &= 0xffffff;
addr = (regs.db << 16) + addr;
addr = ((regs.db << 16) + addr) & 0xffffff;
break;
case OPMODE_PBR:
addr &= 0xffff;
addr = (regs.pc.b << 16) | addr;
addr = (regs.pc.b << 16) + addr;
break;
case OPMODE_DP:
addr &= 0xffff;
@@ -345,9 +253,7 @@ void bCPU::stack_write(uint8 value) {
}
bCPU::bCPU() {
time_init();
mmio = new bCPUMMIO(this);
init_op_tables();
}

View File

@@ -10,11 +10,12 @@ bCPU *cpu;
class bCPU : public CPU {
private:
typedef void (bCPU::*op)();
op optbl[256];
void (bCPU::*optbl[256])();
enum { NTSC = 0, PAL = 1 };
uint8 region;
public:
#include "srtc.h"
#include "bcpu_timing.h"
uint8 apu_port[4];
@@ -31,41 +32,49 @@ CPUReg24 aa, rd;
uint8 dp, sp;
enum {
CPUSTATE_RUN = 0,
CPUSTATE_WAI,
CPUSTATE_STP,
CPUSTATE_DMA
};
enum {
DMASTATE_STOP = 0,
DMASTATE_INIT,
DMASTATE_DMASYNC,
DMASTATE_DMASYNC2,
DMASTATE_DMASYNC3,
DMASTATE_RUN,
DMASTATE_CPUSYNC
DMASTATE_CPUSYNC,
HDMASTATE_IDMASYNC,
HDMASTATE_IDMASYNC2,
HDMASTATE_IDMASYNC3,
HDMASTATE_ICPUSYNC,
HDMASTATE_DMASYNC,
HDMASTATE_DMASYNC2,
HDMASTATE_DMASYNC3,
HDMASTATE_RUN,
HDMASTATE_CPUSYNC
};
struct {
uint8 cpu_state, cycle_count;
bool hdma;
bool dma;
bool irq;
bool stp;
bool wai;
} run_state;
uint8 dma_state;
uint32 dma_cycle_count;
bool hdma_triggered;
struct {
uint8 cycle_pos, cycle_count;
uint8 opcode;
uint32 cycles_executed;
bool nmi_triggered;
bool nmi_pin;
bool r4210_read;
uint8 dma_state, hdma_state;
uint32 dma_cycle_count, hdma_cycle_count;
bool irq_triggered;
bool irq_pin;
//$4207-$420a
uint16 virq_trigger, hirq_trigger;
//$2181-$2183
uint32 wram_addr;
//$4016
uint8 joypad1_strobe_value;
uint8 joypad1_read_pos;
//$4016-$4017
uint8 joypad1_strobe_value, joypad2_strobe_value;
uint8 joypad1_read_pos, joypad2_read_pos;
//$4200
bool nmi_enabled;
@@ -88,13 +97,21 @@ struct {
//$4214-$4216
uint16 r4214;
uint16 r4216;
}status;
} status;
//$43x0.d7
enum {
DMA_CPUTOMMIO = 0,
DMA_MMIOTOCPU = 1
};
struct {
uint32 read_index; //set to 0 at beginning of DMA/HDMA
//$420b
bool active;
//$420c
bool hdma_active;
bool hdma_enabled;
//$43x0
uint8 dmap;
bool direction;
@@ -104,59 +121,69 @@ struct {
uint8 xfermode;
//$43x1
uint8 destaddr;
//$43x2-$43x4
uint32 srcaddr;
//$43x2-$43x3
uint16 srcaddr;
//$43x4
uint8 srcbank;
//$43x5-$43x6
uint16 xfersize;
union {
uint16 xfersize;
uint16 hdma_iaddr;
};
//$43x7
uint8 hdma_indirect_bank;
uint8 hdma_ibank;
//$43x8-$43x9
uint32 hdma_taddr;
uint16 hdma_addr;
//$43xa
uint8 hdma_line_counter;
//$43xb/$43xf
uint8 hdma_unknown;
//hdma-specific
bool hdma_first_line;
bool hdma_repeat;
uint32 hdma_itaddr;
bool hdma_completed;
}channel[8];
bool hdma_active;
bool hdma_do_transfer;
} channel[8];
inline bool hdma_test();
inline bool nmi_test();
inline bool irq_test();
inline void irq(uint16 addr);
inline bool hdma_test();
inline bool nmi_test();
inline bool irq_test();
inline uint8 pio_status();
inline void run();
inline void scanline();
inline void frame();
inline void power();
inline void reset();
inline uint8 pio_status();
inline void run();
inline uint32 cycles_executed();
inline void scanline();
inline void frame();
inline void power();
inline void reset();
inline void irq_run();
//dma commands
inline void dma_add_cycles(uint32 cycles);
inline void hdma_add_cycles(uint32 cycles);
inline void dma_run();
inline void hdma_run();
inline void hdma_initialize();
inline uint16 dma_cputommio(uint8 i, uint8 index);
inline uint16 dma_mmiotocpu(uint8 i, uint8 index);
inline void dma_xfer_type0(uint8 i);
inline void dma_xfer_type1(uint8 i);
inline void dma_xfer_type2(uint8 i);
inline void dma_xfer_type3(uint8 i);
inline void dma_xfer_type4(uint8 i);
inline void dma_xfer_type5(uint8 i);
inline void hdma_write(uint8 i, uint8 l, uint8 x);
inline void hdma_update(uint8 i);
inline uint8 hdma_enabled_channels();
inline uint8 hdma_active_channels();
inline void hdmainit_activate();
inline void hdma_activate();
inline void dma_cputommio(uint8 i, uint8 index);
inline void dma_mmiotocpu(uint8 i, uint8 index);
inline void dma_write(uint8 i, uint8 index);
inline uint32 dma_addr(uint8 i);
inline uint32 hdma_addr(uint8 i);
inline uint32 hdma_iaddr(uint8 i);
inline uint16 hdma_mmio(uint8 i);
inline uint8 hdma_read(uint8 i);
inline void hdma_write(uint8 i, uint8 x);
inline void dma_reset();
//mmio commands
void mmio_reset();
uint8 mmio_r2180();
uint8 mmio_r21c2();
uint8 mmio_r21c3();
uint8 mmio_r4016();
uint8 mmio_r4017();
uint8 mmio_r4210();
uint8 mmio_r4211();
uint8 mmio_r4212();
@@ -167,6 +194,8 @@ struct {
uint8 mmio_r4217();
uint8 mmio_r4218();
uint8 mmio_r4219();
uint8 mmio_r421a();
uint8 mmio_r421b();
uint8 mmio_r43x0(uint8 i);
uint8 mmio_r43x1(uint8 i);
uint8 mmio_r43x2(uint8 i);
@@ -184,6 +213,7 @@ struct {
void mmio_w2182(uint8 value);
void mmio_w2183(uint8 value);
void mmio_w4016(uint8 value);
void mmio_w4017(uint8 value);
void mmio_w4200(uint8 value);
void mmio_w4201(uint8 value);
void mmio_w4202(uint8 value);
@@ -212,8 +242,12 @@ struct {
void mmio_w43xb(uint8 value, uint8 i);
enum { CYCLE_OPREAD = 0, CYCLE_READ, CYCLE_WRITE, CYCLE_IO };
inline void exec_opcode();
inline void cycle_edge();
inline void pre_exec_cycle();
inline void exec_hdma();
inline void exec_dma();
inline void exec_cycle();
inline void last_cycle();
inline bool in_opcode();
//cpu extra-cycle conditions
inline void cpu_c2();

View File

@@ -1,92 +1,53 @@
uint16 bCPU::dma_cputommio(uint8 i, uint8 index) {
uint8 x;
x = mem_bus->read(channel[i].srcaddr);
mem_bus->write(0x2100 | ((channel[i].destaddr + index) & 0xff), x);
if(channel[i].fixedxfer == false) {
channel[i].srcaddr = (channel[i].srcaddr & 0xff0000) + ((channel[i].srcaddr + channel[i].incmode) & 0xffff);
}
add_cycles(8);
return --channel[i].xfersize;
void bCPU::dma_add_cycles(uint32 cycles) {
status.dma_cycle_count += cycles;
}
uint16 bCPU::dma_mmiotocpu(uint8 i, uint8 index) {
void bCPU::hdma_add_cycles(uint32 cycles) {
if(run_state.dma) {
status.dma_cycle_count += cycles;
}
status.hdma_cycle_count += cycles;
}
uint32 bCPU::dma_addr(uint8 i) {
uint32 r;
r = (channel[i].srcbank << 16) | (channel[i].srcaddr);
if(channel[i].fixedxfer == false) {
channel[i].srcaddr += channel[i].incmode;
}
return r;
}
void bCPU::dma_cputommio(uint8 i, uint8 index) {
uint8 x;
if(sdd1->dma_active() == true) {
x = sdd1->dma_read();
} else {
x = mem_bus->read(dma_addr(i));
}
mem_bus->write(0x2100 | ((channel[i].destaddr + index) & 0xff), x);
add_cycles(8);
channel[i].xfersize--;
}
void bCPU::dma_mmiotocpu(uint8 i, uint8 index) {
uint8 x;
x = mem_bus->read(0x2100 | ((channel[i].destaddr + index) & 0xff));
mem_bus->write(channel[i].srcaddr, x);
if(channel[i].fixedxfer == false) {
channel[i].srcaddr = (channel[i].srcaddr & 0xff0000) + ((channel[i].srcaddr + channel[i].incmode) & 0xffff);
}
mem_bus->write(dma_addr(i), x);
add_cycles(8);
return --channel[i].xfersize;
channel[i].xfersize--;
}
void bCPU::dma_xfer_type0(uint8 i) {
void bCPU::dma_write(uint8 i, uint8 index) {
if(channel[i].direction == 0) {
if(dma_cputommio(i, 0) == 0)return;
dma_cputommio(i, index);
} else {
if(dma_mmiotocpu(i, 0) == 0)return;
}
}
void bCPU::dma_xfer_type1(uint8 i) {
if(channel[i].direction == 0) {
if(dma_cputommio(i, 0) == 0)return;
if(dma_cputommio(i, 1) == 0)return;
} else {
if(dma_mmiotocpu(i, 0) == 0)return;
if(dma_mmiotocpu(i, 1) == 0)return;
}
}
void bCPU::dma_xfer_type2(uint8 i) {
if(channel[i].direction == 0) {
if(dma_cputommio(i, 0) == 0)return;
if(dma_cputommio(i, 0) == 0)return;
} else {
if(dma_mmiotocpu(i, 0) == 0)return;
if(dma_mmiotocpu(i, 0) == 0)return;
}
}
void bCPU::dma_xfer_type3(uint8 i) {
if(channel[i].direction == 0) {
if(dma_cputommio(i, 0) == 0)return;
if(dma_cputommio(i, 0) == 0)return;
if(dma_cputommio(i, 1) == 0)return;
if(dma_cputommio(i, 1) == 0)return;
} else {
if(dma_mmiotocpu(i, 0) == 0)return;
if(dma_mmiotocpu(i, 0) == 0)return;
if(dma_mmiotocpu(i, 1) == 0)return;
if(dma_mmiotocpu(i, 1) == 0)return;
}
}
void bCPU::dma_xfer_type4(uint8 i) {
if(channel[i].direction == 0) {
if(dma_cputommio(i, 0) == 0)return;
if(dma_cputommio(i, 1) == 0)return;
if(dma_cputommio(i, 2) == 0)return;
if(dma_cputommio(i, 3) == 0)return;
} else {
if(dma_mmiotocpu(i, 0) == 0)return;
if(dma_mmiotocpu(i, 1) == 0)return;
if(dma_mmiotocpu(i, 2) == 0)return;
if(dma_mmiotocpu(i, 3) == 0)return;
}
}
void bCPU::dma_xfer_type5(uint8 i) {
if(channel[i].direction == 0) {
if(dma_cputommio(i, 0) == 0)return;
if(dma_cputommio(i, 1) == 0)return;
if(dma_cputommio(i, 0) == 0)return;
if(dma_cputommio(i, 1) == 0)return;
} else {
if(dma_mmiotocpu(i, 0) == 0)return;
if(dma_mmiotocpu(i, 1) == 0)return;
if(dma_mmiotocpu(i, 0) == 0)return;
if(dma_mmiotocpu(i, 1) == 0)return;
dma_mmiotocpu(i, index);
}
}
@@ -95,17 +56,26 @@ int i;
for(i=0;i<8;i++) {
if(channel[i].active == false)continue;
switch(channel[i].xfermode) {
case 0:dma_xfer_type0(i);break;
case 1:dma_xfer_type1(i);break;
case 2:dma_xfer_type2(i);break;
case 3:dma_xfer_type3(i);break;
case 4:dma_xfer_type4(i);break;
case 5:dma_xfer_type5(i);break;
case 6:dma_xfer_type2(i);break;
case 7:dma_xfer_type3(i);break;
//first byte transferred?
if(channel[i].read_index == 0) {
sdd1->dma_begin(i, (channel[i].srcbank << 16) | (channel[i].srcaddr),
channel[i].xfersize);
}
switch(channel[i].xfermode) {
case 0:dma_write(i, 0); break; //0
case 1:dma_write(i, channel[i].read_index & 1); break; //0,1
case 2:dma_write(i, 0); break; //0,0
case 3:dma_write(i, (channel[i].read_index >> 1) & 1);break; //0,0,1,1
case 4:dma_write(i, channel[i].read_index & 3); break; //0,1,2,3
case 5:dma_write(i, channel[i].read_index & 1); break; //0,1,0,1
case 6:dma_write(i, 0); break; //0,0 [2]
case 7:dma_write(i, (channel[i].read_index >> 1) & 1);break; //0,0,1,1 [3]
}
channel[i].read_index++;
dma_add_cycles(8);
if(channel[i].xfersize == 0) {
channel[i].active = false;
}
@@ -116,126 +86,165 @@ int i;
status.dma_state = DMASTATE_CPUSYNC;
}
void bCPU::hdma_write(uint8 i, uint8 l, uint8 x) {
uint32 bCPU::hdma_addr(uint8 i) {
return (channel[i].srcbank << 16) | (channel[i].hdma_addr++);
}
uint32 bCPU::hdma_iaddr(uint8 i) {
return (channel[i].hdma_ibank << 16) | (channel[i].hdma_iaddr++);
}
uint16 bCPU::hdma_mmio(uint8 i) {
uint8 l = channel[i].read_index;
uint16 index;
switch(channel[i].xfermode) {
case 0:mem_bus->write(0x2100 | ((channel[i].destaddr) & 0xff), x);break;
case 1:mem_bus->write(0x2100 | ((channel[i].destaddr + l) & 0xff), x);break;
case 2:mem_bus->write(0x2100 | ((channel[i].destaddr) & 0xff), x);break;
case 3:mem_bus->write(0x2100 | ((channel[i].destaddr + (l >> 1)) & 0xff), x);break;
case 4:mem_bus->write(0x2100 | ((channel[i].destaddr + l) & 0xff), x);break;
case 5:mem_bus->write(0x2100 | ((channel[i].destaddr + (l & 1)) & 0xff), x);break;
case 6:mem_bus->write(0x2100 | ((channel[i].destaddr) & 0xff), x);break;
case 7:mem_bus->write(0x2100 | ((channel[i].destaddr + (l >> 1)) & 0xff), x);break;
case 0:index = 0; break; //0
case 1:index = l & 1; break; //0,1
case 2:index = 0; break; //0,0
case 3:index = (l >> 1) & 1;break; //0,0,1,1
case 4:index = l & 3; break; //0,1,2,3
case 5:index = l & 1; break; //0,1,0,1
case 6:index = 0; break; //0,0 [2]
case 7:index = (l >> 1) & 1;break; //0,0,1,1 [3]
}
return (0x2100 | ((channel[i].destaddr + index) & 0xff));
}
uint8 bCPU::hdma_read(uint8 i) {
if(channel[i].direction == DMA_MMIOTOCPU) {
return mem_bus->read(hdma_mmio(i));
} else if(!channel[i].hdma_indirect) {
return mem_bus->read(hdma_addr(i));
} else {
return mem_bus->read(hdma_iaddr(i));
}
}
void bCPU::hdma_write(uint8 i, uint8 x) {
if(channel[i].direction == DMA_CPUTOMMIO) {
mem_bus->write(hdma_mmio(i), x);
} else if(!channel[i].hdma_indirect) {
mem_bus->write(hdma_addr(i), x);
} else {
mem_bus->write(hdma_iaddr(i), x);
}
add_cycles(8);
hdma_add_cycles(8);
}
void bCPU::hdma_update(uint8 i) {
channel[i].hdma_line_counter = mem_bus->read(hdma_addr(i));
add_cycles(8);
hdma_add_cycles(8);
if(channel[i].hdma_indirect) {
channel[i].hdma_iaddr = mem_bus->read(hdma_addr(i)) << 8;
add_cycles(8);
hdma_add_cycles(8);
}
if(channel[i].hdma_line_counter == 0) {
channel[i].hdma_active = false;
channel[i].hdma_do_transfer = false;
return;
}
channel[i].hdma_do_transfer = true;
if(channel[i].hdma_indirect) {
channel[i].hdma_iaddr >>= 8;
channel[i].hdma_iaddr |= mem_bus->read(hdma_addr(i)) << 8;
add_cycles(8);
hdma_add_cycles(8);
}
}
void bCPU::hdma_run() {
int l, xferlen;
uint8 x, active_channels = 0;
static uint8 hdma_xferlen[8] = { 1, 2, 2, 4, 4, 4, 2, 4 };
for(int i=0;i<8;i++) {
if(channel[i].hdma_completed == true)continue;
// add_cycles(8);
active_channels++;
if(channel[i].hdma_line_counter == 0) {
channel[i].hdma_line_counter = mem_bus->read(channel[i].hdma_taddr++);
if(channel[i].hdma_line_counter == 0) {
channel[i].hdma_completed = true;
continue;
}
if(!channel[i].hdma_enabled || !channel[i].hdma_active)continue;
if(channel[i].hdma_line_counter > 0x80) {
channel[i].hdma_repeat = true;
channel[i].hdma_line_counter -= 0x80;
} else {
channel[i].hdma_repeat = false;
}
channel[i].hdma_first_line = true;
if(channel[i].hdma_indirect == false) {
channel[i].hdma_itaddr = channel[i].hdma_taddr;
} else {
channel[i].hdma_itaddr = mem_bus->read(channel[i].hdma_taddr++);
channel[i].hdma_itaddr |= mem_bus->read(channel[i].hdma_taddr++) << 8;
channel[i].hdma_itaddr |= channel[i].hdma_indirect_bank << 16;
// add_cycles(16);
if(channel[i].hdma_do_transfer) {
int xferlen = hdma_xferlen[channel[i].xfermode];
for(channel[i].read_index=0;channel[i].read_index<xferlen;channel[i].read_index++) {
hdma_write(i, hdma_read(i));
}
}
channel[i].hdma_line_counter--;
if(channel[i].hdma_first_line == false && channel[i].hdma_repeat == false)continue;
channel[i].hdma_first_line = false;
if(channel[i].hdma_indirect == false) {
channel[i].hdma_itaddr = channel[i].hdma_taddr;
channel[i].hdma_do_transfer = !!(channel[i].hdma_line_counter & 0x80);
if((channel[i].hdma_line_counter & 0x7f) == 0) {
hdma_update(i);
}
switch(channel[i].xfermode) {
case 0: xferlen = 1;break;
case 1:case 2:case 6: xferlen = 2;break;
case 3:case 4:case 5:case 7:xferlen = 4;break;
}
for(l=0;l<xferlen;l++) {
x = mem_bus->read(channel[i].hdma_itaddr++);
if(channel[i].hdma_indirect == false) {
channel[i].hdma_taddr++;
}
hdma_write(i, l, x);
// add_cycles(8);
}
}
if(active_channels != 0) {
// add_cycles(18);
}
}
void bCPU::hdma_initialize() {
uint8 active_channels = 0;
uint8 bCPU::hdma_enabled_channels() {
int r = 0;
for(int i=0;i<8;i++) {
if(channel[i].hdma_active == false) {
channel[i].hdma_completed = true;
continue;
}
active_channels++;
channel[i].hdma_first_line = true;
channel[i].hdma_repeat = false;
channel[i].hdma_taddr = channel[i].srcaddr;
channel[i].hdma_line_counter = 0x00;
channel[i].hdma_completed = false;
if(channel[i].hdma_indirect == false) {
add_cycles(8);
} else {
add_cycles(24);
}
if(channel[i].hdma_enabled)r++;
}
return r;
}
uint8 bCPU::hdma_active_channels() {
int r = 0;
for(int i=0;i<8;i++) {
if(channel[i].hdma_enabled && channel[i].hdma_active)r++;
}
return r;
}
/* hdmainit_activate()
* hdma_activate()
*
* Functions are called by CPU timing routine
* when an HDMA event (init or run) occurs.
*/
void bCPU::hdmainit_activate() {
for(int i=0;i<8;i++) {
channel[i].hdma_active = false;
channel[i].hdma_do_transfer = false;
}
if(active_channels != 0) {
add_cycles(18);
if(hdma_enabled_channels() != 0) {
status.hdma_state = HDMASTATE_IDMASYNC;
run_state.hdma = true;
}
}
void bCPU::hdma_activate() {
if(hdma_active_channels() != 0) {
status.hdma_state = HDMASTATE_DMASYNC;
run_state.hdma = true;
}
}
void bCPU::dma_reset() {
for(int i=0;i<8;i++) {
channel[i].active = false;
channel[i].hdma_active = false;
channel[i].dmap = 0x00;
channel[i].direction = 0;
channel[i].hdma_indirect = false;
channel[i].incmode = 1;
channel[i].fixedxfer = false;
channel[i].xfermode = 0;
channel[i].destaddr = 0;
channel[i].srcaddr = 0;
channel[i].xfersize = 0;
channel[i].hdma_indirect_bank = 0;
channel[i].hdma_taddr = 0x000000;
channel[i].hdma_line_counter = 0x00;
channel[i].hdma_unknown = 0x00;
channel[i].read_index = 0;
channel[i].active = false;
channel[i].hdma_enabled = false;
channel[i].dmap = 0x00;
channel[i].direction = 0;
channel[i].hdma_indirect = false;
channel[i].incmode = 1;
channel[i].fixedxfer = false;
channel[i].xfermode = 0;
channel[i].destaddr = 0;
channel[i].srcaddr = 0;
channel[i].xfersize = 0x0000;
//xfersize and hdma_iaddr are of union { uint16 };
//channel[i].hdma_iaddr = 0x0000;
channel[i].hdma_ibank = 0;
channel[i].hdma_addr = 0x0000;
channel[i].hdma_line_counter = 0x00;
channel[i].hdma_unknown = 0x00;
channel[i].hdma_first_line = false;
channel[i].hdma_repeat = false;
channel[i].hdma_itaddr = 0x000000;
channel[i].hdma_completed = true;
channel[i].hdma_active = false;
channel[i].hdma_do_transfer = false;
}
}

View File

@@ -1,40 +1,163 @@
inline void bCPU::cycle_edge() {
int c, n, z;
if(status.dma_state != DMASTATE_STOP) {
switch(status.dma_state) {
case DMASTATE_INIT:
status.dma_state = DMASTATE_DMASYNC;
void bCPU::last_cycle() {
time.nmi_pending = nmi_test();
time.irq_pending = irq_test();
}
void bCPU::pre_exec_cycle() {
if(!run_state.dma && !run_state.hdma)return;
int c, z;
if(run_state.hdma) {
switch(status.hdma_state) {
case HDMASTATE_ICPUSYNC:
case HDMASTATE_CPUSYNC:
c = status.cycle_count;
z = c - (status.hdma_cycle_count % c);
if(!z)z = c;
add_cycles(z);
run_state.hdma = false;
break;
case DMASTATE_DMASYNC:
n = 8 - dma_counter() + 8;
add_cycles(n);
status.dma_cycle_count = n;
for(z=0;z<8;z++) {
if(channel[z].active == false)continue;
add_cycles(8);
status.dma_cycle_count += 8;
status.dma_cycle_count += channel[z].xfersize << 3;
}
status.cpu_state = CPUSTATE_DMA;
status.dma_state = DMASTATE_RUN;
while(status.dma_state == DMASTATE_RUN) {
dma_run();
}
status.cpu_state = CPUSTATE_RUN;
}
}
if(run_state.dma) {
switch(status.dma_state) {
case DMASTATE_CPUSYNC:
c = status.cycle_count;
z = c - (status.dma_cycle_count % c);
if(!z)z = c;
add_cycles(z);
status.dma_state = DMASTATE_STOP;
run_state.dma = false;
break;
}
}
}
void bCPU::exec_opcode() {
snes->notify(SNES::CPU_EXEC_OPCODE_BEGIN);
(this->*optbl[op_read()])();
snes->notify(SNES::CPU_EXEC_OPCODE_END);
void bCPU::exec_hdma() {
int n;
static int z;
switch(status.hdma_state) {
case HDMASTATE_IDMASYNC:
status.hdma_cycle_count = 0;
z = 0;
if(!run_state.dma) {
exec_cycle();
status.hdma_state = HDMASTATE_IDMASYNC2;
} else {
status.hdma_state = HDMASTATE_IDMASYNC3;
}
break;
case HDMASTATE_IDMASYNC2:
n = 8 - dma_counter() + 8;
add_cycles(n);
status.hdma_cycle_count += n;
status.hdma_state = HDMASTATE_IDMASYNC3;
break;
case HDMASTATE_IDMASYNC3:
channel[z].hdma_active = channel[z].hdma_enabled;
if(channel[z].hdma_enabled) {
channel[z].hdma_addr = channel[z].srcaddr;
hdma_update(z); //updates status.hdma_cycle_count
}
if(++z < 8)break;
if(!run_state.dma) {
status.hdma_state = HDMASTATE_ICPUSYNC;
} else {
run_state.hdma = false;
}
break;
case HDMASTATE_ICPUSYNC:
exec_cycle();
break;
case HDMASTATE_DMASYNC:
status.hdma_cycle_count = 0;
z = 0;
if(!run_state.dma) {
exec_cycle();
status.hdma_state = HDMASTATE_DMASYNC2;
} else {
status.hdma_state = HDMASTATE_DMASYNC3;
}
break;
case HDMASTATE_DMASYNC2:
n = 8 - dma_counter() + 8;
add_cycles(n);
status.hdma_cycle_count += n;
status.hdma_state = HDMASTATE_DMASYNC3;
break;
case HDMASTATE_DMASYNC3:
if(channel[z].hdma_active) {
add_cycles(8);
status.hdma_cycle_count += 8;
}
if(++z < 8)break;
status.hdma_state = HDMASTATE_RUN;
break;
case HDMASTATE_RUN:
hdma_run(); //updates status.hdma_cycle_count
if(!run_state.dma) {
status.hdma_state = HDMASTATE_CPUSYNC;
} else {
run_state.hdma = false;
}
break;
case HDMASTATE_CPUSYNC:
exec_cycle();
break;
}
}
void bCPU::exec_dma() {
int n;
static int z;
switch(status.dma_state) {
case DMASTATE_DMASYNC:
exec_cycle();
status.dma_state = DMASTATE_DMASYNC2;
break;
case DMASTATE_DMASYNC2:
n = 8 - dma_counter() + 8;
add_cycles(n);
status.dma_cycle_count = n;
z = 0;
status.dma_state = DMASTATE_DMASYNC3;
break;
case DMASTATE_DMASYNC3:
if(channel[z].active == true) {
add_cycles(8);
status.dma_cycle_count += 8;
}
if(++z < 8)break;
status.dma_state = DMASTATE_RUN;
break;
case DMASTATE_RUN:
dma_run(); //updates status.dma_cycle_count
break;
case DMASTATE_CPUSYNC:
exec_cycle();
break;
}
}
void bCPU::exec_cycle() {
//on first cycle?
if(status.cycle_pos == 0) {
snes->notify(SNES::CPU_EXEC_OPCODE_BEGIN);
status.opcode = op_read();
status.cycle_pos = 1;
return;
}
(this->*optbl[status.opcode])();
if(status.cycle_pos == 0) {
snes->notify(SNES::CPU_EXEC_OPCODE_END);
}
}
//only return true when we are on an opcode edge
bool bCPU::in_opcode() {
return (status.cycle_pos != 0);
}
void bCPU::init_op_tables() {

67
src/cpu/bcpu/bcpu_int.cpp Normal file
View File

@@ -0,0 +1,67 @@
/*
[IRQ cycles]
[0] pbr,pc ; opcode
[1] pbr,pc ; io
[2] 0,s ; pbr
[3] 0,s-1 ; pch
[4] 0,s-2 ; pcl
[5] 0,s-3 ; p
[6] 0,va ; aavl
[7] 0,va+1 ; aavh
*/
void bCPU::irq_run() {
//WDC documentation is incorrect, first cycle
//is a memory read fetch from PBR:PC
switch(status.cycle_pos++) {
case 0: add_cycles(mem_bus->speed(regs.pc.d)); break;
case 1: add_cycles(6); break;
case 2: stack_write(regs.pc.b); break;
case 3: stack_write(regs.pc.h); break;
case 4: stack_write(regs.pc.l); break;
case 5: stack_write(regs.p); break;
case 6: rd.l = op_read(OPMODE_ADDR, aa.w); break;
case 7: rd.h = op_read(OPMODE_ADDR, aa.w + 1);
regs.pc.b = 0x00;
regs.pc.w = rd.w;
regs.p.i = 1;
regs.p.d = 0;
//let debugger know the new IRQ opcode address
snes->notify(SNES::CPU_EXEC_OPCODE_END);
status.cycle_pos = 0;
run_state.irq = false;
break;
}
}
bool bCPU::nmi_test() {
if(time.nmi_transition == 0)return false;
time.nmi_transition = 0;
run_state.wai = false;
return true;
}
bool bCPU::irq_test() {
if(time.irq_transition == 1)goto _true;
if(time.irq_read == 0) {
if(time.irq_line == 1 && (irq_trigger_pos_match(0) || irq_trigger_pos_match(2))) {
return false;
}
goto _true;
}
if(time.irq_line == 0) {
time.irq_line = 1;
goto _true;
}
return false;
_true:
time.irq_transition = 0;
run_state.wai = false;
if(regs.p.i)return false;
return true;
}

View File

@@ -2,9 +2,11 @@ void bCPU::mmio_reset() {
//$2181-$2183
status.wram_addr = 0x000000;
//$4016
//$4016-$4017
status.joypad1_strobe_value = 0x00;
status.joypad2_strobe_value = 0x00;
status.joypad1_read_pos = 0;
status.joypad2_read_pos = 0;
//$4200
status.nmi_enabled = false;
@@ -41,29 +43,22 @@ uint8 r;
return r;
}
//???
uint8 bCPU::mmio_r21c2() {
return 0x20;
}
//???
uint8 bCPU::mmio_r21c3() {
return 0x00;
}
/*
The joypad contains a small bit shifter that has 16 bits.
Reading from 4016 reads one bit from this buffer, then moves
the buffer left one, and adds a '1' to the rightmost bit.
Writing a one to $4016 will fill the buffer with the current
joypad button states, and lock the bit shifter at position
zero. All reads will be the first buffer state, or 'B'.
A zero must be written back to $4016 to unlock the buffer,
so that reads will increment the bit shifting position.
*/
//JOYSER0
//7-2 = MDR
//1-0 = Joypad serial data
/* The joypad contains a small bit shifter that has 16 bits.
* Reading from 4016 reads one bit from this buffer, then moves
* the buffer left one, and adds a '1' to the rightmost bit.
* Writing a one to $4016 will fill the buffer with the current
* joypad button states, and lock the bit shifter at position
* zero. All reads will be the first buffer state, or 'B'.
* A zero must be written back to $4016 to unlock the buffer,
* so that reads will increment the bit shifting position.
*/
uint8 bCPU::mmio_r4016() {
uint8 r = 0x00;
uint8 r;
r = regs.mdr & 0xfc;
if(status.joypad1_strobe_value == 1) {
r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD1, SNES::JOYPAD_B);
} else {
@@ -85,59 +80,98 @@ uint8 r = 0x00;
}
if(++status.joypad1_read_pos > 17)status.joypad1_read_pos = 17;
}
return r;
}
//JOYSER1
//7-5 = MDR
//4-2 = Always 1
//1-0 = Joypad serial data
uint8 bCPU::mmio_r4017() {
uint8 r;
r = regs.mdr & 0xe0;
r |= 0x1c;
if(status.joypad2_strobe_value == 1) {
r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_B);
} else {
switch(status.joypad2_read_pos) {
case 0:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_B); break;
case 1:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_Y); break;
case 2:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_SELECT);break;
case 3:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_START); break;
case 4:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_UP); break;
case 5:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_DOWN); break;
case 6:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_LEFT); break;
case 7:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_RIGHT); break;
case 8:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_A); break;
case 9:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_X); break;
case 10:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_L); break;
case 11:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_R); break;
case 16:r |= 1;break; //joypad connected bit
default:r |= 1;break; //after 16th read, all subsequent reads return 1
}
if(++status.joypad2_read_pos > 17)status.joypad2_read_pos = 17;
}
return r;
}
//RDNMI
//7 = NMI acknowledge
//6-4 = MDR
//3-0 = CPU (5a22) version
uint8 bCPU::mmio_r4210() {
uint8 r = 0x00;
uint16 v, h, hc, vs;
v = vcounter();
h = hcounter();
hc = hcycles();
vs = (overscan()?239:224);
uint8 r;
r = regs.mdr & 0x70;
r |= uint8(!time.nmi_read) << 7;
if(status.r4210_read == false) {
if((v == (vs + 1) && hc >= 2) || v > (vs + 1)) {
r |= 0x80;
status.r4210_read = true;
status.nmi_pin = 1;
}
if(!nmi_trigger_pos_match(0) && !nmi_trigger_pos_match(2)) {
time.nmi_read = 1;
}
r |= 0x40;
r |= 0x02; //cpu version number
r |= (cpu_version & 0x0f);
return r;
}
//TIMEUP
//7 = IRQ acknowledge
//6-0 = MDR
uint8 bCPU::mmio_r4211() {
uint8 r = 0x00;
r |= 0x40;
if(status.irq_triggered == true)r |= 0x80;
status.irq_triggered = false;
uint8 r;
r = regs.mdr & 0x7f;
r |= uint8(!time.irq_read) << 7;
if(!irq_trigger_pos_match(0) && !irq_trigger_pos_match(2)) {
time.irq_read = 1;
time.irq_line = 1;
time.irq_transition = 0;
}
return r;
}
//HVBJOY
//7 = in vblank
//6 = in hblank
//5-1 = MDR
//0 = joypad ready
uint8 bCPU::mmio_r4212() {
uint8 r;
uint16 v, h, hc, vs;
r = 0x00;
uint8 r;
r = regs.mdr & 0x3e;
v = vcounter();
h = hcounter();
hc = hcycles();
vs = (overscan()?239:224);
uint16 vs = overscan() ? 240 : 225;
//auto joypad polling
if(v >= (vs + 1) && v <= (vs + 3))r |= 0x01;
if(time.v >= vs && time.v <= (vs + 2))r |= 0x01;
//hblank
if(hc <= 2 || hc >= 1096)r |= 0x40;
if(time.hc <= 2 || time.hc >= 1096)r |= 0x40;
//vblank
if(v >= (vs + 1))r |= 0x80;
if(time.v >= vs)r |= 0x80;
return r;
}
@@ -168,7 +202,7 @@ uint8 bCPU::mmio_r4217() {
//JOY1L
uint8 bCPU::mmio_r4218() {
uint8 r = 0x00;
uint8 r = 0x00;
uint16 v = vcounter();
if(status.auto_joypad_poll == false)return 0x00; //can't read joypad if auto polling not enabled
//if(v >= 225 && v <= 227)return 0x00; //can't read joypad while SNES is polling input
@@ -181,7 +215,7 @@ uint16 v = vcounter();
//JOY1H
uint8 bCPU::mmio_r4219() {
uint8 r = 0x00;
uint8 r = 0x00;
uint16 v = vcounter();
if(status.auto_joypad_poll == false)return 0x00; //can't read joypad if auto polling not enabled
//if(v >= 225 && v <= 227)return 0x00; //can't read joypad while SNES is polling input
@@ -196,6 +230,36 @@ uint16 v = vcounter();
return r;
}
//JOY2L
uint8 bCPU::mmio_r421a() {
uint8 r = 0x00;
uint16 v = vcounter();
if(status.auto_joypad_poll == false)return 0x00; //can't read joypad if auto polling not enabled
//if(v >= 225 && v <= 227)return 0x00; //can't read joypad while SNES is polling input
r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_A) << 7;
r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_X) << 6;
r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_L) << 5;
r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_R) << 4;
return r;
}
//JOY2H
uint8 bCPU::mmio_r421b() {
uint8 r = 0x00;
uint16 v = vcounter();
if(status.auto_joypad_poll == false)return 0x00; //can't read joypad if auto polling not enabled
//if(v >= 225 && v <= 227)return 0x00; //can't read joypad while SNES is polling input
r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_B) << 7;
r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_Y) << 6;
r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_SELECT) << 5;
r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_START) << 4;
r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_UP) << 3;
r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_DOWN) << 2;
r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_LEFT) << 1;
r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_RIGHT);
return r;
}
//DMAPx
uint8 bCPU::mmio_r43x0(uint8 i) {
return channel[i].dmap;
@@ -218,7 +282,7 @@ uint8 bCPU::mmio_r43x3(uint8 i) {
//A1Bx
uint8 bCPU::mmio_r43x4(uint8 i) {
return channel[i].srcaddr >> 16;
return channel[i].srcbank;
}
//DASxL
@@ -233,17 +297,17 @@ uint8 bCPU::mmio_r43x6(uint8 i) {
//DASBx
uint8 bCPU::mmio_r43x7(uint8 i) {
return channel[i].hdma_indirect_bank;
return channel[i].hdma_ibank;
}
//A2AxL
uint8 bCPU::mmio_r43x8(uint8 i) {
return channel[i].hdma_taddr;
return channel[i].hdma_addr;
}
//A2AxH
uint8 bCPU::mmio_r43x9(uint8 i) {
return channel[i].hdma_taddr >> 8;
return channel[i].hdma_addr >> 8;
}
//NTRLx
@@ -257,9 +321,7 @@ uint8 bCPU::mmio_r43xb(uint8 i) {
}
uint8 bCPUMMIO::read(uint32 addr) {
uint8 i;
//cpu->sync();
uint i;
//APU
if(addr >= 0x2140 && addr <= 0x217f) {
return apu->port_read(addr & 3);
@@ -281,19 +343,17 @@ uint8 i;
case 0x9:return cpu->mmio_r43x9(i);
case 0xa:return cpu->mmio_r43xa(i);
case 0xb:return cpu->mmio_r43xb(i);
case 0xc:return 0x43; //unmapped
case 0xd:return 0x43; //unmapped
case 0xe:return 0x43; //unmapped
case 0xc:return cpu->regs.mdr; //unmapped
case 0xd:return cpu->regs.mdr; //unmapped
case 0xe:return cpu->regs.mdr; //unmapped
case 0xf:return cpu->mmio_r43xb(i); //mirror of 43xb
}
}
switch(addr) {
case 0x2180:return cpu->mmio_r2180(); //WMDATA
case 0x21c2:return cpu->mmio_r21c2(); //???
case 0x21c3:return cpu->mmio_r21c3(); //???
case 0x2800:return cpu->srtc_read();
case 0x4016:return cpu->mmio_r4016(); //JOYSER0
case 0x4017:return cpu->mmio_r4017(); //JOYSER1
case 0x4210:return cpu->mmio_r4210(); //RDNMI
case 0x4211:return cpu->mmio_r4211(); //TIMEUP
case 0x4212:return cpu->mmio_r4212(); //HVBJOY
@@ -304,9 +364,12 @@ uint8 i;
case 0x4217:return cpu->mmio_r4217(); //RDMPYH
case 0x4218:return cpu->mmio_r4218(); //JOY1L
case 0x4219:return cpu->mmio_r4219(); //JOY1H
case 0x421a:case 0x421b:case 0x421c:case 0x421d:case 0x421e:case 0x421f:return 0x00;
case 0x421a:return cpu->mmio_r421a(); //JOY2L
case 0x421b:return cpu->mmio_r421b(); //JOY2H
case 0x421c:case 0x421d:case 0x421e:case 0x421f:return 0x00;
}
return 0x00;
return cpu->regs.mdr;
}
//WMDATA
@@ -336,13 +399,22 @@ void bCPU::mmio_w2183(uint8 value) {
//JOYSER0
void bCPU::mmio_w4016(uint8 value) {
status.joypad1_strobe_value = (value & 1);
status.joypad1_strobe_value = !!(value & 1);
if(value == 1) {
snes->poll_input();
snes->poll_input(SNES::DEV_JOYPAD1);
status.joypad1_read_pos = 0;
}
}
//JOYSER1
void bCPU::mmio_w4017(uint8 value) {
status.joypad2_strobe_value = !!(value & 1);
if(value == 1) {
snes->poll_input(SNES::DEV_JOYPAD2);
status.joypad2_read_pos = 0;
}
}
//NMITIMEN
void bCPU::mmio_w4200(uint8 value) {
status.nmi_enabled = !!(value & 0x80);
@@ -350,13 +422,20 @@ void bCPU::mmio_w4200(uint8 value) {
status.hirq_enabled = !!(value & 0x10);
status.auto_joypad_poll = !!(value & 0x01);
if(status.nmi_enabled == false) {
status.nmi_pin = 0;
if(time.nmi_read == 0) {
if(time.nmi_line == 1 && !status.nmi_enabled == 0) {
time.nmi_transition = 1;
}
time.nmi_line = !status.nmi_enabled;
}
if(status.virq_enabled == false && status.hirq_enabled == false) {
status.irq_pin = 0;
time.irq_line = 1;
time.irq_read = 1;
time.irq_transition = 0;
}
update_interrupts();
}
//WRIO
@@ -390,44 +469,48 @@ void bCPU::mmio_w4205(uint8 value) {
//WRDIVB
void bCPU::mmio_w4206(uint8 value) {
status.div_b = value;
status.r4214 = (status.div_b)?status.div_a / status.div_b : 0xffff;
status.r4216 = (status.div_b)?status.div_a % status.div_b : status.div_a;
status.r4214 = (status.div_b) ? status.div_a / status.div_b : 0xffff;
status.r4216 = (status.div_b) ? status.div_a % status.div_b : status.div_a;
}
//HTIMEL
void bCPU::mmio_w4207(uint8 value) {
status.hirq_pos = (status.hirq_pos & 0xff00) | value;
if(status.irq_triggered == false)status.irq_pin = 1;
update_interrupts();
}
//HTIMEH
void bCPU::mmio_w4208(uint8 value) {
status.hirq_pos = (status.hirq_pos & 0x00ff) | (value << 8);
if(status.irq_triggered == false)status.irq_pin = 1;
update_interrupts();
}
//VTIMEL
void bCPU::mmio_w4209(uint8 value) {
status.virq_pos = (status.virq_pos & 0xff00) | value;
if(status.irq_triggered == false)status.irq_pin = 1;
update_interrupts();
}
//VTIMEH
void bCPU::mmio_w420a(uint8 value) {
status.virq_pos = (status.virq_pos & 0x00ff) | (value << 8);
if(status.irq_triggered == false)status.irq_pin = 1;
update_interrupts();
}
//DMAEN
void bCPU::mmio_w420b(uint8 value) {
int len;
if(value != 0x00) {
status.dma_state = DMASTATE_INIT;
run_state.dma = true;
status.dma_state = DMASTATE_DMASYNC;
}
for(int i=0;i<8;i++) {
if(value & (1 << i)) {
channel[i].active = true;
channel[i].hdma_active = false;
channel[i].active = true;
channel[i].hdma_enabled = false;
channel[i].hdma_active = false;
channel[i].read_index = 0;
}
}
}
@@ -435,13 +518,14 @@ void bCPU::mmio_w420b(uint8 value) {
//HDMAEN
void bCPU::mmio_w420c(uint8 value) {
for(int i=0;i<8;i++) {
channel[i].hdma_active = !!(value & (1 << i));
channel[i].hdma_enabled = !!(value & (1 << i));
channel[i].hdma_active = !!(value & (1 << i));
}
}
//MEMSEL
void bCPU::mmio_w420d(uint8 value) {
mem_bus->fastROM = value & 1;
mem_bus->set_speed(value & 1);
}
//DMAPx
@@ -461,17 +545,17 @@ void bCPU::mmio_w43x1(uint8 value, uint8 i) {
//A1TxL
void bCPU::mmio_w43x2(uint8 value, uint8 i) {
channel[i].srcaddr = (channel[i].srcaddr & 0xffff00) | value;
channel[i].srcaddr = (channel[i].srcaddr & 0xff00) | value;
}
//A1TxH
void bCPU::mmio_w43x3(uint8 value, uint8 i) {
channel[i].srcaddr = (channel[i].srcaddr & 0xff00ff) | (value << 8);
channel[i].srcaddr = (channel[i].srcaddr & 0x00ff) | (value << 8);
}
//A1Bx
void bCPU::mmio_w43x4(uint8 value, uint8 i) {
channel[i].srcaddr = (channel[i].srcaddr & 0x00ffff) | (value << 16);
channel[i].srcbank = value;
}
//DASxL
@@ -486,17 +570,17 @@ void bCPU::mmio_w43x6(uint8 value, uint8 i) {
//DASBx
void bCPU::mmio_w43x7(uint8 value, uint8 i) {
channel[i].hdma_indirect_bank = value;
channel[i].hdma_ibank = value;
}
//A2AxL
void bCPU::mmio_w43x8(uint8 value, uint8 i) {
channel[i].hdma_taddr = (channel[i].hdma_taddr & 0xffff00) | value;
channel[i].hdma_addr = (channel[i].hdma_addr & 0xff00) | value;
}
//A2AxH
void bCPU::mmio_w43x9(uint8 value, uint8 i) {
channel[i].hdma_taddr = (channel[i].hdma_taddr & 0xff00ff) | (value << 8);
channel[i].hdma_addr = (channel[i].hdma_addr & 0x00ff) | (value << 8);
}
//NTRLx
@@ -511,8 +595,6 @@ void bCPU::mmio_w43xb(uint8 value, uint8 i) {
void bCPUMMIO::write(uint32 addr, uint8 value) {
uint8 i;
//cpu->sync();
//APU
if(addr >= 0x2140 && addr <= 0x217f) {
cpu->port_write(addr & 3, value);
@@ -547,8 +629,8 @@ uint8 i;
case 0x2181:cpu->mmio_w2181(value);return; //WMADDL
case 0x2182:cpu->mmio_w2182(value);return; //WMADDM
case 0x2183:cpu->mmio_w2183(value);return; //WMADDH
case 0x2801:cpu->srtc_write(value);return;
case 0x4016:cpu->mmio_w4016(value);return; //JOYSER0
case 0x4017:cpu->mmio_w4017(value);return; //JOYSER1
case 0x4200:cpu->mmio_w4200(value);return; //NMITIMEN
case 0x4201:cpu->mmio_w4201(value);return; //WRIO
case 0x4202:cpu->mmio_w4202(value);return; //WRMPYA

View File

@@ -186,7 +186,6 @@ void op_sta_idpy();
void op_sta_ildpy();
void op_sta_sr();
void op_sta_isry();
void op_bra();
void op_bcc();
void op_bcs();
void op_bne();
@@ -195,6 +194,7 @@ void op_bpl();
void op_bmi();
void op_bvc();
void op_bvs();
void op_bra();
void op_brl();
void op_jmp_addr();
void op_jmp_long();

File diff suppressed because it is too large Load Diff

View File

@@ -1,310 +1,482 @@
void bCPU::op_bra() {
l1:
rd.l = op_read();
if(1) {
aa.w = regs.pc.d + (int8)rd.l;
regs.pc.w = aa.w;
} else {
return;
}
l2:
cpu_c6(aa.w);
l3:
cpu_io();
}
void bCPU::op_bcc() {
l1:
rd.l = op_read();
if(!regs.p.c) {
aa.w = regs.pc.d + (int8)rd.l;
regs.pc.w = aa.w;
} else {
return;
switch(status.cycle_pos++) {
case 1:
if(!!regs.p.c)last_cycle();
rd.l = op_read();
if(!regs.p.c) {
aa.w = regs.pc.d + (int8)rd.l;
regs.pc.w = aa.w;
} else {
status.cycle_pos = 0;
}
break;
case 2:
cpu_c6(aa.w);
break;
case 3:
last_cycle();
cpu_io();
status.cycle_pos = 0;
break;
}
l2:
cpu_c6(aa.w);
l3:
cpu_io();
}
void bCPU::op_bcs() {
l1:
rd.l = op_read();
if(regs.p.c) {
aa.w = regs.pc.d + (int8)rd.l;
regs.pc.w = aa.w;
} else {
return;
switch(status.cycle_pos++) {
case 1:
if(!regs.p.c)last_cycle();
rd.l = op_read();
if(regs.p.c) {
aa.w = regs.pc.d + (int8)rd.l;
regs.pc.w = aa.w;
} else {
status.cycle_pos = 0;
}
break;
case 2:
cpu_c6(aa.w);
break;
case 3:
last_cycle();
cpu_io();
status.cycle_pos = 0;
break;
}
l2:
cpu_c6(aa.w);
l3:
cpu_io();
}
void bCPU::op_bne() {
l1:
rd.l = op_read();
if(!regs.p.z) {
aa.w = regs.pc.d + (int8)rd.l;
regs.pc.w = aa.w;
} else {
return;
switch(status.cycle_pos++) {
case 1:
if(!!regs.p.z)last_cycle();
rd.l = op_read();
if(!regs.p.z) {
aa.w = regs.pc.d + (int8)rd.l;
regs.pc.w = aa.w;
} else {
status.cycle_pos = 0;
}
break;
case 2:
cpu_c6(aa.w);
break;
case 3:
last_cycle();
cpu_io();
status.cycle_pos = 0;
break;
}
l2:
cpu_c6(aa.w);
l3:
cpu_io();
}
void bCPU::op_beq() {
l1:
rd.l = op_read();
if(regs.p.z) {
aa.w = regs.pc.d + (int8)rd.l;
regs.pc.w = aa.w;
} else {
return;
switch(status.cycle_pos++) {
case 1:
if(!regs.p.z)last_cycle();
rd.l = op_read();
if(regs.p.z) {
aa.w = regs.pc.d + (int8)rd.l;
regs.pc.w = aa.w;
} else {
status.cycle_pos = 0;
}
break;
case 2:
cpu_c6(aa.w);
break;
case 3:
last_cycle();
cpu_io();
status.cycle_pos = 0;
break;
}
l2:
cpu_c6(aa.w);
l3:
cpu_io();
}
void bCPU::op_bpl() {
l1:
rd.l = op_read();
if(!regs.p.n) {
aa.w = regs.pc.d + (int8)rd.l;
regs.pc.w = aa.w;
} else {
return;
switch(status.cycle_pos++) {
case 1:
if(!!regs.p.n)last_cycle();
rd.l = op_read();
if(!regs.p.n) {
aa.w = regs.pc.d + (int8)rd.l;
regs.pc.w = aa.w;
} else {
status.cycle_pos = 0;
}
break;
case 2:
cpu_c6(aa.w);
break;
case 3:
last_cycle();
cpu_io();
status.cycle_pos = 0;
break;
}
l2:
cpu_c6(aa.w);
l3:
cpu_io();
}
void bCPU::op_bmi() {
l1:
rd.l = op_read();
if(regs.p.n) {
aa.w = regs.pc.d + (int8)rd.l;
regs.pc.w = aa.w;
} else {
return;
switch(status.cycle_pos++) {
case 1:
if(!regs.p.n)last_cycle();
rd.l = op_read();
if(regs.p.n) {
aa.w = regs.pc.d + (int8)rd.l;
regs.pc.w = aa.w;
} else {
status.cycle_pos = 0;
}
break;
case 2:
cpu_c6(aa.w);
break;
case 3:
last_cycle();
cpu_io();
status.cycle_pos = 0;
break;
}
l2:
cpu_c6(aa.w);
l3:
cpu_io();
}
void bCPU::op_bvc() {
l1:
rd.l = op_read();
if(!regs.p.v) {
aa.w = regs.pc.d + (int8)rd.l;
regs.pc.w = aa.w;
} else {
return;
switch(status.cycle_pos++) {
case 1:
if(!!regs.p.v)last_cycle();
rd.l = op_read();
if(!regs.p.v) {
aa.w = regs.pc.d + (int8)rd.l;
regs.pc.w = aa.w;
} else {
status.cycle_pos = 0;
}
break;
case 2:
cpu_c6(aa.w);
break;
case 3:
last_cycle();
cpu_io();
status.cycle_pos = 0;
break;
}
l2:
cpu_c6(aa.w);
l3:
cpu_io();
}
void bCPU::op_bvs() {
l1:
rd.l = op_read();
if(regs.p.v) {
switch(status.cycle_pos++) {
case 1:
if(!regs.p.v)last_cycle();
rd.l = op_read();
if(regs.p.v) {
aa.w = regs.pc.d + (int8)rd.l;
regs.pc.w = aa.w;
} else {
status.cycle_pos = 0;
}
break;
case 2:
cpu_c6(aa.w);
break;
case 3:
last_cycle();
cpu_io();
status.cycle_pos = 0;
break;
}
}
void bCPU::op_bra() {
switch(status.cycle_pos++) {
case 1:
rd.l = op_read();
aa.w = regs.pc.d + (int8)rd.l;
regs.pc.w = aa.w;
} else {
return;
break;
case 2:
cpu_c6(aa.w);
break;
case 3:
last_cycle();
cpu_io();
status.cycle_pos = 0;
break;
}
l2:
cpu_c6(aa.w);
l3:
cpu_io();
}
void bCPU::op_brl() {
l1:
rd.l = op_read();
l2:
rd.h = op_read();
l3:
cpu_io();
regs.pc.w = regs.pc.d + (int16)rd.w;
switch(status.cycle_pos++) {
case 1:
rd.l = op_read();
break;
case 2:
rd.h = op_read();
break;
case 3:
last_cycle();
cpu_io();
regs.pc.w = regs.pc.d + (int16)rd.w;
status.cycle_pos = 0;
break;
}
}
void bCPU::op_jmp_addr() {
l1:
rd.l = op_read();
l2:
rd.h = op_read();
regs.pc.w = rd.w;
switch(status.cycle_pos++) {
case 1:
rd.l = op_read();
break;
case 2:
last_cycle();
rd.h = op_read();
regs.pc.w = rd.w;
status.cycle_pos = 0;
break;
}
}
void bCPU::op_jmp_long() {
l1:
rd.l = op_read();
l2:
rd.h = op_read();
l3:
rd.b = op_read();
regs.pc.d = rd.d & 0xffffff;
switch(status.cycle_pos++) {
case 1:
rd.l = op_read();
break;
case 2:
rd.h = op_read();
break;
case 3:
last_cycle();
rd.b = op_read();
regs.pc.d = rd.d & 0xffffff;
status.cycle_pos = 0;
break;
}
}
void bCPU::op_jmp_iaddr() {
l1:
aa.l = op_read();
l2:
aa.h = op_read();
l3:
rd.l = op_read(OPMODE_ADDR, aa.w);
l4:
rd.h = op_read(OPMODE_ADDR, aa.w + 1);
regs.pc.w = rd.w;
switch(status.cycle_pos++) {
case 1:
aa.l = op_read();
break;
case 2:
aa.h = op_read();
break;
case 3:
rd.l = op_read(OPMODE_ADDR, aa.w);
break;
case 4:
last_cycle();
rd.h = op_read(OPMODE_ADDR, aa.w + 1);
regs.pc.w = rd.w;
status.cycle_pos = 0;
break;
}
}
void bCPU::op_jmp_iaddrx() {
l1:
aa.l = op_read();
l2:
aa.h = op_read();
l3:
cpu_io();
l4:
rd.l = op_read(OPMODE_PBR, aa.w + regs.x.w);
l5:
rd.h = op_read(OPMODE_PBR, aa.w + regs.x.w + 1);
regs.pc.w = rd.w;
switch(status.cycle_pos++) {
case 1:
aa.l = op_read();
break;
case 2:
aa.h = op_read();
break;
case 3:
cpu_io();
break;
case 4:
rd.l = op_read(OPMODE_PBR, aa.w + regs.x.w);
break;
case 5:
last_cycle();
rd.h = op_read(OPMODE_PBR, aa.w + regs.x.w + 1);
regs.pc.w = rd.w;
status.cycle_pos = 0;
break;
}
}
void bCPU::op_jmp_iladdr() {
l1:
aa.l = op_read();
l2:
aa.h = op_read();
l3:
rd.l = op_read(OPMODE_ADDR, aa.w);
l4:
rd.h = op_read(OPMODE_ADDR, aa.w + 1);
l5:
rd.b = op_read(OPMODE_ADDR, aa.w + 2);
regs.pc.d = rd.d & 0xffffff;
switch(status.cycle_pos++) {
case 1:
aa.l = op_read();
break;
case 2:
aa.h = op_read();
break;
case 3:
rd.l = op_read(OPMODE_ADDR, aa.w);
break;
case 4:
rd.h = op_read(OPMODE_ADDR, aa.w + 1);
break;
case 5:
last_cycle();
rd.b = op_read(OPMODE_ADDR, aa.w + 2);
regs.pc.d = rd.d & 0xffffff;
status.cycle_pos = 0;
break;
}
}
void bCPU::op_jsr_addr() {
l1:
aa.l = op_read();
l2:
aa.h = op_read();
l3:
cpu_io();
l4:
regs.pc.w--;
stack_write(regs.pc.h);
l5:
stack_write(regs.pc.l);
regs.pc.w = aa.w;
switch(status.cycle_pos++) {
case 1:
aa.l = op_read();
break;
case 2:
aa.h = op_read();
break;
case 3:
cpu_io();
break;
case 4:
regs.pc.w--;
stack_write(regs.pc.h);
break;
case 5:
last_cycle();
stack_write(regs.pc.l);
regs.pc.w = aa.w;
status.cycle_pos = 0;
break;
}
}
void bCPU::op_jsr_long() {
l1:
aa.l = op_read();
l2:
aa.h = op_read();
l3:
stack_write(regs.pc.b);
l4:
cpu_io();
l5:
aa.b = op_read();
l6:
regs.pc.w--;
stack_write(regs.pc.h);
l7:
stack_write(regs.pc.l);
regs.pc.d = aa.d & 0xffffff;
switch(status.cycle_pos++) {
case 1:
aa.l = op_read();
break;
case 2:
aa.h = op_read();
break;
case 3:
stack_write(regs.pc.b);
break;
case 4:
cpu_io();
break;
case 5:
aa.b = op_read();
break;
case 6:
regs.pc.w--;
stack_write(regs.pc.h);
break;
case 7:
last_cycle();
stack_write(regs.pc.l);
regs.pc.d = aa.d & 0xffffff;
status.cycle_pos = 0;
break;
}
}
void bCPU::op_jsr_iaddrx() {
l1:
aa.l = op_read();
l2:
stack_write(regs.pc.h);
l3:
stack_write(regs.pc.l);
l4:
aa.h = op_read();
l5:
cpu_io();
l6:
rd.l = op_read(OPMODE_PBR, aa.w + regs.x.w);
l7:
rd.h = op_read(OPMODE_PBR, aa.w + regs.x.w + 1);
regs.pc.w = rd.w;
switch(status.cycle_pos++) {
case 1:
aa.l = op_read();
break;
case 2:
stack_write(regs.pc.h);
break;
case 3:
stack_write(regs.pc.l);
break;
case 4:
aa.h = op_read();
break;
case 5:
cpu_io();
break;
case 6:
rd.l = op_read(OPMODE_PBR, aa.w + regs.x.w);
break;
case 7:
last_cycle();
rd.h = op_read(OPMODE_PBR, aa.w + regs.x.w + 1);
regs.pc.w = rd.w;
status.cycle_pos = 0;
break;
}
}
void bCPU::op_rti() {
l1:
cpu_io();
l2:
cpu_io();
l3:
regs.p = stack_read();
if(regs.e)regs.p |= 0x30;
if(regs.p.x) {
regs.x.h = 0x00;
regs.y.h = 0x00;
switch(status.cycle_pos++) {
case 1:
cpu_io();
break;
case 2:
cpu_io();
break;
case 3:
regs.p = stack_read();
if(regs.e)regs.p |= 0x30;
if(regs.p.x) {
regs.x.h = 0x00;
regs.y.h = 0x00;
}
break;
case 4:
rd.l = stack_read();
break;
case 5:
rd.h = stack_read();
if(regs.e) {
regs.pc.w = rd.w;
status.cycle_pos = 0;
}
break;
case 6:
last_cycle();
rd.b = stack_read();
regs.pc.d = rd.d & 0xffffff;
status.cycle_pos = 0;
break;
}
l4:
rd.l = stack_read();
l5:
rd.h = stack_read();
if(regs.e) {
regs.pc.w = rd.w;
return;
}
l6:
rd.b = stack_read();
regs.pc.d = rd.d & 0xffffff;
}
void bCPU::op_rts() {
l1:
cpu_io();
l2:
cpu_io();
l3:
rd.l = stack_read();
l4:
rd.h = stack_read();
l5:
cpu_io();
regs.pc.w = rd.w;
regs.pc.w++;
switch(status.cycle_pos++) {
case 1:
cpu_io();
break;
case 2:
cpu_io();
break;
case 3:
rd.l = stack_read();
break;
case 4:
rd.h = stack_read();
break;
case 5:
last_cycle();
cpu_io();
regs.pc.w = rd.w;
regs.pc.w++;
status.cycle_pos = 0;
break;
}
}
void bCPU::op_rtl() {
l1:
cpu_io();
l2:
cpu_io();
l3:
rd.l = stack_read();
l4:
rd.h = stack_read();
l5:
rd.b = stack_read();
regs.pc.d = rd.d & 0xffffff;
regs.pc.w++;
switch(status.cycle_pos++) {
case 1:
cpu_io();
break;
case 2:
cpu_io();
break;
case 3:
rd.l = stack_read();
break;
case 4:
rd.h = stack_read();
break;
case 5:
last_cycle();
rd.b = stack_read();
regs.pc.d = rd.d & 0xffffff;
regs.pc.w++;
status.cycle_pos = 0;
break;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,340 +1,582 @@
void bCPU::op_sta_addr() {
l1:
aa.l = op_read();
l2:
aa.h = op_read();
l3:
op_write(OPMODE_DBR, aa.w, regs.a.w);
if(regs.p.m)return;
l4:
op_write(OPMODE_DBR, aa.w + 1, regs.a.w >> 8);
switch(status.cycle_pos++) {
case 1:
aa.l = op_read();
break;
case 2:
aa.h = op_read();
break;
case 3:
if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w, regs.a.w);
if(regs.p.m)status.cycle_pos = 0;
break;
case 4:
last_cycle();
op_write(OPMODE_DBR, aa.w + 1, regs.a.w >> 8);
status.cycle_pos = 0;
break;
}
}
void bCPU::op_stx_addr() {
l1:
aa.l = op_read();
l2:
aa.h = op_read();
l3:
op_write(OPMODE_DBR, aa.w, regs.x.w);
if(regs.p.x)return;
l4:
op_write(OPMODE_DBR, aa.w + 1, regs.x.w >> 8);
switch(status.cycle_pos++) {
case 1:
aa.l = op_read();
break;
case 2:
aa.h = op_read();
break;
case 3:
if(regs.p.x)last_cycle();
op_write(OPMODE_DBR, aa.w, regs.x.w);
if(regs.p.x)status.cycle_pos = 0;
break;
case 4:
last_cycle();
op_write(OPMODE_DBR, aa.w + 1, regs.x.w >> 8);
status.cycle_pos = 0;
break;
}
}
void bCPU::op_sty_addr() {
l1:
aa.l = op_read();
l2:
aa.h = op_read();
l3:
op_write(OPMODE_DBR, aa.w, regs.y.w);
if(regs.p.x)return;
l4:
op_write(OPMODE_DBR, aa.w + 1, regs.y.w >> 8);
switch(status.cycle_pos++) {
case 1:
aa.l = op_read();
break;
case 2:
aa.h = op_read();
break;
case 3:
if(regs.p.x)last_cycle();
op_write(OPMODE_DBR, aa.w, regs.y.w);
if(regs.p.x)status.cycle_pos = 0;
break;
case 4:
last_cycle();
op_write(OPMODE_DBR, aa.w + 1, regs.y.w >> 8);
status.cycle_pos = 0;
break;
}
}
void bCPU::op_stz_addr() {
l1:
aa.l = op_read();
l2:
aa.h = op_read();
l3:
op_write(OPMODE_DBR, aa.w, 0x0000);
if(regs.p.m)return;
l4:
op_write(OPMODE_DBR, aa.w + 1, 0x0000 >> 8);
switch(status.cycle_pos++) {
case 1:
aa.l = op_read();
break;
case 2:
aa.h = op_read();
break;
case 3:
if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w, 0x0000);
if(regs.p.m)status.cycle_pos = 0;
break;
case 4:
last_cycle();
op_write(OPMODE_DBR, aa.w + 1, 0x0000 >> 8);
status.cycle_pos = 0;
break;
}
}
void bCPU::op_sta_addrx() {
l1:
aa.l = op_read();
l2:
aa.h = op_read();
l3:
cpu_c4(aa.w, aa.w + regs.x.w);
l4:
op_write(OPMODE_DBR, aa.w + regs.x.w, regs.a.w);
if(regs.p.m)return;
l5:
op_write(OPMODE_DBR, aa.w + regs.x.w + 1, regs.a.w >> 8);
switch(status.cycle_pos++) {
case 1:
aa.l = op_read();
break;
case 2:
aa.h = op_read();
break;
case 3:
cpu_c4(aa.w, aa.w + regs.x.w);
break;
case 4:
if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w + regs.x.w, regs.a.w);
if(regs.p.m)status.cycle_pos = 0;
break;
case 5:
last_cycle();
op_write(OPMODE_DBR, aa.w + regs.x.w + 1, regs.a.w >> 8);
status.cycle_pos = 0;
break;
}
}
void bCPU::op_stz_addrx() {
l1:
aa.l = op_read();
l2:
aa.h = op_read();
l3:
cpu_c4(aa.w, aa.w + regs.x.w);
l4:
op_write(OPMODE_DBR, aa.w + regs.x.w, 0x0000);
if(regs.p.m)return;
l5:
op_write(OPMODE_DBR, aa.w + regs.x.w + 1, 0x0000 >> 8);
switch(status.cycle_pos++) {
case 1:
aa.l = op_read();
break;
case 2:
aa.h = op_read();
break;
case 3:
cpu_c4(aa.w, aa.w + regs.x.w);
break;
case 4:
if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w + regs.x.w, 0x0000);
if(regs.p.m)status.cycle_pos = 0;
break;
case 5:
last_cycle();
op_write(OPMODE_DBR, aa.w + regs.x.w + 1, 0x0000 >> 8);
status.cycle_pos = 0;
break;
}
}
void bCPU::op_sta_addry() {
l1:
aa.l = op_read();
l2:
aa.h = op_read();
l3:
cpu_c4(aa.w, aa.w + regs.y.w);
l4:
op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l);
if(regs.p.m)return;
l5:
op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h);
switch(status.cycle_pos++) {
case 1:
aa.l = op_read();
break;
case 2:
aa.h = op_read();
break;
case 3:
cpu_c4(aa.w, aa.w + regs.y.w);
break;
case 4:
if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l);
if(regs.p.m)status.cycle_pos = 0;
break;
case 5:
last_cycle();
op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h);
status.cycle_pos = 0;
break;
}
}
void bCPU::op_sta_long() {
l1:
aa.l = op_read();
l2:
aa.h = op_read();
l3:
aa.b = op_read();
l4:
op_write(OPMODE_LONG, aa.d, regs.a.l);
if(regs.p.m)return;
l5:
op_write(OPMODE_LONG, aa.d + 1, regs.a.h);
switch(status.cycle_pos++) {
case 1:
aa.l = op_read();
break;
case 2:
aa.h = op_read();
break;
case 3:
aa.b = op_read();
break;
case 4:
if(regs.p.m)last_cycle();
op_write(OPMODE_LONG, aa.d, regs.a.l);
if(regs.p.m)status.cycle_pos = 0;
break;
case 5:
last_cycle();
op_write(OPMODE_LONG, aa.d + 1, regs.a.h);
status.cycle_pos = 0;
break;
}
}
void bCPU::op_sta_longx() {
l1:
aa.l = op_read();
l2:
aa.h = op_read();
l3:
aa.b = op_read();
l4:
op_write(OPMODE_LONG, aa.d + regs.x.w, regs.a.l);
if(regs.p.m)return;
l5:
op_write(OPMODE_LONG, aa.d + regs.x.w + 1, regs.a.h);
switch(status.cycle_pos++) {
case 1:
aa.l = op_read();
break;
case 2:
aa.h = op_read();
break;
case 3:
aa.b = op_read();
break;
case 4:
if(regs.p.m)last_cycle();
op_write(OPMODE_LONG, aa.d + regs.x.w, regs.a.l);
if(regs.p.m)status.cycle_pos = 0;
break;
case 5:
last_cycle();
op_write(OPMODE_LONG, aa.d + regs.x.w + 1, regs.a.h);
status.cycle_pos = 0;
break;
}
}
void bCPU::op_sta_dp() {
l1:
dp = op_read();
l2:
cpu_c2();
l3:
op_write(OPMODE_DP, dp, regs.a.w);
if(regs.p.m)return;
l4:
op_write(OPMODE_DP, dp + 1, regs.a.w >> 8);
switch(status.cycle_pos++) {
case 1:
dp = op_read();
break;
case 2:
cpu_c2();
break;
case 3:
if(regs.p.m)last_cycle();
op_write(OPMODE_DP, dp, regs.a.w);
if(regs.p.m)status.cycle_pos = 0;
break;
case 4:
last_cycle();
op_write(OPMODE_DP, dp + 1, regs.a.w >> 8);
status.cycle_pos = 0;
break;
}
}
void bCPU::op_stx_dp() {
l1:
dp = op_read();
l2:
cpu_c2();
l3:
op_write(OPMODE_DP, dp, regs.x.w);
if(regs.p.x)return;
l4:
op_write(OPMODE_DP, dp + 1, regs.x.w >> 8);
switch(status.cycle_pos++) {
case 1:
dp = op_read();
break;
case 2:
cpu_c2();
break;
case 3:
if(regs.p.x)last_cycle();
op_write(OPMODE_DP, dp, regs.x.w);
if(regs.p.x)status.cycle_pos = 0;
break;
case 4:
last_cycle();
op_write(OPMODE_DP, dp + 1, regs.x.w >> 8);
status.cycle_pos = 0;
break;
}
}
void bCPU::op_sty_dp() {
l1:
dp = op_read();
l2:
cpu_c2();
l3:
op_write(OPMODE_DP, dp, regs.y.w);
if(regs.p.x)return;
l4:
op_write(OPMODE_DP, dp + 1, regs.y.w >> 8);
switch(status.cycle_pos++) {
case 1:
dp = op_read();
break;
case 2:
cpu_c2();
break;
case 3:
if(regs.p.x)last_cycle();
op_write(OPMODE_DP, dp, regs.y.w);
if(regs.p.x)status.cycle_pos = 0;
break;
case 4:
last_cycle();
op_write(OPMODE_DP, dp + 1, regs.y.w >> 8);
status.cycle_pos = 0;
break;
}
}
void bCPU::op_stz_dp() {
l1:
dp = op_read();
l2:
cpu_c2();
l3:
op_write(OPMODE_DP, dp, 0x0000);
if(regs.p.m)return;
l4:
op_write(OPMODE_DP, dp + 1, 0x0000 >> 8);
switch(status.cycle_pos++) {
case 1:
dp = op_read();
break;
case 2:
cpu_c2();
break;
case 3:
if(regs.p.m)last_cycle();
op_write(OPMODE_DP, dp, 0x0000);
if(regs.p.m)status.cycle_pos = 0;
break;
case 4:
last_cycle();
op_write(OPMODE_DP, dp + 1, 0x0000 >> 8);
status.cycle_pos = 0;
break;
}
}
void bCPU::op_sta_dpx() {
l1:
dp = op_read();
l2:
cpu_c2();
l3:
cpu_io();
l4:
op_write(OPMODE_DP, dp + regs.x.w, regs.a.w);
if(regs.p.m)return;
l5:
op_write(OPMODE_DP, dp + regs.x.w + 1, regs.a.w >> 8);
switch(status.cycle_pos++) {
case 1:
dp = op_read();
break;
case 2:
cpu_c2();
break;
case 3:
cpu_io();
break;
case 4:
if(regs.p.m)last_cycle();
op_write(OPMODE_DP, dp + regs.x.w, regs.a.w);
if(regs.p.m)status.cycle_pos = 0;
break;
case 5:
last_cycle();
op_write(OPMODE_DP, dp + regs.x.w + 1, regs.a.w >> 8);
status.cycle_pos = 0;
break;
}
}
void bCPU::op_sty_dpx() {
l1:
dp = op_read();
l2:
cpu_c2();
l3:
cpu_io();
l4:
op_write(OPMODE_DP, dp + regs.x.w, regs.y.w);
if(regs.p.x)return;
l5:
op_write(OPMODE_DP, dp + regs.x.w + 1, regs.y.w >> 8);
switch(status.cycle_pos++) {
case 1:
dp = op_read();
break;
case 2:
cpu_c2();
break;
case 3:
cpu_io();
break;
case 4:
if(regs.p.x)last_cycle();
op_write(OPMODE_DP, dp + regs.x.w, regs.y.w);
if(regs.p.x)status.cycle_pos = 0;
break;
case 5:
last_cycle();
op_write(OPMODE_DP, dp + regs.x.w + 1, regs.y.w >> 8);
status.cycle_pos = 0;
break;
}
}
void bCPU::op_stz_dpx() {
l1:
dp = op_read();
l2:
cpu_c2();
l3:
cpu_io();
l4:
op_write(OPMODE_DP, dp + regs.x.w, 0x0000);
if(regs.p.m)return;
l5:
op_write(OPMODE_DP, dp + regs.x.w + 1, 0x0000 >> 8);
switch(status.cycle_pos++) {
case 1:
dp = op_read();
break;
case 2:
cpu_c2();
break;
case 3:
cpu_io();
break;
case 4:
if(regs.p.m)last_cycle();
op_write(OPMODE_DP, dp + regs.x.w, 0x0000);
if(regs.p.m)status.cycle_pos = 0;
break;
case 5:
last_cycle();
op_write(OPMODE_DP, dp + regs.x.w + 1, 0x0000 >> 8);
status.cycle_pos = 0;
break;
}
}
void bCPU::op_stx_dpy() {
l1:
dp = op_read();
l2:
cpu_c2();
l3:
cpu_io();
l4:
op_write(OPMODE_DP, dp + regs.y.w, regs.x.l);
if(regs.p.x)return;
l5:
op_write(OPMODE_DP, dp + regs.y.w + 1, regs.x.h);
switch(status.cycle_pos++) {
case 1:
dp = op_read();
break;
case 2:
cpu_c2();
break;
case 3:
cpu_io();
break;
case 4:
if(regs.p.x)last_cycle();
op_write(OPMODE_DP, dp + regs.y.w, regs.x.l);
if(regs.p.x)status.cycle_pos = 0;
break;
case 5:
last_cycle();
op_write(OPMODE_DP, dp + regs.y.w + 1, regs.x.h);
status.cycle_pos = 0;
break;
}
}
void bCPU::op_sta_idp() {
l1:
dp = op_read();
l2:
cpu_c2();
l3:
aa.l = op_read(OPMODE_DP, dp);
l4:
aa.h = op_read(OPMODE_DP, dp + 1);
l5:
op_write(OPMODE_DBR, aa.w, regs.a.l);
if(regs.p.m)return;
l6:
op_write(OPMODE_DBR, aa.w + 1, regs.a.h);
switch(status.cycle_pos++) {
case 1:
dp = op_read();
break;
case 2:
cpu_c2();
break;
case 3:
aa.l = op_read(OPMODE_DP, dp);
break;
case 4:
aa.h = op_read(OPMODE_DP, dp + 1);
break;
case 5:
if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w, regs.a.l);
if(regs.p.m)status.cycle_pos = 0;
break;
case 6:
last_cycle();
op_write(OPMODE_DBR, aa.w + 1, regs.a.h);
status.cycle_pos = 0;
break;
}
}
void bCPU::op_sta_ildp() {
l1:
dp = op_read();
l2:
cpu_c2();
l3:
aa.l = op_read(OPMODE_DP, dp);
l4:
aa.h = op_read(OPMODE_DP, dp + 1);
l5:
aa.b = op_read(OPMODE_DP, dp + 2);
l6:
op_write(OPMODE_LONG, aa.d, regs.a.l);
if(regs.p.m)return;
l7:
op_write(OPMODE_LONG, aa.d + 1, regs.a.h);
switch(status.cycle_pos++) {
case 1:
dp = op_read();
break;
case 2:
cpu_c2();
break;
case 3:
aa.l = op_read(OPMODE_DP, dp);
break;
case 4:
aa.h = op_read(OPMODE_DP, dp + 1);
break;
case 5:
aa.b = op_read(OPMODE_DP, dp + 2);
break;
case 6:
if(regs.p.m)last_cycle();
op_write(OPMODE_LONG, aa.d, regs.a.l);
if(regs.p.m)status.cycle_pos = 0;
break;
case 7:
last_cycle();
op_write(OPMODE_LONG, aa.d + 1, regs.a.h);
status.cycle_pos = 0;
break;
}
}
void bCPU::op_sta_idpx() {
l1:
dp = op_read();
l2:
cpu_c2();
l3:
cpu_io();
l4:
aa.l = op_read(OPMODE_DP, dp + regs.x.w);
l5:
aa.h = op_read(OPMODE_DP, dp + regs.x.w + 1);
l6:
op_write(OPMODE_DBR, aa.w, regs.a.l);
if(regs.p.m)return;
l7:
op_write(OPMODE_DBR, aa.w + 1, regs.a.h);
switch(status.cycle_pos++) {
case 1:
dp = op_read();
break;
case 2:
cpu_c2();
break;
case 3:
cpu_io();
break;
case 4:
aa.l = op_read(OPMODE_DP, dp + regs.x.w);
break;
case 5:
aa.h = op_read(OPMODE_DP, dp + regs.x.w + 1);
break;
case 6:
if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w, regs.a.l);
if(regs.p.m)status.cycle_pos = 0;
break;
case 7:
last_cycle();
op_write(OPMODE_DBR, aa.w + 1, regs.a.h);
status.cycle_pos = 0;
break;
}
}
void bCPU::op_sta_idpy() {
l1:
dp = op_read();
l2:
cpu_c2();
l3:
aa.l = op_read(OPMODE_DP, dp);
l4:
aa.h = op_read(OPMODE_DP, dp + 1);
l5:
cpu_c4(aa.w, aa.w + regs.y.w);
l6:
op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l);
if(regs.p.m)return;
l7:
op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h);
switch(status.cycle_pos++) {
case 1:
dp = op_read();
break;
case 2:
cpu_c2();
break;
case 3:
aa.l = op_read(OPMODE_DP, dp);
break;
case 4:
aa.h = op_read(OPMODE_DP, dp + 1);
break;
case 5:
cpu_c4(aa.w, aa.w + regs.y.w);
break;
case 6:
if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l);
if(regs.p.m)status.cycle_pos = 0;
break;
case 7:
last_cycle();
op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h);
status.cycle_pos = 0;
break;
}
}
void bCPU::op_sta_ildpy() {
l1:
dp = op_read();
l2:
cpu_c2();
l3:
aa.l = op_read(OPMODE_DP, dp);
l4:
aa.h = op_read(OPMODE_DP, dp + 1);
l5:
aa.b = op_read(OPMODE_DP, dp + 2);
l6:
op_write(OPMODE_LONG, aa.d + regs.y.w, regs.a.l);
if(regs.p.m)return;
l7:
op_write(OPMODE_LONG, aa.d + regs.y.w + 1, regs.a.h);
switch(status.cycle_pos++) {
case 1:
dp = op_read();
break;
case 2:
cpu_c2();
break;
case 3:
aa.l = op_read(OPMODE_DP, dp);
break;
case 4:
aa.h = op_read(OPMODE_DP, dp + 1);
break;
case 5:
aa.b = op_read(OPMODE_DP, dp + 2);
break;
case 6:
if(regs.p.m)last_cycle();
op_write(OPMODE_LONG, aa.d + regs.y.w, regs.a.l);
if(regs.p.m)status.cycle_pos = 0;
break;
case 7:
last_cycle();
op_write(OPMODE_LONG, aa.d + regs.y.w + 1, regs.a.h);
status.cycle_pos = 0;
break;
}
}
void bCPU::op_sta_sr() {
l1:
sp = op_read();
l2:
cpu_io();
l3:
op_write(OPMODE_SP, sp, regs.a.l);
if(regs.p.m)return;
l4:
op_write(OPMODE_SP, sp + 1, regs.a.h);
switch(status.cycle_pos++) {
case 1:
sp = op_read();
break;
case 2:
cpu_io();
break;
case 3:
if(regs.p.m)last_cycle();
op_write(OPMODE_SP, sp, regs.a.l);
if(regs.p.m)status.cycle_pos = 0;
break;
case 4:
last_cycle();
op_write(OPMODE_SP, sp + 1, regs.a.h);
status.cycle_pos = 0;
break;
}
}
void bCPU::op_sta_isry() {
l1:
sp = op_read();
l2:
cpu_io();
l3:
aa.l = op_read(OPMODE_SP, sp);
l4:
aa.h = op_read(OPMODE_SP, sp + 1);
l5:
cpu_io();
l6:
op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l);
if(regs.p.m)return;
l7:
op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h);
switch(status.cycle_pos++) {
case 1:
sp = op_read();
break;
case 2:
cpu_io();
break;
case 3:
aa.l = op_read(OPMODE_SP, sp);
break;
case 4:
aa.h = op_read(OPMODE_SP, sp + 1);
break;
case 5:
cpu_io();
break;
case 6:
if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l);
if(regs.p.m)status.cycle_pos = 0;
break;
case 7:
last_cycle();
op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h);
status.cycle_pos = 0;
break;
}
}

View File

@@ -186,7 +186,6 @@ optbl[0x91] = &bCPU::op_sta_idpy;
optbl[0x97] = &bCPU::op_sta_ildpy;
optbl[0x83] = &bCPU::op_sta_sr;
optbl[0x93] = &bCPU::op_sta_isry;
optbl[0x80] = &bCPU::op_bra;
optbl[0x90] = &bCPU::op_bcc;
optbl[0xb0] = &bCPU::op_bcs;
optbl[0xd0] = &bCPU::op_bne;
@@ -195,6 +194,7 @@ optbl[0x10] = &bCPU::op_bpl;
optbl[0x30] = &bCPU::op_bmi;
optbl[0x50] = &bCPU::op_bvc;
optbl[0x70] = &bCPU::op_bvs;
optbl[0x80] = &bCPU::op_bra;
optbl[0x82] = &bCPU::op_brl;
optbl[0x4c] = &bCPU::op_jmp_addr;
optbl[0x5c] = &bCPU::op_jmp_long;

View File

@@ -1,21 +1,194 @@
uint16 bCPU::vcounter() { return time.v; }
uint16 bCPU::hcounter() { return get_hcounter(); }
uint16 bCPU::hcycles() { return time.hc; }
bool bCPU::interlace() { return time.interlace; }
bool bCPU::interlace_field() { return time.interlace_field; }
bool bCPU::overscan() { return time.overscan; }
/* Notes about PAL timing:
* As I do not have PAL hardware to run timing tests on, I've
* had to guess on a lot of things. Below is how I've arrived
* at various calculations:
*
* NTSC timing crystal: ~21477272hz
* PAL timing crystal: ~21281370hz
* NTSC ~60fps, PAL ~50fps
* NTSC ~262 lines/frame, PAL ~312 lines/frame
* NTSC 21477272 / (262 * 60) = ~1366 cycles/line
* PAL 21281370 / (312 * 50) = ~1364 cycles/line
*
* As the cycles/line are very close between the two systems,
* I have left the known NTSC anomalies intact for PAL timing.
* In reality, some of these may not exist, and some may be
* slightly different.
*
* [known]
* - DRAM refresh occurs at about the same time every
* scanline on PAL units (per Overload).
* [unknown]
* - Are dots 323/327 still 2 cycles longer than the
* other dots?
* - Is scanline 240 on non-interlace odd frames still
* 4 cycles short?
*/
void bCPU::set_interlace(bool r) { time.interlace = r; }
void bCPU::set_overscan(bool r) { time.overscan = r; }
uint16 bCPU::vcounter() { return time.v; }
uint16 bCPU::hcounter() { return get_hcounter(); }
uint16 bCPU::hcycles() { return time.hc; }
uint8 bCPU::dma_counter() { return (time.dma_counter + time.hc) & 6; }
bool bCPU::interlace() { return time.interlace; }
bool bCPU::interlace_field() { return time.interlace_field; }
bool bCPU::overscan() { return time.overscan; }
uint16 bCPU::region_scanlines() { return time.region_scanlines; }
void bCPU::set_interlace(bool r) { time.interlace = r; update_interrupts(); }
void bCPU::set_overscan (bool r) { time.overscan = r; update_interrupts(); }
uint8 bCPU::dma_counter() { return (time.dma_counter + time.hc) & 6; }
bool bCPU::nmi_trigger_pos_match(uint32 offset) {
uint16 v = overscan() ? 240 : 225;
uint16 hc = 2 + offset;
return (time.v == v && time.hc == hc);
}
bool bCPU::irq_trigger_pos_match(uint32 offset) {
uint16 v = status.virq_pos;
uint16 hc = (status.hirq_enabled) ? status.hirq_pos : 0;
//positions that can never be latched
//region_scanlines() = 262/NTSC, 312/PAL
//PAL results are unverified on hardware
if(v == 240 && hc == 339 && interlace() == false && interlace_field() == 1)return false;
if(v == (region_scanlines() - 1) && hc == 339 && interlace() == false)return false;
if(v == region_scanlines() && interlace() == false)return false;
if(v == region_scanlines() && hc == 339)return false;
if(v > region_scanlines())return false;
if(hc > 339)return false;
hc = (hc != 0) ? ((hc << 2) + 14) : 10;
hc += offset;
if(hc >= time.line_cycles) {
hc -= time.line_cycles;
if(++v >= time.frame_lines) {
v = 0;
}
}
if((status.virq_enabled == true && time.v == v) || status.virq_enabled == false) {
return (time.hc == hc);
}
return false;
}
void bCPU::update_nmi() {
if(time.v == (overscan() ? 240 : 225)) {
time.nmi_read_trigger_pos = 2;
time.nmi_line_trigger_pos = 6;
} else {
time.nmi_read_trigger_pos = -64;
time.nmi_line_trigger_pos = -64;
}
}
void bCPU::update_irq() {
int vpos = status.virq_pos;
int hpos = (status.hirq_enabled) ? status.hirq_pos : 0;
//positions that can never be latched
//region_scanlines() = 262/NTSC, 312/PAL
//PAL results are unverified on hardware
if(vpos == 240 && hpos == 339 && interlace() == false && interlace_field() == 1)goto _nolatch;
if(vpos == (region_scanlines() - 1) && hpos == 339 && interlace() == false)goto _nolatch;
if(vpos == region_scanlines() && interlace() == false)goto _nolatch;
if(vpos == region_scanlines() && hpos == 339)goto _nolatch;
if(vpos > region_scanlines())goto _nolatch;
if(hpos > 339)goto _nolatch;
hpos = (hpos != 0) ? ((hpos << 2) + 14) : 10;
if(hpos >= time.line_cycles) {
hpos -= time.line_cycles;
if(++vpos >= time.frame_lines) {
vpos = 0;
}
}
if((status.virq_enabled == true && time.v == vpos) || status.virq_enabled == false) {
time.irq_read_trigger_pos = hpos;
} else {
time.irq_read_trigger_pos = -64;
}
hpos += 4;
if(hpos >= time.line_cycles) {
hpos -= time.line_cycles;
if(++vpos >= time.frame_lines) {
vpos = 0;
}
}
if((status.virq_enabled == true && time.v == vpos) || status.virq_enabled == false) {
time.irq_line_trigger_pos = hpos;
} else {
time.irq_line_trigger_pos = -64;
}
return;
_nolatch:
time.irq_read_trigger_pos = -64;
time.irq_line_trigger_pos = -64;
}
void bCPU::update_interrupts() {
update_nmi();
update_irq();
}
void bCPU::poll_interrupts(int cycles) {
int16 hc, hc_end;
if(time.hc == 0) {
hc = -1;
hc_end = cycles;
} else {
hc = time.hc;
hc_end = time.hc + cycles;
}
if(hc < time.nmi_read_trigger_pos && time.nmi_read_trigger_pos <= hc_end) {
//nmi_read can go low even with NMI interrupts disabled in $4200.d7
time.nmi_read = 0;
}
if(hc < time.nmi_line_trigger_pos && time.nmi_line_trigger_pos <= hc_end) {
if(status.nmi_enabled == true) {
if(time.nmi_line == 1) {
time.nmi_transition = 1;
}
time.nmi_line = 0;
}
}
if(hc < time.irq_read_trigger_pos && time.irq_read_trigger_pos <= hc_end) {
if(status.virq_enabled == true || status.hirq_enabled == true) {
time.irq_read = 0;
}
}
if(hc < time.irq_line_trigger_pos && time.irq_line_trigger_pos <= hc_end) {
if(status.virq_enabled == true || status.hirq_enabled == true) {
time.irq_line = 0;
time.irq_transition = 1;
}
}
}
//all scanlines are 1364 cycles long, except scanline 240
//on non-interlace odd-frames, which is 1360 cycles long.
//[NTSC]
//interlace mode has 525 scanlines: 263 on the even frame,
//and 262 on the odd.
//non-interlace mode has 524 scanlines: 262 scanlines on
//both even and odd frames.
//[PAL] <PAL info is unverified on hardware>
//interlace mode has 625 scanlines: 313 on the even frame,
//and 312 on the odd.
//non-interlace mode has 624 scanlines: 312 scanlines on
//both even and odd frames.
//
//cycles per frame:
// 263 * 1364 = 358732
@@ -28,114 +201,156 @@ void bCPU::inc_vcounter() {
time.interlace_field ^= 1;
if(time.interlace == true && time.interlace_field == 0) {
time.frame_lines = 263;
time.frame_lines = time.region_scanlines + 1;
} else {
time.frame_lines = 262;
time.frame_lines = time.region_scanlines;
}
}
time.hc = 0;
time.dma_counter = time.line_cycles & 6;
time.dma_counter += time.line_cycles;
if(time.v == 240 && time.interlace == false && time.interlace_field == 1) {
time.line_cycles = 1360;
} else {
time.line_cycles = 1364;
}
time.dram_refreshed = false;
update_interrupts();
}
//all dots are 4 cycles long, except dots 322 and 326. dots 322 and 326
//all dots are 4 cycles long, except dots 323 and 327. dots 323 and 327
//are 6 cycles long. this holds true for all scanlines except scanline
//240 on non-interlace odd frames. the reason for this is because this
//scanline is only 1360 cycles long, instead of 1364 like all other
//scanlines.
//this makes the effective range of hscan_pos 0-339 at all times.
//dot 322 range = { 1288, 1290, 1292 }
//dot 326 range = { 1306, 1308, 1310 }
//dot 323 range = { 1292, 1294, 1296 }
//dot 327 range = { 1310, 1312, 1314 }
uint16 bCPU::get_hcounter() {
if(time.v == 240 && time.interlace == false && time.interlace_field == 1) {
return time.hc >> 2;
}
return (time.hc - ((time.hc > 1288) << 1) - ((time.hc > 1306) << 1)) >> 2;
return (time.hc - ((time.hc > 1292) << 1) - ((time.hc > 1310) << 1)) >> 2;
}
void bCPU::apu_sync() {
int cycles;
while(apusync.cycles >= 0) {
apu->run();
cycles = apu->cycles_executed();
if(cycles) {
apusync.cycles -= apusync.cpu_multbl[cycles];
}
}
}
void bCPU::dram_refresh() {
time.dram_refreshed = true;
add_cycles(40);
if(time.v != 240 || time.interlace != false || time.interlace_field != 1) {
//alternate between 534 and 538 every scanline except 240ni1
time.dram_refresh_pos ^= 12;
}
uint32 bCPU::cycles_executed() {
uint32 r = status.cycles_executed;
status.cycles_executed = 0;
return r;
}
void bCPU::add_cycles(int cycles) {
cycles >>= 1;
while(cycles--) {
apusync.cycles += apusync.apu_freq << 1;
apu_sync();
time.hc += 2;
status.cycles_executed += cycles;
if(time.hc >= time.line_cycles) {
inc_vcounter();
if(time.v == 0) {
frame();
ppu->frame();
}
scanline();
ppu->scanline();
poll_interrupts(cycles);
if(time.hc + cycles >= time.line_cycles) {
cycles = (time.hc + cycles) - time.line_cycles;
time.hc = 0;
inc_vcounter();
poll_interrupts(cycles);
if(time.v == 0) {
frame();
ppu->frame();
snes->frame();
}
if(time.dram_refreshed == false && time.hc >= time.dram_refresh_pos) {
dram_refresh();
}
scanline();
ppu->scanline();
snes->scanline();
time.line_rendered = false;
}
if(hdma_test() == true) {
hdma_run();
if(time.hdmainit_triggered == false) {
if(time.hc + cycles >= time.hdmainit_trigger_pos || time.v) {
time.hdmainit_triggered = true;
hdmainit_activate();
}
}
if(time.dram_refreshed == false) {
if(time.hc + cycles >= time.dram_refresh_pos) {
time.dram_refreshed = true;
status.cycles_executed += 40;
cycles = (time.hc + cycles) - time.dram_refresh_pos;
time.hc = time.dram_refresh_pos + 40;
if(cpu_version == 2) {
if(time.v != 240 || time.interlace != false || time.interlace_field != 1) {
if(time.dram_refresh_pos == 534) {
time.dram_refresh_pos = 538;
} else {
time.dram_refresh_pos = 534;
}
}
}
}
}
if(time.line_rendered == false) {
//rendering should start at H=18 (+256=274), but since the
//current PPU emulation renders the entire scanline at once,
//PPU register changes mid-scanline do not show up.
//therefore, wait a few dots before rendering the scanline
if(time.hc + cycles >= (48 * 4)) {
time.line_rendered = true;
ppu->render_scanline();
}
}
if(time.hdma_triggered == false) {
if(time.v <= (overscan() ? 239 : 224)) {
if(time.hc + cycles >= 1106) {
time.hdma_triggered = true;
hdma_activate();
}
}
}
time.hc += cycles;
}
void bCPU::time_reset() {
//initial latch values for $213c/$213d
//[x]0035 : [y]0000 (53.0 -> 212) [lda $2137]
//[x]0038 : [y]0000 (56.5 -> 226) [nop : lda $2137]
time.v = 0;
time.hc = 186;
time.hc = 0;
//upon SNES reset, start at scanline 0 non-interlace
time.interlace = false;
time.interlace_field = false;
time.overscan = false;
time.line_cycles = 1364;
time.frame_lines = 262;
time.dram_refreshed = false;
time.dram_refresh_pos = 538;
time.dram_refresh_pos = (cpu_version == 2) ? 538 : 530;
time.dma_counter = 0;
apusync.cycles = apusync.cpu_multbl[255];
}
//set at V=0,H=0
time.hdmainit_trigger_pos = 0;
time.hdmainit_triggered = true;
void bCPU::time_init() {
apusync.cpu_freq = 21477272 >> 3;
apusync.apu_freq = 24576000 >> 3;
time.hdma_triggered = false;
int i;
for(i=0;i<1024;i++) {
apusync.cpu_multbl[i] = i * apusync.cpu_freq;
apusync.apu_multbl[i] = i * apusync.apu_freq;
time.nmi_pending = false;
time.irq_pending = false;
time.nmi_line = time.nmi_read = 1;
time.irq_line = time.irq_read = 1;
time.nmi_transition = 0;
time.irq_transition = 0;
update_interrupts();
switch(region) {
case NTSC:
time.region_scanlines = 262;
break;
case PAL:
time.region_scanlines = 312;
break;
}
time.frame_lines = time.region_scanlines;
}

View File

@@ -4,11 +4,47 @@ struct {
bool interlace, interlace_field, overscan;
uint16 line_cycles, frame_lines;
bool line_rendered;
bool dram_refreshed;
uint16 dram_refresh_pos;
uint8 dma_counter;
}time;
uint16 hdmainit_trigger_pos;
bool hdmainit_triggered;
bool hdma_triggered;
uint16 region_scanlines;
//nmi_pending, irq_pending are used by last_cycle()
//nmi_line = /NMI, nmi_read = $4210.7
//irq_line = /IRQ, irq_read = $4211.7
bool nmi_pending, nmi_line, nmi_read;
bool irq_pending, irq_line, irq_read;
//NMI is edge-sensitive, meaning it triggers when /NMI
//transitions from high to low. This value is set to 1
//when that happens, and cleared after the nmi_test()
//routine acknowledges it and invokes the NMI interrupt
bool nmi_transition;
//IRQ is level-sensitive, so it does not need to keep
//track of transitions from high to low. IRQs will
//continue to fire as long as /IRQ stays low.
//However, if a write to $4200 forces IRQs high at the
//exact same clock cycle that /IRQ goes low, the /IRQ
//will still occur. Hence the need for this variable.
bool irq_transition;
//position is relative to time.hc, set at start of each scanline
//-64 means no trigger point on this scanline
//$4210/$4211 status bits get set before /NMI and /IRQ go low,
//hence the need for two variables for each.
int32 nmi_read_trigger_pos, nmi_line_trigger_pos;
int32 irq_read_trigger_pos, irq_line_trigger_pos;
} time;
inline uint16 vcounter();
inline uint16 hcounter();
@@ -16,6 +52,15 @@ inline uint16 hcycles();
inline bool interlace();
inline bool interlace_field();
inline bool overscan();
inline uint16 region_scanlines();
inline bool nmi_trigger_pos_match(uint32 offset);
inline bool irq_trigger_pos_match(uint32 offset);
inline void update_nmi();
inline void update_irq();
inline void update_interrupts();
inline void poll_interrupts(int cycles);
inline void set_interlace(bool r);
inline void set_overscan (bool r);
@@ -24,15 +69,5 @@ inline uint8 dma_counter();
inline void inc_vcounter();
inline uint16 get_hcounter();
inline void apu_sync();
inline void dram_refresh();
inline void add_cycles(int cycles);
inline void time_reset();
inline void time_init();
//APU synchronization
struct {
int32 cpu_freq, apu_freq;
int32 cpu_multbl[1024], apu_multbl[1024];
int32 cycles;
}apusync;

View File

@@ -44,7 +44,7 @@ char t[4096];
i++;
}
sprintf(output_op, "void bCPU::op_$$() {\r\n");
sprintf(output_op, "void bCPU::op_$$() {\r\n switch(status.cycle_pos++) {\r\n");
sprintf(output_header, "void op_$$();\r\n");
sprintf(output_table, "optbl[$0] = &bCPU::op_$$;\r\n");
@@ -53,9 +53,8 @@ char t[4096];
void update_line(int i, int n) {
char t[4096];
sprintf(t, "goto l%d;", n + 2);
replace(line[i], "end;", "return;");
replace(line[i], "skip;", t);
replace(line[i], "end;", "status.cycle_pos = 0;");
replace(line[i], "skip;", "status.cycle_pos++;");
}
void gen_op() {
@@ -67,12 +66,12 @@ char t[4096];
n = strdec(line[i]);
sprintf(t, "%d:", n);
strltrim(line[i], t);
sprintf(t, "l%d:\r\n", n);
sprintf(t, " case %d:\r\n", n);
strcat(output_op, t);
update_line(i, n);
if(strcmp(line[i], "")) {
strcat(output_op, " ");
strcat(output_op, " ");
strcat(output_op, line[i]);
strcat(output_op, "\r\n");
}
@@ -82,13 +81,20 @@ char t[4096];
if((strptr(line[i])[1]) == ':' || !strcmp(line[i], "}"))break;
update_line(i, n);
strcat(output_op, " ");
strcat(output_op, line[i]);
strcat(output_op, "\r\n");
i++;
}
if(!strcmp(line[i], "}")) {
strcat(output_op, " status.cycle_pos = 0;\r\n");
}
strcat(output_op, " break;\r\n");
}
strcat(output_op, "}");
strcat(output_op, " }\r\n}");
line_num = i + 1;
}
@@ -107,16 +113,16 @@ int i, l;
replace(t, "$5", op_list[i].arg[5]);
replace(t, "$6", op_list[i].arg[6]);
replace(t, "$7", op_list[i].arg[7]);
fprintf(fp, "%s\r\n\r\n", *t);
fprintf(fp, "%s\r\n\r\n", strptr(t));
strcpy(t, output_header);
replace(t, "$$", op_list[i].name);
fprintf(fph, "%s", *t);
fprintf(fph, "%s", strptr(t));
strcpy(t, output_table);
replace(t, "$$", op_list[i].name);
replace(t, "$0", op_list[i].arg[0]);
fprintf(fpt, "%s", *t);
fprintf(fpt, "%s", strptr(t));
}
}

Binary file not shown.

View File

@@ -1,15 +1,17 @@
nop(0xea) {
1:cpu_io();
1:last_cycle();
cpu_io();
}
wdm(0x42) {
1:cpu_io();
regs.pc.w++;
1:last_cycle();
op_read();
}
xba(0xeb) {
1:cpu_io();
2:cpu_io();
2:last_cycle();
cpu_io();
regs.a.l ^= regs.a.h;
regs.a.h ^= regs.a.l;
regs.a.l ^= regs.a.h;
@@ -27,7 +29,8 @@ mvp(0x44, --) {
5:cpu_io();
if(regs.p.x) { regs.x.l$1; regs.y.l$1; }
else { regs.x.w$1; regs.y.w$1; }
6:cpu_io();
6:last_cycle();
cpu_io();
if(regs.a.w--)regs.pc.w -= 3;
}
@@ -40,7 +43,8 @@ cop(0x02, 0xfff4, 0xfff5, 0xffe4, 0xffe5) {
4:stack_write(regs.pc.l);
5:stack_write(regs.p);
6:rd.l = op_read(OPMODE_LONG, (regs.e)?$1:$3);
7:rd.h = op_read(OPMODE_LONG, (regs.e)?$2:$4);
7:last_cycle();
rd.h = op_read(OPMODE_LONG, (regs.e)?$2:$4);
regs.pc.b = 0x00;
regs.pc.w = rd.w;
regs.p.i = 1;
@@ -49,20 +53,26 @@ cop(0x02, 0xfff4, 0xfff5, 0xffe4, 0xffe5) {
stp(0xdb) {
1:cpu_io();
status.cpu_state = CPUSTATE_STP;
2:cpu_io();
run_state.stp = true;
2:last_cycle();
cpu_io();
regs.pc.w--;
}
wai(0xcb) {
1:cpu_io();
status.cpu_state = CPUSTATE_WAI;
2:cpu_io();
regs.pc.w--;
run_state.wai = true;
2:last_cycle();
cpu_io();
if(run_state.wai) {
//this can be cleared within last_cycle()
regs.pc.w--;
}
}
xce(0xfb) {
1:cpu_io();
1:last_cycle();
cpu_io();
bool c = regs.p.c;
regs.p.c = regs.e;
regs.e = c;
@@ -81,14 +91,16 @@ clv(0xb8, regs.p.v = 0),
sec(0x38, regs.p.c = 1),
sed(0xf8, regs.p.d = 1),
sei(0x78, regs.p.i = 1) {
1:cpu_io();
1:last_cycle();
cpu_io();
$1;
}
rep(0xc2, &=~),
sep(0xe2, |=) {
1:rd.l = op_read();
2:cpu_io();
2:last_cycle();
cpu_io();
regs.p $1 rd.l;
if(regs.e)regs.p |= 0x30;
if(regs.p.x) {
@@ -103,7 +115,8 @@ txa(0x8a, regs.p.m, a, x),
txy(0x9b, regs.p.x, y, x),
tya(0x98, regs.p.m, a, y),
tyx(0xbb, regs.p.x, x, y) {
1:cpu_io();
1:last_cycle();
cpu_io();
if($1) {
regs.$2.l = regs.$3.l;
regs.p.n = !!(regs.$2.l & 0x80);
@@ -116,27 +129,31 @@ tyx(0xbb, regs.p.x, x, y) {
}
tcd(0x5b) {
1:cpu_io();
1:last_cycle();
cpu_io();
regs.d.w = regs.a.w;
regs.p.n = !!(regs.d.w & 0x8000);
regs.p.z = (regs.d.w == 0);
}
tcs(0x1b) {
1:cpu_io();
1:last_cycle();
cpu_io();
regs.s.w = regs.a.w;
if(regs.e)regs.s.h = 0x01;
}
tdc(0x7b) {
1:cpu_io();
1:last_cycle();
cpu_io();
regs.a.w = regs.d.w;
regs.p.n = !!(regs.a.w & 0x8000);
regs.p.z = (regs.a.w == 0);
}
tsc(0x3b) {
1:cpu_io();
1:last_cycle();
cpu_io();
regs.a.w = regs.s.w;
if(regs.e) {
regs.p.n = !!(regs.a.l & 0x80);
@@ -148,7 +165,8 @@ tsc(0x3b) {
}
tsx(0xba) {
1:cpu_io();
1:last_cycle();
cpu_io();
if(regs.p.x) {
regs.x.l = regs.s.l;
regs.p.n = !!(regs.x.l & 0x80);
@@ -161,7 +179,8 @@ tsx(0xba) {
}
txs(0x9a) {
1:cpu_io();
1:last_cycle();
cpu_io();
if(regs.e) {
regs.s.l = regs.x.l;
regs.p.n = !!(regs.s.l & 0x80);
@@ -180,14 +199,16 @@ phd(0x0b, 0, d) {
1:cpu_io();
if($1)skip;
2:stack_write(regs.$2.h);
3:stack_write(regs.$2.l);
3:last_cycle();
stack_write(regs.$2.l);
}
phb(0x8b, regs.db),
phk(0x4b, regs.pc.b),
php(0x08, regs.p) {
1:cpu_io();
2:stack_write($1);
2:last_cycle();
stack_write($1);
}
pla(0x68, regs.p.m, a),
@@ -196,13 +217,15 @@ ply(0x7a, regs.p.x, y),
pld(0x2b, 0, d) {
1:cpu_io();
2:cpu_io();
3:regs.$2.l = stack_read();
3:if($1)last_cycle();
regs.$2.l = stack_read();
if($1) {
regs.p.n = !!(regs.$2.l & 0x80);
regs.p.z = (regs.$2.l == 0);
end;
}
4:regs.$2.h = stack_read();
4:last_cycle();
regs.$2.h = stack_read();
regs.p.n = !!(regs.$2.w & 0x8000);
regs.p.z = (regs.$2.w == 0);
}
@@ -210,7 +233,8 @@ pld(0x2b, 0, d) {
plb(0xab) {
1:cpu_io();
2:cpu_io();
3:regs.db = stack_read();
3:last_cycle();
regs.db = stack_read();
regs.p.n = !!(regs.db & 0x80);
regs.p.z = (regs.db == 0);
}
@@ -218,7 +242,8 @@ plb(0xab) {
plp(0x28) {
1:cpu_io();
2:cpu_io();
3:regs.p = stack_read();
3:last_cycle();
regs.p = stack_read();
if(regs.e)regs.p |= 0x30;
if(regs.p.x) {
regs.x.h = 0x00;
@@ -230,7 +255,8 @@ pea(0xf4) {
1:aa.l = op_read();
2:aa.h = op_read();
3:stack_write(aa.h);
4:stack_write(aa.l);
4:last_cycle();
stack_write(aa.l);
}
pei(0xd4) {
@@ -239,7 +265,8 @@ pei(0xd4) {
3:aa.l = op_read(OPMODE_DP, dp);
4:aa.h = op_read(OPMODE_DP, dp + 1);
5:stack_write(aa.h);
6:stack_write(aa.l);
6:last_cycle();
stack_write(aa.l);
}
per(0x62) {
@@ -248,5 +275,6 @@ per(0x62) {
3:cpu_io();
rd.w = regs.pc.d + (int16)aa.w;
4:stack_write(rd.h);
5:stack_write(rd.l);
5:last_cycle();
stack_write(rd.l);
}

View File

@@ -1,4 +1,3 @@
bra(0x80, 1),
bcc(0x90, !regs.p.c),
bcs(0xb0, regs.p.c),
bne(0xd0, !regs.p.z),
@@ -7,7 +6,8 @@ bpl(0x10, !regs.p.n),
bmi(0x30, regs.p.n),
bvc(0x50, !regs.p.v),
bvs(0x70, regs.p.v) {
1:rd.l = op_read();
1:if(!$1)last_cycle();
rd.l = op_read();
if($1) {
aa.w = regs.pc.d + (int8)rd.l;
regs.pc.w = aa.w;
@@ -15,26 +15,39 @@ bvs(0x70, regs.p.v) {
end;
}
2:cpu_c6(aa.w);
3:cpu_io();
3:last_cycle();
cpu_io();
}
bra(0x80) {
1:rd.l = op_read();
aa.w = regs.pc.d + (int8)rd.l;
regs.pc.w = aa.w;
2:cpu_c6(aa.w);
3:last_cycle();
cpu_io();
}
brl(0x82) {
1:rd.l = op_read();
2:rd.h = op_read();
3:cpu_io();
3:last_cycle();
cpu_io();
regs.pc.w = regs.pc.d + (int16)rd.w;
}
jmp_addr(0x4c) {
1:rd.l = op_read();
2:rd.h = op_read();
2:last_cycle();
rd.h = op_read();
regs.pc.w = rd.w;
}
jmp_long(0x5c) {
1:rd.l = op_read();
2:rd.h = op_read();
3:rd.b = op_read();
3:last_cycle();
rd.b = op_read();
regs.pc.d = rd.d & 0xffffff;
}
@@ -42,7 +55,8 @@ jmp_iaddr(0x6c) {
1:aa.l = op_read();
2:aa.h = op_read();
3:rd.l = op_read(OPMODE_ADDR, aa.w);
4:rd.h = op_read(OPMODE_ADDR, aa.w + 1);
4:last_cycle();
rd.h = op_read(OPMODE_ADDR, aa.w + 1);
regs.pc.w = rd.w;
}
@@ -51,7 +65,8 @@ jmp_iaddrx(0x7c) {
2:aa.h = op_read();
3:cpu_io();
4:rd.l = op_read(OPMODE_PBR, aa.w + regs.x.w);
5:rd.h = op_read(OPMODE_PBR, aa.w + regs.x.w + 1);
5:last_cycle();
rd.h = op_read(OPMODE_PBR, aa.w + regs.x.w + 1);
regs.pc.w = rd.w;
}
@@ -60,7 +75,8 @@ jmp_iladdr(0xdc) {
2:aa.h = op_read();
3:rd.l = op_read(OPMODE_ADDR, aa.w);
4:rd.h = op_read(OPMODE_ADDR, aa.w + 1);
5:rd.b = op_read(OPMODE_ADDR, aa.w + 2);
5:last_cycle();
rd.b = op_read(OPMODE_ADDR, aa.w + 2);
regs.pc.d = rd.d & 0xffffff;
}
@@ -70,7 +86,8 @@ jsr_addr(0x20) {
3:cpu_io();
4:regs.pc.w--;
stack_write(regs.pc.h);
5:stack_write(regs.pc.l);
5:last_cycle();
stack_write(regs.pc.l);
regs.pc.w = aa.w;
}
@@ -82,7 +99,8 @@ jsr_long(0x22) {
5:aa.b = op_read();
6:regs.pc.w--;
stack_write(regs.pc.h);
7:stack_write(regs.pc.l);
7:last_cycle();
stack_write(regs.pc.l);
regs.pc.d = aa.d & 0xffffff;
}
@@ -93,7 +111,8 @@ jsr_iaddrx(0xfc) {
4:aa.h = op_read();
5:cpu_io();
6:rd.l = op_read(OPMODE_PBR, aa.w + regs.x.w);
7:rd.h = op_read(OPMODE_PBR, aa.w + regs.x.w + 1);
7:last_cycle();
rd.h = op_read(OPMODE_PBR, aa.w + regs.x.w + 1);
regs.pc.w = rd.w;
}
@@ -112,7 +131,8 @@ rti(0x40) {
regs.pc.w = rd.w;
end;
}
6:rd.b = stack_read();
6:last_cycle();
rd.b = stack_read();
regs.pc.d = rd.d & 0xffffff;
}
@@ -121,7 +141,8 @@ rts(0x60) {
2:cpu_io();
3:rd.l = stack_read();
4:rd.h = stack_read();
5:cpu_io();
5:last_cycle();
cpu_io();
regs.pc.w = rd.w;
regs.pc.w++;
}
@@ -131,7 +152,8 @@ rtl(0x6b) {
2:cpu_io();
3:rd.l = stack_read();
4:rd.h = stack_read();
5:rd.b = stack_read();
5:last_cycle();
rd.b = stack_read();
regs.pc.d = rd.d & 0xffffff;
regs.pc.w++;
}

View File

@@ -9,9 +9,11 @@ ldx_const(0xa2, ldx, regs.p.x),
ldy_const(0xa0, ldy, regs.p.x),
ora_const(0x09, ora, regs.p.m),
sbc_const(0xe9, sbc, regs.p.m) {
1:rd.l = op_read();
1:if($2)last_cycle();
rd.l = op_read();
if($2) { op_$1_b(); end; }
2:rd.h = op_read();
2:last_cycle();
rd.h = op_read();
op_$1_w();
}
@@ -29,9 +31,11 @@ ora_addr(0x0d, ora, regs.p.m),
sbc_addr(0xed, sbc, regs.p.m) {
1:aa.l = op_read();
2:aa.h = op_read();
3:rd.l = op_read(OPMODE_DBR, aa.w);
3:if($2)last_cycle();
rd.l = op_read(OPMODE_DBR, aa.w);
if($2) { op_$1_b(); end; }
4:rd.h = op_read(OPMODE_DBR, aa.w + 1);
4:last_cycle();
rd.h = op_read(OPMODE_DBR, aa.w + 1);
op_$1_w();
}
@@ -47,9 +51,11 @@ sbc_addrx(0xfd, sbc, regs.p.m) {
1:aa.l = op_read();
2:aa.h = op_read();
3:cpu_c4(aa.w, aa.w + regs.x.w);
4:rd.l = op_read(OPMODE_DBR, aa.w + regs.x.w);
4:if($2)last_cycle();
rd.l = op_read(OPMODE_DBR, aa.w + regs.x.w);
if($2) { op_$1_b(); end; }
5:rd.h = op_read(OPMODE_DBR, aa.w + regs.x.w + 1);
5:last_cycle();
rd.h = op_read(OPMODE_DBR, aa.w + regs.x.w + 1);
op_$1_w();
}
@@ -64,9 +70,11 @@ sbc_addry(0xf9, sbc, regs.p.m) {
1:aa.l = op_read();
2:aa.h = op_read();
3:cpu_c4(aa.w, aa.w + regs.y.w);
4:rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w);
4:if($2)last_cycle();
rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w);
if($2) { op_$1_b(); end; }
5:rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1);
5:last_cycle();
rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1);
op_$1_w();
}
@@ -80,9 +88,11 @@ sbc_long(0xef, sbc, regs.p.m) {
1:aa.l = op_read();
2:aa.h = op_read();
3:aa.b = op_read();
4:rd.l = op_read(OPMODE_LONG, aa.d);
4:if($2)last_cycle();
rd.l = op_read(OPMODE_LONG, aa.d);
if($2) { op_$1_b(); end; }
5:rd.h = op_read(OPMODE_LONG, aa.d + 1);
5:last_cycle();
rd.h = op_read(OPMODE_LONG, aa.d + 1);
op_$1_w();
}
@@ -96,9 +106,11 @@ sbc_longx(0xff, sbc, regs.p.m) {
1:aa.l = op_read();
2:aa.h = op_read();
3:aa.b = op_read();
4:rd.l = op_read(OPMODE_LONG, aa.d + regs.x.w);
4:if($2)last_cycle();
rd.l = op_read(OPMODE_LONG, aa.d + regs.x.w);
if($2) { op_$1_b(); end; }
5:rd.h = op_read(OPMODE_LONG, aa.d + regs.x.w + 1);
5:last_cycle();
rd.h = op_read(OPMODE_LONG, aa.d + regs.x.w + 1);
op_$1_w();
}
@@ -116,9 +128,11 @@ ora_dp(0x05, ora, regs.p.m),
sbc_dp(0xe5, sbc, regs.p.m) {
1:dp = op_read();
2:cpu_c2();
3:rd.l = op_read(OPMODE_DP, dp);
3:if($2)last_cycle();
rd.l = op_read(OPMODE_DP, dp);
if($2) { op_$1_b(); end; }
4:rd.h = op_read(OPMODE_DP, dp + 1);
4:last_cycle();
rd.h = op_read(OPMODE_DP, dp + 1);
op_$1_w();
}
@@ -134,9 +148,11 @@ sbc_dpx(0xf5, sbc, regs.p.m) {
1:dp = op_read();
2:cpu_c2();
3:cpu_io();
4:rd.l = op_read(OPMODE_DP, dp + regs.x.w);
4:if($2)last_cycle();
rd.l = op_read(OPMODE_DP, dp + regs.x.w);
if($2) { op_$1_b(); end; }
5:rd.h = op_read(OPMODE_DP, dp + regs.x.w + 1);
5:last_cycle();
rd.h = op_read(OPMODE_DP, dp + regs.x.w + 1);
op_$1_w();
}
@@ -144,9 +160,11 @@ ldx_dpy(0xb6, ldx, regs.p.x) {
1:dp = op_read();
2:cpu_c2();
3:cpu_io();
4:rd.l = op_read(OPMODE_DP, dp + regs.y.w);
4:if($2)last_cycle();
rd.l = op_read(OPMODE_DP, dp + regs.y.w);
if($2) { op_$1_b(); end; }
5:rd.h = op_read(OPMODE_DP, dp + regs.y.w + 1);
5:last_cycle();
rd.h = op_read(OPMODE_DP, dp + regs.y.w + 1);
op_$1_w();
}
@@ -161,9 +179,11 @@ sbc_idp(0xf2, sbc, regs.p.m) {
2:cpu_c2();
3:aa.l = op_read(OPMODE_DP, dp);
4:aa.h = op_read(OPMODE_DP, dp + 1);
5:rd.l = op_read(OPMODE_DBR, aa.w);
5:if($2)last_cycle();
rd.l = op_read(OPMODE_DBR, aa.w);
if($2) { op_$1_b(); end; }
6:rd.h = op_read(OPMODE_DBR, aa.w + 1);
6:last_cycle();
rd.h = op_read(OPMODE_DBR, aa.w + 1);
op_$1_w();
}
@@ -179,9 +199,11 @@ sbc_idpx(0xe1, sbc, regs.p.m) {
3:cpu_io();
4:aa.l = op_read(OPMODE_DP, dp + regs.x.w);
5:aa.h = op_read(OPMODE_DP, dp + regs.x.w + 1);
6:rd.l = op_read(OPMODE_DBR, aa.w);
6:if($2)last_cycle();
rd.l = op_read(OPMODE_DBR, aa.w);
if($2) { op_$1_b(); end; }
7:rd.h = op_read(OPMODE_DBR, aa.w + 1);
7:last_cycle();
rd.h = op_read(OPMODE_DBR, aa.w + 1);
op_$1_w();
}
@@ -197,9 +219,11 @@ sbc_idpy(0xf1, sbc, regs.p.m) {
3:aa.l = op_read(OPMODE_DP, dp);
4:aa.h = op_read(OPMODE_DP, dp + 1);
5:cpu_c4(aa.w, aa.w + regs.y.w);
6:rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w);
6:if($2)last_cycle();
rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w);
if($2) { op_$1_b(); end; }
7:rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1);
7:last_cycle();
rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1);
op_$1_w();
}
@@ -215,9 +239,11 @@ sbc_ildp(0xe7, sbc, regs.p.m) {
3:aa.l = op_read(OPMODE_DP, dp);
4:aa.h = op_read(OPMODE_DP, dp + 1);
5:aa.b = op_read(OPMODE_DP, dp + 2);
6:rd.l = op_read(OPMODE_LONG, aa.d);
6:if($2)last_cycle();
rd.l = op_read(OPMODE_LONG, aa.d);
if($2) { op_$1_b(); end; }
7:rd.h = op_read(OPMODE_LONG, aa.d + 1);
7:last_cycle();
rd.h = op_read(OPMODE_LONG, aa.d + 1);
op_$1_w();
}
@@ -233,9 +259,11 @@ sbc_ildpy(0xf7, sbc, regs.p.m) {
3:aa.l = op_read(OPMODE_DP, dp);
4:aa.h = op_read(OPMODE_DP, dp + 1);
5:aa.b = op_read(OPMODE_DP, dp + 2);
6:rd.l = op_read(OPMODE_LONG, aa.d + regs.y.w);
6:if($2)last_cycle();
rd.l = op_read(OPMODE_LONG, aa.d + regs.y.w);
if($2) { op_$1_b(); end; }
7:rd.h = op_read(OPMODE_LONG, aa.d + regs.y.w + 1);
7:last_cycle();
rd.h = op_read(OPMODE_LONG, aa.d + regs.y.w + 1);
op_$1_w();
}
@@ -248,9 +276,11 @@ ora_sr(0x03, ora, regs.p.m),
sbc_sr(0xe3, sbc, regs.p.m) {
1:sp = op_read();
2:cpu_io();
3:rd.l = op_read(OPMODE_SP, sp);
3:if($2)last_cycle();
rd.l = op_read(OPMODE_SP, sp);
if($2) { op_$1_b(); end; }
4:rd.h = op_read(OPMODE_SP, sp + 1);
4:last_cycle();
rd.h = op_read(OPMODE_SP, sp + 1);
op_$1_w();
}
@@ -266,18 +296,22 @@ sbc_isry(0xf3, sbc) {
3:aa.l = op_read(OPMODE_SP, sp);
4:aa.h = op_read(OPMODE_SP, sp + 1);
5:cpu_io();
6:rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w);
6:if(regs.p.m)last_cycle();
rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w);
if(regs.p.m) { op_$1_b(); end; }
7:rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1);
7:last_cycle();
rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1);
op_$1_w();
}
bit_const(0x89) {
1:rd.l = op_read();
1:if(regs.p.m)last_cycle();
rd.l = op_read();
if(regs.p.m) {
regs.p.z = ((rd.l & regs.a.l) == 0);
end;
}
2:rd.h = op_read();
2:last_cycle();
rd.h = op_read();
regs.p.z = ((rd.w & regs.a.w) == 0);
}

View File

@@ -1,7 +1,8 @@
inc(0x1a, regs.p.m, a),
inx(0xe8, regs.p.x, x),
iny(0xc8, regs.p.x, y) {
1:cpu_io();
1:last_cycle();
cpu_io();
if($1) {
regs.$2.l++;
regs.p.n = !!(regs.$2.l & 0x80);
@@ -16,7 +17,8 @@ iny(0xc8, regs.p.x, y) {
dec(0x3a, regs.p.m, a),
dex(0xca, regs.p.x, x),
dey(0x88, regs.p.x, y) {
1:cpu_io();
1:last_cycle();
cpu_io();
if($1) {
regs.$2.l--;
regs.p.n = !!(regs.$2.l & 0x80);
@@ -29,7 +31,8 @@ dey(0x88, regs.p.x, y) {
}
asl(0x0a) {
1:cpu_io();
1:last_cycle();
cpu_io();
if(regs.p.m) {
regs.p.c = !!(regs.a.l & 0x80);
regs.a.l <<= 1;
@@ -44,7 +47,8 @@ asl(0x0a) {
}
lsr(0x4a) {
1:cpu_io();
1:last_cycle();
cpu_io();
if(regs.p.m) {
regs.p.c = regs.a.l & 1;
regs.a.l >>= 1;
@@ -59,7 +63,8 @@ lsr(0x4a) {
}
rol(0x2a) {
1:cpu_io();
1:last_cycle();
cpu_io();
uint16 c = regs.p.c;
if(regs.p.m) {
regs.p.c = !!(regs.a.l & 0x80);
@@ -77,7 +82,8 @@ rol(0x2a) {
}
ror(0x6a) {
1:cpu_io();
1:last_cycle();
cpu_io();
uint16 c;
if(regs.p.m) {
c = (regs.p.c)?0x80:0;
@@ -113,7 +119,8 @@ tsb_addr(0x0c, tsb) {
if(regs.p.m) { op_$1_b(); skip; }
else op_$1_w();
6:op_write(OPMODE_DBR, aa.w + 1, rd.h);
7:op_write(OPMODE_DBR, aa.w, rd.l);
7:last_cycle();
op_write(OPMODE_DBR, aa.w, rd.l);
}
inc_addrx(0xfe, inc),
@@ -132,7 +139,8 @@ ror_addrx(0x7e, ror) {
if(regs.p.m) { op_$1_b(); skip; }
else op_$1_w();
7:op_write(OPMODE_DBR, aa.w + regs.x.w + 1, rd.h);
8:op_write(OPMODE_DBR, aa.w + regs.x.w, rd.l);
8:last_cycle();
op_write(OPMODE_DBR, aa.w + regs.x.w, rd.l);
}
inc_dp(0xe6, inc),
@@ -152,7 +160,8 @@ tsb_dp(0x04, tsb) {
if(regs.p.m) { op_$1_b(); skip; }
else op_$1_w();
6:op_write(OPMODE_DP, dp + 1, rd.h);
7:op_write(OPMODE_DP, dp, rd.l);
7:last_cycle();
op_write(OPMODE_DP, dp, rd.l);
}
inc_dpx(0xf6, inc),
@@ -171,5 +180,6 @@ ror_dpx(0x76, ror) {
if(regs.p.m) { op_$1_b(); skip; }
else op_$1_w();
7:op_write(OPMODE_DP, dp + regs.x.w + 1, rd.h);
8:op_write(OPMODE_DP, dp + regs.x.w, rd.l);
8:last_cycle();
op_write(OPMODE_DP, dp + regs.x.w, rd.l);
}

View File

@@ -4,9 +4,11 @@ sty_addr(0x8c, regs.p.x, regs.y.w),
stz_addr(0x9c, regs.p.m, 0x0000) {
1:aa.l = op_read();
2:aa.h = op_read();
3:op_write(OPMODE_DBR, aa.w, $2);
3:if($1)last_cycle();
op_write(OPMODE_DBR, aa.w, $2);
if($1)end;
4:op_write(OPMODE_DBR, aa.w + 1, $2 >> 8);
4:last_cycle();
op_write(OPMODE_DBR, aa.w + 1, $2 >> 8);
}
sta_addrx(0x9d, regs.p.m, regs.a.w),
@@ -14,36 +16,44 @@ stz_addrx(0x9e, regs.p.m, 0x0000) {
1:aa.l = op_read();
2:aa.h = op_read();
3:cpu_c4(aa.w, aa.w + regs.x.w);
4:op_write(OPMODE_DBR, aa.w + regs.x.w, $2);
4:if($1)last_cycle();
op_write(OPMODE_DBR, aa.w + regs.x.w, $2);
if($1)end;
5:op_write(OPMODE_DBR, aa.w + regs.x.w + 1, $2 >> 8);
5:last_cycle();
op_write(OPMODE_DBR, aa.w + regs.x.w + 1, $2 >> 8);
}
sta_addry(0x99) {
1:aa.l = op_read();
2:aa.h = op_read();
3:cpu_c4(aa.w, aa.w + regs.y.w);
4:op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l);
4:if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l);
if(regs.p.m)end;
5:op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h);
5:last_cycle();
op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h);
}
sta_long(0x8f) {
1:aa.l = op_read();
2:aa.h = op_read();
3:aa.b = op_read();
4:op_write(OPMODE_LONG, aa.d, regs.a.l);
4:if(regs.p.m)last_cycle();
op_write(OPMODE_LONG, aa.d, regs.a.l);
if(regs.p.m)end;
5:op_write(OPMODE_LONG, aa.d + 1, regs.a.h);
5:last_cycle();
op_write(OPMODE_LONG, aa.d + 1, regs.a.h);
}
sta_longx(0x9f) {
1:aa.l = op_read();
2:aa.h = op_read();
3:aa.b = op_read();
4:op_write(OPMODE_LONG, aa.d + regs.x.w, regs.a.l);
4:if(regs.p.m)last_cycle();
op_write(OPMODE_LONG, aa.d + regs.x.w, regs.a.l);
if(regs.p.m)end;
5:op_write(OPMODE_LONG, aa.d + regs.x.w + 1, regs.a.h);
5:last_cycle();
op_write(OPMODE_LONG, aa.d + regs.x.w + 1, regs.a.h);
}
sta_dp(0x85, regs.p.m, regs.a.w),
@@ -52,9 +62,11 @@ sty_dp(0x84, regs.p.x, regs.y.w),
stz_dp(0x64, regs.p.m, 0x0000) {
1:dp = op_read();
2:cpu_c2();
3:op_write(OPMODE_DP, dp, $2);
3:if($1)last_cycle();
op_write(OPMODE_DP, dp, $2);
if($1)end;
4:op_write(OPMODE_DP, dp + 1, $2 >> 8);
4:last_cycle();
op_write(OPMODE_DP, dp + 1, $2 >> 8);
}
sta_dpx(0x95, regs.p.m, regs.a.w),
@@ -63,18 +75,22 @@ stz_dpx(0x74, regs.p.m, 0x0000) {
1:dp = op_read();
2:cpu_c2();
3:cpu_io();
4:op_write(OPMODE_DP, dp + regs.x.w, $2);
4:if($1)last_cycle();
op_write(OPMODE_DP, dp + regs.x.w, $2);
if($1)end;
5:op_write(OPMODE_DP, dp + regs.x.w + 1, $2 >> 8);
5:last_cycle();
op_write(OPMODE_DP, dp + regs.x.w + 1, $2 >> 8);
}
stx_dpy(0x96) {
1:dp = op_read();
2:cpu_c2();
3:cpu_io();
4:op_write(OPMODE_DP, dp + regs.y.w, regs.x.l);
4:if(regs.p.x)last_cycle();
op_write(OPMODE_DP, dp + regs.y.w, regs.x.l);
if(regs.p.x)end;
5:op_write(OPMODE_DP, dp + regs.y.w + 1, regs.x.h);
5:last_cycle();
op_write(OPMODE_DP, dp + regs.y.w + 1, regs.x.h);
}
sta_idp(0x92) {
@@ -82,9 +98,11 @@ sta_idp(0x92) {
2:cpu_c2();
3:aa.l = op_read(OPMODE_DP, dp);
4:aa.h = op_read(OPMODE_DP, dp + 1);
5:op_write(OPMODE_DBR, aa.w, regs.a.l);
5:if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w, regs.a.l);
if(regs.p.m)end;
6:op_write(OPMODE_DBR, aa.w + 1, regs.a.h);
6:last_cycle();
op_write(OPMODE_DBR, aa.w + 1, regs.a.h);
}
sta_ildp(0x87) {
@@ -93,9 +111,11 @@ sta_ildp(0x87) {
3:aa.l = op_read(OPMODE_DP, dp);
4:aa.h = op_read(OPMODE_DP, dp + 1);
5:aa.b = op_read(OPMODE_DP, dp + 2);
6:op_write(OPMODE_LONG, aa.d, regs.a.l);
6:if(regs.p.m)last_cycle();
op_write(OPMODE_LONG, aa.d, regs.a.l);
if(regs.p.m)end;
7:op_write(OPMODE_LONG, aa.d + 1, regs.a.h);
7:last_cycle();
op_write(OPMODE_LONG, aa.d + 1, regs.a.h);
}
sta_idpx(0x81) {
@@ -104,9 +124,11 @@ sta_idpx(0x81) {
3:cpu_io();
4:aa.l = op_read(OPMODE_DP, dp + regs.x.w);
5:aa.h = op_read(OPMODE_DP, dp + regs.x.w + 1);
6:op_write(OPMODE_DBR, aa.w, regs.a.l);
6:if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w, regs.a.l);
if(regs.p.m)end;
7:op_write(OPMODE_DBR, aa.w + 1, regs.a.h);
7:last_cycle();
op_write(OPMODE_DBR, aa.w + 1, regs.a.h);
}
sta_idpy(0x91) {
@@ -115,9 +137,11 @@ sta_idpy(0x91) {
3:aa.l = op_read(OPMODE_DP, dp);
4:aa.h = op_read(OPMODE_DP, dp + 1);
5:cpu_c4(aa.w, aa.w + regs.y.w);
6:op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l);
6:if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l);
if(regs.p.m)end;
7:op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h);
7:last_cycle();
op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h);
}
sta_ildpy(0x97) {
@@ -126,17 +150,21 @@ sta_ildpy(0x97) {
3:aa.l = op_read(OPMODE_DP, dp);
4:aa.h = op_read(OPMODE_DP, dp + 1);
5:aa.b = op_read(OPMODE_DP, dp + 2);
6:op_write(OPMODE_LONG, aa.d + regs.y.w, regs.a.l);
6:if(regs.p.m)last_cycle();
op_write(OPMODE_LONG, aa.d + regs.y.w, regs.a.l);
if(regs.p.m)end;
7:op_write(OPMODE_LONG, aa.d + regs.y.w + 1, regs.a.h);
7:last_cycle();
op_write(OPMODE_LONG, aa.d + regs.y.w + 1, regs.a.h);
}
sta_sr(0x83) {
1:sp = op_read();
2:cpu_io();
3:op_write(OPMODE_SP, sp, regs.a.l);
3:if(regs.p.m)last_cycle();
op_write(OPMODE_SP, sp, regs.a.l);
if(regs.p.m)end;
4:op_write(OPMODE_SP, sp + 1, regs.a.h);
4:last_cycle();
op_write(OPMODE_SP, sp + 1, regs.a.h);
}
sta_isry(0x93) {
@@ -145,7 +173,9 @@ sta_isry(0x93) {
3:aa.l = op_read(OPMODE_SP, sp);
4:aa.h = op_read(OPMODE_SP, sp + 1);
5:cpu_io();
6:op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l);
6:if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l);
if(regs.p.m)end;
7:op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h);
7:last_cycle();
op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h);
}

View File

@@ -2,5 +2,6 @@
#include "dcpu.cpp"
CPU::CPU() {
cpu_version = 1;
mmio = &mmio_unmapped;
}

View File

@@ -2,6 +2,12 @@
class CPU {
public:
//CPU version number
//* 1 and 2 are known
//* reported by $4210
//* affects DRAM refresh behavior
uint8 cpu_version;
//timing
virtual uint16 vcounter() = 0;
virtual uint16 hcounter() = 0;
@@ -9,7 +15,7 @@ public:
virtual bool interlace() = 0;
virtual bool interlace_field() = 0;
virtual bool overscan() = 0;
virtual uint16 region_scanlines() = 0;
virtual void set_interlace(bool r) = 0;
virtual void set_overscan (bool r) = 0;
@@ -26,33 +32,37 @@ CPURegs regs;
};
virtual uint8 pio_status() = 0;
virtual void run() = 0;
virtual uint32 cycles_executed() = 0;
virtual void scanline() = 0;
virtual void frame() = 0;
virtual void power() = 0;
virtual void reset() = 0;
//opcode disassembler
enum {
OPTYPE_DP = 0, //dp
OPTYPE_DPX, //dp,x
OPTYPE_DPY, //dp,y
OPTYPE_IDP, //(dp)
OPTYPE_IDPX, //(dp,x)
OPTYPE_IDPY, //(dp),y
OPTYPE_ILDP, //[dp]
OPTYPE_ILDPY, //[dp],y
OPTYPE_ADDR, //addr
OPTYPE_ADDRX, //addr,x
OPTYPE_ADDRY, //addr,y
OPTYPE_IADDRX, //(addr,x)
OPTYPE_ILADDR, //[addr]
OPTYPE_LONG, //long
OPTYPE_LONGX, //long, x
OPTYPE_SR, //sr,s
OPTYPE_ISRY, //(sr,s),y
OPTYPE_ADDR_PC, //pbr:addr
OPTYPE_IADDR_PC //pbr:(addr)
};
enum {
OPTYPE_DP = 0, //dp
OPTYPE_DPX, //dp,x
OPTYPE_DPY, //dp,y
OPTYPE_IDP, //(dp)
OPTYPE_IDPX, //(dp,x)
OPTYPE_IDPY, //(dp),y
OPTYPE_ILDP, //[dp]
OPTYPE_ILDPY, //[dp],y
OPTYPE_ADDR, //addr
OPTYPE_ADDRX, //addr,x
OPTYPE_ADDRY, //addr,y
OPTYPE_IADDRX, //(addr,x)
OPTYPE_ILADDR, //[addr]
OPTYPE_LONG, //long
OPTYPE_LONGX, //long, x
OPTYPE_SR, //sr,s
OPTYPE_ISRY, //(sr,s),y
OPTYPE_ADDR_PC, //pbr:addr
OPTYPE_IADDR_PC //pbr:(addr)
};
//see dcpu.cpp for notes on this function
virtual bool in_opcode();
void disassemble_opcode(char *output);
uint32 resolve_offset(uint8 offset_type, uint32 addr);
uint8 opcode_length();

View File

@@ -13,7 +13,7 @@ private:
inline bool operator ^= (bool i) { if(i)_b ^= B; return (_b & B); }
};
public:
union {
union {
uint8 _b;
bit<0x80> n;
bit<0x40> v;
@@ -23,7 +23,7 @@ public:
bit<0x04> i;
bit<0x02> z;
bit<0x01> c;
};
};
CPURegFlags() { _b = 0; }
inline operator uint8() { return _b; }
@@ -35,10 +35,14 @@ public:
class CPUReg16 {
public:
union {
union {
uint16 w;
#ifdef ARCH_LSB
struct { uint8 l, h; };
};
#else
struct { uint8 h, l; };
#endif;
};
CPUReg16() { w = 0; }
inline operator uint16() { return w; }
@@ -56,11 +60,16 @@ public:
class CPUReg24 {
public:
union {
uint16 w;
union {
uint32 d;
struct { uint8 l, h, b; };
};
#ifdef ARCH_LSB
struct { uint16 w, null_w; };
struct { uint8 l, h, b, null_b; };
#else
struct { uint16 null_w, w; };
struct { uint8 null_b, b, h, l; };
#endif
};
CPUReg24() { d = 0; }
inline operator uint32() { return (d & 0xffffff); }
@@ -82,6 +91,7 @@ CPUReg24 pc;
CPUReg16 a, x, y, s, d;
CPURegFlags p;
uint8 db;
uint8 mdr; //memory data register (openbus)
bool e;
CPURegs() { db = 0; e = false; }
CPURegs() { db = 0; mdr = 0x00; e = false; }
};

View File

@@ -1,3 +1,17 @@
//this is a virtual function.
//in opcode-based CPU emulators, the main emulation routine
//will only be able to call the disassemble_opcode() function
//on clean opcode edges. but with cycle-based CPU emulators,
//the CPU may be in the middle of executing an opcode when the
//emulator (e.g. debugger) wants to disassemble an opcode. this
//would mean that important registers may not reflect what they
//did at the start of the opcode (especially regs.pc), so in
//cycle-based emulators, this function should be overridden to
//reflect whether or not an opcode has only been partially
//executed. if not, the debugger should abort attempts to skip,
//disable, or disassemble the current opcode.
bool CPU::in_opcode() { return false; }
uint16 CPU::__relb(int8 offset) {
uint32 addr;
addr = (regs.pc.d & 0xff0000) | ((regs.pc.d + 2) & 0xffff);
@@ -81,16 +95,24 @@ uint32 r = 0;
}
void CPU::disassemble_opcode(char *output) {
char *s = output;
char *s;
char t[256];
uint8 op, op0, op1, op2;
sprintf(s, "%0.6x ", regs.pc.d);
static CPUReg24 pc;
s = output;
//TODO: This should wrap around the bank byte
op = mem_bus->read(regs.pc.d);
op0 = mem_bus->read(regs.pc.d + 1);
op1 = mem_bus->read(regs.pc.d + 2);
op2 = mem_bus->read(regs.pc.d + 3);
if(in_opcode() == true) {
strcpy(s, "?????? <CPU within opcode>");
return;
}
pc.d = regs.pc.d;
sprintf(s, "%0.6x ", pc.d);
op = mem_bus->read(pc.d); pc.w++;
op0 = mem_bus->read(pc.d); pc.w++;
op1 = mem_bus->read(pc.d); pc.w++;
op2 = mem_bus->read(pc.d);
switch(op) {
case 0x00:sprintf(t, "brk #$%0.2x ", op0);break;
@@ -431,6 +453,10 @@ static uint8 op_len_tbl[256] = {
6,2,2,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0xen
2,2,2,2, 3,2,2,2, 1,3,1,1, 3,3,3,4 //0xfn
};
if(in_opcode() == true) {
return 0;
}
op = mem_bus->read(regs.pc.d);
len = op_len_tbl[op];
if(len == 5)return (regs.p.m)?2:3;

586
src/dsp/bdsp/bdsp.cpp Normal file
View File

@@ -0,0 +1,586 @@
#include "../../base.h"
#include "bdsp_tables.cpp"
uint8 bDSP::read_8 (uint16 addr) { return (spcram[addr]); }
uint16 bDSP::read_16 (uint16 addr) { return (spcram[(addr + 1) & 0xffff] << 8) + (spcram[addr]); }
void bDSP::write_8 (uint16 addr, uint8 data) { spcram[addr] = data; }
void bDSP::write_16(uint16 addr, uint16 data) { spcram[addr++] = data; spcram[addr] = data >> 8; }
uint8 bDSP::read(uint8 addr) {
int i, v, n;
addr &= 127;
v = addr >> 4;
n = addr & 15;
switch(addr) {
case 0x00:case 0x10:case 0x20:case 0x30:
case 0x40:case 0x50:case 0x60:case 0x70:
return voice[v].VOLL;
case 0x01:case 0x11:case 0x21:case 0x31:
case 0x41:case 0x51:case 0x61:case 0x71:
return voice[v].VOLR;
case 0x02:case 0x12:case 0x22:case 0x32:
case 0x42:case 0x52:case 0x62:case 0x72:
return voice[v].PITCH >> 8;
case 0x03:case 0x13:case 0x23:case 0x33:
case 0x43:case 0x53:case 0x63:case 0x73:
return voice[v].PITCH;
case 0x04:case 0x14:case 0x24:case 0x34:
case 0x44:case 0x54:case 0x64:case 0x74:
return voice[v].SRCN;
case 0x05:case 0x15:case 0x25:case 0x35:
case 0x45:case 0x55:case 0x65:case 0x75:
return voice[v].ADSR1;
case 0x06:case 0x16:case 0x26:case 0x36:
case 0x46:case 0x56:case 0x66:case 0x76:
return voice[v].ADSR2;
case 0x07:case 0x17:case 0x27:case 0x37:
case 0x47:case 0x57:case 0x67:case 0x77:
return voice[v].GAIN;
case 0x08:case 0x18:case 0x28:case 0x38:
case 0x48:case 0x58:case 0x68:case 0x78:
return voice[v].ENVX;
case 0x09:case 0x19:case 0x29:case 0x39:
case 0x49:case 0x59:case 0x69:case 0x79:
return voice[v].OUTX;
case 0x0f:case 0x1f:case 0x2f:case 0x3f:
case 0x4f:case 0x5f:case 0x6f:case 0x7f:
return status.FIR[v];
case 0x0c:return status.MVOLL;
case 0x1c:return status.MVOLR;
case 0x2c:return status.EVOLL;
case 0x3c:return status.EVOLR;
case 0x4c:return status.KON;
case 0x5c:return status.KOFF;
case 0x6c:return status.FLG;
case 0x7c:return status.ENDX;
case 0x0d:return status.EFB;
case 0x2d:return status.PMON;
case 0x3d:return status.NON;
case 0x4d:return status.EON;
case 0x5d:return status.DIR;
case 0x6d:return status.ESA;
case 0x7d:return status.EDL;
}
return dspram[addr];
}
void bDSP::write(uint8 addr, uint8 data) {
int i, v, n;
//0x80-0xff is a read-only mirror of 0x00-0x7f
if(addr & 0x80)return;
v = addr >> 4;
n = addr & 15;
switch(addr) {
case 0x00:case 0x10:case 0x20:case 0x30:
case 0x40:case 0x50:case 0x60:case 0x70:
voice[v].VOLL = data;
break;
case 0x01:case 0x11:case 0x21:case 0x31:
case 0x41:case 0x51:case 0x61:case 0x71:
voice[v].VOLR = data;
break;
case 0x02:case 0x12:case 0x22:case 0x32:
case 0x42:case 0x52:case 0x62:case 0x72:
voice[v].PITCH &= 0xff00;
voice[v].PITCH |= data;
break;
case 0x03:case 0x13:case 0x23:case 0x33:
case 0x43:case 0x53:case 0x63:case 0x73:
voice[v].PITCH &= 0x00ff;
voice[v].PITCH |= data << 8;
break;
case 0x04:case 0x14:case 0x24:case 0x34:
case 0x44:case 0x54:case 0x64:case 0x74:
//voice[v].SRCN = data;
//below is anomie's code, but TRAC says writing SRCN doesn't affect anything until a
//BRR-with-end block is encountered, where it loads the loop address from the new SRCN
if(voice[v].SRCN != data) {
voice[v].SRCN = data;
voice[v].brr_ptr = read_16((status.DIR << 8) + (voice[v].SRCN << 2) + ((voice[v].brr_looped)?2:0));
voice[v].brr_index = 0;
}
break;
case 0x05:case 0x15:case 0x25:case 0x35:
case 0x45:case 0x55:case 0x65:case 0x75:
voice[v].ADSR1 = data;
voice[v].AdjustEnvelope();
break;
case 0x06:case 0x16:case 0x26:case 0x36:
case 0x46:case 0x56:case 0x66:case 0x76:
voice[v].ADSR2 = data;
//sustain_level = 0-7, 7 is a special case handled by ATTACK envx mode
voice[v].env_sustain = (voice[v].ADSR_sus_level() + 1) << 8;
voice[v].AdjustEnvelope();
break;
case 0x07:case 0x17:case 0x27:case 0x37:
case 0x47:case 0x57:case 0x67:case 0x77:
voice[v].GAIN = data;
voice[v].AdjustEnvelope();
break;
case 0x08:case 0x18:case 0x28:case 0x38:
case 0x48:case 0x58:case 0x68:case 0x78:
voice[v].ENVX = data;
break;
case 0x09:case 0x19:case 0x29:case 0x39:
case 0x49:case 0x59:case 0x69:case 0x79:
voice[v].OUTX = data;
break;
case 0x0f:case 0x1f:case 0x2f:case 0x3f:
case 0x4f:case 0x5f:case 0x6f:case 0x7f:
status.FIR[v] = data;
break;
case 0x0c:status.MVOLL = data;break;
case 0x1c:status.MVOLR = data;break;
case 0x2c:status.EVOLL = data;break;
case 0x3c:status.EVOLR = data;break;
case 0x4c:
status.KON = data;
// status.kon = data;
status.key_flag = true;
break;
case 0x5c:
status.KOFF = data;
status.key_flag = true;
break;
case 0x6c:
status.FLG = data;
status.key_flag = true;
status.noise_rate = RateTable[data & 0x1f];
break;
case 0x7c:
//read-only register, writes clear all bits of ENDX
status.ENDX = 0;
break;
case 0x0d:status.EFB = data;break;
case 0x2d:status.PMON = data;break;
case 0x3d:status.NON = data;break;
case 0x4d:status.EON = data;break;
case 0x5d:status.DIR = data;break;
case 0x6d:status.ESA = data;break;
case 0x7d:
status.EDL = data;
status.echo_size = (data & 0x0f) << 11;
break;
}
dspram[addr] = data;
}
void bDSP::power() {
int v;
spcram = apu->get_spcram_handle();
memset(dspram, 0x00, 128);
for(v=0;v<8;v++) {
voice[v].VOLL = 0;
voice[v].VOLR = 0;
voice[v].PITCH = 0;
voice[v].SRCN = 0;
voice[v].ADSR1 = 0;
voice[v].ADSR2 = 0;
voice[v].GAIN = 0;
status.FIR[v] = 0;
}
status.MVOLL = status.MVOLR = 0;
status.EVOLL = status.EVOLR = 0;
status.ENDX = 0;
status.EFB = 0;
status.PMON = 0;
status.NON = 0;
status.EON = 0;
status.DIR = 0;
status.ESA = 0;
status.EDL = 0;
status.echo_size = 0;
status.echo_target = 0;
reset();
}
void bDSP::reset() {
int v;
status.KON = 0x00;
status.KOFF = 0x00;
status.FLG |= 0xe0;
//status.kon = 0x00;
status.key_flag = false;
status.noise_ctr = 0;
status.noise_rate = 0;
status.noise_sample = 0x4000;
status.echo_index = 0;
status.fir_buffer_index = 0;
for(v=0;v<8;v++) {
voice[v].ENVX = 0;
voice[v].OUTX = 0;
voice[v].pitch_ctr = 0;
voice[v].brr_index = 0;
voice[v].brr_ptr = read_16((status.DIR << 8) + (voice[v].SRCN << 2));
voice[v].brr_looped = false;
voice[v].brr_data[0] = 0;
voice[v].brr_data[1] = 0;
voice[v].brr_data[2] = 0;
voice[v].brr_data[3] = 0;
voice[v].brr_data_index = 0;
voice[v].envx = 0;
voice[v].env_ctr = 0;
voice[v].env_rate = 0;
voice[v].env_state = SILENCE;
voice[v].env_mode = DIRECT;
status.fir_buffer[0][v] = 0;
status.fir_buffer[1][v] = 0;
}
dsp_counter = 0;
}
int32 bDSP::clamp(int32 bits, int32 x) {
int32 b = 1 << (bits - 1);
if(x > (b - 1)) {
return (b - 1);
} else if(x < -b) {
return -b;
} else {
return x;
}
}
int32 bDSP::clip(int32 bits, int32 x) {
int32 b = 1 << (bits - 1);
if(x & b) {
return x | ~(b - 1);
} else {
return x & (b - 1);
}
}
uint32 bDSP::run() {
int v, d;
uint8 pmon;
int32 sample;
int32 msamplel, msampler;
int32 esamplel, esampler;
int32 fir_samplel, fir_sampler;
pmon = status.PMON & ~status.NON & ~1;
if(!(dsp_counter++ & 1) && status.key_flag) {
for(v=0;v<8;v++) {
uint8 mask = 1 << v;
if(status.soft_reset()) {
if(voice[v].env_state != SILENCE) {
voice[v].env_state = SILENCE;
voice[v].AdjustEnvelope();
}
} else if(status.KOFF & mask) {
if(voice[v].env_state != SILENCE && voice[v].env_state != RELEASE) {
voice[v].env_state = RELEASE;
voice[v].AdjustEnvelope();
}
} else if(status.KON & mask) { //status.kon
status.KON &= ~mask; //new code
status.ENDX &= ~mask; //new code
voice[v].brr_ptr = read_16((status.DIR << 8) + (voice[v].SRCN << 2));
voice[v].brr_index = -9;
voice[v].brr_looped = false;
voice[v].brr_data[0] = 0;
voice[v].brr_data[1] = 0;
voice[v].brr_data[2] = 0;
voice[v].brr_data[3] = 0;
voice[v].envx = 0;
voice[v].env_state = ATTACK;
voice[v].AdjustEnvelope();
}
}
// status.ENDX &= ~status.kon;
// status.kon = 0;
status.key_flag = false;
}
//update noise
status.noise_ctr += status.noise_rate;
if(status.noise_ctr >= 0x7800) {
status.noise_ctr -= 0x7800;
status.noise_sample = (status.noise_sample >> 1) |
(((status.noise_sample << 14) ^ (status.noise_sample << 13)) & 0x4000);
}
msamplel = msampler = 0;
esamplel = esampler = 0;
for(v=0;v<8;v++) {
if(voice[v].brr_index < -1) {
voice[v].brr_index++;
voice[v].OUTX = voice[v].outx = 0;
voice[v].ENVX = 0;
continue;
}
if(voice[v].brr_index >= 0) {
if(pmon & (1 << v)) {
voice[v].pitch_ctr += (voice[v].pitch_rate() * (voice[v - 1].outx + 0x8000)) >> 15;
} else {
voice[v].pitch_ctr += voice[v].pitch_rate();
}
} else {
voice[v].pitch_ctr = 0x3000;
voice[v].brr_index = 0;
}
//decode BRR samples
while(voice[v].pitch_ctr >= 0) {
voice[v].pitch_ctr -= 0x1000;
voice[v].brr_data_index++;
voice[v].brr_data_index &= 3;
if(voice[v].brr_index == 0) {
voice[v].brr_header = read_8(voice[v].brr_ptr);
if(voice[v].brr_header_flags() & BRR_END) {
status.ENDX |= (1 << v);
}
if(voice[v].brr_header_flags() == BRR_END) {
voice[v].env_state = SILENCE;
voice[v].AdjustEnvelope();
}
}
#define S(x) voice[v].brr_data[(voice[v].brr_data_index + (x)) & 3]
if(voice[v].env_state != SILENCE) {
sample = read_8(voice[v].brr_ptr + 1 + (voice[v].brr_index >> 1));
if(voice[v].brr_index & 1) {
sample = clip(4, sample);
} else {
sample = clip(4, sample >> 4);
}
if(voice[v].brr_header_shift() <= 12) {
sample = (sample << voice[v].brr_header_shift() >> 1);
} else {
sample &= ~0x7ff;
}
switch(voice[v].brr_header_filter()) {
case 0: //direct
break;
case 1: //15/16
sample += S(-1) + ((-S(-1)) >> 4);
break;
case 2: //61/32 - 15/16
sample += (S(-1) << 1) + ((-((S(-1) << 1) + S(-1))) >> 5)
- S(-2) + (S(-2) >> 4);
break;
case 3: //115/64 - 13/16
sample += (S(-1) << 1) + ((-(S(-1) + (S(-1) << 2) + (S(-1) << 3))) >> 6)
- S(-2) + (((S(-2) << 1) + S(-2)) >> 4);
break;
}
S(0) = sample = clip(15, clamp(16, sample));
} else {
S(0) = sample = 0;
}
if(++voice[v].brr_index > 15) {
voice[v].brr_index = 0;
if(voice[v].brr_header_flags() & BRR_END) {
voice[v].brr_ptr = read_16((status.DIR << 8) + (voice[v].SRCN << 2) + 2);
voice[v].brr_looped = true;
} else {
voice[v].brr_ptr += 9;
}
}
}
//volume envelope adjust
voice[v].env_ctr += voice[v].env_rate;
if(voice[v].env_ctr >= 0x7800) {
voice[v].env_ctr -= 0x7800;
switch(voice[v].env_mode) {
case DIRECT:
voice[v].env_rate = 0;
break;
case LINEAR_DEC:
voice[v].envx -= 32;
if(voice[v].envx <= 0) {
voice[v].envx = 0;
voice[v].env_rate = 0;
voice[v].env_mode = DIRECT;
}
break;
case LINEAR_INC:
voice[v].envx += 32;
if(voice[v].envx >= 0x7ff) {
voice[v].envx = 0x7ff;
voice[v].env_rate = 0;
voice[v].env_mode = DIRECT;
if(voice[v].ADSR_enabled() && voice[v].env_state == ATTACK) {
voice[v].env_state = ((voice[v].env_sustain == 0x800) ? SUSTAIN : DECAY);
voice[v].AdjustEnvelope();
}
}
break;
case EXP_DEC:
//multiply by 255/256ths
voice[v].envx -= ((voice[v].envx - 1) >> 8) + 1;
if(voice[v].ADSR_enabled() && voice[v].env_state == DECAY && voice[v].envx <= voice[v].env_sustain) {
voice[v].env_state = SUSTAIN;
voice[v].AdjustEnvelope();
} else if(voice[v].envx <= 0) {
voice[v].envx = 0;
voice[v].env_rate = 0;
voice[v].env_mode = DIRECT;
}
break;
case BENT_INC:
if(voice[v].envx < 0x600) {
voice[v].envx += 32;
} else {
voice[v].envx += 8;
if(voice[v].envx >= 0x7ff) {
voice[v].envx = 0x7ff;
voice[v].env_rate = 0;
voice[v].env_mode = DIRECT;
}
}
break;
case FAST_ATTACK:
voice[v].envx += 0x400;
if(voice[v].envx >= 0x7ff) {
voice[v].envx = 0x7ff;
//attack raises to max. envx, if sustain is also set to max. envx, skip decay phase
voice[v].env_state = ((voice[v].env_sustain == 0x800) ? SUSTAIN : DECAY);
voice[v].AdjustEnvelope();
}
break;
case RELEASE_DEC:
voice[v].envx -= 8;
if(voice[v].envx <= 0) {
voice[v].env_state = SILENCE;
voice[v].AdjustEnvelope();
}
break;
}
}
voice[v].ENVX = voice[v].envx >> 4;
//gaussian interpolation / noise
if(status.NON & (1 << v)) {
sample = status.noise_sample;
} else {
d = voice[v].pitch_ctr >> 4; //-256 <= sample <= -1
sample = ((GaussTable[ -1-d] * S(-3)) >> 11);
sample += ((GaussTable[255-d] * S(-2)) >> 11);
sample += ((GaussTable[512+d] * S(-1)) >> 11);
sample = clip (15, sample);
sample += ((GaussTable[256+d] * S( 0)) >> 11);
sample = clamp(15, sample);
}
#undef S
//envelope / volume adjust
sample = (sample * voice[v].envx) >> 11;
voice[v].outx = sample << 1;
voice[v].OUTX = sample >> 7;
if(!status.mute()) {
msamplel += ((sample * voice[v].VOLL) >> 7) << 1;
msampler += ((sample * voice[v].VOLR) >> 7) << 1;
}
if((status.EON & (1 << v)) && status.echo_write()) {
esamplel += ((sample * voice[v].VOLL) >> 7) << 1;
esampler += ((sample * voice[v].VOLR) >> 7) << 1;
}
}
//echo (FIR) adjust
#define F(c,x) status.fir_buffer[c][(status.fir_buffer_index+(x)) & 7]
status.fir_buffer_index++;
F(0,0) = read_16((status.ESA << 8) + status.echo_index);
F(1,0) = read_16((status.ESA << 8) + status.echo_index + 2);
fir_samplel = (F(0,-0) * status.FIR[7] +
F(0,-1) * status.FIR[6] +
F(0,-2) * status.FIR[5] +
F(0,-3) * status.FIR[4] +
F(0,-4) * status.FIR[3] +
F(0,-5) * status.FIR[2] +
F(0,-6) * status.FIR[1] +
F(0,-7) * status.FIR[0]);
fir_sampler = (F(1,-0) * status.FIR[7] +
F(1,-1) * status.FIR[6] +
F(1,-2) * status.FIR[5] +
F(1,-3) * status.FIR[4] +
F(1,-4) * status.FIR[3] +
F(1,-5) * status.FIR[2] +
F(1,-6) * status.FIR[1] +
F(1,-7) * status.FIR[0]);
#undef F
//update echo buffer
if(status.echo_write()) {
esamplel += (fir_samplel * status.EFB) >> 14;
esampler += (fir_sampler * status.EFB) >> 14;
esamplel = clamp(16, esamplel);
esampler = clamp(16, esampler);
write_16((status.ESA << 8) + status.echo_index, esamplel);
write_16((status.ESA << 8) + status.echo_index + 2, esampler);
}
status.echo_index += 4;
if(status.echo_index >= status.echo_target) {
status.echo_index = 0;
status.echo_target = status.echo_size;
}
//main output adjust
if(!status.mute()) {
msamplel = (msamplel * status.MVOLL) >> 7;
msampler = (msampler * status.MVOLR) >> 7;
msamplel += (fir_samplel * status.EVOLL) >> 14;
msampler += (fir_sampler * status.EVOLR) >> 14;
msamplel = clamp(16, msamplel);
msampler = clamp(16, msampler);
}
return (uint32)(((uint16)msamplel) | ((uint16)msampler << 16));
}
bDSP::bDSP() {}
bDSP::~bDSP() {}

176
src/dsp/bdsp/bdsp.h Normal file
View File

@@ -0,0 +1,176 @@
class bDSP : public DSP {
private:
uint8 dspram[128];
uint8 *spcram;
uint32 dsp_counter;
enum {
BRR_END = 1,
BRR_LOOP = 2
};
uint8 read_8 (uint16 addr);
uint16 read_16 (uint16 addr);
void write_8 (uint16 addr, uint8 data);
void write_16(uint16 addr, uint16 data);
public:
static const uint16 RateTable[32];
static const int16 GaussTable[512];
enum EnvelopeStates {
ATTACK,
DECAY,
SUSTAIN,
RELEASE,
SILENCE
};
enum EnvelopeModes {
DIRECT,
LINEAR_DEC,
EXP_DEC,
LINEAR_INC,
BENT_INC,
FAST_ATTACK,
RELEASE_DEC
};
private:
struct Status {
//$0c,$1c
int8 MVOLL, MVOLR;
//$2c,$3c
int8 EVOLL, EVOLR;
//$4c,$5c
uint8 KON, KOFF;
//$6c
uint8 FLG;
//$7c
uint8 ENDX;
//$0d
int8 EFB;
//$2d,$3d,$4d
uint8 PMON, NON, EON;
//$5d
uint8 DIR;
//$6d,$7d
uint8 ESA, EDL;
//$xf
int8 FIR[8];
//internal variables
//uint8 kon;
bool key_flag;
int16 noise_ctr, noise_rate;
uint16 noise_sample;
uint16 echo_index, echo_size, echo_target;
int16 fir_buffer[2][8];
uint8 fir_buffer_index;
//functions
bool soft_reset() { return !!(FLG & 0x80); }
bool mute() { return !!(FLG & 0x40); }
bool echo_write() { return !(FLG & 0x20); }
} status;
struct Voice {
//$x0-$x1
int8 VOLL, VOLR;
//$x2-$x3
int16 PITCH;
//$x4
uint8 SRCN;
//$x5-$x7
uint8 ADSR1, ADSR2, GAIN;
//$x8-$x9
uint8 ENVX, OUTX;
//internal variables
int16 pitch_ctr;
int8 brr_index;
uint16 brr_ptr;
uint8 brr_header;
bool brr_looped;
int16 brr_data[4];
uint8 brr_data_index;
int16 envx;
uint16 env_ctr, env_rate, env_sustain;
enum EnvelopeStates env_state;
enum EnvelopeModes env_mode;
int16 outx;
//functions
int16 pitch_rate() { return PITCH & 0x3fff; }
uint8 brr_header_shift() { return brr_header >> 4; }
uint8 brr_header_filter() { return (brr_header >> 2) & 3; }
uint8 brr_header_flags() { return brr_header & 3; }
bool ADSR_enabled() { return !!(ADSR1 & 0x80); }
uint8 ADSR_decay() { return (ADSR1 >> 4) & 7; }
uint8 ADSR_attack() { return ADSR1 & 15; }
uint8 ADSR_sus_level() { return ADSR2 >> 5; }
uint8 ADSR_sus_rate() { return ADSR2 & 31; }
void AdjustEnvelope() {
if(env_state == SILENCE) {
env_mode = DIRECT;
env_rate = 0;
envx = 0;
} else if(env_state == RELEASE) {
env_mode = RELEASE_DEC;
env_rate = 0x7800;
} else if(ADSR_enabled()) {
switch(env_state) {
case ATTACK:
env_rate = RateTable[(ADSR_attack() << 1) + 1];
env_mode = (env_rate == 0x7800) ? FAST_ATTACK : LINEAR_INC;
break;
case DECAY:
env_rate = RateTable[(ADSR_decay() << 1) + 0x10];
env_mode = EXP_DEC;
break;
case SUSTAIN:
env_rate = RateTable[ADSR_sus_rate()];
env_mode = (env_rate == 0) ? DIRECT : EXP_DEC;
break;
}
} else if(GAIN & 0x80) {
switch(GAIN & 0x60) {
case 0x00:env_mode = LINEAR_DEC; break;
case 0x20:env_mode = EXP_DEC; break;
case 0x40:env_mode = LINEAR_INC; break;
case 0x60:env_mode = BENT_INC; break;
}
env_rate = RateTable[GAIN & 0x1f];
} else {
env_mode = DIRECT;
env_rate = 0;
envx = (GAIN & 0x7f) << 4;
}
}
} voice[8];
int32 clamp(int32 bits, int32 x);
int32 clip (int32 bits, int32 x);
public:
uint8 read (uint8 addr);
void write(uint8 addr, uint8 data);
void power();
void reset();
uint32 run();
bDSP();
~bDSP();
};

View File

@@ -0,0 +1,73 @@
const uint16 bDSP::RateTable[32] = {
0x0000, 0x000F, 0x0014, 0x0018, 0x001E, 0x0028, 0x0030, 0x003C,
0x0050, 0x0060, 0x0078, 0x00A0, 0x00C0, 0x00F0, 0x0140, 0x0180,
0x01E0, 0x0280, 0x0300, 0x03C0, 0x0500, 0x0600, 0x0780, 0x0A00,
0x0C00, 0x0F00, 0x1400, 0x1800, 0x1E00, 0x2800, 0x3C00, 0x7800
};
const int16 bDSP::GaussTable[512] = {
0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001,
0x001, 0x001, 0x001, 0x002, 0x002, 0x002, 0x002, 0x002,
0x002, 0x002, 0x003, 0x003, 0x003, 0x003, 0x003, 0x004,
0x004, 0x004, 0x004, 0x004, 0x005, 0x005, 0x005, 0x005,
0x006, 0x006, 0x006, 0x006, 0x007, 0x007, 0x007, 0x008,
0x008, 0x008, 0x009, 0x009, 0x009, 0x00A, 0x00A, 0x00A,
0x00B, 0x00B, 0x00B, 0x00C, 0x00C, 0x00D, 0x00D, 0x00E,
0x00E, 0x00F, 0x00F, 0x00F, 0x010, 0x010, 0x011, 0x011,
0x012, 0x013, 0x013, 0x014, 0x014, 0x015, 0x015, 0x016,
0x017, 0x017, 0x018, 0x018, 0x019, 0x01A, 0x01B, 0x01B,
0x01C, 0x01D, 0x01D, 0x01E, 0x01F, 0x020, 0x020, 0x021,
0x022, 0x023, 0x024, 0x024, 0x025, 0x026, 0x027, 0x028,
0x029, 0x02A, 0x02B, 0x02C, 0x02D, 0x02E, 0x02F, 0x030,
0x031, 0x032, 0x033, 0x034, 0x035, 0x036, 0x037, 0x038,
0x03A, 0x03B, 0x03C, 0x03D, 0x03E, 0x040, 0x041, 0x042,
0x043, 0x045, 0x046, 0x047, 0x049, 0x04A, 0x04C, 0x04D,
0x04E, 0x050, 0x051, 0x053, 0x054, 0x056, 0x057, 0x059,
0x05A, 0x05C, 0x05E, 0x05F, 0x061, 0x063, 0x064, 0x066,
0x068, 0x06A, 0x06B, 0x06D, 0x06F, 0x071, 0x073, 0x075,
0x076, 0x078, 0x07A, 0x07C, 0x07E, 0x080, 0x082, 0x084,
0x086, 0x089, 0x08B, 0x08D, 0x08F, 0x091, 0x093, 0x096,
0x098, 0x09A, 0x09C, 0x09F, 0x0A1, 0x0A3, 0x0A6, 0x0A8,
0x0AB, 0x0AD, 0x0AF, 0x0B2, 0x0B4, 0x0B7, 0x0BA, 0x0BC,
0x0BF, 0x0C1, 0x0C4, 0x0C7, 0x0C9, 0x0CC, 0x0CF, 0x0D2,
0x0D4, 0x0D7, 0x0DA, 0x0DD, 0x0E0, 0x0E3, 0x0E6, 0x0E9,
0x0EC, 0x0EF, 0x0F2, 0x0F5, 0x0F8, 0x0FB, 0x0FE, 0x101,
0x104, 0x107, 0x10B, 0x10E, 0x111, 0x114, 0x118, 0x11B,
0x11E, 0x122, 0x125, 0x129, 0x12C, 0x130, 0x133, 0x137,
0x13A, 0x13E, 0x141, 0x145, 0x148, 0x14C, 0x150, 0x153,
0x157, 0x15B, 0x15F, 0x162, 0x166, 0x16A, 0x16E, 0x172,
0x176, 0x17A, 0x17D, 0x181, 0x185, 0x189, 0x18D, 0x191,
0x195, 0x19A, 0x19E, 0x1A2, 0x1A6, 0x1AA, 0x1AE, 0x1B2,
0x1B7, 0x1BB, 0x1BF, 0x1C3, 0x1C8, 0x1CC, 0x1D0, 0x1D5,
0x1D9, 0x1DD, 0x1E2, 0x1E6, 0x1EB, 0x1EF, 0x1F3, 0x1F8,
0x1FC, 0x201, 0x205, 0x20A, 0x20F, 0x213, 0x218, 0x21C,
0x221, 0x226, 0x22A, 0x22F, 0x233, 0x238, 0x23D, 0x241,
0x246, 0x24B, 0x250, 0x254, 0x259, 0x25E, 0x263, 0x267,
0x26C, 0x271, 0x276, 0x27B, 0x280, 0x284, 0x289, 0x28E,
0x293, 0x298, 0x29D, 0x2A2, 0x2A6, 0x2AB, 0x2B0, 0x2B5,
0x2BA, 0x2BF, 0x2C4, 0x2C9, 0x2CE, 0x2D3, 0x2D8, 0x2DC,
0x2E1, 0x2E6, 0x2EB, 0x2F0, 0x2F5, 0x2FA, 0x2FF, 0x304,
0x309, 0x30E, 0x313, 0x318, 0x31D, 0x322, 0x326, 0x32B,
0x330, 0x335, 0x33A, 0x33F, 0x344, 0x349, 0x34E, 0x353,
0x357, 0x35C, 0x361, 0x366, 0x36B, 0x370, 0x374, 0x379,
0x37E, 0x383, 0x388, 0x38C, 0x391, 0x396, 0x39B, 0x39F,
0x3A4, 0x3A9, 0x3AD, 0x3B2, 0x3B7, 0x3BB, 0x3C0, 0x3C5,
0x3C9, 0x3CE, 0x3D2, 0x3D7, 0x3DC, 0x3E0, 0x3E5, 0x3E9,
0x3ED, 0x3F2, 0x3F6, 0x3FB, 0x3FF, 0x403, 0x408, 0x40C,
0x410, 0x415, 0x419, 0x41D, 0x421, 0x425, 0x42A, 0x42E,
0x432, 0x436, 0x43A, 0x43E, 0x442, 0x446, 0x44A, 0x44E,
0x452, 0x455, 0x459, 0x45D, 0x461, 0x465, 0x468, 0x46C,
0x470, 0x473, 0x477, 0x47A, 0x47E, 0x481, 0x485, 0x488,
0x48C, 0x48F, 0x492, 0x496, 0x499, 0x49C, 0x49F, 0x4A2,
0x4A6, 0x4A9, 0x4AC, 0x4AF, 0x4B2, 0x4B5, 0x4B7, 0x4BA,
0x4BD, 0x4C0, 0x4C3, 0x4C5, 0x4C8, 0x4CB, 0x4CD, 0x4D0,
0x4D2, 0x4D5, 0x4D7, 0x4D9, 0x4DC, 0x4DE, 0x4E0, 0x4E3,
0x4E5, 0x4E7, 0x4E9, 0x4EB, 0x4ED, 0x4EF, 0x4F1, 0x4F3,
0x4F5, 0x4F6, 0x4F8, 0x4FA, 0x4FB, 0x4FD, 0x4FF, 0x500,
0x502, 0x503, 0x504, 0x506, 0x507, 0x508, 0x50A, 0x50B,
0x50C, 0x50D, 0x50E, 0x50F, 0x510, 0x511, 0x511, 0x512,
0x513, 0x514, 0x514, 0x515, 0x516, 0x516, 0x517, 0x517,
0x517, 0x518, 0x518, 0x518, 0x518, 0x518, 0x519, 0x519
};

9
src/dsp/dsp.h Normal file
View File

@@ -0,0 +1,9 @@
class DSP {
public:
virtual uint8 read (uint8 addr) = 0;
virtual void write(uint8 addr, uint8 data) = 0;
virtual void power() = 0;
virtual void reset() = 0;
virtual uint32 run() = 0;
};

View File

@@ -1,3 +1,6 @@
#define BSNES_VERSION "0.013"
#define BSNES_TITLE "bsnes v" BSNES_VERSION
#include "reader/reader.h"
#include "memory/memory.h"
@@ -14,6 +17,10 @@ extern CPU *cpu;
#include "apu/bapuskip/bapuskip.h"
extern APU *apu;
#include "dsp/dsp.h"
#include "dsp/bdsp/bdsp.h"
extern DSP *dsp;
#include "ppu/ppu.h"
#include "ppu/bppu/bppu.h"
extern PPU *ppu;
@@ -21,10 +28,22 @@ extern PPU *ppu;
#include "snes/snes.h"
extern SNES *snes;
#include "chip/srtc/srtc.h"
#include "chip/sdd1/sdd1.h"
extern SRTC *srtc;
extern SDD1 *sdd1;
#include "config/config.h"
#ifdef INTERFACE_MAIN
#include "config/config.cpp"
MemBus *mem_bus;
CPU *cpu;
APU *apu;
DSP *dsp;
PPU *ppu;
SNES *snes;
SRTC *srtc;
SDD1 *sdd1;
#endif

View File

@@ -1,5 +1,5 @@
/*
libbase : version 0.01 ~byuu
libbase : version 0.03 ~byuu (08/20/05)
*/
#ifndef __LIBBASE
@@ -10,10 +10,23 @@
#include <stdarg.h>
#include <string.h>
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#define zerofree(__n) if(__n) { free(__n); __n = 0; }
typedef unsigned int uint;
typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned long ulong;
typedef unsigned char bool8;
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned long uint32;

View File

@@ -1,317 +1,161 @@
#include "libbase.h"
#include "libconfig.h"
//if this function returns true, the option is written
//to the configuration file. by always returning true,
//every option is always written. disable the first line
//to only output options after they have been changed by
//the program from their default values first.
bool config_item::changed() {
return true;
void Setting::toggle() {
data ^= 1;
set(data);
}
if(is_string == true) {
if(!strcmp(*strsource, strdef))return false;
return true;
uint Setting::get() {
return data;
}
void Setting::set(uint _data) {
printf("%s %d\n", name, _data);
data = _data;
if(type != DEC && type != HEX) {
//data is a boolean type
data &= 1;
}
return (*source != def);
}
config_item::config_item() {
strcpy(name, "");
strcpy(strdef, "");
is_string = false;
source = 0;
strsource = 0;
def = 0;
type = 0;
}
void config::add(uint32 *variable, char *name, uint32 def, uint32 type) {
int n;
if(item_count >= 4096)return;
n = item_count;
item[n] = new config_item();
strcpy(item[n]->name, name);
item[n]->is_string = false;
item[n]->def = def;
item[n]->source = variable;
*item[n]->source = item[n]->def;
item[n]->type = type;
item_count++;
}
void config::add(string *variable, char *name, char *def, uint32 type) {
int n;
if(item_count >= 4096)return;
n = item_count;
item[n] = new config_item();
strcpy(item[n]->name, name);
item[n]->is_string = true;
strcpy(item[n]->strdef, def);
item[n]->strsource = variable;
strcpy(*item[n]->strsource, item[n]->strdef);
item[n]->type = type;
item_count++;
}
uint32 config::find(char *name) {
for(int i=0;i<item_count;i++) {
if(!strcmp(item[i]->name, name)) {
return i;
}
Setting::Setting(Config *_parent, char *_name, char *_desc, uint _data, uint _type) {
int s;
if(_parent) {
_parent->add(this);
}
return null;
}
uint32 config::get(char *name) {
int i = find(name);
if(i == null)return 0;
return *item[i]->source;
}
s = strlen(_name);
name = (char*)malloc(s + 1);
strcpy(name, _name);
string &config::strget(char *name) {
int i = find(name);
if(i == null) {
static string not_found;
strcmp(not_found, "");
return not_found;
if(_desc) {
s = strlen(_desc);
desc = (char*)malloc(s + 1);
strcpy(desc, _desc);
} else {
desc = (char*)malloc(1);
*desc = 0;
}
return *item[i]->strsource;
data = _data;
def = _data;
type = _type;
}
void config::set(char *name, uint32 value) {
int i = find(name);
if(i == null)return;
*item[i]->source = value;
void Config::add(Setting *setting) {
list[list_count++] = setting;
}
void config::set(char *name, char *value) {
int i = find(name);
if(i == null)return;
strcpy(*item[i]->strsource, value);
uint Config::string_to_uint(uint type, char *input) {
if(!strcmp(input, "true") ||
!strcmp(input, "enabled") ||
!strcmp(input, "on") ||
!strcmp(input, "yes")
) {
return (uint)true;
}
if(!strcmp(input, "false") ||
!strcmp(input, "disabled") ||
!strcmp(input, "off") ||
!strcmp(input, "no")
) {
return (uint)false;
}
if(!strbegin(input, "0x")) {
return strhex(input + 2);
}
return strdec(input);
}
void config::load(char *fn) {
char *Config::uint_to_string(uint type, uint input) {
static char output[512];
switch(type) {
case Setting::TRUE_FALSE:
sprintf(output, "%s", (input & 1)?"true":"false");
break;
case Setting::ENABLED_DISABLED:
sprintf(output, "%s", (input & 1)?"enabled":"disabled");
break;
case Setting::ON_OFF:
sprintf(output, "%s", (input & 1)?"on":"off");
break;
case Setting::YES_NO:
sprintf(output, "%s", (input & 1)?"yes":"no");
break;
case Setting::DEC:
sprintf(output, "%d", input);
break;
case Setting::HEX:
sprintf(output, "0x%x", input);
break;
}
return output;
}
bool Config::load(char *fn) {
FILE *fp;
char *buffer;
int i, fsize;
uint32 l;
fp = fopen(fn, "rb");
//file doesn't exist yet, do nothing
if(!fp)return;
if(!fp)return false;
//load the config file into memory
fseek(fp, 0, SEEK_END);
fsize = ftell(fp);
int fsize = ftell(fp);
fseek(fp, 0, SEEK_SET);
//blank file, do nothing
if(fsize == 0) {
fclose(fp);
return;
}
buffer = (char*)malloc(fsize + 1);
char *buffer = (char*)malloc(fsize + 1);
fread(buffer, 1, fsize, fp);
fclose(fp);
buffer[fsize] = 0;
*(buffer + fsize) = 0;
strcpy(data, buffer);
free(buffer);
replace(data, "\r\n", "\n");
qreplace(data, "\t", " ");
//split the file into lines
replace(data, "\r\n", "\n");
qreplace(data, "\t", "");
qreplace(data, " ", "");
split(line, "\n", data);
for(i=0;i<count(line);i++) {
qreplace(line[i], " ", "");
//remove comment, if it exists
if(strqpos(line[i], "#") != null) {
strset(line[i], strqpos(line[i], "#"), 0);
}
//ignore blank lines
for(int i=0;i<count(line);i++) {
if(strlen(line[i]) == 0)continue;
if(*strptr(line[i]) == '#')continue;
qsplit(part, "=", line[i]);
l = find(*part[0]);
if(l != null) {
//if the config item name is valid... update item value
if(item[l]->is_string == true) {
strunquote(part[1]);
strcpy(*item[l]->strsource, part[1]);
} else {
if(!strcmp(part[1], "true") || !strcmp(part[1], "yes") ||
!strcmp(part[1], "on") || !strcmp(part[1], "enabled")) {
*item[l]->source = 1;
} else if(!strcmp(part[1], "false") || !strcmp(part[1], "no") ||
!strcmp(part[1], "off") || !strcmp(part[1], "disabled")) {
*item[l]->source = 0;
} else if(item[l]->type == HEX) {
*item[l]->source = strhex(*part[1] + 2); //skip 0x prefix
} else { /* fall back on DEC */
*item[l]->source = strdec(part[1]);
}
split(part, "=", line[i]);
for(int l=0;l<list_count;l++) {
if(!strcmp(list[l]->name, part[0])) {
list[l]->set(string_to_uint(list[l]->type, strptr(part[1])));
}
}
}
}
//create a text string from config item[i] to be output via config->save()
void config::set_newline(int i) {
char t[16];
if(item[i]->is_string == true) {
strcpy(newline, item[i]->name);
strcat(newline, " = \"");
strcat(newline, *item[i]->strsource);
strcat(newline, "\"");
} else {
strcpy(newline, item[i]->name);
strcat(newline, " = ");
switch(item[i]->type) {
case TRUEFALSE:
strcat(newline, (*item[i]->source)?"true":"false");
break;
case YESNO:
strcat(newline, (*item[i]->source)?"yes":"no");
break;
case ONOFF:
strcat(newline, (*item[i]->source)?"on":"off");
break;
case ENABLED:
strcat(newline, (*item[i]->source)?"enabled":"disabled");
break;
case HEX:
sprintf(t, "0x%0.2x", *item[i]->source);
strcat(newline, t);
break;
case DEC:
default:
sprintf(t, "%d", *item[i]->source);
strcat(newline, t);
break;
}
}
return true;
}
bool Config::load(substring &fn) { return load(strptr(fn)); }
void config::save(char *fn) {
bool Config::save(char *fn) {
FILE *fp;
int i, fsize;
uint32 l;
char *buffer;
uint8 set[4096];
bool blank = false;
fp = fopen(fn, "rb");
if(!fp) {
blank = true;
} else {
fseek(fp, 0, SEEK_END);
fsize = ftell(fp);
fseek(fp, 0, SEEK_SET);
if(fsize == 0) {
blank = true;
} else {
buffer = (char*)malloc(fsize + 1);
fread(buffer, 1, fsize, fp);
buffer[fsize] = 0;
strcpy(data, buffer);
free(buffer);
}
fclose(fp);
}
fp = fopen(fn, "wb");
//no write access?
if(!fp)return;
if(!fp)return false;
//list of config items. if the item is set in the
//existing config file, then don't test it to see
//if it needs to be written again later on
memset(set, 0, item_count);
if(blank == false) {
for(int i=0;i<list_count;i++) {
strcpy(data, list[i]->desc);
replace(data, "\r\n", "\n");
qreplace(data, "\t", " ");
split(line, "\n", data);
split(oldline, "\n", data);
for(i=0;i<count(line);i++) {
qreplace(line[i], " ", "");
if(strqpos(line[i], "#") != null) {
strset(line[i], strqpos(line[i], "#"), 0);
}
//this line is empty, restore the old line and continue
if(strlen(line[i]) == 0) {
strcpy(line[i], oldline[i]);
continue;
}
qsplit(part, "=", line[i]);
l = find(*part[0]);
if(l == null) {
//invalid item name, restore the old line and continue
strcpy(line[i], oldline[i]);
continue;
}
set[l] = 1;
set_newline(l);
//copy the user comment from the old config file, if it exists
if(strqpos(oldline[i], "#") != null) {
strcat(newline, " ");
strcat(newline, *oldline[i] + strqpos(oldline[i], "#"));
}
strcpy(line[i], newline);
}
//write out the old config file + changes first
for(i=0;i<count(line);) {
fprintf(fp, "%s", *line[i]);
//write a line feed on all lines but the last.
//keeps the file from growing everytime it is saved
if(++i < count(line))fprintf(fp, "\r\n");
split(line, "\n", data);
for(int l=0;l<count(line);l++) {
fprintf(fp, "# %s\r\n", strptr(line[l]));
}
fprintf(fp, "# (default = %s)\r\n", uint_to_string(list[i]->type, list[i]->def));
fprintf(fp, "%s = %s\r\n\r\n", list[i]->name, uint_to_string(list[i]->type, list[i]->data));
}
int lines_written;
for(i=lines_written=0;i<item_count;i++) {
//if the item was written to the file above...
if(set[i] == 1)continue;
//...or if it is still set to the default value,
//then don't output it to the file here
if(item[i]->changed() == false)continue;
set_newline(i);
//prevent a newline from appearing at the top of the file
//when the config file is created for the first time
if(lines_written == 0 && blank == false)fprintf(fp, "\r\n");
fprintf(fp, "%s\r\n", *newline);
lines_written++;
}
fclose(fp);
return true;
}
bool Config::save(substring &fn) { return save(strptr(fn)); }
config::config() {
item_count = 0;
}
config::~config() {
for(int i=0;i<item_count;i++) {
if(item[i])delete(item[i]);
}
Config::Config() {
list_count = 0;
}

View File

@@ -1,5 +1,5 @@
/*
libconfig : version 0.02 ~byuu
libconfig : version 0.05 ~byuu (09/13/05)
*/
#ifndef __LIBCONFIG
@@ -7,45 +7,92 @@
#include "libstring.h"
class config_item {
public:
uint32 *source, def, type;
string *strsource, strdef;
bool is_string;
string name;
bool changed();
config_item();
};
class Config;
//operator= is the /only/ overloaded operator that isn't inherited by derived classes.
//similarly, base constructor/destructors with arguments are not inherited.
//the inclusion of the overloaded virtual function 'toggle' is to allow toggle to call
//the overloaded set() function, if it exists. for some reason, Setting::toggle() calls
//Setting::set() no matter what, even if the derived class defines set()...
//the below macro is a quick way to take care of all of these issues.
//usage example:
// class T : public Setting { public: SettingOperators(T); } t;
// t = 0; // -> t.set(0);
#define SettingOperators(__name) \
inline __name &operator=(const bool _data) { set((uint)_data); return *this; } \
inline __name &operator=(const uint _data) { set(_data); return *this; } \
inline __name &operator=(const uint8 _data) { set(_data); return *this; } \
inline __name &operator=(const uint16 _data) { set(_data); return *this; } \
inline __name &operator=(const uint32 _data) { set(_data); return *this; } \
inline __name &operator=(const int _data) { set(_data); return *this; } \
inline __name &operator=(const int8 _data) { set(_data); return *this; } \
inline __name &operator=(const int16 _data) { set(_data); return *this; } \
inline __name &operator=(const int32 _data) { set(_data); return *this; } \
void toggle() { data ^= 1; set(data); } \
__name(Config *_parent, char *_name, char *_desc = 0, uint _data = 0, uint _type = Setting::DEC) : \
Setting(_parent, _name, _desc, _data, _type) {}
class Setting {
friend class Config;
protected:
uint data, type, def;
class config {
private:
uint32 item_count;
config_item *item[4096];
string data, line, oldline, newline, part;
public:
enum {
TRUEFALSE = 0,
YESNO = 1,
ONOFF = 2,
ENABLED = 3,
DEC = 4,
HEX = 5,
STR = 6
TRUE_FALSE,
ENABLED_DISABLED,
ON_OFF,
YES_NO,
BOOL,
DEC,
HEX
};
void add(uint32 *variable, char *name, uint32 def, uint32 type = DEC);
void add(string *variable, char *name, char *def, uint32 type = STR);
uint32 find(char *name);
uint32 get(char *name);
string &strget(char *name);
void set(char *name, uint32 value);
void set(char *name, char *value);
char *name, *desc;
virtual void toggle();
virtual uint get();
virtual void set(uint _data);
void load(char *fn);
void save(char *fn);
void set_newline(int i);
Setting(Config *_parent, char *_name, char *_desc = 0, uint _data = 0, uint _type = DEC);
config();
~config();
inline operator bool() { return (bool)get(); }
inline operator uint() { return get(); }
inline operator uint8() { return get(); }
inline operator uint16() { return get(); }
inline operator uint32() { return get(); }
inline operator int() { return get(); }
inline operator int8() { return get(); }
inline operator int16() { return get(); }
inline operator int32() { return get(); }
inline Setting &operator=(const bool _data) { set((uint)_data); return *this; }
inline Setting &operator=(const uint _data) { set(_data); return *this; }
inline Setting &operator=(const uint8 _data) { set(_data); return *this; }
inline Setting &operator=(const uint16 _data) { set(_data); return *this; }
inline Setting &operator=(const uint32 _data) { set(_data); return *this; }
inline Setting &operator=(const int _data) { set(_data); return *this; }
inline Setting &operator=(const int8 _data) { set(_data); return *this; }
inline Setting &operator=(const int16 _data) { set(_data); return *this; }
inline Setting &operator=(const int32 _data) { set(_data); return *this; }
};
class Config {
protected:
vector<Setting*> list;
uint list_count;
string data, line, part, subpart;
uint string_to_uint(uint type, char *input);
char *uint_to_string(uint type, uint input);
public:
void add(Setting *setting);
bool load(char *fn);
bool load(substring &fn);
bool save(char *fn);
bool save(substring &fn);
Config();
};
#endif

View File

@@ -1,23 +1,26 @@
#include "libbase.h"
#include "libstring.h"
_string::_string() {
substring::substring() {
size = 16;
s = (char*)malloc(size + 1);
*s = 0;
}
_string::~_string() {
free(s);
}
void string::addto(uint32 num) {
while(listcount < (num + 1)) {
list[listcount++] = new _string();
substring::~substring() {
if(s) {
free(s);
s = 0;
}
}
_string &string::str(uint32 num) {
void string::addto(uint num) {
while(listcount < (num + 1)) {
list[listcount++] = new substring();
}
}
substring &string::str(uint num) {
if(listcount < (num + 1)) { addto(num); }
return *list[num];
}
@@ -31,60 +34,68 @@ string::string() {
string::~string() {
int i;
for(i=listcount-1;i>=0;i--) {
delete((_string*)list[i]);
delete((substring*)list[i]);
}
}
uint32 count(string &str) {
return str.count;
char chrlower(char c) {
if(c >= 'A' && c <= 'Z')return c + ('a' - 'A');
return c;
}
void strresize(_string &str, uint32 size) {
char *t;
int sl;
if(str.size == size)return;
sl = strlen(str.s);
t = (char*)malloc(size + 1);
strcpy(t, str.s);
free(str.s);
str.s = t;
char chrupper(char c) {
if(c >= 'a' && c <= 'z')return c - ('a' - 'A');
return c;
}
uint count(string &str) { return str.count; }
void strresize(substring &str, uint size) {
str.s = (char*)realloc(str.s, size + 1);
str.s[size] = 0;
str.size = size;
}
char *strptr(_string &str) {
return str.s;
}
char *strptr(substring &str) { return str.s; }
void strcpy(_string &dest, char *src) {
uint strlen(substring &str) { return strlen(strptr(str)); }
int strcmp(substring &dest, const char *src) { return strcmp(strptr(dest), src); }
int strcmp(const char *dest, substring &src) { return strcmp(dest, strptr(src)); }
int strcmp(substring &dest, substring &src) { return strcmp(strptr(dest), strptr(src)); }
void strcpy(substring &dest, const char *src) {
int srclen = strlen(src);
if(srclen > dest.size) { strresize(dest, srclen); }
strcpy(dest.s, src);
}
void strcpy(substring &dest, substring &src) { strcpy(dest, strptr(src)); }
void strset(_string &dest, uint32 pos, uint8 c) {
void strset(substring &dest, uint pos, uint8 c) {
char *s;
if(pos > dest.size) { strresize(dest, pos); }
dest.s[pos] = c;
}
void strcat(_string &dest, char *src) {
void strcat(substring &dest, const char *src) {
int srclen, destlen;
srclen = strlen(src);
destlen = strlen(dest.s);
if(srclen + destlen > dest.size) { strresize(dest, srclen + destlen); }
strcat(dest.s, src);
}
void strcat(substring &dest, substring &src) { strcat(dest, strptr(src)); }
void strinsert(_string &dest, char *src, uint32 pos) {
_string *s = new _string();
strcpy(*s, strptr(dest) + pos);
void strinsert(substring &dest, const char *src, uint pos) {
static substring s;
strcpy(s, strptr(dest) + pos);
strset(dest, pos, 0);
strcat(dest, src);
strcat(dest, *s);
delete(s);
strcat(dest, s);
}
void strinsert(substring &dest, substring &src, uint pos) { strinsert(dest, strptr(src), pos); }
void strremove(_string &dest, uint32 start, uint32 length) {
void strremove(substring &dest, uint start, uint length) {
int destlen;
char *s;
int i, sl = strlen(dest.s);
@@ -98,36 +109,35 @@ int i, sl = strlen(dest.s);
s[i] = 0;
}
bool stricmp(char *dest, char *src) {
int i, sl = strlen(dest);
if(sl != strlen(src))return false;
for(i=0;i<sl;i++) {
if(dest[i] >= 'A' && dest[i] <= 'Z') {
if(dest[i] != src[i] && dest[i] + 0x20 != src[i])return false;
} else if(dest[i] >='a' && dest[i] <= 'z') {
if(dest[i] != src[i] && dest[i] - 0x20 != src[i])return false;
} else {
if(dest[i] != src[i])return false;
}
int __stricmp(const char *dest, const char *src) {
while(*dest && *src) {
if(chrlower(*dest) != chrlower(*src))break;
dest++;
src++;
}
return true;
return (int)chrlower(*dest) - (int)chrlower(*src);
}
int stricmp(substring &dest, const char *src) { return __stricmp(strptr(dest), src); }
int stricmp(const char *dest, substring &src) { return __stricmp(dest, strptr(src)); }
int stricmp(substring &dest, substring &src) { return __stricmp(strptr(dest), strptr(src)); }
void strlower(char *str) {
int i, sl = strlen(str);
for(i=0;i<sl;i++) {
if(str[i] >= 'A' && str[i] <= 'Z')str[i] += 0x20;
while(*str) {
*str = chrlower(*str);
str++;
}
}
void strlower(substring &str) { strlower(strptr(str)); }
void strupper(char *str) {
int i, sl = strlen(str);
for(i=0;i<sl;i++) {
if(str[i] >= 'a' && str[i] <= 'z')str[i] -= 0x20;
while(*str) {
*str = chrupper(*str);
str++;
}
}
void strupper(substring &str) { strupper(strptr(str)); }
uint32 strpos(char *str, char *key) {
uint strpos(const char *str, const char *key) {
int i, ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl)return null;
for(i=0;i<=ssl-ksl;i++) {
@@ -135,8 +145,11 @@ int i, ssl = strlen(str), ksl = strlen(key);
}
return null;
}
uint strpos(substring &str, const char *key) { return strpos(strptr(str), key); }
uint strpos(const char *str, substring &key) { return strpos(str, strptr(key)); }
uint strpos(substring &str, substring &key) { return strpos(strptr(str), strptr(key)); }
uint32 strqpos(char *str, char *key) {
uint qstrpos(const char *str, const char *key) {
int i, z, ssl = strlen(str), ksl = strlen(key);
uint8 x;
if(ksl > ssl)return null;
@@ -155,8 +168,11 @@ uint8 x;
}
return null;
}
uint qstrpos(substring &str, const char *key) { return qstrpos(strptr(str), key); }
uint qstrpos(const char *str, substring &key) { return qstrpos(str, strptr(key)); }
uint qstrpos(substring &str, substring &key) { return qstrpos(strptr(str), strptr(key)); }
void strtr(char *dest, char *before, char *after) {
void strtr(char *dest, const char *before, const char *after) {
int i, l, sl = strlen(dest), bsl = strlen(before), asl = strlen(after);
if((bsl != asl) || bsl == 0)return;
for(i=0;i<sl;i++) {
@@ -165,15 +181,17 @@ int i, l, sl = strlen(dest), bsl = strlen(before), asl = strlen(after);
}
}
}
void strtr(substring &dest, const char *before, const char *after) { strtr(strptr(dest), before, after); }
uint32 strbegin(char *str, char *key) {
uint strbegin(const char *str, const char *key) {
int i, ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl)return 1;
if(!memcmp(str, key, ksl))return 0;
return 1;
}
uint strbegin(substring &str, const char *key) { return strbegin(strptr(str), key); }
uint32 stribegin(char *str, char *key) {
uint stribegin(const char *str, const char *key) {
int i, ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl)return 1;
for(i=0;i<ksl;i++) {
@@ -187,15 +205,17 @@ int i, ssl = strlen(str), ksl = strlen(key);
}
return 0;
}
uint stribegin(substring &str, const char *key) { return stribegin(strptr(str), key); }
uint32 strend(char *str, char *key) {
uint strend(const char *str, const char *key) {
int i, ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl)return 1;
if(!memcmp(str + ssl - ksl, key, ksl))return 0;
return 1;
}
uint strend(substring &str, const char *key) { return strend(strptr(str), key); }
uint32 striend(char *str, char *key) {
uint striend(const char *str, const char *key) {
int i, z, ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl)return 1;
for(i=ssl-ksl, z=0;i<ssl;i++, z++) {
@@ -209,8 +229,9 @@ int i, z, ssl = strlen(str), ksl = strlen(key);
}
return 0;
}
uint striend(substring &str, const char *key) { return striend(strptr(str), key); }
void strltrim(char *str, char *key) {
void strltrim(char *str, const char *key) {
int i, ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl)return;
if(!strbegin(str, key)) {
@@ -218,8 +239,9 @@ int i, ssl = strlen(str), ksl = strlen(key);
str[i] = 0;
}
}
void strltrim(substring &str, const char *key) { strltrim(strptr(str), key); }
void striltrim(char *str, char *key) {
void striltrim(char *str, const char *key) {
int i, ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl)return;
if(!stribegin(str, key)) {
@@ -227,25 +249,28 @@ int i, ssl = strlen(str), ksl = strlen(key);
str[i] = 0;
}
}
void striltrim(substring &str, const char *key) { striltrim(strptr(str), key); }
void strrtrim(char *str, char *key) {
void strrtrim(char *str, const char *key) {
int ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl)return;
if(!strend(str, key)) {
str[ssl - ksl] = 0;
}
}
void strrtrim(substring &str, const char *key) { strrtrim(strptr(str), key); }
void strirtrim(char *str, char *key) {
void strirtrim(char *str, const char *key) {
int ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl)return;
if(!striend(str, key)) {
str[ssl - ksl] = 0;
}
}
void strirtrim(substring &str, const char *key) { strirtrim(strptr(str), key); }
/* does not work on type char* because function increases string length */
void strquote(_string &str) {
//does not work on type char* because function increases string length
void strquote(substring &str) {
static string t;
strcpy(t, "\"");
strcat(t, str);
@@ -255,15 +280,15 @@ static string t;
bool strunquote(char *str) {
int i, ssl = strlen(str);
/* make sure string is long enough to have quotes */
//make sure string is long enough to have quotes
if(ssl < 2)return false;
/* make sure string actually has quotes */
//make sure string actually has quotes
if(str[0] == '\"' && str[ssl - 1] == '\"');
else if(str[0] == '\'' && str[ssl - 1] == '\'');
else return false;
/* now remove them */
//now remove them
for(i=0;i<ssl;i++) {
str[i] = str[i + 1];
}
@@ -271,9 +296,10 @@ int i, ssl = strlen(str);
return true;
}
bool strunquote(substring &str) { return strunquote(strptr(str)); }
uint32 strhex(char *str) {
uint32 r = 0, m = 0;
uint strhex(const char *str) {
uint r = 0, m = 0;
int i, ssl = strlen(str);
uint8 x;
for(i=0;i<ssl;i++) {
@@ -292,31 +318,44 @@ uint8 x;
}
return r;
}
uint strhex(substring &str) { return strhex(strptr(str)); }
int strdec(char *str) {
uint32 m = 1;
int sstrhex(const char *str) {
if(str[0] == '-') {
return -strhex(str + 1);
}
return strhex(str);
}
int sstrhex(substring &str) { return sstrhex(strptr(str)); }
uint strdec(const char *str) {
uint m = 1;
int i, r = 0, ssl = strlen(str);
uint8 x;
for(i=0;i<ssl;i++) {
if(str[i] >= '0' && str[i] <= '9');
else if(str[i] == '-' && i == 0);
else break;
}
for(--i;i>=0;i--, m*=10) {
x = str[i];
if(x >= '0' && x <= '9')x -= '0';
else if(i == 0 && str[i] == '-') {
r *= -1;
return r;
}
else return r;
r += x * m;
}
return r;
}
uint strdec(substring &str) { return strdec(strptr(str)); }
uint32 strbin(char *str) {
uint32 r = 0, m = 0;
int sstrdec(const char *str) {
if(str[0] == '-') {
return -strdec(str + 1);
}
return strdec(str);
}
int sstrdec(substring &str) { return sstrdec(strptr(str)); }
uint strbin(const char *str) {
uint r = 0, m = 0;
int i, ssl = strlen(str);
uint8 x;
for(i=0;i<ssl;i++) {
@@ -331,6 +370,15 @@ uint8 x;
}
return r;
}
uint strbin(substring &str) { return strbin(strptr(str)); }
int sstrbin(const char *str) {
if(str[0] == '-') {
return -strbin(str + 1);
}
return strbin(str);
}
int sstrbin(substring &str) { return sstrbin(strptr(str)); }
#include "libstring_math.cpp"
#include "libstring_split.cpp"

View File

@@ -1,5 +1,5 @@
/*
libstring : version 0.05 ~byuu
libstring : version 0.06a ~byuu (08/22/05)
*/
#ifndef __LIBSTRING
@@ -8,247 +8,172 @@
#include "libvector.h"
class string;
class _string;
class substring;
uint32 count(string &str);
void strresize(_string &str, uint32 size);
char* strptr(_string &str);
void strcpy(_string &dest, char *src);
void strset(_string &dest, uint32 pos, uint8 c);
void strcat(_string &dest, char *src);
void strinsert(_string &dest, char *src, uint32 pos);
void strremove(_string &dest, uint32 start, uint32 length = 0);
bool stricmp(char *dest, char *src);
void strlower(char *str);
void strupper(char *str);
uint32 strpos(char *str, char *key);
uint32 strqpos(char *str, char *key);
void strtr(char *dest, char *before, char *after);
uint32 strbegin(char *str, char *key);
uint32 stribegin(char *str, char *key);
uint32 strend(char *str, char *key);
uint32 striend(char *str, char *key);
void strltrim(char *str, char *key);
void striltrim(char *str, char *key);
void strrtrim(char *str, char *key);
void strirtrim(char *str, char *key);
void strquote(_string &str);
bool strunquote(char *str);
uint32 strhex(char *str);
int strdec(char *str);
uint32 strbin(char *str);
uint32 strmath(char *in_str);
uint32 strmathentity(char *str);
void replace (_string &str, char *key, char *token);
void qreplace(_string &str, char *key, char *token);
void split (string &dest, char *key, char *src);
void qsplit(string &dest, char *key, char *src);
void sprintf(_string &str, char *s, ...);
char chrlower(char c);
char chrupper(char c);
class _string {
uint count(string &str);
void strresize(substring &str, uint size);
char* strptr(substring &str);
uint strlen(substring &str);
int strcmp(substring &dest, const char *src);
int strcmp(const char *dest, substring &src);
int strcmp(substring &dest, substring &src);
void strcpy(substring &dest, const char *src);
void strcpy(substring &dest, substring &src);
void strset(substring &dest, uint pos, uint8 c);
void strcat(substring &dest, const char *src);
void strcat(substring &dest, substring &src);
void strinsert(substring &dest, const char *src, uint pos);
void strinsert(substring &dest, substring &src, uint pos);
void strremove(substring &dest, uint start, uint length = 0);
//vc6/win32 and gcc/dos only support stricmp, whereas
//gcc/unix only supports strcasecmp. this is an attempt
//to avoid platform-specific defines...
#define stricmp __stricmp
int __stricmp(const char *dest, const char *src);
int stricmp(substring &dest, const char *src);
int stricmp(const char *dest, substring &src);
int stricmp(substring &dest, substring &src);
void strlower(char *str);
void strlower(substring &str);
void strupper(char *str);
void strupper(substring &str);
uint strpos(const char *str, const char *key);
uint strpos(substring &str, const char *key);
uint strpos(const char *str, substring &key);
uint strpos(substring &str, substring &key);
uint qstrpos(const char *str, const char *key);
uint qstrpos(substring &str, const char *key);
uint qstrpos(const char *str, substring &key);
uint qstrpos(substring &str, substring &key);
void strtr(char *dest, const char *before, const char *after);
void strtr(substring &dest, const char *before, const char *after);
uint strbegin(const char *str, const char *key);
uint strbegin(substring &str, const char *key);
uint stribegin(const char *str, const char *key);
uint stribegin(substring &str, const char *key);
uint strend(const char *str, const char *key);
uint strend(substring &str, const char *key);
uint striend(const char *str, const char *key);
uint striend(substring &str, const char *key);
void strltrim(char *str, const char *key);
void strltrim(substring &str, const char *key);
void striltrim(char *str, const char *key);
void striltrim(substring &str, const char *key);
void strrtrim(char *str, const char *key);
void strrtrim(substring &str, const char *key);
void strirtrim(char *str, const char *key);
void strirtrim(substring &str, const char *key);
void strquote(substring &str);
bool strunquote(char *str);
bool strunquote(substring &str);
uint strhex(const char *str);
uint strhex(substring &str);
int sstrhex(const char *str);
int sstrhex(substring &str);
uint strdec(const char *str);
uint strdec(substring &str);
int sstrdec(const char *str);
int sstrdec(substring &str);
uint strbin(const char *str);
uint strbin(substring &str);
int sstrbin(const char *str);
int sstrbin(substring &str);
uint strmath(const char *in_str);
uint strmath(substring &in_str);
bool strmathentity(const char *str);
bool strmathentity(substring &str);
void replace(substring &str, const char *key, const char *token);
void replace(substring &str, const char *key, substring &token);
void qreplace(substring &str, const char *key, const char *token);
void qreplace(substring &str, const char *key, substring &token);
void split(string &dest, const char *key, char *src);
void split(string &dest, const char *key, substring &src);
void qsplit(string &dest, const char *key, char *src);
void qsplit(string &dest, const char *key, substring &src);
void sprintf(substring &str, const char *s, ...);
class substring {
public:
char *s;
uint32 size;
inline char* operator*() { return s; }
uint size;
//inline char* operator*() { return s; }
//inline operator char*() { return s; }
#ifdef __LIBSTRING_OVERLOADS
inline _string& operator=(char *cpy);
inline _string& operator=(_string &cpy);
inline _string& operator=(string &cpy);
inline _string& operator+=(char *cat);
inline _string& operator+=(_string &cat);
inline _string& operator+=(string &cat);
inline _string& operator-=(char *cut);
inline _string& operator-=(_string &cut);
inline _string& operator-=(string &cut);
inline bool operator==(char *cmp);
inline bool operator==(_string &cmp);
inline bool operator==(string &cmp);
inline bool operator!=(char *cmp);
inline bool operator!=(_string &cmp);
inline bool operator!=(string &cmp);
#endif
inline operator char*() { return s; }
_string();
~_string();
substring();
~substring();
};
/* listcount is the actual size of list[], used for allocation
* of substrings.
* count is used by split() and count() to return the number of
* active/valid substrings. example: if string T contains 16
* substrings, and split is called, which sets three substrings,
* then count() needs to reflect that only three substrings are
* now used, but listcount still needs to reflect the true size
* of list[].
*/
class string {
public:
vector<_string*> list;
uint32 count, listcount;
void addto(uint32 num); //creates all needed strings to make list[num] valid
_string &str(uint32 num); //gets a _string reference, creating it + new strings if needed
vector<substring*> list;
uint listcount, count;
void addto(uint num); //creates all needed strings to make list[num] valid
substring &str(uint num); //gets a substring reference, creating it + new strings if needed
inline char* operator*() { return strptr(str(0)); }
#ifdef __LIBSTRING_OVERLOADS
inline string& operator=(char *cpy);
inline string& operator=(_string &cpy);
inline string& operator=(string &cpy);
inline string& operator+=(char *cat);
inline string& operator+=(_string &cat);
inline string& operator+=(string &cat);
inline string& operator-=(char *cut);
inline string& operator-=(_string &cut);
inline string& operator-=(string &cut);
inline bool operator==(char *cmp);
inline bool operator==(_string &cmp);
inline bool operator==(string &cmp);
inline bool operator!=(char *cmp);
inline bool operator!=(_string &cmp);
inline bool operator!=(string &cmp);
#endif
inline operator char*() { return str(0).s; }
inline operator _string&() { return str(0); }
inline _string& operator[](uint32 i) { return str(i); }
inline _string& operator[](int i) { return str(i); }
//inline char* operator*() { return strptr(str(0)); }
//inline operator char*() { return str(0).s; }
inline operator substring&() { return str(0); }
inline substring& operator[](uint i) { return str(i); }
inline substring& operator[](uint8 i) { return str(i); }
inline substring& operator[](uint16 i) { return str(i); }
inline substring& operator[](uint32 i) { return str(i); }
inline substring& operator[](int i) { return str(i); }
inline substring& operator[](int8 i) { return str(i); }
inline substring& operator[](int16 i) { return str(i); }
inline substring& operator[](int32 i) { return str(i); }
string();
~string();
};
#ifdef __LIBSTRING_OVERLOADS
inline _string& _string::operator=(char *cpy) {
strcpy(*this, cpy);
return *this;
}
inline _string& _string::operator=(_string &cpy) {
strcpy(*this, cpy);
return *this;
}
inline _string& _string::operator=(string &cpy) {
strcpy(*this, cpy.str(0));
return *this;
}
inline string& string::operator=(char *cpy) {
strcpy(str(0), cpy);
return *this;
}
inline string& string::operator=(_string &cpy) {
strcpy(str(0), cpy);
return *this;
}
inline string& string::operator=(string &cpy) {
strcpy(str(0), cpy.str(0));
return *this;
}
inline _string& _string::operator+=(char *cat) {
strcat(*this, cat);
return *this;
}
inline _string& _string::operator+=(_string &cat) {
strcat(*this, cat);
return *this;
}
inline _string& _string::operator+=(string &cat) {
strcat(*this, cat.str(0));
return *this;
}
inline string& string::operator+=(char *cat) {
strcat(str(0), cat);
return *this;
}
inline string& string::operator+=(_string &cat) {
strcat(str(0), cat);
return *this;
}
inline string& string::operator+=(string &cat) {
strcat(str(0), cat.str(0));
return *this;
}
inline _string& _string::operator-=(char *cut) {
strrtrim(*this, cut);
return *this;
}
inline _string& _string::operator-=(_string &cut) {
strrtrim(*this, cut);
return *this;
}
inline _string& _string::operator-=(string &cut) {
strrtrim(*this, cut.str(0));
return *this;
}
inline string& string::operator-=(char *cut) {
strrtrim(str(0), cut);
return *this;
}
inline string& string::operator-=(_string &cut) {
strrtrim(str(0), cut);
return *this;
}
inline string& string::operator-=(string &cut) {
strrtrim(str(0), cut.str(0));
return *this;
}
inline bool _string::operator==(char *cmp) {
if(!strcmp(*this, cmp))return true;
return false;
}
inline bool _string::operator==(_string &cmp) {
if(!strcmp(*this, cmp))return true;
return false;
}
inline bool _string::operator==(string &cmp) {
if(!strcmp(*this, cmp.str(0)))return true;
return false;
}
inline bool string::operator==(char *cmp) {
if(!strcmp(str(0), cmp))return true;
return false;
}
inline bool string::operator==(_string &cmp) {
if(!strcmp(str(0), cmp))return true;
return false;
}
inline bool string::operator==(string &cmp) {
if(!strcmp(str(0), cmp.str(0)))return true;
return false;
}
inline bool _string::operator!=(char *cmp) {
if(!strcmp(*this, cmp))return false;
return true;
}
inline bool _string::operator!=(_string &cmp) {
if(!strcmp(*this, cmp))return false;
return true;
}
inline bool _string::operator!=(string &cmp) {
if(!strcmp(*this, cmp.str(0)))return false;
return true;
}
inline bool string::operator!=(char *cmp) {
if(!strcmp(str(0), cmp))return false;
return true;
}
inline bool string::operator!=(_string &cmp) {
if(!strcmp(str(0), cmp))return false;
return true;
}
inline bool string::operator!=(string &cmp) {
if(!strcmp(str(0), cmp.str(0)))return false;
return true;
}
#endif //__LIBSTRING_OVERLOADS
#endif //__LIBSTRING

View File

@@ -43,10 +43,10 @@ strmath(str)
str, and returns numerical result
example: strmath("5+5")=10
***************************************/
uint32 p_strmath(char *str) {
int i = 0, ssl = strlen(str);
uint p_strmath(const char *str) {
int i = 0, ssl = strlen(str);
uint r, array[128], array_size = 0, z = 0;
uint8 x, mode = 0;
uint32 r, array[128], array_size = 0, z = 0;
uint8 array_gate[128];
char *s1;
if(!ssl)return 0;
@@ -95,11 +95,11 @@ char *s1;
return r;
}
uint32 strmath(char *in_str) {
uint32 r = 0;
uint32 pdepth = 0, cpdepth, maxpdepth = 0;
uint32 pstart, pend, spos;
int i, sc, sl = strlen(in_str);
uint strmath(const char *in_str) {
uint r = 0;
uint pdepth = 0, cpdepth, maxpdepth = 0;
uint pstart, pend, spos;
int i, sc, sl = strlen(in_str);
char *str = (char*)malloc(sl + 1), *str0;
char *pstr;
char num[64];
@@ -165,13 +165,15 @@ char num[64];
free(str);
return r;
}
uint strmath(substring &in_str) { return strmath(strptr(in_str)); }
uint32 strmathentity(char *str) {
bool strmathentity(const char *str) {
int i, ssl = strlen(str);
for(i=0;i<ssl;i++) {
if(str[i] == '+' || str[i] == '-' || str[i] == '*' || str[i] == '/' ||
str[i] == '%' || str[i] == '&' || str[i] == '|' || str[i] == '^' ||
(str[i] == '<' && str[i+1] == '<') || (str[i] == '>' && str[i+1] == '>'))return 1;
(str[i] == '<' && str[i+1] == '<') || (str[i] == '>' && str[i+1] == '>'))return true;
}
return 0;
return false;
}
bool strmathentity(substring &str) { return strmathentity(strptr(str)); }

View File

@@ -1,6 +1,6 @@
void replace(_string &str, char *key, char *token) {
int i, z, ksl = strlen(key), tsl = strlen(token), ssl = strlen(str);
uint32 replace_count = 0, size = ssl;
void replace(substring &str, const char *key, const char *token) {
int i, z, ksl = strlen(key), tsl = strlen(token), ssl = strlen(str);
uint replace_count = 0, size = ssl;
char *data;
if(ksl > ssl)return;
if(tsl > ksl) { //the new string may be longer than the old string...
@@ -27,11 +27,12 @@ char *data;
strcpy(str, data);
free(data);
}
void replace(substring &str, const char *key, substring &token) { replace(str, key, strptr(token)); }
void qreplace(_string &str, char *key, char *token) {
int i, l, z, ksl = strlen(key), tsl = strlen(token), ssl = strlen(str);
void qreplace(substring &str, const char *key, const char *token) {
int i, l, z, ksl = strlen(key), tsl = strlen(token), ssl = strlen(str);
uint replace_count = 0, size = ssl;
uint8 x;
uint32 replace_count = 0, size = ssl;
char *data;
if(ksl > ssl)return;
if(tsl > ksl) {
@@ -80,3 +81,4 @@ char *data;
strcpy(str, data);
free(data);
}
void qreplace(substring &str, const char *key, substring &token) { qreplace(str, key, strptr(token)); }

View File

@@ -1,7 +1,7 @@
void split(string &dest, char *key, char *src) {
int i, ssl = strlen(src), ksl = strlen(key);
void split(string &dest, const char *key, char *src) {
int i, ssl = strlen(src), ksl = strlen(key);
uint lp = 0, split_count = 0;
uint8 x;
uint32 lp = 0, split_count = 0;
for(i=0;i<=ssl-ksl;) {
if(!memcmp(src + i, key, ksl)) {
x = src[i];
@@ -15,11 +15,12 @@ uint32 lp = 0, split_count = 0;
strcpy(dest[split_count++], src + lp);
dest.count = split_count;
}
void split(string &dest, const char *key, substring &src) { split(dest, key, strptr(src)); }
void qsplit(string &dest, char *key, char *src) {
int i, z, ssl = strlen(src), ksl = strlen(key);
void qsplit(string &dest, const char *key, char *src) {
int i, z, ssl = strlen(src), ksl = strlen(key);
uint lp = 0, split_count = 0;
uint8 x;
uint32 lp = 0, split_count = 0;
for(i=0;i<=ssl-ksl;) {
x = src[i];
if(x=='\"' || x=='\'') {
@@ -39,3 +40,4 @@ uint32 lp = 0, split_count = 0;
strcpy(dest[split_count++], src + lp);
dest.count = split_count;
}
void qsplit(string &dest, const char *key, substring &src) { qsplit(dest, key, strptr(src)); }

View File

@@ -1,5 +1,5 @@
void numtobin(char *s, uint32 num) {
uint32 mask = 0x80000000, len = 0, z = 0;
void numtobin(char *s, uint num) {
uint mask = 0x80000000, len = 0, z = 0;
for(;mask;mask>>=1,len++) { if(num&mask)break; }
len = 32 - len;
do {
@@ -9,7 +9,7 @@ uint32 mask = 0x80000000, len = 0, z = 0;
s[z] = 0;
}
void sprintf(_string &str, char *s, ...) {
void sprintf(substring &str, const char *s, ...) {
va_list args;
char t[2], n[256];
int i, l, sl, z;
@@ -17,7 +17,7 @@ uint8 pad_type, pad_len;
uint32 num;
char *r;
va_start(args, s);
strcpy(*str, "");
strcpy(str, "");
for(i=0;i<strlen(s);i++) {
if(s[i] == '%') {
i++;

View File

@@ -1,65 +1,70 @@
/*
libvector : version 0.01 ~byuu
libvector : version 0.03 ~byuu (08/16/05)
*/
#ifndef __LIBVECTOR
#define __LIBVECTOR
template<typename T> class vector {
public:
private:
T *array;
int size, sizelimit;
//find next array size that is a power of two
int findsize(int newsize) {
int r = 1;
while(r >= 1) {
r <<= 1;
if(r > sizelimit)return sizelimit;
if(r >= newsize)return r;
if(r > sizelimit)return sizelimit;
if(r >= newsize) return r;
}
return size;
}
public:
void resize(int newsize) {
T *newarray;
newsize = findsize(newsize);
if(newsize > sizelimit)newsize = sizelimit;
if(newsize == size)return;
newarray = (T*)malloc(sizeof(T) * newsize);
if(newsize >= size) {
memcpy(newarray, array, sizeof(T) * size);
} else {
memcpy(newarray, array, sizeof(T) * newsize);
}
free(array);
array = (T*)realloc(array, sizeof(T) * newsize);
array = newarray;
size = newsize;
if(newsize > size) {
for(int i=size;i<newsize;i+=sizeof(T)) {
array[i] = (T)0;
}
}
size = newsize;
}
vector(int newsize, int newsizelimit) {
size = newsize;
sizelimit = newsizelimit;
array = (T*)malloc(sizeof(T) * size);
array = (T*)calloc(size, sizeof(T));
}
vector(int newsize) {
size = newsize;
sizelimit = 1 << 24;
array = (T*)malloc(sizeof(T) * size);
array = (T*)calloc(size, sizeof(T));
}
vector() {
size = 16;
sizelimit = 1 << 24;
array = (T*)malloc(sizeof(T) * size);
array = (T*)calloc(size, sizeof(T));
}
~vector() {
if(array)free(array);
if(array) {
free(array);
array = 0;
}
}
//operator T() { return array[0]; }
inline T &operator[](int index) {
if(index >= size)resize(index + 1);
if(index > sizelimit)return array[size - 1];

View File

@@ -6,7 +6,7 @@ public:
uint8 *rom, *sram;
uint32 rom_size, sram_size;
uint8 read (uint32 addr);
void write(uint32 addr, byte value);
void write(uint32 addr, uint8 value);
void write_protect(bool r);
void set_cartinfo(CartInfo *ci);

View File

@@ -0,0 +1,106 @@
void bCartExLoROM::write_protect(bool r) { write_protected = r; }
uint8 bCartExLoROM::read(uint32 addr) {
uint32 b, w;
addr &= 0xffffff;
b = (addr >> 16);
w = (addr & 0xffff);
//SRAM Region A
if((b & 0x7f) >= 0x30 && (b & 0x7f) <= 0x3f && (w & 0xe000) == 0x6000) {
b &= 0x7f;
if(b >= 0x30 && b <= 0x3f) {
if(sram_size) {
addr = (b - 0x30) * 0x2000 + (w - 0x6000);
addr &= (sram_size - 1);
return sram[addr];
} else {
return 0x00; //no SRAM available
}
} else {
return 0x00; //unmapped
}
}
//SRAM Region B
if(b >= 0x70 && b <= 0x7d) {
if(sram_size) {
addr = (addr & 0xffffff) - 0x700000;
addr &= (sram_size - 1);
return sram[addr];
} else {
return 0x00; //no SRAM available
}
}
if(b <= 0x3f) {
addr = (b << 15) | (addr & 0x7fff);
} else if(b <= 0x7f) {
addr &= 0x3fffff;
} else if(b <= 0xbf) {
b &= 0x7f;
addr = (b << 15) | (addr & 0x7fff);
} else {
addr = sdd1->offset(addr);
}
if(addr < rom_size)return rom[addr];
return 0x00;
}
void bCartExLoROM::write(uint32 addr, uint8 value) {
uint32 b, w;
addr &= 0xffffff;
b = (addr >> 16);
w = (addr & 0xffff);
//SRAM Region A
if((b & 0x7f) >= 0x30 && (b & 0x7f) <= 0x3f && (w & 0xe000) == 0x6000) {
b &= 0x7f;
if(b >= 0x30 && b <= 0x3f) {
if(sram_size) {
addr = (b - 0x30) * 0x2000 + (w - 0x6000);
addr &= (sram_size - 1);
sram[addr] = value;
return;
} else {
return; //no SRAM available
}
} else {
return; //unmapped
}
}
//SRAM Region B
if(b >= 0x70 && b <= 0x7d) {
if(sram_size) {
addr = (addr & 0xffffff) - 0x700000;
addr &= (sram_size - 1);
sram[addr] = value;
} else {
return; //no SRAM available
}
}
if(write_protected == true)return;
if(b <= 0x3f) {
addr = (b << 15) | (addr & 0x7fff);
} else if(b <= 0x7f) {
addr &= 0x3fffff;
} else if(b <= 0xbf) {
b &= 0x7f;
addr = (b << 15) | (addr & 0x7fff);
} else {
addr = sdd1->offset(addr);
}
if(addr < rom_size)rom[addr] = value;
}
void bCartExLoROM::set_cartinfo(CartInfo *ci) {
rom = ci->rom;
sram = ci->sram;
rom_size = ci->rom_size;
sram_size = ci->sram_size;
}

View File

@@ -0,0 +1,14 @@
class bCartExLoROM : public Cart {
private:
bool write_protected;
public:
uint8 *rom, *sram;
uint32 rom_size, sram_size;
uint8 read (uint32 addr);
void write(uint32 addr, uint8 value);
void write_protect(bool r);
void set_cartinfo(CartInfo *ci);
bCartExLoROM() : write_protected(true) {}
};

View File

@@ -33,10 +33,13 @@ uint32 b, w;
}
}
addr &= 0x3fffff;
if(addr < rom_size)return rom[addr];
addr &= ROM_mask;
return 0x00;
if(addr >= P0_size) {
addr = P0_size + (addr & (P1_size - 1));
}
return rom[addr];
}
void bCartHiROM::write(uint32 addr, uint8 value) {
@@ -74,8 +77,14 @@ uint32 b, w;
}
if(write_protected == true)return;
addr &= 0x3fffff;
if(addr < rom_size)rom[addr] = value;
addr &= ROM_mask;
if(addr >= P0_size) {
addr = P0_size + (addr & (P1_size - 1));
}
rom[addr] = value;
}
void bCartHiROM::set_cartinfo(CartInfo *ci) {
@@ -83,4 +92,15 @@ void bCartHiROM::set_cartinfo(CartInfo *ci) {
sram = ci->sram;
rom_size = ci->rom_size;
sram_size = ci->sram_size;
//calculate highest power of 2, which is the size of the first ROM chip
P0_size = 0x800000;
while(!(rom_size & P0_size))P0_size >>= 1;
P1_size = rom_size - P0_size;
if(rom_size == P0_size) { //cart only contains one ROM chip
ROM_mask = (P0_size - 1);
} else { //cart contains two ROM chips
ROM_mask = (P0_size + P0_size - 1);
}
}

View File

@@ -1,12 +1,13 @@
class bCartHiROM : public Cart {
private:
bool write_protected;
bool write_protected;
uint32 P0_size, P1_size, ROM_mask;
public:
uint8 *rom, *sram;
uint32 rom_size, sram_size;
uint8 read (uint32 addr);
void write(uint32 addr, byte value);
void write(uint32 addr, uint8 value);
void write_protect(bool r);
void set_cartinfo(CartInfo *ci);

View File

@@ -35,17 +35,20 @@ uint32 b, w;
if(w & 0x8000) {
b &= 0x7f;
b %= 0x60;
addr = (b << 15) | (addr & 0x7fff);
} else {
b &= 0x7f;
b %= 0x60;
if(b == 0x00)b = 0x60;
if(b == 0x00)b = 0x7f;
addr = (((b << 15) | (addr & 0x7fff)) - 0x8000);
}
if(addr < rom_size)return rom[addr];
return 0x00;
addr &= ROM_mask;
if(addr >= P0_size) {
addr = P0_size + (addr & (P1_size - 1));
}
return rom[addr];
}
void bCartLoROM::write(uint32 addr, uint8 value) {
@@ -83,17 +86,23 @@ uint32 b, w;
}
if(write_protected == true)return;
if(w & 0x8000) {
b &= 0x7f;
b %= 0x60;
addr = (b << 15) | (addr & 0x7fff);
} else {
b &= 0x7f;
b %= 0x60;
if(b == 0x00)b = 0x60;
if(b == 0x00)b = 0x7f;
addr = (((b << 15) | (addr & 0x7fff)) - 0x8000);
}
if(addr < rom_size)rom[addr] = value;
addr &= ROM_mask;
if(addr >= P0_size) {
addr = P0_size + (addr & (P1_size - 1));
}
rom[addr] = value;
}
void bCartLoROM::set_cartinfo(CartInfo *ci) {
@@ -101,4 +110,15 @@ void bCartLoROM::set_cartinfo(CartInfo *ci) {
sram = ci->sram;
rom_size = ci->rom_size;
sram_size = ci->sram_size;
//calculate highest power of 2, which is the size of the first ROM chip
P0_size = 0x800000;
while(!(rom_size & P0_size))P0_size >>= 1;
P1_size = rom_size - P0_size;
if(rom_size == P0_size) { //cart only contains one ROM chip
ROM_mask = (P0_size - 1);
} else { //cart contains two ROM chips
ROM_mask = (P0_size + P0_size - 1);
}
}

View File

@@ -1,12 +1,13 @@
class bCartLoROM : public Cart {
private:
bool write_protected;
bool write_protected;
uint32 P0_size, P1_size, ROM_mask;
public:
uint8 *rom, *sram;
uint32 rom_size, sram_size;
uint8 read (uint32 addr);
void write(uint32 addr, byte value);
void write(uint32 addr, uint8 value);
void write_protect(bool r);
void set_cartinfo(CartInfo *ci);

View File

@@ -1,22 +1,36 @@
#include "../../base.h"
#include "bcart_lorom.cpp"
#include "bcart_hirom.cpp"
#include "bcart_exlorom.cpp"
#include "bcart_exhirom.cpp"
bool bMemBus::load_cart(Reader *rf) {
uint32 cksum, icksum, index;
char cart_title[24];
uint8 mapper;
uint8 mapper, region;
if(rom_loaded == true)return false;
rf->read(&rom);
rf->read(&rom_image);
rom = rom_image;
rom_size = rf->size();
if(rom_size < 32768)return false;
if(rom_size < 32768) {
free(rom_image);
return false;
}
//check for ROM header (currently unused)
if((rom_size & 0x1fff) == 0x0200) {
rom_size -= 512;
rom += 512;
}
if(rom_size >= 0x410000) {
mapper = EXHIROM;
if(rom[0x7fd5] == 0x32) {
mapper = EXLOROM;
} else {
mapper = EXHIROM;
}
goto end;
}
@@ -33,10 +47,17 @@ uint8 mapper;
mapper = LOROM;
}
if(rom[0x7fd5] == 0x32 && rom[0xffd5] == 0x32) {
//SFA2 detected
mapper = EXLOROM;
goto end;
}
end:
switch(mapper) {
case LOROM: index = 0x007fc0;break;
case HIROM: index = 0x00ffc0;break;
case EXLOROM:index = 0x007fc0;break;
case EXHIROM:index = 0x40ffc0;break;
}
memcpy(cart_title, (char*)rom + index, 21);
@@ -53,7 +74,10 @@ end:
case 7:sram_size = 128 * 1024;break;
}
region = rom[index + 0x19];
dprintf("* Image Name : \"%s\"", cart_title);
dprintf("* Region : %s", (region <= 1)?"NTSC":"PAL");
dprintf("* MAD : %0.2x", mapper);
dprintf("* SRAM Size : %dkb", sram_size / 1024);
dprintf("* Reset:%0.4x NMI:%0.4x IRQ:%0.4x BRK[n]:%0.4x COP[n]:%0.4x BRK[e]:%0.4x COP[e]:%0.4x",
@@ -76,12 +100,20 @@ CartInfo ci;
switch(mapper) {
case LOROM: cart = new bCartLoROM(); break;
case HIROM: cart = new bCartHiROM(); break;
case EXLOROM:cart = new bCartExLoROM();break;
case EXHIROM:cart = new bCartExHiROM();break;
default:return false;
}
cart->set_cartinfo(&ci);
rom_loaded = true;
if(region == 0 || region == 1) {
snes->set_region(SNES::NTSC);
} else {
snes->set_region(SNES::PAL);
}
return true;
}
@@ -108,7 +140,7 @@ bool bMemBus::save_sram(Writer *wf) {
void bMemBus::unload_cart() {
if(rom_loaded == false)return;
if(rom) free(rom);
if(rom_image)free(rom_image);
if(sram)free(sram);
delete(cart);
@@ -139,33 +171,39 @@ void bMemBus::get_cartinfo(CartInfo *ci) {
***************************************/
uint8 bMemBus::read(uint32 addr) {
uint32 b, w, r;
addr &= 0xffffff;
b = (addr >> 16);
w = (addr & 0xffff);
static uint32 r;
switch(addr & 0xc00000) {
case 0x400000:
if((addr & 0xfe0000) == 0x7e0000) {
r = wram[addr & 0x01ffff];
break;
}
//fallthrough
case 0xc00000:
r = cart->read(addr);
break;
if(b <= 0x3f) {
if(w <= 0x1fff) {
r = wram[w];
} else if(w <= 0x5fff) {
r = mmio[w - 0x2000]->read(w);
} else {
//case 0x000000:
//case 0x800000:
default:
switch(addr & 0x00e000) {
case 0x0000:
r = wram[addr & 0x1fff];
break;
case 0x2000:
case 0x4000:
r = mmio[(addr - 0x2000) & 0x3fff]->read(addr & 0x7fff);
break;
// case 0x6000:
// case 0x8000:
// case 0xa000:
// case 0xc000:
// case 0xe000:
default:
r = cart->read(addr);
break;
}
} else if(b <= 0x7d) {
r = cart->read(addr);
} else if(b <= 0x7f) {
r = wram[addr & 0x01ffff];
} else if(b <= 0xbf) {
if(w <= 0x1fff) {
r = wram[w];
} else if(w <= 0x5fff) {
r = mmio[w - 0x2000]->read(w);
} else {
r = cart->read(addr);
}
} else {
r = cart->read(addr);
break;
}
snes->notify(SNES::MEM_READ, addr, r);
@@ -173,33 +211,38 @@ uint32 b, w, r;
}
void bMemBus::write(uint32 addr, uint8 value) {
uint32 b, w;
addr &= 0xffffff;
b = (addr >> 16);
w = (addr & 0xffff);
switch(addr & 0xc00000) {
case 0x400000:
if((addr & 0xfe0000) == 0x7e0000) {
wram[addr & 0x01ffff] = value;
break;
}
//fallthrough
case 0xc00000:
cart->write(addr, value);
break;
if(b <= 0x3f) {
if(w <= 0x1fff) {
wram[w] = value;
} else if(w <= 0x5fff) {
mmio[w - 0x2000]->write(w, value);
} else {
//case 0x000000:
//case 0x800000:
default:
switch(addr & 0x00e000) {
case 0x0000:
wram[addr & 0x1fff] = value;
break;
case 0x2000:
case 0x4000:
mmio[(addr - 0x2000) & 0x3fff]->write(addr & 0x7fff, value);
break;
// case 0x6000:
// case 0x8000:
// case 0xa000:
// case 0xc000:
// case 0xe000:
default:
cart->write(addr, value);
break;
}
} else if(b <= 0x7d) {
cart->write(addr, value);
} else if(b <= 0x7f) {
wram[addr & 0x01ffff] = value;
} else if(b <= 0xbf) {
if(w <= 0x1fff) {
wram[w] = value;
} else if(w <= 0x5fff) {
mmio[w - 0x2000]->write(w, value);
} else {
cart->write(addr, value);
}
} else {
cart->write(addr, value);
break;
}
snes->notify(SNES::MEM_WRITE, addr, value);
@@ -211,7 +254,7 @@ void bMemBus::power() {
}
void bMemBus::reset() {
fastROM = false;
set_speed(false);
}
bMemBus::bMemBus() {
@@ -221,5 +264,5 @@ bMemBus::bMemBus() {
}
bMemBus::~bMemBus() {
if(wram)free(wram);
zerofree(wram);
}

View File

@@ -1,23 +1,30 @@
#include "bcart_lorom.h"
#include "bcart_hirom.h"
#include "bcart_exlorom.h"
#include "bcart_exhirom.h"
class bMemBus : public MemBus {
public:
uint8 *rom, *sram, *wram;
//rom_image is the actual image, including header.
//rom is the image sans header, which is actually
//just a pointer to rom_image (with no header), or
//rom_image + 512 (if a header is present).
//rom should never be allocated or released directly.
uint8 *rom_image, *rom, *sram, *wram;
uint32 rom_size, sram_size;
bool rom_loaded;
enum { LOROM = 0x20, HIROM = 0x21, EXHIROM = 0x25 };
enum { LOROM = 0x20, HIROM = 0x21, EXLOROM = 0x22, EXHIROM = 0x25 };
uint8 read (uint32 addr);
void write(uint32 addr, byte value);
void write(uint32 addr, uint8 value);
bool load_cart(Reader *rf);
bool load_sram(Reader *rf);
bool save_sram(Writer *wf);
void unload_cart();
void get_cartinfo(CartInfo *ci);
bool load_cart(Reader *rf);
bool load_sram(Reader *rf);
bool save_sram(Writer *wf);
void unload_cart();
void get_cartinfo(CartInfo *ci);
void power();
void reset();

View File

@@ -1,6 +1,6 @@
#include "../base.h"
uint16 Memory::read_word(uint32 addr, uint8 wrap) {
uint16 Memory::read_word(uint32 addr, uint wrap) {
uint16 r;
switch(wrap) {
case WRAP_NONE:
@@ -19,24 +19,24 @@ uint16 r;
return r;
}
void Memory::write_word(uint32 addr, uint16 value, uint8 wrap) {
void Memory::write_word(uint32 addr, uint16 data, uint wrap) {
switch(wrap) {
case WRAP_NONE:
write(addr, value);
write(addr + 1, value >> 8);
write(addr, data);
write(addr + 1, data >> 8);
return;
case WRAP_BANK:
write(addr, value);
write((addr & 0xff0000) | ((addr + 1) & 0xffff), value >> 8);
write(addr, data);
write((addr & 0xff0000) | ((addr + 1) & 0xffff), data >> 8);
return;
case WRAP_PAGE:
write(addr, value);
write((addr & 0xffff00) | ((addr + 1) & 0xff), value >> 8);
write(addr, data);
write((addr & 0xffff00) | ((addr + 1) & 0xff), data >> 8);
return;
}
}
uint32 Memory::read_long(uint32 addr, uint8 wrap) {
uint32 Memory::read_long(uint32 addr, uint wrap) {
uint32 r;
switch(wrap) {
case WRAP_NONE:
@@ -58,40 +58,54 @@ uint32 r;
return r;
}
void Memory::write_long(uint32 addr, uint32 value, uint8 wrap) {
void Memory::write_long(uint32 addr, uint32 data, uint wrap) {
switch(wrap) {
case WRAP_NONE:
write(addr, value);
write(addr + 1, value >> 8);
write(addr + 2, value >> 16);
write(addr, data);
write(addr + 1, data >> 8);
write(addr + 2, data >> 16);
return;
case WRAP_BANK:
write(addr, value);
write((addr & 0xff0000) | ((addr + 1) & 0xffff), value >> 8);
write((addr & 0xff0000) | ((addr + 2) & 0xffff), value >> 16);
write(addr, data);
write((addr & 0xff0000) | ((addr + 1) & 0xffff), data >> 8);
write((addr & 0xff0000) | ((addr + 2) & 0xffff), data >> 16);
return;
case WRAP_PAGE:
write(addr, value);
write((addr & 0xffff00) | ((addr + 1) & 0xff), value >> 8);
write((addr & 0xffff00) | ((addr + 2) & 0xff), value >> 16);
write(addr, data);
write((addr & 0xffff00) | ((addr + 1) & 0xff), data >> 8);
write((addr & 0xffff00) | ((addr + 2) & 0xff), data >> 16);
return;
}
}
MMIO mmio_unmapped;
uint8 MMIO::read (uint32 addr) { return 0x00; }
uint8 MMIO::read (uint32 addr) { return cpu->regs.mdr; }
void MMIO::write(uint32 addr, uint8 value) {}
uint8 MemBus::speed(uint32 addr) {
uint8 MemBus::calc_speed(uint32 addr, bool fast) {
if((addr & 0xc00000) == 0x400000)return 8;
if((addr & 0x808000) == 0x808000)return fastROM?6:8;
if((addr & 0xc00000) == 0xc00000)return fastROM?6:8;
if((addr & 0x808000) == 0x808000)return fast?6:8;
if((addr & 0xc00000) == 0xc00000)return fast?6:8;
if((addr & 0xe000) == 0x2000)return 6;
if((addr & 0xfe00) == 0x4000)return 12;
if((addr & 0xe000) == 0x4000)return 6;
return 8;
}
uint8 MemBus::speed(uint32 addr) {
return speed_table[addr >> 9];
}
void MemBus::set_speed(bool fast) {
fastROM = fast;
if(fastROM) {
speed_table = (uint8*)speed_table_fastrom;
} else {
speed_table = (uint8*)speed_table_slowrom;
}
}
void MemBus::flush_mmio_mappers() {
for(int i=0;i<0x4000;i++) {
mmio[i] = &mmio_unmapped;
@@ -99,7 +113,7 @@ void MemBus::flush_mmio_mappers() {
}
bool MemBus::set_mmio_mapper(uint16 addr, MMIO *mapper) {
/* out of range? */
//out of range?
if(addr < 0x2000 || addr >= 0x6000)return false;
mmio[(addr - 0x2000) & 0x3fff] = mapper;
@@ -110,6 +124,12 @@ MemBus::MemBus() {
int i;
fastROM = false;
flush_mmio_mappers();
for(i=0;i<32768;i++) {
speed_table_slowrom[i] = calc_speed(i << 9, false);
speed_table_fastrom[i] = calc_speed(i << 9, true);
}
speed_table = (uint8*)speed_table_slowrom;
}
MemBus::~MemBus() {}

View File

@@ -1,18 +1,18 @@
class Memory {
public:
enum { WRAP_NONE = 0, WRAP_BANK = 1, WRAP_PAGE = 2 };
virtual uint8 read (uint32 addr) = 0;
virtual void write(uint32 addr, uint8 value) = 0;
virtual uint16 read_word (uint32 addr, uint8 wrap = WRAP_NONE);
virtual void write_word(uint32 addr, uint16 value, uint8 wrap = WRAP_NONE);
virtual uint32 read_long (uint32 addr, uint8 wrap = WRAP_NONE);
virtual void write_long(uint32 addr, uint32 value, uint8 wrap = WRAP_NONE);
virtual uint8 read (uint32 addr) = 0;
virtual void write (uint32 addr, uint8 data) = 0;
virtual uint16 read_word (uint32 addr, uint wrap = WRAP_NONE);
virtual void write_word(uint32 addr, uint16 data, uint wrap = WRAP_NONE);
virtual uint32 read_long (uint32 addr, uint wrap = WRAP_NONE);
virtual void write_long(uint32 addr, uint32 data, uint wrap = WRAP_NONE);
};
typedef struct {
uint8 *rom, *sram;
uint32 rom_size, sram_size;
}CartInfo;
uint8 *rom, *sram;
uint32 rom_size, sram_size;
} CartInfo;
class Cart : public Memory {
public:
@@ -31,15 +31,26 @@ public:
Cart *cart;
MMIO *mmio[0x4000];
bool fastROM;
virtual void flush_mmio_mappers();
virtual bool set_mmio_mapper(uint16 addr, MMIO *mapper);
virtual uint8 speed(uint32 addr);
void flush_mmio_mappers();
bool set_mmio_mapper(uint16 addr, MMIO *mapper);
virtual bool load_cart(Reader *rf) = 0;
virtual bool load_sram(Reader *rf) = 0;
virtual bool save_sram(Writer *wf) = 0;
virtual void unload_cart() = 0;
virtual void get_cartinfo(CartInfo *ci) = 0;
private:
//0x1000000 / 512 = 32768
//512 = 0x200, smallest block of a different-speed memory range
//ex. $4000-$41ff = 512
uint8 *speed_table,
speed_table_slowrom[32768],
speed_table_fastrom[32768];
inline uint8 calc_speed(uint32 addr, bool fast);
public:
uint8 speed(uint32 addr);
void set_speed(bool fast);
virtual bool load_cart(Reader *rf) = 0;
virtual bool load_sram(Reader *rf) = 0;
virtual bool save_sram(Writer *wf) = 0;
virtual void unload_cart() = 0;
virtual void get_cartinfo(CartInfo *ci) = 0;
virtual void power() = 0;
virtual void reset() = 0;

View File

@@ -6,7 +6,7 @@ void bPPU::run() {}
void bPPU::scanline() {
_y = cpu->vcounter();
_screen_width = (regs.bg_mode == 5 || regs.bg_mode == 6)?512:256;
_screen_width = (regs.bg_mode == 5 || regs.bg_mode == 6) ? 512 : 256;
_interlace = cpu->interlace();
_interlace_field = cpu->interlace_field();
@@ -25,65 +25,44 @@ void bPPU::scanline() {
}
}
if(_y == (cpu->overscan()?239:224) && regs.display_disabled == false) {
if(_y == (cpu->overscan() ? 239 : 224) && regs.display_disabled == false) {
//OAM address reset
regs.oam_addr = ((regs.oam_addrh << 8) | regs.oam_addrl) << 1;
}
}
//only allow frameskip setting to ignore actual rendering; not RTO, etc.
if(settings.frameskip_pos != 0)return;
void bPPU::render_scanline() {
if(status.render_output == false)return;
if(_y > 0 && _y < (cpu->overscan()?239:224)) {
if(regs.bg_mode == 5 || regs.bg_mode == 6) {
output->hires = true;
output->line[_y].hires = true;
}
if(_interlace == true) {
output->interlace = true;
output->line[_y].interlace = true;
}
if(_y > 0 && _y < (cpu->overscan() ? 239 : 224)) {
render_line();
}
}
void bPPU::frame() {
if(settings.frameskip_changed == true) {
settings.frameskip_changed = false;
settings.frameskip_pos = 0;
} else {
settings.frameskip_pos++;
settings.frameskip_pos %= (settings.frameskip + 1);
}
if(settings.frameskip_pos != 0)return;
PPU::frame();
snes->notify(SNES::RENDER_FRAME);
output->hires = false;
output->interlace = false;
for(int i=0;i<239;i++) {
output->line[i].hires = false;
output->line[i].interlace = false;
}
}
void bPPU::set_frameskip(int fs) {
settings.frameskip = fs;
settings.frameskip_changed = true;
}
void bPPU::power() {
memset(vram, 0, 65536);
memset(oam, 0, 544);
memset(cgram, 0, 512);
region = snes->region();
reset();
}
void bPPU::reset() {
memset(output->buffer, 0, 512 * 478 * 2);
frame();
memset(sprite_list, 0, sizeof(sprite_list));
//open bus support
regs.ppu1_mdr = 0xff;
regs.ppu2_mdr = 0xff;
//$2100
regs.display_disabled = 0;
regs.display_brightness = 0;
@@ -133,6 +112,8 @@ void bPPU::reset() {
regs.bg_tdaddr[BG4] = 0x0000;
//$210d-$2114
regs.bg_ofslatch = 0x00;
regs.m7_hofs = regs.m7_vofs = 0x0000;
regs.bg_hofs[BG1] = regs.bg_vofs[BG1] = 0x0000;
regs.bg_hofs[BG2] = regs.bg_vofs[BG2] = 0x0000;
regs.bg_hofs[BG3] = regs.bg_vofs[BG3] = 0x0000;
@@ -152,6 +133,7 @@ void bPPU::reset() {
regs.mode7_hflip = false;
//$211b-$2120
regs.m7_latch = 0x00;
regs.m7a = 0x0000;
regs.m7b = 0x0000;
regs.m7c = 0x0000;
@@ -163,30 +145,33 @@ void bPPU::reset() {
regs.cgram_addr = 0x0000;
//$2123-$2125
regs.bg_window1_enabled[BG1] = false;
regs.bg_window1_enabled[BG2] = false;
regs.bg_window1_enabled[BG3] = false;
regs.bg_window1_enabled[BG4] = false;
regs.bg_window1_enabled[OAM] = false;
regs.bg_window1_invert [BG1] = false;
regs.bg_window1_invert [BG2] = false;
regs.bg_window1_invert [BG3] = false;
regs.bg_window1_invert [BG4] = false;
regs.bg_window1_invert [OAM] = false;
regs.bg_window2_enabled[BG1] = false;
regs.bg_window2_enabled[BG2] = false;
regs.bg_window2_enabled[BG3] = false;
regs.bg_window2_enabled[BG4] = false;
regs.bg_window2_enabled[OAM] = false;
regs.bg_window2_invert [BG1] = false;
regs.bg_window2_invert [BG2] = false;
regs.bg_window2_invert [BG3] = false;
regs.bg_window2_invert [BG4] = false;
regs.bg_window2_invert [OAM] = false;
regs.color_window1_enabled = false;
regs.color_window1_invert = false;
regs.color_window2_enabled = false;
regs.color_window2_invert = false;
regs.window1_enabled[BG1] = false;
regs.window1_enabled[BG2] = false;
regs.window1_enabled[BG3] = false;
regs.window1_enabled[BG4] = false;
regs.window1_enabled[OAM] = false;
regs.window1_enabled[COL] = false;
regs.window1_invert [BG1] = false;
regs.window1_invert [BG2] = false;
regs.window1_invert [BG3] = false;
regs.window1_invert [BG4] = false;
regs.window1_invert [OAM] = false;
regs.window1_invert [COL] = false;
regs.window2_enabled[BG1] = false;
regs.window2_enabled[BG2] = false;
regs.window2_enabled[BG3] = false;
regs.window2_enabled[BG4] = false;
regs.window2_enabled[OAM] = false;
regs.window2_enabled[COL] = false;
regs.window2_invert [BG1] = false;
regs.window2_invert [BG2] = false;
regs.window2_invert [BG3] = false;
regs.window2_invert [BG4] = false;
regs.window2_invert [OAM] = false;
regs.window2_invert [COL] = false;
//$2126-$2129
regs.window1_left = 0;
@@ -195,12 +180,12 @@ void bPPU::reset() {
regs.window2_right = 0;
//$212a-$212b
regs.bg_window_mask[BG1] = 0;
regs.bg_window_mask[BG2] = 0;
regs.bg_window_mask[BG3] = 0;
regs.bg_window_mask[BG4] = 0;
regs.bg_window_mask[OAM] = 0;
regs.color_window_mask = 0;
regs.window_mask[BG1] = 0;
regs.window_mask[BG2] = 0;
regs.window_mask[BG3] = 0;
regs.window_mask[BG4] = 0;
regs.window_mask[OAM] = 0;
regs.window_mask[COL] = 0;
//$212c-$212d
regs.bg_enabled[BG1] = false;
@@ -215,16 +200,16 @@ void bPPU::reset() {
regs.bgsub_enabled[OAM] = false;
//$212e-$212f
regs.bg_window_enabled[BG1] = false;
regs.bg_window_enabled[BG2] = false;
regs.bg_window_enabled[BG3] = false;
regs.bg_window_enabled[BG4] = false;
regs.bg_window_enabled[OAM] = false;
regs.bgsub_window_enabled[BG1] = false;
regs.bgsub_window_enabled[BG2] = false;
regs.bgsub_window_enabled[BG3] = false;
regs.bgsub_window_enabled[BG4] = false;
regs.bgsub_window_enabled[OAM] = false;
regs.window_enabled[BG1] = false;
regs.window_enabled[BG2] = false;
regs.window_enabled[BG3] = false;
regs.window_enabled[BG4] = false;
regs.window_enabled[OAM] = false;
regs.sub_window_enabled[BG1] = false;
regs.sub_window_enabled[BG2] = false;
regs.sub_window_enabled[BG3] = false;
regs.sub_window_enabled[BG4] = false;
regs.sub_window_enabled[OAM] = false;
//$2130
regs.color_mask = 0;
@@ -232,14 +217,14 @@ void bPPU::reset() {
regs.addsub_mode = 0;
//$2131
regs.color_mode = 0;
regs.color_halve = false;
regs.bg_color_enabled[BACK] = false;
regs.bg_color_enabled[OAM] = false;
regs.bg_color_enabled[BG4] = false;
regs.bg_color_enabled[BG3] = false;
regs.bg_color_enabled[BG2] = false;
regs.bg_color_enabled[BG1] = false;
regs.color_mode = 0;
regs.color_halve = false;
regs.color_enabled[BACK] = false;
regs.color_enabled[OAM] = false;
regs.color_enabled[BG4] = false;
regs.color_enabled[BG3] = false;
regs.color_enabled[BG2] = false;
regs.color_enabled[BG1] = false;
//$2132
regs.color_r = 0x00;
@@ -267,8 +252,10 @@ void bPPU::reset() {
regs.time_over = false;
regs.range_over = false;
_screen_width = 256; //needed for clear_window_cache()
update_sprite_list_sizes();
clear_tiledata_cache();
clear_window_cache();
}
uint8 bPPU::vram_read(uint16 addr) {
@@ -300,48 +287,52 @@ void bPPU::oam_write(uint16 addr, uint8 value) {
uint8 bPPU::cgram_read(uint16 addr) {
uint8 r;
r = cgram[addr & 511];
addr &= 511;
r = cgram[addr];
if(addr & 1) {
r &= 0x7f;
}
snes->notify(SNES::CGRAM_READ, addr, r);
return r;
}
void bPPU::cgram_write(uint16 addr, uint8 value) {
cgram[addr & 511] = value;
addr &= 511;
if(addr & 1) {
value &= 0x7f;
}
cgram[addr] = value;
snes->notify(SNES::CGRAM_WRITE, addr, value);
}
bPPU::bPPU() {
settings.frameskip = 0;
settings.frameskip_pos = 0;
settings.frameskip_changed = false;
mmio = new bPPUMMIO(this);
vram = (uint8*)memalloc(65536, "bPPU::vram");
oam = (uint8*)memalloc( 544, "bPPU::oam");
cgram = (uint8*)memalloc( 512, "bPPU::cgram");
vram = (uint8*)malloc(65536);
oam = (uint8*)malloc( 544);
cgram = (uint8*)malloc( 512);
memset(vram, 0, 65536);
memset(oam, 0, 544);
memset(cgram, 0, 512);
init_tiledata_cache();
int i, l;
byte r, g, b;
double m;
int i, l;
uint8 r, g, b;
double m;
uint16 *ptr;
for(l=0;l<16;l++) {
mosaic_table[l] = (uint16*)memalloc(4096 * 2, "bPPU::mosaic_table[%2d]", l);
mosaic_table[l] = (uint16*)malloc(4096 * 2);
for(i=0;i<4096;i++) {
mosaic_table[l][i] = (i / (l + 1)) * (l + 1);
}
}
light_table = (uint16*)memalloc(16 * 65536 * 2, "bPPU::light_table");
ptr = (word*)light_table;
light_table = (uint16*)malloc(16 * 32768 * 2);
ptr = (uint16*)light_table;
for(l=0;l<16;l++) {
m = (double)l / 15.0;
for(i=0;i<65536;i++) {
for(i=0;i<32768;i++) {
r = (i ) & 31;
g = (i >> 5) & 31;
b = (i >> 10) & 31;
@@ -360,14 +351,15 @@ uint16 *ptr;
bPPU::~bPPU() {
delete(mmio);
if(vram) memfree(vram, "bPPU::vram");
if(oam) memfree(oam, "bPPU::oam");
if(cgram)memfree(cgram, "bPPU::cgram");
zerofree(vram);
zerofree(oam);
zerofree(cgram);
for(int i=0;i<16;i++) {
if(mosaic_table[i])memfree(mosaic_table[i], "bPPU::mosaic_table[%2d]", i);
zerofree(mosaic_table[i]);
}
if(light_table)memfree(light_table, "bPPU::light_table");
zerofree(light_table);
}
bPPUMMIO::bPPUMMIO(bPPU *_ppu) {

View File

@@ -11,8 +11,10 @@ bPPU *ppu;
class bPPU : public PPU {
public:
uint8 *vram, *oam, *cgram;
uint8 region;
enum { BG1 = 0, BG2 = 1, BG3 = 2, BG4 = 3, OAM = 4, BACK = 5 };
enum { NTSC = 0, PAL = 1 };
enum { BG1 = 0, BG2 = 1, BG3 = 2, BG4 = 3, OAM = 4, BACK = 5, COL = 5 };
enum { SC_32x32 = 0, SC_32x64 = 1, SC_64x32 = 2, SC_64x64 = 3 };
struct sprite_item {
@@ -23,14 +25,12 @@ struct sprite_item {
bool vflip, hflip;
uint8 palette;
uint8 priority;
}sprite_list[128];
} sprite_list[128];
struct {
int32 frameskip, frameskip_pos;
bool frameskip_changed;
}settings;
//open bus support
uint8 ppu1_mdr, ppu2_mdr;
struct {
//$2100
bool display_disabled;
uint8 display_brightness;
@@ -64,6 +64,8 @@ struct {
uint16 bg_tdaddr[4];
//$210d-$2114
uint8 bg_ofslatch;
uint16 m7_hofs, m7_vofs;
uint16 bg_hofs[4];
uint16 bg_vofs[4];
@@ -81,40 +83,39 @@ struct {
bool mode7_hflip;
//$211b-$2120
uint8 m7_latch;
uint16 m7a, m7b, m7c, m7d, m7x, m7y;
//$2121
uint16 cgram_addr;
//$2123-$2125
bool bg_window1_enabled[5];
bool bg_window1_invert [5];
bool bg_window2_enabled[5];
bool bg_window2_invert [5];
bool color_window1_enabled, color_window1_invert;
bool color_window2_enabled, color_window2_invert;
bool window1_enabled[6];
bool window1_invert [6];
bool window2_enabled[6];
bool window2_invert [6];
//$2126-$2129
uint8 window1_left, window1_right;
uint8 window2_left, window2_right;
//$212a-$212b
uint8 bg_window_mask[5];
uint8 color_window_mask;
uint8 window_mask[6];
//$212c-$212d
bool bg_enabled[5], bgsub_enabled[5];
//$212e-$212f
bool bg_window_enabled[5], bgsub_window_enabled[5];
bool window_enabled[5], sub_window_enabled[5];
//$2130
uint8 color_mask, colorsub_mask;
bool addsub_mode;
bool direct_color;
//$2131
bool color_mode, color_halve;
bool bg_color_enabled[6];
bool color_enabled[6];
//$2132
uint8 color_r, color_g, color_b;
@@ -137,7 +138,7 @@ struct {
//$213e
bool time_over, range_over;
uint16 oam_itemcount, oam_tilecount;
}regs;
} regs;
uint8 vram_read (uint16 addr);
void vram_write (uint16 addr, uint8 value);
uint8 oam_read (uint16 addr);
@@ -149,6 +150,8 @@ struct {
void update_sprite_list(uint16 addr);
void update_sprite_list_sizes();
uint16 get_vram_address();
bool vram_can_read();
bool vram_can_write(uint8 &value);
void mmio_w2100(uint8 value); //INIDISP
void mmio_w2101(uint8 value); //OBSEL
@@ -217,7 +220,7 @@ struct {
void latch_counters();
/* PPU render functions */
//PPU render functions
#include "bppu_render.h"
@@ -226,13 +229,15 @@ uint16 *mosaic_table[16];
void render_line();
void update_oam_status();
/* Required functions */
//required functions
void run();
void scanline();
void render_scanline();
void frame();
void power();
void reset();
void set_frameskip(int fs);
bool scanline_is_hires() { return (regs.bg_mode == 5 || regs.bg_mode == 6); }
bPPU();
~bPPU();

View File

@@ -16,6 +16,55 @@ uint16 addr;
return (addr << 1);
}
bool bPPU::vram_can_read() {
if(regs.display_disabled == true) {
return true;
}
uint16 v = cpu->vcounter();
uint16 hc = cpu->hcycles();
uint16 ls;
if(cpu->interlace() && !cpu->interlace_field()) {
ls = cpu->region_scanlines();
} else {
ls = cpu->region_scanlines() - 1;
}
if(v == ls && hc == 1362)return false;
if(v < (cpu->overscan() ? 239 : 224))return false;
if(v == (cpu->overscan() ? 239 : 224)) {
if(hc == 1362)return true;
return false;
}
return true;
}
bool bPPU::vram_can_write(uint8 &value) {
if(regs.display_disabled == true) {
return true;
}
uint16 v = cpu->vcounter();
uint16 hc = cpu->hcycles();
if(v == 0) {
if(hc <= 4)return true;
if(hc == 6) { value = cpu->regs.mdr; return true; }
return false;
}
if(v < (cpu->overscan() ? 240 : 225))return false;
if(v == (cpu->overscan() ? 240 : 225)) {
if(hc <= 4)return false;
return true;
}
return true;
}
//INIDISP
void bPPU::mmio_w2100(uint8 value) {
regs.display_disabled = !!(value & 0x80);
@@ -68,6 +117,13 @@ void bPPU::mmio_w2105(uint8 value) {
regs.bg_tilesize[BG1] = !!(value & 0x10);
regs.bg3_priority = !!(value & 0x08);
regs.bg_mode = (value & 7);
window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true;
window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true;
window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true;
window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true;
window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true;
window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true;
}
//MOSAIC
@@ -117,42 +173,56 @@ void bPPU::mmio_w210c(uint8 value) {
//BG1HOFS
void bPPU::mmio_w210d(uint8 value) {
regs.bg_hofs[BG1] = (value << 8) | (regs.bg_hofs[BG1] >> 8);
regs.m7_hofs = (value << 8) | regs.m7_latch;
regs.m7_latch = value;
regs.bg_hofs[BG1] = (value << 8) | (regs.bg_ofslatch & 0xf8) | ((regs.bg_hofs[BG1] >> 8) & 7);
regs.bg_ofslatch = value;
}
//BG1VOFS
void bPPU::mmio_w210e(uint8 value) {
regs.bg_vofs[BG1] = (value << 8) | (regs.bg_vofs[BG1] >> 8);
regs.m7_vofs = (value << 8) | regs.m7_latch;
regs.m7_latch = value;
regs.bg_vofs[BG1] = (value << 8) | (regs.bg_ofslatch & 0xf8) | ((regs.bg_vofs[BG1] >> 8) & 7);
regs.bg_ofslatch = value;
}
//BG2HOFS
void bPPU::mmio_w210f(uint8 value) {
regs.bg_hofs[BG2] = (value << 8) | (regs.bg_hofs[BG2] >> 8);
regs.bg_hofs[BG2] = (value << 8) | (regs.bg_ofslatch & 0xf8) | ((regs.bg_hofs[BG2] >> 8) & 7);
regs.bg_ofslatch = value;
}
//BG2VOFS
void bPPU::mmio_w2110(uint8 value) {
regs.bg_vofs[BG2] = (value << 8) | (regs.bg_vofs[BG2] >> 8);
regs.bg_vofs[BG2] = (value << 8) | (regs.bg_ofslatch & 0xf8) | ((regs.bg_vofs[BG2] >> 8) & 7);
regs.bg_ofslatch = value;
}
//BG3HOFS
void bPPU::mmio_w2111(uint8 value) {
regs.bg_hofs[BG3] = (value << 8) | (regs.bg_hofs[BG3] >> 8);
regs.bg_hofs[BG3] = (value << 8) | (regs.bg_ofslatch & 0xf8) | ((regs.bg_hofs[BG3] >> 8) & 7);
regs.bg_ofslatch = value;
}
//BG3VOFS
void bPPU::mmio_w2112(uint8 value) {
regs.bg_vofs[BG3] = (value << 8) | (regs.bg_vofs[BG3] >> 8);
regs.bg_vofs[BG3] = (value << 8) | (regs.bg_ofslatch & 0xf8) | ((regs.bg_vofs[BG3] >> 8) & 7);
regs.bg_ofslatch = value;
}
//BG4HOFS
void bPPU::mmio_w2113(uint8 value) {
regs.bg_hofs[BG4] = (value << 8) | (regs.bg_hofs[BG4] >> 8);
regs.bg_hofs[BG4] = (value << 8) | (regs.bg_ofslatch & 0xf8) | ((regs.bg_hofs[BG4] >> 8) & 7);
regs.bg_ofslatch = value;
}
//BG4VOFS
void bPPU::mmio_w2114(uint8 value) {
regs.bg_vofs[BG4] = (value << 8) | (regs.bg_vofs[BG4] >> 8);
regs.bg_vofs[BG4] = (value << 8) | (regs.bg_ofslatch & 0xf8) | ((regs.bg_vofs[BG4] >> 8) & 7);
regs.bg_ofslatch = value;
}
//VMAIN
@@ -171,77 +241,97 @@ void bPPU::mmio_w2115(uint8 value) {
void bPPU::mmio_w2116(uint8 value) {
regs.vram_addr = (regs.vram_addr & 0xff00) | value;
uint16 addr = get_vram_address();
regs.vram_readbuffer = vram_read(addr);
regs.vram_readbuffer |= vram_read(addr + 1) << 8;
if(vram_can_read()) {
regs.vram_readbuffer = vram_read(addr);
regs.vram_readbuffer |= vram_read(addr + 1) << 8;
} else {
regs.vram_readbuffer = 0x0000;
}
}
//VMADDH
void bPPU::mmio_w2117(uint8 value) {
regs.vram_addr = (value << 8) | (regs.vram_addr & 0x00ff);
uint16 addr = get_vram_address();
regs.vram_readbuffer = vram_read(addr);
regs.vram_readbuffer |= vram_read(addr + 1) << 8;
if(vram_can_read()) {
regs.vram_readbuffer = vram_read(addr);
regs.vram_readbuffer |= vram_read(addr + 1) << 8;
} else {
regs.vram_readbuffer = 0x0000;
}
}
//VMDATAL
void bPPU::mmio_w2118(uint8 value) {
uint16 addr = get_vram_address();
vram_write(addr, value);
if(vram_can_write(value)) {
vram_write(addr, value);
bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1;
bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1;
bg_tiledata_state[TILE_8BIT][(addr >> 6)] = 1;
}
if(regs.vram_incmode == 0) {
regs.vram_addr += regs.vram_incsize;
}
bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1;
bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1;
bg_tiledata_state[TILE_8BIT][(addr >> 6)] = 1;
}
//VMDATAH
void bPPU::mmio_w2119(uint8 value) {
uint16 addr = get_vram_address() + 1;
vram_write(addr, value);
if(vram_can_write(value)) {
vram_write(addr, value);
bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1;
bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1;
bg_tiledata_state[TILE_8BIT][(addr >> 6)] = 1;
}
if(regs.vram_incmode == 1) {
regs.vram_addr += regs.vram_incsize;
}
bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1;
bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1;
bg_tiledata_state[TILE_8BIT][(addr >> 6)] = 1;
}
//M7SEL
void bPPU::mmio_w211a(uint8 value) {
regs.mode7_repeat = (value >> 6) & 3;
regs.mode7_vflip = !!(value & 0x02);
regs.mode7_hflip = !!(value & 0x01);
regs.mode7_vflip = !!(value & 0x02);
regs.mode7_hflip = !!(value & 0x01);
}
//M7A
void bPPU::mmio_w211b(uint8 value) {
regs.m7a = (value << 8) | (regs.m7a >> 8);
regs.m7a = (value << 8) | regs.m7_latch;
regs.m7_latch = value;
}
//M7B
void bPPU::mmio_w211c(uint8 value) {
regs.m7b = (value << 8) | (regs.m7b >> 8);
regs.m7b = (value << 8) | regs.m7_latch;
regs.m7_latch = value;
}
//M7C
void bPPU::mmio_w211d(uint8 value) {
regs.m7c = (value << 8) | (regs.m7c >> 8);
regs.m7c = (value << 8) | regs.m7_latch;
regs.m7_latch = value;
}
//M7D
void bPPU::mmio_w211e(uint8 value) {
regs.m7d = (value << 8) | (regs.m7d >> 8);
regs.m7d = (value << 8) | regs.m7_latch;
regs.m7_latch = value;
}
//M7X
void bPPU::mmio_w211f(uint8 value) {
regs.m7x = (value << 8) | (regs.m7x >> 8);
regs.m7x = (value << 8) | regs.m7_latch;
regs.m7_latch = value;
}
//M7Y
void bPPU::mmio_w2120(uint8 value) {
regs.m7y = (value << 8) | (regs.m7y >> 8);
regs.m7y = (value << 8) | regs.m7_latch;
regs.m7_latch = value;
}
//CGADD
@@ -250,7 +340,13 @@ void bPPU::mmio_w2121(uint8 value) {
}
//CGDATA
//note: CGRAM palette data format is 15-bits
//(0,bbbbb,ggggg,rrrrr). Highest bit is ignored,
//as evidenced by $213b CGRAM data reads.
void bPPU::mmio_w2122(uint8 value) {
if(regs.cgram_addr & 1) {
value &= 0x7f;
}
cgram_write(regs.cgram_addr, value);
regs.cgram_addr++;
regs.cgram_addr &= 0x01ff;
@@ -258,72 +354,117 @@ void bPPU::mmio_w2122(uint8 value) {
//W12SEL
void bPPU::mmio_w2123(uint8 value) {
regs.bg_window2_enabled[BG2] = !!(value & 0x80);
regs.bg_window2_invert [BG2] = !!(value & 0x40);
regs.bg_window1_enabled[BG2] = !!(value & 0x20);
regs.bg_window1_invert [BG2] = !!(value & 0x10);
regs.bg_window2_enabled[BG1] = !!(value & 0x08);
regs.bg_window2_invert [BG1] = !!(value & 0x04);
regs.bg_window1_enabled[BG1] = !!(value & 0x02);
regs.bg_window1_invert [BG1] = !!(value & 0x01);
regs.window2_enabled[BG2] = !!(value & 0x80);
regs.window2_invert [BG2] = !!(value & 0x40);
regs.window1_enabled[BG2] = !!(value & 0x20);
regs.window1_invert [BG2] = !!(value & 0x10);
regs.window2_enabled[BG1] = !!(value & 0x08);
regs.window2_invert [BG1] = !!(value & 0x04);
regs.window1_enabled[BG1] = !!(value & 0x02);
regs.window1_invert [BG1] = !!(value & 0x01);
window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true;
window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true;
}
//W34SEL
void bPPU::mmio_w2124(uint8 value) {
regs.bg_window2_enabled[BG4] = !!(value & 0x80);
regs.bg_window2_invert [BG4] = !!(value & 0x40);
regs.bg_window1_enabled[BG4] = !!(value & 0x20);
regs.bg_window1_invert [BG4] = !!(value & 0x10);
regs.bg_window2_enabled[BG3] = !!(value & 0x08);
regs.bg_window2_invert [BG3] = !!(value & 0x04);
regs.bg_window1_enabled[BG3] = !!(value & 0x02);
regs.bg_window1_invert [BG3] = !!(value & 0x01);
regs.window2_enabled[BG4] = !!(value & 0x80);
regs.window2_invert [BG4] = !!(value & 0x40);
regs.window1_enabled[BG4] = !!(value & 0x20);
regs.window1_invert [BG4] = !!(value & 0x10);
regs.window2_enabled[BG3] = !!(value & 0x08);
regs.window2_invert [BG3] = !!(value & 0x04);
regs.window1_enabled[BG3] = !!(value & 0x02);
regs.window1_invert [BG3] = !!(value & 0x01);
window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true;
window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true;
}
//WOBJSEL
void bPPU::mmio_w2125(uint8 value) {
regs.color_window2_enabled = !!(value & 0x80);
regs.color_window2_invert = !!(value & 0x40);
regs.color_window1_enabled = !!(value & 0x20);
regs.color_window1_invert = !!(value & 0x10);
regs.bg_window2_enabled[OAM] = !!(value & 0x08);
regs.bg_window2_invert [OAM] = !!(value & 0x04);
regs.bg_window1_enabled[OAM] = !!(value & 0x02);
regs.bg_window1_invert [OAM] = !!(value & 0x01);
regs.window2_enabled[COL] = !!(value & 0x80);
regs.window2_invert [COL] = !!(value & 0x40);
regs.window1_enabled[COL] = !!(value & 0x20);
regs.window1_invert [COL] = !!(value & 0x10);
regs.window2_enabled[OAM] = !!(value & 0x08);
regs.window2_invert [OAM] = !!(value & 0x04);
regs.window1_enabled[OAM] = !!(value & 0x02);
regs.window1_invert [OAM] = !!(value & 0x01);
window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true;
window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true;
}
//WH0
void bPPU::mmio_w2126(uint8 value) {
regs.window1_left = value;
window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true;
window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true;
window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true;
window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true;
window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true;
window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true;
}
//WH1
void bPPU::mmio_w2127(uint8 value) {
regs.window1_right = value;
window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true;
window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true;
window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true;
window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true;
window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true;
window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true;
}
//WH2
void bPPU::mmio_w2128(uint8 value) {
regs.window2_left = value;
window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true;
window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true;
window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true;
window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true;
window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true;
window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true;
}
//WH3
void bPPU::mmio_w2129(uint8 value) {
regs.window2_right = value;
window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true;
window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true;
window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true;
window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true;
window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true;
window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true;
}
//WBGLOG
void bPPU::mmio_w212a(uint8 value) {
regs.bg_window_mask[BG4] = (value >> 6) & 3;
regs.bg_window_mask[BG3] = (value >> 4) & 3;
regs.bg_window_mask[BG2] = (value >> 2) & 3;
regs.bg_window_mask[BG1] = (value ) & 3;
regs.window_mask[BG4] = (value >> 6) & 3;
regs.window_mask[BG3] = (value >> 4) & 3;
regs.window_mask[BG2] = (value >> 2) & 3;
regs.window_mask[BG1] = (value ) & 3;
window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true;
window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true;
window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true;
window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true;
}
//WOBJLOG
void bPPU::mmio_w212b(uint8 value) {
regs.color_window_mask = (value >> 2) & 3;
regs.bg_window_mask[OAM] = (value ) & 3;
regs.window_mask[COL] = (value >> 2) & 3;
regs.window_mask[OAM] = (value ) & 3;
window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true;
window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true;
}
//TM
@@ -346,20 +487,32 @@ void bPPU::mmio_w212d(uint8 value) {
//TMW
void bPPU::mmio_w212e(uint8 value) {
regs.bg_window_enabled[OAM] = !!(value & 0x10);
regs.bg_window_enabled[BG4] = !!(value & 0x08);
regs.bg_window_enabled[BG3] = !!(value & 0x04);
regs.bg_window_enabled[BG2] = !!(value & 0x02);
regs.bg_window_enabled[BG1] = !!(value & 0x01);
regs.window_enabled[OAM] = !!(value & 0x10);
regs.window_enabled[BG4] = !!(value & 0x08);
regs.window_enabled[BG3] = !!(value & 0x04);
regs.window_enabled[BG2] = !!(value & 0x02);
regs.window_enabled[BG1] = !!(value & 0x01);
window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true;
window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true;
window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true;
window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true;
window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true;
}
//TSW
void bPPU::mmio_w212f(uint8 value) {
regs.bgsub_window_enabled[OAM] = !!(value & 0x10);
regs.bgsub_window_enabled[BG4] = !!(value & 0x08);
regs.bgsub_window_enabled[BG3] = !!(value & 0x04);
regs.bgsub_window_enabled[BG2] = !!(value & 0x02);
regs.bgsub_window_enabled[BG1] = !!(value & 0x01);
regs.sub_window_enabled[OAM] = !!(value & 0x10);
regs.sub_window_enabled[BG4] = !!(value & 0x08);
regs.sub_window_enabled[BG3] = !!(value & 0x04);
regs.sub_window_enabled[BG2] = !!(value & 0x02);
regs.sub_window_enabled[BG1] = !!(value & 0x01);
window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true;
window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true;
window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true;
window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true;
window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true;
}
//CGWSEL
@@ -367,18 +520,21 @@ void bPPU::mmio_w2130(uint8 value) {
regs.color_mask = (value >> 6) & 3;
regs.colorsub_mask = (value >> 4) & 3;
regs.addsub_mode = !!(value & 0x02);
regs.direct_color = !!(value & 0x01);
window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true;
}
//CGADDSUB
void bPPU::mmio_w2131(uint8 value) {
regs.color_mode = !!(value & 0x80);
regs.color_halve = !!(value & 0x40);
regs.bg_color_enabled[BACK] = !!(value & 0x20);
regs.bg_color_enabled[OAM] = !!(value & 0x10);
regs.bg_color_enabled[BG4] = !!(value & 0x08);
regs.bg_color_enabled[BG3] = !!(value & 0x04);
regs.bg_color_enabled[BG2] = !!(value & 0x02);
regs.bg_color_enabled[BG1] = !!(value & 0x01);
regs.color_mode = !!(value & 0x80);
regs.color_halve = !!(value & 0x40);
regs.color_enabled[BACK] = !!(value & 0x20);
regs.color_enabled[OAM] = !!(value & 0x10);
regs.color_enabled[BG4] = !!(value & 0x08);
regs.color_enabled[BG3] = !!(value & 0x04);
regs.color_enabled[BG2] = !!(value & 0x02);
regs.color_enabled[BG1] = !!(value & 0x01);
}
//COLDATA
@@ -404,21 +560,24 @@ void bPPU::mmio_w2133(uint8 value) {
uint8 bPPU::mmio_r2134() {
uint32 r;
r = ((int16)regs.m7a * (int8)(regs.m7b >> 8));
return (r);
regs.ppu1_mdr = r;
return regs.ppu1_mdr;
}
//MPYM
uint8 bPPU::mmio_r2135() {
uint32 r;
r = ((int16)regs.m7a * (int8)(regs.m7b >> 8));
return (r >> 8);
regs.ppu1_mdr = r >> 8;
return regs.ppu1_mdr;
}
//MPYH
uint8 bPPU::mmio_r2136() {
uint32 r;
r = ((int16)regs.m7a * (int8)(regs.m7b >> 8));
return (r >> 16);
regs.ppu1_mdr = r >> 16;
return regs.ppu1_mdr;
}
//SLHV
@@ -426,79 +585,102 @@ uint8 bPPU::mmio_r2137() {
if(cpu->pio_status() & 0x80) {
latch_counters();
}
return 0x00;
return cpu->regs.mdr;
}
//OAMDATAREAD
uint8 bPPU::mmio_r2138() {
uint8 r;
r = oam_read(regs.oam_addr);
regs.ppu1_mdr = oam_read(regs.oam_addr);
if(!(regs.oam_addr & 1)) {
regs.oam_latchdata = r;
regs.oam_latchdata = regs.ppu1_mdr;
}
regs.oam_addr++;
regs.oam_addr &= 0x03ff;
return r;
return regs.ppu1_mdr;
}
//VMDATALREAD
uint8 bPPU::mmio_r2139() {
uint16 addr = get_vram_address();
uint8 r = regs.vram_readbuffer;
regs.ppu1_mdr = regs.vram_readbuffer;
if(regs.vram_incmode == 0) {
addr &= 0xfffe;
regs.vram_readbuffer = vram_read(addr);
regs.vram_readbuffer |= vram_read(addr + 1) << 8;
if(vram_can_read()) {
regs.vram_readbuffer = vram_read(addr);
regs.vram_readbuffer |= vram_read(addr + 1) << 8;
} else {
regs.vram_readbuffer = 0x0000;
}
regs.vram_addr += regs.vram_incsize;
}
return r;
return regs.ppu1_mdr;
}
//VMDATAHREAD
uint8 bPPU::mmio_r213a() {
uint16 addr = get_vram_address() + 1;
uint8 r = regs.vram_readbuffer >> 8;
regs.ppu1_mdr = regs.vram_readbuffer >> 8;
if(regs.vram_incmode == 1) {
addr &= 0xfffe;
regs.vram_readbuffer = vram_read(addr);
regs.vram_readbuffer |= vram_read(addr + 1) << 8;
if(vram_can_read()) {
regs.vram_readbuffer = vram_read(addr);
regs.vram_readbuffer |= vram_read(addr + 1) << 8;
} else {
regs.vram_readbuffer = 0x0000;
}
regs.vram_addr += regs.vram_incsize;
}
return r;
return regs.ppu1_mdr;
}
//CGDATAREAD
//note: CGRAM palette data is 15-bits (0,bbbbb,ggggg,rrrrr)
//therefore, the high byte read from each color does not
//update bit 7 of the PPU2 MDR.
uint8 bPPU::mmio_r213b() {
uint8 r;
r = cgram_read(regs.cgram_addr);
if(!(regs.cgram_addr & 1)) {
regs.ppu2_mdr = cgram_read(regs.cgram_addr) & 0xff;
} else {
regs.ppu2_mdr &= 0x80;
regs.ppu2_mdr |= cgram_read(regs.cgram_addr) & 0x7f;
}
regs.cgram_addr++;
regs.cgram_addr &= 0x01ff;
return r;
return regs.ppu2_mdr;
}
//OPHCT
uint8 bPPU::mmio_r213c() {
uint16 r = regs.hcounter;
if(regs.latch_hcounter)r >>= 8;
if(!regs.latch_hcounter) {
regs.ppu2_mdr = regs.hcounter & 0xff;
} else {
regs.ppu2_mdr &= 0xfe;
regs.ppu2_mdr |= (regs.hcounter >> 8) & 1;
}
regs.latch_hcounter ^= 1;
return r;
return regs.ppu2_mdr;
}
//OPVCT
uint8 bPPU::mmio_r213d() {
uint16 r = regs.vcounter;
if(regs.latch_vcounter)r >>= 8;
if(!regs.latch_vcounter) {
regs.ppu2_mdr = regs.vcounter & 0xff;
} else {
regs.ppu2_mdr &= 0xfe;
regs.ppu2_mdr |= (regs.vcounter >> 8) & 1;
}
regs.latch_vcounter ^= 1;
return r;
return regs.ppu2_mdr;
}
//STAT77
uint8 bPPU::mmio_r213e() {
uint8 r = 0x00;
r |= (regs.time_over) ?0x80:0x00;
r |= (regs.range_over)?0x40:0x00;
r |= 0x01; //PPU1 version number
return r;
r |= (regs.time_over) ? 0x80 : 0x00;
r |= (regs.range_over) ? 0x40 : 0x00;
r |= (ppu1_version & 0x0f);
regs.ppu1_mdr = r;
return regs.ppu1_mdr;
}
//STAT78
@@ -509,19 +691,39 @@ uint8 r = 0x00;
r |= cpu->interlace_field() << 7;
if(!(cpu->pio_status() & 0x80)) {
r |= 1 << 6;
r |= 0x40;
} else if(regs.counters_latched == true) {
r |= 1 << 6;
r |= 0x40;
regs.counters_latched = false;
}
r |= (1 << 5);
r |= 0x03; //PPU2 version number
return r;
r |= (regs.ppu2_mdr & 0x20);
r |= (region << 4); //0 = NTSC, 1 = PAL
r |= (ppu2_version & 0x0f);
regs.ppu2_mdr = r;
return regs.ppu2_mdr;
}
uint8 bPPUMMIO::read(uint32 addr) {
//cpu->sync();
switch(addr) {
case 0x2104:
case 0x2105:
case 0x2106:
case 0x2108:
case 0x2109:
case 0x210a:
case 0x2114:
case 0x2115:
case 0x2116:
case 0x2118:
case 0x2119:
case 0x211a:
case 0x2124:
case 0x2125:
case 0x2126:
case 0x2128:
case 0x2129:
case 0x212a:
return ppu->regs.ppu1_mdr;
case 0x2134:return ppu->mmio_r2134(); //MPYL
case 0x2135:return ppu->mmio_r2135(); //MPYM
case 0x2136:return ppu->mmio_r2136(); //MPYH
@@ -535,11 +737,11 @@ uint8 bPPUMMIO::read(uint32 addr) {
case 0x213e:return ppu->mmio_r213e(); //STAT77
case 0x213f:return ppu->mmio_r213f(); //STAT78
}
return 0x00;
return cpu->regs.mdr;
}
void bPPUMMIO::write(uint32 addr, uint8 value) {
//cpu->sync();
switch(addr) {
case 0x2100:ppu->mmio_w2100(value);return; //INIDISP
case 0x2101:ppu->mmio_w2101(value);return; //OBSEL

View File

@@ -104,32 +104,30 @@ Mode7: ->
1, 2, 3, 4, 5
OAM0, BG1n, OAM1, OAM2, OAM3
***
This appears to be incorrect, possibly should be...
Mode 7 EXTBG: ->
1, 2, 3, 4, 5, 6, 7
BG2B, OAM0, BG1n, OAM1, BG2A, OAM2, OAM3
***
Mode 7 (extbg): ->
1, 2, 3, 4, 5, 6
BG2B, OAM0, OAM1, BG2A, OAM2, OAM3
*/
inline void bPPU::render_line_mode7() {
if(regs.mode7_extbg == false) {
render_line_mode7(2, 0, 0); //bg2 priorities are ignored
render_line_mode7(BG1, 2, 2);
render_line_oam(1, 3, 4, 5);
} else {
render_line_mode7(0, 1, 4); //bg1 priority is ignored
render_line_oam(2, 3, 5, 6);
render_line_mode7(BG1, 3, 3);
render_line_mode7(BG2, 1, 5);
render_line_oam(2, 4, 6, 7);
}
}
void bPPU::render_line() {
if(regs.display_disabled == true) {
memset(output->buffer + (_y << 10), 0, 2048);
memset(snes->get_ppu_output_handle(), 0, 1024);
return;
}
clear_pixel_cache();
build_color_window_tables();
build_window_tables(COL);
switch(regs.bg_mode) {
case 0:render_line_mode0();break;
case 1:render_line_mode1();break;
@@ -140,5 +138,6 @@ void bPPU::render_line() {
case 6:render_line_mode6();break;
case 7:render_line_mode7();break;
}
render_line_output();
}

View File

@@ -18,32 +18,35 @@ enum { TILE_2BIT = 0, TILE_4BIT = 1, TILE_8BIT = 2 };
enum { PC_BG1 = 0x80, PC_BG2 = 0x81, PC_BG3 = 0x82, PC_BG4 = 0x83, PC_OAM = 0x84, PC_BACK = 0x00 };
struct _pixel {
//palette # index for main/subscreen pixels
//0 = transparent / use palette color # 0
uint8 src_main, src_sub;
//bgr555 color data for main/subscreen pixels: 0x0000 = transparent / use palette color # 0
//needs to be bgr555 instead of palette index for direct color mode ($2130 bit 0) to work
uint16 src_main, src_sub;
//indicates source of palette # for main/subscreen (BG1-4, OAM, or back)
uint8 bg_main, bg_sub;
uint8 bg_main, bg_sub;
//true when bg_main == OAM && palette index >= 192, disables color add/sub effects
uint8 color_exempt;
//priority level of src_n. to set src_n,
//the priority of the pixel must be >pri_n
uint8 pri_main, pri_sub;
}pixel_cache[512];
uint8 pri_main, pri_sub;
} pixel_cache[512];
uint8 *bg_tiledata[3];
uint8 *bg_tiledata_state[3];
void render_bg_tile(uint8 color_depth, uint16 tile_num);
inline void clear_pixel_cache();
void init_tiledata_cache();
void clear_tiledata_cache();
inline void init_tiledata_cache();
inline void clear_tiledata_cache();
//bppu_render_windows.cpp
uint8 main_windowtable[5][512], sub_windowtable[5][512],
main_colorwindowtable[512], sub_colorwindowtable[512];
struct _window {
bool main_dirty, sub_dirty;
uint8 main[512], sub[512];
} window_cache[6];
void build_window_table(uint8 bg, uint8 *wtbl, bool mainscreen);
void build_window_table(uint8 bg, bool mainscreen);
void build_window_tables(uint8 bg);
void build_color_window_table(uint8 *wtbl, uint8 mask);
void build_color_window_tables();
inline void clear_window_cache();
//bppu_render_bg.cpp
void render_line_bg(uint8 bg, uint8 color_depth, uint8 pri0_pos, uint8 pri1_pos);
@@ -55,7 +58,7 @@ uint8 oam_itemlist[32];
struct oam_tileitem {
uint16 x, y, pri, pal, tile;
bool hflip;
}oam_tilelist[34];
} oam_tilelist[34];
enum { OAM_PRI_NONE = 4 };
uint8 oam_line_pal[512], oam_line_pri[512];
@@ -66,15 +69,16 @@ void render_oam_tile(int tile_num);
void render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos);
//bppu_render_mode7.cpp
void render_line_mode7(uint8 bg1_pri, uint8 bg2b_pri, uint8 bg2a_pri);
void render_line_mode7(uint8 bg, uint8 pri0_pos, uint8 pri1_pos);
//bppu_render_addsub.cpp
inline uint16 addsub_pixels(int cdest_index, int cdest_bg, int csrc_index, int csrc_bg);
inline uint16 addsub_pixel(int cdest_index, int cdest_bg);
inline uint16 addsub_pixels(uint32 cdest, uint32 csrc);
inline uint16 addsub_pixel (uint32 cdest);
//bppu_render_line.cpp
enum { BLENDTYPE_BACK = 0, BLENDTYPE_MAIN = 1, BLENDTYPE_SUB = 2, BLENDTYPE_COMBINE = 3 };
inline uint16 get_palette(int index);
inline uint16 get_palette(uint8 index);
inline uint16 get_direct_color(uint8 p, uint8 t);
inline uint16 get_pixel(int x);
inline void render_line_output();

View File

@@ -1,15 +1,6 @@
inline uint16 bPPU::addsub_pixels(int cdest_index, int cdest_bg, int csrc_index, int csrc_bg) {
inline uint16 bPPU::addsub_pixels(uint32 cdest, uint32 csrc) {
int r, g, b;
uint32 cdest = get_palette(cdest_index);
uint32 csrc = get_palette(csrc_index);
uint16 res;
//oam palettes 0-3 are not affected by color add/sub
if(cdest_bg == OAM) {
if(cdest_index < 192) {
return cdest;
}
}
switch(regs.color_mode) {
case 0: //COLORMODE_ADD:
if(regs.color_halve == true) {
@@ -53,18 +44,10 @@ uint16 res;
return 0x0000; //prevent annoying warning message
}
inline uint16 bPPU::addsub_pixel(int cdest_index, int cdest_bg) {
inline uint16 bPPU::addsub_pixel(uint32 cdest) {
int r, g, b;
uint32 cdest = get_palette(cdest_index);
uint32 csrc = (regs.color_r) | (regs.color_g << 5) | (regs.color_b << 10);
uint16 res;
//only oam palettes 4-7 are affected by color add/sub
if(cdest_bg == OAM) {
if(cdest_index < 192) {
return cdest;
}
}
switch(regs.color_mode) {
case 0: //COLORMODE_ADD:
if(regs.color_halve == true && regs.addsub_mode == 0) {

View File

@@ -3,7 +3,7 @@ void bPPU::render_line_bg(uint8 bg, uint8 color_depth, uint8 pri0_pos, uint8 pri
return;
}
int x;
int x;
int _scaddr = regs.bg_scaddr[bg];
int _tdaddr = regs.bg_tdaddr[bg];
bool _bg_enabled = regs.bg_enabled[bg];
@@ -18,6 +18,15 @@ uint16 opt_valid_bit; //offset-per-tile valid flag bit
opt_valid_bit = 0x0000;
}
//Mode 0 uses a special palette-indexing mode.
//Since there are 8 selectable palettes per tile,
//and there are 4 colors per palette on all four
//BGs for Mode 0, each BG has a unique palette
//entry point. This allows all 256 palette colors
//to be used, instead of just the first 32.
//entry = bg * 32, where 32 is from 8 * 4
uint8 bgpal_index = (regs.bg_mode == 0) ? (bg << 5) : 0;
uint8 pal_size, tiledata_size;
switch(color_depth) {
case COLORDEPTH_4:
@@ -95,37 +104,44 @@ int mosaic_x, mosaic_y;
} else {
mtable = (uint16*)mosaic_table[0];
}
mosaic_x = mtable[bg_x];
mosaic_y = mtable[bg_y];
uint8 tile_x;
uint16 t, base_xpos, base_pos, pos;
uint16 tile_num;
int mirror_x, mirror_y;
uint8 pal_index;
int mirror_x, mirror_y;
uint8 pal_index, pal_num;
uint8 *tile_ptr;
int xpos, ypos;
uint16 map_index, hoffset, voffset, col;
uint8 *wt_main = main_windowtable[bg];
uint8 *wt_sub = sub_windowtable[bg];
build_window_tables(bg);
for(screen_x=0;screen_x<_screen_width;screen_x++) {
//offset-per-tile mode. horizontal OPT is buggy, so it is disabled
//vertical OPT seems to be working OK...
uint8 *wt_main = window_cache[bg].main;
uint8 *wt_sub = window_cache[bg].sub;
screen_x = 0;
do { //for(screen_x=0;screen_x<_screen_width;screen_x++) {
if(regs.bg_mode == 2 || regs.bg_mode == 4 || regs.bg_mode == 6) {
if(regs.bg_mode == 6) {
//hires adjust
tile_x = (mtable[screen_x + (hscroll & 15)] >> 4);
} else {
tile_x = (mtable[screen_x + (hscroll & 7)] >> 3);
}
hoffset = hscroll;
voffset = vscroll;
//tile 0 is unaffected by OPT mode...
if(tile_x != 0) {
tile_x = (tile_x - 1) & 31;
//multiply by two to index into 16-bit table entries
tile_x = ((tile_x - 1) & 31) << 1;
if(regs.bg_mode == 4) {
pos = regs.bg_scaddr[BG3] + (tile_x << 1);
t = *((uint16*)vram + (pos >> 1));
pos = regs.bg_scaddr[BG3] + tile_x;
t = read16(vram, pos);
if(t & opt_valid_bit) {
if(!(t & 0x8000)) {
hoffset = ((t & 0x1ff8) | (hscroll & 7)) & screen_width_mask;
@@ -134,18 +150,19 @@ uint8 *wt_sub = sub_windowtable[bg];
}
}
} else {
pos = regs.bg_scaddr[BG3] + (tile_x << 1);
t = *((uint16*)vram + (pos >> 1));
pos = regs.bg_scaddr[BG3] + tile_x;
t = read16(vram, pos);
if(t & opt_valid_bit) {
hoffset = ((t & 0x1ff8) | (hscroll & 7)) & screen_width_mask;
}
pos = regs.bg_scaddr[BG3] + 64 + (tile_x << 1);
t = *((uint16*)vram + (pos >> 1));
pos = regs.bg_scaddr[BG3] + 64 + tile_x;
t = read16(vram, pos);
if(t & opt_valid_bit) {
voffset = (t & 0x1fff) & screen_height_mask;
}
}
}
mosaic_x = mtable[(screen_x + hoffset) & screen_width_mask ];
mosaic_y = mtable[(screen_y + voffset) & screen_height_mask];
}
@@ -169,31 +186,37 @@ uint8 *wt_sub = sub_windowtable[bg];
base_xpos = ((mosaic_x >> 3) & 31);
base_pos = (((mosaic_y >> tile_height) & 31) << 5) + ((mosaic_x >> tile_width) & 31);
pos = _scaddr + map_index + (base_pos << 1);
t = *((uint16*)vram + (pos >> 1));
mirror_y = (t & 0x8000)?1:0;
mirror_x = (t & 0x4000)?1:0;
t = read16(vram, pos);
mirror_y = !!(t & 0x8000);
mirror_x = !!(t & 0x4000);
int _pri;
_pri = (t & 0x2000) ? pri1_pos : pri0_pos;
tile_num = t & 0x03ff;
//16x16 horizontal tile mirroring
if(tile_width == 4) {
if(((mosaic_x & 15) >= 8 && !mirror_x) ||
((mosaic_x & 15) < 8 && mirror_x))tile_num++;
tile_num &= 0x03ff;
}
//16x16 vertical tile mirroring
if(tile_height == 4) {
if(((mosaic_y & 15) >= 8 && !mirror_y) ||
((mosaic_y & 15) < 8 && mirror_y))tile_num += 16;
tile_num &= 0x03ff;
}
tile_num += (_tdaddr >> tiledata_size);
if(bg_td_state[tile_num] == 1) {
render_bg_tile(color_depth, tile_num);
}
pal_index = ((t >> 10) & 7) * pal_size;
pal_num = ((t >> 10) & 7);
pal_index = pal_num * pal_size + bgpal_index;
if(mirror_y) { ypos = (7 - (mosaic_y & 7)); }
else { ypos = ( (mosaic_y & 7)); }
@@ -201,23 +224,30 @@ int _pri;
//loop while we are rendering from the same tile, as there's no need to do all of the above work
//unless we have rendered all of the visible tile, taking mosaic into account.
tile_ptr = (uint8*)bg_td + (tile_num << 6) + (ypos << 3);
while(1) {
do {
if(mirror_x) { xpos = (7 - (mosaic_x & 7)); }
else { xpos = ( (mosaic_x & 7)); }
col = *(tile_ptr + xpos);
if(col && main_colorwindowtable[screen_x]) {
if(col && window_cache[COL].main[screen_x]) {
if(regs.direct_color == true && bg == BG1 && (regs.bg_mode == 3 || regs.bg_mode == 4)) {
col = get_direct_color(pal_num, col);
} else {
col = get_palette(col + pal_index);
}
if(_bg_enabled == true && !wt_main[screen_x]) {
if(pixel_cache[screen_x].pri_main < _pri) {
pixel_cache[screen_x].pri_main = _pri;
pixel_cache[screen_x].bg_main = 0x80 | bg;
pixel_cache[screen_x].src_main = col + pal_index;
pixel_cache[screen_x].src_main = col;
pixel_cache[screen_x].color_exempt = false;
}
}
if(_bgsub_enabled == true && !wt_sub[screen_x]) {
if(pixel_cache[screen_x].pri_sub < _pri) {
pixel_cache[screen_x].pri_sub = _pri;
pixel_cache[screen_x].bg_sub = 0x80 | bg;
pixel_cache[screen_x].src_sub = col + pal_index;
pixel_cache[screen_x].src_sub = col;
}
}
}
@@ -227,8 +257,7 @@ int _pri;
mosaic_x = mtable[bg_x];
if(base_xpos != ((mosaic_x >> 3) & 31))break;
screen_x++;
if(screen_x >= _screen_width)break;
}
}
if(++screen_x >= _screen_width)break;
} while(1);
} while(++screen_x < _screen_width);
}

View File

@@ -117,3 +117,17 @@ void bPPU::clear_tiledata_cache() {
memset(bg_tiledata_state[TILE_4BIT], 0, 2048);
memset(bg_tiledata_state[TILE_8BIT], 0, 1024);
}
void bPPU::clear_window_cache() {
for(int i=0;i<6;i++) {
window_cache[i].main_dirty = true;
window_cache[i].sub_dirty = true;
}
build_window_tables(BG1);
build_window_tables(BG2);
build_window_tables(BG3);
build_window_tables(BG4);
build_window_tables(OAM);
build_window_tables(COL);
}

View File

@@ -1,46 +1,55 @@
inline uint16 bPPU::get_palette(int index) {
return *((uint16*)cgram + index);
inline uint16 bPPU::get_palette(uint8 index) {
return read16(cgram, index << 1);
}
inline uint16 bPPU::get_direct_color(uint8 p, uint8 t) {
//p = 00000bgr <palette data>
//t = BBGGGRRR <tilemap data>
//r = 0BBb00GGGg0RRRr0 <return data>
return ((t & 7) << 2) | ((p & 1) << 1) |
(((t >> 3) & 7) << 7) | (((p >> 1) & 1) << 6) |
((t >> 6) << 13) | ((p >> 2) << 12);
}
inline uint16 bPPU::get_pixel(int x) {
_pixel *p = &pixel_cache[x];
uint16 _r;
uint16 _r, src_back = get_palette(0);
if(p->bg_main && p->bg_sub) {
if(regs.bg_color_enabled[p->bg_main & 0x7f] && sub_colorwindowtable[x]) {
if(p->color_exempt == false && regs.color_enabled[p->bg_main & 0x7f] && window_cache[COL].sub[x]) {
if(regs.addsub_mode) {
_r = addsub_pixels(p->src_main, p->bg_main & 0x7f, p->src_sub, p->bg_sub & 0x7f);
_r = addsub_pixels(p->src_main, p->src_sub);
} else {
_r = addsub_pixel(p->src_main, p->bg_main & 0x7f);
_r = addsub_pixel(p->src_main);
}
} else {
_r = get_palette(p->src_main);
_r = p->src_main;
}
} else if(p->bg_main) {
if(regs.bg_color_enabled[p->bg_main & 0x7f] && sub_colorwindowtable[x]) {
_r = addsub_pixel(p->src_main, p->bg_main & 0x7f);
if(p->color_exempt == false && regs.color_enabled[p->bg_main & 0x7f] && window_cache[COL].sub[x]) {
_r = addsub_pixel(p->src_main);
} else {
_r = get_palette(p->src_main);
_r = p->src_main;
}
} else if(p->bg_sub) {
if(regs.bg_color_enabled[BACK]) {
if(sub_colorwindowtable[x]) {
if(regs.color_enabled[BACK]) {
if(window_cache[COL].sub[x]) {
if(regs.addsub_mode) {
_r = addsub_pixels(0, BACK, p->src_sub, p->bg_sub & 0x7f);
_r = addsub_pixels(src_back, p->src_sub);
} else {
_r = addsub_pixel(0, BACK);
_r = addsub_pixel(src_back);
}
} else {
_r = get_palette(0);
_r = src_back;
}
} else {
_r = 0x0000;
_r = src_back; //was 0x0000 -- possibly another condition here?
}
} else {
if(main_colorwindowtable[x]) {
if(regs.bg_color_enabled[BACK] && sub_colorwindowtable[x]) {
_r = addsub_pixel(0, BACK);
if(window_cache[COL].main[x]) {
if(regs.color_enabled[BACK] && window_cache[COL].sub[x]) {
_r = addsub_pixel(src_back);
} else {
_r = get_palette(0);
_r = src_back;
}
} else {
_r = 0x0000;
@@ -53,19 +62,15 @@ inline void bPPU::render_line_output() {
int x;
uint16 _r;
uint16 *ptr;
ptr = (uint16*)output->buffer + (_y << 10);
if(_interlace == true) {
ptr += _interlace_field << 9;
}
ptr = (uint16*)snes->get_ppu_output_handle();
uint16 *ltable;
ltable = (uint16*)light_table + (regs.display_brightness << 16);
ltable = (uint16*)light_table + (regs.display_brightness << 15);
if(_screen_width == 256) {
for(x=0;x<256;x++) {
_r = get_pixel(x);
*ptr = *(ltable + _r);
ptr += 2;
*ptr++ = *(ltable + _r);
}
} else {
for(x=0;x<512;x++) {

View File

@@ -1,62 +1,73 @@
#define CLIP_10BIT_SIGNED(x) \
((x) & ((1 << 10) - 1)) + (((((x) & (1 << 13)) ^ (1 << 13)) - (1 << 13)) >> 3)
/*
bsnes mode7 renderer
#define CAST_WORDTOINT(x) \
(int)(((x & 0x8000)?(x | 0xffff0000):(x & 0x00007fff)))
base algorithm written by anomie
bsnes implementation written by byuu
void bPPU::render_line_mode7(uint8 bg1_pri, uint8 bg2b_pri, uint8 bg2a_pri) {
int x;
int step_m7a, step_m7c, m7a, m7b, m7c, m7d;
int hoffset, voffset;
int centerx, centery;
int xx, yy;
int px, py;
int tx, ty, tile, palette;
uint8 layer_pos;
hoffset = (CAST_WORDTOINT(regs.bg_hofs[BG1]) << 7) >> 7;
voffset = (CAST_WORDTOINT(regs.bg_vofs[BG1]) << 7) >> 7;
supports mode 7 + extbg + rotate + zoom + direct color + scrolling + m7sel + windowing + mosaic
does not support pseudo-hires
interlace support is automatic via main rendering routine
*/
centerx = (CAST_WORDTOINT(regs.m7x) << 7) >> 7;
centery = (CAST_WORDTOINT(regs.m7y) << 7) >> 7;
//13-bit sign extend
//--s---vvvvvvvvvv -> ssssssvvvvvvvvvv
#define CLIP(x) ( ((x) & 0x2000) ? ( (x) | ~0x03ff) : ((x) & 0x03ff) )
//#define CLIP(x) ( ((x) & 0x03ff) | (((x) & 0x2000) ? -0x0400 : 0) )
void bPPU::render_line_mode7(uint8 bg, uint8 pri0_pos, uint8 pri1_pos) {
if(regs.bg_enabled[bg] == false && regs.bgsub_enabled[bg] == false)return;
int32 x, y;
int32 a, b, c, d, cx, cy;
int32 hofs, vofs;
int32 px, py;
int32 tx, ty, tile, palette;
a = int32(int16(regs.m7a));
b = int32(int16(regs.m7b));
c = int32(int16(regs.m7c));
d = int32(int16(regs.m7d));
cx = (int32(regs.m7x) << 19) >> 19;
cy = (int32(regs.m7y) << 19) >> 19;
hofs = (int32(regs.m7_hofs) << 19) >> 19;
//+1 breaks FF5 title screen mirror alignment...
vofs = (int32(regs.m7_vofs + 0) << 19) >> 19;
int _pri, _x;
bool _bg_enabled = regs.bg_enabled[bg];
bool _bgsub_enabled = regs.bgsub_enabled[bg];
build_window_tables(bg);
uint8 *wt_main = window_cache[bg].main;
uint8 *wt_sub = window_cache[bg].sub;
if(regs.mode7_vflip == true) {
yy = 223 - _y;
y = 255 - _y;
} else {
yy = _y;
}
yy += CLIP_10BIT_SIGNED(voffset - centery);
m7b = CAST_WORDTOINT(regs.m7b) * yy + (centerx << 8);
m7d = CAST_WORDTOINT(regs.m7d) * yy + (centery << 8);
step_m7a = CAST_WORDTOINT(regs.m7a);
step_m7c = CAST_WORDTOINT(regs.m7c);
xx = CLIP_10BIT_SIGNED(hoffset - centerx);
m7a = CAST_WORDTOINT(regs.m7a) * xx;
m7c = CAST_WORDTOINT(regs.m7c) * xx;
int _pri, _x, _bg;
bool _bg_enabled, _bgsub_enabled;
if(regs.mode7_extbg == false) {
_pri = bg1_pri;
_bg = BG1;
_bg_enabled = regs.bg_enabled[BG1];
_bgsub_enabled = regs.bgsub_enabled[BG1];
} else {
_bg = BG2;
_bg_enabled = regs.bg_enabled[BG2];
_bgsub_enabled = regs.bgsub_enabled[BG2];
y = _y;
}
uint8 *wt_main = main_windowtable[_bg];
uint8 *wt_sub = sub_windowtable[_bg];
build_window_tables(_bg);
uint16 *mtable_x, *mtable_y;
if(bg == BG1) {
mtable_x = (uint16*)mosaic_table[(regs.mosaic_enabled[BG1] == true) ? regs.mosaic_size : 0];
mtable_y = (uint16*)mosaic_table[(regs.mosaic_enabled[BG1] == true) ? regs.mosaic_size : 0];
} else { //bg == BG2
//Mode7 EXTBG BG2 uses BG1 mosaic enable to control vertical mosaic,
//and BG2 mosaic enable to control horizontal mosaic...
mtable_x = (uint16*)mosaic_table[(regs.mosaic_enabled[BG2] == true) ? regs.mosaic_size : 0];
mtable_y = (uint16*)mosaic_table[(regs.mosaic_enabled[BG1] == true) ? regs.mosaic_size : 0];
}
int32 psx = ((a * CLIP(hofs - cx)) & ~63) + ((b * CLIP(vofs - cy)) & ~63) + ((b * mtable_y[y]) & ~63) + (cx << 8);
int32 psy = ((c * CLIP(hofs - cx)) & ~63) + ((d * CLIP(vofs - cy)) & ~63) + ((d * mtable_y[y]) & ~63) + (cy << 8);
for(x=0;x<256;x++) {
px = ((m7a + m7b) >> 8);
py = ((m7c + m7d) >> 8);
px = psx + (a * mtable_x[x]);
py = psy + (c * mtable_x[x]);
//mask floating-point bits (low 8 bits)
px >>= 8;
py >>= 8;
switch(regs.mode7_repeat) {
case 0: //screen repitition outside of screen area
@@ -68,20 +79,7 @@ uint8 *wt_sub = sub_windowtable[_bg];
tile = vram[(ty * 128 + tx) << 1];
palette = vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1];
break;
case 2: //character 0 repetition outside of screen area
if(px < 0 || px > 1023 || py < 0 || py > 1023) {
tx = 0;
ty = 0;
} else {
px &= 1023;
py &= 1023;
tx = ((px >> 3) & 127);
ty = ((py >> 3) & 127);
}
tile = vram[(ty * 128 + tx) << 1];
palette = vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1];
break;
case 3: //palette color 0 outside of screen area
case 2: //palette color 0 outside of screen area
if(px < 0 || px > 1023 || py < 0 || py > 1023) {
palette = 0;
} else {
@@ -93,46 +91,61 @@ uint8 *wt_sub = sub_windowtable[_bg];
palette = vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1];
}
break;
case 3: //character 0 repetition outside of screen area
if(px < 0 || px > 1023 || py < 0 || py > 1023) {
tile = 0;
} else {
px &= 1023;
py &= 1023;
tx = ((px >> 3) & 127);
ty = ((py >> 3) & 127);
tile = vram[(ty * 128 + tx) << 1];
}
palette = vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1];
break;
}
if(!palette)goto _end_setpixel;
if(regs.mode7_extbg == false) {
//_pri set at top of function, as it is static
if(regs.mode7_hflip == true) {
_x = 255 - x;
} else {
_x = x;
}
if(bg == BG1) {
_pri = pri0_pos;
} else {
_pri = (palette >> 7) ? bg2a_pri : bg2b_pri;
_pri = (palette >> 7) ? pri1_pos : pri0_pos;
palette &= 0x7f;
if(regs.mode7_hflip == true) {
_x = 255 - x;
} else {
_x = x;
}
}
if(main_colorwindowtable[_x]) {
if(_bg_enabled == true && !wt_main[_x]) {
if(!palette)continue;
if(regs.mode7_hflip == true) {
_x = 255 - x;
} else {
_x = x;
}
if(window_cache[COL].main[_x]) {
uint32 col;
if(regs.direct_color == true && bg == BG1) {
//direct color mode does not apply to bg2, as it is only 128 colors...
col = get_direct_color(0, palette);
} else {
col = get_palette(palette);
}
if(regs.bg_enabled[bg] == true && !wt_main[_x]) {
if(pixel_cache[_x].pri_main < _pri) {
pixel_cache[_x].pri_main = _pri;
pixel_cache[_x].bg_main = 0x80 | _bg;
pixel_cache[_x].src_main = palette;
pixel_cache[_x].bg_main = 0x80 | bg;
pixel_cache[_x].src_main = col;
pixel_cache[_x].color_exempt = false;
}
}
if(_bgsub_enabled == true && !wt_sub[_x]) {
if(regs.bgsub_enabled[bg] == true && !wt_sub[_x]) {
if(pixel_cache[_x].pri_sub < _pri) {
pixel_cache[_x].pri_sub = _pri;
pixel_cache[_x].bg_sub = 0x80 | _bg;
pixel_cache[_x].src_sub = palette;
pixel_cache[_x].bg_sub = 0x80 | bg;
pixel_cache[_x].src_sub = col;
}
}
}
_end_setpixel:
m7a += step_m7a;
m7c += step_m7c;
}
}
#undef CLIP

View File

@@ -207,9 +207,9 @@ int s, x;
bool _bg_enabled = regs.bg_enabled[OAM];
bool _bgsub_enabled = regs.bgsub_enabled[OAM];
uint8 *wt_main = main_windowtable[OAM];
uint8 *wt_sub = sub_windowtable[OAM];
build_window_tables(OAM);
uint8 *wt_main = window_cache[OAM].main;
uint8 *wt_sub = window_cache[OAM].sub;
regs.oam_itemcount = 0;
regs.oam_tilecount = 0;
@@ -240,6 +240,7 @@ uint8 *wt_sub = sub_windowtable[OAM];
regs.range_over |= (regs.oam_itemcount > 32);
if(_bg_enabled == false && _bgsub_enabled == false)return;
int _pri;
for(x=0;x<_screen_width;x++) {
if(oam_line_pri[x] == OAM_PRI_NONE)continue;
@@ -251,19 +252,20 @@ int _pri;
case 3:_pri = pri3_pos;break;
}
if(main_colorwindowtable[x]) {
if(window_cache[COL].main[x]) {
if(_bg_enabled == true && !wt_main[x]) {
if(pixel_cache[x].pri_main < _pri) {
pixel_cache[x].pri_main = _pri;
pixel_cache[x].bg_main = PC_OAM;
pixel_cache[x].src_main = oam_line_pal[x];
pixel_cache[x].src_main = get_palette(oam_line_pal[x]);
pixel_cache[x].color_exempt = (oam_line_pal[x] < 192);
}
}
if(_bgsub_enabled == true && !wt_sub[x]) {
if(pixel_cache[x].pri_sub < _pri) {
pixel_cache[x].pri_sub = _pri;
pixel_cache[x].bg_sub = PC_OAM;
pixel_cache[x].src_sub = oam_line_pal[x];
pixel_cache[x].src_sub = get_palette(oam_line_pal[x]);
}
}
}

View File

@@ -1,11 +1,50 @@
void bPPU::build_window_table(uint8 bg, uint8 *wtbl, bool mainscreen) {
if(mainscreen == true && regs.bg_window_enabled[bg] == false) {
memset(wtbl, 0, _screen_width);
return;
void bPPU::build_window_table(uint8 bg, bool mainscreen) {
uint8 set = true, clr = false;
uint8 *wtbl;
if(mainscreen == true) {
wtbl = (uint8*)window_cache[bg].main;
} else {
wtbl = (uint8*)window_cache[bg].sub;
}
if(mainscreen == false && regs.bgsub_window_enabled[bg] == false) {
memset(wtbl, 0, _screen_width);
return;
if(bg != COL) {
if(mainscreen == true && regs.window_enabled[bg] == false) {
memset(wtbl, 0, _screen_width);
return;
}
if(mainscreen == false && regs.sub_window_enabled[bg] == false) {
memset(wtbl, 0, _screen_width);
return;
}
} else {
uint8 mask;
if(mainscreen == true) {
mask = regs.color_mask;
} else {
mask = regs.colorsub_mask;
}
if(mask == 0) {
//always
memset(wtbl, 1, _screen_width);
return;
}
if(mask == 3) {
//never
memset(wtbl, 0, _screen_width);
return;
}
if(mask == 1) {
//inside window only
set = 1;
clr = 0;
} else { //mask == 2
//outside window only
set = 0;
clr = 1;
}
}
uint16 window1_left, window1_right, window2_left, window2_right;
@@ -24,54 +63,54 @@ bool r;
window2_right <<= 1;
}
if(regs.bg_window1_enabled[bg] == false && regs.bg_window2_enabled[bg] == false) {
memset(wtbl, 0, _screen_width);
} else if(regs.bg_window1_enabled[bg] == true && regs.bg_window2_enabled[bg] == false) {
if(regs.bg_window1_invert[bg] == false) {
if(regs.window1_enabled[bg] == false && regs.window2_enabled[bg] == false) {
memset(wtbl, clr, _screen_width);
} else if(regs.window1_enabled[bg] == true && regs.window2_enabled[bg] == false) {
if(regs.window1_invert[bg] == false) {
for(x=0;x<_screen_width;x++) {
wtbl[x] = (x >= window1_left && x <= window1_right)?true:false;
wtbl[x] = (x >= window1_left && x <= window1_right) ? set : clr;
}
} else {
for(x=0;x<_screen_width;x++) {
wtbl[x] = (x < window1_left || x > window1_right)?true:false;
wtbl[x] = (x < window1_left || x > window1_right) ? set : clr;
}
}
} else if(regs.bg_window1_enabled[bg] == false && regs.bg_window2_enabled[bg] == true) {
if(regs.bg_window2_invert[bg] == false) {
} else if(regs.window1_enabled[bg] == false && regs.window2_enabled[bg] == true) {
if(regs.window2_invert[bg] == false) {
for(x=0;x<_screen_width;x++) {
wtbl[x] = (x >= window2_left && x <= window2_right)?true:false;
wtbl[x] = (x >= window2_left && x <= window2_right) ? set : clr;
}
} else {
for(x=0;x<_screen_width;x++) {
wtbl[x] = (x < window2_left || x > window2_right)?true:false;
wtbl[x] = (x < window2_left || x > window2_right) ? set : clr;
}
}
} else { //if(regs.bg_window1_enabled[bg] == true && regs.bg_window2_enabled[bg] == true) {
} else { //if(regs.window1_enabled[bg] == true && regs.window2_enabled[bg] == true) {
for(x=0;x<_screen_width;x++) {
if(regs.bg_window1_invert[bg] == false) {
if(regs.window1_invert[bg] == false) {
w1_mask = (x >= window1_left && x <= window1_right);
} else {
w1_mask = (x < window1_left || x > window1_right);
}
if(regs.bg_window2_invert[bg] == false) {
if(regs.window2_invert[bg] == false) {
w2_mask = (x >= window2_left && x <= window2_right);
} else {
w2_mask = (x < window2_left || x > window2_right);
}
switch(regs.bg_window_mask[bg]) {
switch(regs.window_mask[bg]) {
case 0: //WINDOWMASK_OR:
wtbl[x] = ((w1_mask | w2_mask) == 1)?true:false;
wtbl[x] = ((w1_mask | w2_mask) == 1) ? set : clr;
break;
case 1: //WINDOWMASK_AND:
wtbl[x] = ((w1_mask & w2_mask) == 1)?true:false;
wtbl[x] = ((w1_mask & w2_mask) == 1) ? set : clr;
break;
case 2: //WINDOWMASK_XOR:
wtbl[x] = ((w1_mask ^ w2_mask) == 1)?true:false;
wtbl[x] = ((w1_mask ^ w2_mask) == 1) ? set : clr;
break;
case 3: //WINDOWMASK_XNOR:
wtbl[x] = ((w1_mask ^ w2_mask) == 0)?true:false;
wtbl[x] = ((w1_mask ^ w2_mask) == 0) ? set : clr;
break;
}
}
@@ -79,105 +118,13 @@ bool r;
}
void bPPU::build_window_tables(uint8 bg) {
build_window_table(bg, main_windowtable[bg], true);
build_window_table(bg, sub_windowtable[bg], false);
}
void bPPU::build_color_window_table(uint8 *wtbl, uint8 mask) {
if(mask == 0) {
//always
memset(wtbl, 1, _screen_width);
return;
if(window_cache[bg].main_dirty == true) {
window_cache[bg].main_dirty = false;
build_window_table(bg, true);
}
if(mask == 3) {
//never
memset(wtbl, 0, _screen_width);
return;
}
int _true, _false;
if(mask == 1) {
//inside window only
_true = 1;
_false = 0;
} else { //mask == 2
//outside window only
_true = 0;
_false = 1;
}
uint16 window1_left, window1_right, window2_left, window2_right;
int w1_mask, w2_mask; //1 = masked, 0 = not masked
int x;
bool r;
window1_left = regs.window1_left;
window1_right = regs.window1_right;
window2_left = regs.window2_left;
window2_right = regs.window2_right;
if(_screen_width == 512) {
window1_left <<= 1;
window1_right <<= 1;
window2_left <<= 1;
window2_right <<= 1;
}
if(regs.color_window1_enabled == false && regs.color_window2_enabled == false) {
memset(wtbl, _false, _screen_width);
} else if(regs.color_window1_enabled == true && regs.color_window2_enabled == false) {
if(regs.color_window1_invert == false) {
for(x=0;x<_screen_width;x++) {
wtbl[x] = (x >= window1_left && x <= window1_right)?_true:_false;
}
} else {
for(x=0;x<_screen_width;x++) {
wtbl[x] = (x < window1_left || x > window1_right)?_true:_false;
}
}
} else if(regs.color_window1_enabled == false && regs.color_window2_enabled == true) {
if(regs.color_window2_invert == false) {
for(x=0;x<_screen_width;x++) {
wtbl[x] = (x >= window2_left && x <= window2_right)?_true:_false;
}
} else {
for(x=0;x<_screen_width;x++) {
wtbl[x] = (x < window2_left || x > window2_right)?_true:_false;
}
}
} else { //if(regs.color_window1_enabled == true && regs.color_window2_enabled == true) {
for(x=0;x<_screen_width;x++) {
if(regs.color_window1_invert == false) {
w1_mask = (x >= window1_left && x <= window1_right);
} else {
w1_mask = (x < window1_left || x > window1_right);
}
if(regs.color_window2_invert == false) {
w2_mask = (x >= window2_left && x <= window2_right);
} else {
w2_mask = (x < window2_left || x > window2_right);
}
switch(regs.color_window_mask) {
case 0: //WINDOWMASK_OR:
wtbl[x] = ((w1_mask | w2_mask) == 1)?_true:_false;
break;
case 1: //WINDOWMASK_AND:
wtbl[x] = ((w1_mask & w2_mask) == 1)?_true:_false;
break;
case 2: //WINDOWMASK_XOR:
wtbl[x] = ((w1_mask ^ w2_mask) == 1)?_true:_false;
break;
case 3: //WINDOWMASK_XNOR:
wtbl[x] = ((w1_mask ^ w2_mask) == 0)?_true:_false;
break;
}
}
if(window_cache[bg].sub_dirty == true) {
window_cache[bg].sub_dirty = false;
build_window_table(bg, false);
}
}
void bPPU::build_color_window_tables() {
build_color_window_table(main_colorwindowtable, regs.color_mask);
build_color_window_table(sub_colorwindowtable, regs.colorsub_mask);
}

View File

@@ -1,230 +0,0 @@
void bPPU::set_sprite_attributes(uint8 sprite_num) {
uint32 t;
uint8 size, b;
uint16 x;
t = *((uint32*)oam + sprite_num);
b = oam[512 + (sprite_num >> 2)];
switch(sprite_num & 3) {
case 0: size = !!(b & 0x02); x = (b & 0x01)?0x100:0; break;
case 1: size = !!(b & 0x08); x = (b & 0x04)?0x100:0; break;
case 2: size = !!(b & 0x20); x = (b & 0x10)?0x100:0; break;
case 3: size = !!(b & 0x80); x = (b & 0x40)?0x100:0; break;
}
current_sprite.num = sprite_num;
current_sprite.priority = (t >> 28) & 3;
current_sprite.x = x | (t & 0xff);
current_sprite.y = ((t >> 8) + 1) & 0xff;
current_sprite.v_flip = !!(t & 0x80000000);
current_sprite.h_flip = !!(t & 0x40000000);
current_sprite.palette = (t >> 25) & 7;
current_sprite.character = (t >> 16) & 0x01ff;
//size: 0 = small, 1 = large
switch(regs.oam_basesize) {
case 0:
if(!size) { current_sprite.width = 8; current_sprite.height = 8; }
else { current_sprite.width = 16; current_sprite.height = 16; }
break;
case 1:
if(!size) { current_sprite.width = 8; current_sprite.height = 8; }
else { current_sprite.width = 32; current_sprite.height = 32; }
break;
case 2:
if(!size) { current_sprite.width = 8; current_sprite.height = 8; }
else { current_sprite.width = 64; current_sprite.height = 64; }
break;
case 3:
if(!size) { current_sprite.width = 16; current_sprite.height = 16; }
else { current_sprite.width = 32; current_sprite.height = 32; }
break;
case 4:
if(!size) { current_sprite.width = 16; current_sprite.height = 16; }
else { current_sprite.width = 64; current_sprite.height = 64; }
break;
case 5:
if(!size) { current_sprite.width = 32; current_sprite.height = 32; }
else { current_sprite.width = 64; current_sprite.height = 64; }
break;
case 6:
if(!size) { current_sprite.width = 16; current_sprite.height = 32; }
else { current_sprite.width = 32; current_sprite.height = 64; }
break;
case 7:
if(!size) { current_sprite.width = 16; current_sprite.height = 32; }
else { current_sprite.width = 32; current_sprite.height = 32; }
break;
}
}
void bPPU::render_oam_sprite() {
uint16 pos, col, chr, tiledata_inc;
uint8 d0, d1, d2, d3, pal_index;
int x, y, z, x1, mx, mask, p;
int tile_width;
//uint8 item_inc, tile_inc;
tile_width = current_sprite.width >> 3; //e.x. 16x16 sprite = 2x2 tiles
regs.oam_itemcount++;
//add all visible tiles to regs.oam_tilecount
if(current_sprite.x > 256) {
//if sprite clips on left edge of the screen
regs.oam_tilecount += ((current_sprite.x + current_sprite.width + 7) & 511) >> 3;
} else if(current_sprite.x + current_sprite.width >= 257) {
//if sprite clips on right edge of screen
regs.oam_tilecount += (257 - current_sprite.x + 7) >> 3;
} else {
//if entire sprite is visible
regs.oam_tilecount += current_sprite.width >> 3;
}
//if(_y == 0xa0)dprintf("* oam_itemcount=%d, oam_tilecount=%d", regs.oam_itemcount, regs.oam_tilecount);
if(regs.bg_enabled[OAM] == false && regs.bgsub_enabled[OAM] == false)return;
if(regs.oam_tilecount > 34)return;
if(regs.oam_itemcount > 32)return;
if(_interlace == true && _screen_width == 512) {
y = (_y << 1) + _interlace_field;
} else {
y = _y;
}
x = current_sprite.x;
if(_screen_width == 512) {
x <<= 1;
}
if(current_sprite.v_flip) {
y = ((current_sprite.height - 1) - (_y - current_sprite.y));
} else {
y = (_y - current_sprite.y);
}
if(regs.oam_halve == true) {
y <<= 1;
if(_interlace == true && _screen_width == 512) {
y += _interlace_field;
}
}
y &= 255;
chr = current_sprite.character;
tiledata_inc = (chr & 0x100)?(regs.oam_nameselect << 13):0;
chr += (y >> 3) << 4;
pal_index = (current_sprite.palette << 4);
for(x1=0;x1<tile_width;x1++) {
if(current_sprite.h_flip)mx = (tile_width - 1) - x1;
else mx = x1;
pos = regs.oam_tdaddr + ((chr + mx) << 5) + ((y & 7) << 1) + tiledata_inc;
d0 = vram[pos ];
d1 = vram[pos + 1];
d2 = vram[pos + 16];
d3 = vram[pos + 17];
for(z=0;z<8;z++) {
if(current_sprite.h_flip) {
mask = 0x01 << z;
} else {
mask = 0x80 >> z;
}
x &= 511;
if(x < _screen_width) {
col = 0;
if(d0 & mask)col += 1;
if(d1 & mask)col += 2;
if(d2 & mask)col += 4;
if(d3 & mask)col += 8;
if(col) {
col += pal_index;
col += 128;
if(oam_line_pri[x] == OAM_PRI_NONE) {
oam_line_pal[x] = col;
oam_line_pri[x] = current_sprite.priority;
}
if(_screen_width == 512) {
if(oam_line_pri[x + 1] == OAM_PRI_NONE) {
oam_line_pal[x + 1] = col;
oam_line_pri[x + 1] = current_sprite.priority;
}
}
}
}
x += (_screen_width == 512)?2:1;
}
}
}
void bPPU::render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos) {
int s, x;
bool _bg_enabled = regs.bg_enabled[OAM];
bool _bgsub_enabled = regs.bgsub_enabled[OAM];
uint8 *wt_main = main_windowtable[OAM];
uint8 *wt_sub = sub_windowtable[OAM];
build_window_tables(OAM);
regs.oam_itemcount = 0;
regs.oam_tilecount = 0;
memset(oam_line_pri, OAM_PRI_NONE, 512);
for(s=0;s<128;s++) {
set_sprite_attributes((s + regs.oam_firstsprite) & 127);
//if sprite is entirely offscreen and doesn't wrap around to the left side of the screen,
//then it is not counted. 256 is correct, and not 255 -- as one might first expect
if(current_sprite.x > 256 && ((current_sprite.x + current_sprite.width) & 511) > 256)continue;
//if(_y == 0xa0)
//dprintf("* y: %d, fs: %d, sprite: %d, width: %d, height: %d, x: %d, y: %d",
// _y, regs.oam_firstsprite, (s + regs.oam_firstsprite) & 127,
// current_sprite.width, current_sprite.height, current_sprite.x, current_sprite.y);
if(regs.oam_halve == false) {
if(_y >= current_sprite.y && _y < (current_sprite.y + current_sprite.height)) {
render_oam_sprite();
} else if((current_sprite.y + current_sprite.height) >= 256 && _y < ((current_sprite.y + current_sprite.height) & 255)) {
render_oam_sprite();
}
} else {
if(_y >= current_sprite.y && _y < (current_sprite.y + (current_sprite.height >> 1))) {
render_oam_sprite();
} else if((current_sprite.y + (current_sprite.height >> 1)) >= 256 && _y < ((current_sprite.y + (current_sprite.height >> 1)) & 255)) {
render_oam_sprite();
}
}
}
regs.time_over |= (regs.oam_tilecount > 34);
regs.range_over |= (regs.oam_itemcount > 32);
//dprintf("* line: %d, t = %d, r = %d, tc = %d, ic = %d", _y,
//regs.time_over, regs.range_over, regs.oam_tilecount, regs.oam_itemcount);
if(_bg_enabled == false && _bgsub_enabled == false)return;
int _pri;
for(x=0;x<_screen_width;x++) {
if(oam_line_pri[x] == OAM_PRI_NONE)continue;
switch(oam_line_pri[x]) {
case 0:_pri = pri0_pos;break;
case 1:_pri = pri1_pos;break;
case 2:_pri = pri2_pos;break;
case 3:_pri = pri3_pos;break;
}
if(main_colorwindowtable[x]) {
if(_bg_enabled == true && !wt_main[x]) {
if(pixel_cache[x].pri_main < _pri) {
pixel_cache[x].pri_main = _pri;
pixel_cache[x].bg_main = PC_OAM;
pixel_cache[x].src_main = oam_line_pal[x];
}
}
if(_bgsub_enabled == true && !wt_sub[x]) {
if(pixel_cache[x].pri_sub < _pri) {
pixel_cache[x].pri_sub = _pri;
pixel_cache[x].bg_sub = PC_OAM;
pixel_cache[x].src_sub = oam_line_pal[x];
}
}
}
}
}

View File

@@ -1,27 +1,38 @@
#include "../base.h"
PPUOutput::PPUOutput() {
buffer = (uint16*)memalloc(512 * 478 * 2, "PPUOutput::buffer");
memset(buffer, 0, 512 * 478 * 2);
hires = false;
interlace = false;
for(int i=0;i<239;i++) {
line[i].hires = false;
line[i].interlace = false;
void PPU::get_scanline_info(scanline_info *info) {
info->hires = scanline_is_hires();
info->interlace = cpu->interlace();
}
void PPU::enable_renderer(bool r) { status.render_output = r; }
bool PPU::renderer_enabled() { return status.render_output; }
void PPU::frame() {
static fr = 0, fe = 0;
static time_t prev, curr;
fe++;
if(status.render_output)fr++;
time(&curr);
if(curr != prev) {
status.frames_updated = true;
status.frames_rendered = fr;
status.frames_executed = fe;
fr = fe = 0;
}
prev = curr;
}
PPUOutput::~PPUOutput() {
if(buffer)memfree(buffer, "PPUOutput::buffer");
}
void PPU::set_frameskip(int fs) {}
PPU::PPU() {
status.render_output = true;
status.frames_updated = false;
status.frames_rendered = 0;
status.frames_executed = 0;
ppu1_version = 1;
ppu2_version = 1;
mmio = &mmio_unmapped;
output = new PPUOutput();
}
PPU::~PPU() {
if(output)delete(output);
}
PPU::~PPU() {}

View File

@@ -1,19 +1,36 @@
class PPUOutput {
public:
bool hires, interlace;
struct {
bool hires, interlace;
}line[239];
uint16 *buffer;
PPUOutput();
~PPUOutput();
};
class PPU {
public:
int _y;
PPUOutput *output;
MMIO *mmio;
//this struct should be read-only to
//functions outside of this class
struct {
bool render_output;
bool frames_updated;
uint32 frames_rendered;
uint32 frames_executed;
} status;
//PPU1 version number
//* 1 is known
//* reported by $213e
uint8 ppu1_version;
//PPU2 version number
//* 1 and 3 are known
//* reported by $213f
uint8 ppu2_version;
int _y;
MMIO *mmio;
struct scanline_info {
bool hires;
bool interlace;
};
virtual bool scanline_is_hires() = 0;
virtual void get_scanline_info(scanline_info *info);
virtual uint8 vram_read (uint16 addr) = 0;
virtual void vram_write (uint16 addr, uint8 value) = 0;
virtual uint8 oam_read (uint16 addr) = 0;
@@ -25,10 +42,12 @@ MMIO *mmio;
virtual void run() = 0;
virtual void scanline() = 0;
virtual void frame() = 0;
virtual void render_scanline() = 0;
virtual void frame();
virtual void power() = 0;
virtual void reset() = 0;
virtual void set_frameskip(int fs);
virtual void enable_renderer(bool r);
virtual bool renderer_enabled();
PPU();
~PPU();

73
src/reader/filereader.cpp Normal file
View File

@@ -0,0 +1,73 @@
uint32 FileReader::size() {
return fsize;
}
//This function will allocate memory even if open() fails.
//This is needed so that when SRAM files do not exist, the
//memory for the SRAM data will be allocated still.
//The memory is flushed to 0x00 when no file is opened.
void FileReader::read(uint8 **buffer, uint32 length) {
uint8 *data;
if(length == 0) {
//read the entire file into RAM
data = (uint8*)memalloc(fsize);
memset(data, 0, fsize);
if(fp)fread(data, 1, fsize, fp);
} else if(length > fsize) {
//read the entire file into RAM, pad the rest with 0x00s
data = (uint8*)memalloc(length);
memset(data, 0, length);
if(fp)fread(data, 1, fsize, fp);
} else { //fsize >= length
//read only what was requested
data = (uint8*)memalloc(length);
memset(data, 0, length);
if(fp)fread(data, 1, length, fp);
}
*buffer = data;
}
bool FileReader::open(char *fn) {
fp = fopen(fn, "rb");
if(!fp)return false;
fseek(fp, 0, SEEK_END);
fsize = ftell(fp);
fseek(fp, 0, SEEK_SET);
//empty file?
if(fsize == 0) {
fclose(fp);
fp = 0;
return false;
}
return true;
}
void FileReader::close() {
if(fp) {
fclose(fp);
fp = 0;
}
}
void FileWriter::write(uint8 *buffer, uint32 length) {
if(!fp)return;
fwrite(buffer, 1, length, fp);
}
bool FileWriter::open(char *fn) {
fp = fopen(fn, "wb");
if(!fp)return false;
return true;
}
void FileWriter::close() {
if(fp) {
fclose(fp);
fp = 0;
}
}

27
src/reader/filereader.h Normal file
View File

@@ -0,0 +1,27 @@
class FileReader : public Reader {
private:
FILE *fp;
uint32 fsize;
public:
uint32 size();
void read(uint8 **buffer, uint32 length = 0);
bool open(char *fn);
void close();
FileReader() { fp = 0; fsize = 0; }
~FileReader() { if(fp)fclose(fp); }
};
class FileWriter : public Writer {
private:
FILE *fp;
public:
void write(uint8 *buffer, uint32 length);
bool open(char *fn);
void close();
FileWriter() { fp = 0; }
~FileWriter() { if(fp)fclose(fp); }
};

View File

@@ -1,79 +1,2 @@
#include "../base.h"
uint32 FileReader::size() {
return fsize;
}
/*
This function will allocate memory even if open() fails.
This is needed so that when SRAM files do not exist, the
memory for the SRAM data will be allocated still.
The memory is flushed to 0x00 when no file is opened.
*/
void FileReader::read(uint8 **buffer, uint32 length) {
uint8 *data;
if(length == 0) {
/* read the entire file into RAM */
data = (uint8*)memalloc(fsize);
memset(data, 0, fsize);
if(fp)fread(data, 1, fsize, fp);
} else if(length > fsize) {
/* read the entire file into RAM, pad the rest with 0x00s */
data = (uint8*)memalloc(length);
memset(data, 0, length);
if(fp)fread(data, 1, fsize, fp);
} else { //fsize >= length
/* read as much of the file as possible, truncate the rest */
data = (uint8*)memalloc(length);
memset(data, 0, length);
if(fp)fread(data, 1, length, fp);
}
*buffer = data;
}
bool FileReader::open(uint8 type, char *fn) {
fp = fopen(fn, "rb");
if(!fp)return false;
fseek(fp, 0, SEEK_END);
fsize = ftell(fp);
fseek(fp, 0, SEEK_SET);
if(type == TYPE_ROM) {
/* remove header if it exists */
if((fsize & 0xfff) == 0x200) {
fsize -= 0x200;
fseek(fp, 0x200, SEEK_SET);
}
}
/* empty file? */
if(fsize == 0) {
fclose(fp);
return false;
}
return true;
}
void FileReader::close() {
if(fp)fclose(fp);
}
void FileWriter::write(uint8 *buffer, uint32 length) {
if(!fp)return;
fwrite(buffer, 1, length, fp);
}
bool FileWriter::open(char *fn) {
fp = fopen(fn, "wb");
if(!fp)return false;
return true;
}
void FileWriter::close() {
if(fp)fclose(fp);
}
#include "filereader.cpp"

View File

@@ -4,39 +4,9 @@ public:
virtual void read(uint8 **buffer, uint32 length = 0) = 0;
};
class FileReader : public Reader {
private:
FILE *fp;
uint32 fsize;
public:
enum {
TYPE_ROM = 0,
TYPE_SRAM = 1
};
uint32 size();
void read(uint8 **buffer, uint32 length = 0);
bool open(uint8 type, char *fn);
void close();
FileReader() { fp = 0; fsize = 0; }
~FileReader() { if(fp)fclose(fp); }
};
class Writer {
public:
virtual void write(uint8 *buffer, uint32 length) = 0;
};
class FileWriter : public Writer {
private:
FILE *fp;
public:
void write(uint8 *buffer, uint32 length);
bool open(char *fn);
void close();
FileWriter() { fp = 0; }
~FileWriter() { if(fp)fclose(fp); }
};
#include "filereader.h"

View File

@@ -1,13 +1,15 @@
CC = c++
CFLAGS = -O2
CFLAGS = -O3 -fomit-frame-pointer -ffast-math
OBJS = sdlmain.o \
libstring.o libconfig.o \
reader.o \
memory.o bmemory.o \
cpu.o bcpu.o \
apu.o bapu.o bapuskip.o \
bdsp.o \
ppu.o bppu.o \
snes.o
snes.o \
srtc.o sdd1.o
all: $(OBJS)
$(CC) $(CFLAGS) $(OBJS) `sdl11-config --cflags --libs` -o bsnes_sdl
@@ -59,6 +61,12 @@ bapu.o: ../apu/bapu/*
bapuskip.o: ../apu/bapuskip/*
$(CC) $(CFLAGS) -c ../apu/bapuskip/bapuskip.cpp
###########
### dsp ###
###########
bdsp.o: ../dsp/bdsp/*
$(CC) $(CFLAGS) -c ../dsp/bdsp/bdsp.cpp
###########
### ppu ###
###########
@@ -71,7 +79,7 @@ bppu.o: ../ppu/bppu/*
##############
### reader ###
##############
reader.o: ../reader/reader.cpp ../reader/reader.h
reader.o: ../reader/*.cpp ../reader/*.h
$(CC) $(CFLAGS) -c ../reader/reader.cpp
############
@@ -79,3 +87,15 @@ reader.o: ../reader/reader.cpp ../reader/reader.h
############
snes.o: ../snes/*.cpp ../snes/*.h
$(CC) $(CFLAGS) -c ../snes/snes.cpp
############
### srtc ###
############
srtc.o: ../chip/srtc/*.cpp ../chip/srtc/*.h
$(CC) $(CFLAGS) -c ../chip/srtc/srtc.cpp
############
### sdd1 ###
############
sdd1.o: ../chip/sdd1/*.cpp ../chip/sdd1/*.h
$(CC) $(CFLAGS) -c ../chip/sdd1/sdd1.cpp

View File

@@ -1,13 +1,15 @@
CC = cl
CFLAGS = /nologo /O2 /Ogityb2 /Gr /Gs
CFLAGS = /nologo /O2 /Ogityb2 /Gr /Gs /DARCH_LSB
OBJS = sdlmain.obj \
libstring.obj libconfig.obj \
reader.obj \
memory.obj bmemory.obj \
cpu.obj bcpu.obj \
apu.obj bapu.obj bapuskip.obj \
bdsp.obj \
ppu.obj bppu.obj \
snes.obj
snes.obj \
srtc.obj sdd1.obj
LIBS = kernel32.lib user32.lib gdi32.lib sdlmain.lib sdl.lib
all: $(OBJS)
@@ -60,6 +62,12 @@ bapu.obj: ../apu/bapu/*
bapuskip.obj: ../apu/bapuskip/*
$(CC) $(CFLAGS) /c ../apu/bapuskip/bapuskip.cpp
###########
### dsp ###
###########
bdsp.obj: ../dsp/bdsp/*
$(CC) $(CFLAGS) /c ../dsp/bdsp/bdsp.cpp
###########
### ppu ###
###########
@@ -72,7 +80,7 @@ bppu.obj: ../ppu/bppu/*
##############
### reader ###
##############
reader.obj: ../reader/reader.cpp ../reader/reader.h
reader.obj: ../reader/*.cpp ../reader/*.h
$(CC) $(CFLAGS) /c ../reader/reader.cpp
############
@@ -80,3 +88,15 @@ reader.obj: ../reader/reader.cpp ../reader/reader.h
############
snes.obj: ../snes/*.cpp ../snes/*.h
$(CC) $(CFLAGS) /c ../snes/snes.cpp
############
### srtc ###
############
srtc.obj: ../chip/srtc/*.cpp ../chip/srtc/*.h
$(CC) $(CFLAGS) /c ../chip/srtc/srtc.cpp
############
### sdd1 ###
############
sdd1.obj: ../chip/sdd1/*.cpp ../chip/sdd1/*.h
$(CC) $(CFLAGS) /c ../chip/sdd1/sdd1.cpp

View File

@@ -1,13 +1,13 @@
void bSNES::set_status(uint32 new_status) { run_status = new_status; }
uint32 bSNES::get_status() { return run_status; }
void bSNES::snes_run() {
void bSNES::run() {
if(!rom_image->loaded())return;
switch(run_status) {
case RUN:
while(update_frame == false) {
run();
SNES::run();
}
update_frame = false;
render();
@@ -17,29 +17,46 @@ void bSNES::snes_run() {
}
}
void bSNES::render_frame() {}
void bSNES::video_run() {
if(ppu->status.frames_updated) {
char s[512], t[512];
ppu->status.frames_updated = false;
// if((bool)config::gui.show_fps == true) {
sprintf(s, "%s : %d fps", BSNES_TITLE, ppu->status.frames_executed);
// if(w_main->frameskip != 0) {
// sprintf(t, " (%d frames)", ppu->status.frames_rendered);
// strcat(s, t);
// }
SDL_WM_SetCaption(s, 0);
// }
}
render();
}
void bSNES::sound_run() {}
/***********************
*** Input functions ***
***********************/
//It would appear that keystate does not need to be free'd
//It would appear that keystate does not need to be released
//after calling SDL_GetKeyState... doing so causes libSDL
//to throw error messages about a bad free call to stdout...
void bSNES::poll_input() {
void bSNES::poll_input(uint8 type) {
uint8 *keystate = SDL_GetKeyState(0);
joypad1.up = keystate[cfg.input.joypad1.up];
joypad1.down = keystate[cfg.input.joypad1.down];
joypad1.left = keystate[cfg.input.joypad1.left];
joypad1.right = keystate[cfg.input.joypad1.right];
joypad1.select = keystate[cfg.input.joypad1.select];
joypad1.start = keystate[cfg.input.joypad1.start];
joypad1.y = keystate[cfg.input.joypad1.y];
joypad1.b = keystate[cfg.input.joypad1.b];
joypad1.x = keystate[cfg.input.joypad1.x];
joypad1.a = keystate[cfg.input.joypad1.a];
joypad1.l = keystate[cfg.input.joypad1.l];
joypad1.r = keystate[cfg.input.joypad1.r];
joypad1.up = keystate[(int)config::input.joypad1.up];
joypad1.down = keystate[(int)config::input.joypad1.down];
joypad1.left = keystate[(int)config::input.joypad1.left];
joypad1.right = keystate[(int)config::input.joypad1.right];
joypad1.select = keystate[(int)config::input.joypad1.select];
joypad1.start = keystate[(int)config::input.joypad1.start];
joypad1.y = keystate[(int)config::input.joypad1.y];
joypad1.b = keystate[(int)config::input.joypad1.b];
joypad1.x = keystate[(int)config::input.joypad1.x];
joypad1.a = keystate[(int)config::input.joypad1.a];
joypad1.l = keystate[(int)config::input.joypad1.l];
joypad1.r = keystate[(int)config::input.joypad1.r];
//It's impossible to hold both up+down, or left+right down
//at the same time on a directional pad; and besides, allowing

View File

@@ -14,13 +14,15 @@ bJoypad joypad1, joypad2;
public:
enum { STOP = 0, RUN };
void run();
void video_run();
void sound_run();
void set_status(uint32 new_status);
uint32 get_status();
void snes_run();
void render_frame();
//input functions
void poll_input();
void poll_input(uint8 type);
bool get_input_status(uint8 device, uint8 button);
void notify(uint32 message, uint32 param1 = 0, uint32 param2 = 0);

Some files were not shown because too many files have changed in this diff Show More