mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-08-11 01:34:01 +02:00
Update to v101r24 release.
byuu says: Changelog: - SMS: extended bus mapping of in/out ports: now decoding them fully inside ms/bus - SMS: moved Z80 disassembly code from processor/z80 to ms/cpu (cosmetic) - SMS: hooked up non-functional silent PSG sample generation, so I can cap the framerate at 60fps - SMS: hooked up the VDP main loop: 684 clocks/scanline, 262 scanlines/frame (no PAL support yet) - SMS: emulated the VDP Vcounter and Hcounter polling ... hopefully it's right, as it's very bizarre - SMS: emulated VDP in/out ports (data read, data write, status read, control write, register write) - SMS: decoding and caching all VDP register flags (variable names will probably change) - nall: \#undef IN on Windows port (prevent compilation warning on processor/z80) Watching Sonic the Hedgehog, I can definitely see some VDP register writes going through, which is a good sign. Probably the big thing that's needed before I can get enough into the VDP to start showing graphics is interrupt support. And interrupts are never fun to figure out :/ What really sucks on this front is I'm flying blind on the Z80 CPU core. Without a working VDP, I can't run any Z80 test ROMs to look for CPU bugs. And the CPU is certainly too buggy still to run said test ROM anyway. I can't find any SMS emulators with trace logging from reset. Such logs vastly accelerate tracking down CPU logic bugs, so without them, it's going to take a lot longer.
This commit is contained in:
@@ -12,7 +12,7 @@ using namespace nall;
|
|||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const string Name = "higan";
|
static const string Name = "higan";
|
||||||
static const string Version = "101.23";
|
static const string Version = "101.24";
|
||||||
static const string Author = "byuu";
|
static const string Author = "byuu";
|
||||||
static const string License = "GPLv3";
|
static const string License = "GPLv3";
|
||||||
static const string Website = "http://byuu.org/";
|
static const string Website = "http://byuu.org/";
|
||||||
|
@@ -15,14 +15,37 @@ auto Bus::write(uint16 addr, uint8 data) -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto Bus::in(uint8 addr) -> uint8 {
|
auto Bus::in(uint8 addr) -> uint8 {
|
||||||
switch(addr) {
|
switch(addr >> 6) {
|
||||||
case 0x7e: return vdp.in(addr);
|
|
||||||
case 0x7f: return vdp.in(addr);
|
case 0: {
|
||||||
|
return 0xff; //SMS1 = MDR, SMS2 = 0xff
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 1: {
|
||||||
|
return !addr.bit(0) ? vdp.vcounter() : vdp.hcounter();
|
||||||
|
}
|
||||||
|
|
||||||
|
case 2: {
|
||||||
|
return !addr.bit(0) ? vdp.data() : vdp.status();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return 0x00;
|
return 0x00;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Bus::out(uint8 addr, uint8 data) -> void {
|
auto Bus::out(uint8 addr, uint8 data) -> void {
|
||||||
|
switch(addr >> 6) {
|
||||||
|
|
||||||
|
case 2: {
|
||||||
|
return !addr.bit(0) ? vdp.data(data) : vdp.control(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
case 3: {
|
||||||
|
return; //unmapped
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,12 @@ auto CPU::Enter() -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::main() -> void {
|
auto CPU::main() -> void {
|
||||||
|
#if 1
|
||||||
|
if(instructionsExecuted < 20)
|
||||||
|
print(disassemble(r.pc), "\n");
|
||||||
|
instructionsExecuted++;
|
||||||
|
#endif
|
||||||
|
|
||||||
instruction();
|
instruction();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,6 +32,8 @@ auto CPU::power() -> void {
|
|||||||
auto CPU::reset() -> void {
|
auto CPU::reset() -> void {
|
||||||
Z80::reset();
|
Z80::reset();
|
||||||
create(CPU::Enter, system.colorburst());
|
create(CPU::Enter, system.colorburst());
|
||||||
|
|
||||||
|
instructionsExecuted = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -7,6 +7,9 @@ struct CPU : Processor::Z80, Thread {
|
|||||||
|
|
||||||
auto power() -> void;
|
auto power() -> void;
|
||||||
auto reset() -> void;
|
auto reset() -> void;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint64 instructionsExecuted;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern CPU cpu;
|
extern CPU cpu;
|
||||||
|
@@ -10,6 +10,7 @@ auto PSG::Enter() -> void {
|
|||||||
|
|
||||||
auto PSG::main() -> void {
|
auto PSG::main() -> void {
|
||||||
step(1);
|
step(1);
|
||||||
|
stream->sample(0.0, 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PSG::step(uint clocks) -> void {
|
auto PSG::step(uint clocks) -> void {
|
||||||
@@ -22,6 +23,7 @@ auto PSG::power() -> void {
|
|||||||
|
|
||||||
auto PSG::reset() -> void {
|
auto PSG::reset() -> void {
|
||||||
create(PSG::Enter, system.colorburst());
|
create(PSG::Enter, system.colorburst());
|
||||||
|
stream = Emulator::audio.createStream(2, system.colorburst());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
//TI SN76489
|
//TI SN76489
|
||||||
|
|
||||||
struct PSG : Thread {
|
struct PSG : Thread {
|
||||||
|
shared_pointer<Emulator::Stream> stream;
|
||||||
|
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
auto step(uint clocks) -> void;
|
auto step(uint clocks) -> void;
|
||||||
|
152
higan/ms/vdp/io.cpp
Normal file
152
higan/ms/vdp/io.cpp
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
auto VDP::vcounter() -> uint8 {
|
||||||
|
if(io.lines240) {
|
||||||
|
//NTSC 256x240
|
||||||
|
return io.vcounter;
|
||||||
|
} else if(io.lines224) {
|
||||||
|
//NTSC 256x224
|
||||||
|
return io.vcounter <= 234 ? io.vcounter : io.vcounter - 6;
|
||||||
|
} else {
|
||||||
|
//NTSC 256x192
|
||||||
|
return io.vcounter <= 218 ? io.vcounter : io.vcounter - 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto VDP::hcounter() -> uint8 {
|
||||||
|
uint hcounter = io.hcounter >> 2;
|
||||||
|
return hcounter <= 233 ? hcounter : hcounter - 86;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto VDP::data() -> uint8 {
|
||||||
|
io.controlLatch = 0;
|
||||||
|
|
||||||
|
auto data = io.vramLatch;
|
||||||
|
io.vramLatch = vram[io.address++];
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto VDP::status() -> uint8 {
|
||||||
|
io.controlLatch = 0;
|
||||||
|
|
||||||
|
return 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto VDP::data(uint8 data) -> void {
|
||||||
|
io.controlLatch = 0;
|
||||||
|
|
||||||
|
if(io.code <= 2) {
|
||||||
|
vram[io.address++] = data;
|
||||||
|
} else {
|
||||||
|
cram[io.address++ & 0x3f] = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto VDP::control(uint8 data) -> void {
|
||||||
|
if(io.controlLatch == 0) {
|
||||||
|
io.controlLatch = 1;
|
||||||
|
io.address.bits(0,7) = data.bits(0,7);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
io.controlLatch = 0;
|
||||||
|
io.address.bits(8,13) = data.bits(0,5);
|
||||||
|
io.code.bits(0,1) = data.bits(6,7);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(io.code == 0) {
|
||||||
|
io.vramLatch = vram[io.address++];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(io.code == 2) {
|
||||||
|
registerWrite(io.address.bits(11,8), io.address.bits(7,0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto VDP::registerWrite(uint4 addr, uint8 data) -> void {
|
||||||
|
switch(addr) {
|
||||||
|
|
||||||
|
//mode control 1
|
||||||
|
case 0x0: {
|
||||||
|
io.externalSync = data.bit(0);
|
||||||
|
io.extendedHeight = data.bit(1);
|
||||||
|
io.mode4 = data.bit(2);
|
||||||
|
io.spriteShift = data.bit(3);
|
||||||
|
io.lineInterrupts = data.bit(4);
|
||||||
|
io.leftClip = data.bit(5);
|
||||||
|
io.horizontalScrollLock = data.bit(6);
|
||||||
|
io.verticalScrollLock = data.bit(7);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//mode control 2
|
||||||
|
case 0x1: {
|
||||||
|
io.spriteDouble = data.bit(0);
|
||||||
|
io.spriteTile = data.bit(1);
|
||||||
|
io.lines240 = data.bit(3);
|
||||||
|
io.lines224 = data.bit(4);
|
||||||
|
io.frameInterrupts = data.bit(5);
|
||||||
|
io.displayEnable = data.bit(6);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//name table base address
|
||||||
|
case 0x2: {
|
||||||
|
io.nameTableMask = data.bit(0);
|
||||||
|
io.nameTableAddress = data.bits(1,3);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//color table base address
|
||||||
|
case 0x3: {
|
||||||
|
io.colorTableAddress = data.bits(0,7);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//pattern table base address
|
||||||
|
case 0x4: {
|
||||||
|
io.patternTableAddress = data.bits(0,7);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//sprite attribute table base address
|
||||||
|
case 0x5: {
|
||||||
|
io.spriteAttributeTableMask = data.bit(0);
|
||||||
|
io.spriteAttributeTableAddress = data.bits(1,6);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//sprite pattern table base address
|
||||||
|
case 0x6: {
|
||||||
|
io.spritePatternTableMask = data.bits(0,1);
|
||||||
|
io.spritePatternTableAddress = data.bit(2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//backdrop color
|
||||||
|
case 0x7: {
|
||||||
|
io.backdropColor = data.bits(0,3);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//horizontal scroll offset
|
||||||
|
case 0x8: {
|
||||||
|
io.hscroll = data.bits(0,7);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//vertical scroll offset
|
||||||
|
case 0x9: {
|
||||||
|
io.vscroll = data.bits(0,7);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//line counter
|
||||||
|
case 0xa: {
|
||||||
|
io.lineCounter = data.bits(0,7);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//0xb - 0xf unmapped
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -3,6 +3,7 @@
|
|||||||
namespace MasterSystem {
|
namespace MasterSystem {
|
||||||
|
|
||||||
VDP vdp;
|
VDP vdp;
|
||||||
|
#include "io.cpp"
|
||||||
|
|
||||||
auto VDP::Enter() -> void {
|
auto VDP::Enter() -> void {
|
||||||
while(true) scheduler.synchronize(), vdp.main();
|
while(true) scheduler.synchronize(), vdp.main();
|
||||||
@@ -10,7 +11,7 @@ auto VDP::Enter() -> void {
|
|||||||
|
|
||||||
auto VDP::main() -> void {
|
auto VDP::main() -> void {
|
||||||
for(uint y : range(262)) {
|
for(uint y : range(262)) {
|
||||||
for(uint x : range(342)) {
|
for(uint x : range(684)) {
|
||||||
step(1);
|
step(1);
|
||||||
}
|
}
|
||||||
if(y == 240) scheduler.exit(Scheduler::Event::Frame);
|
if(y == 240) scheduler.exit(Scheduler::Event::Frame);
|
||||||
@@ -18,6 +19,13 @@ auto VDP::main() -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto VDP::step(uint clocks) -> void {
|
auto VDP::step(uint clocks) -> void {
|
||||||
|
if(++io.hcounter == 684) {
|
||||||
|
io.hcounter = 0;
|
||||||
|
if(++io.vcounter == 262) {
|
||||||
|
io.vcounter = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Thread::step(clocks);
|
Thread::step(clocks);
|
||||||
synchronize(cpu);
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
@@ -26,23 +34,13 @@ auto VDP::refresh() -> void {
|
|||||||
Emulator::video.refresh(buffer, 256 * sizeof(uint32), 256, 240);
|
Emulator::video.refresh(buffer, 256 * sizeof(uint32), 256, 240);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto VDP::in(uint8 addr) -> uint8 {
|
|
||||||
switch(addr) {
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0xb0;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto VDP::out(uint8 addr, uint8 data) -> void {
|
|
||||||
switch(addr) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto VDP::power() -> void {
|
auto VDP::power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto VDP::reset() -> void {
|
auto VDP::reset() -> void {
|
||||||
create(VDP::Enter, system.colorburst());
|
create(VDP::Enter, system.colorburst() * 15.0 / 5.0);
|
||||||
|
|
||||||
|
memory::fill(&io, sizeof(IO));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -6,14 +6,83 @@ struct VDP : Thread {
|
|||||||
auto step(uint clocks) -> void;
|
auto step(uint clocks) -> void;
|
||||||
auto refresh() -> void;
|
auto refresh() -> void;
|
||||||
|
|
||||||
auto in(uint8 addr) -> uint8;
|
|
||||||
auto out(uint8 addr, uint8 data) -> void;
|
|
||||||
|
|
||||||
auto power() -> void;
|
auto power() -> void;
|
||||||
auto reset() -> void;
|
auto reset() -> void;
|
||||||
|
|
||||||
|
//io.cpp
|
||||||
|
auto vcounter() -> uint8;
|
||||||
|
auto hcounter() -> uint8;
|
||||||
|
auto data() -> uint8;
|
||||||
|
auto status() -> uint8;
|
||||||
|
|
||||||
|
auto data(uint8) -> void;
|
||||||
|
auto control(uint8) -> void;
|
||||||
|
auto registerWrite(uint4 addr, uint8 data) -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32 buffer[256 * 240];
|
uint32 buffer[256 * 240];
|
||||||
|
uint8 vram[0x4000];
|
||||||
|
uint8 cram[0x40];
|
||||||
|
|
||||||
|
struct IO {
|
||||||
|
uint vcounter;
|
||||||
|
uint hcounter;
|
||||||
|
|
||||||
|
bool controlLatch;
|
||||||
|
uint16 controlData;
|
||||||
|
uint2 code;
|
||||||
|
uint14 address;
|
||||||
|
|
||||||
|
uint8 vramLatch;
|
||||||
|
|
||||||
|
//$00 mode control 1
|
||||||
|
bool externalSync;
|
||||||
|
bool extendedHeight;
|
||||||
|
bool mode4;
|
||||||
|
bool spriteShift;
|
||||||
|
bool lineInterrupts;
|
||||||
|
bool leftClip;
|
||||||
|
bool horizontalScrollLock;
|
||||||
|
bool verticalScrollLock;
|
||||||
|
|
||||||
|
//$01 mode control 2
|
||||||
|
bool spriteDouble;
|
||||||
|
bool spriteTile;
|
||||||
|
bool lines240;
|
||||||
|
bool lines224;
|
||||||
|
bool frameInterrupts;
|
||||||
|
bool displayEnable;
|
||||||
|
|
||||||
|
//$02 name table base address
|
||||||
|
uint1 nameTableMask;
|
||||||
|
uint3 nameTableAddress;
|
||||||
|
|
||||||
|
//$03 color table base address
|
||||||
|
uint8 colorTableAddress;
|
||||||
|
|
||||||
|
//$04 pattern table base address
|
||||||
|
uint8 patternTableAddress;
|
||||||
|
|
||||||
|
//$05 sprite attribute table base address
|
||||||
|
uint1 spriteAttributeTableMask;
|
||||||
|
uint6 spriteAttributeTableAddress;
|
||||||
|
|
||||||
|
//$06 sprite pattern table base address
|
||||||
|
uint2 spritePatternTableMask;
|
||||||
|
uint1 spritePatternTableAddress;
|
||||||
|
|
||||||
|
//$07 backdrop color
|
||||||
|
uint4 backdropColor;
|
||||||
|
|
||||||
|
//$08 horizontal scroll offset
|
||||||
|
uint8 hscroll;
|
||||||
|
|
||||||
|
//$09 vertical scroll offset
|
||||||
|
uint8 vscroll;
|
||||||
|
|
||||||
|
//$0a line counter
|
||||||
|
uint8 lineCounter;
|
||||||
|
} io;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern VDP vdp;
|
extern VDP vdp;
|
||||||
|
@@ -1,11 +1,4 @@
|
|||||||
auto Z80::instruction() -> void {
|
auto Z80::instruction() -> void {
|
||||||
#if 1
|
|
||||||
if(instructionsExecuted < 20)
|
|
||||||
print(disassemble(r.pc), "\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
instructionsExecuted++;
|
|
||||||
|
|
||||||
auto code = opcode();
|
auto code = opcode();
|
||||||
if(code == 0xdd) { r.hlp = &r.ix; return; }
|
if(code == 0xdd) { r.hlp = &r.ix; return; }
|
||||||
if(code == 0xfd) { r.hlp = &r.iy; return; }
|
if(code == 0xfd) { r.hlp = &r.iy; return; }
|
||||||
|
@@ -15,7 +15,6 @@ auto Z80::power() -> void {
|
|||||||
auto Z80::reset() -> void {
|
auto Z80::reset() -> void {
|
||||||
memory::fill(&r, sizeof(Registers));
|
memory::fill(&r, sizeof(Registers));
|
||||||
r.hlp = &r.hl;
|
r.hlp = &r.hl;
|
||||||
instructionsExecuted = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Z80::parity(uint8 value) const -> bool {
|
auto Z80::parity(uint8 value) const -> bool {
|
||||||
|
@@ -212,9 +212,6 @@ struct Z80 {
|
|||||||
} r;
|
} r;
|
||||||
|
|
||||||
Bus* bus = nullptr;
|
Bus* bus = nullptr;
|
||||||
|
|
||||||
private:
|
|
||||||
uint64 instructionsExecuted = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -59,8 +59,7 @@ namespace Math {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(PLATFORM_WINDOWS)
|
#if defined(PLATFORM_WINDOWS)
|
||||||
//fight Microsoft's ardent efforts at vendor lock-in
|
#undef IN
|
||||||
|
|
||||||
#undef interface
|
#undef interface
|
||||||
#define dllexport __declspec(dllexport)
|
#define dllexport __declspec(dllexport)
|
||||||
#define MSG_NOSIGNAL 0
|
#define MSG_NOSIGNAL 0
|
||||||
|
Reference in New Issue
Block a user