Update to v101r05 release.

byuu says:

Changelog:

  - 68K: fixed bug that affected BSR return address
  - VDP: added very preliminary emulation of planes A, B, W (W is
    entirely broken though)
  - VDP: added command/address stuff so you can write to VRAM, CRAM,
    VSRAM
  - VDP: added VRAM fill DMA

I would be really surprised if any commercial games showed anything at
all, so I'd probably recommend against wasting your time trying, unless
you're really bored :P

Also, I wanted to add: I am accepting patches\! So if anyone wants to
look over the 68K core for bugs, that would save me untold amounts of
time in the near future :D
This commit is contained in:
Tim Allen 2016-08-13 09:47:30 +10:00
parent 1df2549d18
commit ac2d0ba1cf
14 changed files with 371 additions and 97 deletions

View File

@ -12,7 +12,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "higan";
static const string Version = "101.04";
static const string Version = "101.05";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "http://byuu.org/";

View File

@ -15,6 +15,12 @@ auto CPU::boot() -> void {
}
auto CPU::main() -> void {
#if 0
static file fp;
if(!fp) fp.open({Path::user(), "Desktop/trace.log"}, file::mode::write);
fp.print(pad(disassemble(r.pc), -60, ' '), " ", disassembleRegisters().replace("\n", " "), "\n");
#endif
instruction();
}

View File

