mirror of https://github.com/bsnes-emu/bsnes.git
Update to v106r69 release.
byuu says: The biggest change was improving WonderSwan emulation. With help from trap15, I tracked down a bug where I was checking the wrong bit for reverse DMA transfers. Then I also emulated VTOTAL to support variable refresh rate. Then I improved HyperVoice emulation which should be unsigned samples in three of four modes. That got Fire Lancer running great. I also rewrote the disassembler. The old one disassembled many instructions completely wrong, and deviated too much from any known x86 syntax. I also emulated some of the quirks of the V30 (two-byte POP into registers fails, SALC is just XLAT mirrored, etc) which probably don't matter unless someone tries to run code to verify it's a NEC CPU and not an Intel CPU, but hey, why not? I also put more work into the MSX skeleton, but it's still just a skeleton with no real emulation yet.
This commit is contained in:
parent
3159285eaa
commit
aaf094e7c4
|
@ -22,13 +22,15 @@ using namespace nall;
|
|||
|
||||
#include <libco/libco.h>
|
||||
#include <emulator/types.hpp>
|
||||
#include <emulator/memory/readable.hpp>
|
||||
#include <emulator/memory/writable.hpp>
|
||||
#include <emulator/audio/audio.hpp>
|
||||
#include <emulator/video/video.hpp>
|
||||
#include <emulator/resource/resource.hpp>
|
||||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "106.68";
|
||||
static const string Version = "106.69";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "https://byuu.org/";
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
template<typename type>
|
||||
struct WritableMemory {
|
||||
inline auto reset() -> void {
|
||||
delete[] self.data;
|
||||
self.data = nullptr;
|
||||
self.size = 0;
|
||||
self.mask = 0;
|
||||
}
|
||||
|
||||
inline auto allocate(uint size, type fill = ~0ull) -> void {
|
||||
delete[] self.data;
|
||||
self.size = size;
|
||||
self.mask = bit::round(size) - 1;
|
||||
self.data = new uint8[self.mask];
|
||||
memory::fill<type>(self.data, size, fill);
|
||||
}
|
||||
|
||||
explicit operator bool() const { return (bool)self.data; }
|
||||
inline auto data() -> type* { return self.data; }
|
||||
inline auto size() const -> uint { return self.size; }
|
||||
inline auto mask() const -> uint { return self.mask; }
|
||||
|
||||
inline auto operator[](uint address) -> type& {
|
||||
return self.data[address & self.mask];
|
||||
}
|
||||
|
||||
inline auto read(uint address) -> type {
|
||||
return self.data[address & self.mask];
|
||||
}
|
||||
|
||||
inline auto write(uint address, type data) -> void {
|
||||
self.data[address & self.mask] = data;
|
||||
}
|
||||
|
||||
struct {
|
||||
type* data = nullptr;
|
||||
uint size = 0;
|
||||
uint mask = 0;
|
||||
} self;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
namespace Emulator::Memory {
|
||||
|
||||
inline auto mirror(uint address, uint size) -> uint {
|
||||
if(size == 0) return 0;
|
||||
uint base = 0;
|
||||
uint mask = 1 << 31;
|
||||
while(address >= size) {
|
||||
while(!(address & mask)) mask >>= 1;
|
||||
address -= mask;
|
||||
if(size > mask) {
|
||||
size -= mask;
|
||||
base += mask;
|
||||
}
|
||||
mask >>= 1;
|
||||
}
|
||||
return base + address;
|
||||
}
|
||||
|
||||
inline auto reduce(uint address, uint mask) -> uint {
|
||||
while(mask) {
|
||||
uint bits = (mask & -mask) - 1;
|
||||
address = address >> 1 & ~bits | address & bits;
|
||||
mask = (mask & mask - 1) >> 1;
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
|
||||
#include <emulator/memory/memory.hpp>
|
||||
|
||||
namespace Emulator::Memory {
|
||||
|
||||
template<typename T>
|
||||
struct Readable {
|
||||
~Readable() { reset(); }
|
||||
|
||||
inline auto reset() -> void {
|
||||
delete[] self.data;
|
||||
self.data = nullptr;
|
||||
self.size = 0;
|
||||
self.mask = 0;
|
||||
}
|
||||
|
||||
inline auto allocate(uint size, T fill = ~0ull) -> void {
|
||||
if(!size) return reset();
|
||||
delete[] self.data;
|
||||
self.size = size;
|
||||
self.mask = bit::round(self.size) - 1;
|
||||
self.data = new T[self.mask + 1];
|
||||
memory::fill<T>(self.data, self.mask + 1, fill);
|
||||
}
|
||||
|
||||
inline auto load(vfs::shared::file fp) -> void {
|
||||
fp->read(self.data, min(fp->size(), self.size * sizeof(T)));
|
||||
for(uint address = self.size; address <= self.mask; address++) {
|
||||
self.data[address] = self.data[mirror(address, self.size)];
|
||||
}
|
||||
}
|
||||
|
||||
inline auto save(vfs::shared::file fp) -> void {
|
||||
fp->write(self.data, self.size * sizeof(T));
|
||||
}
|
||||
|
||||
explicit operator bool() const { return (bool)self.data; }
|
||||
inline auto data() const -> const T* { return self.data; }
|
||||
inline auto size() const -> uint { return self.size; }
|
||||
inline auto mask() const -> uint { return self.mask; }
|
||||
|
||||
inline auto operator[](uint address) const -> T { return self.data[address & self.mask]; }
|
||||
inline auto read(uint address) const -> T { return self.data[address & self.mask]; }
|
||||
inline auto write(uint address, T data) const -> void {}
|
||||
|
||||
private:
|
||||
struct {
|
||||
T* data = nullptr;
|
||||
uint size = 0;
|
||||
uint mask = 0;
|
||||
} self;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
#pragma once
|
||||
|
||||
#include <emulator/memory/memory.hpp>
|
||||
|
||||
namespace Emulator::Memory {
|
||||
|
||||
template<typename T>
|
||||
struct Writable {
|
||||
~Writable() { reset(); }
|
||||
|
||||
inline auto reset() -> void {
|
||||
delete[] self.data;
|
||||
self.data = nullptr;
|
||||
self.size = 0;
|
||||
self.mask = 0;
|
||||
}
|
||||
|
||||
inline auto allocate(uint size, T fill = ~0ull) -> void {
|
||||
if(!size) return reset();
|
||||
delete[] self.data;
|
||||
self.size = size;
|
||||
self.mask = bit::round(self.size) - 1;
|
||||
self.data = new T[self.mask + 1];
|
||||
memory::fill<T>(self.data, self.mask + 1, fill);
|
||||
}
|
||||
|
||||
inline auto load(vfs::shared::file fp) -> void {
|
||||
fp->read(self.data, min(fp->size(), self.size * sizeof(T)));
|
||||
for(uint address = self.size; address <= self.mask; address++) {
|
||||
self.data[address] = self.data[mirror(address, self.size)];
|
||||
}
|
||||
}
|
||||
|
||||
inline auto save(vfs::shared::file fp) -> void {
|
||||
fp->write(self.data, self.size * sizeof(T));
|
||||
}
|
||||
|
||||
explicit operator bool() const { return (bool)self.data; }
|
||||
inline auto data() -> T* { return self.data; }
|
||||
inline auto data() const -> const T* { return self.data; }
|
||||
inline auto size() const -> uint { return self.size; }
|
||||
inline auto mask() const -> uint { return self.mask; }
|
||||
|
||||
inline auto operator[](uint address) -> T& { return self.data[address & self.mask]; }
|
||||
inline auto operator[](uint address) const -> T { return self.data[address & self.mask]; }
|
||||
inline auto read(uint address) const -> T { return self.data[address & self.mask]; }
|
||||
inline auto write(uint address, T data) -> void { self.data[address & self.mask] = data; }
|
||||
|
||||
private:
|
||||
struct {
|
||||
T* data = nullptr;
|
||||
uint size = 0;
|
||||
uint mask = 0;
|
||||
} self;
|
||||
};
|
||||
|
||||
}
|
|
@ -51,21 +51,17 @@ auto Cartridge::load() -> bool {
|
|||
information.title = document["game/label"].text();
|
||||
|
||||
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=ROM,content=Program)"]}) {
|
||||
rom.size = memory.size;
|
||||
rom.mask = bit::round(rom.size) - 1;
|
||||
rom.data = new uint8[rom.mask + 1];
|
||||
rom.allocate(memory.size);
|
||||
if(auto fp = platform->open(pathID(), memory.name(), File::Read, File::Required)) {
|
||||
fp->read(rom.data, rom.size);
|
||||
}
|
||||
rom.load(fp);
|
||||
} else return false;
|
||||
}
|
||||
|
||||
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=RAM,content=Save)"]}) {
|
||||
ram.size = memory.size;
|
||||
ram.mask = bit::round(ram.size) - 1;
|
||||
ram.data = new uint8[ram.mask + 1];
|
||||
ram.allocate(memory.size);
|
||||
if(memory.nonVolatile) {
|
||||
if(auto fp = platform->open(pathID(), memory.name(), File::Read)) {
|
||||
fp->read(ram.data, ram.size);
|
||||
ram.load(fp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,17 +75,15 @@ auto Cartridge::save() -> void {
|
|||
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=RAM,content=Save)"]}) {
|
||||
if(memory.nonVolatile) {
|
||||
if(auto fp = platform->open(pathID(), memory.name(), File::Write)) {
|
||||
fp->write(ram.data, ram.size);
|
||||
ram.save(fp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Cartridge::unload() -> void {
|
||||
delete[] rom.data;
|
||||
delete[] ram.data;
|
||||
rom = {};
|
||||
ram = {};
|
||||
rom.reset();
|
||||
ram.reset();
|
||||
}
|
||||
|
||||
auto Cartridge::power() -> void {
|
||||
|
@ -99,29 +93,4 @@ auto Cartridge::power() -> void {
|
|||
mapper.romPage2 = 2;
|
||||
}
|
||||
|
||||
auto Cartridge::Memory::mirror(uint addr, uint size) -> uint {
|
||||
uint base = 0;
|
||||
uint mask = 1 << 21;
|
||||
while(addr >= size) {
|
||||
while(!(addr & mask)) mask >>= 1;
|
||||
addr -= mask;
|
||||
if(size > mask) {
|
||||
size -= mask;
|
||||
base += mask;
|
||||
}
|
||||
mask >>= 1;
|
||||
}
|
||||
return base + addr;
|
||||
}
|
||||
|
||||
auto Cartridge::Memory::read(uint addr) -> uint8 {
|
||||
if(!size) return 0x00;
|
||||
return this->data[mirror(addr, size)];
|
||||
}
|
||||
|
||||
auto Cartridge::Memory::write(uint addr, uint8 data) -> void {
|
||||
if(!size) return;
|
||||
this->data[mirror(addr, size)] = data;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,18 +27,8 @@ struct Cartridge {
|
|||
string title;
|
||||
} information;
|
||||
|
||||
struct Memory {
|
||||
uint8* data = nullptr;
|
||||
uint size = 0;
|
||||
uint mask = 0;
|
||||
|
||||
static auto mirror(uint addr, uint size) -> uint;
|
||||
auto read(uint addr) -> uint8;
|
||||
auto write(uint addr, uint8 data) -> void;
|
||||
};
|
||||
|
||||
Memory rom;
|
||||
Memory ram;
|
||||
Emulator::Memory::Readable<uint8> rom;
|
||||
Emulator::Memory::Writable<uint8> ram;
|
||||
|
||||
struct Mapper {
|
||||
//$fffc
|
||||
|
|
|
@ -14,7 +14,7 @@ auto Cartridge::read(uint16 addr) -> maybe<uint8> {
|
|||
}
|
||||
|
||||
case 2: {
|
||||
if(mapper.ramEnablePage2) {
|
||||
if(ram && mapper.ramEnablePage2) {
|
||||
return ram.read(mapper.ramPage2 << 14 | addr);
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ auto Cartridge::read(uint16 addr) -> maybe<uint8> {
|
|||
}
|
||||
|
||||
case 3: {
|
||||
if(mapper.ramEnablePage3) {
|
||||
if(ram && mapper.ramEnablePage3) {
|
||||
return ram.read(addr);
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ auto Cartridge::write(uint16 addr, uint8 data) -> bool {
|
|||
}
|
||||
|
||||
case 2: {
|
||||
if(mapper.ramEnablePage2) {
|
||||
if(ram && mapper.ramEnablePage2) {
|
||||
ram.write(mapper.ramPage2 << 14 | addr, data);
|
||||
return true;
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ auto Cartridge::write(uint16 addr, uint8 data) -> bool {
|
|||
}
|
||||
|
||||
case 3: {
|
||||
if(mapper.ramEnablePage3) {
|
||||
if(ram && mapper.ramEnablePage3) {
|
||||
ram.write(addr, data);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
auto Cartridge::serialize(serializer& s) -> void {
|
||||
if(ram.size) s.array(ram.data, ram.size);
|
||||
if(ram) s.array(ram.data(), ram.size());
|
||||
}
|
||||
|
|
|
@ -39,8 +39,8 @@ struct CPU : Processor::Z80, Processor::Z80::Bus, Thread {
|
|||
vector<Thread*> peripherals;
|
||||
|
||||
private:
|
||||
Emulator::WritableMemory<uint8> ram;
|
||||
Emulator::WritableMemory<uint8> expansion;
|
||||
Emulator::Memory::Writable<uint8> ram;
|
||||
Emulator::Memory::Writable<uint8> expansion;
|
||||
|
||||
struct State {
|
||||
bool nmiLine = 0;
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include <emulator/emulator.hpp>
|
||||
#include <emulator/thread.hpp>
|
||||
#include <emulator/scheduler.hpp>
|
||||
#include <emulator/memory.hpp>
|
||||
#include <emulator/cheat.hpp>
|
||||
|
||||
#include <processor/z80/z80.hpp>
|
||||
|
|
|
@ -7,6 +7,7 @@ struct System {
|
|||
auto region() const -> Region { return information.region; }
|
||||
auto colorburst() const -> double { return information.colorburst; }
|
||||
|
||||
//system.cpp
|
||||
auto run() -> void;
|
||||
auto runToSave() -> void;
|
||||
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
processors += z80
|
||||
|
||||
objects += msx-interface msx-system
|
||||
objects += msx-interface msx-system msx-cartridge
|
||||
objects += msx-cpu msx-vdp msx-psg
|
||||
|
||||
obj/msx-interface.o: msx/interface/interface.cpp
|
||||
obj/msx-system.o: msx/system/system.cpp
|
||||
obj/msx-cartridge.o: msx/cartridge/cartridge.cpp
|
||||
obj/msx-cpu.o: msx/cpu/cpu.cpp
|
||||
obj/msx-vdp.o: msx/vdp/vdp.cpp
|
||||
obj/msx-psg.o: msx/psg/psg.cpp
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
#include <msx/msx.hpp>
|
||||
|
||||
namespace MSX {
|
||||
|
||||
Cartridge cartridge;
|
||||
Cartridge expansion;
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto Cartridge::load() -> bool {
|
||||
information = {};
|
||||
|
||||
if(Model::MSX()) {
|
||||
if(auto loaded = platform->load(ID::MSX, "MSX", "msx", {"NTSC", "PAL"})) {
|
||||
information.pathID = loaded.pathID;
|
||||
information.region = loaded.option;
|
||||
} else return false;
|
||||
}
|
||||
|
||||
if(auto fp = platform->open(pathID(), "manifest.bml", File::Read, File::Required)) {
|
||||
information.manifest = fp->reads();
|
||||
} else return false;
|
||||
|
||||
auto document = BML::unserialize(information.manifest);
|
||||
information.title = document["game/label"].text();
|
||||
|
||||
if(auto memory = document["game/board/memory(type=ROM,content=Program)"]) {
|
||||
rom.allocate(memory["size"].natural());
|
||||
if(auto fp = platform->open(pathID(), "program.rom", File::Read, File::Required)) {
|
||||
rom.load(fp);
|
||||
} else return false;
|
||||
}
|
||||
|
||||
if(auto memory = document["game/board/memory(type=RAM,content=Save)"]) {
|
||||
ram.allocate(memory["size"].natural());
|
||||
if(auto fp = platform->open(pathID(), "save.ram", File::Read)) {
|
||||
ram.load(fp);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Cartridge::save() -> void {
|
||||
auto document = BML::unserialize(information.manifest);
|
||||
|
||||
if(auto memory = document["game/board/memory(type=RAM,content=Save)"]) {
|
||||
if(auto fp = platform->open(pathID(), "save.ram", File::Write)) {
|
||||
ram.save(fp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Cartridge::unload() -> void {
|
||||
rom.reset();
|
||||
ram.reset();
|
||||
}
|
||||
|
||||
auto Cartridge::power() -> void {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
struct Cartridge {
|
||||
auto pathID() const -> uint { return information.pathID; }
|
||||
auto region() const -> string { return information.region; }
|
||||
auto hash() const -> string { return information.sha256; }
|
||||
auto manifest() const -> string { return information.manifest; }
|
||||
auto title() const -> string { return information.title; }
|
||||
|
||||
//cartridge.cpp
|
||||
auto load() -> bool;
|
||||
auto save() -> void;
|
||||
auto unload() -> void;
|
||||
|
||||
auto power() -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
private:
|
||||
struct Information {
|
||||
uint pathID = 0;
|
||||
string region;
|
||||
string sha256;
|
||||
string manifest;
|
||||
string title;
|
||||
} information;
|
||||
|
||||
Emulator::Memory::Readable<uint8> rom;
|
||||
Emulator::Memory::Writable<uint8> ram;
|
||||
};
|
||||
|
||||
extern Cartridge cartridge;
|
||||
extern Cartridge expansion;
|
|
@ -0,0 +1,3 @@
|
|||
auto Cartridge::serialize(serializer& s) -> void {
|
||||
if(ram) s.array(ram.data(), ram.size());
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
#include <msx/msx.hpp>
|
||||
|
||||
namespace MSX {
|
||||
|
||||
CPU cpu;
|
||||
#include "memory.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto CPU::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), cpu.main();
|
||||
}
|
||||
|
||||
auto CPU::main() -> void {
|
||||
instruction();
|
||||
}
|
||||
|
||||
auto CPU::step(uint clocks) -> void {
|
||||
Thread::step(clocks);
|
||||
}
|
||||
|
||||
auto CPU::synchronizing() const -> bool {
|
||||
return scheduler.synchronizing();
|
||||
}
|
||||
|
||||
auto CPU::power() -> void {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
struct CPU : Processor::Z80, Processor::Z80::Bus, Thread {
|
||||
//cpu.cpp
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void override;
|
||||
auto synchronizing() const -> bool override;
|
||||
|
||||
auto power() -> void;
|
||||
|
||||
//memory.cpp
|
||||
auto read(uint16 address) -> uint8 override;
|
||||
auto write(uint16 address, uint8 data) -> void override;
|
||||
|
||||
auto in(uint8 address) -> uint8 override;
|
||||
auto out(uint8 address, uint8 data) -> void override;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
};
|
||||
|
||||
extern CPU cpu;
|
|
@ -0,0 +1,13 @@
|
|||
auto CPU::read(uint16 address) -> uint8 {
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto CPU::write(uint16 address, uint8 data) -> void {
|
||||
}
|
||||
|
||||
auto CPU::in(uint8 address) -> uint8 {
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto CPU::out(uint8 address, uint8 data) -> void {
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
auto CPU::serialize(serializer& s) -> void {
|
||||
Z80::serialize(s);
|
||||
Z80::Bus::serialize(s);
|
||||
Thread::serialize(s);
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
//license: GPLv3
|
||||
//started: ...
|
||||
//started: 2018-12-28
|
||||
|
||||
#include <emulator/emulator.hpp>
|
||||
#include <emulator/thread.hpp>
|
||||
|
@ -42,6 +42,11 @@ namespace MSX {
|
|||
};
|
||||
|
||||
#include <msx/system/system.hpp>
|
||||
#include <msx/cartridge/cartridge.hpp>
|
||||
|
||||
#include <msx/cpu/cpu.hpp>
|
||||
#include <msx/vdp/vdp.hpp>
|
||||
#include <msx/psg/psg.hpp>
|
||||
}
|
||||
|
||||
#include <msx/interface/interface.hpp>
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
#include <msx/msx.hpp>
|
||||
|
||||
namespace MSX {
|
||||
|
||||
PSG psg;
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto PSG::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), psg.main();
|
||||
}
|
||||
|
||||
auto PSG::main() -> void {
|
||||
stream->sample(0.0);
|
||||
step(1);
|
||||
}
|
||||
|
||||
auto PSG::step(uint clocks) -> void {
|
||||
Thread::step(clocks);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto PSG::power() -> void {
|
||||
create(PSG::Enter, system.colorburst() / 2.0);
|
||||
stream = Emulator::audio.createStream(1, frequency());
|
||||
stream->addHighPassFilter(20.0, Emulator::Filter::Order::First);
|
||||
stream->addDCRemovalFilter();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
//General Instrument AY-3-8910
|
||||
|
||||
struct PSG : Thread {
|
||||
shared_pointer<Emulator::Stream> stream;
|
||||
|
||||
//psg.cpp
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
|
||||
auto power() -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
};
|
||||
|
||||
extern PSG psg;
|
|
@ -0,0 +1,3 @@
|
|||
auto PSG::serialize(serializer& s) -> void {
|
||||
Thread::serialize(s);
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
auto System::serializeInit() -> void {
|
||||
serializer s;
|
||||
|
||||
uint signature = 0;
|
||||
char version[16] = {};
|
||||
char description[512] = {};
|
||||
|
||||
s.integer(signature);
|
||||
s.array(version);
|
||||
s.array(description);
|
||||
|
||||
serializeAll(s);
|
||||
information.serializeSize = s.size();
|
||||
}
|
||||
|
||||
auto System::serialize() -> serializer {
|
||||
serializer s{information.serializeSize};
|
||||
|
||||
uint signature = 0x31545342;
|
||||
char version[16] = {};
|
||||
char description[512] = {};
|
||||
memory::copy(&version, (const char*)Emulator::SerializerVersion, Emulator::SerializerVersion.size());
|
||||
|
||||
s.integer(signature);
|
||||
s.array(version);
|
||||
s.array(description);
|
||||
|
||||
serializeAll(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
auto System::unserialize(serializer& s) -> bool {
|
||||
uint signature = 0;
|
||||
char version[16] = {};
|
||||
char description[512] = {};
|
||||
|
||||
s.integer(signature);
|
||||
s.array(version);
|
||||
s.array(description);
|
||||
|
||||
if(signature != 0x31545342) return false;
|
||||
if(string{version} != Emulator::SerializerVersion) return false;
|
||||
|
||||
power();
|
||||
serializeAll(s);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto System::serializeAll(serializer& s) -> void {
|
||||
system.serialize(s);
|
||||
cartridge.serialize(s);
|
||||
cpu.serialize(s);
|
||||
vdp.serialize(s);
|
||||
psg.serialize(s);
|
||||
}
|
||||
|
||||
auto System::serialize(serializer& s) -> void {
|
||||
}
|
|
@ -5,5 +5,65 @@ namespace MSX {
|
|||
System system;
|
||||
Scheduler scheduler;
|
||||
Cheat cheat;
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto System::run() -> void {
|
||||
if(scheduler.enter() == Scheduler::Event::Frame) {
|
||||
vdp.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
auto System::runToSave() -> void {
|
||||
scheduler.synchronize(cpu);
|
||||
scheduler.synchronize(vdp);
|
||||
scheduler.synchronize(psg);
|
||||
}
|
||||
|
||||
auto System::load(Emulator::Interface* interface, Model model) -> bool {
|
||||
information = {};
|
||||
information.model = model;
|
||||
|
||||
if(auto fp = platform->open(ID::System, "manifest.bml", File::Read, File::Required)) {
|
||||
information.manifest = fp->reads();
|
||||
} else return false;
|
||||
|
||||
auto document = BML::unserialize(information.manifest);
|
||||
|
||||
if(!cartridge.load()) return false;
|
||||
|
||||
if(cartridge.region() == "NTSC") {
|
||||
information.region = Region::NTSC;
|
||||
information.colorburst = Emulator::Constants::Colorburst::NTSC;
|
||||
}
|
||||
if(cartridge.region() == "PAL") {
|
||||
information.region = Region::PAL;
|
||||
information.colorburst = Emulator::Constants::Colorburst::PAL;
|
||||
}
|
||||
|
||||
this->interface = interface;
|
||||
return information.loaded = true;
|
||||
}
|
||||
|
||||
auto System::save() -> void {
|
||||
cartridge.save();
|
||||
}
|
||||
|
||||
auto System::unload() -> void {
|
||||
cartridge.unload();
|
||||
}
|
||||
|
||||
auto System::power() -> void {
|
||||
Emulator::video.reset(interface);
|
||||
Emulator::video.setPalette();
|
||||
|
||||
Emulator::audio.reset(interface);
|
||||
|
||||
scheduler.reset();
|
||||
cartridge.power();
|
||||
cpu.power();
|
||||
vdp.power();
|
||||
psg.power();
|
||||
scheduler.primary(cpu);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,12 +5,35 @@ struct System {
|
|||
auto loaded() const -> bool { return information.loaded; }
|
||||
auto model() const -> Model { return information.model; }
|
||||
auto region() const -> Region { return information.region; }
|
||||
auto colorburst() const -> double { return information.colorburst; }
|
||||
|
||||
//system.cpp
|
||||
auto run() -> void;
|
||||
auto runToSave() -> void;
|
||||
|
||||
auto load(Emulator::Interface*, Model) -> bool;
|
||||
auto save() -> void;
|
||||
auto unload() -> void;
|
||||
|
||||
auto power() -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serializeInit() -> void;
|
||||
auto serialize() -> serializer;
|
||||
auto unserialize(serializer&) -> bool;
|
||||
auto serializeAll(serializer&) -> void;
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
private:
|
||||
Emulator::Interface* interface = nullptr;
|
||||
|
||||
struct Information {
|
||||
string manifest;
|
||||
bool loaded = false;
|
||||
Model model = Model::MSX;
|
||||
Region region = Region::NTSC;
|
||||
double colorburst = Emulator::Constants::Colorburst::NTSC;
|
||||
string manifest;
|
||||
uint serializeSize = 0;
|
||||
} information;
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
auto VDP::serialize(serializer& s) -> void {
|
||||
Thread::serialize(s);
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
#include <msx/msx.hpp>
|
||||
|
||||
//228 clocks/scanline
|
||||
|
||||
namespace MSX {
|
||||
|
||||
VDP vdp;
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto VDP::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), vdp.main();
|
||||
}
|
||||
|
||||
auto VDP::main() -> void {
|
||||
step(1);
|
||||
}
|
||||
|
||||
auto VDP::step(uint clocks) -> void {
|
||||
Thread::step(clocks);
|
||||
}
|
||||
|
||||
auto VDP::refresh() -> void {
|
||||
Emulator::video.refresh(buffer, 256 * sizeof(uint32), 256, 192);
|
||||
}
|
||||
|
||||
auto VDP::power() -> void {
|
||||
create(VDP::Enter, system.colorburst());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
//Texas Instruments TMS9918A
|
||||
|
||||
struct VDP : Thread {
|
||||
//vdp.cpp
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
auto refresh() -> void;
|
||||
|
||||
auto power() -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
private:
|
||||
uint32 buffer[256 * 192];
|
||||
};
|
||||
|
||||
extern VDP vdp;
|
|
@ -0,0 +1,387 @@
|
|||
using format = string_format;
|
||||
|
||||
//todo: this is horribly broken in many cases; needs a total rewrite
|
||||
auto V30MZ::disassemble(uint16 cs, uint16 ip, bool registers, bool bytes) -> string {
|
||||
string s;
|
||||
uint20 ea = cs * 16 + ip;
|
||||
vector<uint8> bytesRead;
|
||||
|
||||
auto fetch = [&](bool inc = true) -> uint8 {
|
||||
uint8 data = read(cs * 16 + ip);
|
||||
if(inc) ip++, bytesRead.append(data);
|
||||
return data;
|
||||
};
|
||||
auto readByte = [&]() -> string {
|
||||
uint8 byte = fetch();
|
||||
return {"$", hex(byte, 2L)};
|
||||
};
|
||||
auto readWord = [&]() -> string {
|
||||
uint16 word = fetch(); word |= fetch() << 8;
|
||||
return {"$", hex(word, 4L)};
|
||||
};
|
||||
auto readByteSigned = [&]() -> string {
|
||||
uint8 byte = fetch();
|
||||
return {"$", byte & 0x80 ? "-" : "+", hex(byte & 0x80 ? uint8(256 - byte) : byte, 2L)};
|
||||
};
|
||||
auto readRelativeByte = [&]() -> string {
|
||||
uint8 byte = fetch();
|
||||
return {"$", hex(ip + (int8)byte, 4L)};
|
||||
};
|
||||
auto readRelativeWord = [&]() -> string {
|
||||
uint16 word = fetch(); word |= fetch() << 8;
|
||||
return {"$", hex(ip + (int16)word, 4L)};
|
||||
};
|
||||
auto readIndirectByte = [&]() -> string {
|
||||
return {"[", readWord(), "]"};
|
||||
};
|
||||
auto readIndirectWord = [&]() -> string {
|
||||
return {"{", readWord(), "}"};
|
||||
};
|
||||
auto readRegByte = [&](bool inc = true) -> string {
|
||||
uint8 modRM = fetch(inc);
|
||||
static const string reg[] = {"al", "cl", "dl", "bl", "ah", "ch", "dh", "bh"};
|
||||
return reg[(uint3)(modRM >> 3)];
|
||||
};
|
||||
auto readRegWord = [&](bool inc = true) -> string {
|
||||
uint8 modRM = fetch(inc);
|
||||
static const string reg[] = {"ax", "cx", "dx", "bx", "sp", "bp", "si", "di"};
|
||||
return reg[(uint3)(modRM >> 3)];
|
||||
};
|
||||
auto readSeg = [&](bool inc = true) -> string {
|
||||
uint8 modRM = fetch(inc);
|
||||
static const string seg[] = {"es", "cs", "ss", "ds"};
|
||||
return seg[(uint2)(modRM >> 3)];
|
||||
};
|
||||
auto readMemByte = [&](bool inc = true) -> string {
|
||||
uint8 modRM = fetch(inc);
|
||||
static const string reg[] = {"al", "cl", "dl", "bl", "ah", "ch", "dh", "bh"};
|
||||
if(modRM >= 0xc0) return reg[(uint3)modRM];
|
||||
if((modRM & 0xc7) == 0x06) return {"[", readWord(), "]"};
|
||||
static const string mem[] = {"bx+si", "bx+di", "bp+si", "bp+di", "si", "di", "bp", "bx"};
|
||||
if((modRM & 0xc0) == 0x40) return {"[", mem[(uint3)modRM], "+", readByte(), "]"};
|
||||
if((modRM & 0xc0) == 0x80) return {"[", mem[(uint3)modRM], "+", readWord(), "]"};
|
||||
return {"[", mem[(uint3)modRM], "]"};
|
||||
};
|
||||
auto readMemWord = [&](bool inc = true) -> string {
|
||||
uint8 modRM = fetch(inc);
|
||||
static const string reg[] = {"ax", "cx", "dx", "bx", "sp", "bp", "si", "di"};
|
||||
if(modRM >= 0xc0) return reg[(uint3)modRM];
|
||||
if((modRM & 0xc7) == 0x06) return {"{", readWord(), "}"};
|
||||
static const string mem[] = {"bx+si", "bx+di", "bp+si", "bp+di", "si", "di", "bp", "bx"};
|
||||
if((modRM & 0xc0) == 0x40) return {"{", mem[(uint3)modRM], "+", readByte(), "}"};
|
||||
if((modRM & 0xc0) == 0x80) return {"{", mem[(uint3)modRM], "+", readWord(), "}"};
|
||||
return {"{", mem[(uint3)modRM], "}"};
|
||||
};
|
||||
auto readGroup = [&](uint group) -> string {
|
||||
uint8 modRM = fetch(false);
|
||||
static const string opcode[4][8] = {
|
||||
{"add ", "or ", "adc ", "sbb ", "and ", "sub ", "xor ", "cmp "},
|
||||
{"rol ", "ror ", "rcl ", "rcr ", "shl ", "shr ", "sal ", "sar "},
|
||||
{"test", "test", "not ", "neg ", "mul ", "imul", "div ", "idiv"},
|
||||
{"inc ", "dec ", "call", "calf", "jmp ", "jmpf", "push", "??? "},
|
||||
};
|
||||
return opcode[group - 1][(uint3)(modRM >> 3)];
|
||||
};
|
||||
|
||||
auto opcode = fetch();
|
||||
switch(opcode) {
|
||||
case 0x00: s = {"add {0},{1}", format{readMemByte(0), readRegByte()}}; break;
|
||||
case 0x01: s = {"add {0},{1}", format{readMemWord(0), readRegWord()}}; break;
|
||||
case 0x02: s = {"add {0},{1}", format{readRegByte(0), readMemByte()}}; break;
|
||||
case 0x03: s = {"add {0},{1}", format{readRegWord(0), readMemWord()}}; break;
|
||||
case 0x04: s = {"add al,{0}", format{readByte()}}; break;
|
||||
case 0x05: s = {"add ax,{0}", format{readWord()}}; break;
|
||||
case 0x06: s = {"push es"}; break;
|
||||
case 0x07: s = {"pop es"}; break;
|
||||
case 0x08: s = {"or {0},{1}", format{readMemByte(0), readRegByte()}}; break;
|
||||
case 0x09: s = {"or {0},{1}", format{readMemWord(0), readRegWord()}}; break;
|
||||
case 0x0a: s = {"or {0},{1}", format{readRegByte(0), readMemByte()}}; break;
|
||||
case 0x0b: s = {"or {0},{1}", format{readRegWord(0), readMemWord()}}; break;
|
||||
case 0x0c: s = {"or al,{0}", format{readByte()}}; break;
|
||||
case 0x0d: s = {"or ax,{0}", format{readWord()}}; break;
|
||||
case 0x0e: s = {"push cs"}; break;
|
||||
//case 0x0f:
|
||||
case 0x10: s = {"adc {0},{1}", format{readMemByte(0), readRegByte()}}; break;
|
||||
case 0x11: s = {"adc {0},{1}", format{readMemWord(0), readRegWord()}}; break;
|
||||
case 0x12: s = {"adc {0},{1}", format{readRegByte(0), readMemByte()}}; break;
|
||||
case 0x13: s = {"adc {0},{1}", format{readRegWord(0), readMemWord()}}; break;
|
||||
case 0x14: s = {"adc al,{0}", format{readByte()}}; break;
|
||||
case 0x15: s = {"adc ax,{0}", format{readWord()}}; break;
|
||||
case 0x16: s = {"push ss"}; break;
|
||||
case 0x17: s = {"pop ss"}; break;
|
||||
case 0x18: s = {"sbb {0},{1}", format{readMemByte(0), readRegByte()}}; break;
|
||||
case 0x19: s = {"sbb {0},{1}", format{readMemWord(0), readRegWord()}}; break;
|
||||
case 0x1a: s = {"sbb {0},{1}", format{readRegByte(0), readMemByte()}}; break;
|
||||
case 0x1b: s = {"sbb {0},{1}", format{readRegWord(0), readMemWord()}}; break;
|
||||
case 0x1c: s = {"sbb al,{0}", format{readByte()}}; break;
|
||||
case 0x1d: s = {"sbb ax,{0}", format{readWord()}}; break;
|
||||
case 0x1e: s = {"push ds"}; break;
|
||||
case 0x1f: s = {"pop ds"}; break;
|
||||
case 0x20: s = {"and {0},{1}", format{readMemByte(0), readRegByte()}}; break;
|
||||
case 0x21: s = {"and {0},{1}", format{readMemWord(0), readRegWord()}}; break;
|
||||
case 0x22: s = {"and {0},{1}", format{readRegByte(0), readMemByte()}}; break;
|
||||
case 0x23: s = {"and {0},{1}", format{readRegWord(0), readMemWord()}}; break;
|
||||
case 0x24: s = {"and al,{0}", format{readByte()}}; break;
|
||||
case 0x25: s = {"and ax,{0}", format{readWord()}}; break;
|
||||
case 0x26: s = {"es: "}; break;
|
||||
case 0x27: s = {"daa "}; break;
|
||||
case 0x28: s = {"sub {0},{1}", format{readMemByte(0), readRegByte()}}; break;
|
||||
case 0x29: s = {"sub {0},{1}", format{readMemWord(0), readRegWord()}}; break;
|
||||
case 0x2a: s = {"sub {0},{1}", format{readRegByte(0), readMemByte()}}; break;
|
||||
case 0x2b: s = {"sub {0},{1}", format{readRegWord(0), readMemWord()}}; break;
|
||||
case 0x2c: s = {"sub al,{0}", format{readByte()}}; break;
|
||||
case 0x2d: s = {"sub ax,{0}", format{readWord()}}; break;
|
||||
case 0x2e: s = {"cs: "}; break;
|
||||
case 0x2f: s = {"das "}; break;
|
||||
case 0x30: s = {"xor {0},{1}", format{readMemByte(0), readRegByte()}}; break;
|
||||
case 0x31: s = {"xor {0},{1}", format{readMemWord(0), readRegWord()}}; break;
|
||||
case 0x32: s = {"xor {0},{1}", format{readRegByte(0), readMemByte()}}; break;
|
||||
case 0x33: s = {"xor {0},{1}", format{readRegWord(0), readMemWord()}}; break;
|
||||
case 0x34: s = {"xor al,{0}", format{readByte()}}; break;
|
||||
case 0x35: s = {"xor ax,{0}", format{readWord()}}; break;
|
||||
case 0x36: s = {"ss: "}; break;
|
||||
case 0x37: s = {"aaa "}; break;
|
||||
case 0x38: s = {"cmp {0},{1}", format{readMemByte(0), readRegByte()}}; break;
|
||||
case 0x39: s = {"cmp {0},{1}", format{readMemWord(0), readRegWord()}}; break;
|
||||
case 0x3a: s = {"cmp {0},{1}", format{readRegByte(0), readMemByte()}}; break;
|
||||
case 0x3b: s = {"cmp {0},{1}", format{readRegWord(0), readMemWord()}}; break;
|
||||
case 0x3c: s = {"cmp al,{0}", format{readByte()}}; break;
|
||||
case 0x3d: s = {"cmp ax,{0}", format{readWord()}}; break;
|
||||
case 0x3e: s = {"ds: "}; break;
|
||||
case 0x3f: s = {"aas "}; break;
|
||||
case 0x40: s = {"inc ax"}; break;
|
||||
case 0x41: s = {"inc cx"}; break;
|
||||
case 0x42: s = {"inc dx"}; break;
|
||||
case 0x43: s = {"inc bx"}; break;
|
||||
case 0x44: s = {"inc sp"}; break;
|
||||
case 0x45: s = {"inc bp"}; break;
|
||||
case 0x46: s = {"inc si"}; break;
|
||||
case 0x47: s = {"inc di"}; break;
|
||||
case 0x48: s = {"dec ax"}; break;
|
||||
case 0x49: s = {"dec cx"}; break;
|
||||
case 0x4a: s = {"dec dx"}; break;
|
||||
case 0x4b: s = {"dec bx"}; break;
|
||||
case 0x4c: s = {"dec sp"}; break;
|
||||
case 0x4d: s = {"dec bp"}; break;
|
||||
case 0x4e: s = {"dec si"}; break;
|
||||
case 0x4f: s = {"dec di"}; break;
|
||||
case 0x50: s = {"push ax"}; break;
|
||||
case 0x51: s = {"push cx"}; break;
|
||||
case 0x52: s = {"push dx"}; break;
|
||||
case 0x53: s = {"push bx"}; break;
|
||||
case 0x54: s = {"push sp"}; break;
|
||||
case 0x55: s = {"push bp"}; break;
|
||||
case 0x56: s = {"push si"}; break;
|
||||
case 0x57: s = {"push di"}; break;
|
||||
case 0x58: s = {"pop ax"}; break;
|
||||
case 0x59: s = {"pop cx"}; break;
|
||||
case 0x5a: s = {"pop dx"}; break;
|
||||
case 0x5b: s = {"pop bx"}; break;
|
||||
case 0x5c: s = {"pop sp"}; break;
|
||||
case 0x5d: s = {"pop bp"}; break;
|
||||
case 0x5e: s = {"pop si"}; break;
|
||||
case 0x5f: s = {"pop di"}; break;
|
||||
case 0x60: s = {"pusha "}; break;
|
||||
case 0x61: s = {"popa "}; break;
|
||||
case 0x62: s = {"bound {0},{1}", format{readRegWord(0), readMemWord()}}; break;
|
||||
//case 0x63:
|
||||
//case 0x64:
|
||||
//case 0x65:
|
||||
//case 0x66:
|
||||
//case 0x67:
|
||||
case 0x68: s = {"push {0}", format{readWord()}}; break;
|
||||
case 0x69: s = {"imul {0},{1},{2}", format{readRegWord(0), readMemWord(), readWord()}}; break;
|
||||
case 0x6a: s = {"push {0}", format{readByteSigned()}}; break;
|
||||
case 0x6b: s = {"imul {0},{1},{2}", format{readRegWord(0), readMemWord(), readByteSigned()}}; break;
|
||||
case 0x6c: s = {"insb "}; break;
|
||||
case 0x6d: s = {"insw "}; break;
|
||||
case 0x6e: s = {"outsb "}; break;
|
||||
case 0x6f: s = {"outsw "}; break;
|
||||
case 0x70: s = {"jo {0}", format{readRelativeByte()}}; break;
|
||||
case 0x71: s = {"jno {0}", format{readRelativeByte()}}; break;
|
||||
case 0x72: s = {"jc {0}", format{readRelativeByte()}}; break;
|
||||
case 0x73: s = {"jnc {0}", format{readRelativeByte()}}; break;
|
||||
case 0x74: s = {"jz {0}", format{readRelativeByte()}}; break;
|
||||
case 0x75: s = {"jnz {0}", format{readRelativeByte()}}; break;
|
||||
case 0x76: s = {"jcz {0}", format{readRelativeByte()}}; break;
|
||||
case 0x77: s = {"jncz {0}", format{readRelativeByte()}}; break;
|
||||
case 0x78: s = {"js {0}", format{readRelativeByte()}}; break;
|
||||
case 0x79: s = {"jns {0}", format{readRelativeByte()}}; break;
|
||||
case 0x7a: s = {"jp {0}", format{readRelativeByte()}}; break;
|
||||
case 0x7b: s = {"jnp {0}", format{readRelativeByte()}}; break;
|
||||
case 0x7c: s = {"jl {0}", format{readRelativeByte()}}; break;
|
||||
case 0x7d: s = {"jnl {0}", format{readRelativeByte()}}; break;
|
||||
case 0x7e: s = {"jle {0}", format{readRelativeByte()}}; break;
|
||||
case 0x7f: s = {"jnle {0}", format{readRelativeByte()}}; break;
|
||||
case 0x80: s = {"{0} {1},{2}", format{readGroup(1), readMemByte(), readByte()}}; break;
|
||||
case 0x81: s = {"{0} {1},{2}", format{readGroup(1), readMemWord(), readWord()}}; break;
|
||||
case 0x82: s = {"{0} {1},{2}", format{readGroup(1), readMemByte(), readByteSigned()}}; break;
|
||||
case 0x83: s = {"{0} {1},{2}", format{readGroup(1), readMemWord(), readByteSigned()}}; break;
|
||||
case 0x84: s = {"test {0},{1}", format{readMemByte(0), readRegByte()}}; break;
|
||||
case 0x85: s = {"test {0},{1}", format{readMemWord(0), readRegWord()}}; break;
|
||||
case 0x86: s = {"xchg {0},{1}", format{readMemByte(0), readRegByte()}}; break;
|
||||
case 0x87: s = {"xchg {0},{1}", format{readMemWord(0), readRegWord()}}; break;
|
||||
case 0x88: s = {"mov {0},{1}", format{readMemByte(0), readRegByte()}}; break;
|
||||
case 0x89: s = {"mov {0},{1}", format{readMemWord(0), readRegWord()}}; break;
|
||||
case 0x8a: s = {"mov {0},{1}", format{readRegByte(0), readMemByte()}}; break;
|
||||
case 0x8b: s = {"mov {0},{1}", format{readRegWord(0), readMemWord()}}; break;
|
||||
case 0x8c: s = {"mov {0},{1}", format{readMemWord(0), readSeg()}}; break;
|
||||
case 0x8d: s = {"lea {0},{1}", format{readRegWord(0), readMemWord()}}; break;
|
||||
case 0x8e: s = {"mov {0},{1}", format{readSeg(0), readMemWord()}}; break;
|
||||
case 0x8f: s = {"pop {0}", format{readMemWord()}}; break;
|
||||
case 0x90: s = {"nop "}; break;
|
||||
case 0x91: s = {"xchg ax,cx"}; break;
|
||||
case 0x92: s = {"xchg ax,dx"}; break;
|
||||
case 0x93: s = {"xchg ax,bx"}; break;
|
||||
case 0x94: s = {"xchg ax,sp"}; break;
|
||||
case 0x95: s = {"xchg ax,bp"}; break;
|
||||
case 0x96: s = {"xchg ax,si"}; break;
|
||||
case 0x97: s = {"xchg ax,di"}; break;
|
||||
case 0x98: s = {"cbw "}; break;
|
||||
case 0x99: s = {"cwd "}; break;
|
||||
case 0x9a: s = {"call {1}:{0}", format{readWord(), readWord()}}; break;
|
||||
case 0x9b: s = {"wait "}; break;
|
||||
case 0x9c: s = {"pushf "}; break;
|
||||
case 0x9d: s = {"popf "}; break;
|
||||
case 0x9e: s = {"sahf "}; break;
|
||||
case 0x9f: s = {"lahf "}; break;
|
||||
case 0xa0: s = {"mov al,{0}", format{readIndirectByte()}}; break;
|
||||
case 0xa1: s = {"mov ax,{0}", format{readIndirectWord()}}; break;
|
||||
case 0xa2: s = {"mov {0},al", format{readIndirectByte()}}; break;
|
||||
case 0xa3: s = {"mov {0},ax", format{readIndirectWord()}}; break;
|
||||
case 0xa4: s = {"movsb "}; break;
|
||||
case 0xa5: s = {"movsw "}; break;
|
||||
case 0xa6: s = {"cmpsb "}; break;
|
||||
case 0xa7: s = {"cmpsw "}; break;
|
||||
case 0xa8: s = {"test al,{0}", format{readByte()}}; break;
|
||||
case 0xa9: s = {"test ax,{0}", format{readWord()}}; break;
|
||||
case 0xaa: s = {"stosb "}; break;
|
||||
case 0xab: s = {"stosw "}; break;
|
||||
case 0xac: s = {"lodsb "}; break;
|
||||
case 0xad: s = {"lodsw "}; break;
|
||||
case 0xae: s = {"scasb "}; break;
|
||||
case 0xaf: s = {"scasw "}; break;
|
||||
case 0xb0: s = {"mov al,{0}", format{readByte()}}; break;
|
||||
case 0xb1: s = {"mov cl,{0}", format{readByte()}}; break;
|
||||
case 0xb2: s = {"mov dl,{0}", format{readByte()}}; break;
|
||||
case 0xb3: s = {"mov bl,{0}", format{readByte()}}; break;
|
||||
case 0xb4: s = {"mov ah,{0}", format{readByte()}}; break;
|
||||
case 0xb5: s = {"mov ch,{0}", format{readByte()}}; break;
|
||||
case 0xb6: s = {"mov dh,{0}", format{readByte()}}; break;
|
||||
case 0xb7: s = {"mov bh,{0}", format{readByte()}}; break;
|
||||
case 0xb8: s = {"mov ax,{0}", format{readWord()}}; break;
|
||||
case 0xb9: s = {"mov cx,{0}", format{readWord()}}; break;
|
||||
case 0xba: s = {"mov dx,{0}", format{readWord()}}; break;
|
||||
case 0xbb: s = {"mov bx,{0}", format{readWord()}}; break;
|
||||
case 0xbc: s = {"mov sp,{0}", format{readWord()}}; break;
|
||||
case 0xbd: s = {"mov bp,{0}", format{readWord()}}; break;
|
||||
case 0xbe: s = {"mov si,{0}", format{readWord()}}; break;
|
||||
case 0xbf: s = {"mov di,{0}", format{readWord()}}; break;
|
||||
case 0xc0: s = {"{0} {1},{2}", format{readGroup(2), readMemByte(), readByte()}}; break;
|
||||
case 0xc1: s = {"{0} {1},{2}", format{readGroup(2), readMemWord(), readByte()}}; break;
|
||||
case 0xc2: s = {"ret {0}", format{readWord()}}; break;
|
||||
case 0xc3: s = {"ret "}; break;
|
||||
case 0xc4: s = {"les {0}", format{readMemWord()}}; break;
|
||||
case 0xc5: s = {"lds {0}", format{readMemWord()}}; break;
|
||||
case 0xc6: s = {"mov {0},{1}", format{readMemByte(), readByte()}}; break;
|
||||
case 0xc7: s = {"mov {0},{1}", format{readMemWord(), readWord()}}; break;
|
||||
case 0xc8: s = {"enter {0},{1}", format{readWord(), readByte()}}; break;
|
||||
case 0xc9: s = {"leave "}; break;
|
||||
case 0xca: s = {"retf {0}", format{readWord()}}; break;
|
||||
case 0xcb: s = {"retf "}; break;
|
||||
case 0xcc: s = {"int3 "}; break;
|
||||
case 0xcd: s = {"int ", format{readByte()}}; break;
|
||||
case 0xce: s = {"into "}; break;
|
||||
case 0xcf: s = {"iret "}; break;
|
||||
case 0xd0: s = {"{0} {1},1", format{readGroup(2), readMemByte()}}; break;
|
||||
case 0xd1: s = {"{0} {1},1", format{readGroup(2), readMemWord()}}; break;
|
||||
case 0xd2: s = {"{0} {1},cl", format{readGroup(2), readMemByte()}}; break;
|
||||
case 0xd3: s = {"{0} {1},cl", format{readGroup(2), readMemWord()}}; break;
|
||||
case 0xd4: s = {"aam {0}", format{readByte()}}; break;
|
||||
case 0xd5: s = {"aad {0}", format{readByte()}}; break;
|
||||
//case 0xd6:
|
||||
case 0xd7: s = {"xlat "}; break;
|
||||
//case 0xd8:
|
||||
//case 0xd9:
|
||||
//case 0xda:
|
||||
//case 0xdb:
|
||||
//case 0xdc:
|
||||
//case 0xdd:
|
||||
//case 0xde:
|
||||
//case 0xdf:
|
||||
case 0xe0: s = {"loopnz "}; break;
|
||||
case 0xe1: s = {"loopz "}; break;
|
||||
case 0xe2: s = {"loop "}; break;
|
||||
case 0xe3: s = {"jcxz {0}", format{readRelativeByte()}}; break;
|
||||
case 0xe4: s = {"in al,{0}", format{readByte()}}; break;
|
||||
case 0xe5: s = {"in ax,{0}", format{readByte()}}; break;
|
||||
case 0xe6: s = {"out {0},al", format{readByte()}}; break;
|
||||
case 0xe7: s = {"out {0},ax", format{readByte()}}; break;
|
||||
case 0xe8: s = {"call {0}", format{readRelativeWord()}}; break;
|
||||
case 0xe9: s = {"jmp {0}", format{readRelativeWord()}}; break;
|
||||
case 0xea: s = {"jmp {1}:{0}", format{readWord(), readWord()}}; break;
|
||||
case 0xeb: s = {"jmp {0}", format{readRelativeByte()}}; break;
|
||||
case 0xec: s = {"in al,dx"}; break;
|
||||
case 0xed: s = {"in ax,dx"}; break;
|
||||
case 0xee: s = {"out dx,al"}; break;
|
||||
case 0xef: s = {"out dx,ax"}; break;
|
||||
case 0xf0: s = {"lock: "}; break;
|
||||
//case 0xf1:
|
||||
case 0xf2: s = {"repnz: "}; break;
|
||||
case 0xf3: s = {"repz: "}; break;
|
||||
case 0xf4: s = {"hlt "}; break;
|
||||
case 0xf5: s = {"cmc "}; break;
|
||||
case 0xf6: s = {"{0} {1},{2}", format{readGroup(3), readMemByte(), readByte()}}; break;
|
||||
case 0xf7: s = {"{0} {1},{2}", format{readGroup(3), readMemWord(), readWord()}}; break;
|
||||
case 0xf8: s = {"clc "}; break;
|
||||
case 0xf9: s = {"stc "}; break;
|
||||
case 0xfa: s = {"cli "}; break;
|
||||
case 0xfb: s = {"sti "}; break;
|
||||
case 0xfc: s = {"cld "}; break;
|
||||
case 0xfd: s = {"std "}; break;
|
||||
case 0xfe: s = {"{0} {1},{2}", format{readGroup(4), readMemByte(), readByte()}}; break;
|
||||
case 0xff: s = {"{0} {1},{2}", format{readGroup(4), readMemWord(), readWord()}}; break;
|
||||
default: s = {"??? {0}", format{hex(opcode, 2L)}}; break;
|
||||
}
|
||||
while(s.size() < 24) s.append(" ");
|
||||
|
||||
string l;
|
||||
if(registers) {
|
||||
l = {
|
||||
" ax:", hex(r.ax, 4L),
|
||||
" bx:", hex(r.bx, 4L),
|
||||
" cx:", hex(r.cx, 4L),
|
||||
" dx:", hex(r.dx, 4L),
|
||||
" si:", hex(r.si, 4L),
|
||||
" di:", hex(r.di, 4L),
|
||||
" bp:", hex(r.bp, 4L),
|
||||
" sp:", hex(r.sp, 4L),
|
||||
" ip:", hex(r.ip, 4L),
|
||||
" cs:", hex(r.cs, 4L),
|
||||
" ds:", hex(r.ds, 4L),
|
||||
" es:", hex(r.es, 4L),
|
||||
" ss:", hex(r.ss, 4L), " ",
|
||||
r.f.m ? "M" : "m",
|
||||
r.f.v ? "V" : "v",
|
||||
r.f.d ? "D" : "d",
|
||||
r.f.i ? "I" : "i",
|
||||
r.f.b ? "B" : "b",
|
||||
r.f.s ? "S" : "s",
|
||||
r.f.z ? "Z" : "z",
|
||||
r.f.h ? "H" : "h",
|
||||
r.f.p ? "P" : "p",
|
||||
r.f.c ? "C" : "c"
|
||||
};
|
||||
}
|
||||
|
||||
string b;
|
||||
if(bytes) {
|
||||
b = " ";
|
||||
while(bytesRead) {
|
||||
b.append(hex(bytesRead.takeLeft(), 2L), " ");
|
||||
}
|
||||
b.stripRight();
|
||||
}
|
||||
|
||||
return {hex(ea, 5L), " ", s, l, b};
|
||||
}
|
|
@ -1,387 +1,455 @@
|
|||
using format = string_format;
|
||||
|
||||
//todo: this is horribly broken in many cases; needs a total rewrite
|
||||
auto V30MZ::disassemble(uint16 cs, uint16 ip, bool registers, bool bytes) -> string {
|
||||
string s;
|
||||
uint20 ea = cs * 16 + ip;
|
||||
vector<uint8> bytesRead;
|
||||
|
||||
auto fetch = [&](bool inc = true) -> uint8 {
|
||||
uint8 data = read(cs * 16 + ip);
|
||||
if(inc) ip++, bytesRead.append(data);
|
||||
return data;
|
||||
};
|
||||
auto readByte = [&]() -> string {
|
||||
uint8 byte = fetch();
|
||||
return {"$", hex(byte, 2L)};
|
||||
};
|
||||
auto readWord = [&]() -> string {
|
||||
uint16 word = fetch(); word |= fetch() << 8;
|
||||
return {"$", hex(word, 4L)};
|
||||
};
|
||||
auto readByteSigned = [&]() -> string {
|
||||
uint8 byte = fetch();
|
||||
return {"$", byte & 0x80 ? "-" : "+", hex(byte & 0x80 ? uint8(256 - byte) : byte, 2L)};
|
||||
};
|
||||
auto readRelativeByte = [&]() -> string {
|
||||
uint8 byte = fetch();
|
||||
return {"$", hex(ip + (int8)byte, 4L)};
|
||||
};
|
||||
auto readRelativeWord = [&]() -> string {
|
||||
uint16 word = fetch(); word |= fetch() << 8;
|
||||
return {"$", hex(ip + (int16)word, 4L)};
|
||||
};
|
||||
auto readIndirectByte = [&]() -> string {
|
||||
return {"[", readWord(), "]"};
|
||||
};
|
||||
auto readIndirectWord = [&]() -> string {
|
||||
return {"{", readWord(), "}"};
|
||||
};
|
||||
auto readRegByte = [&](bool inc = true) -> string {
|
||||
uint8 modRM = fetch(inc);
|
||||
static const string reg[] = {"al", "cl", "dl", "bl", "ah", "ch", "dh", "bh"};
|
||||
return reg[(uint3)(modRM >> 3)];
|
||||
};
|
||||
auto readRegWord = [&](bool inc = true) -> string {
|
||||
uint8 modRM = fetch(inc);
|
||||
static const string reg[] = {"ax", "cx", "dx", "bx", "sp", "bp", "si", "di"};
|
||||
return reg[(uint3)(modRM >> 3)];
|
||||
};
|
||||
auto readSeg = [&](bool inc = true) -> string {
|
||||
uint8 modRM = fetch(inc);
|
||||
static const string seg[] = {"es", "cs", "ss", "ds"};
|
||||
return seg[(uint2)(modRM >> 3)];
|
||||
};
|
||||
auto readMemByte = [&](bool inc = true) -> string {
|
||||
uint8 modRM = fetch(inc);
|
||||
static const string reg[] = {"al", "cl", "dl", "bl", "ah", "ch", "dh", "bh"};
|
||||
if(modRM >= 0xc0) return reg[(uint3)modRM];
|
||||
if((modRM & 0xc7) == 0x06) return {"[", readWord(), "]"};
|
||||
static const string mem[] = {"bx+si", "bx+di", "bp+si", "bp+di", "si", "di", "bp", "bx"};
|
||||
if((modRM & 0xc0) == 0x40) return {"[", mem[(uint3)modRM], "+", readByte(), "]"};
|
||||
if((modRM & 0xc0) == 0x80) return {"[", mem[(uint3)modRM], "+", readWord(), "]"};
|
||||
return {"[", mem[(uint3)modRM], "]"};
|
||||
};
|
||||
auto readMemWord = [&](bool inc = true) -> string {
|
||||
uint8 modRM = fetch(inc);
|
||||
static const string reg[] = {"ax", "cx", "dx", "bx", "sp", "bp", "si", "di"};
|
||||
if(modRM >= 0xc0) return reg[(uint3)modRM];
|
||||
if((modRM & 0xc7) == 0x06) return {"{", readWord(), "}"};
|
||||
static const string mem[] = {"bx+si", "bx+di", "bp+si", "bp+di", "si", "di", "bp", "bx"};
|
||||
if((modRM & 0xc0) == 0x40) return {"{", mem[(uint3)modRM], "+", readByte(), "}"};
|
||||
if((modRM & 0xc0) == 0x80) return {"{", mem[(uint3)modRM], "+", readWord(), "}"};
|
||||
return {"{", mem[(uint3)modRM], "}"};
|
||||
};
|
||||
auto readGroup = [&](uint group) -> string {
|
||||
uint8 modRM = fetch(false);
|
||||
static const string opcode[4][8] = {
|
||||
{"add ", "or ", "adc ", "sbb ", "and ", "sub ", "xor ", "cmp "},
|
||||
{"rol ", "ror ", "rcl ", "rcr ", "shl ", "shr ", "sal ", "sar "},
|
||||
{"test", "??? ", "not ", "neg ", "mul ", "imul", "div ", "idiv"},
|
||||
{"inc ", "dec ", "??? ", "??? ", "??? ", "??? ", "??? ", "??? "},
|
||||
};
|
||||
return opcode[group - 1][(uint3)(modRM >> 3)];
|
||||
};
|
||||
|
||||
auto opcode = fetch();
|
||||
switch(opcode) {
|
||||
case 0x00: s = {"add {0},{1}", format{readMemByte(0), readRegByte()}}; break;
|
||||
case 0x01: s = {"add {0},{1}", format{readMemWord(0), readRegWord()}}; break;
|
||||
case 0x02: s = {"add {0},{1}", format{readRegByte(0), readMemByte()}}; break;
|
||||
case 0x03: s = {"add {0},{1}", format{readRegWord(0), readMemWord()}}; break;
|
||||
case 0x04: s = {"add al,{0}", format{readByte()}}; break;
|
||||
case 0x05: s = {"add ax,{0}", format{readWord()}}; break;
|
||||
case 0x06: s = {"push es"}; break;
|
||||
case 0x07: s = {"pop es"}; break;
|
||||
case 0x08: s = {"or {0},{1}", format{readMemByte(0), readRegByte()}}; break;
|
||||
case 0x09: s = {"or {0},{1}", format{readMemWord(0), readRegWord()}}; break;
|
||||
case 0x0a: s = {"or {0},{1}", format{readRegByte(0), readMemByte()}}; break;
|
||||
case 0x0b: s = {"or {0},{1}", format{readRegWord(0), readMemWord()}}; break;
|
||||
case 0x0c: s = {"or al,{0}", format{readByte()}}; break;
|
||||
case 0x0d: s = {"or ax,{0}", format{readWord()}}; break;
|
||||
case 0x0e: s = {"push cs"}; break;
|
||||
//case 0x0f:
|
||||
case 0x10: s = {"adc {0},{1}", format{readMemByte(0), readRegByte()}}; break;
|
||||
case 0x11: s = {"adc {0},{1}", format{readMemWord(0), readRegWord()}}; break;
|
||||
case 0x12: s = {"adc {0},{1}", format{readRegByte(0), readMemByte()}}; break;
|
||||
case 0x13: s = {"adc {0},{1}", format{readRegWord(0), readMemWord()}}; break;
|
||||
case 0x14: s = {"adc al,{0}", format{readByte()}}; break;
|
||||
case 0x15: s = {"adc ax,{0}", format{readWord()}}; break;
|
||||
case 0x16: s = {"push ss"}; break;
|
||||
case 0x17: s = {"pop ss"}; break;
|
||||
case 0x18: s = {"sbb {0},{1}", format{readMemByte(0), readRegByte()}}; break;
|
||||
case 0x19: s = {"sbb {0},{1}", format{readMemWord(0), readRegWord()}}; break;
|
||||
case 0x1a: s = {"sbb {0},{1}", format{readRegByte(0), readMemByte()}}; break;
|
||||
case 0x1b: s = {"sbb {0},{1}", format{readRegWord(0), readMemWord()}}; break;
|
||||
case 0x1c: s = {"sbb al,{0}", format{readByte()}}; break;
|
||||
case 0x1d: s = {"sbb ax,{0}", format{readWord()}}; break;
|
||||
case 0x1e: s = {"push ds"}; break;
|
||||
case 0x1f: s = {"pop ds"}; break;
|
||||
case 0x20: s = {"and {0},{1}", format{readMemByte(0), readRegByte()}}; break;
|
||||
case 0x21: s = {"and {0},{1}", format{readMemWord(0), readRegWord()}}; break;
|
||||
case 0x22: s = {"and {0},{1}", format{readRegByte(0), readMemByte()}}; break;
|
||||
case 0x23: s = {"and {0},{1}", format{readRegWord(0), readMemWord()}}; break;
|
||||
case 0x24: s = {"and al,{0}", format{readByte()}}; break;
|
||||
case 0x25: s = {"and ax,{0}", format{readWord()}}; break;
|
||||
case 0x26: s = {"es: "}; break;
|
||||
case 0x27: s = {"daa "}; break;
|
||||
case 0x28: s = {"sub {0},{1}", format{readMemByte(0), readRegByte()}}; break;
|
||||
case 0x29: s = {"sub {0},{1}", format{readMemWord(0), readRegWord()}}; break;
|
||||
case 0x2a: s = {"sub {0},{1}", format{readRegByte(0), readMemByte()}}; break;
|
||||
case 0x2b: s = {"sub {0},{1}", format{readRegWord(0), readMemWord()}}; break;
|
||||
case 0x2c: s = {"sub al,{0}", format{readByte()}}; break;
|
||||
case 0x2d: s = {"sub ax,{0}", format{readWord()}}; break;
|
||||
case 0x2e: s = {"cs: "}; break;
|
||||
case 0x2f: s = {"das "}; break;
|
||||
case 0x30: s = {"xor {0},{1}", format{readMemByte(0), readRegByte()}}; break;
|
||||
case 0x31: s = {"xor {0},{1}", format{readMemWord(0), readRegWord()}}; break;
|
||||
case 0x32: s = {"xor {0},{1}", format{readRegByte(0), readMemByte()}}; break;
|
||||
case 0x33: s = {"xor {0},{1}", format{readRegWord(0), readMemWord()}}; break;
|
||||
case 0x34: s = {"xor al,{0}", format{readByte()}}; break;
|
||||
case 0x35: s = {"xor ax,{0}", format{readWord()}}; break;
|
||||
case 0x36: s = {"ss: "}; break;
|
||||
case 0x37: s = {"aaa "}; break;
|
||||
case 0x38: s = {"cmp {0},{1}", format{readMemByte(0), readRegByte()}}; break;
|
||||
case 0x39: s = {"cmp {0},{1}", format{readMemWord(0), readRegWord()}}; break;
|
||||
case 0x3a: s = {"cmp {0},{1}", format{readRegByte(0), readMemByte()}}; break;
|
||||
case 0x3b: s = {"cmp {0},{1}", format{readRegWord(0), readMemWord()}}; break;
|
||||
case 0x3c: s = {"cmp al,{0}", format{readByte()}}; break;
|
||||
case 0x3d: s = {"cmp ax,{0}", format{readWord()}}; break;
|
||||
case 0x3e: s = {"ds: "}; break;
|
||||
case 0x3f: s = {"aas "}; break;
|
||||
case 0x40: s = {"inc ax"}; break;
|
||||
case 0x41: s = {"inc cx"}; break;
|
||||
case 0x42: s = {"inc dx"}; break;
|
||||
case 0x43: s = {"inc bx"}; break;
|
||||
case 0x44: s = {"inc sp"}; break;
|
||||
case 0x45: s = {"inc bp"}; break;
|
||||
case 0x46: s = {"inc si"}; break;
|
||||
case 0x47: s = {"inc di"}; break;
|
||||
case 0x48: s = {"dec ax"}; break;
|
||||
case 0x49: s = {"dec cx"}; break;
|
||||
case 0x4a: s = {"dec dx"}; break;
|
||||
case 0x4b: s = {"dec bx"}; break;
|
||||
case 0x4c: s = {"dec sp"}; break;
|
||||
case 0x4d: s = {"dec bp"}; break;
|
||||
case 0x4e: s = {"dec si"}; break;
|
||||
case 0x4f: s = {"dec di"}; break;
|
||||
case 0x50: s = {"push ax"}; break;
|
||||
case 0x51: s = {"push cx"}; break;
|
||||
case 0x52: s = {"push dx"}; break;
|
||||
case 0x53: s = {"push bx"}; break;
|
||||
case 0x54: s = {"push sp"}; break;
|
||||
case 0x55: s = {"push bp"}; break;
|
||||
case 0x56: s = {"push si"}; break;
|
||||
case 0x57: s = {"push di"}; break;
|
||||
case 0x58: s = {"pop ax"}; break;
|
||||
case 0x59: s = {"pop cx"}; break;
|
||||
case 0x5a: s = {"pop dx"}; break;
|
||||
case 0x5b: s = {"pop bx"}; break;
|
||||
case 0x5c: s = {"pop sp"}; break;
|
||||
case 0x5d: s = {"pop bp"}; break;
|
||||
case 0x5e: s = {"pop si"}; break;
|
||||
case 0x5f: s = {"pop di"}; break;
|
||||
case 0x60: s = {"pusha "}; break;
|
||||
case 0x61: s = {"popa "}; break;
|
||||
case 0x62: s = {"bound {0},{1}", format{readRegWord(0), readMemWord()}}; break;
|
||||
//case 0x63:
|
||||
//case 0x64:
|
||||
//case 0x65:
|
||||
//case 0x66:
|
||||
//case 0x67:
|
||||
case 0x68: s = {"push {0}", format{readWord()}}; break;
|
||||
case 0x69: s = {"imul {0},{1},{2}", format{readRegWord(0), readMemWord(), readWord()}}; break;
|
||||
case 0x6a: s = {"push {0}", format{readByteSigned()}}; break;
|
||||
case 0x6b: s = {"imul {0},{1},{2}", format{readRegWord(0), readMemWord(), readByteSigned()}}; break;
|
||||
case 0x6c: s = {"insb "}; break;
|
||||
case 0x6d: s = {"insw "}; break;
|
||||
case 0x6e: s = {"outsb "}; break;
|
||||
case 0x6f: s = {"outsw "}; break;
|
||||
case 0x70: s = {"jo {0}", format{readRelativeByte()}}; break;
|
||||
case 0x71: s = {"jno {0}", format{readRelativeByte()}}; break;
|
||||
case 0x72: s = {"jc {0}", format{readRelativeByte()}}; break;
|
||||
case 0x73: s = {"jnc {0}", format{readRelativeByte()}}; break;
|
||||
case 0x74: s = {"jz {0}", format{readRelativeByte()}}; break;
|
||||
case 0x75: s = {"jnz {0}", format{readRelativeByte()}}; break;
|
||||
case 0x76: s = {"jcz {0}", format{readRelativeByte()}}; break;
|
||||
case 0x77: s = {"jncz {0}", format{readRelativeByte()}}; break;
|
||||
case 0x78: s = {"js {0}", format{readRelativeByte()}}; break;
|
||||
case 0x79: s = {"jns {0}", format{readRelativeByte()}}; break;
|
||||
case 0x7a: s = {"jp {0}", format{readRelativeByte()}}; break;
|
||||
case 0x7b: s = {"jnp {0}", format{readRelativeByte()}}; break;
|
||||
case 0x7c: s = {"jl {0}", format{readRelativeByte()}}; break;
|
||||
case 0x7d: s = {"jnl {0}", format{readRelativeByte()}}; break;
|
||||
case 0x7e: s = {"jle {0}", format{readRelativeByte()}}; break;
|
||||
case 0x7f: s = {"jnle {0}", format{readRelativeByte()}}; break;
|
||||
case 0x80: s = {"{0} {1},{2}", format{readGroup(1), readMemByte(), readByte()}}; break;
|
||||
case 0x81: s = {"{0} {1},{2}", format{readGroup(1), readMemWord(), readWord()}}; break;
|
||||
case 0x82: s = {"{0} {1},{2}", format{readGroup(1), readMemByte(), readByteSigned()}}; break;
|
||||
case 0x83: s = {"{0} {1},{2}", format{readGroup(1), readMemWord(), readByteSigned()}}; break;
|
||||
case 0x84: s = {"test {0},{1}", format{readMemByte(0), readRegByte()}}; break;
|
||||
case 0x85: s = {"test {0},{1}", format{readMemWord(0), readRegWord()}}; break;
|
||||
case 0x86: s = {"xchg {0},{1}", format{readMemByte(0), readRegByte()}}; break;
|
||||
case 0x87: s = {"xchg {0},{1}", format{readMemWord(0), readRegWord()}}; break;
|
||||
case 0x88: s = {"mov {0},{1}", format{readMemByte(0), readRegByte()}}; break;
|
||||
case 0x89: s = {"mov {0},{1}", format{readMemWord(0), readRegWord()}}; break;
|
||||
case 0x8a: s = {"mov {0},{1}", format{readRegByte(0), readMemByte()}}; break;
|
||||
case 0x8b: s = {"mov {0},{1}", format{readRegWord(0), readMemWord()}}; break;
|
||||
case 0x8c: s = {"mov {0},{1}", format{readMemWord(0), readSeg()}}; break;
|
||||
case 0x8d: s = {"lea {0},{1}", format{readRegWord(0), readMemWord()}}; break;
|
||||
case 0x8e: s = {"mov {0},{1}", format{readSeg(0), readMemWord()}}; break;
|
||||
case 0x8f: s = {"pop {0}", format{readMemWord()}}; break;
|
||||
case 0x90: s = {"nop "}; break;
|
||||
case 0x91: s = {"xchg ax,cx"}; break;
|
||||
case 0x92: s = {"xchg ax,dx"}; break;
|
||||
case 0x93: s = {"xchg ax,bx"}; break;
|
||||
case 0x94: s = {"xchg ax,sp"}; break;
|
||||
case 0x95: s = {"xchg ax,bp"}; break;
|
||||
case 0x96: s = {"xchg ax,si"}; break;
|
||||
case 0x97: s = {"xchg ax,di"}; break;
|
||||
case 0x98: s = {"cbw "}; break;
|
||||
case 0x99: s = {"cwd "}; break;
|
||||
case 0x9a: s = {"call {1}:{0}", format{readWord(), readWord()}}; break;
|
||||
case 0x9b: s = {"wait "}; break;
|
||||
case 0x9c: s = {"pushf "}; break;
|
||||
case 0x9d: s = {"popf "}; break;
|
||||
case 0x9e: s = {"sahf "}; break;
|
||||
case 0x9f: s = {"lahf "}; break;
|
||||
case 0xa0: s = {"mov al,{0}", format{readIndirectByte()}}; break;
|
||||
case 0xa1: s = {"mov ax,{0}", format{readIndirectWord()}}; break;
|
||||
case 0xa2: s = {"mov {0},al", format{readIndirectByte()}}; break;
|
||||
case 0xa3: s = {"mov {0},ax", format{readIndirectWord()}}; break;
|
||||
case 0xa4: s = {"movsb "}; break;
|
||||
case 0xa5: s = {"movsw "}; break;
|
||||
case 0xa6: s = {"cmpsb "}; break;
|
||||
case 0xa7: s = {"cmpsw "}; break;
|
||||
case 0xa8: s = {"test al,{0}", format{readByte()}}; break;
|
||||
case 0xa9: s = {"test ax,{0}", format{readWord()}}; break;
|
||||
case 0xaa: s = {"stosb "}; break;
|
||||
case 0xab: s = {"stosw "}; break;
|
||||
case 0xac: s = {"lodsb "}; break;
|
||||
case 0xad: s = {"lodsw "}; break;
|
||||
case 0xae: s = {"scasb "}; break;
|
||||
case 0xaf: s = {"scasw "}; break;
|
||||
case 0xb0: s = {"mov al,{0}", format{readByte()}}; break;
|
||||
case 0xb1: s = {"mov cl,{0}", format{readByte()}}; break;
|
||||
case 0xb2: s = {"mov dl,{0}", format{readByte()}}; break;
|
||||
case 0xb3: s = {"mov bl,{0}", format{readByte()}}; break;
|
||||
case 0xb4: s = {"mov ah,{0}", format{readByte()}}; break;
|
||||
case 0xb5: s = {"mov ch,{0}", format{readByte()}}; break;
|
||||
case 0xb6: s = {"mov dh,{0}", format{readByte()}}; break;
|
||||
case 0xb7: s = {"mov bh,{0}", format{readByte()}}; break;
|
||||
case 0xb8: s = {"mov ax,{0}", format{readWord()}}; break;
|
||||
case 0xb9: s = {"mov cx,{0}", format{readWord()}}; break;
|
||||
case 0xba: s = {"mov dx,{0}", format{readWord()}}; break;
|
||||
case 0xbb: s = {"mov bx,{0}", format{readWord()}}; break;
|
||||
case 0xbc: s = {"mov sp,{0}", format{readWord()}}; break;
|
||||
case 0xbd: s = {"mov bp,{0}", format{readWord()}}; break;
|
||||
case 0xbe: s = {"mov si,{0}", format{readWord()}}; break;
|
||||
case 0xbf: s = {"mov di,{0}", format{readWord()}}; break;
|
||||
case 0xc0: s = {"{0} {1},{2}", format{readGroup(2), readMemByte(), readByte()}}; break;
|
||||
case 0xc1: s = {"{0} {1},{2}", format{readGroup(2), readMemWord(), readByte()}}; break;
|
||||
case 0xc2: s = {"ret {0}", format{readWord()}}; break;
|
||||
case 0xc3: s = {"ret "}; break;
|
||||
case 0xc4: s = {"les {0}", format{readMemWord()}}; break;
|
||||
case 0xc5: s = {"lds {0}", format{readMemWord()}}; break;
|
||||
case 0xc6: s = {"mov {0},{1}", format{readMemByte(), readByte()}}; break;
|
||||
case 0xc7: s = {"mov {0},{1}", format{readMemWord(), readWord()}}; break;
|
||||
case 0xc8: s = {"enter {0},{1}", format{readWord(), readByte()}}; break;
|
||||
case 0xc9: s = {"leave "}; break;
|
||||
case 0xca: s = {"retf {0}", format{readWord()}}; break;
|
||||
case 0xcb: s = {"retf "}; break;
|
||||
case 0xcc: s = {"int3 "}; break;
|
||||
case 0xcd: s = {"int ", format{readByte()}}; break;
|
||||
case 0xce: s = {"into "}; break;
|
||||
case 0xcf: s = {"iret "}; break;
|
||||
case 0xd0: s = {"{0} {1},1", format{readGroup(2), readMemByte()}}; break;
|
||||
case 0xd1: s = {"{0} {1},1", format{readGroup(2), readMemWord()}}; break;
|
||||
case 0xd2: s = {"{0} {1},cl", format{readGroup(2), readMemByte()}}; break;
|
||||
case 0xd3: s = {"{0} {1},cl", format{readGroup(2), readMemWord()}}; break;
|
||||
case 0xd4: s = {"aam {0}", format{readByte()}}; break;
|
||||
case 0xd5: s = {"aad {0}", format{readByte()}}; break;
|
||||
//case 0xd6:
|
||||
case 0xd7: s = {"xlat "}; break;
|
||||
//case 0xd8:
|
||||
//case 0xd9:
|
||||
//case 0xda:
|
||||
//case 0xdb:
|
||||
//case 0xdc:
|
||||
//case 0xdd:
|
||||
//case 0xde:
|
||||
//case 0xdf:
|
||||
case 0xe0: s = {"loopnz "}; break;
|
||||
case 0xe1: s = {"loopz "}; break;
|
||||
case 0xe2: s = {"loop "}; break;
|
||||
case 0xe3: s = {"jcxz {0}", format{readRelativeByte()}}; break;
|
||||
case 0xe4: s = {"in al,{0}", format{readByte()}}; break;
|
||||
case 0xe5: s = {"in ax,{0}", format{readByte()}}; break;
|
||||
case 0xe6: s = {"out {0},al", format{readByte()}}; break;
|
||||
case 0xe7: s = {"out {0},ax", format{readByte()}}; break;
|
||||
case 0xe8: s = {"call {0}", format{readRelativeWord()}}; break;
|
||||
case 0xe9: s = {"jmp {0}", format{readRelativeWord()}}; break;
|
||||
case 0xea: s = {"jmp {1}:{0}", format{readWord(), readWord()}}; break;
|
||||
case 0xeb: s = {"jmp {0}", format{readRelativeByte()}}; break;
|
||||
case 0xec: s = {"in al,dx"}; break;
|
||||
case 0xed: s = {"in ax,dx"}; break;
|
||||
case 0xee: s = {"out dx,al"}; break;
|
||||
case 0xef: s = {"out dx,ax"}; break;
|
||||
case 0xf0: s = {"lock: "}; break;
|
||||
//case 0xf1:
|
||||
case 0xf2: s = {"repnz: "}; break;
|
||||
case 0xf3: s = {"repz: "}; break;
|
||||
case 0xf4: s = {"hlt "}; break;
|
||||
case 0xf5: s = {"cmc "}; break;
|
||||
case 0xf6: s = {"{0} {1},{2}", format{readGroup(3), readMemByte(), readByte()}}; break;
|
||||
case 0xf7: s = {"{0} {1},{2}", format{readGroup(3), readMemWord(), readWord()}}; break;
|
||||
case 0xf8: s = {"clc "}; break;
|
||||
case 0xf9: s = {"stc "}; break;
|
||||
case 0xfa: s = {"cli "}; break;
|
||||
case 0xfb: s = {"sti "}; break;
|
||||
case 0xfc: s = {"cld "}; break;
|
||||
case 0xfd: s = {"std "}; break;
|
||||
case 0xfe: s = {"{0} {1},{2}", format{readGroup(4), readMemByte(), readByte()}}; break;
|
||||
case 0xff: s = {"{0} {1},{2}", format{readGroup(4), readMemWord(), readWord()}}; break;
|
||||
default: s = {"??? {0}", format{hex(opcode, 2L)}}; break;
|
||||
}
|
||||
while(s.size() < 24) s.append(" ");
|
||||
|
||||
string l;
|
||||
if(registers) {
|
||||
l = {
|
||||
" ax:", hex(r.ax, 4L),
|
||||
" bx:", hex(r.bx, 4L),
|
||||
" cx:", hex(r.cx, 4L),
|
||||
" dx:", hex(r.dx, 4L),
|
||||
" si:", hex(r.si, 4L),
|
||||
" di:", hex(r.di, 4L),
|
||||
" bp:", hex(r.bp, 4L),
|
||||
" sp:", hex(r.sp, 4L),
|
||||
" ip:", hex(r.ip, 4L),
|
||||
" cs:", hex(r.cs, 4L),
|
||||
" ds:", hex(r.ds, 4L),
|
||||
" es:", hex(r.es, 4L),
|
||||
" ss:", hex(r.ss, 4L), " ",
|
||||
r.f.m ? "M" : "m",
|
||||
r.f.v ? "V" : "v",
|
||||
r.f.d ? "D" : "d",
|
||||
r.f.i ? "I" : "i",
|
||||
r.f.b ? "B" : "b",
|
||||
r.f.s ? "S" : "s",
|
||||
r.f.z ? "Z" : "z",
|
||||
r.f.h ? "H" : "h",
|
||||
r.f.p ? "P" : "p",
|
||||
r.f.c ? "C" : "c"
|
||||
};
|
||||
}
|
||||
|
||||
string b;
|
||||
if(bytes) {
|
||||
b = " ";
|
||||
while(bytesRead) {
|
||||
b.append(hex(bytesRead.takeLeft(), 2L), " ");
|
||||
}
|
||||
b.stripRight();
|
||||
}
|
||||
|
||||
return {hex(ea, 5L), " ", s, l, b};
|
||||
auto V30MZ::disassemble() -> string {
|
||||
return disassemble(r.cs, r.ip);
|
||||
}
|
||||
|
||||
auto V30MZ::disassemble(uint16 cs, uint16 ip) -> string {
|
||||
//hack: prefixes execute as separate instructions; combine them instead
|
||||
static uint32 suppress = 0xffffffff;
|
||||
if((cs << 16 | ip) == suppress) return {};
|
||||
|
||||
string output, repeat, prefix;
|
||||
output.append(hex(r.cs * 16 + r.ip, 5L), " ");
|
||||
|
||||
auto read = [&](uint offset) -> uint8 {
|
||||
return V30MZ::read(Byte, cs, ip + offset);
|
||||
};
|
||||
|
||||
auto modRM = [&](uint offset = 1) -> uint {
|
||||
auto modRM = read(offset++);
|
||||
if((modRM & 0xc0) == 0x40) offset += 1;
|
||||
if((modRM & 0xc0) == 0x80) offset += 2;
|
||||
return offset;
|
||||
};
|
||||
|
||||
auto instruction = [&](string_view name) -> string {
|
||||
if(name.size() >= 7) return name;
|
||||
return pad(name, -7);
|
||||
};
|
||||
|
||||
auto segment = [&](string_view name) -> string {
|
||||
if(prefix) return {prefix, ":"};
|
||||
return {name, ":"};
|
||||
};
|
||||
|
||||
auto repeatable = [&](string_view opcode) -> string {
|
||||
if(repeat) return {pad(string{repeat, ":"}, -7), opcode};
|
||||
return {opcode};
|
||||
};
|
||||
|
||||
auto segmentRegister = [&](uint offset = 1) -> string {
|
||||
auto modRM = read(offset);
|
||||
static const string seg[] = {"es", "cs", "ss", "ds"};
|
||||
return {seg[modRM >> 3 & 2]};
|
||||
};
|
||||
|
||||
auto readByte = [&](uint offset) -> string {
|
||||
return hex(read(offset), 2L);
|
||||
};
|
||||
|
||||
auto immediateByte = [&](uint offset = 1) -> string {
|
||||
return {"0x", readByte(offset)};
|
||||
};
|
||||
|
||||
auto immediateWord = [&](uint offset = 1) -> string {
|
||||
return {"0x", readByte(offset + 1), readByte(offset + 0)};
|
||||
};
|
||||
|
||||
auto immediateLong = [&](uint offset = 1) -> string {
|
||||
return {"0x", readByte(offset + 3), readByte(offset + 2), ":",
|
||||
"0x", readByte(offset + 1), readByte(offset + 0)};
|
||||
};
|
||||
|
||||
auto indirectByte = [&](uint offset = 1) -> string {
|
||||
return {"[", immediateByte(), "]"};
|
||||
};
|
||||
|
||||
auto indirectWord = [&](uint offset = 1) -> string {
|
||||
return {"[", immediateWord(), "]"};
|
||||
};
|
||||
|
||||
auto relativeByte = [&](uint offset = 1) -> string {
|
||||
int8 displacement = read(offset);
|
||||
return {"cs:0x", hex(ip + offset + 1 + displacement, 4L)};
|
||||
};
|
||||
|
||||
auto relativeWord = [&](uint offset = 1) -> string {
|
||||
int16 displacement = read(offset + 1) << 8 | read(offset + 0) << 0;
|
||||
return {"cs:0x", hex(ip + offset + 2 + displacement, 4L)};
|
||||
};
|
||||
|
||||
auto adjustByte = [&](uint offset = 2) -> string {
|
||||
int8 displacement = read(offset);
|
||||
if(displacement >= 0) return {"+0x", hex(displacement, 2L)};
|
||||
return {"-0x", hex(abs(displacement), 2L)};
|
||||
};
|
||||
|
||||
auto adjustWord = [&](uint offset = 2) -> string {
|
||||
int16 displacement = read(offset + 1) << 8 | read(offset + 0) << 0;
|
||||
if(displacement >= 0) return {"+0x", hex(displacement, 4L)};
|
||||
return {"-0x", hex(abs(displacement), 2L)};
|
||||
};
|
||||
|
||||
auto registerByte = [&](uint offset = 1) -> string {
|
||||
auto modRM = read(offset);
|
||||
static const string reg[] = {"al", "cl", "dl", "bl", "ah", "ch", "dh", "bh"};
|
||||
return reg[modRM >> 3 & 7];
|
||||
};
|
||||
|
||||
auto registerWord = [&](uint offset = 1) -> string {
|
||||
auto modRM = read(offset);
|
||||
static const string reg[] = {"ax", "cx", "dx", "bx", "sp", "bp", "si", "di"};
|
||||
return reg[modRM >> 3 & 7];
|
||||
};
|
||||
|
||||
auto memoryByte = [&](uint offset = 1) -> string {
|
||||
auto modRM = read(offset);
|
||||
if(modRM >= 0xc0) return registerByte(modRM & 7);
|
||||
if((modRM & 0xc7) == 0x06) return {"byte[", segment("ds"), immediateByte(), "]"};
|
||||
static const string seg[] = {"ds", "ds", "ss", "ss", "ds", "ds", "ss", "ds"};
|
||||
static const string mem[] = {"bx+si", "bx+di", "bp+si", "bp+di", "si", "di", "bp", "bx"};
|
||||
if((modRM & 0xc0) == 0x40) return {"byte[", segment(seg[modRM & 7]), mem[modRM & 7], "+", adjustByte(), "]"};
|
||||
if((modRM & 0xc0) == 0x80) return {"byte[", segment(seg[modRM & 7]), mem[modRM & 7], "+", adjustWord(), "]"};
|
||||
return {"byte[", segment(seg[modRM & 7]), mem[modRM & 7], "]"};
|
||||
};
|
||||
|
||||
auto memoryWord = [&](uint offset = 1) -> string {
|
||||
auto modRM = read(offset);
|
||||
if(modRM >= 0xc0) return registerWord(modRM & 7);
|
||||
if((modRM & 0xc7) == 0x06) return {"word[", segment("ds"), immediateWord(), "]"};
|
||||
static const string seg[] = {"ds", "ds", "ss", "ss", "ds", "ds", "ss", "ds"};
|
||||
static const string mem[] = {"bx+si", "bx+di", "bp+si", "bp+di", "si", "di", "bp", "bx"};
|
||||
if((modRM & 0xc0) == 0x40) return {"word[", segment(seg[modRM & 7]), mem[modRM & 7], adjustByte(), "]"};
|
||||
if((modRM & 0xc0) == 0x80) return {"word[", segment(seg[modRM & 7]), mem[modRM & 7], adjustWord(), "]"};
|
||||
return {"word[", segment(seg[modRM & 7]), mem[modRM & 7], "]"};
|
||||
};
|
||||
|
||||
auto group1 = [&](uint offset = 1) -> string {
|
||||
auto modRM = read(offset);
|
||||
static const string opcode[] = {"add", "or", "adc", "sbb", "and", "sub", "xor", "cmp"};
|
||||
return opcode[modRM >> 3 & 7];
|
||||
};
|
||||
|
||||
auto group2 = [&](uint offset = 1) -> string {
|
||||
auto modRM = read(offset);
|
||||
static const string opcode[] = {"rol", "ror", "rcl", "rcr", "shl", "shr", "sal", "sar"};
|
||||
return opcode[modRM >> 3 & 7];
|
||||
};
|
||||
|
||||
auto group3 = [&](uint offset = 1) -> string {
|
||||
auto modRM = read(offset);
|
||||
static const string opcode[] = {"test", "test", "not", "neg", "mul", "imul", "div", "idiv"};
|
||||
return opcode[modRM >> 3 & 7];
|
||||
};
|
||||
|
||||
auto group4 = [&](uint offset = 1) -> string {
|
||||
auto modRM = read(offset);
|
||||
static const string opcode[] = {"inc", "dec", "call", "callf", "jmp", "jmpf", "push", "push"};
|
||||
return opcode[modRM >> 3 & 7];
|
||||
};
|
||||
|
||||
#define op(id, name, ...) case id: \
|
||||
output.append(instruction(name), vector<string>{__VA_ARGS__}.merge(",")); \
|
||||
break
|
||||
|
||||
auto opcode = read(0);
|
||||
for(uint index : range(7)) {
|
||||
if(opcode == 0x26) { prefix = "es"; ip++; opcode = read(0); suppress = cs << 16 | ip; continue; }
|
||||
if(opcode == 0x2e) { prefix = "cs"; ip++; opcode = read(0); suppress = cs << 16 | ip; continue; }
|
||||
if(opcode == 0x36) { prefix = "ss"; ip++; opcode = read(0); suppress = cs << 16 | ip; continue; }
|
||||
if(opcode == 0x3e) { prefix = "ds"; ip++; opcode = read(0); suppress = cs << 16 | ip; continue; }
|
||||
if(opcode == 0xf2) { repeat = "repnz"; ip++; opcode = read(0); suppress = cs << 16 | ip; continue; }
|
||||
if(opcode == 0xf3) { repeat = "repz"; ip++; opcode = read(0); suppress = cs << 16 | ip; continue; }
|
||||
break;
|
||||
}
|
||||
|
||||
switch(opcode) {
|
||||
op(0x00, "add", memoryByte(), registerByte());
|
||||
op(0x01, "add", memoryWord(), registerWord());
|
||||
op(0x02, "add", registerByte(), memoryByte());
|
||||
op(0x03, "add", registerWord(), memoryWord());
|
||||
op(0x04, "add", "al", immediateByte());
|
||||
op(0x05, "add", "ax", immediateWord());
|
||||
op(0x06, "push", "es");
|
||||
op(0x07, "pop", "es");
|
||||
op(0x08, "or", memoryByte(), registerByte());
|
||||
op(0x09, "or", memoryWord(), registerWord());
|
||||
op(0x0a, "or", registerByte(), memoryByte());
|
||||
op(0x0b, "or", registerWord(), memoryWord());
|
||||
op(0x0c, "or", "al", immediateByte());
|
||||
op(0x0d, "or", "ax", immediateWord());
|
||||
op(0x0e, "push", "cx");
|
||||
op(0x0f, "pop", "cs");
|
||||
op(0x10, "adc", memoryByte(), registerByte());
|
||||
op(0x11, "adc", memoryWord(), registerWord());
|
||||
op(0x12, "adc", registerByte(), memoryByte());
|
||||
op(0x13, "adc", registerWord(), memoryWord());
|
||||
op(0x14, "adc", "al", immediateByte());
|
||||
op(0x15, "adc", "ax", immediateWord());
|
||||
op(0x16, "push", "ss");
|
||||
op(0x17, "pop", "ss");
|
||||
op(0x18, "sbb", memoryByte(), registerByte());
|
||||
op(0x19, "sbb", memoryWord(), registerWord());
|
||||
op(0x1a, "sbb", registerByte(), memoryByte());
|
||||
op(0x1b, "sbb", registerWord(), memoryWord());
|
||||
op(0x1c, "sbb", "al", immediateByte());
|
||||
op(0x1d, "sbb", "ax", immediateWord());
|
||||
op(0x1e, "push", "ds");
|
||||
op(0x1f, "pop", "ds");
|
||||
op(0x20, "and", memoryByte(), registerByte());
|
||||
op(0x21, "and", memoryWord(), registerWord());
|
||||
op(0x22, "and", registerByte(), memoryByte());
|
||||
op(0x23, "and", registerWord(), memoryWord());
|
||||
op(0x24, "and", "al", immediateByte());
|
||||
op(0x25, "and", "ax", immediateWord());
|
||||
op(0x26, "es:");
|
||||
op(0x27, "daa");
|
||||
op(0x28, "sub", memoryByte(), registerByte());
|
||||
op(0x29, "sub", memoryWord(), registerWord());
|
||||
op(0x2a, "sub", registerByte(), memoryByte());
|
||||
op(0x2b, "sub", registerWord(), memoryWord());
|
||||
op(0x2c, "sub", "al", immediateByte());
|
||||
op(0x2d, "sub", "ax", immediateWord());
|
||||
op(0x2e, "cs:");
|
||||
op(0x2f, "das");
|
||||
op(0x30, "xor", memoryByte(), registerByte());
|
||||
op(0x31, "xor", memoryWord(), registerWord());
|
||||
op(0x32, "xor", registerByte(), memoryByte());
|
||||
op(0x33, "xor", registerWord(), memoryWord());
|
||||
op(0x34, "xor", "al", immediateByte());
|
||||
op(0x35, "xor", "ax", immediateWord());
|
||||
op(0x36, "ss:");
|
||||
op(0x37, "aaa");
|
||||
op(0x38, "cmp", memoryByte(), registerByte());
|
||||
op(0x39, "cmp", memoryWord(), registerWord());
|
||||
op(0x3a, "cmp", registerByte(), memoryByte());
|
||||
op(0x3b, "cmp", registerWord(), memoryWord());
|
||||
op(0x3c, "cmp", "al", immediateByte());
|
||||
op(0x3d, "cmp", "ax", immediateWord());
|
||||
op(0x3e, "ds:");
|
||||
op(0x3f, "aas");
|
||||
op(0x40, "inc", "ax");
|
||||
op(0x41, "inc", "cx");
|
||||
op(0x42, "inc", "dx");
|
||||
op(0x43, "inc", "bx");
|
||||
op(0x44, "inc", "sp");
|
||||
op(0x45, "inc", "bp");
|
||||
op(0x46, "inc", "si");
|
||||
op(0x47, "inc", "di");
|
||||
op(0x48, "dec", "ax");
|
||||
op(0x49, "dec", "cx");
|
||||
op(0x4a, "dec", "dx");
|
||||
op(0x4b, "dec", "bx");
|
||||
op(0x4c, "dec", "sp");
|
||||
op(0x4d, "dec", "bp");
|
||||
op(0x4e, "dec", "si");
|
||||
op(0x4f, "dec", "di");
|
||||
op(0x50, "push", "ax");
|
||||
op(0x51, "push", "cx");
|
||||
op(0x52, "push", "dx");
|
||||
op(0x53, "push", "bx");
|
||||
op(0x54, "push", "sp");
|
||||
op(0x55, "push", "bp");
|
||||
op(0x56, "push", "si");
|
||||
op(0x57, "push", "di");
|
||||
op(0x58, "pop", "ax");
|
||||
op(0x59, "pop", "cx");
|
||||
op(0x5a, "pop", "dx");
|
||||
op(0x5b, "pop", "bx");
|
||||
op(0x5c, "pop", "sp");
|
||||
op(0x5d, "pop", "bp");
|
||||
op(0x5e, "pop", "si");
|
||||
op(0x5f, "pop", "di");
|
||||
op(0x60, "pusha");
|
||||
op(0x61, "popa");
|
||||
op(0x62, "bound", registerWord(), memoryWord());
|
||||
//op(0x63);
|
||||
//op(0x64);
|
||||
//op(0x65);
|
||||
//op(0x66);
|
||||
//op(0x67);
|
||||
op(0x68, "push", immediateWord());
|
||||
op(0x69, "imul", registerWord(), memoryWord(), immediateWord(modRM()));
|
||||
op(0x6a, "push", adjustByte(1));
|
||||
op(0x6b, "imul", registerWord(), memoryWord(), adjustByte(modRM()));
|
||||
op(0x6c, repeatable("insb"));
|
||||
op(0x6d, repeatable("insw"));
|
||||
op(0x6e, repeatable("outsb"));
|
||||
op(0x6f, repeatable("outsw"));
|
||||
op(0x70, "jo", relativeByte());
|
||||
op(0x71, "jno", relativeByte());
|
||||
op(0x72, "jb", relativeByte());
|
||||
op(0x73, "jnb", relativeByte());
|
||||
op(0x74, "jz", relativeByte());
|
||||
op(0x75, "jnz", relativeByte());
|
||||
op(0x76, "jbe", relativeByte());
|
||||
op(0x77, "ja", relativeByte());
|
||||
op(0x78, "js", relativeByte());
|
||||
op(0x79, "jns", relativeByte());
|
||||
op(0x7a, "jpe", relativeByte());
|
||||
op(0x7b, "jpo", relativeByte());
|
||||
op(0x7c, "jl", relativeByte());
|
||||
op(0x7d, "jge", relativeByte());
|
||||
op(0x7e, "jle", relativeByte());
|
||||
op(0x7f, "jg", relativeByte());
|
||||
op(0x80, group1(), memoryByte(), immediateByte(modRM()));
|
||||
op(0x81, group1(), memoryWord(), immediateWord(modRM()));
|
||||
op(0x82, group1(), memoryByte(), adjustByte(modRM()));
|
||||
op(0x83, group1(), memoryWord(), adjustByte(modRM()));
|
||||
op(0x84, "test", memoryByte(), registerByte());
|
||||
op(0x85, "test", memoryWord(), registerWord());
|
||||
op(0x86, "xchg", memoryByte(), registerByte());
|
||||
op(0x87, "xchg", memoryWord(), registerWord());
|
||||
op(0x88, "mov", memoryByte(), registerByte());
|
||||
op(0x89, "mov", memoryWord(), registerWord());
|
||||
op(0x8a, "mov", registerByte(), memoryByte());
|
||||
op(0x8b, "mov", registerWord(), memoryWord());
|
||||
op(0x8c, "mov", memoryWord(), segmentRegister());
|
||||
op(0x8d, "lea", registerWord(), memoryWord());
|
||||
op(0x8e, "mov", segmentRegister(), memoryWord());
|
||||
op(0x8f, "pop", memoryWord());
|
||||
op(0x90, "nop");
|
||||
op(0x91, "xchg", "ax", "cx");
|
||||
op(0x92, "xchg", "ax", "dx");
|
||||
op(0x93, "xchg", "ax", "bx");
|
||||
op(0x94, "xchg", "ax", "sp");
|
||||
op(0x95, "xchg", "ax", "bp");
|
||||
op(0x96, "xchg", "ax", "si");
|
||||
op(0x97, "xchg", "ax", "di");
|
||||
op(0x98, "cbw");
|
||||
op(0x99, "cwd");
|
||||
op(0x9a, "call", immediateLong());
|
||||
op(0x9b, "wait");
|
||||
op(0x9c, "pushf");
|
||||
op(0x9d, "popf");
|
||||
op(0x9e, "sahf");
|
||||
op(0x9f, "lahf");
|
||||
op(0xa0, "mov", "al", indirectByte());
|
||||
op(0xa1, "mov", "ax", indirectWord());
|
||||
op(0xa2, "mov", indirectByte(), "al");
|
||||
op(0xa3, "mov", indirectWord(), "ax");
|
||||
op(0xa4, repeatable("movsb"));
|
||||
op(0xa5, repeatable("movsw"));
|
||||
op(0xa6, repeatable("cmpsb"));
|
||||
op(0xa7, repeatable("cmpsw"));
|
||||
op(0xa8, "test", immediateByte());
|
||||
op(0xa9, "test", immediateWord());
|
||||
op(0xaa, repeatable("stosb"));
|
||||
op(0xab, repeatable("stosw"));
|
||||
op(0xac, repeatable("lodsb"));
|
||||
op(0xad, repeatable("lodsw"));
|
||||
op(0xae, repeatable("scasb"));
|
||||
op(0xaf, repeatable("scasw"));
|
||||
op(0xb0, "mov", "al", immediateByte());
|
||||
op(0xb1, "mov", "cl", immediateByte());
|
||||
op(0xb2, "mov", "dl", immediateByte());
|
||||
op(0xb3, "mov", "bl", immediateByte());
|
||||
op(0xb4, "mov", "ah", immediateByte());
|
||||
op(0xb5, "mov", "ch", immediateByte());
|
||||
op(0xb6, "mov", "dh", immediateByte());
|
||||
op(0xb7, "mov", "bh", immediateByte());
|
||||
op(0xb8, "mov", "ax", immediateWord());
|
||||
op(0xb9, "mov", "cx", immediateWord());
|
||||
op(0xba, "mov", "dx", immediateWord());
|
||||
op(0xbb, "mov", "bx", immediateWord());
|
||||
op(0xbc, "mov", "sp", immediateWord());
|
||||
op(0xbd, "mov", "bp", immediateWord());
|
||||
op(0xbe, "mov", "si", immediateWord());
|
||||
op(0xbf, "mov", "di", immediateWord());
|
||||
op(0xc0, group2(), memoryByte(), immediateByte(modRM()));
|
||||
op(0xc1, group2(), memoryWord(), immediateByte(modRM()));
|
||||
op(0xc2, "ret", immediateWord());
|
||||
op(0xc3, "ret");
|
||||
op(0xc4, "les", memoryWord());
|
||||
op(0xc5, "lds", memoryWord());
|
||||
op(0xc6, "mov", memoryByte(), immediateByte(modRM()));
|
||||
op(0xc7, "mov", memoryWord(), immediateWord(modRM()));
|
||||
op(0xc8, "enter", immediateWord(), immediateByte(3));
|
||||
op(0xc9, "leave");
|
||||
op(0xca, "retf", immediateWord());
|
||||
op(0xcb, "retf");
|
||||
op(0xcc, "int", "0x3");
|
||||
op(0xcd, "int", immediateByte());
|
||||
op(0xce, "into");
|
||||
op(0xcf, "iret");
|
||||
op(0xd0, group2(), memoryByte(), "1");
|
||||
op(0xd1, group2(), memoryWord(), "1");
|
||||
op(0xd2, group2(), memoryByte(), "cl");
|
||||
op(0xd3, group2(), memoryWord(), "cl");
|
||||
op(0xd4, "aam", immediateByte());
|
||||
op(0xd5, "aad", immediateByte());
|
||||
op(0xd6, "xlat"); //undocumented mirror
|
||||
op(0xd7, "xlat");
|
||||
//op(0xd8);
|
||||
//op(0xd9);
|
||||
//op(0xda);
|
||||
//op(0xdb);
|
||||
//op(0xdc);
|
||||
//op(0xdd);
|
||||
//op(0xde);
|
||||
//op(0xdf);
|
||||
op(0xe0, "loopnz");
|
||||
op(0xe1, "loopz");
|
||||
op(0xe2, "loop");
|
||||
op(0xe3, "jcxz", relativeByte());
|
||||
op(0xe4, "in", "al", immediateByte());
|
||||
op(0xe5, "in", "ax", immediateWord());
|
||||
op(0xe6, "out", immediateByte(), "al");
|
||||
op(0xe7, "out", immediateWord(), "ax");
|
||||
op(0xe8, "call", relativeWord());
|
||||
op(0xe9, "jmp", relativeWord());
|
||||
op(0xea, "jmp", immediateLong());
|
||||
op(0xeb, "jmp", relativeByte());
|
||||
op(0xec, "in", "al", "dx");
|
||||
op(0xed, "in", "ax", "dx");
|
||||
op(0xee, "out", "dx", "al");
|
||||
op(0xef, "out", "dx", "ax");
|
||||
op(0xf0, "lock:");
|
||||
//op(0xf1);
|
||||
op(0xf2, "repnz:");
|
||||
op(0xf3, "repz:");
|
||||
op(0xf4, "hlt");
|
||||
op(0xf5, "cmc");
|
||||
op(0xf6, group3(), memoryByte(), immediateByte(modRM()));
|
||||
op(0xf7, group3(), memoryWord(), immediateWord(modRM()));
|
||||
op(0xf8, "clc");
|
||||
op(0xf9, "stc");
|
||||
op(0xfa, "cli");
|
||||
op(0xfb, "sti");
|
||||
op(0xfc, "cld");
|
||||
op(0xfd, "std");
|
||||
op(0xfe, group4(), memoryByte(), immediateByte(modRM()));
|
||||
op(0xff, group4(), memoryWord(), immediateWord(modRM()));
|
||||
default: output.append("??? ", hex(read(0), 2L)); break;
|
||||
}
|
||||
|
||||
#undef op
|
||||
|
||||
output.size(-48); //todo: determine the minimum value that will never clip here
|
||||
output.append(" ",
|
||||
" ax:", hex(r.ax, 4L),
|
||||
" bx:", hex(r.bx, 4L),
|
||||
" cx:", hex(r.cx, 4L),
|
||||
" dx:", hex(r.dx, 4L),
|
||||
" si:", hex(r.si, 4L),
|
||||
" di:", hex(r.di, 4L),
|
||||
" bp:", hex(r.bp, 4L),
|
||||
" sp:", hex(r.sp, 4L),
|
||||
" ip:", hex(r.ip, 4L),
|
||||
" cs:", hex(r.cs, 4L),
|
||||
" ds:", hex(r.ds, 4L),
|
||||
" es:", hex(r.es, 4L),
|
||||
" ss:", hex(r.ss, 4L), " ",
|
||||
r.f.m ? "M" : "m",
|
||||
r.f.v ? "V" : "v",
|
||||
r.f.d ? "D" : "d",
|
||||
r.f.i ? "I" : "i",
|
||||
r.f.b ? "B" : "b",
|
||||
r.f.s ? "S" : "s",
|
||||
r.f.z ? "Z" : "z",
|
||||
r.f.h ? "H" : "h",
|
||||
r.f.p ? "P" : "p",
|
||||
r.f.c ? "C" : "c"
|
||||
);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ auto V30MZ::instruction() -> void {
|
|||
op(0x0c, OrAccImm, Byte)
|
||||
op(0x0d, OrAccImm, Word)
|
||||
op(0x0e, PushReg, r.cs)
|
||||
//op(0x0f, ...) //pop cs
|
||||
op(0x0f, PopReg, r.cs)
|
||||
op(0x10, AdcMemReg, Byte)
|
||||
op(0x11, AdcMemReg, Word)
|
||||
op(0x12, AdcRegMem, Byte)
|
||||
|
@ -132,10 +132,10 @@ auto V30MZ::instruction() -> void {
|
|||
op(0x61, PopAll)
|
||||
op(0x62, Bound)
|
||||
//op(0x63, ...)
|
||||
//op(0x64, ...)
|
||||
//op(0x65, ...)
|
||||
//op(0x66, ...)
|
||||
//op(0x67, ...)
|
||||
//op(0x64, ...) repnc
|
||||
//op(0x65, ...) repc
|
||||
//op(0x66, ...) fpo2
|
||||
//op(0x67, ...) fpo2
|
||||
op(0x68, PushImm, Word)
|
||||
op(0x69, MultiplySignedRegMemImm, Word)
|
||||
op(0x6a, PushImm, Byte)
|
||||
|
@ -246,8 +246,8 @@ auto V30MZ::instruction() -> void {
|
|||
op(0xd3, Group2MemImm, Word, (uint8)r.cl)
|
||||
op(0xd4, AdjustAfterMultiply)
|
||||
op(0xd5, AdjustAfterDivide)
|
||||
//op(0xd6, ...)
|
||||
op(0xd7, Translate)
|
||||
op(0xd6, Translate) //xlat (undocumented mirror)
|
||||
op(0xd7, Translate) //xlat
|
||||
//op(0xd8, ...) //fpo1
|
||||
//op(0xd9, ...) //fpo1
|
||||
//op(0xda, ...) //fpo1
|
||||
|
@ -274,8 +274,8 @@ auto V30MZ::instruction() -> void {
|
|||
op(0xef, OutDX, Word)
|
||||
op(0xf0, Lock)
|
||||
//op(0xf1, ...)
|
||||
op(0xf2, Repeat, 0) //repnz
|
||||
op(0xf3, Repeat, 1) //repz
|
||||
op(0xf2, Repeat) //repnz
|
||||
op(0xf3, Repeat) //repz
|
||||
op(0xf4, Halt)
|
||||
op(0xf5, ComplementCarry)
|
||||
op(0xf6, Group3MemImm, Byte)
|
||||
|
|
|
@ -32,7 +32,8 @@ auto V30MZ::instructionAdjustAfterMultiply() -> void {
|
|||
wait(16);
|
||||
auto imm = fetch();
|
||||
if(imm == 0) return interrupt(0);
|
||||
r.ah = r.al / imm;
|
||||
//NEC CPUs do not honor the immediate and always use (base) 10
|
||||
r.ah = r.al / 10;
|
||||
r.al %= imm;
|
||||
r.f.p = parity(r.al);
|
||||
r.f.s = r.ax & 0x8000;
|
||||
|
@ -42,7 +43,8 @@ auto V30MZ::instructionAdjustAfterMultiply() -> void {
|
|||
auto V30MZ::instructionAdjustAfterDivide() -> void {
|
||||
wait(5);
|
||||
auto imm = fetch();
|
||||
r.al += r.ah * imm;
|
||||
//NEC CPUs do not honor the immediate and always use (base) 10
|
||||
r.al += r.ah * 10;
|
||||
r.ah = 0;
|
||||
r.f.p = parity(r.al);
|
||||
r.f.s = r.ax & 0x8000;
|
||||
|
|
|
@ -184,5 +184,9 @@ auto V30MZ::instructionPushImm(Size size) -> void {
|
|||
|
||||
auto V30MZ::instructionPopMem() -> void {
|
||||
modRM();
|
||||
setMem(Word, pop());
|
||||
auto data = pop();
|
||||
//NEC bug: pop into a register will adjust the stack, but fail to set the register properly
|
||||
//in practice, this isn't an issue as assemblers will favor one-byte pop instructions,
|
||||
//but this difference can be used to distinguish Intel x86 chips from NEC V20/V30 chips.
|
||||
if(modrm.mod != 3) setMem(Word, data);
|
||||
}
|
||||
|
|
|
@ -40,8 +40,8 @@ auto V30MZ::instructionGroup3MemImm(Size size) -> void {
|
|||
modRM();
|
||||
auto mem = getMem(size);
|
||||
switch(modrm.reg) {
|
||||
case 0: AND(size, mem, fetch(size)); break;
|
||||
case 1: warning("[V30MZ] GRP3.1"); break;
|
||||
case 0: AND(size, mem, fetch(size)); break; //test
|
||||
case 1: AND(size, mem, fetch(size)); break; //test (undocumented mirror)
|
||||
case 2: wait(2); setMem(size, NOT(size, mem)); break;
|
||||
case 3: wait(2); setMem(size, NEG(size, mem)); break;
|
||||
case 4: wait(2); setAcc(size * 2, MUL(size, getAcc(size), mem)); break;
|
||||
|
@ -54,46 +54,42 @@ auto V30MZ::instructionGroup3MemImm(Size size) -> void {
|
|||
auto V30MZ::instructionGroup4MemImm(Size size) -> void {
|
||||
modRM();
|
||||
switch(modrm.reg) {
|
||||
case 0:
|
||||
case 0: //inc
|
||||
wait(2);
|
||||
setMem(size, INC(size, getMem(size)));
|
||||
break;
|
||||
case 1:
|
||||
case 1: //dec
|
||||
wait(2);
|
||||
setMem(size, DEC(size, getMem(size)));
|
||||
break;
|
||||
case 2:
|
||||
if(size == Byte) { warning("[V30MZ] GRP4.2"); break; }
|
||||
case 2: //call
|
||||
wait(5);
|
||||
push(r.ip);
|
||||
r.ip = getMem(Word);
|
||||
break;
|
||||
case 3:
|
||||
if(size == Byte) { warning("[V30MZ] GRP4.3"); break; }
|
||||
case 3: //callf
|
||||
wait(11);
|
||||
push(r.cs);
|
||||
push(r.ip);
|
||||
r.ip = getMem(Word, 0);
|
||||
r.cs = getMem(Word, 2);
|
||||
break;
|
||||
case 4:
|
||||
if(size == Byte) { warning("[V30MZ] GRP4.4"); break; }
|
||||
case 4: //jmp
|
||||
wait(4);
|
||||
r.ip = getMem(Word);
|
||||
break;
|
||||
case 5:
|
||||
if(size == Byte) { warning("[V30MZ] GRP4.5"); break; }
|
||||
case 5: //jmpf
|
||||
wait(9);
|
||||
r.ip = getMem(Word, 0);
|
||||
r.cs = getMem(Word, 2);
|
||||
break;
|
||||
case 6:
|
||||
if(size == Byte) { warning("[V30MZ] GRP4.6"); break; }
|
||||
case 6: //push
|
||||
wait(1);
|
||||
push(getMem(Word));
|
||||
break;
|
||||
case 7:
|
||||
warning("[V30MZ] GRP4.7");
|
||||
case 7: //push (undocumented mirror)
|
||||
wait(1);
|
||||
push(getMem(Word));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ auto V30MZ::instructionSegment(uint16 segment) -> void {
|
|||
state.poll = false;
|
||||
}
|
||||
|
||||
auto V30MZ::instructionRepeat(bool flag) -> void {
|
||||
auto V30MZ::instructionRepeat() -> void {
|
||||
if(prefixes.size() >= 7) prefixes.removeRight();
|
||||
prefixes.prepend(opcode);
|
||||
wait(4);
|
||||
|
|
|
@ -51,8 +51,8 @@ auto V30MZ::instructionCompareString(Size size) -> void {
|
|||
SUB(size, x, y);
|
||||
|
||||
if(!repeat() || !--r.cx) return;
|
||||
if(repeat() == RepeatWhileZero && r.f.z == 0) return;
|
||||
if(repeat() == RepeatWhileNotZero && r.f.z == 1) return;
|
||||
if(repeat() == RepeatWhileZeroLo && r.f.z == 1) return;
|
||||
if(repeat() == RepeatWhileZeroHi && r.f.z == 0) return;
|
||||
|
||||
state.prefix = true;
|
||||
r.ip--;
|
||||
|
@ -94,8 +94,8 @@ auto V30MZ::instructionScanString(Size size) -> void {
|
|||
SUB(size, x, y);
|
||||
|
||||
if(!repeat() || !--r.cx) return;
|
||||
if(repeat() == RepeatWhileZero && r.f.z == 0) return;
|
||||
if(repeat() == RepeatWhileNotZero && r.f.z == 1) return;
|
||||
if(repeat() == RepeatWhileZeroLo && r.f.z == 1) return;
|
||||
if(repeat() == RepeatWhileZeroHi && r.f.z == 0) return;
|
||||
|
||||
state.prefix = true;
|
||||
r.ip--;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
auto V30MZ::repeat() -> uint8 {
|
||||
for(auto prefix : prefixes) {
|
||||
if(prefix == RepeatWhileZero) return prefix;
|
||||
if(prefix == RepeatWhileNotZero) return prefix;
|
||||
if(prefix == RepeatWhileZeroLo) return prefix;
|
||||
if(prefix == RepeatWhileZeroHi) return prefix;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ namespace Processor {
|
|||
#include "instructions-move.cpp"
|
||||
#include "instructions-string.cpp"
|
||||
#include "serialization.cpp"
|
||||
#include "disassembler.cpp"
|
||||
|
||||
auto V30MZ::warning(string text) -> void {
|
||||
//print(text, "\n");
|
||||
|
@ -54,4 +53,7 @@ auto V30MZ::exec() -> void {
|
|||
if(!state.prefix) prefixes.reset();
|
||||
}
|
||||
|
||||
#undef bits
|
||||
#include "disassembler.cpp"
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,44 @@
|
|||
//NEC V30MZ
|
||||
//NEC V30MZ (reduced functionality NEC V30 for embedded use)
|
||||
|
||||
//V30 missing instructions:
|
||||
// 0f 10,11,18,19 test1
|
||||
// 0f 12,13,1a,1b clr1
|
||||
// 0f 14,15,1c,1d set1
|
||||
// 0f 16,17,1e,1f not1
|
||||
// 0f 20 add4s
|
||||
// 0f 22 sub4s
|
||||
// 0f 26 cmp4s
|
||||
// 0f 28 rol4
|
||||
// 0f 2a ror4
|
||||
// 0f 31,39 ins
|
||||
// 0f 33,3b ext
|
||||
// 0f ff brkem (8080 emulation mode) [retem, calln]
|
||||
// 64 repnc
|
||||
// 65 repc
|
||||
// 66,67 fpo2
|
||||
// d8-df fpo1
|
||||
|
||||
//x86 variant instructions:
|
||||
// 8f c0-c7 pop reg [CPU bug: pops from stack; fails to set register]
|
||||
// d4 xx aam [ignores the immediate; always uses (base) 10]
|
||||
// d5 xx aad [ignores the immediate; always uses (base) 10]
|
||||
// d6 xlat (mirror of d7) [this is salc on x86 CPUs]
|
||||
// f1 ??? [this is int 0x1 on x86 CPUs; said to be a two-byte NOP on V20; unknown on V30/V30MZ]
|
||||
// ff f8-ff push (mirror of ff f0-f7)
|
||||
|
||||
//x86 unemulated variation:
|
||||
// after interrupts, NEC V20/V30 CPUs resume string instructions with prefixes intact. unlike x86 CPUs
|
||||
// I need more information on this behavior in order to emulate it ...
|
||||
// also, the opcode f1 behavior is not currently known
|
||||
|
||||
//V30 opcode prefix functionality:
|
||||
// there is a seven-level stack for opcode prefixes. once full, older prefixes are pushed off the stack
|
||||
|
||||
//other notes:
|
||||
// 0f pop cs (not nop) [on the V20; the V30 uses this for instruction extensions; unsure on the V30MZ]
|
||||
// 8e xx mov cs,modRM (works as expected; able to set CS)
|
||||
|
||||
//I currently emulate opcode 0f as pop cs, although it's unknown if that is correct.
|
||||
|
||||
#pragma once
|
||||
|
||||
|
@ -13,8 +53,8 @@ struct V30MZ {
|
|||
SegmentOverrideSS = 0x36,
|
||||
SegmentOverrideDS = 0x3e,
|
||||
Lock = 0xf0,
|
||||
RepeatWhileNotZero = 0xf2,
|
||||
RepeatWhileZero = 0xf3,
|
||||
RepeatWhileZeroLo = 0xf2,
|
||||
RepeatWhileZeroHi = 0xf3,
|
||||
};
|
||||
|
||||
virtual auto wait(uint clocks = 1) -> void = 0;
|
||||
|
@ -169,7 +209,7 @@ struct V30MZ {
|
|||
|
||||
//instructions-misc.cpp
|
||||
auto instructionSegment(uint16) -> void;
|
||||
auto instructionRepeat(bool) -> void;
|
||||
auto instructionRepeat() -> void;
|
||||
auto instructionLock() -> void;
|
||||
auto instructionWait() -> void;
|
||||
auto instructionHalt() -> void;
|
||||
|
@ -209,7 +249,8 @@ struct V30MZ {
|
|||
auto serialize(serializer&) -> void;
|
||||
|
||||
//disassembler.cpp
|
||||
auto disassemble(uint16 cs, uint16 ip, bool registers = true, bool bytes = true) -> string;
|
||||
auto disassemble() -> string;
|
||||
auto disassemble(uint16 cs, uint16 ip) -> string;
|
||||
|
||||
struct State {
|
||||
bool halt; //set to true for hlt instruction; blocks execution until next interrupt
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
auto APU::Channel5::run() -> void {
|
||||
int11 output = (int8)s.data;
|
||||
int11 output;
|
||||
switch(r.scale) {
|
||||
case 0: output <<= 3 - r.volume; break;
|
||||
case 1: output <<= 3 - r.volume; output |= -0x100 << (3 - r.volume); break;
|
||||
case 2: output <<= 3 - r.volume; break;
|
||||
case 3: output <<= 3; break;
|
||||
case 0: output = (uint8)s.data << 3 - r.volume; break;
|
||||
case 1: output = (uint8)s.data - 0x100 << 3 - r.volume; break;
|
||||
case 2: output = (int8)s.data << 3 - r.volume; break;
|
||||
case 3: output = (uint8)s.data << 3; break;
|
||||
}
|
||||
|
||||
o.left = r.leftEnable ? output : (int11)0;
|
||||
o.left = r.leftEnable ? output : (int11)0;
|
||||
o.right = r.rightEnable ? output : (int11)0;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ auto CPU::Enter() -> void {
|
|||
|
||||
auto CPU::main() -> void {
|
||||
poll();
|
||||
//static uint c=0;if(auto d = disassemble()) if(++c<60) print(d, "\n");
|
||||
exec();
|
||||
}
|
||||
|
||||
|
|
|
@ -105,7 +105,7 @@ auto CPU::portWrite(uint16 addr, uint8 data) -> void {
|
|||
|
||||
//DMA_CTRL
|
||||
if(addr == 0x0048) {
|
||||
r.dmaMode = data.bit(0);
|
||||
r.dmaMode = data.bit(6);
|
||||
r.dmaEnable = data.bit(7);
|
||||
if(r.dmaEnable) dmaTransfer();
|
||||
}
|
||||
|
|
|
@ -13,17 +13,14 @@ auto InternalRAM::serialize(serializer& s) -> void {
|
|||
s.array(memory, Model::WonderSwan() || Model::PocketChallengeV2() ? 0x4000 : 0x10000);
|
||||
}
|
||||
|
||||
auto InternalRAM::read(uint16 addr, uint size) -> uint32 {
|
||||
if(size == Long) return read(addr + 0, Word) << 0 | read(addr + 2, Word) << 16;
|
||||
if(size == Word) return read(addr + 0, Byte) << 0 | read(addr + 1, Byte) << 8;
|
||||
|
||||
if(addr >= 0x4000 && !system.color()) return 0x90;
|
||||
return memory[addr];
|
||||
auto InternalRAM::read(uint16 address) -> uint8 {
|
||||
if(address >= 0x4000 && !system.color()) return 0x90;
|
||||
return memory[address];
|
||||
}
|
||||
|
||||
auto InternalRAM::write(uint16 addr, uint8 data) -> void {
|
||||
if(addr >= 0x4000 && !system.color()) return;
|
||||
memory[addr] = data;
|
||||
auto InternalRAM::write(uint16 address, uint8 data) -> void {
|
||||
if(address >= 0x4000 && !system.color()) return;
|
||||
memory[address] = data;
|
||||
}
|
||||
|
||||
auto Bus::power() -> void {
|
||||
|
|
|
@ -7,8 +7,29 @@ struct InternalRAM {
|
|||
auto power() -> void;
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
auto read(uint16 addr, uint size = Byte) -> uint32;
|
||||
auto write(uint16 addr, uint8 data) -> void;
|
||||
auto read(uint16 address) -> uint8;
|
||||
auto write(uint16 address, uint8 data) -> void;
|
||||
|
||||
//PPU byte reads only:
|
||||
//WS: address is always < 0x4000
|
||||
alwaysinline auto read8(uint16 address) const -> uint16 {
|
||||
return memory[address];
|
||||
}
|
||||
|
||||
//PPU word reads only:
|
||||
//address & 1 is always 0
|
||||
//WS: address is always < 0x4000
|
||||
alwaysinline auto read16(uint16 address) const -> uint16 {
|
||||
return memory[address + 0] << 0 | memory[address + 1] << 8;
|
||||
}
|
||||
|
||||
//PPU long reads only:
|
||||
//address & 3 is always 0
|
||||
//WS: address is always < 0x4000
|
||||
alwaysinline auto read32(uint16 address) const -> uint32 {
|
||||
return memory[address + 0] << 0 | memory[address + 1] << 8
|
||||
| memory[address + 2] << 16 | memory[address + 3] << 24;
|
||||
}
|
||||
|
||||
private:
|
||||
uint8 memory[65536];
|
||||
|
|
|
@ -15,7 +15,8 @@ auto PPU::portRead(uint16 addr) -> uint8 {
|
|||
);
|
||||
|
||||
//LINE_CUR
|
||||
if(addr == 0x0002) return s.vclk;
|
||||
//todo: unknown if this is vtime or vtime%(vtotal+1)
|
||||
if(addr == 0x0002) return s.vtime;
|
||||
|
||||
//LINE_CMP
|
||||
if(addr == 0x0003) return r.lineCompare;
|
||||
|
@ -93,8 +94,8 @@ auto PPU::portRead(uint16 addr) -> uint8 {
|
|||
//LCD_VTOTAL
|
||||
if(addr == 0x0016) return r.vtotal;
|
||||
|
||||
//LCD_VBLANK
|
||||
if(addr == 0x0017) return r.vblank;
|
||||
//LCD_VSYNC
|
||||
if(addr == 0x0017) return r.vsync;
|
||||
|
||||
//PALMONO_POOL
|
||||
if(addr >= 0x001c && addr <= 0x001f) return (
|
||||
|
@ -232,8 +233,8 @@ auto PPU::portWrite(uint16 addr, uint8 data) -> void {
|
|||
//LCD_VTOTAL
|
||||
if(addr == 0x0016) r.vtotal = data;
|
||||
|
||||
//LCD_VBLANK
|
||||
if(addr == 0x0017) r.vblank = data;
|
||||
//LCD_VSYNC
|
||||
if(addr == 0x0017) r.vsync = data;
|
||||
|
||||
//PALMONO_POOL
|
||||
if(addr >= 0x001c && addr <= 0x001f) {
|
||||
|
|
|
@ -25,12 +25,12 @@ auto PPU::latchRegisters() -> void {
|
|||
l.spriteWindowY1 = r.spriteWindowY1;
|
||||
}
|
||||
|
||||
auto PPU::latchSprites() -> void {
|
||||
auto PPU::latchSprites(uint8 y) -> void {
|
||||
l.spriteCount = 0;
|
||||
if(!l.spriteEnable) return;
|
||||
for(auto index : range(l.oamCount)) {
|
||||
for(uint index : range(l.oamCount)) {
|
||||
uint32 attributes = l.oam[!s.field][index];
|
||||
if((uint8)(s.vclk - attributes.bits(16,23)) > 7) continue;
|
||||
if((uint8)(y - attributes.bits(16,23)) > 7) continue;
|
||||
l.sprite[l.spriteCount] = attributes;
|
||||
if(++l.spriteCount >= 32) break;
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ auto PPU::latchOAM() -> void {
|
|||
uint8 spriteCount = min(128, (uint)r.spriteCount);
|
||||
uint16 spriteBase = r.spriteBase.bits(0, 4 + system.depth()) << 9;
|
||||
l.oamCount = spriteCount;
|
||||
for(auto index : range(spriteCount)) {
|
||||
l.oam[s.field][index] = iram.read(spriteBase + (spriteIndex++ << 2), Long);
|
||||
for(uint index : range(spriteCount)) {
|
||||
l.oam[s.field][index] = iram.read32(spriteBase + (spriteIndex++ << 2));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,22 +13,24 @@ auto PPU::Enter() -> void {
|
|||
}
|
||||
|
||||
auto PPU::main() -> void {
|
||||
if(s.vclk == 142) {
|
||||
if(s.vtime == 142) {
|
||||
latchOAM();
|
||||
}
|
||||
|
||||
if(s.vclk < 144) {
|
||||
if(s.vtime < 144) {
|
||||
uint y = s.vtime % (r.vtotal + 1);
|
||||
auto output = this->output + y * 224;
|
||||
latchRegisters();
|
||||
latchSprites();
|
||||
for(auto x : range(224)) {
|
||||
latchSprites(y);
|
||||
for(uint x : range(224)) {
|
||||
s.pixel = {Pixel::Source::Back, 0x000};
|
||||
if(r.lcdEnable) {
|
||||
renderBack();
|
||||
if(l.screenOneEnable) renderScreenOne();
|
||||
if(l.screenTwoEnable) renderScreenTwo();
|
||||
if(l.spriteEnable) renderSprite();
|
||||
if(l.screenOneEnable) renderScreenOne(x, y);
|
||||
if(l.screenTwoEnable) renderScreenTwo(x, y);
|
||||
if(l.spriteEnable) renderSprite(x, y);
|
||||
}
|
||||
output[s.vclk * 224 + x] = s.pixel.color;
|
||||
*output++ = s.pixel.color;
|
||||
step(1);
|
||||
}
|
||||
step(32);
|
||||
|
@ -48,13 +50,14 @@ auto PPU::main() -> void {
|
|||
}
|
||||
}
|
||||
|
||||
//vtotal+1 = scanlines per frame
|
||||
//vtotal<143 inhibits vblank and repeats the screen image until vtime=144
|
||||
//todo: unknown how votal<143 interferes with line compare interrupts
|
||||
auto PPU::scanline() -> void {
|
||||
s.hclk = 0;
|
||||
if(++s.vclk == 159) frame();
|
||||
if(s.vclk == r.lineCompare) {
|
||||
cpu.raise(CPU::Interrupt::LineCompare);
|
||||
}
|
||||
if(s.vclk == 144) {
|
||||
s.vtime++;
|
||||
if(s.vtime >= max(144, r.vtotal + 1)) return frame();
|
||||
if(s.vtime == r.lineCompare) cpu.raise(CPU::Interrupt::LineCompare);
|
||||
if(s.vtime == 144) {
|
||||
cpu.raise(CPU::Interrupt::Vblank);
|
||||
if(r.vtimerEnable && r.vtimerCounter < r.vtimerFrequency) {
|
||||
if(++r.vtimerCounter == r.vtimerFrequency) {
|
||||
|
@ -71,7 +74,7 @@ auto PPU::scanline() -> void {
|
|||
|
||||
auto PPU::frame() -> void {
|
||||
s.field = !s.field;
|
||||
s.vclk = 0;
|
||||
s.vtime = 0;
|
||||
scheduler.exit(Scheduler::Event::Frame);
|
||||
}
|
||||
|
||||
|
@ -80,8 +83,6 @@ auto PPU::refresh() -> void {
|
|||
}
|
||||
|
||||
auto PPU::step(uint clocks) -> void {
|
||||
s.hclk += clocks;
|
||||
|
||||
Thread::step(clocks);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ struct PPU : Thread, IO {
|
|||
|
||||
//latch.cpp
|
||||
auto latchRegisters() -> void;
|
||||
auto latchSprites() -> void;
|
||||
auto latchSprites(uint8 y) -> void;
|
||||
auto latchOAM() -> void;
|
||||
|
||||
//render.cpp
|
||||
|
@ -21,9 +21,9 @@ struct PPU : Thread, IO {
|
|||
auto renderTransparent(bool palette, uint4 color) -> bool;
|
||||
auto renderPalette(uint4 palette, uint4 color) -> uint12;
|
||||
auto renderBack() -> void;
|
||||
auto renderScreenOne() -> void;
|
||||
auto renderScreenTwo() -> void;
|
||||
auto renderSprite() -> void;
|
||||
auto renderScreenOne(uint8 x, uint8 y) -> void;
|
||||
auto renderScreenTwo(uint8 x, uint8 y) -> void;
|
||||
auto renderSprite(uint8 x, uint8 y) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
@ -38,9 +38,8 @@ struct PPU : Thread, IO {
|
|||
uint32 output[224 * 144];
|
||||
|
||||
struct State {
|
||||
bool field = 0;
|
||||
uint vclk = 0;
|
||||
uint hclk = 0;
|
||||
uint1 field = 0;
|
||||
uint8 vtime = 0;
|
||||
Pixel pixel;
|
||||
} s;
|
||||
|
||||
|
@ -160,8 +159,8 @@ struct PPU : Thread, IO {
|
|||
//$0016 LCD_VTOTAL
|
||||
uint8 vtotal = 158;
|
||||
|
||||
//$0017 LCD_VBLANK
|
||||
uint8 vblank = 155;
|
||||
//$0017 LCD_VSYNC
|
||||
uint8 vsync = 155;
|
||||
|
||||
//$001c-001f PALMONO_POOL
|
||||
uint4 pool[8];
|
||||
|
|
|
@ -4,11 +4,11 @@ auto PPU::renderFetch(uint10 tile, uint3 y, uint3 x) -> uint4 {
|
|||
|
||||
if(system.planar()) {
|
||||
if(!system.depth()) {
|
||||
uint16 data = iram.read(offset + (y << 1), Word);
|
||||
uint16 data = iram.read16(offset + (y << 1));
|
||||
color |= data.bit( 7 - x) << 0;
|
||||
color |= data.bit(15 - x) << 1;
|
||||
} else {
|
||||
uint32 data = iram.read(offset + (y << 2), Long);
|
||||
uint32 data = iram.read32(offset + (y << 2));
|
||||
color |= data.bit( 7 - x) << 0;
|
||||
color |= data.bit(15 - x) << 1;
|
||||
color |= data.bit(23 - x) << 2;
|
||||
|
@ -18,11 +18,11 @@ auto PPU::renderFetch(uint10 tile, uint3 y, uint3 x) -> uint4 {
|
|||
|
||||
if(system.packed()) {
|
||||
if(!system.depth()) {
|
||||
uint8 data = iram.read(offset + (y << 1) + (x >> 2));
|
||||
uint8 data = iram.read8(offset + (y << 1) + (x >> 2));
|
||||
color = data >> (6 - (x.bits(0,1) << 1));
|
||||
color = color.bits(0,1);
|
||||
} else {
|
||||
uint8 data = iram.read(offset + (y << 2) + (x >> 1));
|
||||
uint8 data = iram.read8(offset + (y << 2) + (x >> 1));
|
||||
color = data >> (4 - (x.bit(0) << 2));
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ auto PPU::renderPalette(uint4 palette, uint4 color) -> uint12 {
|
|||
uint4 poolColor = 15 - r.pool[paletteColor];
|
||||
return poolColor << 0 | poolColor << 4 | poolColor << 8;
|
||||
} else {
|
||||
return iram.read(0xfe00 + (palette << 5) + (color << 1), Word);
|
||||
return iram.read16(0xfe00 + (palette << 5) + (color << 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,60 +51,60 @@ auto PPU::renderBack() -> void {
|
|||
uint4 poolColor = 15 - r.pool[l.backColor.bits(0,2)];
|
||||
s.pixel = {Pixel::Source::Back, poolColor << 0 | poolColor << 4 | poolColor << 8};
|
||||
} else {
|
||||
uint12 color = iram.read(0xfe00 + (l.backColor << 1), Word);
|
||||
uint12 color = iram.read16(0xfe00 + (l.backColor << 1));
|
||||
s.pixel = {Pixel::Source::Back, color};
|
||||
}
|
||||
}
|
||||
|
||||
auto PPU::renderScreenOne() -> void {
|
||||
uint8 scrollY = s.vclk + l.scrollOneY;
|
||||
uint8 scrollX = s.hclk + l.scrollOneX;
|
||||
auto PPU::renderScreenOne(uint8 x, uint8 y) -> void {
|
||||
uint8 scrollY = y + l.scrollOneY;
|
||||
uint8 scrollX = x + l.scrollOneX;
|
||||
|
||||
uint16 tilemapOffset = l.screenOneMapBase.bits(0, 2 + system.depth()) << 11;
|
||||
tilemapOffset += (scrollY >> 3) << 6;
|
||||
tilemapOffset += (scrollX >> 3) << 1;
|
||||
|
||||
uint16 tile = iram.read(tilemapOffset, Word);
|
||||
uint16 tile = iram.read16(tilemapOffset);
|
||||
uint3 tileY = scrollY ^ tile.bit(15) * 7;
|
||||
uint3 tileX = scrollX ^ tile.bit(14) * 7;
|
||||
uint4 tileColor = renderFetch(tile.bit(13) << 9 | tile.bits(0,8), tileY, tileX);
|
||||
uint4 tileColor = renderFetch((tile.bit(13) & system.depth()) << 9 | tile.bits(0,8), tileY, tileX);
|
||||
if(renderTransparent(tile.bit(11), tileColor)) return;
|
||||
|
||||
s.pixel = {Pixel::Source::ScreenOne, renderPalette(tile.bits(9,12), tileColor)};
|
||||
}
|
||||
|
||||
auto PPU::renderScreenTwo() -> void {
|
||||
bool windowInside = s.vclk >= l.screenTwoWindowY0 && s.vclk <= l.screenTwoWindowY1
|
||||
&& s.hclk >= l.screenTwoWindowX0 && s.hclk <= l.screenTwoWindowX1;
|
||||
auto PPU::renderScreenTwo(uint8 x, uint8 y) -> void {
|
||||
bool windowInside = y >= l.screenTwoWindowY0 && y <= l.screenTwoWindowY1
|
||||
&& x >= l.screenTwoWindowX0 && x <= l.screenTwoWindowX1;
|
||||
windowInside ^= l.screenTwoWindowInvert;
|
||||
if(l.screenTwoWindowEnable && !windowInside) return;
|
||||
|
||||
uint8 scrollY = s.vclk + l.scrollTwoY;
|
||||
uint8 scrollX = s.hclk + l.scrollTwoX;
|
||||
uint8 scrollY = y + l.scrollTwoY;
|
||||
uint8 scrollX = x + l.scrollTwoX;
|
||||
|
||||
uint16 tilemapOffset = l.screenTwoMapBase.bits(0, 2 + system.depth()) << 11;
|
||||
tilemapOffset += (scrollY >> 3) << 6;
|
||||
tilemapOffset += (scrollX >> 3) << 1;
|
||||
|
||||
uint16 tile = iram.read(tilemapOffset, Word);
|
||||
uint16 tile = iram.read16(tilemapOffset);
|
||||
uint3 tileY = scrollY ^ tile.bit(15) * 7;
|
||||
uint3 tileX = scrollX ^ tile.bit(14) * 7;
|
||||
uint4 tileColor = renderFetch(tile.bit(13) << 9 | tile.bits(0,8), tileY, tileX);
|
||||
uint4 tileColor = renderFetch((tile.bit(13) & system.depth()) << 9 | tile.bits(0,8), tileY, tileX);
|
||||
if(renderTransparent(tile.bit(11), tileColor)) return;
|
||||
|
||||
s.pixel = {Pixel::Source::ScreenTwo, renderPalette(tile.bits(9,12), tileColor)};
|
||||
}
|
||||
|
||||
auto PPU::renderSprite() -> void {
|
||||
bool windowInside = s.vclk >= l.spriteWindowY0 && s.vclk <= l.spriteWindowY1
|
||||
&& s.hclk >= l.spriteWindowX0 && s.hclk <= l.spriteWindowX1;
|
||||
auto PPU::renderSprite(uint8 x, uint8 y) -> void {
|
||||
bool windowInside = y >= l.spriteWindowY0 && y <= l.spriteWindowY1
|
||||
&& x >= l.spriteWindowX0 && x <= l.spriteWindowX1;
|
||||
for(auto index : range(l.spriteCount)) {
|
||||
auto sprite = l.sprite[index];
|
||||
if(l.spriteWindowEnable && sprite.bit(12) == windowInside) continue;
|
||||
if((uint8)(s.hclk - sprite.bits(24,31)) > 7) continue;
|
||||
if((uint8)(x - sprite.bits(24,31)) > 7) continue;
|
||||
|
||||
uint3 tileY = (s.vclk - sprite.bits(16,23)) ^ sprite.bit(15) * 7;
|
||||
uint3 tileX = (s.hclk - sprite.bits(24,31)) ^ sprite.bit(14) * 7;
|
||||
uint3 tileY = (y - sprite.bits(16,23)) ^ sprite.bit(15) * 7;
|
||||
uint3 tileX = (x - sprite.bits(24,31)) ^ sprite.bit(14) * 7;
|
||||
uint4 tileColor = renderFetch(sprite.bits(0,8), tileY, tileX);
|
||||
if(renderTransparent(sprite.bit(11), tileColor)) continue;
|
||||
if(!sprite.bit(13) && s.pixel.source == Pixel::Source::ScreenTwo) continue;
|
||||
|
|
|
@ -2,8 +2,7 @@ auto PPU::serialize(serializer& s) -> void {
|
|||
Thread::serialize(s);
|
||||
|
||||
s.integer(this->s.field);
|
||||
s.integer(this->s.vclk);
|
||||
s.integer(this->s.hclk);
|
||||
s.integer(this->s.vtime);
|
||||
s.integer((uint&)this->s.pixel.source);
|
||||
s.integer(this->s.pixel.color);
|
||||
|
||||
|
@ -70,7 +69,7 @@ auto PPU::serialize(serializer& s) -> void {
|
|||
s.integer(r.iconAux2);
|
||||
s.integer(r.iconAux3);
|
||||
s.integer(r.vtotal);
|
||||
s.integer(r.vblank);
|
||||
s.integer(r.vsync);
|
||||
s.array(r.pool);
|
||||
for(uint n : range(16)) s.array(r.palette[n].color);
|
||||
s.integer(r.htimerEnable);
|
||||
|
|
|
@ -12,6 +12,9 @@ auto pSizable::minimumSize() const -> Size {
|
|||
return {0, 0};
|
||||
}
|
||||
|
||||
auto pSizable::setCollapsible(bool collapsible) -> void {
|
||||
}
|
||||
|
||||
auto pSizable::setGeometry(Geometry geometry) -> void {
|
||||
self().doSize();
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ struct pSizable : pObject {
|
|||
Declare(Sizable, Object)
|
||||
|
||||
virtual auto minimumSize() const -> Size;
|
||||
virtual auto setCollapsible(bool collapsible) -> void;
|
||||
virtual auto setGeometry(Geometry geometry) -> void;
|
||||
};
|
||||
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
}
|
||||
|
||||
-(void) run:(NSTimer*)instance {
|
||||
if(Application::state().quit) return;
|
||||
|
||||
if(timer->enabled()) {
|
||||
timer->doActivate();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
#if defined(Hiro_Label)
|
||||
|
||||
//todo:
|
||||
//* Label::onButtonPress()
|
||||
//* Label::onButtonRelease()
|
||||
|
||||
@implementation CocoaLabel : NSTextView
|
||||
|
||||
-(id) initWith:(hiro::mLabel&)labelReference {
|
||||
|
|
|
@ -106,3 +106,7 @@
|
|||
#if defined(Hiro_Button) && defined(Hiro_ComboButton) && defined(Hiro_LineEdit) && defined(Hiro_ListView) && defined(Hiro_MessageDialog)
|
||||
#define Hiro_BrowserDialog
|
||||
#endif
|
||||
|
||||
#if defined(Hiro_Label)
|
||||
#define Hiro_AboutDialog
|
||||
#endif
|
||||
|
|
|
@ -72,6 +72,14 @@ auto Color::setRed(signed red) -> type& {
|
|||
return *this;
|
||||
}
|
||||
|
||||
auto Color::setValue(uint32_t value) -> type& {
|
||||
state.alpha = value >> 24 & 255;
|
||||
state.red = value >> 16 & 255;
|
||||
state.green = value >> 8 & 255;
|
||||
state.blue = value >> 0 & 255;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto Color::value() const -> uint32_t {
|
||||
return state.alpha << 24 | state.red << 16 | state.green << 8 | state.blue << 0;
|
||||
}
|
||||
|
|
|
@ -121,6 +121,7 @@ struct Color {
|
|||
auto setColor(int red, int green, int blue, int alpha = 255) -> type&;
|
||||
auto setGreen(int green) -> type&;
|
||||
auto setRed(int red) -> type&;
|
||||
auto setValue(uint32_t value) -> type&;
|
||||
auto value() const -> uint32_t;
|
||||
|
||||
//private:
|
||||
|
@ -180,29 +181,7 @@ struct Alignment {
|
|||
};
|
||||
#endif
|
||||
|
||||
#if defined(Hiro_Cursor)
|
||||
struct Cursor {
|
||||
using type = Cursor;
|
||||
|
||||
Cursor(int offset = 0, int length = 0);
|
||||
|
||||
explicit operator bool() const;
|
||||
auto operator==(const Cursor& source) const -> bool;
|
||||
auto operator!=(const Cursor& source) const -> bool;
|
||||
|
||||
auto length() const -> int;
|
||||
auto offset() const -> int;
|
||||
auto setCursor(int offset = 0, int length = 0) -> type&;
|
||||
auto setLength(int length = 0) -> type&;
|
||||
auto setOffset(int offset = 0) -> type&;
|
||||
|
||||
//private:
|
||||
struct State {
|
||||
int offset;
|
||||
int length;
|
||||
} state;
|
||||
};
|
||||
#endif
|
||||
#include "cursor.hpp"
|
||||
|
||||
#if defined(Hiro_Position)
|
||||
struct Position {
|
||||
|
@ -1063,28 +1042,7 @@ struct mIconViewItem : mObject {
|
|||
};
|
||||
#endif
|
||||
|
||||
#if defined(Hiro_Label)
|
||||
struct mLabel : mWidget {
|
||||
Declare(Label)
|
||||
|
||||
auto alignment() const -> Alignment;
|
||||
auto backgroundColor() const -> Color;
|
||||
auto foregroundColor() const -> Color;
|
||||
auto setAlignment(Alignment alignment = {}) -> type&;
|
||||
auto setBackgroundColor(Color color = {}) -> type&;
|
||||
auto setForegroundColor(Color color = {}) -> type&;
|
||||
auto setText(const string& text = "") -> type&;
|
||||
auto text() const -> string;
|
||||
|
||||
//private:
|
||||
struct State {
|
||||
Alignment alignment;
|
||||
Color backgroundColor;
|
||||
Color foregroundColor;
|
||||
string text;
|
||||
} state;
|
||||
};
|
||||
#endif
|
||||
#include "widget/label.hpp"
|
||||
|
||||
#if defined(Hiro_LineEdit)
|
||||
struct mLineEdit : mWidget {
|
||||
|
@ -1227,91 +1185,8 @@ struct mTextEdit : mWidget {
|
|||
};
|
||||
#endif
|
||||
|
||||
#if defined(Hiro_TreeView)
|
||||
struct mTreeView : mWidget {
|
||||
Declare(TreeView)
|
||||
using mObject::remove;
|
||||
|
||||
auto append(sTreeViewItem item) -> type&;
|
||||
auto backgroundColor() const -> Color;
|
||||
auto doActivate() const -> void;
|
||||
auto doChange() const -> void;
|
||||
auto doContext() const -> void;
|
||||
auto doToggle(sTreeViewItem item) const -> void;
|
||||
auto foregroundColor() const -> Color;
|
||||
auto item(const string& path) const -> TreeViewItem;
|
||||
auto itemCount() const -> uint;
|
||||
auto items() const -> vector<TreeViewItem>;
|
||||
auto onActivate(const function<void ()>& callback = {}) -> type&;
|
||||
auto onChange(const function<void ()>& callback = {}) -> type&;
|
||||
auto onContext(const function<void ()>& callback = {}) -> type&;
|
||||
auto onToggle(const function<void (sTreeViewItem)>& callback = {}) -> type&;
|
||||
auto remove(sTreeViewItem item) -> type&;
|
||||
auto reset() -> type&;
|
||||
auto selected() const -> TreeViewItem;
|
||||
auto setBackgroundColor(Color color = {}) -> type&;
|
||||
auto setForegroundColor(Color color = {}) -> type&;
|
||||
auto setParent(mObject* parent = nullptr, int offset = -1) -> type&;
|
||||
|
||||
//private:
|
||||
struct State {
|
||||
Color backgroundColor;
|
||||
Color foregroundColor;
|
||||
vector<sTreeViewItem> items;
|
||||
function<void ()> onActivate;
|
||||
function<void ()> onChange;
|
||||
function<void ()> onContext;
|
||||
function<void (sTreeViewItem)> onToggle;
|
||||
string selectedPath;
|
||||
} state;
|
||||
|
||||
auto destruct() -> void override;
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(Hiro_TreeView)
|
||||
struct mTreeViewItem : mObject {
|
||||
Declare(TreeViewItem)
|
||||
|
||||
auto append(sTreeViewItem item) -> type&;
|
||||
auto backgroundColor(bool recursive = false) const -> Color;
|
||||
auto checkable() const -> bool;
|
||||
auto checked() const -> bool;
|
||||
auto foregroundColor(bool recursive = false) const -> Color;
|
||||
auto icon() const -> image;
|
||||
auto item(const string& path) const -> TreeViewItem;
|
||||
auto itemCount() const -> uint;
|
||||
auto items() const -> vector<TreeViewItem>;
|
||||
auto path() const -> string;
|
||||
auto remove() -> type& override;
|
||||
auto remove(sTreeViewItem item) -> type&;
|
||||
auto selected() const -> bool;
|
||||
auto setBackgroundColor(Color color = {}) -> type&;
|
||||
auto setCheckable(bool checkable = true) -> type&;
|
||||
auto setChecked(bool checked = true) -> type&;
|
||||
auto setExpanded(bool expanded = true) -> type&;
|
||||
auto setFocused() -> type& override;
|
||||
auto setForegroundColor(Color color = {}) -> type&;
|
||||
auto setIcon(const image& icon = {}) -> type&;
|
||||
auto setParent(mObject* parent = nullptr, int offset = -1) -> type&;
|
||||
auto setSelected() -> type&;
|
||||
auto setText(const string& text = "") -> type&;
|
||||
auto text() const -> string;
|
||||
|
||||
//private:
|
||||
struct State {
|
||||
Color backgroundColor;
|
||||
bool checkable = false;
|
||||
bool checked = false;
|
||||
Color foregroundColor;
|
||||
image icon;
|
||||
vector<sTreeViewItem> items;
|
||||
string text;
|
||||
} state;
|
||||
|
||||
auto destruct() -> void override;
|
||||
};
|
||||
#endif
|
||||
#include "widget/tree-view.hpp"
|
||||
#include "widget/tree-view-item.hpp"
|
||||
|
||||
#if defined(Hiro_VerticalScrollBar)
|
||||
struct mVerticalScrollBar : mWidget {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
#if defined(Hiro_Cursor)
|
||||
|
||||
Cursor::Cursor(signed offset, signed length) {
|
||||
Cursor::Cursor(int offset, int length) {
|
||||
setCursor(offset, length);
|
||||
}
|
||||
|
||||
Cursor::operator bool() const {
|
||||
return offset() && length();
|
||||
return offset() || length();
|
||||
}
|
||||
|
||||
auto Cursor::operator==(const Cursor& source) const -> bool {
|
||||
|
@ -16,27 +16,27 @@ auto Cursor::operator!=(const Cursor& source) const -> bool {
|
|||
return !operator==(source);
|
||||
}
|
||||
|
||||
auto Cursor::length() const -> signed {
|
||||
auto Cursor::length() const -> int {
|
||||
return state.length;
|
||||
}
|
||||
|
||||
auto Cursor::offset() const -> signed {
|
||||
auto Cursor::offset() const -> int {
|
||||
return state.offset;
|
||||
}
|
||||
|
||||
auto Cursor::setCursor(signed offset, signed length) -> type& {
|
||||
auto Cursor::setCursor(int offset, int length) -> type& {
|
||||
state.offset = offset;
|
||||
state.length = length;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto Cursor::setOffset(signed offset) -> type& {
|
||||
state.offset = offset;
|
||||
auto Cursor::setLength(int length) -> type& {
|
||||
state.length = length;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto Cursor::setLength(signed length) -> type& {
|
||||
state.length = length;
|
||||
auto Cursor::setOffset(int offset) -> type& {
|
||||
state.offset = offset;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
#if defined(Hiro_Cursor)
|
||||
struct Cursor {
|
||||
using type = Cursor;
|
||||
|
||||
Cursor(int offset = 0, int length = 0);
|
||||
|
||||
explicit operator bool() const;
|
||||
auto operator==(const Cursor& source) const -> bool;
|
||||
auto operator!=(const Cursor& source) const -> bool;
|
||||
|
||||
auto length() const -> int;
|
||||
auto offset() const -> int;
|
||||
auto setCursor(int offset = 0, int length = 0) -> type&;
|
||||
auto setLength(int length = 0) -> type&;
|
||||
auto setOffset(int offset = 0) -> type&;
|
||||
|
||||
//private:
|
||||
struct State {
|
||||
int offset;
|
||||
int length;
|
||||
} state;
|
||||
};
|
||||
#endif
|
|
@ -52,10 +52,12 @@
|
|||
|
||||
#define DeclareSharedSizable(Name) \
|
||||
DeclareSharedObject(Name) \
|
||||
auto collapsible() const { return self().collapsible(); } \
|
||||
auto doSize() const { return self().doSize(); } \
|
||||
auto geometry() const { return self().geometry(); } \
|
||||
auto minimumSize() const { return self().minimumSize(); } \
|
||||
auto onSize(const function<void ()>& callback = {}) { return self().onSize(callback), *this; } \
|
||||
auto setCollapsible(bool collapsible = true) { return self().setCollapsible(collapsible), *this; } \
|
||||
auto setGeometry(Geometry geometry) { return self().setGeometry(geometry), *this; } \
|
||||
|
||||
#define DeclareSharedWidget(Name) \
|
||||
|
@ -461,7 +463,11 @@ struct Label : sLabel {
|
|||
|
||||
auto alignment() const { return self().alignment(); }
|
||||
auto backgroundColor() const { return self().backgroundColor(); }
|
||||
auto doMousePress(Mouse::Button button) const { return self().doMousePress(button); }
|
||||
auto doMouseRelease(Mouse::Button button) const { return self().doMouseRelease(button); }
|
||||
auto foregroundColor() const { return self().foregroundColor(); }
|
||||
auto onMousePress(const function<void (Mouse::Button)>& callback = {}) { return self().onMousePress(callback), *this; }
|
||||
auto onMouseRelease(const function<void (Mouse::Button)>& callback = {}) { return self().onMouseRelease(callback), *this; }
|
||||
auto setAlignment(Alignment alignment = {}) { return self().setAlignment(alignment), *this; }
|
||||
auto setBackgroundColor(Color color = {}) { return self().setBackgroundColor(color), *this; }
|
||||
auto setForegroundColor(Color color = {}) { return self().setForegroundColor(color), *this; }
|
||||
|
@ -540,10 +546,16 @@ struct SourceEdit : sSourceEdit {
|
|||
auto doChange() const { return self().doChange(); }
|
||||
auto doMove() const { return self().doMove(); }
|
||||
auto editable() const { return self().editable(); }
|
||||
auto language() const { return self().language(); }
|
||||
auto numbered() const { return self().numbered(); }
|
||||
auto onChange(const function<void ()>& callback = {}) { return self().onChange(callback), *this; }
|
||||
auto onMove(const function<void ()>& callback = {}) { return self().onMove(callback), *this; }
|
||||
auto scheme() const { return self().scheme(); }
|
||||
auto setCursor(Cursor cursor = {}) { return self().setCursor(cursor), *this; }
|
||||
auto setEditable(bool editable = true) { return self().setEditable(editable), *this; }
|
||||
auto setLanguage(const string& language = "") { return self().setLanguage(language), *this; }
|
||||
auto setNumbered(bool numbered = true) { return self().setNumbered(numbered), *this; }
|
||||
auto setScheme(const string& scheme = "") { return self().setScheme(scheme), *this; }
|
||||
auto setText(const string& text = "") { return self().setText(text), *this; }
|
||||
auto setWordWrap(bool wordWrap = true) { return self().setWordWrap(wordWrap), *this; }
|
||||
auto text() const { return self().text(); }
|
||||
|
@ -749,6 +761,7 @@ struct TreeViewItem : sTreeViewItem {
|
|||
auto backgroundColor() const { return self().backgroundColor(); }
|
||||
auto checkable() const { return self().checkable(); }
|
||||
auto checked() const { return self().checked(); }
|
||||
auto expanded() const { return self().expanded(); }
|
||||
auto foregroundColor() const { return self().foregroundColor(); }
|
||||
auto icon() const { return self().icon(); }
|
||||
auto item(const string& path) const { return self().item(path); }
|
||||
|
|
|
@ -4,6 +4,10 @@ auto mSizable::allocate() -> pObject* {
|
|||
return new pSizable(*this);
|
||||
}
|
||||
|
||||
auto mSizable::collapsible() const -> bool {
|
||||
return state.collapsible;
|
||||
}
|
||||
|
||||
auto mSizable::doSize() const -> void {
|
||||
if(state.onSize) return state.onSize();
|
||||
}
|
||||
|
@ -21,6 +25,12 @@ auto mSizable::onSize(const function<void ()>& callback) -> type& {
|
|||
return *this;
|
||||
}
|
||||
|
||||
auto mSizable::setCollapsible(bool collapsible) -> type& {
|
||||
state.collapsible = collapsible;
|
||||
signal(setCollapsible, collapsible);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto mSizable::setGeometry(Geometry geometry) -> type& {
|
||||
state.geometry = geometry;
|
||||
signal(setGeometry, geometry);
|
||||
|
|
|
@ -2,15 +2,17 @@
|
|||
struct mSizable : mObject {
|
||||
Declare(Sizable)
|
||||
|
||||
auto collapsible() const -> bool;
|
||||
auto doSize() const -> void;
|
||||
auto geometry() const -> Geometry;
|
||||
virtual auto minimumSize() const -> Size;
|
||||
auto onSize(const function<void ()>& callback = {}) -> type&;
|
||||
virtual auto setCollapsible(bool collapsible = true) -> type&;
|
||||
virtual auto setGeometry(Geometry geometry) -> type&;
|
||||
|
||||
//private:
|
||||
//sizeof(mSizable) == 24
|
||||
struct State {
|
||||
bool collapsible = false;
|
||||
Geometry geometry;
|
||||
function<void ()> onSize;
|
||||
} state;
|
||||
|
|
|
@ -14,10 +14,28 @@ auto mLabel::backgroundColor() const -> Color {
|
|||
return state.backgroundColor;
|
||||
}
|
||||
|
||||
auto mLabel::doMousePress(Mouse::Button button) const -> void {
|
||||
if(state.onMousePress) return state.onMousePress(button);
|
||||
}
|
||||
|
||||
auto mLabel::doMouseRelease(Mouse::Button button) const -> void {
|
||||
if(state.onMouseRelease) return state.onMouseRelease(button);
|
||||
}
|
||||
|
||||
auto mLabel::foregroundColor() const -> Color {
|
||||
return state.foregroundColor;
|
||||
}
|
||||
|
||||
auto mLabel::onMousePress(const function<void (Mouse::Button)>& callback) -> type& {
|
||||
state.onMousePress = callback;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto mLabel::onMouseRelease(const function<void (Mouse::Button)>& callback) -> type& {
|
||||
state.onMouseRelease = callback;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto mLabel::setAlignment(Alignment alignment) -> type& {
|
||||
state.alignment = alignment;
|
||||
signal(setAlignment, alignment);
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
#if defined(Hiro_Label)
|
||||
struct mLabel : mWidget {
|
||||
Declare(Label)
|
||||
|
||||
auto alignment() const -> Alignment;
|
||||
auto backgroundColor() const -> Color;
|
||||
auto doMousePress(Mouse::Button button) const -> void;
|
||||
auto doMouseRelease(Mouse::Button button) const -> void;
|
||||
auto foregroundColor() const -> Color;
|
||||
auto onMousePress(const function<void (Mouse::Button)>& callback = {}) -> type&;
|
||||
auto onMouseRelease(const function<void (Mouse::Button)>& callback = {}) -> type&;
|
||||
auto setAlignment(Alignment alignment = {}) -> type&;
|
||||
auto setBackgroundColor(Color color = {}) -> type&;
|
||||
auto setForegroundColor(Color color = {}) -> type&;
|
||||
auto setText(const string& text = "") -> type&;
|
||||
auto text() const -> string;
|
||||
|
||||
//private:
|
||||
struct State {
|
||||
Alignment alignment;
|
||||
Color backgroundColor;
|
||||
Color foregroundColor;
|
||||
function<void (Mouse::Button)> onMousePress;
|
||||
function<void (Mouse::Button)> onMouseRelease;
|
||||
string text;
|
||||
} state;
|
||||
};
|
||||
#endif
|
|
@ -7,7 +7,7 @@ auto mSourceEdit::allocate() -> pObject* {
|
|||
//
|
||||
|
||||
auto mSourceEdit::cursor() const -> Cursor {
|
||||
return state.cursor;
|
||||
return signal(cursor);
|
||||
}
|
||||
|
||||
auto mSourceEdit::doChange() const -> void {
|
||||
|
@ -22,6 +22,14 @@ auto mSourceEdit::editable() const -> bool {
|
|||
return state.editable;
|
||||
}
|
||||
|
||||
auto mSourceEdit::language() const -> string {
|
||||
return state.language;
|
||||
}
|
||||
|
||||
auto mSourceEdit::numbered() const -> bool {
|
||||
return state.numbered;
|
||||
}
|
||||
|
||||
auto mSourceEdit::onChange(const function<void ()>& callback) -> type& {
|
||||
state.onChange = callback;
|
||||
return *this;
|
||||
|
@ -32,8 +40,11 @@ auto mSourceEdit::onMove(const function<void ()>& callback) -> type& {
|
|||
return *this;
|
||||
}
|
||||
|
||||
auto mSourceEdit::scheme() const -> string {
|
||||
return state.scheme;
|
||||
}
|
||||
|
||||
auto mSourceEdit::setCursor(Cursor cursor) -> type& {
|
||||
state.cursor = cursor;
|
||||
signal(setCursor, cursor);
|
||||
return *this;
|
||||
}
|
||||
|
@ -44,6 +55,24 @@ auto mSourceEdit::setEditable(bool editable) -> type& {
|
|||
return *this;
|
||||
}
|
||||
|
||||
auto mSourceEdit::setLanguage(const string& language) -> type& {
|
||||
state.language = language;
|
||||
signal(setLanguage, language);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto mSourceEdit::setNumbered(bool numbered) -> type& {
|
||||
state.numbered = numbered;
|
||||
signal(setNumbered, numbered);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto mSourceEdit::setScheme(const string& scheme) -> type& {
|
||||
state.scheme = scheme;
|
||||
signal(setScheme, scheme);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto mSourceEdit::setText(const string& text) -> type& {
|
||||
state.text = text;
|
||||
signal(setText, text);
|
||||
|
|
|
@ -6,10 +6,16 @@ struct mSourceEdit : mWidget {
|
|||
auto doChange() const -> void;
|
||||
auto doMove() const -> void;
|
||||
auto editable() const -> bool;
|
||||
auto language() const -> string;
|
||||
auto numbered() const -> bool;
|
||||
auto onChange(const function<void ()>& callback = {}) -> type&;
|
||||
auto onMove(const function<void ()>& callback = {}) -> type&;
|
||||
auto scheme() const -> string;
|
||||
auto setCursor(Cursor cursor = {}) -> type&;
|
||||
auto setEditable(bool editable) -> type&;
|
||||
auto setLanguage(const string& language = "") -> type&;
|
||||
auto setNumbered(bool numbered = true) -> type&;
|
||||
auto setScheme(const string& scheme = "") -> type&;
|
||||
auto setText(const string& text = "") -> type&;
|
||||
auto setWordWrap(bool wordWrap = true) -> type&;
|
||||
auto text() const -> string;
|
||||
|
@ -17,10 +23,12 @@ struct mSourceEdit : mWidget {
|
|||
|
||||
//private:
|
||||
struct State {
|
||||
Cursor cursor;
|
||||
bool editable = true;
|
||||
string language;
|
||||
bool numbered = true;
|
||||
function<void ()> onChange;
|
||||
function<void ()> onMove;
|
||||
string scheme;
|
||||
string text;
|
||||
bool wordWrap = true;
|
||||
} state;
|
||||
|
|
|
@ -39,6 +39,10 @@ auto mTreeViewItem::checked() const -> bool {
|
|||
return state.checked;
|
||||
}
|
||||
|
||||
auto mTreeViewItem::expanded() const -> bool {
|
||||
return state.expanded;
|
||||
}
|
||||
|
||||
auto mTreeViewItem::foregroundColor(bool recursive) const -> Color {
|
||||
if(auto color = state.foregroundColor) return color;
|
||||
if(recursive) {
|
||||
|
@ -122,6 +126,7 @@ auto mTreeViewItem::setChecked(bool checked) -> type& {
|
|||
}
|
||||
|
||||
auto mTreeViewItem::setExpanded(bool expanded) -> type& {
|
||||
state.expanded = expanded;
|
||||
signal(setExpanded, expanded);
|
||||
return *this;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
#if defined(Hiro_TreeView)
|
||||
struct mTreeViewItem : mObject {
|
||||
Declare(TreeViewItem)
|
||||
|
||||
auto append(sTreeViewItem item) -> type&;
|
||||
auto backgroundColor(bool recursive = false) const -> Color;
|
||||
auto checkable() const -> bool;
|
||||
auto checked() const -> bool;
|
||||
auto expanded() const -> bool;
|
||||
auto foregroundColor(bool recursive = false) const -> Color;
|
||||
auto icon() const -> image;
|
||||
auto item(const string& path) const -> TreeViewItem;
|
||||
auto itemCount() const -> uint;
|
||||
auto items() const -> vector<TreeViewItem>;
|
||||
auto path() const -> string;
|
||||
auto remove() -> type& override;
|
||||
auto remove(sTreeViewItem item) -> type&;
|
||||
auto selected() const -> bool;
|
||||
auto setBackgroundColor(Color color = {}) -> type&;
|
||||
auto setCheckable(bool checkable = true) -> type&;
|
||||
auto setChecked(bool checked = true) -> type&;
|
||||
auto setExpanded(bool expanded = true) -> type&;
|
||||
auto setFocused() -> type& override;
|
||||
auto setForegroundColor(Color color = {}) -> type&;
|
||||
auto setIcon(const image& icon = {}) -> type&;
|
||||
auto setParent(mObject* parent = nullptr, int offset = -1) -> type&;
|
||||
auto setSelected() -> type&;
|
||||
auto setText(const string& text = "") -> type&;
|
||||
auto text() const -> string;
|
||||
|
||||
//private:
|
||||
struct State {
|
||||
Color backgroundColor;
|
||||
bool checkable = false;
|
||||
bool checked = false;
|
||||
bool expanded = false;
|
||||
Color foregroundColor;
|
||||
image icon;
|
||||
vector<sTreeViewItem> items;
|
||||
string text;
|
||||
} state;
|
||||
|
||||
auto destruct() -> void override;
|
||||
};
|
||||
#endif
|
|
@ -0,0 +1,41 @@
|
|||
#if defined(Hiro_TreeView)
|
||||
struct mTreeView : mWidget {
|
||||
Declare(TreeView)
|
||||
using mObject::remove;
|
||||
|
||||
auto append(sTreeViewItem item) -> type&;
|
||||
auto backgroundColor() const -> Color;
|
||||
auto doActivate() const -> void;
|
||||
auto doChange() const -> void;
|
||||
auto doContext() const -> void;
|
||||
auto doToggle(sTreeViewItem item) const -> void;
|
||||
auto foregroundColor() const -> Color;
|
||||
auto item(const string& path) const -> TreeViewItem;
|
||||
auto itemCount() const -> uint;
|
||||
auto items() const -> vector<TreeViewItem>;
|
||||
auto onActivate(const function<void ()>& callback = {}) -> type&;
|
||||
auto onChange(const function<void ()>& callback = {}) -> type&;
|
||||
auto onContext(const function<void ()>& callback = {}) -> type&;
|
||||
auto onToggle(const function<void (sTreeViewItem)>& callback = {}) -> type&;
|
||||
auto remove(sTreeViewItem item) -> type&;
|
||||
auto reset() -> type&;
|
||||
auto selected() const -> TreeViewItem;
|
||||
auto setBackgroundColor(Color color = {}) -> type&;
|
||||
auto setForegroundColor(Color color = {}) -> type&;
|
||||
auto setParent(mObject* parent = nullptr, int offset = -1) -> type&;
|
||||
|
||||
//private:
|
||||
struct State {
|
||||
Color backgroundColor;
|
||||
Color foregroundColor;
|
||||
vector<sTreeViewItem> items;
|
||||
function<void ()> onActivate;
|
||||
function<void ()> onChange;
|
||||
function<void ()> onContext;
|
||||
function<void (sTreeViewItem)> onToggle;
|
||||
string selectedPath;
|
||||
} state;
|
||||
|
||||
auto destruct() -> void override;
|
||||
};
|
||||
#endif
|
|
@ -0,0 +1,141 @@
|
|||
#if defined(Hiro_AboutDialog)
|
||||
|
||||
auto AboutDialog::setAuthor(const string& author) -> type& {
|
||||
state.author = author;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto AboutDialog::setDescription(const string& description) -> type& {
|
||||
state.description = description;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto AboutDialog::setLicense(const string& license) -> type& {
|
||||
state.license = license;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto AboutDialog::setLogo(const image& logo) -> type& {
|
||||
state.logo = logo;
|
||||
state.logo.transform();
|
||||
state.logo.alphaBlend(0xfffff0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto AboutDialog::setName(const string& name) -> type& {
|
||||
state.name = name;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto AboutDialog::setParent(sWindow parent) -> type& {
|
||||
state.parent = parent;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto AboutDialog::setVersion(const string& version) -> type& {
|
||||
state.version = version;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto AboutDialog::setWebsite(const string& website) -> type& {
|
||||
state.website = website;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto AboutDialog::show() -> void {
|
||||
Window window;
|
||||
window.onClose([&] { window.setModal(false); });
|
||||
|
||||
VerticalLayout layout{&window};
|
||||
layout.setPadding(5);
|
||||
|
||||
Label nameLabel{&layout, Size{~0, 0}};
|
||||
nameLabel.setCollapsible();
|
||||
nameLabel.setAlignment(0.5);
|
||||
nameLabel.setForegroundColor({0, 0, 0});
|
||||
nameLabel.setFont(Font().setFamily("Georgia").setBold().setSize(36.0));
|
||||
nameLabel.setText(state.name ? state.name : Application::name());
|
||||
nameLabel.setVisible(!state.logo);
|
||||
|
||||
Canvas logoCanvas{&layout, Size{~0, 0}};
|
||||
logoCanvas.setCollapsible();
|
||||
logoCanvas.setIcon(state.logo);
|
||||
logoCanvas.setVisible((bool)state.logo);
|
||||
|
||||
Label descriptionLabel{&layout, Size{~0, 0}};
|
||||
descriptionLabel.setCollapsible();
|
||||
descriptionLabel.setAlignment(0.5);
|
||||
descriptionLabel.setForegroundColor({0, 0, 0});
|
||||
descriptionLabel.setText(state.description);
|
||||
if(!state.description) descriptionLabel.setVisible(false);
|
||||
|
||||
HorizontalLayout versionLayout{&layout, Size{~0, 0}, 0};
|
||||
versionLayout.setCollapsible();
|
||||
Label versionLabel{&versionLayout, Size{~0, 0}, 3};
|
||||
versionLabel.setAlignment(1.0);
|
||||
versionLabel.setFont(Font().setBold());
|
||||
versionLabel.setForegroundColor({0, 0, 0});
|
||||
versionLabel.setText("Version:");
|
||||
Label versionValue{&versionLayout, Size{~0, 0}};
|
||||
versionValue.setAlignment(0.0);
|
||||
versionValue.setFont(Font().setBold());
|
||||
versionValue.setForegroundColor({0, 0, 0});
|
||||
versionValue.setText(state.version);
|
||||
if(!state.version) versionLayout.setVisible(false);
|
||||
|
||||
HorizontalLayout authorLayout{&layout, Size{~0, 0}, 0};
|
||||
authorLayout.setCollapsible();
|
||||
Label authorLabel{&authorLayout, Size{~0, 0}, 3};
|
||||
authorLabel.setAlignment(1.0);
|
||||
authorLabel.setFont(Font().setBold());
|
||||
authorLabel.setForegroundColor({0, 0, 0});
|
||||
authorLabel.setText("Author:");
|
||||
Label authorValue{&authorLayout, Size{~0, 0}};
|
||||
authorValue.setAlignment(0.0);
|
||||
authorValue.setFont(Font().setBold());
|
||||
authorValue.setForegroundColor({0, 0, 0});
|
||||
authorValue.setText(state.author);
|
||||
if(!state.author) authorLayout.setVisible(false);
|
||||
|
||||
HorizontalLayout licenseLayout{&layout, Size{~0, 0}, 0};
|
||||
licenseLayout.setCollapsible();
|
||||
Label licenseLabel{&licenseLayout, Size{~0, 0}, 3};
|
||||
licenseLabel.setAlignment(1.0);
|
||||
licenseLabel.setFont(Font().setBold());
|
||||
licenseLabel.setForegroundColor({0, 0, 0});
|
||||
licenseLabel.setText("License:");
|
||||
Label licenseValue{&licenseLayout, Size{~0, 0}};
|
||||
licenseValue.setAlignment(0.0);
|
||||
licenseValue.setFont(Font().setBold());
|
||||
licenseValue.setForegroundColor({0, 0, 0});
|
||||
licenseValue.setText(state.license);
|
||||
if(!state.license) licenseLayout.setVisible(false);
|
||||
|
||||
HorizontalLayout websiteLayout{&layout, Size{~0, 0}, 0};
|
||||
websiteLayout.setCollapsible();
|
||||
Label websiteLabel{&websiteLayout, Size{~0, 0}, 3};
|
||||
websiteLabel.setAlignment(1.0);
|
||||
websiteLabel.setFont(Font().setBold());
|
||||
websiteLabel.setForegroundColor({0, 0, 0});
|
||||
websiteLabel.setText("Website:");
|
||||
Label websiteValue{&websiteLayout, Size{~0, 0}};
|
||||
websiteValue.setAlignment(0.0);
|
||||
websiteValue.setFont(Font().setBold());
|
||||
websiteValue.setForegroundColor({0, 0, 240});
|
||||
websiteValue.setText(state.website);
|
||||
websiteValue.onMouseRelease([&](Mouse::Button button) {
|
||||
if(button == Mouse::Button::Left) invoke(state.website);
|
||||
});
|
||||
if(!state.website) websiteLayout.setVisible(false);
|
||||
|
||||
window.setTitle({"About ", state.name ? state.name : Application::name(), " ..."});
|
||||
window.setBackgroundColor({255, 255, 240});
|
||||
window.setSize({max(360, layout.minimumSize().width()), layout.minimumSize().height()});
|
||||
window.setResizable(false);
|
||||
window.setCentered(state.parent);
|
||||
window.setDismissable();
|
||||
window.setVisible();
|
||||
window.setModal();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,29 @@
|
|||
#if defined(Hiro_AboutDialog)
|
||||
|
||||
struct AboutDialog {
|
||||
using type = AboutDialog;
|
||||
|
||||
auto setAuthor(const string& author = "") -> type&;
|
||||
auto setDescription(const string& description = "") -> type&;
|
||||
auto setLicense(const string& license = "") -> type&;
|
||||
auto setLogo(const image& logo = {}) -> type&;
|
||||
auto setName(const string& name = "") -> type&;
|
||||
auto setParent(sWindow parent = {}) -> type&;
|
||||
auto setVersion(const string& version = "") -> type&;
|
||||
auto setWebsite(const string& website = "") -> type&;
|
||||
auto show() -> void;
|
||||
|
||||
private:
|
||||
struct State {
|
||||
string author;
|
||||
string description;
|
||||
string license;
|
||||
image logo;
|
||||
string name;
|
||||
sWindow parent;
|
||||
string version;
|
||||
string website;
|
||||
} state;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -162,7 +162,7 @@ auto BrowserDialogWindow::run() -> BrowserDialog::Response {
|
|||
auto part = filter.split("|", 1L);
|
||||
filterList.append(ComboButtonItem().setText(part.left()));
|
||||
}
|
||||
optionList.setVisible((bool)state.options).onChange([&] { response.option = optionList.selected().text(); });
|
||||
optionList.setCollapsible().setVisible((bool)state.options).onChange([&] { response.option = optionList.selected().text(); });
|
||||
for(auto& option : state.options) {
|
||||
optionList.append(ComboButtonItem().setText(option));
|
||||
}
|
||||
|
|
|
@ -10,4 +10,5 @@ namespace hiro {
|
|||
#include "list-view.cpp"
|
||||
#include "browser-dialog.cpp"
|
||||
#include "message-dialog.cpp"
|
||||
#include "about-dialog.cpp"
|
||||
}
|
||||
|
|
|
@ -8,4 +8,5 @@ namespace hiro {
|
|||
#include "shared.hpp"
|
||||
#include "browser-dialog.hpp"
|
||||
#include "message-dialog.hpp"
|
||||
#include "about-dialog.hpp"
|
||||
}
|
||||
|
|
|
@ -40,19 +40,23 @@ auto mHorizontalLayout::destruct() -> void {
|
|||
|
||||
auto mHorizontalLayout::minimumSize() const -> Size {
|
||||
float width = 0;
|
||||
float spacing = 0;
|
||||
for(auto index : range(cellCount())) {
|
||||
auto cell = this->cell(index);
|
||||
if(cell.collapsible()) continue;
|
||||
if(cell.size().width() == Size::Minimum || cell.size().width() == Size::Maximum) {
|
||||
width += cell.sizable().minimumSize().width();
|
||||
} else {
|
||||
width += cell.size().width();
|
||||
}
|
||||
if(index != cellCount() - 1) width += cell.spacing();
|
||||
width += spacing;
|
||||
spacing = cell.spacing();
|
||||
}
|
||||
|
||||
float height = 0;
|
||||
for(auto index : range(cellCount())) {
|
||||
auto cell = this->cell(index);
|
||||
if(cell.collapsible()) continue;
|
||||
if(cell.size().height() == Size::Minimum || cell.size().height() == Size::Maximum) {
|
||||
height = max(height, cell.sizable().minimumSize().height());
|
||||
continue;
|
||||
|
@ -108,10 +112,10 @@ auto mHorizontalLayout::setFont(const Font& font) -> type& {
|
|||
return *this;
|
||||
}
|
||||
|
||||
auto mHorizontalLayout::setGeometry(Geometry geometry) -> type& {
|
||||
mSizable::setGeometry(geometry);
|
||||
if(!visible(true)) return *this;
|
||||
auto mHorizontalLayout::setGeometry(Geometry requestedGeometry) -> type& {
|
||||
if(!visible(true)) return mSizable::setGeometry(requestedGeometry), *this;
|
||||
|
||||
auto geometry = requestedGeometry;
|
||||
geometry.setX(geometry.x() + padding().x());
|
||||
geometry.setY(geometry.y() + padding().y());
|
||||
geometry.setWidth (geometry.width() - padding().x() - padding().width());
|
||||
|
@ -120,8 +124,9 @@ auto mHorizontalLayout::setGeometry(Geometry geometry) -> type& {
|
|||
vector<float> widths;
|
||||
widths.resize(cellCount());
|
||||
uint maximumWidths = 0;
|
||||
for(auto index : range(cellCount())) {
|
||||
for(uint index : range(cellCount())) {
|
||||
auto cell = this->cell(index);
|
||||
if(cell.collapsible()) continue;
|
||||
float width = 0;
|
||||
if(cell.size().width() == Size::Maximum) {
|
||||
width = Size::Maximum;
|
||||
|
@ -135,9 +140,13 @@ auto mHorizontalLayout::setGeometry(Geometry geometry) -> type& {
|
|||
}
|
||||
|
||||
float fixedWidth = 0;
|
||||
for(uint index : range(state.cells.size())) {
|
||||
float spacing = 0;
|
||||
for(uint index : range(cellCount())) {
|
||||
auto cell = this->cell(index);
|
||||
if(cell.collapsible()) continue;
|
||||
if(widths[index] != Size::Maximum) fixedWidth += widths[index];
|
||||
if(index != cellCount() - 1) fixedWidth += cell(index).spacing();
|
||||
fixedWidth += spacing;
|
||||
spacing = cell.spacing();
|
||||
}
|
||||
|
||||
float maximumWidth = (geometry.width() - fixedWidth) / maximumWidths;
|
||||
|
@ -146,8 +155,9 @@ auto mHorizontalLayout::setGeometry(Geometry geometry) -> type& {
|
|||
}
|
||||
|
||||
float height = 0;
|
||||
for(auto index : range(cellCount())) {
|
||||
for(uint index : range(cellCount())) {
|
||||
auto cell = this->cell(index);
|
||||
if(cell.collapsible()) continue;
|
||||
if(cell.size().height() == Size::Maximum) {
|
||||
height = geometry.height();
|
||||
break;
|
||||
|
@ -160,10 +170,11 @@ auto mHorizontalLayout::setGeometry(Geometry geometry) -> type& {
|
|||
|
||||
float geometryX = geometry.x();
|
||||
float geometryY = geometry.y();
|
||||
for(auto index : range(cellCount())) {
|
||||
for(uint index : range(cellCount())) {
|
||||
auto cell = this->cell(index);
|
||||
if(cell.collapsible()) continue;
|
||||
float geometryWidth = widths[index];
|
||||
float geometryHeight = height;
|
||||
auto cell = this->cell(index);
|
||||
auto alignment = cell.alignment();
|
||||
if(!alignment) alignment = this->alignment();
|
||||
if(!alignment) alignment = 0.5;
|
||||
|
@ -177,6 +188,7 @@ auto mHorizontalLayout::setGeometry(Geometry geometry) -> type& {
|
|||
geometryX += geometryWidth + cell.spacing();
|
||||
}
|
||||
|
||||
mSizable::setGeometry(requestedGeometry);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -218,6 +230,11 @@ auto mHorizontalLayoutCell::alignment() const -> maybe<float> {
|
|||
return state.alignment;
|
||||
}
|
||||
|
||||
auto mHorizontalLayoutCell::collapsible() const -> bool {
|
||||
if(state.sizable) return state.sizable->collapsible() && !state.sizable->visible();
|
||||
return false;
|
||||
}
|
||||
|
||||
auto mHorizontalLayoutCell::destruct() -> void {
|
||||
if(auto& sizable = state.sizable) sizable->destruct();
|
||||
mObject::destruct();
|
||||
|
|
|
@ -49,6 +49,7 @@ struct mHorizontalLayoutCell : mObject {
|
|||
using type = mHorizontalLayoutCell;
|
||||
|
||||
auto alignment() const -> maybe<float>;
|
||||
auto collapsible() const -> bool;
|
||||
auto setAlignment(maybe<float> alignment) -> type&;
|
||||
auto setEnabled(bool enabled) -> type& override;
|
||||
auto setFont(const Font& font) -> type& override;
|
||||
|
|
|
@ -26,6 +26,7 @@ struct HorizontalLayoutCell : sHorizontalLayoutCell {
|
|||
DeclareSharedObject(HorizontalLayoutCell)
|
||||
|
||||
auto alignment() const { return self().alignment(); }
|
||||
auto collapsible() const { return self().collapsible(); }
|
||||
auto setAlignment(maybe<float> alignment = {}) { return self().setAlignment(alignment), *this; }
|
||||
auto setSizable(sSizable sizable) { return self().setSizable(sizable), *this; }
|
||||
auto setSize(Size size) { return self().setSize(size), *this; }
|
||||
|
@ -58,6 +59,7 @@ struct VerticalLayoutCell : sVerticalLayoutCell {
|
|||
DeclareSharedObject(VerticalLayoutCell)
|
||||
|
||||
auto alignment() const { return self().alignment(); }
|
||||
auto collapsible() const { return self().collapsible(); }
|
||||
auto setAlignment(maybe<float> alignment = {}) { return self().setAlignment(alignment), *this; }
|
||||
auto setSizable(sSizable sizable) { return self().setSizable(sizable), *this; }
|
||||
auto setSize(Size size) { return self().setSize(size), *this; }
|
||||
|
|
|
@ -143,10 +143,10 @@ auto mTableLayout::setFont(const Font& font) -> type& {
|
|||
return *this;
|
||||
}
|
||||
|
||||
auto mTableLayout::setGeometry(Geometry geometry) -> type& {
|
||||
mSizable::setGeometry(geometry);
|
||||
if(!visible(true)) return *this;
|
||||
auto mTableLayout::setGeometry(Geometry requestedGeometry) -> type& {
|
||||
if(!visible(true)) return mSizable::setGeometry(requestedGeometry), *this;
|
||||
|
||||
auto geometry = requestedGeometry;
|
||||
geometry.setX(geometry.x() + padding().x());
|
||||
geometry.setY(geometry.y() + padding().y());
|
||||
geometry.setWidth (geometry.width() - padding().x() - padding().width());
|
||||
|
@ -250,6 +250,7 @@ auto mTableLayout::setGeometry(Geometry geometry) -> type& {
|
|||
geometryY += heights[y] + row.spacing();
|
||||
}
|
||||
|
||||
mSizable::setGeometry(requestedGeometry);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ auto mVerticalLayout::minimumSize() const -> Size {
|
|||
float width = 0;
|
||||
for(auto index : range(cellCount())) {
|
||||
auto cell = this->cell(index);
|
||||
if(cell.collapsible()) continue;
|
||||
if(cell.size().width() == Size::Minimum || cell.size().width() == Size::Maximum) {
|
||||
width = max(width, cell.sizable().minimumSize().width());
|
||||
continue;
|
||||
|
@ -50,14 +51,17 @@ auto mVerticalLayout::minimumSize() const -> Size {
|
|||
}
|
||||
|
||||
float height = 0;
|
||||
float spacing = 0;
|
||||
for(auto index : range(cellCount())) {
|
||||
auto cell = this->cell(index);
|
||||
if(cell.collapsible()) continue;
|
||||
if(cell.size().height() == Size::Minimum || cell.size().height() == Size::Maximum) {
|
||||
height += cell.sizable().minimumSize().height();
|
||||
} else {
|
||||
height += cell.size().height();
|
||||
}
|
||||
if(index != cellCount() - 1) height += cell.spacing();
|
||||
height += spacing;
|
||||
spacing = cell.spacing();
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -108,18 +112,19 @@ auto mVerticalLayout::setFont(const Font& font) -> type& {
|
|||
return *this;
|
||||
}
|
||||
|
||||
auto mVerticalLayout::setGeometry(Geometry geometry) -> type& {
|
||||
mSizable::setGeometry(geometry);
|
||||
if(!visible(true)) return *this;
|
||||
auto mVerticalLayout::setGeometry(Geometry requestedGeometry) -> type& {
|
||||
if(!visible(true)) return mSizable::setGeometry(requestedGeometry), *this;
|
||||
|
||||
auto geometry = requestedGeometry;
|
||||
geometry.setX(geometry.x() + padding().x());
|
||||
geometry.setY(geometry.y() + padding().y());
|
||||
geometry.setWidth (geometry.width() - padding().x() - padding().width());
|
||||
geometry.setHeight(geometry.height() - padding().y() - padding().height());
|
||||
|
||||
float width = 0;
|
||||
for(auto index : range(cellCount())) {
|
||||
for(uint index : range(cellCount())) {
|
||||
auto cell = this->cell(index);
|
||||
if(cell.collapsible()) continue;
|
||||
if(cell.size().width() == Size::Maximum) {
|
||||
width = geometry.width();
|
||||
break;
|
||||
|
@ -133,8 +138,9 @@ auto mVerticalLayout::setGeometry(Geometry geometry) -> type& {
|
|||
vector<float> heights;
|
||||
heights.resize(cellCount());
|
||||
uint maximumHeights = 0;
|
||||
for(auto index : range(cellCount())) {
|
||||
for(uint index : range(cellCount())) {
|
||||
auto cell = this->cell(index);
|
||||
if(cell.collapsible()) continue;
|
||||
float height = 0;
|
||||
if(cell.size().height() == Size::Maximum) {
|
||||
height = Size::Maximum;
|
||||
|
@ -148,9 +154,13 @@ auto mVerticalLayout::setGeometry(Geometry geometry) -> type& {
|
|||
}
|
||||
|
||||
float fixedHeight = 0;
|
||||
for(uint index : range(state.cells.size())) {
|
||||
float spacing = 0;
|
||||
for(uint index : range(cellCount())) {
|
||||
auto cell = this->cell(index);
|
||||
if(cell.collapsible()) continue;
|
||||
if(heights[index] != Size::Maximum) fixedHeight += heights[index];
|
||||
if(index != cellCount() - 1) fixedHeight += cell(index).spacing();
|
||||
fixedHeight += spacing;
|
||||
spacing = cell.spacing();
|
||||
}
|
||||
|
||||
float maximumHeight = (geometry.height() - fixedHeight) / maximumHeights;
|
||||
|
@ -160,10 +170,11 @@ auto mVerticalLayout::setGeometry(Geometry geometry) -> type& {
|
|||
|
||||
float geometryX = geometry.x();
|
||||
float geometryY = geometry.y();
|
||||
for(auto index : range(cellCount())) {
|
||||
for(uint index : range(cellCount())) {
|
||||
auto cell = this->cell(index);
|
||||
if(cell.collapsible()) continue;
|
||||
float geometryWidth = width;
|
||||
float geometryHeight = heights[index];
|
||||
auto cell = this->cell(index);
|
||||
auto alignment = cell.alignment();
|
||||
if(!alignment) alignment = this->alignment();
|
||||
if(!alignment) alignment = 0.0;
|
||||
|
@ -177,6 +188,7 @@ auto mVerticalLayout::setGeometry(Geometry geometry) -> type& {
|
|||
geometryY += geometryHeight + cell.spacing();
|
||||
}
|
||||
|
||||
mSizable::setGeometry(requestedGeometry);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -218,6 +230,11 @@ auto mVerticalLayoutCell::alignment() const -> maybe<float> {
|
|||
return state.alignment;
|
||||
}
|
||||
|
||||
auto mVerticalLayoutCell::collapsible() const -> bool {
|
||||
if(state.sizable) return state.sizable->collapsible() && !state.sizable->visible();
|
||||
return false;
|
||||
}
|
||||
|
||||
auto mVerticalLayoutCell::destruct() -> void {
|
||||
if(auto& sizable = state.sizable) sizable->destruct();
|
||||
mObject::destruct();
|
||||
|
|
|
@ -49,6 +49,7 @@ struct mVerticalLayoutCell : mObject {
|
|||
using type = mVerticalLayoutCell;
|
||||
|
||||
auto alignment() const -> maybe<float>;
|
||||
auto collapsible() const -> bool;
|
||||
auto setAlignment(maybe<float> alignment) -> type&;
|
||||
auto setEnabled(bool enabled) -> type& override;
|
||||
auto setFont(const Font& font) -> type& override;
|
||||
|
|
|
@ -10,6 +10,9 @@ auto pApplication::run() -> void {
|
|||
while(!Application::state().quit) {
|
||||
Application::doMain();
|
||||
processEvents();
|
||||
//avoid spinlooping the thread when there is no main loop ...
|
||||
//when there is one, Application::onMain() is expected to sleep when possible instead
|
||||
if(!Application::state().onMain) usleep(2000);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,7 +21,13 @@ auto pApplication::pendingEvents() -> bool {
|
|||
}
|
||||
|
||||
auto pApplication::processEvents() -> void {
|
||||
while(pendingEvents()) gtk_main_iteration_do(false);
|
||||
//GTK can sometimes return gtk_pending_events() == true forever,
|
||||
//no matter how many times gtk_main_iteration_do() is called.
|
||||
//implement a timeout to prevent hiro from hanging forever in this case.
|
||||
auto time = chrono::millisecond();
|
||||
while(pendingEvents() && chrono::millisecond() - time < 50) {
|
||||
gtk_main_iteration_do(false);
|
||||
}
|
||||
for(auto& window : state().windows) window->_synchronizeGeometry();
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,8 @@ auto pFont::size(PangoFontDescription* font, const string& text) -> Size {
|
|||
pango_layout_set_text(layout, text, -1);
|
||||
int width = 0, height = 0;
|
||||
pango_layout_get_pixel_size(layout, &width, &height);
|
||||
g_object_unref((gpointer)layout);
|
||||
g_object_unref(layout);
|
||||
g_object_unref(context);
|
||||
return {width, height};
|
||||
}
|
||||
|
||||
|
@ -29,7 +30,7 @@ auto pFont::family(const string& family) -> string {
|
|||
#elif defined(DISPLAY_XORG)
|
||||
if(family == Font::Sans ) return "Sans";
|
||||
if(family == Font::Serif) return "Serif";
|
||||
if(family == Font::Mono ) return "Liberation Mono";
|
||||
if(family == Font::Mono ) return "Monospace";
|
||||
return family ? family : "Sans";
|
||||
#else
|
||||
return family;
|
||||
|
|
|
@ -12,6 +12,9 @@ auto pSizable::minimumSize() const -> Size {
|
|||
return {0, 0};
|
||||
}
|
||||
|
||||
auto pSizable::setCollapsible(bool collapsible) -> void {
|
||||
}
|
||||
|
||||
auto pSizable::setGeometry(Geometry geometry) -> void {
|
||||
self().doSize();
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ struct pSizable : pObject {
|
|||
Declare(Sizable, Object)
|
||||
|
||||
virtual auto minimumSize() const -> Size;
|
||||
virtual auto setCollapsible(bool collapsible) -> void;
|
||||
virtual auto setGeometry(Geometry geometry) -> void;
|
||||
};
|
||||
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
namespace hiro {
|
||||
|
||||
static auto Timer_trigger(pTimer* p) -> signed {
|
||||
//prevent all timers from firing once the program has been terminated
|
||||
if(Application::state().quit) return false;
|
||||
|
||||
//timer may have been disabled prior to triggering, so check state
|
||||
if(p->self().enabled(true)) p->self().doActivate();
|
||||
|
||||
|
|
|
@ -46,6 +46,24 @@ static auto Label_expose(GtkWidget* widget, GdkEvent* event, pLabel* p) -> int {
|
|||
return false;
|
||||
}
|
||||
|
||||
static auto Label_mousePress(GtkWidget* widget, GdkEventButton* event, pLabel* p) -> int {
|
||||
switch(event->button) {
|
||||
case 1: p->self().doMousePress(Mouse::Button::Left); break;
|
||||
case 2: p->self().doMousePress(Mouse::Button::Middle); break;
|
||||
case 3: p->self().doMousePress(Mouse::Button::Right); break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static auto Label_mouseRelease(GtkWidget* widget, GdkEventButton* event, pLabel* p) -> int {
|
||||
switch(event->button) {
|
||||
case 1: p->self().doMouseRelease(Mouse::Button::Left); break;
|
||||
case 2: p->self().doMouseRelease(Mouse::Button::Middle); break;
|
||||
case 3: p->self().doMouseRelease(Mouse::Button::Right); break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
auto pLabel::construct() -> void {
|
||||
gtkWidget = gtk_event_box_new();
|
||||
subWidget = gtk_label_new("");
|
||||
|
@ -57,6 +75,8 @@ auto pLabel::construct() -> void {
|
|||
setForegroundColor(state().foregroundColor);
|
||||
setText(state().text);
|
||||
|
||||
g_signal_connect(G_OBJECT(gtkWidget), "button-press-event", G_CALLBACK(Label_mousePress), (gpointer)this);
|
||||
g_signal_connect(G_OBJECT(gtkWidget), "button-release-event", G_CALLBACK(Label_mouseRelease), (gpointer)this);
|
||||
#if HIRO_GTK==2
|
||||
g_signal_connect(G_OBJECT(subWidget), "expose-event", G_CALLBACK(Label_expose), (gpointer)this);
|
||||
#elif HIRO_GTK==3
|
||||
|
|
|
@ -7,13 +7,7 @@ static auto SourceEdit_change(GtkTextBuffer*, pSourceEdit* p) -> void {
|
|||
}
|
||||
|
||||
static auto SourceEdit_move(GObject*, GParamSpec*, pSourceEdit* p) -> void {
|
||||
signed offset = 0;
|
||||
g_object_get(G_OBJECT(p->gtkSourceBuffer), "cursor-position", &offset, nullptr);
|
||||
|
||||
if(p->state().cursor.offset() != offset) {
|
||||
p->state().cursor.setOffset(offset);
|
||||
if(!p->locked()) p->self().doMove();
|
||||
}
|
||||
if(!p->locked()) p->self().doMove();
|
||||
}
|
||||
|
||||
auto pSourceEdit::construct() -> void {
|
||||
|
@ -24,16 +18,16 @@ auto pSourceEdit::construct() -> void {
|
|||
gtk_scrolled_window_set_shadow_type(gtkScrolledWindow, GTK_SHADOW_ETCHED_IN);
|
||||
|
||||
gtkSourceLanguageManager = gtk_source_language_manager_get_default();
|
||||
gtkSourceLanguage = gtk_source_language_manager_get_language(gtkSourceLanguageManager, "cpp");
|
||||
gtkSourceLanguage = gtk_source_language_manager_get_language(gtkSourceLanguageManager, "");
|
||||
|
||||
gtkSourceStyleSchemeManager = gtk_source_style_scheme_manager_get_default();
|
||||
gtkSourceStyleScheme = gtk_source_style_scheme_manager_get_scheme(gtkSourceStyleSchemeManager, "oblivion");
|
||||
gtkSourceStyleScheme = gtk_source_style_scheme_manager_get_scheme(gtkSourceStyleSchemeManager, "classic");
|
||||
|
||||
gtkSourceBuffer = gtk_source_buffer_new(nullptr);
|
||||
gtkTextBuffer = GTK_TEXT_BUFFER(gtkSourceBuffer);
|
||||
gtk_source_buffer_set_highlight_matching_brackets(gtkSourceBuffer, true);
|
||||
gtk_source_buffer_set_highlight_syntax(gtkSourceBuffer, true);
|
||||
//gtk_source_buffer_set_language(gtkSourceBuffer, gtkSourceLanguage);
|
||||
gtk_source_buffer_set_language(gtkSourceBuffer, gtkSourceLanguage);
|
||||
gtk_source_buffer_set_style_scheme(gtkSourceBuffer, gtkSourceStyleScheme);
|
||||
|
||||
gtkSourceView = (GtkSourceView*)gtk_source_view_new_with_buffer(gtkSourceBuffer);
|
||||
|
@ -55,6 +49,9 @@ auto pSourceEdit::construct() -> void {
|
|||
gtk_widget_show(gtkWidgetSourceView);
|
||||
|
||||
setEditable(state().editable);
|
||||
setLanguage(state().language);
|
||||
setNumbered(state().numbered);
|
||||
setScheme(state().scheme);
|
||||
setText(state().text);
|
||||
setWordWrap(state().wordWrap);
|
||||
|
||||
|
@ -70,6 +67,23 @@ auto pSourceEdit::destruct() -> void {
|
|||
gtk_widget_destroy(gtkWidget);
|
||||
}
|
||||
|
||||
auto pSourceEdit::cursor() const -> Cursor {
|
||||
Cursor cursor;
|
||||
int offset = 0;
|
||||
g_object_get(G_OBJECT(gtkSourceBuffer), "cursor-position", &offset, nullptr);
|
||||
cursor.setOffset(offset);
|
||||
GtkTextIter start, end;
|
||||
if(gtk_text_buffer_get_selection_bounds(gtkTextBuffer, &start, &end)) {
|
||||
//if selecting text from left to right, the cursor may be ahead of the selection start ...
|
||||
//since hiro combines selection bounds (end-start) into length, move the offset to the start
|
||||
int origin = gtk_text_iter_get_offset(&start);
|
||||
cursor.setOffset(origin);
|
||||
int length = gtk_text_iter_get_offset(&end) - origin;
|
||||
cursor.setLength(length);
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
auto pSourceEdit::setCursor(Cursor cursor) -> void {
|
||||
lock();
|
||||
GtkTextIter offset, length;
|
||||
|
@ -92,45 +106,33 @@ auto pSourceEdit::setFocused() -> void {
|
|||
gtk_widget_grab_focus(gtkWidgetSourceView);
|
||||
}
|
||||
|
||||
/*
|
||||
auto pSourceEdit::setPosition(signed position) -> void {
|
||||
lock();
|
||||
GtkTextIter iter;
|
||||
//note: iterators must be initialized via get_iter() before calling set_offset()
|
||||
gtk_text_buffer_get_end_iter(gtkTextBuffer, &iter);
|
||||
if(position >= 0) {
|
||||
gtk_text_iter_set_offset(&iter, position);
|
||||
} else {
|
||||
state().position = gtk_text_iter_get_offset(&iter);
|
||||
}
|
||||
gtk_text_buffer_place_cursor(gtkTextBuffer, &iter);
|
||||
auto mark = gtk_text_buffer_get_mark(gtkTextBuffer, "insert");
|
||||
gtk_text_view_scroll_mark_onscreen(gtkTextView, mark);
|
||||
unlock();
|
||||
auto pSourceEdit::setLanguage(const string& language) -> void {
|
||||
string name;
|
||||
if(language == "C") name = "c";
|
||||
if(language == "C++") name = "cpp";
|
||||
if(language == "Make") name = "makefile";
|
||||
gtkSourceLanguage = gtk_source_language_manager_get_language(gtkSourceLanguageManager, name);
|
||||
gtk_source_buffer_set_language(gtkSourceBuffer, gtkSourceLanguage);
|
||||
}
|
||||
|
||||
auto pSourceEdit::setSelected(Position selected) -> void {
|
||||
lock();
|
||||
GtkTextIter iter;
|
||||
gtk_text_buffer_get_end_iter(gtkTextBuffer, &iter);
|
||||
signed offset = gtk_text_iter_get_offset(&iter);
|
||||
if(selected.x() < 0 || selected.x() > offset) selected.setX(offset);
|
||||
if(selected.y() < 0 || selected.y() > offset) selected.setY(offset);
|
||||
state().selected = selected;
|
||||
GtkTextIter startIter;
|
||||
gtk_text_buffer_get_start_iter(gtkTextBuffer, &startIter);
|
||||
gtk_text_iter_set_offset(&startIter, selected.x());
|
||||
GtkTextIter endIter;
|
||||
gtk_text_buffer_get_end_iter(gtkTextBuffer, &endIter);
|
||||
gtk_text_iter_set_offset(&endIter, selected.y());
|
||||
gtk_text_buffer_select_range(gtkTextBuffer, &startIter, &endIter);
|
||||
unlock();
|
||||
auto pSourceEdit::setNumbered(bool numbered) -> void {
|
||||
gtk_source_view_set_show_line_numbers(gtkSourceView, numbered);
|
||||
}
|
||||
|
||||
auto pSourceEdit::setScheme(const string& requestedScheme) -> void {
|
||||
auto scheme = requestedScheme ? requestedScheme : "classic";
|
||||
gtkSourceStyleScheme = gtk_source_style_scheme_manager_get_scheme(gtkSourceStyleSchemeManager, scheme.downcase());
|
||||
if(!gtkSourceStyleScheme) gtkSourceStyleScheme = gtk_source_style_scheme_manager_get_scheme(gtkSourceStyleSchemeManager, "classic");
|
||||
gtk_source_buffer_set_style_scheme(gtkSourceBuffer, gtkSourceStyleScheme);
|
||||
}
|
||||
*/
|
||||
|
||||
auto pSourceEdit::setText(const string& text) -> void {
|
||||
lock();
|
||||
//prevent Ctrl+Z from undoing the newly assigned text ...
|
||||
//for instance, a text editor widget setting the initial document here
|
||||
gtk_source_buffer_begin_not_undoable_action(gtkSourceBuffer);
|
||||
gtk_text_buffer_set_text(gtkTextBuffer, text, -1);
|
||||
gtk_source_buffer_end_not_undoable_action(gtkSourceBuffer);
|
||||
unlock();
|
||||
}
|
||||
|
||||
|
|
|
@ -5,9 +5,13 @@ namespace hiro {
|
|||
struct pSourceEdit : pWidget {
|
||||
Declare(SourceEdit, Widget)
|
||||
|
||||
auto cursor() const -> Cursor;
|
||||
auto setCursor(Cursor cursor) -> void;
|
||||
auto setEditable(bool editable) -> void;
|
||||
auto setFocused() -> void override;
|
||||
auto setLanguage(const string& language) -> void;
|
||||
auto setNumbered(bool numbered) -> void;
|
||||
auto setScheme(const string& scheme) -> void;
|
||||
auto setText(const string& text) -> void;
|
||||
auto setWordWrap(bool wordWrap) -> void;
|
||||
auto text() const -> string;
|
||||
|
|
|
@ -294,30 +294,40 @@ auto pTableView::_doEdit(GtkCellRendererText* gtkCellRendererText, const char* p
|
|||
}
|
||||
}
|
||||
|
||||
auto pTableView::_doEvent(GdkEventButton* event) -> signed {
|
||||
GtkTreePath* path = nullptr;
|
||||
gtk_tree_view_get_path_at_pos(gtkTreeView, event->x, event->y, &path, nullptr, nullptr, nullptr);
|
||||
auto pTableView::_doEvent(GdkEventButton* gdkEvent) -> signed {
|
||||
if(gdkEvent->type == GDK_BUTTON_PRESS) {
|
||||
//detect when the empty space of the GtkTreeView is clicked; and clear the selection
|
||||
GtkTreePath* gtkPath = nullptr;
|
||||
gtk_tree_view_get_path_at_pos(gtkTreeView, gdkEvent->x, gdkEvent->y, >kPath, nullptr, nullptr, nullptr);
|
||||
if(!gtkPath) {
|
||||
//the first time a GtkTreeView widget is clicked, even if the empty space of the widget is clicked,
|
||||
//a "changed" signal will be sent after the "button-press-event", to activate the first item in the tree
|
||||
//this is undesirable, so set a flag to undo the next selection change during the "changed" signal
|
||||
suppressChange = true;
|
||||
if(gtk_tree_selection_count_selected_rows(gtkTreeSelection) > 0) {
|
||||
gtk_tree_selection_unselect_all(gtkTreeSelection);
|
||||
for(auto& item : state().items) item->setSelected(false);
|
||||
self().doChange();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(event->type == GDK_BUTTON_PRESS) {
|
||||
//when clicking in empty space below the last table view item; GTK+ does not deselect all items;
|
||||
//below code enables this functionality, to match behavior with all other UI toolkits (and because it's very convenient to have)
|
||||
if(path == nullptr && gtk_tree_selection_count_selected_rows(gtkTreeSelection) > 0) {
|
||||
for(auto& item : state().items) item->setSelected(false);
|
||||
self().doChange();
|
||||
return true;
|
||||
if(gdkEvent->button == 3) {
|
||||
//multi-selection mode:
|
||||
//if multiple items are selected, and one item is right-clicked on (for a context menu), GTK clears selection on all other items
|
||||
//block this behavior so that onContext() handler can work on more than one selected item at a time
|
||||
if(gtkPath && gtk_tree_selection_path_is_selected(gtkTreeSelection, gtkPath)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(event->type == GDK_BUTTON_PRESS && event->button == 3) {
|
||||
//this check prevents the loss of selection on other items if the item under the mouse cursor is currently selected
|
||||
if(path && gtk_tree_selection_path_is_selected(gtkTreeSelection, path)) return true;
|
||||
}
|
||||
|
||||
if(event->type == GDK_BUTTON_RELEASE && event->button == 3) {
|
||||
//handle action during right-click release; as button-press-event is sent prior to selection update
|
||||
//without this, the callback handler would see the previous selection state instead
|
||||
self().doContext();
|
||||
return false;
|
||||
if(gdkEvent->type == GDK_BUTTON_RELEASE) {
|
||||
suppressChange = false;
|
||||
if(gdkEvent->button == 3) {
|
||||
//handle action during right-click release; as button-press-event is sent prior to selection update
|
||||
//without this, the callback handler would see the previous selection state instead
|
||||
self().doContext();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -365,6 +375,12 @@ auto pTableView::_doToggle(GtkCellRendererToggle* gtkCellRendererToggle, const c
|
|||
//this prevents firing an onChange event when the actual selection has not changed
|
||||
//this is particularly important for the motion-notify-event binding
|
||||
auto pTableView::_updateSelected() -> void {
|
||||
if(suppressChange) {
|
||||
suppressChange = false;
|
||||
gtk_tree_selection_unselect_all(gtkTreeSelection);
|
||||
return;
|
||||
}
|
||||
|
||||
vector<unsigned> selected;
|
||||
|
||||
GList* list = gtk_tree_selection_get_selected_rows(gtkTreeSelection, >kTreeModel);
|
||||
|
|
|
@ -44,6 +44,7 @@ struct pTableView : pWidget {
|
|||
GtkListStore* gtkListStore = nullptr;
|
||||
GtkTreeModel* gtkTreeModel = nullptr;
|
||||
vector<uint> currentSelection;
|
||||
bool suppressChange = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ auto pTreeView::construct() -> void {
|
|||
gtkTreeView = GTK_TREE_VIEW(gtkWidgetChild);
|
||||
gtkTreeSelection = gtk_tree_view_get_selection(gtkTreeView);
|
||||
gtk_tree_view_set_headers_visible(gtkTreeView, false);
|
||||
gtk_tree_view_set_show_expanders(gtkTreeView, false);
|
||||
gtk_tree_view_set_level_indentation(gtkTreeView, 20);
|
||||
gtk_container_add(GTK_CONTAINER(gtkWidget), gtkWidgetChild);
|
||||
gtk_widget_show(gtkWidgetChild);
|
||||
|
||||
|
@ -58,10 +60,19 @@ auto pTreeView::construct() -> void {
|
|||
g_signal_connect(G_OBJECT(gtkTreeSelection), "changed", G_CALLBACK(TreeView_change), (gpointer)this);
|
||||
g_signal_connect(G_OBJECT(gtkCellToggle), "toggled", G_CALLBACK(TreeView_toggle), (gpointer)this);
|
||||
|
||||
//Ctrl+F triggers a small popup window at the bottom of the GtkTreeView, which clears the currently selected item(s)
|
||||
//this is undesirable for amethyst, which uses the active item to display a document to edit, and binds Ctrl+F to a document find function
|
||||
//for now, disable GtkTreeView's interactive search: longer term, more thought will need to go into if this is ever desirable or not
|
||||
//gtk_tree_view_set_enable_search(gtkTreeView, false) does not work
|
||||
//gtk_tree_view_set_search_column(gtkTreeView, -1) does not work
|
||||
gtkEntry = (GtkEntry*)gtk_entry_new();
|
||||
gtk_tree_view_set_search_entry(gtkTreeView, gtkEntry);
|
||||
|
||||
pWidget::construct();
|
||||
}
|
||||
|
||||
auto pTreeView::destruct() -> void {
|
||||
gtk_widget_destroy(GTK_WIDGET(gtkEntry));
|
||||
gtk_widget_destroy(gtkWidgetChild);
|
||||
gtk_widget_destroy(gtkWidget);
|
||||
}
|
||||
|
@ -105,24 +116,39 @@ auto pTreeView::_activatePath(GtkTreePath* gtkPath) -> void {
|
|||
}
|
||||
|
||||
auto pTreeView::_buttonEvent(GdkEventButton* gdkEvent) -> signed {
|
||||
GtkTreePath* gtkPath = nullptr;
|
||||
gtk_tree_view_get_path_at_pos(gtkTreeView, gdkEvent->x, gdkEvent->y, >kPath, nullptr, nullptr, nullptr);
|
||||
|
||||
if(gdkEvent->type == GDK_BUTTON_PRESS) {
|
||||
//detect when the empty space of the GtkTreeView is clicked; and clear the selection
|
||||
if(gtkPath == nullptr && gtk_tree_selection_count_selected_rows(gtkTreeSelection) > 0) {
|
||||
gtk_tree_selection_unselect_all(gtkTreeSelection);
|
||||
state().selectedPath.reset();
|
||||
self().doChange();
|
||||
return true;
|
||||
GtkTreePath* gtkPath = nullptr;
|
||||
gtk_tree_view_get_path_at_pos(gtkTreeView, gdkEvent->x, gdkEvent->y, >kPath, nullptr, nullptr, nullptr);
|
||||
if(!gtkPath) {
|
||||
//the first time a GtkTreeView widget is clicked, even if the empty space of the widget is clicked,
|
||||
//a "changed" signal will be sent after the "button-press-event", to activate the first item in the tree
|
||||
//this is undesirable, so set a flag to undo the next selection change during the "changed" signal
|
||||
suppressChange = true;
|
||||
if(gtk_tree_selection_count_selected_rows(gtkTreeSelection) > 0) {
|
||||
gtk_tree_selection_unselect_all(gtkTreeSelection);
|
||||
state().selectedPath.reset();
|
||||
self().doChange();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(gdkEvent->button == 3) {
|
||||
//multi-selection mode: (not implemented in TreeView yet ... but code is here anyway for future use)
|
||||
//if multiple items are selected, and one item is right-clicked on (for a context menu), GTK clears selection on all other items
|
||||
//block this behavior so that onContext() handler can work on more than one selected item at a time
|
||||
if(gtkPath && gtk_tree_selection_path_is_selected(gtkTreeSelection, gtkPath)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(gdkEvent->type == GDK_BUTTON_RELEASE && gdkEvent->button == 3) {
|
||||
//handle right-click context menu
|
||||
//have to detect on button release instead of press; as GTK+ does not update new selection prior to press event
|
||||
self().doContext();
|
||||
return false;
|
||||
if(gdkEvent->type == GDK_BUTTON_RELEASE) {
|
||||
suppressChange = false;
|
||||
if(gdkEvent->button == 3) {
|
||||
//handle action during right-click release; as button-press-event is sent prior to selection update
|
||||
//without this, the callback handler would see the previous selection state instead
|
||||
self().doContext();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -173,6 +199,12 @@ auto pTreeView::_togglePath(string path) -> void {
|
|||
}
|
||||
|
||||
auto pTreeView::_updateSelected() -> void {
|
||||
if(suppressChange) {
|
||||
suppressChange = false;
|
||||
gtk_tree_selection_unselect_all(gtkTreeSelection);
|
||||
return;
|
||||
}
|
||||
|
||||
GtkTreeIter iter;
|
||||
if(gtk_tree_selection_get_selected(gtkTreeSelection, >kTreeModel, &iter)) {
|
||||
char* gtkPath = gtk_tree_model_get_string_from_iter(gtkTreeModel, &iter);
|
||||
|
|
|
@ -27,6 +27,8 @@ struct pTreeView : pWidget {
|
|||
GtkCellRenderer* gtkCellToggle = nullptr;
|
||||
GtkCellRenderer* gtkCellPixbuf = nullptr;
|
||||
GtkCellRenderer* gtkCellText = nullptr;
|
||||
GtkEntry* gtkEntry = nullptr;
|
||||
bool suppressChange = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -329,7 +329,9 @@ auto pWindow::setGeometry(Geometry geometry) -> void {
|
|||
setMinimumSize(state().minimumSize);
|
||||
|
||||
auto time1 = chrono::millisecond();
|
||||
while(chrono::millisecond() - time1 < 20) Application::processEvents();
|
||||
while(chrono::millisecond() - time1 < 20) {
|
||||
Application::processEvents();
|
||||
}
|
||||
|
||||
gtk_window_resize(GTK_WINDOW(widget), geometry.width(), geometry.height() + _menuHeight() + _statusHeight());
|
||||
|
||||
|
@ -627,7 +629,7 @@ auto pWindow::_synchronizeState() -> void {
|
|||
if(!gtk_widget_get_realized(widget)) return;
|
||||
|
||||
#if defined(DISPLAY_WINDOWS)
|
||||
auto window = GDK_WINDOW_HWND(gtk_widget_get_window(widget));
|
||||
auto window = (HWND)GDK_WINDOW_HWND(gtk_widget_get_window(widget));
|
||||
|
||||
bool maximized = IsZoomed(window);
|
||||
bool minimized = IsIconic(window);
|
||||
|
|
|
@ -189,6 +189,8 @@ struct QtLabel : public QWidget {
|
|||
Q_OBJECT
|
||||
public:
|
||||
QtLabel(pLabel& p) : p(p) {}
|
||||
auto mousePressEvent(QMouseEvent*) -> void;
|
||||
auto mouseReleaseEvent(QMouseEvent*) -> void;
|
||||
auto paintEvent(QPaintEvent*) -> void;
|
||||
pLabel& p;
|
||||
};
|
||||
|
|
1379
hiro/qt/qt.moc
1379
hiro/qt/qt.moc
File diff suppressed because it is too large
Load Diff
|
@ -12,6 +12,9 @@ auto pSizable::minimumSize() const -> Size {
|
|||
return {0, 0};
|
||||
}
|
||||
|
||||
auto pSizable::setCollapsible(bool collapsible) -> void {
|
||||
}
|
||||
|
||||
auto pSizable::setGeometry(Geometry geometry) -> void {
|
||||
self().doSize();
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ struct pSizable : pObject {
|
|||
Declare(Sizable, Object)
|
||||
|
||||
virtual auto minimumSize() const -> Size;
|
||||
virtual auto setCollapsible(bool collapsible) -> void;
|
||||
virtual auto setGeometry(Geometry geometry) -> void;
|
||||
};
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ auto pTimer::setInterval(unsigned interval) -> void {
|
|||
}
|
||||
|
||||
auto QtTimer::onActivate() -> void {
|
||||
if(Application::state().quit) return;
|
||||
p.self().doActivate();
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue