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:
Tim Allen 2016-12-17 22:31:34 +11:00
parent 1d7b674dd4
commit bab2ac812a
13 changed files with 279 additions and 34 deletions

View File

@ -12,7 +12,7 @@ using namespace nall;
namespace Emulator {
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 License = "GPLv3";
static const string Website = "http://byuu.org/";

View File

@ -15,14 +15,37 @@ auto Bus::write(uint16 addr, uint8 data) -> void {
}
auto Bus::in(uint8 addr) -> uint8 {
switch(addr) {
case 0x7e: return vdp.in(addr);
case 0x7f: return vdp.in(addr);
switch(addr >> 6) {
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;
}
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
}
}
}
}

View File

@ -9,6 +9,12 @@ auto CPU::Enter() -> void {
}
auto CPU::main() -> void {
#if 1
if(instructionsExecuted < 20)
print(disassemble(r.pc), "\n");
instructionsExecuted++;
#endif
instruction();
}
@ -26,6 +32,8 @@ auto CPU::power() -> void {
auto CPU::reset() -> void {
Z80::reset();
create(CPU::Enter, system.colorburst());
instructionsExecuted = 0;
}
}

View File

@ -7,6 +7,9 @@ struct CPU : Processor::Z80, Thread {
auto power() -> void;
auto reset() -> void;
private:
uint64 instructionsExecuted;
};
extern CPU cpu;

View File

@ -10,6 +10,7 @@ auto PSG::Enter() -> void {
auto PSG::main() -> void {
step(1);
stream->sample(0.0, 0.0);
}
auto PSG::step(uint clocks) -> void {
@ -22,6 +23,7 @@ auto PSG::power() -> void {
auto PSG::reset() -> void {
create(PSG::Enter, system.colorburst());
stream = Emulator::audio.createStream(2, system.colorburst());
}
}

View File

@ -1,6 +1,8 @@
//TI SN76489
struct PSG : Thread {
shared_pointer<Emulator::Stream> stream;
static auto Enter() -> void;
auto main() -> void;
auto step(uint clocks) -> void;

152
higan/ms/vdp/io.cpp Normal file
View 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
}
}

View File

@ -3,6 +3,7 @@
namespace MasterSystem {
VDP vdp;
#include "io.cpp"
auto VDP::Enter() -> void {
while(true) scheduler.synchronize(), vdp.main();
@ -10,7 +11,7 @@ auto VDP::Enter() -> void {
auto VDP::main() -> void {
for(uint y : range(262)) {
for(uint x : range(342)) {
for(uint x : range(684)) {
step(1);
}
if(y == 240) scheduler.exit(Scheduler::Event::Frame);
@ -18,6 +19,13 @@ auto VDP::main() -> void {
}
auto VDP::step(uint clocks) -> void {
if(++io.hcounter == 684) {
io.hcounter = 0;
if(++io.vcounter == 262) {
io.vcounter = 0;
}
}
Thread::step(clocks);
synchronize(cpu);
}
@ -26,23 +34,13 @@ auto VDP::refresh() -> void {
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::reset() -> void {
create(VDP::Enter, system.colorburst());
create(VDP::Enter, system.colorburst() * 15.0 / 5.0);
memory::fill(&io, sizeof(IO));
}
}

View File

@ -6,14 +6,83 @@ struct VDP : Thread {
auto step(uint clocks) -> void;
auto refresh() -> void;
auto in(uint8 addr) -> uint8;
auto out(uint8 addr, uint8 data) -> void;
auto power() -> 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:
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;

View File

@ -1,11 +1,4 @@
auto Z80::instruction() -> void {
#if 1
if(instructionsExecuted < 20)
print(disassemble(r.pc), "\n");
#endif
instructionsExecuted++;
auto code = opcode();
if(code == 0xdd) { r.hlp = &r.ix; return; }
if(code == 0xfd) { r.hlp = &r.iy; return; }

View File

@ -15,7 +15,6 @@ auto Z80::power() -> void {
auto Z80::reset() -> void {
memory::fill(&r, sizeof(Registers));
r.hlp = &r.hl;
instructionsExecuted = 0;
}
auto Z80::parity(uint8 value) const -> bool {

View File

@ -212,9 +212,6 @@ struct Z80 {
} r;
Bus* bus = nullptr;
private:
uint64 instructionsExecuted = 0;
};
}

View File

@ -59,8 +59,7 @@ namespace Math {
#endif
#if defined(PLATFORM_WINDOWS)
//fight Microsoft's ardent efforts at vendor lock-in
#undef IN
#undef interface
#define dllexport __declspec(dllexport)
#define MSG_NOSIGNAL 0