@ -61,9 +61,9 @@ auto Interface::videoColors() -> uint32 {
}
auto Interface::videoColor(uint32 color) -> uint64 {
uint B = color.bits(0,2);
uint R = color.bits(0,2);
uint G = color.bits(3,5);
uint R = color.bits(6,8);
uint B = color.bits(6,8);
uint64 r = image::normalize(R, 3, 16);
uint64 g = image::normalize(G, 3, 16);

View File

@ -0,0 +1,32 @@
auto VDP::Background::scanline() -> void {
}
auto VDP::Background::run(uint x, uint y) -> void {
output.priority = 0;
output.color = 0;
uint tileX = x >> 3;
uint tileY = y >> 3;
uint15 nametableAddress = io.nametableAddress;
nametableAddress += (tileY << io.nametableWidth) + tileX;
uint16 tileAttributes = vdp.vram[nametableAddress];
uint15 tileAddress = tileAttributes.bits(0,10) << 4;
uint pixelX = (x & 7) ^ (tileAttributes.bit(11) ? 7 : 0);
uint pixelY = (y & 7) ^ (tileAttributes.bit(12) ? 7 : 0);
tileAddress += pixelY << 1 | pixelX >> 2;
uint16 tileData = vdp.vram[tileAddress];
uint4 palette = tileData >> (((pixelX & 3) ^ 3) << 2);
if(palette) {
output.color = tileAttributes.bits(13,14) << 4 | palette;
output.priority = tileAttributes.bit(15);
}
}
auto VDP::Background::power() -> void {
}
auto VDP::Background::reset() -> void {
memory::fill(&io, sizeof(IO));
}

18
higan/md/vdp/dma.cpp Normal file
View File

@ -0,0 +1,18 @@
auto VDP::dmaRun() -> void {
if(!io.dmaEnable) return;
if(!io.dmaActive) return;
if(io.dmaMode == 2) return dmaFillVRAM();
}
auto VDP::dmaFillVRAM() -> void {
auto address = io.address.bits(1,15);
auto data = io.dmaFillWord;
if(io.address.bit(0)) data = data >> 8 | data << 8;
vram[address] = data;
io.address += io.dataIncrement;
if(--io.dmaLength == 0) {
io.dmaActive = false;
}
}

View File

@ -1,10 +1,25 @@
//todo: does data mirroring occur for all VDP addresses; or just data/control ports?
auto VDP::readByte(uint24 addr) -> uint8 {
return 0x00;
auto data = readWord(addr & ~1);
return data << 8 | data << 0;
}
auto VDP::writeByte(uint24 addr, uint8 data) -> void {
return writeWord(addr & ~1, data << 8 | data << 0);
}
//
auto VDP::readWord(uint24 addr) -> uint16 {
switch(addr & 0xc0001f) {
//data port
case 0xc00000: case 0xc00002: {
return readDataPort();
}
//control port
case 0xc00004: case 0xc00006: {
return readControlPort();
}
@ -14,19 +29,19 @@ auto VDP::readWord(uint24 addr) -> uint16 {
return 0x0000;
}
auto VDP::writeByte(uint24 addr, uint8 data) -> void {
//print("[VDP] ", hex(addr, 6L), "=", hex(data, 2L), "\n");
}
auto VDP::writeWord(uint24 addr, uint16 data) -> void {
//print("[VDP] ", hex(addr, 6L), "=", hex(data, 4L), "\n");
switch(addr & 0xc0001f) {
//data port
case 0xc00000: case 0xc00002: {
return writeDataPort(data);
}
//control port
case 0xc00004: case 0xc00006: {
if(!data.bit(15)) return;
return writeControlPort(data.bits(8,14), data.bits(0,7));
return writeControlPort(data);
}
}
@ -34,15 +49,99 @@ auto VDP::writeWord(uint24 addr, uint16 data) -> void {
//
auto VDP::readDataPort() -> uint16 {
io.commandPending = false;
//VRAM read
if(io.command.bits(0,3) == 0) {
return 0x0000;
}
//VSRAM read
if(io.command.bits(0,3) == 4) {
return 0x0000;
}
//CRAM read
if(io.command.bits(0,3) == 8) {
return 0x0000;
}
}
auto VDP::writeDataPort(uint16 data) -> void {
io.commandPending = false;
//DMA VRAM fill
if(io.command.bits(4,5) == 2) {
io.dmaActive = true;
io.dmaFillWord = data;
return;
}
//VRAM write
if(io.command.bits(0,3) == 1) {
auto address = io.address.bits(1,15);
if(io.address.bit(0)) data = data >> 8 | data << 8;
vram[address] = data;
io.address += io.dataIncrement;
return;
}
//VSRAM write
if(io.command.bits(0,3) == 5) {
auto address = io.address.bits(1,6);
if(address >= 40) return;
//data format: ---- --yy yyyy yyyy
vsram[address] = data.bits(0,9);
io.address += io.dataIncrement;
return;
}
//CRAM write
if(io.command.bits(0,3) == 3) {
auto address = io.address.bits(1,6);
//data format: ---- bbb- ggg- rrr-
cram[address] = data.bits(1,3) << 0 | data.bits(5,7) << 3 | data.bits(9,11) << 6;
io.address += io.dataIncrement;
return;
}
}
//
auto VDP::readControlPort() -> uint16 {
io.commandPending = false;
uint16 result = 0b0011'0100'0000'0000;
result |= io.dmaActive << 1;
return result;
}
auto VDP::writeControlPort(uint7 addr, uint8 data) -> void {
//print("[VDPC] ", hex(addr, 2L), "=", hex(data, 2L), "\n");
auto VDP::writeControlPort(uint16 data) -> void {
//print("[VDPC] ", hex(data, 4L), "\n");
switch(addr) {
//command write (lo)
if(io.commandPending) {
io.commandPending = false;
io.command.bits(2,5) = data.bits(4,7);
io.address.bits(14,15) = data.bits(0,1);
return;
}
//command write (hi)
if(data.bits(14,15) != 2) {
io.commandPending = true;
io.command.bits(0,1) = data.bits(14,15);
io.address.bits(0,13) = data.bits(0,13);
return;
}
//register write (d13 is ignored)
if(data.bits(14,15) == 2)
switch(data.bits(8,12)) {
//mode register 1
case 0x00: {
@ -61,24 +160,27 @@ auto VDP::writeControlPort(uint7 addr, uint8 data) -> void {
io.verticalBlankInterruptEnable = data.bit(5);
io.displayEnable = data.bit(6);
io.externalVRAM = data.bit(7);
if(!io.dmaEnable) io.command.bit(5) = 0;
return;
}
//plane A name table location
case 0x02: {
io.nametablePlaneA = data.bits(3,6);
planeA.io.nametableAddress = data.bits(3,6) << 12;
return;
}
//window name table location
case 0x03: {
io.nametableWindow = data.bits(1,6);
window.io.nametableAddress = data.bits(1,6) << 10;
return;
}
//plane B name table location
case 0x04: {
io.nametablePlaneB = data.bits(0,3);
planeB.io.nametableAddress = data.bits(0,3) << 12;
return;
}
@ -96,18 +198,7 @@ auto VDP::writeControlPort(uint7 addr, uint8 data) -> void {
//background color
case 0x07: {
io.backgroundIndex = data.bits(0,3);
io.backgroundPalette = data.bits(4,5);
return;
}
//unused
case 0x08: {
return;
}
//unused
case 0x09: {
io.backgroundColor = data.bits(0,5);
return;
}
@ -149,30 +240,64 @@ auto VDP::writeControlPort(uint7 addr, uint8 data) -> void {
return;
}
//VRAM auto-increment value
//data port auto-increment value
case 0x0f: {
io.vramAutoIncrement = data.bits(0,7);
io.dataIncrement = data.bits(0,7);
return;
}
//plane size
case 0x10: {
io.horizontalPlaneSize = data.bits(0,1);
io.verticalPlaneSize = data.bits(4,5);
//0 = 32 tiles
//1 = 64 tiles
//2 = invalid (repeats first row for every scanline)
//3 = 128 tiles
static const uint shift[] = {5, 6, 0, 7};
planeA.io.nametableWidth = shift[data.bits(0,1)];
window.io.nametableWidth = shift[data.bits(0,1)];
planeB.io.nametableWidth = shift[data.bits(0,1)];
planeA.io.nametableHeight = shift[data.bits(4,5)];
window.io.nametableHeight = shift[data.bits(4,5)];
planeB.io.nametableHeight = shift[data.bits(4,5)];
return;
}
//window plane horizontal position
case 0x11: {
io.horizontalWindowPlanePosition = data.bits(0,4);
io.horizontalWindowPlaneRight = data.bit(7);
if(!data) {
//disable
io.windowHorizontalLo = ~0;
io.windowHorizontalHi = ~0;
} else if(data.bit(7) == 0) {
//left
io.windowHorizontalLo = 0;
io.windowHorizontalHi = (data.bits(0,4) << 4) - 1;
} else {
//right
io.windowHorizontalLo = (data.bits(0,4) << 4) - 1;
io.windowHorizontalHi = ~0;
}
return;
}
//window plane vertical position
case 0x12: {
io.verticalWindowPlanePosition = data.bits(0,4);
io.verticalWindowPlaneDown = data.bit(7);
if(!data) {
//disable
io.windowVerticalLo = ~0;
io.windowVerticalHi = ~0;
} else if(data.bit(7) == 0) {
//up
io.windowVerticalLo = 0;
io.windowVerticalHi = (data.bits(0,4) << 3) - 1;
} else {
//down
io.windowVerticalLo = (data.bits(0,4) << 3) - 1;
io.windowVerticalHi = ~0;
}
return;
}
@ -207,5 +332,10 @@ auto VDP::writeControlPort(uint7 addr, uint8 data) -> void {
return;
}
//unused
default: {
return;
}
}
}

36
higan/md/vdp/render.cpp Normal file
View File

@ -0,0 +1,36 @@
auto VDP::scanline() -> void {
state.x = 0;
if(++state.y >= 262) state.y = 0;
if(state.y == 0) scheduler.exit(Scheduler::Event::Frame);
state.output = buffer + (state.y * 2 + 0) * 1280;
}
auto VDP::run() -> void {
if(!io.displayEnable) return outputPixel(0);
bool windowed = false; //todo: broken
windowed &= state.x >= io.windowHorizontalLo && state.x <= io.windowHorizontalHi;
windowed &= state.y >= io.windowVerticalLo && state.y <= io.windowVerticalHi;
auto& planeA = windowed ? this->window : this->planeA;
planeA.run(state.x, state.y);
planeB.run(state.x, state.y);
auto output = io.backgroundColor;
if(auto color = planeB.output.color) output = color;
if(auto color = planeA.output.color) output = color;
if(planeB.output.priority) if(auto color = planeB.output.color) output = color;
if(planeA.output.priority) if(auto color = planeA.output.color) output = color;
outputPixel(cram[output]);
state.x++;
}
auto VDP::outputPixel(uint9 color) -> void {
for(auto n : range(4)) {
state.output[ 0 + n] = color;
state.output[1280 + n] = color;
}
state.output += 4;
}

View File

@ -7,32 +7,33 @@ namespace MegaDrive {
VDP vdp;
#include "io.cpp"
#include "dma.cpp"
#include "render.cpp"
#include "background.cpp"
auto VDP::Enter() -> void {
while(true) scheduler.synchronize(), vdp.main();
}
auto VDP::main() -> void {
for(uint y : range(262)) {
auto outputLo = buffer + (y * 2 + 0) * 1280;
auto outputHi = buffer + (y * 2 + 1) * 1280;
for(uint x : range(342)) {
if(y < 240 && x < 320) {
for(uint n : range(4)) {
*outputLo++ = 511;
*outputHi++ = 511;
}
}
scanline();
if(state.y < 240) {
for(uint x : range(320)) {
run();
step(1);
}
if(y == 240) scheduler.exit(Scheduler::Event::Frame);
} else {
step(342);
}
step(22);
}
auto VDP::step(uint clocks) -> void {
Thread::step(clocks);
synchronize(cpu);
while(clocks--) {
dmaRun();
Thread::step(1);
synchronize(cpu);
}
}
auto VDP::refresh() -> void {
@ -40,12 +41,19 @@ auto VDP::refresh() -> void {
}
auto VDP::power() -> void {
planeA.power();
window.power();
planeB.power();
}
auto VDP::reset() -> void {
create(VDP::Enter, system.colorburst() * 15.0 / 10.0);
memory::fill(&io, sizeof(IO));
planeA.reset();
window.reset();
planeB.reset();
}
}

View File

@ -9,16 +9,67 @@ struct VDP : Thread {
auto power() -> void;
auto reset() -> void;
//io.cpp
auto readByte(uint24 addr) -> uint8;
auto readWord(uint24 addr) -> uint16;
auto writeByte(uint24 addr, uint8 data) -> void;
auto readWord(uint24 addr) -> uint16;
auto writeWord(uint24 addr, uint16 data) -> void;
auto readDataPort() -> uint16;
auto writeDataPort(uint16 data) -> void;
auto readControlPort() -> uint16;
auto writeControlPort(uint7 addr, uint8 data) -> void;
auto writeControlPort(uint16 data) -> void;
//dma.cpp
auto dmaRun() -> void;
auto dmaFillVRAM() -> void;
//render.cpp
auto scanline() -> void;
auto run() -> void;
auto outputPixel(uint9 color) -> void;
//background.cpp
struct Background {
auto scanline() -> void;
auto run(uint x, uint y) -> void;
auto power() -> void;
auto reset() -> void;
struct IO {
uint15 nametableAddress;
uint3 nametableWidth; //1 << value
uint3 nametableHeight; //1 << value
} io;
struct Output {
uint6 color;
boolean priority;
} output;
};
Background planeA;
Background window;
Background planeB;
uint16 vram[32768];
uint16 vramExpansion[32768]; //not present in stock Mega Drive hardware
uint9 cram[64];
uint10 vsram[40];
private:
struct IO {
//internal state
boolean dmaActive;
uint8 dmaFillWord;
//command
uint6 command;
uint16 address;
boolean commandPending;
//$00 mode register 1
uint1 displayOverlayEnable;
uint1 counterLatch;
@ -33,15 +84,6 @@ private:
uint1 displayEnable;
uint1 externalVRAM;
//$02 plane A name table location
uint4 nametablePlaneA;
//$03 window name table location
uint6 nametableWindow;
//$04 plane B name table location
uint4 nametablePlaneB;
//$05 sprite attribute table location
uint8 attrtableSprite;
@ -49,8 +91,7 @@ private:
uint1 nametableBaseSprite;
//$07 background color
uint4 backgroundIndex;
uint2 backgroundPalette;
uint6 backgroundColor;
//$0a horizontal interrupt counter
uint8 horizontalInterruptCounter;
@ -75,20 +116,16 @@ private:
uint1 nametableBasePatternA;
uint1 nametableBasePatternB;
//$0f VRAM auto-increment value
uint8 vramAutoIncrement;
//$10 plane size
uint2 horizontalPlaneSize;
uint2 verticalPlaneSize;
//$0f data port auto-increment value
uint8 dataIncrement;
//$11 window plane horizontal position
uint5 horizontalWindowPlanePosition;
uint1 horizontalWindowPlaneRight;
uint10 windowHorizontalLo;
uint10 windowHorizontalHi;
//$12 window plane vertical position
uint5 verticalWindowPlanePosition;
uint1 verticalWindowPlaneDown;
uint10 windowVerticalLo;
uint10 windowVerticalHi;
//$13-$14 DMA length
uint16 dmaLength;
@ -98,6 +135,12 @@ private:
uint2 dmaMode;
} io;
struct State {
uint32* output = nullptr;
uint x;
uint y;
} state;
uint32 buffer[1280 * 480];
};

View File

@ -1,12 +1,14 @@
auto M68K::instruction() -> void {
instructionsExecuted++;
//if(instructionsExecuted >= 2000010) trap();
//if(instructionsExecuted >= 2000000) {
// print(disassembleRegisters(), "\n");
// print(disassemble(r.pc), "\n");
// print("\n");
//}
#if 0
if(instructionsExecuted >= 10000010) while(true) step(1);
if(instructionsExecuted >= 10000000) {
print(disassembleRegisters(), "\n");
print(disassemble(r.pc), "\n");
print("\n");
}
#endif
opcode = readPC();
return instructionTable[opcode]();

View File

@ -248,13 +248,10 @@ auto M68K::instructionASR(EffectiveAddress modify) -> void {
auto M68K::instructionBCC(uint4 condition, uint8 displacement) -> void {
auto extension = readPC<Word>();
if(displacement) r.pc -= 2;
if(condition >= 2 && !testCondition(condition)) return;
if(condition == 1) push<Long>(r.pc);
if(condition >= 2 && !testCondition(condition)) { //0 = BRA; 1 = BSR
if(displacement) r.pc -= 2;
} else {
r.pc -= 2;
r.pc += displacement ? sign<Byte>(displacement) : sign<Word>(extension);
}
r.pc += displacement ? (int8_t)displacement : (int16_t)extension - 2;
}
template<uint Size> auto M68K::instructionBCHG(DataRegister bit, EffectiveAddress with) -> void {

View File

@ -35,13 +35,13 @@ struct M68K {
};};
struct Vector { enum : uint {
Illegal = 4,
DivisionByZero = 5,
BoundsCheck = 6,
Overflow = 7,
Unprivileged = 8,
IllegalLineA = 10,
IllegalLineF = 11,
Illegal = 4,
DivisionByZero = 5,
BoundsCheck = 6,
Overflow = 7,
Unprivileged = 8,
IllegalLineA = 10,
IllegalLineF = 11,
};};
M68K();

View File

@ -112,12 +112,12 @@ auto pCanvas::_paint() -> void {
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biHeight = -height; //GDI stores bitmaps upside now; negative height flips bitmap
bmi.bmiHeader.biSizeImage = pixels.size() * sizeof(uint32);
bmi.bmiHeader.biSizeImage = pixels.size() * sizeof(uint32_t);
void* bits = nullptr;
HBITMAP bitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &bits, nullptr, 0);
if(bits) {
auto source = (const uint8*)pixels.data();
auto target = (uint8*)bits;
auto source = (const uint8_t*)pixels.data();
auto target = (uint8_t*)bits;
for(auto n : range(width * height)) {
target[0] = (source[0] * source[3]) / 255;
target[1] = (source[1] * source[3]) / 255;
@ -155,7 +155,7 @@ auto pCanvas::_rasterize() -> void {
pixels.resize(width * height);
if(auto& icon = state().icon) {
memory::copy(pixels.data(), icon.data(), width * height * sizeof(uint32));
memory::copy(pixels.data(), icon.data(), width * height * sizeof(uint32_t));
} else if(auto& gradient = state().gradient) {
auto& colors = gradient.state.colors;
image fill;
@ -163,7 +163,7 @@ auto pCanvas::_rasterize() -> void {
fill.gradient(colors[0].value(), colors[1].value(), colors[2].value(), colors[3].value());
memory::copy(pixels.data(), fill.data(), fill.size());
} else {
uint32 color = state().color.value();
uint32_t color = state().color.value();
for(auto& pixel : pixels) pixel = color;
}
}

View File

@ -59,6 +59,7 @@ template<uint Bits> struct Natural {
struct Reference {
inline Reference(Natural& source, uint lo, uint hi) : source(source), Lo(lo), Hi(hi) {}
inline auto& operator=(Reference& source) { return set(source.get()); }
inline auto get() const -> type {
const type RangeBits = Hi - Lo + 1;
@ -162,6 +163,7 @@ template<uint Bits> struct Integer {
struct Reference {
inline Reference(Integer& source, uint lo, uint hi) : source(source), Lo(lo), Hi(hi) {}
inline auto& operator=(const Reference& source) { return set(source.get()); }
inline auto get() const -> utype {
const type RangeBits = Hi - Lo + 1;