Update to v083r10 release.

byuu says:

Changelog:
- NES: added VRC1, VRC2, VRC3, MMC6 emulation
- shrink window doesn't do anything when no cartridge is loaded
- phoenix Horizontal,VerticalLayout use const Size& instead of unsigned
  width,height [for consistency]

So, all official NES ASICs are supported now. Just need sound output for
MMC5+VRC7 to complete them; and then some board re-arrangement stuff for
VRC2+MMC3.

Note that MMC6 uses the same mapper ID as MMC3, and VRC2 uses the same
ID as VRC4, so you have to make a BML board mapping or toggle which type
is chosen in the source file to use these two chips.

Side note: NES overscan clamping is obviously still assuming 16-bit, as
only half the lines are erased. Need to fix that.
This commit is contained in:
Tim Allen 2011-11-04 22:57:54 +11:00
parent bf78e66027
commit 891f1ab7af
51 changed files with 1102 additions and 270 deletions

View File

@ -7,9 +7,11 @@
namespace nall {
template<typename T> struct reference_array {
struct exception_out_of_bounds{};
protected:
typedef typename std::remove_reference<T>::type *Tptr;
Tptr *pool;
typedef typename std::remove_reference<T>::type type_t;
type_t **pool;
unsigned poolsize, buffersize;
public:
@ -26,7 +28,7 @@ namespace nall {
void reserve(unsigned newsize) {
if(newsize == poolsize) return;
pool = (Tptr*)realloc(pool, newsize * sizeof(T));
pool = (type_t**)realloc(pool, sizeof(type_t*) * newsize);
poolsize = newsize;
buffersize = min(buffersize, newsize);
}
@ -37,13 +39,13 @@ namespace nall {
}
template<typename... Args>
bool append(const T& data, Args&&... args) {
bool append(type_t& data, Args&&... args) {
bool result = append(data);
append(std::forward<Args>(args)...);
return result;
}
bool append(const T data) {
bool append(type_t& data) {
for(unsigned index = 0; index < buffersize; index++) {
if(pool[index] == &data) return false;
}
@ -54,7 +56,7 @@ namespace nall {
return true;
}
bool remove(const T data) {
bool remove(type_t& data) {
for(unsigned index = 0; index < buffersize; index++) {
if(pool[index] == &data) {
for(unsigned i = index; i < buffersize - 1; i++) pool[i] = pool[i + 1];
@ -77,8 +79,8 @@ namespace nall {
if(pool) free(pool);
buffersize = source.buffersize;
poolsize = source.poolsize;
pool = (Tptr*)malloc(sizeof(T) * poolsize);
memcpy(pool, source.pool, sizeof(T) * buffersize);
pool = (type_t**)malloc(sizeof(type_t*) * poolsize);
memcpy(pool, source.pool, sizeof(type_t*) * buffersize);
return *this;
}
@ -92,20 +94,20 @@ namespace nall {
return *this;
}
inline T operator[](unsigned index) {
if(index >= buffersize) throw "reference_array[] out of bounds";
inline type_t& operator[](unsigned index) {
if(index >= buffersize) throw exception_out_of_bounds();
return *pool[index];
}
inline const T operator[](unsigned index) const {
if(index >= buffersize) throw "reference_array[] out of bounds";
inline type_t& operator[](unsigned index) const {
if(index >= buffersize) throw exception_out_of_bounds();
return *pool[index];
}
//iteration
struct iterator {
bool operator!=(const iterator &source) const { return index != source.index; }
T& operator*() { return array.operator[](index); }
type_t& operator*() { return array.operator[](index); }
iterator& operator++() { index++; return *this; }
iterator(const reference_array &array, unsigned index) : array(array), index(index) {}
private:

View File

@ -1,4 +1,7 @@
#include "bandai-fcg.cpp"
#include "konami-vrc1.cpp"
#include "konami-vrc2.cpp"
#include "konami-vrc3.cpp"
#include "konami-vrc4.cpp"
#include "konami-vrc6.cpp"
#include "konami-vrc7.cpp"
@ -8,6 +11,7 @@
#include "nes-exrom.cpp"
#include "nes-fxrom.cpp"
#include "nes-gxrom.cpp"
#include "nes-hkrom.cpp"
#include "nes-nrom.cpp"
#include "nes-pxrom.cpp"
#include "nes-sxrom.cpp"
@ -113,6 +117,9 @@ Board* Board::load(const string &markup, const uint8_t *data, unsigned size) {
if(type == "BANDAI-FCG") return new BandaiFCG(board, data, size);
if(type == "KONAMI-VRC-1") return new KonamiVRC1(board, data, size);
if(type == "KONAMI-VRC-2") return new KonamiVRC2(board, data, size);
if(type == "KONAMI-VRC-3") return new KonamiVRC3(board, data, size);
if(type == "KONAMI-VRC-4") return new KonamiVRC4(board, data, size);
if(type == "KONAMI-VRC-6") return new KonamiVRC6(board, data, size);
if(type == "KONAMI-VRC-7") return new KonamiVRC7(board, data, size);
@ -137,6 +144,8 @@ Board* Board::load(const string &markup, const uint8_t *data, unsigned size) {
if(type == "NES-GNROM" ) return new NES_GxROM(board, data, size);
if(type == "NES-MHROM" ) return new NES_GxROM(board, data, size);
if(type == "NES-HKROM" ) return new NES_HKROM(board, data, size);
if(type == "NES-NROM-128") return new NES_NROM(board, data, size);
if(type == "NES-NROM-256") return new NES_NROM(board, data, size);

View File

@ -0,0 +1,40 @@
struct KonamiVRC1 : Board {
VRC1 vrc1;
uint8 prg_read(unsigned addr) {
if(addr & 0x8000) return prgrom.read(vrc1.prg_addr(addr));
return cpu.mdr();
}
void prg_write(unsigned addr, uint8 data) {
if(addr & 0x8000) return vrc1.reg_write(addr, data);
}
uint8 chr_read(unsigned addr) {
if(addr & 0x2000) return ppu.ciram_read(vrc1.ciram_addr(addr));
return Board::chr_read(vrc1.chr_addr(addr));
}
void chr_write(unsigned addr, uint8 data) {
if(addr & 0x2000) return ppu.ciram_write(vrc1.ciram_addr(addr), data);
return Board::chr_write(vrc1.chr_addr(addr), data);
}
void power() {
vrc1.power();
}
void reset() {
vrc1.reset();
}
void serialize(serializer &s) {
Board::serialize(s);
vrc1.serialize(s);
}
KonamiVRC1(BML::Node &board, const uint8_t *data, unsigned size) : Board(board, data, size), vrc1(*this) {
}
};

View File

@ -0,0 +1,45 @@
struct KonamiVRC2 : Board {
VRC2 vrc2;
bool latch;
uint8 prg_read(unsigned addr) {
if(addr == 0x6000) return latch;
if(addr & 0x8000) return prgrom.read(vrc2.prg_addr(addr));
return cpu.mdr();
}
void prg_write(unsigned addr, uint8 data) {
if(addr == 0x6000) latch = data & 0x01;
if(addr & 0x8000) return vrc2.reg_write(addr, data);
}
uint8 chr_read(unsigned addr) {
if(addr & 0x2000) return ppu.ciram_read(vrc2.ciram_addr(addr));
return Board::chr_read(vrc2.chr_addr(addr));
}
void chr_write(unsigned addr, uint8 data) {
if(addr & 0x2000) return ppu.ciram_write(vrc2.ciram_addr(addr), data);
return Board::chr_write(vrc2.chr_addr(addr), data);
}
void power() {
vrc2.power();
}
void reset() {
vrc2.reset();
latch = 0;
}
void serialize(serializer &s) {
Board::serialize(s);
vrc2.serialize(s);
s.integer(latch);
}
KonamiVRC2(BML::Node &board, const uint8_t *data, unsigned size) : Board(board, data, size), vrc2(*this) {
}
};

View File

@ -0,0 +1,57 @@
struct KonamiVRC3 : Board {
struct Settings {
bool mirror; //0 = horizontal, 1 = vertical
} settings;
VRC3 vrc3;
void main() {
vrc3.main();
}
uint8 prg_read(unsigned addr) {
if((addr & 0xe000) == 0x6000) return prgram.read(addr & 0x1fff);
if(addr & 0x8000) return prgrom.read(vrc3.prg_addr(addr));
return cpu.mdr();
}
void prg_write(unsigned addr, uint8 data) {
if((addr & 0xe000) == 0x6000) return prgram.write(addr & 0x1fff, data);
if(addr & 0x8000) return vrc3.reg_write(addr, data);
}
uint8 chr_read(unsigned addr) {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_read(addr & 0x07ff);
}
return chrram.read(addr);
}
void chr_write(unsigned addr, uint8 data) {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_write(addr & 0x07ff, data);
}
return chrram.write(addr, data);
}
void power() {
vrc3.power();
}
void reset() {
vrc3.reset();
}
void serialize(serializer &s) {
Board::serialize(s);
vrc3.serialize(s);
}
KonamiVRC3(BML::Node &board, const uint8_t *data, unsigned size) : Board(board, data, size), vrc3(*this) {
settings.mirror = board["mirror"].value == "vertical" ? 1 : 0;
}
};

View File

@ -0,0 +1,48 @@
struct NES_HKROM : Board {
MMC6 mmc6;
void main() {
mmc6.main();
}
uint8 prg_read(unsigned addr) {
if((addr & 0xf000) == 0x7000) return mmc6.ram_read(addr);
if(addr & 0x8000) return prgrom.read(mmc6.prg_addr(addr));
return cpu.mdr();
}
void prg_write(unsigned addr, uint8 data) {
if((addr & 0xf000) == 0x7000) return mmc6.ram_write(addr, data);
if(addr & 0x8000) return mmc6.reg_write(addr, data);
}
uint8 chr_read(unsigned addr) {
mmc6.irq_test(addr);
if(addr & 0x2000) return ppu.ciram_read(mmc6.ciram_addr(addr));
return Board::chr_read(mmc6.chr_addr(addr));
}
void chr_write(unsigned addr, uint8 data) {
mmc6.irq_test(addr);
if(addr & 0x2000) return ppu.ciram_write(mmc6.ciram_addr(addr), data);
return Board::chr_write(mmc6.chr_addr(addr), data);
}
void power() {
mmc6.power();
}
void reset() {
mmc6.reset();
}
void serialize(serializer &s) {
Board::serialize(s);
mmc6.serialize(s);
}
NES_HKROM(BML::Node &board, const uint8_t *data, unsigned size) : Board(board, data, size), mmc6(*this) {
}
};

View File

@ -32,53 +32,7 @@ uint8 prg_read(unsigned addr) {
void prg_write(unsigned addr, uint8 data) {
if((addr & 0xe000) == 0x6000) return mmc3.ram_write(addr, data);
switch(addr & 0xe001) {
case 0x8000:
mmc3.chr_mode = data & 0x80;
mmc3.prg_mode = data & 0x40;
mmc3.bank_select = data & 0x07;
break;
case 0x8001:
switch(mmc3.bank_select) {
case 0: mmc3.chr_bank[0] = data & ~1; break;
case 1: mmc3.chr_bank[1] = data & ~1; break;
case 2: mmc3.chr_bank[2] = data; break;
case 3: mmc3.chr_bank[3] = data; break;
case 4: mmc3.chr_bank[4] = data; break;
case 5: mmc3.chr_bank[5] = data; break;
case 6: mmc3.prg_bank[0] = data & 0x3f; break;
case 7: mmc3.prg_bank[1] = data & 0x3f; break;
}
break;
case 0xa000:
mmc3.mirror = data & 0x01;
break;
case 0xa001:
mmc3.ram_enable = data & 0x80;
mmc3.ram_write_protect = data & 0x40;
break;
case 0xc000:
mmc3.irq_latch = data;
break;
case 0xc001:
mmc3.irq_counter = 0;
break;
case 0xe000:
mmc3.irq_enable = false;
mmc3.irq_line = 0;
break;
case 0xe001:
mmc3.irq_enable = true;
break;
}
if(addr & 0x8000) return mmc3.reg_write(addr, data);
}
uint8 chr_read(unsigned addr) {

View File

@ -1,6 +1,10 @@
#include "mmc1.cpp"
#include "mmc3.cpp"
#include "mmc5.cpp"
#include "mmc6.cpp"
#include "vrc1.cpp"
#include "vrc2.cpp"
#include "vrc3.cpp"
#include "vrc4.cpp"
#include "vrc6.cpp"
#include "vrc7.cpp"

View File

@ -89,6 +89,55 @@ void ram_write(unsigned addr, uint8 data) {
if(ram_enable && !ram_write_protect) board.prgram.data[addr & 0x1fff] = data;
}
void reg_write(unsigned addr, uint8 data) {
switch(addr & 0xe001) {
case 0x8000:
chr_mode = data & 0x80;
prg_mode = data & 0x40;
bank_select = data & 0x07;
break;
case 0x8001:
switch(bank_select) {
case 0: chr_bank[0] = data & ~1; break;
case 1: chr_bank[1] = data & ~1; break;
case 2: chr_bank[2] = data; break;
case 3: chr_bank[3] = data; break;
case 4: chr_bank[4] = data; break;
case 5: chr_bank[5] = data; break;
case 6: prg_bank[0] = data & 0x3f; break;
case 7: prg_bank[1] = data & 0x3f; break;
}
break;
case 0xa000:
mirror = data & 0x01;
break;
case 0xa001:
ram_enable = data & 0x80;
ram_write_protect = data & 0x40;
break;
case 0xc000:
irq_latch = data;
break;
case 0xc001:
irq_counter = 0;
break;
case 0xe000:
irq_enable = false;
irq_line = 0;
break;
case 0xe001:
irq_enable = true;
break;
}
}
void power() {
}

200
bsnes/nes/cartridge/chip/mmc6.cpp Executable file
View File

@ -0,0 +1,200 @@
struct MMC6 : Chip {
bool chr_mode;
bool prg_mode;
bool ram_enable;
uint3 bank_select;
uint8 prg_bank[2];
uint8 chr_bank[6];
bool mirror;
bool ram_readable[2];
bool ram_writable[2];
uint8 irq_latch;
uint8 irq_counter;
bool irq_enable;
unsigned irq_delay;
bool irq_line;
uint16 chr_abus;
void main() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(irq_delay) irq_delay--;
cpu.set_irq_line(irq_line);
tick();
}
}
void irq_test(unsigned addr) {
if(!(chr_abus & 0x1000) && (addr & 0x1000)) {
if(irq_delay == 0) {
if(irq_counter == 0) {
irq_counter = irq_latch;
} else if(--irq_counter == 0) {
if(irq_enable) irq_line = 1;
}
}
irq_delay = 6;
}
chr_abus = addr;
}
unsigned prg_addr(unsigned addr) const {
switch((addr >> 13) & 3) {
case 0:
if(prg_mode == 1) return (0x3e << 13) | (addr & 0x1fff);
return (prg_bank[0] << 13) | (addr & 0x1fff);
case 1:
return (prg_bank[1] << 13) | (addr & 0x1fff);
case 2:
if(prg_mode == 0) return (0x3e << 13) | (addr & 0x1fff);
return (prg_bank[0] << 13) | (addr & 0x1fff);
case 3:
return (0x3f << 13) | (addr & 0x1fff);
}
}
unsigned chr_addr(unsigned addr) const {
if(chr_mode == 0) {
if(addr <= 0x07ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
if(addr <= 0x0fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
if(addr <= 0x13ff) return (chr_bank[2] << 10) | (addr & 0x03ff);
if(addr <= 0x17ff) return (chr_bank[3] << 10) | (addr & 0x03ff);
if(addr <= 0x1bff) return (chr_bank[4] << 10) | (addr & 0x03ff);
if(addr <= 0x1fff) return (chr_bank[5] << 10) | (addr & 0x03ff);
} else {
if(addr <= 0x03ff) return (chr_bank[2] << 10) | (addr & 0x03ff);
if(addr <= 0x07ff) return (chr_bank[3] << 10) | (addr & 0x03ff);
if(addr <= 0x0bff) return (chr_bank[4] << 10) | (addr & 0x03ff);
if(addr <= 0x0fff) return (chr_bank[5] << 10) | (addr & 0x03ff);
if(addr <= 0x17ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
if(addr <= 0x1fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
}
}
unsigned ciram_addr(unsigned addr) const {
if(mirror == 0) return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
if(mirror == 1) return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
}
uint8 ram_read(unsigned addr) {
if(ram_enable == false) return cpu.mdr();
if(ram_readable[0] == false && ram_readable[1] == false) return cpu.mdr();
bool region = addr & 0x0200;
if(ram_readable[region] == false) return 0x00;
return board.prgram.read((region * 0x0200) + (addr & 0x01ff));
}
void ram_write(unsigned addr, uint8 data) {
if(ram_enable == false) return;
bool region = addr & 0x0200;
if(ram_writable[region] == false) return;
return board.prgram.write((region * 0x0200) + (addr & 0x01ff), data);
}
void reg_write(unsigned addr, uint8 data) {
switch(addr & 0xe001) {
case 0x8000:
chr_mode = data & 0x80;
prg_mode = data & 0x40;
ram_enable = data & 0x20;
bank_select = data & 0x07;
if(ram_enable == false) {
for(auto &n : ram_readable) n = false;
for(auto &n : ram_writable) n = false;
}
break;
case 0x8001:
switch(bank_select) {
case 0: chr_bank[0] = data & ~1; break;
case 1: chr_bank[1] = data & ~1; break;
case 2: chr_bank[2] = data; break;
case 3: chr_bank[3] = data; break;
case 4: chr_bank[4] = data; break;
case 5: chr_bank[5] = data; break;
case 6: prg_bank[0] = data & 0x3f; break;
case 7: prg_bank[1] = data & 0x3f; break;
}
break;
case 0xa000:
mirror = data & 0x01;
break;
case 0xa001:
if(ram_enable == false) break;
ram_readable[1] = data & 0x80;
ram_writable[1] = data & 0x40;
ram_readable[0] = data & 0x20;
ram_writable[0] = data & 0x10;
break;
case 0xc000:
irq_latch = data;
break;
case 0xc001:
irq_counter = 0;
break;
case 0xe000:
irq_enable = false;
irq_line = 0;
break;
case 0xe001:
irq_enable = true;
break;
}
}
void power() {
}
void reset() {
chr_mode = 0;
prg_mode = 0;
ram_enable = 0;
bank_select = 0;
for(auto &n : prg_bank) n = 0;
for(auto &n : chr_bank) n = 0;
mirror = 0;
for(auto &n : ram_readable) n = 0;
for(auto &n : ram_writable) n = 0;
irq_latch = 0;
irq_counter = 0;
irq_enable = 0;
irq_delay = 0;
irq_line = 0;
chr_abus = 0;
}
void serialize(serializer &s) {
s.integer(chr_mode);
s.integer(prg_mode);
s.integer(ram_enable);
s.integer(bank_select);
for(auto &n : prg_bank) s.integer(n);
for(auto &n : chr_bank) s.integer(n);
s.integer(mirror);
for(auto &n : ram_readable) s.integer(n);
for(auto &n : ram_writable) s.integer(n);
s.integer(irq_latch);
s.integer(irq_counter);
s.integer(irq_enable);
s.integer(irq_delay);
s.integer(irq_line);
s.integer(chr_abus);
}
MMC6(Board &board) : Chip(board) {
}
};

View File

@ -0,0 +1,80 @@
struct VRC1 : Chip {
uint4 prg_bank[3];
uint4 chr_banklo[2];
bool chr_bankhi[2];
bool mirror;
unsigned prg_addr(unsigned addr) const {
unsigned bank = 0x0f;
if((addr & 0xe000) == 0x8000) bank = prg_bank[0];
if((addr & 0xe000) == 0xa000) bank = prg_bank[1];
if((addr & 0xe000) == 0xc000) bank = prg_bank[2];
return (bank * 0x2000) + (addr & 0x1fff);
}
unsigned chr_addr(unsigned addr) const {
unsigned bank = chr_banklo[(bool)(addr & 0x1000)];
bank |= chr_bankhi[(bool)(addr & 0x1000)] << 4;
return (bank * 0x1000) + (addr & 0x0fff);
}
unsigned ciram_addr(unsigned addr) const {
switch(mirror) {
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
}
throw;
}
void reg_write(unsigned addr, uint8 data) {
switch(addr & 0xf000) {
case 0x8000:
prg_bank[0] = data & 0x0f;
break;
case 0x9000:
chr_bankhi[1] = data & 0x04;
chr_bankhi[0] = data & 0x02;
mirror = data & 0x01;
break;
case 0xa000:
prg_bank[1] = data & 0x0f;
break;
case 0xc000:
prg_bank[2] = data & 0x0f;
break;
case 0xe000:
chr_banklo[0] = data & 0x0f;
break;
case 0xf000:
chr_banklo[1] = data & 0x0f;
break;
}
}
void power() {
}
void reset() {
for(auto &n : prg_bank) n = 0;
for(auto &n : chr_banklo) n = 0;
for(auto &n : chr_bankhi) n = 0;
mirror = 0;
}
void serialize(serializer &s) {
for(auto &n : prg_bank) s.integer(n);
for(auto &n : chr_banklo) s.integer(n);
for(auto &n : chr_bankhi) s.integer(n);
s.integer(mirror);
}
VRC1(Board &board) : Chip(board) {
}
};

View File

@ -0,0 +1,91 @@
struct VRC2 : Chip {
uint4 prg_bank[2];
uint8 chr_bank[8];
uint2 mirror;
unsigned prg_addr(unsigned addr) const {
unsigned bank;
switch(addr & 0xe000) {
case 0x8000: bank = prg_bank[0]; break;
case 0xa000: bank = prg_bank[1]; break;
case 0xc000: bank = 0x0e; break;
case 0xe000: bank = 0x0f; break;
}
return (bank * 0x2000) + (addr & 0x1fff);
}
unsigned chr_addr(unsigned addr) const {
unsigned bank = chr_bank[addr / 0x0400];
return (bank * 0x0400) + (addr & 0x03ff);
}
unsigned ciram_addr(unsigned addr) const {
switch(mirror) {
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
}
throw;
}
void reg_write(unsigned addr, uint8 data) {
switch(addr) {
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
prg_bank[0] = data & 0x0f;
break;
case 0x9000: case 0x9001: case 0x9002: case 0x9003:
mirror = data & 0x03;
break;
case 0xa000: case 0xa001: case 0xa002: case 0xa003:
prg_bank[1] = data & 0x0f;
break;
case 0xb000: chr_bank[0] = (chr_bank[0] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xb001: chr_bank[0] = (chr_bank[0] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xb002: chr_bank[1] = (chr_bank[1] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xb003: chr_bank[1] = (chr_bank[1] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xc000: chr_bank[2] = (chr_bank[2] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xc001: chr_bank[2] = (chr_bank[2] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xc002: chr_bank[3] = (chr_bank[3] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xc003: chr_bank[3] = (chr_bank[3] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xd000: chr_bank[4] = (chr_bank[4] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xd001: chr_bank[4] = (chr_bank[4] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xd002: chr_bank[5] = (chr_bank[5] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xd003: chr_bank[5] = (chr_bank[5] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xe000: chr_bank[6] = (chr_bank[6] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xe001: chr_bank[6] = (chr_bank[6] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xe002: chr_bank[7] = (chr_bank[7] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xe003: chr_bank[7] = (chr_bank[7] & 0x0f) | ((data & 0x0f) << 4); break;
}
}
void power() {
}
void reset() {
for(auto &n : prg_bank) n = 0;
for(auto &n : chr_bank) n = 0;
mirror = 0;
}
void serialize(serializer &s) {
for(auto &n : prg_bank) s.integer(n);
for(auto &n : chr_bank) s.integer(n);
s.integer(mirror);
}
VRC2(Board &board) : Chip(board) {
}
};

100
bsnes/nes/cartridge/chip/vrc3.cpp Executable file
View File

@ -0,0 +1,100 @@
struct VRC3 : Chip {
uint4 prg_bank;
bool irq_mode;
bool irq_enable;
bool irq_acknowledge;
uint16 irq_latch;
struct {
union {
uint16 w;
struct { uint8 order_lsb2(l, h); };
};
} irq_counter;
bool irq_line;
void main() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(irq_enable) {
if(irq_mode == 0) { //16-bit
if(++irq_counter.w == 0) {
irq_line = 1;
irq_enable = irq_acknowledge;
irq_counter.w = irq_latch;
}
}
if(irq_mode == 1) { //8-bit
if(++irq_counter.l == 0) {
irq_line = 1;
irq_enable = irq_acknowledge;
irq_counter.l = irq_latch;
}
}
}
cpu.set_irq_line(irq_line);
tick();
}
}
unsigned prg_addr(unsigned addr) const {
unsigned bank = (addr < 0xc000 ? (unsigned)prg_bank : 0x0f);
return (bank * 0x4000) + (addr & 0x3fff);
}
void reg_write(unsigned addr, uint8 data) {
switch(addr & 0xf000) {
case 0x8000: irq_latch = (irq_latch & 0xfff0) | ((data & 0x0f) << 0); break;
case 0x9000: irq_latch = (irq_latch & 0xff0f) | ((data & 0x0f) << 4); break;
case 0xa000: irq_latch = (irq_latch & 0xf0ff) | ((data & 0x0f) << 8); break;
case 0xb000: irq_latch = (irq_latch & 0x0fff) | ((data & 0x0f) << 12); break;
case 0xc000:
irq_mode = data & 0x04;
irq_enable = data & 0x02;
irq_acknowledge = data & 0x01;
if(irq_enable) irq_counter.w = irq_latch;
break;
case 0xd000:
irq_line = 0;
irq_enable = irq_acknowledge;
break;
case 0xf000:
prg_bank = data & 0x0f;
break;
}
}
void power() {
}
void reset() {
prg_bank = 0;
irq_mode = 0;
irq_enable = 0;
irq_acknowledge = 0;
irq_latch = 0;
irq_counter.w = 0;
irq_line = 0;
}
void serialize(serializer &s) {
s.integer(prg_bank);
s.integer(irq_mode);
s.integer(irq_enable);
s.integer(irq_acknowledge);
s.integer(irq_latch);
s.integer(irq_counter.w);
s.integer(irq_line);
}
VRC3(Board &board) : Chip(board) {
}
};

View File

@ -140,6 +140,7 @@ unsigned ciram_addr(unsigned addr) const {
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
}
throw;
}
void power() {

View File

@ -14,7 +14,7 @@ static string iNES(const uint8_t *data, unsigned size) {
unsigned prgram = 0;
unsigned chrram = chrrom == 0 ? 8192 : 0;
//print("iNES mapper: ", mapper, "\n");
print("iNES mapper: ", mapper, "\n");
output.append("cartridge\n");
@ -41,9 +41,14 @@ static string iNES(const uint8_t *data, unsigned size) {
break;
case 4:
//MMC3
output.append("\tboard type:NES-TLROM\n");
output.append("\t\tchip type:MMC3B\n");
prgram = 8192;
//MMC6
//output.append("\tboard type:NES-HKROM\n");
//output.append("\t\tchip type:MMC6\n");
//prgram = 1024;
break;
case 5:
@ -76,6 +81,10 @@ static string iNES(const uint8_t *data, unsigned size) {
case 21:
case 23:
case 25:
//VRC2
//output.append("\tboard type:KONAMI-VRC-2\n");
//output.append("\t\tchip type:VRC2\n");
//VRC4
output.append("\tboard type:KONAMI-VRC-4\n");
output.append("\t\tchip type:VRC4\n");
output.append("\t\t\tpinout a0=1 a1=0\n");
@ -109,6 +118,18 @@ static string iNES(const uint8_t *data, unsigned size) {
prgram = 8192;
break;
case 73:
output.append("\tboard type:KONAMI-VRC-3\n");
output.append("\t\tchip type:VRC3\n");
output.append("\t\tmirror:", mirror == 0 ? "horizontal" : "vertical", "\n");
prgram = 8192;
break;
case 75:
output.append("\tboard type:KONAMI-VRC-1\n");
output.append("\t\tchip type:VRC1\n");
break;
case 85:
output.append("\tboard type:KONAMI-VRC-7\n");
output.append("\t\tchip type:VRC7\n");
@ -119,7 +140,7 @@ static string iNES(const uint8_t *data, unsigned size) {
output.append("\t\tprg rom=", prgrom, " ram=", prgram, "\n");
output.append("\t\tchr rom=", chrrom, " ram=", chrram, "\n");
//print(output, "\n");
print(output, "\n");
return output;
}

View File

@ -162,6 +162,7 @@ string CPU::disassemble() {
op(0xe6, inc, zpg);
op(0xe8, inx, imp);
op(0xe9, sbc, imm);
op(0xea, nop, imp);
op(0xec, cpx, abs);
op(0xed, sbc, abs);
op(0xee, inc, abs);

View File

@ -27,6 +27,85 @@ Font::Font(const string &description):
description(description) {
}
//Image
//=====
bool Image::load(const string &filename, const Color &alpha) {
if(data) { delete[] data; data = nullptr; }
file fp;
if(fp.open(filename, file::mode::read) == false) return false;
uint8_t d0 = fp.read();
uint8_t d1 = fp.read();
uint8_t d2 = fp.read();
uint8_t d3 = fp.read();
fp.close();
if(d0 == 'B' && d1 == 'M') {
bmp::read(filename, data, width, height);
}
if(d0 == 0x89 && d1 == 'P' && d2 == 'N' && d3 == 'G') {
png image;
if(image.decode(filename)) {
image.alphaTransform((alpha.red << 16) + (alpha.green << 8) + (alpha.blue << 0));
width = image.info.width, height = image.info.height;
data = new uint32_t[width * height];
memcpy(data, image.data, width * height * sizeof(uint32_t));
}
}
return data;
}
void Image::load(const uint32_t *data, const Size &size) {
if(data) { delete[] data; data = nullptr; }
width = size.width, height = size.height;
this->data = new uint32_t[width * height];
memcpy(this->data, data, width * height * sizeof(uint32_t));
}
Image& Image::operator=(const Image &source) {
if(this == &source) return *this;
if(data) { delete[] data; data = nullptr; }
if(source.data == nullptr) return *this;
width = source.width, height = source.height;
data = new uint32_t[width * height];
memcpy(data, source.data, width * height * sizeof(uint32_t));
return *this;
}
Image& Image::operator=(Image &&source) {
if(this == &source) return *this;
if(data) { delete[] data; data = nullptr; }
data = source.data, width = source.width, height = source.height;
source.data = nullptr;
return *this;
}
Image::Image() : data(nullptr) {
}
Image::Image(const string &filename, const Color &alpha) : data(nullptr) {
load(filename, alpha);
}
Image::Image(const uint32_t *data, const Size &size) {
load(data, size);
}
Image::Image(const Image &source) : data(nullptr) {
operator=(source);
}
Image::Image(Image &&source) : data(nullptr) {
operator=(std::forward<Image>(source));
}
Image::~Image() {
if(data) delete[] data;
}
//Object
//======
@ -656,8 +735,29 @@ Button::~Button() {
//Canvas
//======
uint32_t* Canvas::buffer() {
return p.buffer();
uint32_t* Canvas::data() {
return state.data;
}
bool Canvas::setImage(const Image &image) {
if(image.data == nullptr || image.width == 0 || image.height == 0) return false;
state.width = image.width;
state.height = image.height;
setSize({ state.width, state.height });
memcpy(state.data, image.data, state.width * state.height * sizeof(uint32_t));
return true;
}
void Canvas::setSize(const Size &size) {
state.width = size.width;
state.height = size.height;
delete[] state.data;
state.data = new uint32_t[size.width * size.height];
return p.setSize(size);
}
Size Canvas::size() {
return { state.width, state.height };
}
void Canvas::update() {
@ -665,14 +765,18 @@ void Canvas::update() {
}
Canvas::Canvas():
state(*new State),
base_from_member<pCanvas&>(*new pCanvas(*this)),
Widget(base_from_member<pCanvas&>::value),
p(base_from_member<pCanvas&>::value) {
state.data = new uint32_t[state.width * state.height];
p.constructor();
}
Canvas::~Canvas() {
p.destructor();
delete[] state.data;
delete &state;
}
//CheckBox

View File

@ -41,6 +41,12 @@ enum : unsigned {
MinimumSize = 0u,
};
struct Color {
uint8_t red, green, blue, alpha;
inline Color() : red(0), green(0), blue(0), alpha(255) {}
inline Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha = 255) : red(red), green(green), blue(blue), alpha(alpha) {}
};
struct Geometry {
signed x, y;
unsigned width, height;
@ -48,16 +54,37 @@ struct Geometry {
inline Geometry(signed x, signed y, unsigned width, unsigned height) : x(x), y(y), width(width), height(height) {}
};
struct Position {
signed x, y;
inline Position() : x(0), y(0) {}
inline Position(signed x, signed y) : x(x), y(y) {}
};
struct Size {
unsigned width, height;
inline Size() : width(0), height(0) {}
inline Size(unsigned width, unsigned height) : width(width), height(height) {}
};
struct Font {
nall::string description;
Geometry geometry(const nall::string &text);
Font(const nall::string &description = "");
};
struct Color {
uint8_t red, green, blue, alpha;
inline Color() : red(0), green(0), blue(0), alpha(255) {}
inline Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha = 255) : red(red), green(green), blue(blue), alpha(alpha) {}
struct Image {
uint32_t *data;
unsigned width, height;
bool load(const nall::string &filename, const Color &alpha = Color{255, 255, 255});
void load(const uint32_t *data, const Size &size);
Image& operator=(const Image &source);
Image& operator=(Image &&source);
Image();
Image(const nall::string &filename, const Color &alpha = Color{255, 255, 255});
Image(const uint32_t *data, const Size &size);
Image(const Image &source);
Image(Image &&source);
~Image();
};
struct Object {
@ -302,11 +329,16 @@ struct Button : private nall::base_from_member<pButton&>, Widget {
};
struct Canvas : private nall::base_from_member<pCanvas&>, Widget {
uint32_t* buffer();
uint32_t* data();
bool setImage(const Image &image);
void setSize(const Size &size);
Size size();
void update();
Canvas();
~Canvas();
struct State;
State &state;
pCanvas &p;
};

View File

@ -1,6 +1,6 @@
void HorizontalLayout::append(Sizable &sizable, unsigned width, unsigned height, unsigned spacing) {
void HorizontalLayout::append(Sizable &sizable, const Size &size, unsigned spacing) {
for(auto &child : children) if(child.sizable == &sizable) return;
children.append({ &sizable, width, height, spacing });
children.append({ &sizable, size.width, size.height, spacing });
synchronizeLayout();
if(window()) window()->synchronizeLayout();
}

View File

@ -1,5 +1,5 @@
struct HorizontalLayout : public Layout {
void append(Sizable &sizable, unsigned width, unsigned height, unsigned spacing = 0);
void append(Sizable &sizable, const Size &size, unsigned spacing = 0);
void append(Sizable &sizable);
bool enabled();
Geometry minimumGeometry();

View File

@ -1,6 +1,6 @@
void VerticalLayout::append(Sizable &sizable, unsigned width, unsigned height, unsigned spacing) {
void VerticalLayout::append(Sizable &sizable, const Size &size, unsigned spacing) {
for(auto &child : children) if(child.sizable == &sizable) return;
children.append({ &sizable, width, height, spacing });
children.append({ &sizable, size.width, size.height, spacing });
synchronizeLayout();
if(window()) window()->synchronizeLayout();
}

View File

@ -1,5 +1,5 @@
struct VerticalLayout : public Layout {
void append(Sizable &sizable, unsigned width, unsigned height, unsigned spacing = 0);
void append(Sizable &sizable, const Size &size, unsigned spacing = 0);
void append(Sizable &sizable);
bool enabled();
Geometry minimumGeometry();

View File

@ -119,6 +119,18 @@ struct Button::State {
}
};
struct Canvas::State {
uint32_t *data;
unsigned width;
unsigned height;
State() {
data = nullptr;
width = 256;
height = 256;
}
};
struct CheckBox::State {
bool checked;
string text;

View File

@ -231,8 +231,7 @@ struct pCanvas : public pWidget {
Canvas &canvas;
cairo_surface_t *surface;
uint32_t* buffer();
void setGeometry(const Geometry &geometry);
void setSize(const Size &size);
void update();
pCanvas(Canvas &canvas) : pWidget(canvas), canvas(canvas) {}

View File

@ -6,33 +6,21 @@ static gboolean Canvas_expose(GtkWidget *widget, GdkEvent *event, pCanvas *self)
return true;
}
uint32_t* pCanvas::buffer() {
return (uint32_t*)cairo_image_surface_get_data(surface);
}
void pCanvas::setGeometry(const Geometry &geometry) {
if(geometry.width != cairo_image_surface_get_width (surface)
|| geometry.height != cairo_image_surface_get_height(surface)
) {
void pCanvas::setSize(const Size &size) {
cairo_surface_destroy(surface);
surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, geometry.width, geometry.height);
}
pWidget::setGeometry(geometry);
update();
surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, canvas.state.width, canvas.state.height);
}
void pCanvas::update() {
memcpy(cairo_image_surface_get_data(surface), canvas.state.data, canvas.state.width * canvas.state.height * sizeof(uint32_t));
if(gtk_widget_get_realized(gtkWidget) == false) return;
gdk_window_invalidate_rect(gtk_widget_get_window(gtkWidget), 0, true);
}
void pCanvas::constructor() {
surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, 256, 256);
surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, canvas.state.width, canvas.state.height);
memcpy(cairo_image_surface_get_data(surface), canvas.state.data, canvas.state.width * canvas.state.height * sizeof(uint32_t));
gtkWidget = gtk_drawing_area_new();
GdkColor color;
color.pixel = color.red = color.green = color.blue = 0;
gtk_widget_modify_bg(gtkWidget, GTK_STATE_NORMAL, &color);
gtk_widget_set_double_buffered(gtkWidget, false);
gtk_widget_add_events(gtkWidget, GDK_EXPOSURE_MASK);
g_signal_connect(G_OBJECT(gtkWidget), "expose_event", G_CALLBACK(Canvas_expose), (gpointer)this);

View File

@ -2,8 +2,10 @@
#define PHOENIX_HPP
#include <nall/array.hpp>
#include <nall/bmp.hpp>
#include <nall/config.hpp>
#include <nall/function.hpp>
#include <nall/png.hpp>
#include <nall/reference_array.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>

View File

@ -1,7 +1,7 @@
/****************************************************************************
** Meta object code from reading C++ file 'platform.moc.hpp'
**
** Created: Sun Oct 23 22:41:00 2011
** Created: Wed Nov 2 21:16:00 2011
** by: The Qt Meta Object Compiler version 62 (Qt 4.7.0)
**
** WARNING! All changes made in this file will be lost!

View File

@ -282,8 +282,7 @@ public:
QtCanvas(pCanvas &self);
} *qtCanvas;
uint32_t* buffer();
void setGeometry(const Geometry &geometry);
void setSize(const Size &size);
void update();
pCanvas(Canvas &canvas) : pWidget(canvas), canvas(canvas) {}

View File

@ -1,21 +1,17 @@
uint32_t* pCanvas::buffer() {
return (uint32_t*)qtImage->bits();
}
void pCanvas::setGeometry(const Geometry &geometry) {
qtImage = new QImage(geometry.width, geometry.height, QImage::Format_RGB32);
qtImage->fill(0);
update();
pWidget::setGeometry(geometry);
void pCanvas::setSize(const Size &size) {
delete qtImage;
qtImage = new QImage(size.width, size.height, QImage::Format_RGB32);
}
void pCanvas::update() {
memcpy(qtImage->bits(), canvas.state.data, canvas.state.width * canvas.state.height * sizeof(uint32_t));
qtCanvas->update();
}
void pCanvas::constructor() {
qtWidget = qtCanvas = new QtCanvas(*this);
qtImage = new QImage(256, 256, QImage::Format_RGB32);
qtImage = new QImage(canvas.state.width, canvas.state.height, QImage::Format_RGB32);
memcpy(qtImage->bits(), canvas.state.data, canvas.state.width * canvas.state.height * sizeof(uint32_t));
pWidget::synchronizeState();
update();
@ -36,6 +32,13 @@ void pCanvas::orphan() {
void pCanvas::QtCanvas::paintEvent(QPaintEvent *event) {
QPainter painter(self.qtCanvas);
painter.drawImage(0, 0, *self.qtImage);
//this will scale the source image to fit the target widget size (nearest-neighbor):
//painter.drawImage(
// QRect(0, 0, geometry().width(), geometry().height()),
// *self.qtImage,
// QRect(0, 0, self.canvas.state.width, self.canvas.state.height)
//);
}
pCanvas::QtCanvas::QtCanvas(pCanvas &self) : self(self) {

View File

@ -185,7 +185,7 @@ struct pButton : public pWidget {
struct pCanvas : public pWidget {
Canvas &canvas;
uint32_t* buffer();
void setSize(const Size &size);
void update();
pCanvas(Canvas &canvas) : pWidget(canvas), canvas(canvas) {}

View File

@ -1,5 +1,4 @@
uint32_t* pCanvas::buffer() {
return 0;
void pCanvas::setSize(const Size &size) {
}
void pCanvas::update() {

View File

@ -153,24 +153,11 @@ void OS_processDialogMessage(MSG &msg) {
OS_keyboardProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
}
wchar_t className[256];
GetClassName(msg.hwnd, className, 255);
//if this HWND accepts tabs to move between controls ...
if(!wcscmp(className, L"BUTTON") //Button, CheckBox, RadioBox
|| !wcscmp(className, L"COMBOBOX") //ComboBox
|| !wcscmp(className, L"EDIT") //HexEdit, LineEdit, TextEdit
|| !wcscmp(className, L"SCROLLBAR") //HorizontalScrollBar, VerticalScrollBar
|| !wcscmp(className, TRACKBAR_CLASS) //HorizontalSlider, VerticalSlider
|| !wcscmp(className, WC_LISTVIEW) //ListView
) {
//... return if the message is a dialog command
if(IsDialogMessage(msg.hwnd, &msg)) return;
}
if(!IsDialogMessage(GetForegroundWindow(), &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
void pOS::quit() {
PostQuitMessage(0);
@ -195,7 +182,7 @@ void pOS::initialize() {
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 0));
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hInstance = GetModuleHandle(0);

View File

@ -214,10 +214,9 @@ struct pButton : public pWidget {
struct pCanvas : public pWidget {
Canvas &canvas;
uint32_t *bufferRGB;
uint32_t *data;
uint32_t* buffer();
void setGeometry(const Geometry &geometry);
void setSize(const Size &size);
void update();
pCanvas(Canvas &canvas) : pWidget(canvas), canvas(canvas) {}

View File

@ -1,29 +1,30 @@
static LRESULT CALLBACK Canvas_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
if(msg == WM_GETDLGCODE) {
return DLGC_STATIC | DLGC_WANTCHARS;
}
if(msg == WM_PAINT) {
Object *object = (Object*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if(object && dynamic_cast<Canvas*>(object)) {
Canvas &canvas = (Canvas&)*object;
canvas.update();
}
return TRUE;
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
uint32_t* pCanvas::buffer() {
return bufferRGB;
}
void pCanvas::setGeometry(const Geometry &geometry) {
delete[] bufferRGB;
bufferRGB = new uint32_t[geometry.width * geometry.height]();
pWidget::setGeometry(geometry);
update();
void pCanvas::setSize(const Size &size) {
delete[] data;
data = new uint32_t[size.width * size.height];
memcpy(data, canvas.state.data, size.width * size.height * sizeof(uint32_t));
}
void pCanvas::update() {
RECT rc;
GetClientRect(hwnd, &rc);
unsigned width = rc.right, height = rc.bottom;
unsigned width = canvas.state.width, height = canvas.state.height; //rc.right, height = rc.bottom;
BITMAPINFO bmi;
memset(&bmi, 0, sizeof(BITMAPINFO));
@ -37,13 +38,14 @@ void pCanvas::update() {
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
SetDIBitsToDevice(ps.hdc, 0, 0, width, height, 0, 0, 0, height, (void*)bufferRGB, &bmi, DIB_RGB_COLORS);
SetDIBitsToDevice(ps.hdc, 0, 0, width, height, 0, 0, 0, height, (void*)data, &bmi, DIB_RGB_COLORS);
EndPaint(hwnd, &ps);
InvalidateRect(hwnd, 0, false);
}
void pCanvas::constructor() {
bufferRGB = new uint32_t[256 * 256]();
data = new uint32_t[canvas.state.width * canvas.state.height];
memcpy(data, canvas.state.data, canvas.state.width * canvas.state.height * sizeof(uint32_t));
hwnd = CreateWindow(L"phoenix_canvas", L"", WS_CHILD, 0, 0, 0, 0, parentWindow->p.hwnd, (HMENU)id, GetModuleHandle(0), 0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&canvas);
synchronize();
@ -51,6 +53,7 @@ void pCanvas::constructor() {
void pCanvas::destructor() {
DestroyWindow(hwnd);
delete[] data;
}
void pCanvas::orphan() {

View File

@ -30,6 +30,10 @@ static LRESULT CALLBACK Label_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPA
Label *label = (Label*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if(!window || !label) return DefWindowProc(hwnd, msg, wparam, lparam);
if(msg == WM_GETDLGCODE) {
return DLGC_STATIC | DLGC_WANTCHARS;
}
if(msg == WM_ERASEBKGND) {
//background is erased during WM_PAINT to prevent flickering
return TRUE;

View File

@ -34,7 +34,7 @@ string pTextEdit::text() {
void pTextEdit::constructor() {
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE, L"EDIT", L"",
WS_CHILD | ES_AUTOVSCROLL | ES_MULTILINE | ES_WANTRETURN | (textEdit.state.wordWrap == false ? ES_AUTOHSCROLL : 0),
WS_CHILD | WS_TABSTOP | ES_AUTOVSCROLL | ES_MULTILINE | ES_WANTRETURN | (textEdit.state.wordWrap == false ? ES_AUTOHSCROLL : 0),
0, 0, 0, 0, parentWindow->p.hwnd, (HMENU)id, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&textEdit);

View File

@ -18,5 +18,6 @@ void pViewport::orphan() {
}
static LRESULT CALLBACK Viewport_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
if(msg == WM_GETDLGCODE) return DLGC_STATIC | DLGC_WANTCHARS;
return DefWindowProc(hwnd, msg, wparam, lparam);
}

View File

@ -1,8 +1,8 @@
DipSwitches *dipSwitches = 0;
DipSwitch::DipSwitch() {
append(name, ~0, 0, 5);
append(value, ~0, 0);
append(name, { ~0, 0 }, 5);
append(value, { ~0, 0 }, 0);
}
DipSwitches::DipSwitches() {
@ -13,10 +13,10 @@ DipSwitches::DipSwitches() {
append(layout);
for(unsigned n = 0; n < 8; n++)
layout.append(dip[n], ~0, 0, 5);
layout.append(controlLayout, ~0, 0, 5);
controlLayout.append(spacer, ~0, 0);
controlLayout.append(acceptButton, 0, 0);
layout.append(dip[n], { ~0, 0 }, 5);
layout.append(controlLayout, { ~0, 0 }, 5);
controlLayout.append(spacer, { ~0, 0 }, 0);
controlLayout.append(acceptButton, { 0, 0 }, 0);
setGeometry({ 128, 128, 400, layout.minimumGeometry().height });
windowManager->append(this, "DipSwitches");

View File

@ -10,14 +10,14 @@ FileBrowser::FileBrowser() {
openButton.setText("Open");
append(layout);
layout.append(pathLayout, ~0, 0, 5);
pathLayout.append(pathEdit, ~0, 0, 5);
pathLayout.append(pathBrowse, 0, 0, 5);
pathLayout.append(pathUp, 0, 0);
layout.append(fileList, ~0, ~0, 5);
layout.append(controlLayout, ~0, 0);
controlLayout.append(filterLabel, ~0, 0, 5);
controlLayout.append(openButton, 80, 0);
layout.append(pathLayout, { ~0, 0 }, 5);
pathLayout.append(pathEdit, { ~0, 0 }, 5);
pathLayout.append(pathBrowse, { 0, 0 }, 5);
pathLayout.append(pathUp, { 0, 0 });
layout.append(fileList, { ~0, ~0 }, 5);
layout.append(controlLayout, { ~0, 0 });
controlLayout.append(filterLabel, { ~0, 0 }, 5);
controlLayout.append(openButton, { 80, 0 });
pathEdit.onActivate = [&] {
string path = pathEdit.text();

View File

@ -2,9 +2,9 @@ SlotLoader *slotLoader = 0;
SlotLoaderPath::SlotLoaderPath() {
browse.setText("Browse ...");
append(label, 40, 0, 5);
append(path, ~0, 0, 5);
append(browse, 80, 0);
append(label, { 40, 0 }, 5);
append(path, { ~0, 0 }, 5);
append(browse, { 80, 0 }, 0);
}
SlotLoader::SlotLoader() {
@ -15,12 +15,12 @@ SlotLoader::SlotLoader() {
loadButton.setText("Load");
append(layout);
layout.append(base, ~0, 0, 5);
layout.append(slot[0], ~0, 0, 5);
layout.append(slot[1], ~0, 0, 5);
layout.append(controlLayout, ~0, 0);
controlLayout.append(spacer, ~0, 0);
controlLayout.append(loadButton, 80, 0);
layout.append(base, { ~0, 0 }, 5);
layout.append(slot[0], { ~0, 0 }, 5);
layout.append(slot[1], { ~0, 0 }, 5);
layout.append(controlLayout, { ~0, 0 }, 0);
controlLayout.append(spacer, { ~0, 0 }, 0);
controlLayout.append(loadButton, { 80, 0 }, 0);
setGeometry({ 128, 128, 500, layout.minimumGeometry().height });
windowManager->append(this, "SlotLoader");

View File

@ -27,7 +27,7 @@ void Application::run() {
}
Application::Application(int argc, char **argv) {
title = "bsnes v083.09";
title = "bsnes v083.10";
application = this;
quit = false;

View File

@ -41,22 +41,22 @@ AdvancedSettings::AdvancedSettings() {
if(list[n] == input.default_driver() && config->input.driver == "") inputDriver.setSelection(n);
}
append(title, ~0, 0, 5);
append(driverLabel, ~0, 0);
append(driverLayout, ~0, 0, 5);
driverLayout.append(videoLabel, 0, 0, 5);
driverLayout.append(videoDriver, ~0, 0, 5);
driverLayout.append(audioLabel, 0, 0, 5);
driverLayout.append(audioDriver, ~0, 0, 5);
driverLayout.append(inputLabel, 0, 0, 5);
driverLayout.append(inputDriver, ~0, 0);
append(focusPolicyLabel, ~0, 0);
append(focusPolicyLayout, ~0, 0, 5);
focusPolicyLayout.append(focusPolicy[0], ~0, 0, 5);
focusPolicyLayout.append(focusPolicy[1], ~0, 0, 5);
focusPolicyLayout.append(focusPolicy[2], ~0, 0);
append(spacer, ~0, ~0);
append(aboutLabel, ~0, 0);
append(title, { ~0, 0 }, 5);
append(driverLabel, { ~0, 0 }, 0);
append(driverLayout, { ~0, 0 }, 5);
driverLayout.append(videoLabel, { 0, 0 }, 5);
driverLayout.append(videoDriver, { ~0, 0 }, 5);
driverLayout.append(audioLabel, { 0, 0 }, 5);
driverLayout.append(audioDriver, { ~0, 0 }, 5);
driverLayout.append(inputLabel, { 0, 0 }, 5);
driverLayout.append(inputDriver, { ~0, 0 }, 0);
append(focusPolicyLabel, { ~0, 0 }, 0);
append(focusPolicyLayout, { ~0, 0 }, 5);
focusPolicyLayout.append(focusPolicy[0], { ~0, 0 }, 5);
focusPolicyLayout.append(focusPolicy[1], { ~0, 0 }, 5);
focusPolicyLayout.append(focusPolicy[2], { ~0, 0 }, 0);
append(spacer, { ~0, ~0 }, 0);
append(aboutLabel, { ~0, 0 }, 0);
videoDriver.onChange = [&] {
lstring list;

View File

@ -1,9 +1,9 @@
AudioSettings *audioSettings = 0;
AudioSlider::AudioSlider() {
append(name, 75, 0);
append(value, 75, 0);
append(slider, ~0, 0);
append(name, { 75, 0 });
append(value, { 75, 0 });
append(slider, { ~0, 0 });
}
unsigned AudioSlider::position() {
@ -68,20 +68,20 @@ AudioSettings::AudioSettings() {
gameBoy.base = 4194304;
gameBoy.step = 131;
append(title, ~0, 0, 5);
append(outputLabel, ~0, 0);
append(outputLayout, ~0, 0, 5);
outputLayout.append(frequencyLabel, 0, 0, 5);
outputLayout.append(frequencySelection, ~0, 0, 5);
outputLayout.append(latencyLabel, 0, 0, 5);
outputLayout.append(latencySelection, ~0, 0, 5);
outputLayout.append(resamplerLabel, 0, 0, 5);
outputLayout.append(resamplerSelection, ~0, 0);
append(volume, ~0, 0, 5);
append(frequencyAdjustmentLabel, ~0, 0);
append(nes, ~0, 0);
append(snes, ~0, 0);
append(gameBoy, ~0, 0);
append(title, { ~0, 0 }, 5);
append(outputLabel, { ~0, 0 }, 0);
append(outputLayout, { ~0, 0 }, 5);
outputLayout.append(frequencyLabel, { 0, 0 }, 5);
outputLayout.append(frequencySelection, { ~0, 0 }, 5);
outputLayout.append(latencyLabel, { 0, 0 }, 5);
outputLayout.append(latencySelection, { ~0, 0 }, 5);
outputLayout.append(resamplerLabel, { 0, 0 }, 5);
outputLayout.append(resamplerSelection, { ~0, 0 }, 0);
append(volume, { ~0, 0 }, 5);
append(frequencyAdjustmentLabel, { ~0, 0 }, 0);
append(nes, { ~0, 0 }, 0);
append(snes, { ~0, 0 }, 0);
append(gameBoy, { ~0, 0 }, 0);
frequencySelection.setSelection(
config->audio.frequency == 32000 ? 0 :

View File

@ -15,18 +15,18 @@ InputSettings::InputSettings() : activeInput(0) {
}
primaryChange();
append(title, ~0, 0, 5);
append(selectionLayout, ~0, 0, 5);
selectionLayout.append(primary, ~0, 0, 5);
selectionLayout.append(secondary, ~0, 0, 5);
selectionLayout.append(tertiary, ~0, 0);
append(inputList, ~0, ~0, 5);
append(controlLayout, ~0, 0);
controlLayout.append(assignPrimary, 100, 0, 5);
controlLayout.append(assignSecondary, 100, 0, 5);
controlLayout.append(assignTertiary, 100, 0, 5);
controlLayout.append(spacer, ~0, 0);
controlLayout.append(clearButton, 80, 0);
append(title, { ~0, 0 }, 5);
append(selectionLayout, { ~0, 0 }, 5);
selectionLayout.append(primary, { ~0, 0 }, 5);
selectionLayout.append(secondary, { ~0, 0 }, 5);
selectionLayout.append(tertiary, { ~0, 0 }, 0);
append(inputList, { ~0, ~0 }, 5);
append(controlLayout, { ~0, 0 }, 0);
controlLayout.append(assignPrimary, { 100, 0 }, 5);
controlLayout.append(assignSecondary, { 100, 0 }, 5);
controlLayout.append(assignTertiary, { 100, 0 }, 5);
controlLayout.append(spacer, { ~0, 0 }, 0);
controlLayout.append(clearButton, { 80, 0 }, 0);
primary.onChange = { &InputSettings::primaryChange, this };
secondary.onChange = { &InputSettings::secondaryChange, this };

View File

@ -5,14 +5,14 @@
#include "advanced.cpp"
SettingsWindow *settingsWindow = 0;
void SettingsLayout::append(Sizable &sizable, unsigned width, unsigned height, unsigned spacing) {
layout.append(sizable, width, height, spacing);
void SettingsLayout::append(Sizable &sizable, const Size &size, unsigned spacing) {
layout.append(sizable, size, spacing);
}
SettingsLayout::SettingsLayout() {
setMargin(5);
HorizontalLayout::append(spacer, 120, ~0, 5);
HorizontalLayout::append(layout, ~0, ~0);
HorizontalLayout::append(spacer, { 120, ~0 }, 5);
HorizontalLayout::append(layout, { ~0, ~0 }, 0);
}
SettingsWindow::SettingsWindow() {
@ -34,7 +34,7 @@ SettingsWindow::SettingsWindow() {
advancedSettings = new AdvancedSettings;
append(layout);
layout.append(panelList, 120, ~0, 5);
layout.append(panelList, { 120, ~0 }, 5);
append(*videoSettings);
append(*audioSettings);
append(*inputSettings);

View File

@ -2,7 +2,7 @@ struct SettingsLayout : HorizontalLayout {
Widget spacer;
VerticalLayout layout;
void append(Sizable &widget, unsigned width, unsigned height, unsigned spacing = 0);
void append(Sizable &widget, const Size &size, unsigned spacing = 0);
SettingsLayout();
};

View File

@ -1,9 +1,9 @@
VideoSettings *videoSettings = 0;
VideoSlider::VideoSlider() {
append(name, 75, 0);
append(value, 75, 0);
append(slider, ~0, 0);
append(name, { 75, 0 });
append(value, { 75, 0 });
append(slider, { ~0, 0 });
}
VideoSettings::VideoSettings() {
@ -36,24 +36,24 @@ VideoSettings::VideoSettings() {
compositor[2].setText("Always");
RadioBox::group(compositor[0], compositor[1], compositor[2]);
append(title, ~0, 0, 5);
append(colorAdjustment, ~0, 0);
append(brightness, ~0, 0);
append(contrast, ~0, 0);
append(gamma, ~0, 0, 5);
append(overscanAdjustment, ~0, 0);
append(overscanHorizontal, ~0, 0);
append(overscanVertical, ~0, 0, 5);
append(fullScreenMode, ~0, 0);
append(fullScreenLayout, ~0, 0, 5);
fullScreenLayout.append(fullScreen[0], ~0, 0, 5);
fullScreenLayout.append(fullScreen[1], ~0, 0, 5);
fullScreenLayout.append(fullScreen[2], ~0, 0);
append(compositorLabel, ~0, 0);
append(compositorLayout, ~0, 0);
compositorLayout.append(compositor[0], ~0, 0, 5);
compositorLayout.append(compositor[1], ~0, 0, 5);
compositorLayout.append(compositor[2], ~0, 0);
append(title, { ~0, 0 }, 5);
append(colorAdjustment, { ~0, 0 }, 0);
append(brightness, { ~0, 0 }, 0);
append(contrast, { ~0, 0 }, 0);
append(gamma, { ~0, 0 }, 5);
append(overscanAdjustment, { ~0, 0 }, 0);
append(overscanHorizontal, { ~0, 0 }, 0);
append(overscanVertical, { ~0, 0 }, 5);
append(fullScreenMode, { ~0, 0 }, 0);
append(fullScreenLayout, { ~0, 0 }, 5);
fullScreenLayout.append(fullScreen[0], { ~0, 0 }, 5);
fullScreenLayout.append(fullScreen[1], { ~0, 0 }, 5);
fullScreenLayout.append(fullScreen[2], { ~0, 0 }, 0);
append(compositorLabel, { ~0, 0 }, 0);
append(compositorLayout, { ~0, 0 }, 0);
compositorLayout.append(compositor[0], { ~0, 0 }, 5);
compositorLayout.append(compositor[1], { ~0, 0 }, 5);
compositorLayout.append(compositor[2], { ~0, 0 }, 0);
brightness.slider.setPosition(config->video.brightness);
contrast.slider.setPosition(config->video.contrast);
@ -95,9 +95,6 @@ void VideoSettings::synchronize() {
config->video.gamma = gamma.slider.position();
config->video.maskOverscanHorizontal = overscanHorizontal.slider.position();
config->video.maskOverscanVertical = overscanVertical.slider.position();
if(fullScreen[0].checked()) { config->video.fullScreenMode = 0; }
if(fullScreen[1].checked()) { config->video.fullScreenMode = 1; }
if(fullScreen[2].checked()) { config->video.fullScreenMode = 2; }
brightness.value.setText({ config->video.brightness, "%" });
contrast.value.setText({ config->video.contrast, "%" });

View File

@ -11,12 +11,12 @@ CheatDatabase::CheatDatabase() {
acceptButton.setText("Add Codes");
append(layout);
layout.append(cheatList, ~0, ~0, 5);
layout.append(controlLayout, ~0, 0);
controlLayout.append(selectAllButton, 100, 0, 5);
controlLayout.append(unselectAllButton, 100, 0);
controlLayout.append(spacer, ~0, 0);
controlLayout.append(acceptButton, 80, 0);
layout.append(cheatList, { ~0, ~0 }, 5);
layout.append(controlLayout, { ~0, 0 }, 0);
controlLayout.append(selectAllButton, { 100, 0 }, 5);
controlLayout.append(unselectAllButton, { 100, 0 }, 0);
controlLayout.append(spacer, { ~0, 0 }, 0);
controlLayout.append(acceptButton, { 80, 0 }, 0);
selectAllButton.onTick = [&] {
for(unsigned n = 0; n < cheatCode.size(); n++) cheatList.setChecked(n, true);

View File

@ -16,18 +16,18 @@ CheatEditor::CheatEditor() {
append(layout);
layout.setMargin(5);
layout.append(cheatList, ~0, ~0, 5);
layout.append(codeLayout, ~0, 0, 5);
codeLayout.append(codeLabel, 80, 0);
codeLayout.append(codeEdit, ~0, 0);
layout.append(descLayout, ~0, 0, 5);
descLayout.append(descLabel, 80, 0);
descLayout.append(descEdit, ~0, 0);
layout.append(controlLayout, ~0, 0);
controlLayout.append(findButton, 100, 0);
controlLayout.append(spacer, ~0, 0);
controlLayout.append(clearAllButton, 80, 0, 5);
controlLayout.append(clearButton, 80, 0);
layout.append(cheatList, { ~0, ~0 }, 5);
layout.append(codeLayout, { ~0, 0 }, 5);
codeLayout.append(codeLabel, { 80, 0 }, 0);
codeLayout.append(codeEdit, { ~0, 0 }, 0);
layout.append(descLayout, { ~0, 0 }, 5);
descLayout.append(descLabel, { 80, 0 }, 0);
descLayout.append(descEdit, { ~0, 0 }, 0);
layout.append(controlLayout, { ~0, 0 }, 0);
controlLayout.append(findButton, { 100, 0 }, 0);
controlLayout.append(spacer, { ~0, 0 }, 0);
controlLayout.append(clearAllButton, { 80, 0 }, 5);
controlLayout.append(clearButton, { 80, 0 }, 0);
for(unsigned n = 0; n < 128; n++) cheatList.append("", "", "");
updateUI();

View File

@ -14,15 +14,15 @@ StateManager::StateManager() {
append(layout);
layout.setMargin(5);
layout.append(stateList, ~0, ~0, 5);
layout.append(descLayout, ~0, 0, 5);
descLayout.append(descLabel, 0, 0, 5);
descLayout.append(descEdit, ~0, 0);
layout.append(controlLayout, ~0, 0);
controlLayout.append(spacer, ~0, 0);
controlLayout.append(loadButton, 80, 0, 5);
controlLayout.append(saveButton, 80, 0, 5);
controlLayout.append(eraseButton, 80, 0);
layout.append(stateList, { ~0, ~0 }, 5);
layout.append(descLayout, { ~0, 0 }, 5);
descLayout.append(descLabel, { 0, 0 }, 5);
descLayout.append(descEdit, { ~0, 0 }, 0);
layout.append(controlLayout, { ~0, 0 }, 0);
controlLayout.append(spacer, { ~0, 0 }, 0);
controlLayout.append(loadButton, { 80, 0 }, 5);
controlLayout.append(saveButton, { 80, 0 }, 5);
controlLayout.append(eraseButton, { 80, 0 }, 0);
for(unsigned n = 0; n < 32; n++) stateList.append(decimal<2>(n + 1), "(empty)");
stateList.autoSizeColumns();

View File

@ -47,6 +47,7 @@ void Utility::resizeMainWindow(bool shrink) {
unsigned width = geometry.width, height = geometry.height;
switch(interface->mode()) {
case Interface::Mode::None: return;
case Interface::Mode::NES: width = 256, height = 240; break;
case Interface::Mode::SNES: width = 256, height = 240; break;
case Interface::Mode::GameBoy: width = 160, height = 144; break;