mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-02-23 22:52:34 +01:00
byuu says: Changelog: - gb/mbc6: mapper is now functional, but Net de Get has some text corruption¹ - gb/mbc7: mapper is now functional² - gb/cpu: HDMA syncs other components after each byte transfer now - gb/ppu: LY,LX forced to zero when LCDC.d7 is lowered (eg disabled), not when it's raised (eg enabled) - gb/ppu: the LCD does not run at all when LCDC.d7 is clear³ - fixes graphical corruption between scene transitions in Legend of Zelda - Oracle of Ages - thanks to Cydrak, Shonumi, gekkio for their input on the cause of this issue - md/controller: renamed "Gamepad" to "Control Pad" per official terminology - md/controller: added "Fighting Pad" (6-button controller) emulation [hex\_usr] - processor/m68k: fixed TAS to set data.d7 when EA.mode==DataRegisterDirect; fixes Asterix - hiro/windows: removed carriage returns from mouse.cpp and desktop.cpp - ruby/audio/alsa: added device driver selection [SuperMikeMan] - ruby/audio/ao: set format.matrix=nullptr to prevent a crash on some systems [SuperMikeMan] - ruby/video/cgl: rename term() to terminate() to fix a crash on macOS [Sintendo] ¹: The observation that this mapper split $4000-7fff into two banks came from MAME's implementation. But their implementation was quite broken and incomplete, so I didn't actually use any of it. The observation that this mapper split $a000-bfff into two banks came from Tauwasser, and I did directly use that information, plus the knowledge that $0400/$0800 are the RAM bank select registers. The text corruption is due to a race condition with timing. The game is transferring font letters via HDMA, but the game code ends up setting the bank# with the font a bit too late after the HDMA has already occurred. I'm not sure how to fix this ... as a whole, I assumed my Game Boy timing was pretty good, but apparently it's not that good. ²: The entire design of this mapper comes from endrift's notes. endrift gets full credit for higan being able to emulate this mapper. Note that the accelerometer implementation is still not tested, and probably won't work right until I tweak the sensitivity a lot. ³: So the fun part of this is ... it breaks the strict 60fps rate of the Game Boy. This was always inevitable: certain timing conditions can stretch frames, too. But this is pretty much an absolute deal breaker for something like Vsync timing. This pretty much requires adaptive sync to run well without audio stuttering during the transition. There's currently one very important detail missing: when the LCD is turned off, presumably the image on the screen fades to white. I do not know how long this process takes, or how to really go about emulating it. Right now as an incomplete patch, I'm simply leaving the last displayed image on the screen until the LCD is turned on again. But I will have to output white, as well as add code to break out of the emulation loop periodically when the LCD is left off eg indefinitely, or bad things would happen. I'll work something out and then implement. Another detail is I'm not sure how long it takes for the LCD to start rendering again once enabled. Right now, it's immediate. I've heard it's as long as 1/60th of a second, but that really seems incredibly excessive? I'd like to know at least a reasonably well-supported estimate before I implement that.
1174 lines
33 KiB
C++
1174 lines
33 KiB
C++
auto M68K::testCondition(uint4 condition) -> bool {
|
|
switch(condition) {
|
|
case 0: return true; //T
|
|
case 1: return false; //F
|
|
case 2: return !r.c && !r.z; //HI
|
|
case 3: return r.c || r.z; //LS
|
|
case 4: return !r.c; //CC,HS
|
|
case 5: return r.c; //CS,LO
|
|
case 6: return !r.z; //NE
|
|
case 7: return r.z; //EQ
|
|
case 8: return !r.v; //VC
|
|
case 9: return r.v; //VS
|
|
case 10: return !r.n; //PL
|
|
case 11: return r.n; //MI
|
|
case 12: return r.n == r.v; //GE
|
|
case 13: return r.n != r.v; //LT
|
|
case 14: return r.n == r.v && !r.z; //GT
|
|
case 15: return r.n != r.v || r.z; //LE
|
|
}
|
|
unreachable;
|
|
}
|
|
|
|
//
|
|
|
|
template<> auto M68K::bytes<Byte>() -> uint { return 1; }
|
|
template<> auto M68K::bytes<Word>() -> uint { return 2; }
|
|
template<> auto M68K::bytes<Long>() -> uint { return 4; }
|
|
|
|
template<> auto M68K::bits<Byte>() -> uint { return 8; }
|
|
template<> auto M68K::bits<Word>() -> uint { return 16; }
|
|
template<> auto M68K::bits<Long>() -> uint { return 32; }
|
|
|
|
template<uint Size> auto M68K::lsb() -> uint32 { return 1; }
|
|
|
|
template<> auto M68K::msb<Byte>() -> uint32 { return 0x80; }
|
|
template<> auto M68K::msb<Word>() -> uint32 { return 0x8000; }
|
|
template<> auto M68K::msb<Long>() -> uint32 { return 0x80000000; }
|
|
|
|
template<> auto M68K::mask<Byte>() -> uint32 { return 0xff; }
|
|
template<> auto M68K::mask<Word>() -> uint32 { return 0xffff; }
|
|
template<> auto M68K::mask<Long>() -> uint32 { return 0xffffffff; }
|
|
|
|
template<> auto M68K::clip<Byte>(uint32 data) -> uint32 { return data & 0xff; }
|
|
template<> auto M68K::clip<Word>(uint32 data) -> uint32 { return data & 0xffff; }
|
|
template<> auto M68K::clip<Long>(uint32 data) -> uint32 { return data & 0xffffffff; }
|
|
|
|
template<> auto M68K::sign<Byte>(uint32 data) -> int32 { return (int8)data; }
|
|
template<> auto M68K::sign<Word>(uint32 data) -> int32 { return (int16)data; }
|
|
template<> auto M68K::sign<Long>(uint32 data) -> int32 { return (int32)data; }
|
|
|
|
//
|
|
|
|
auto M68K::instructionABCD(EffectiveAddress with, EffectiveAddress from) -> void {
|
|
auto source = read<Byte>(from);
|
|
auto target = read<Byte, Hold>(with);
|
|
auto result = source + target + r.x;
|
|
bool c = false;
|
|
bool v = false;
|
|
|
|
if(((target ^ source ^ result) & 0x10) || (result & 0x0f) >= 0x0a) {
|
|
auto previous = result;
|
|
result += 0x06;
|
|
v |= ((~previous & 0x80) & (result & 0x80));
|
|
}
|
|
|
|
if(result >= 0xa0) {
|
|
auto previous = result;
|
|
result += 0x60;
|
|
c = true;
|
|
v |= ((~previous & 0x80) & (result & 0x80));
|
|
}
|
|
|
|
write<Byte>(with, result);
|
|
|
|
r.c = c;
|
|
r.v = v;
|
|
r.z = clip<Byte>(result) ? 0 : r.z;
|
|
r.n = sign<Byte>(result) < 0;
|
|
r.x = r.c;
|
|
}
|
|
|
|
template<uint Size, bool Extend> auto M68K::ADD(uint32 source, uint32 target) -> uint32 {
|
|
auto result = (uint64)source + target;
|
|
if(Extend) result += r.x;
|
|
|
|
r.c = sign<Size>(result >> 1) < 0;
|
|
r.v = sign<Size>(~(target ^ source) & (target ^ result)) < 0;
|
|
r.z = clip<Size>(result) ? 0 : (Extend ? r.z : 1);
|
|
r.n = sign<Size>(result) < 0;
|
|
r.x = r.c;
|
|
|
|
return clip<Size>(result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionADD(EffectiveAddress from, DataRegister with) -> void {
|
|
auto source = read<Size>(from);
|
|
auto target = read<Size>(with);
|
|
auto result = ADD<Size>(source, target);
|
|
write<Size>(with, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionADD(DataRegister from, EffectiveAddress with) -> void {
|
|
auto source = read<Size>(from);
|
|
auto target = read<Size, Hold>(with);
|
|
auto result = ADD<Size>(source, target);
|
|
write<Size>(with, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionADDA(AddressRegister ar, EffectiveAddress ea) -> void {
|
|
auto source = sign<Size>(read<Size>(ea));
|
|
auto target = read<Long>(ar);
|
|
write<Long>(ar, source + target);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionADDI(EffectiveAddress modify) -> void {
|
|
auto source = readPC<Size>();
|
|
auto target = read<Size, Hold>(modify);
|
|
auto result = ADD<Size>(source, target);
|
|
write<Size>(modify, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionADDQ(uint4 immediate, EffectiveAddress with) -> void {
|
|
auto source = immediate;
|
|
auto target = read<Size, Hold>(with);
|
|
auto result = ADD<Size>(source, target);
|
|
write<Size>(with, result);
|
|
}
|
|
|
|
//Size is ignored: always uses Long
|
|
template<uint Size> auto M68K::instructionADDQ(uint4 immediate, AddressRegister with) -> void {
|
|
auto result = read<Long>(with) + immediate;
|
|
write<Long>(with, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionADDX(EffectiveAddress with, EffectiveAddress from) -> void {
|
|
auto source = read<Size>(from);
|
|
auto target = read<Size, Hold>(with);
|
|
auto result = ADD<Size, Extend>(source, target);
|
|
write<Size>(with, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::AND(uint32 source, uint32 target) -> uint32 {
|
|
uint32 result = target & source;
|
|
|
|
r.c = 0;
|
|
r.v = 0;
|
|
r.z = clip<Size>(result) == 0;
|
|
r.n = sign<Size>(result) < 0;
|
|
|
|
return clip<Size>(result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionAND(EffectiveAddress from, DataRegister with) -> void {
|
|
auto source = read<Size>(from);
|
|
auto target = read<Size>(with);
|
|
auto result = AND<Size>(source, target);
|
|
write<Size>(with, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionAND(DataRegister from, EffectiveAddress with) -> void {
|
|
auto source = read<Size>(from);
|
|
auto target = read<Size, Hold>(with);
|
|
auto result = AND<Size>(source, target);
|
|
write<Size>(with, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionANDI(EffectiveAddress with) -> void {
|
|
auto source = readPC<Size>();
|
|
auto target = read<Size, Hold>(with);
|
|
auto result = AND<Size>(source, target);
|
|
write<Size>(with, result);
|
|
}
|
|
|
|
auto M68K::instructionANDI_TO_CCR() -> void {
|
|
auto data = readPC<Word>();
|
|
writeCCR(readCCR() & data);
|
|
}
|
|
|
|
auto M68K::instructionANDI_TO_SR() -> void {
|
|
if(!supervisor()) return;
|
|
|
|
auto data = readPC<Word>();
|
|
writeSR(readSR() & data);
|
|
}
|
|
|
|
template<uint Size> auto M68K::ASL(uint32 result, uint shift) -> uint32 {
|
|
bool carry = false;
|
|
uint32 overflow = 0;
|
|
for(auto _ : range(shift)) {
|
|
carry = result & msb<Size>();
|
|
uint32 before = result;
|
|
result <<= 1;
|
|
overflow |= before ^ result;
|
|
}
|
|
|
|
r.c = carry;
|
|
r.v = sign<Size>(overflow) < 0;
|
|
r.z = clip<Size>(result) == 0;
|
|
r.n = sign<Size>(result) < 0;
|
|
if(shift) r.x = r.c;
|
|
|
|
return clip<Size>(result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionASL(uint4 shift, DataRegister modify) -> void {
|
|
auto result = ASL<Size>(read<Size>(modify), shift);
|
|
write<Size>(modify, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionASL(DataRegister shift, DataRegister modify) -> void {
|
|
auto count = read<Long>(shift) & 63;
|
|
auto result = ASL<Size>(read<Size>(modify), count);
|
|
write<Size>(modify, result);
|
|
}
|
|
|
|
auto M68K::instructionASL(EffectiveAddress modify) -> void {
|
|
auto result = ASL<Word>(read<Word, Hold>(modify), 1);
|
|
write<Word>(modify, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::ASR(uint32 result, uint shift) -> uint32 {
|
|
bool carry = false;
|
|
uint32 overflow = 0;
|
|
for(auto _ : range(shift)) {
|
|
carry = result & lsb<Size>();
|
|
uint32 before = result;
|
|
result = sign<Size>(result) >> 1;
|
|
overflow |= before ^ result;
|
|
}
|
|
|
|
r.c = carry;
|
|
r.v = sign<Size>(overflow) < 0;
|
|
r.z = clip<Size>(result) == 0;
|
|
r.n = sign<Size>(result) < 0;
|
|
if(shift) r.x = r.c;
|
|
|
|
return clip<Size>(result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionASR(uint4 shift, DataRegister modify) -> void {
|
|
auto result = ASR<Size>(read<Size>(modify), shift);
|
|
write<Size>(modify, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionASR(DataRegister shift, DataRegister modify) -> void {
|
|
auto count = read<Long>(shift) & 63;
|
|
auto result = ASR<Size>(read<Size>(modify), count);
|
|
write<Size>(modify, result);
|
|
}
|
|
|
|
auto M68K::instructionASR(EffectiveAddress modify) -> void {
|
|
auto result = ASR<Word>(read<Word, Hold>(modify), 1);
|
|
write<Word>(modify, result);
|
|
}
|
|
|
|
auto M68K::instructionBCC(uint4 condition, uint8 displacement) -> void {
|
|
auto extension = readPC<Word>();
|
|
if(displacement) r.pc -= 2;
|
|
if(condition >= 2 && !testCondition(condition)) return;
|
|
if(condition == 1) push<Long>(r.pc);
|
|
r.pc += displacement ? (int8_t)displacement : (int16_t)extension - 2;
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionBCHG(DataRegister bit, EffectiveAddress with) -> void {
|
|
auto index = read<Size>(bit) & bits<Size>() - 1;
|
|
auto test = read<Size, Hold>(with);
|
|
r.z = test.bit(index) == 0;
|
|
test.bit(index) ^= 1;
|
|
write<Size>(with, test);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionBCHG(EffectiveAddress with) -> void {
|
|
auto index = readPC<Word>() & bits<Size>() - 1;
|
|
auto test = read<Size, Hold>(with);
|
|
r.z = test.bit(index) == 0;
|
|
test.bit(index) ^= 1;
|
|
write<Size>(with, test);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionBCLR(DataRegister bit, EffectiveAddress with) -> void {
|
|
auto index = read<Size>(bit) & bits<Size>() - 1;
|
|
auto test = read<Size, Hold>(with);
|
|
r.z = test.bit(index) == 0;
|
|
test.bit(index) = 0;
|
|
write<Size>(with, test);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionBCLR(EffectiveAddress with) -> void {
|
|
auto index = readPC<Word>() & bits<Size>() - 1;
|
|
auto test = read<Size, Hold>(with);
|
|
r.z = test.bit(index) == 0;
|
|
test.bit(index) = 0;
|
|
write<Size>(with, test);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionBSET(DataRegister bit, EffectiveAddress with) -> void {
|
|
auto index = read<Size>(bit) & bits<Size>() - 1;
|
|
auto test = read<Size, Hold>(with);
|
|
r.z = test.bit(index) == 0;
|
|
test.bit(index) = 1;
|
|
write<Size>(with, test);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionBSET(EffectiveAddress with) -> void {
|
|
auto index = readPC<Word>() & bits<Size>() - 1;
|
|
auto test = read<Size, Hold>(with);
|
|
r.z = test.bit(index) == 0;
|
|
test.bit(index) = 1;
|
|
write<Size>(with, test);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionBTST(DataRegister bit, EffectiveAddress with) -> void {
|
|
auto index = read<Size>(bit) & bits<Size>() - 1;
|
|
auto test = read<Size>(with);
|
|
r.z = test.bit(index) == 0;
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionBTST(EffectiveAddress with) -> void {
|
|
auto index = readPC<Word>() & bits<Size>() - 1;
|
|
auto test = read<Size>(with);
|
|
r.z = test.bit(index) == 0;
|
|
}
|
|
|
|
auto M68K::instructionCHK(DataRegister compare, EffectiveAddress maximum) -> void {
|
|
auto source = read<Word>(maximum);
|
|
auto target = read<Word>(compare);
|
|
|
|
r.z = clip<Word>(target) == 0;
|
|
r.n = sign<Word>(target) < 0;
|
|
if(r.n) return exception(Exception::BoundsCheck, Vector::BoundsCheck);
|
|
|
|
auto result = (uint64)target - source;
|
|
r.c = sign<Word>(result >> 1) < 0;
|
|
r.v = sign<Word>((target ^ source) & (target ^ result)) < 0;
|
|
r.z = clip<Word>(result) == 0;
|
|
r.n = sign<Word>(result) < 0;
|
|
if(r.n == r.v && !r.z) return exception(Exception::BoundsCheck, Vector::BoundsCheck);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionCLR(EffectiveAddress ea) -> void {
|
|
read<Size, Hold>(ea);
|
|
write<Size>(ea, 0);
|
|
|
|
r.c = 0;
|
|
r.v = 0;
|
|
r.z = 1;
|
|
r.n = 0;
|
|
}
|
|
|
|
template<uint Size> auto M68K::CMP(uint32 source, uint32 target) -> uint32 {
|
|
auto result = (uint64)target - source;
|
|
|
|
r.c = sign<Size>(result >> 1) < 0;
|
|
r.v = sign<Size>((target ^ source) & (target ^ result)) < 0;
|
|
r.z = clip<Size>(result) == 0;
|
|
r.n = sign<Size>(result) < 0;
|
|
|
|
return clip<Size>(result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionCMP(DataRegister dr, EffectiveAddress ea) -> void {
|
|
auto source = read<Size>(ea);
|
|
auto target = read<Size>(dr);
|
|
CMP<Size>(source, target);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionCMPA(AddressRegister ar, EffectiveAddress ea) -> void {
|
|
auto source = sign<Size>(read<Size>(ea));
|
|
auto target = read<Long>(ar);
|
|
CMP<Long>(source, target);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionCMPI(EffectiveAddress ea) -> void {
|
|
auto source = readPC<Size>();
|
|
auto target = read<Size>(ea);
|
|
CMP<Size>(source, target);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionCMPM(EffectiveAddress ax, EffectiveAddress ay) -> void {
|
|
auto source = read<Size>(ay);
|
|
auto target = read<Size>(ax);
|
|
CMP<Size>(source, target);
|
|
}
|
|
|
|
auto M68K::instructionDBCC(uint4 condition, DataRegister dr) -> void {
|
|
auto displacement = readPC<Word>();
|
|
if(!testCondition(condition)) {
|
|
uint16 result = read<Word>(dr);
|
|
write<Word>(dr, result - 1);
|
|
if(result) r.pc -= 2, r.pc += sign<Word>(displacement);
|
|
}
|
|
}
|
|
|
|
template<bool Sign> auto M68K::DIV(uint16 divisor, DataRegister with) -> void {
|
|
auto dividend = read<Long>(with);
|
|
bool negativeQuotient = false;
|
|
bool negativeRemainder = false;
|
|
bool overflow = false;
|
|
|
|
if(divisor == 0) return exception(Exception::DivisionByZero, Vector::DivisionByZero);
|
|
|
|
if(Sign) {
|
|
negativeQuotient = (dividend >> 31) ^ (divisor >> 15);
|
|
if(dividend >> 31) dividend = -dividend, negativeRemainder = true;
|
|
if(divisor >> 15) divisor = -divisor;
|
|
}
|
|
|
|
auto result = dividend;
|
|
|
|
for(auto _ : range(16)) {
|
|
bool lb = false;
|
|
if(result >= (uint32)divisor << 15) result -= divisor << 15, lb = true;
|
|
|
|
bool ob = result >> 31;
|
|
result = result << 1 | lb;
|
|
|
|
if(ob) overflow = true;
|
|
}
|
|
|
|
if(Sign) {
|
|
if((uint16)result > 0x7fff + negativeQuotient) overflow = true;
|
|
}
|
|
|
|
if(result >> 16 >= divisor) overflow = true;
|
|
|
|
if(Sign && !overflow) {
|
|
if(negativeQuotient) result = ((-result) & 0xffff) | (result & 0xffff0000);
|
|
if(negativeRemainder) result = (((-(result >> 16)) << 16) & 0xffff0000) | (result & 0xffff);
|
|
}
|
|
|
|
if(!overflow) write<Long>(with, result);
|
|
|
|
r.c = 0;
|
|
r.v = overflow;
|
|
r.z = clip<Word>(result) == 0;
|
|
r.n = sign<Word>(result) < 0;
|
|
}
|
|
|
|
auto M68K::instructionDIVS(DataRegister with, EffectiveAddress from) -> void {
|
|
auto divisor = read<Word>(from);
|
|
DIV<1>(divisor, with);
|
|
}
|
|
|
|
auto M68K::instructionDIVU(DataRegister with, EffectiveAddress from) -> void {
|
|
auto divisor = read<Word>(from);
|
|
DIV<0>(divisor, with);
|
|
}
|
|
|
|
template<uint Size> auto M68K::EOR(uint32 source, uint32 target) -> uint32 {
|
|
uint32 result = target ^ source;
|
|
|
|
r.c = 0;
|
|
r.v = 0;
|
|
r.z = clip<Size>(result) == 0;
|
|
r.n = sign<Size>(result) < 0;
|
|
|
|
return clip<Size>(result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionEOR(DataRegister from, EffectiveAddress with) -> void {
|
|
auto source = read<Size>(from);
|
|
auto target = read<Size, Hold>(with);
|
|
auto result = EOR<Size>(source, target);
|
|
write<Size>(with, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionEORI(EffectiveAddress with) -> void {
|
|
auto source = readPC<Size>();
|
|
auto target = read<Size, Hold>(with);
|
|
auto result = EOR<Size>(source, target);
|
|
write<Size>(with, result);
|
|
}
|
|
|
|
auto M68K::instructionEORI_TO_CCR() -> void {
|
|
auto data = readPC<Word>();
|
|
writeCCR(readCCR() ^ data);
|
|
}
|
|
|
|
auto M68K::instructionEORI_TO_SR() -> void {
|
|
if(!supervisor()) return;
|
|
|
|
auto data = readPC<Word>();
|
|
writeSR(readSR() ^ data);
|
|
}
|
|
|
|
auto M68K::instructionEXG(DataRegister x, DataRegister y) -> void {
|
|
auto z = read<Long>(x);
|
|
write<Long>(x, read<Long>(y));
|
|
write<Long>(y, z);
|
|
}
|
|
|
|
auto M68K::instructionEXG(AddressRegister x, AddressRegister y) -> void {
|
|
auto z = read<Long>(x);
|
|
write<Long>(x, read<Long>(y));
|
|
write<Long>(y, z);
|
|
}
|
|
|
|
auto M68K::instructionEXG(DataRegister x, AddressRegister y) -> void {
|
|
auto z = read<Long>(x);
|
|
write<Long>(x, read<Long>(y));
|
|
write<Long>(y, z);
|
|
}
|
|
|
|
template<> auto M68K::instructionEXT<Word>(DataRegister with) -> void {
|
|
auto result = (int8)read<Byte>(with);
|
|
write<Word>(with, result);
|
|
|
|
r.c = 0;
|
|
r.v = 0;
|
|
r.z = clip<Word>(result) == 0;
|
|
r.n = sign<Word>(result) < 0;
|
|
}
|
|
|
|
template<> auto M68K::instructionEXT<Long>(DataRegister with) -> void {
|
|
auto result = (int16)read<Word>(with);
|
|
write<Long>(with, result);
|
|
|
|
r.c = 0;
|
|
r.v = 0;
|
|
r.z = clip<Long>(result) == 0;
|
|
r.n = sign<Long>(result) < 0;
|
|
}
|
|
|
|
auto M68K::instructionILLEGAL() -> void {
|
|
r.pc -= 2;
|
|
if(opcode >> 12 == 0xa) return exception(Exception::Illegal, Vector::IllegalLineA);
|
|
if(opcode >> 12 == 0xf) return exception(Exception::Illegal, Vector::IllegalLineF);
|
|
return exception(Exception::Illegal, Vector::Illegal);
|
|
}
|
|
|
|
auto M68K::instructionJMP(EffectiveAddress target) -> void {
|
|
r.pc = fetch<Long>(target);
|
|
}
|
|
|
|
auto M68K::instructionJSR(EffectiveAddress target) -> void {
|
|
auto pc = fetch<Long>(target);
|
|
push<Long>(r.pc);
|
|
r.pc = pc;
|
|
}
|
|
|
|
auto M68K::instructionLEA(AddressRegister ar, EffectiveAddress ea) -> void {
|
|
write<Long>(ar, fetch<Long>(ea));
|
|
}
|
|
|
|
auto M68K::instructionLINK(AddressRegister with) -> void {
|
|
auto displacement = (int16)readPC<Word>();
|
|
auto sp = AddressRegister{7};
|
|
push<Long>(read<Long>(with));
|
|
write<Long>(with, read<Long>(sp));
|
|
write<Long>(sp, read<Long>(sp) + displacement);
|
|
}
|
|
|
|
template<uint Size> auto M68K::LSL(uint32 result, uint shift) -> uint32 {
|
|
bool carry = false;
|
|
for(auto _ : range(shift)) {
|
|
carry = result & msb<Size>();
|
|
result <<= 1;
|
|
}
|
|
|
|
r.c = carry;
|
|
r.v = 0;
|
|
r.z = clip<Size>(result) == 0;
|
|
r.n = sign<Size>(result) < 0;
|
|
if(shift) r.x = r.c;
|
|
|
|
return clip<Size>(result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionLSL(uint4 immediate, DataRegister dr) -> void {
|
|
auto result = LSL<Size>(read<Size>(dr), immediate);
|
|
write<Size>(dr, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionLSL(DataRegister sr, DataRegister dr) -> void {
|
|
auto shift = read<Long>(sr) & 63;
|
|
auto result = LSL<Size>(read<Size>(dr), shift);
|
|
write<Size>(dr, result);
|
|
}
|
|
|
|
auto M68K::instructionLSL(EffectiveAddress ea) -> void {
|
|
auto result = LSL<Word>(read<Word, Hold>(ea), 1);
|
|
write<Word>(ea, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::LSR(uint32 result, uint shift) -> uint32 {
|
|
bool carry = false;
|
|
for(auto _ : range(shift)) {
|
|
carry = result & lsb<Size>();
|
|
result >>= 1;
|
|
}
|
|
|
|
r.c = carry;
|
|
r.v = 0;
|
|
r.z = clip<Size>(result) == 0;
|
|
r.n = sign<Size>(result) < 0;
|
|
if(shift) r.x = r.c;
|
|
|
|
return clip<Size>(result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionLSR(uint4 immediate, DataRegister dr) -> void {
|
|
auto result = LSR<Size>(read<Size>(dr), immediate);
|
|
write<Size>(dr, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionLSR(DataRegister shift, DataRegister dr) -> void {
|
|
auto count = read<Long>(shift) & 63;
|
|
auto result = LSR<Size>(read<Size>(dr), count);
|
|
write<Size>(dr, result);
|
|
}
|
|
|
|
auto M68K::instructionLSR(EffectiveAddress ea) -> void {
|
|
auto result = LSR<Word>(read<Word, Hold>(ea), 1);
|
|
write<Word>(ea, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionMOVE(EffectiveAddress to, EffectiveAddress from) -> void {
|
|
auto data = read<Size>(from);
|
|
write<Size>(to, data);
|
|
|
|
r.c = 0;
|
|
r.v = 0;
|
|
r.z = clip<Size>(data) == 0;
|
|
r.n = sign<Size>(data) < 0;
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionMOVEA(AddressRegister ar, EffectiveAddress ea) -> void {
|
|
auto data = sign<Size>(read<Size>(ea));
|
|
write<Long>(ar, data);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionMOVEM_TO_MEM(EffectiveAddress to) -> void {
|
|
auto list = readPC<Word>();
|
|
auto addr = fetch<Long>(to);
|
|
|
|
for(uint n : range(16)) {
|
|
if(!list.bit(n)) continue;
|
|
//pre-decrement mode traverses registers in reverse order {A7-A0, D7-D0}
|
|
uint index = to.mode == AddressRegisterIndirectWithPreDecrement ? 15 - n : n;
|
|
|
|
if(to.mode == AddressRegisterIndirectWithPreDecrement) addr -= bytes<Size>();
|
|
auto data = index < 8 ? read<Size>(DataRegister{index}) : read<Size>(AddressRegister{index});
|
|
write<Size>(addr, data);
|
|
if(to.mode != AddressRegisterIndirectWithPreDecrement) addr += bytes<Size>();
|
|
}
|
|
|
|
AddressRegister with{to.reg};
|
|
if(to.mode == AddressRegisterIndirectWithPreDecrement ) write<Long>(with, addr);
|
|
if(to.mode == AddressRegisterIndirectWithPostIncrement) write<Long>(with, addr);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionMOVEM_TO_REG(EffectiveAddress from) -> void {
|
|
auto list = readPC<Word>();
|
|
auto addr = fetch<Long>(from);
|
|
|
|
for(uint n : range(16)) {
|
|
if(!list.bit(n)) continue;
|
|
uint index = from.mode == AddressRegisterIndirectWithPreDecrement ? 15 - n : n;
|
|
|
|
if(from.mode == AddressRegisterIndirectWithPreDecrement) addr -= bytes<Size>();
|
|
auto data = read<Size>(addr);
|
|
data = sign<Size>(data);
|
|
index < 8 ? write<Long>(DataRegister{index}, data) : write<Long>(AddressRegister{index}, data);
|
|
if(from.mode != AddressRegisterIndirectWithPreDecrement) addr += bytes<Size>();
|
|
}
|
|
|
|
AddressRegister with{from.reg};
|
|
if(from.mode == AddressRegisterIndirectWithPreDecrement ) write<Long>(with, addr);
|
|
if(from.mode == AddressRegisterIndirectWithPostIncrement) write<Long>(with, addr);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionMOVEP(DataRegister from, EffectiveAddress to) -> void {
|
|
auto address = fetch<Size>(to);
|
|
auto data = read<Long>(from);
|
|
uint shift = bits<Size>();
|
|
for(auto _ : range(bytes<Size>())) {
|
|
shift -= 8;
|
|
write<Byte>(address, data >> shift);
|
|
address += 2;
|
|
}
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionMOVEP(EffectiveAddress from, DataRegister to) -> void {
|
|
auto address = fetch<Size>(from);
|
|
auto data = read<Long>(to);
|
|
uint shift = bits<Size>();
|
|
for(auto _ : range(bytes<Size>())) {
|
|
shift -= 8;
|
|
data &= ~(0xff << shift);
|
|
data |= read<Byte>(address) << shift;
|
|
address += 2;
|
|
}
|
|
write<Long>(to, data);
|
|
}
|
|
|
|
auto M68K::instructionMOVEQ(DataRegister dr, uint8 immediate) -> void {
|
|
write<Long>(dr, sign<Byte>(immediate));
|
|
|
|
r.c = 0;
|
|
r.v = 0;
|
|
r.z = clip<Byte>(immediate) == 0;
|
|
r.n = sign<Byte>(immediate) < 0;
|
|
}
|
|
|
|
auto M68K::instructionMOVE_FROM_SR(EffectiveAddress ea) -> void {
|
|
auto data = readSR();
|
|
write<Word>(ea, data);
|
|
}
|
|
|
|
auto M68K::instructionMOVE_TO_CCR(EffectiveAddress ea) -> void {
|
|
auto data = read<Word>(ea);
|
|
writeCCR(data);
|
|
}
|
|
|
|
auto M68K::instructionMOVE_TO_SR(EffectiveAddress ea) -> void {
|
|
if(!supervisor()) return;
|
|
|
|
auto data = read<Word>(ea);
|
|
writeSR(data);
|
|
}
|
|
|
|
auto M68K::instructionMOVE_FROM_USP(AddressRegister to) -> void {
|
|
if(!supervisor()) return;
|
|
|
|
write<Long>(to, r.sp);
|
|
}
|
|
|
|
auto M68K::instructionMOVE_TO_USP(AddressRegister from) -> void {
|
|
if(!supervisor()) return;
|
|
|
|
r.sp = read<Long>(from);
|
|
}
|
|
|
|
auto M68K::instructionMULS(DataRegister with, EffectiveAddress from) -> void {
|
|
auto source = read<Word>(from);
|
|
auto target = read<Word>(with);
|
|
auto result = (int16)source * (int16)target;
|
|
write<Long>(with, result);
|
|
|
|
r.c = 0;
|
|
r.v = 0;
|
|
r.z = clip<Long>(result) == 0;
|
|
r.n = sign<Long>(result) < 0;
|
|
}
|
|
|
|
auto M68K::instructionMULU(DataRegister with, EffectiveAddress from) -> void {
|
|
auto source = read<Word>(from);
|
|
auto target = read<Word>(with);
|
|
auto result = source * target;
|
|
write<Long>(with, result);
|
|
|
|
r.c = 0;
|
|
r.v = 0;
|
|
r.z = clip<Long>(result) == 0;
|
|
r.n = sign<Long>(result) < 0;
|
|
}
|
|
|
|
auto M68K::instructionNBCD(EffectiveAddress with) -> void {
|
|
auto source = read<Byte, Hold>(with);
|
|
auto target = 0u;
|
|
auto result = target - source - r.x;
|
|
bool c = false;
|
|
bool v = false;
|
|
|
|
const bool adjustLo = (target ^ source ^ result) & 0x10;
|
|
const bool adjustHi = result & 0x100;
|
|
|
|
if(adjustLo) {
|
|
auto previous = result;
|
|
result -= 0x06;
|
|
c = (~previous & 0x80) & ( result & 0x80);
|
|
v |= ( previous & 0x80) & (~result & 0x80);
|
|
}
|
|
|
|
if(adjustHi) {
|
|
auto previous = result;
|
|
result -= 0x60;
|
|
c = true;
|
|
v |= (previous & 0x80) & (~result & 0x80);
|
|
}
|
|
|
|
write<Byte>(with, result);
|
|
|
|
r.c = c;
|
|
r.v = v;
|
|
r.z = clip<Byte>(result) ? 0 : r.z;
|
|
r.n = sign<Byte>(result) < 0;
|
|
r.x = r.c;
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionNEG(EffectiveAddress with) -> void {
|
|
auto result = SUB<Size>(read<Size, Hold>(with), 0);
|
|
write<Size>(with, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionNEGX(EffectiveAddress with) -> void {
|
|
auto result = SUB<Size, Extend>(read<Size, Hold>(with), 0);
|
|
write<Size>(with, result);
|
|
}
|
|
|
|
auto M68K::instructionNOP() -> void {
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionNOT(EffectiveAddress with) -> void {
|
|
auto result = ~read<Size, Hold>(with);
|
|
write<Size>(with, result);
|
|
|
|
r.c = 0;
|
|
r.v = 0;
|
|
r.z = clip<Size>(result) == 0;
|
|
r.n = sign<Size>(result) < 0;
|
|
}
|
|
|
|
template<uint Size> auto M68K::OR(uint32 source, uint32 target) -> uint32 {
|
|
auto result = target | source;
|
|
|
|
r.c = 0;
|
|
r.v = 0;
|
|
r.z = clip<Size>(result) == 0;
|
|
r.n = sign<Size>(result) < 0;
|
|
|
|
return clip<Size>(result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionOR(EffectiveAddress from, DataRegister with) -> void {
|
|
auto source = read<Size>(from);
|
|
auto target = read<Size>(with);
|
|
auto result = OR<Size>(source, target);
|
|
write<Size>(with, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionOR(DataRegister from, EffectiveAddress with) -> void {
|
|
auto source = read<Size>(from);
|
|
auto target = read<Size, Hold>(with);
|
|
auto result = OR<Size>(source, target);
|
|
write<Size>(with, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionORI(EffectiveAddress with) -> void {
|
|
auto source = readPC<Size>();
|
|
auto target = read<Size, Hold>(with);
|
|
auto result = OR<Size>(source, target);
|
|
write<Size>(with, result);
|
|
}
|
|
|
|
auto M68K::instructionORI_TO_CCR() -> void {
|
|
auto data = readPC<Word>();
|
|
writeCCR(readCCR() | data);
|
|
}
|
|
|
|
auto M68K::instructionORI_TO_SR() -> void {
|
|
if(!supervisor()) return;
|
|
|
|
auto data = readPC<Word>();
|
|
writeSR(readSR() | data);
|
|
}
|
|
|
|
auto M68K::instructionPEA(EffectiveAddress from) -> void {
|
|
auto data = fetch<Long>(from);
|
|
push<Long>(data);
|
|
}
|
|
|
|
auto M68K::instructionRESET() -> void {
|
|
if(!supervisor()) return;
|
|
|
|
r.reset = true;
|
|
}
|
|
|
|
template<uint Size> auto M68K::ROL(uint32 result, uint shift) -> uint32 {
|
|
bool carry = false;
|
|
for(auto _ : range(shift)) {
|
|
carry = result & msb<Size>();
|
|
result = result << 1 | carry;
|
|
}
|
|
|
|
r.c = carry;
|
|
r.v = 0;
|
|
r.z = clip<Size>(result) == 0;
|
|
r.n = sign<Size>(result) < 0;
|
|
|
|
return clip<Size>(result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionROL(uint4 shift, DataRegister modify) -> void {
|
|
auto result = ROL<Size>(read<Size>(modify), shift);
|
|
write<Size>(modify, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionROL(DataRegister shift, DataRegister modify) -> void {
|
|
auto count = read<Long>(shift) & 63;
|
|
auto result = ROL<Size>(read<Size>(modify), count);
|
|
write<Size>(modify, result);
|
|
}
|
|
|
|
auto M68K::instructionROL(EffectiveAddress modify) -> void {
|
|
auto result = ROL<Word>(read<Word, Hold>(modify), 1);
|
|
write<Word>(modify, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::ROR(uint32 result, uint shift) -> uint32 {
|
|
bool carry = false;
|
|
for(auto _ : range(shift)) {
|
|
carry = result & lsb<Size>();
|
|
result >>= 1;
|
|
if(carry) result |= msb<Size>();
|
|
}
|
|
|
|
r.c = carry;
|
|
r.v = 0;
|
|
r.z = clip<Size>(result) == 0;
|
|
r.n = sign<Size>(result) < 0;
|
|
|
|
return clip<Size>(result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionROR(uint4 shift, DataRegister modify) -> void {
|
|
auto result = ROR<Size>(read<Size>(modify), shift);
|
|
write<Size>(modify, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionROR(DataRegister shift, DataRegister modify) -> void {
|
|
auto count = read<Long>(shift) & 63;
|
|
auto result = ROR<Size>(read<Size>(modify), count);
|
|
write<Size>(modify, result);
|
|
}
|
|
|
|
auto M68K::instructionROR(EffectiveAddress modify) -> void {
|
|
auto result = ROR<Word>(read<Word, Hold>(modify), 1);
|
|
write<Word>(modify, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::ROXL(uint32 result, uint shift) -> uint32 {
|
|
bool carry = r.x;
|
|
for(auto _ : range(shift)) {
|
|
bool extend = carry;
|
|
carry = result & msb<Size>();
|
|
result = result << 1 | extend;
|
|
}
|
|
|
|
r.c = carry;
|
|
r.v = 0;
|
|
r.z = clip<Size>(result) == 0;
|
|
r.n = sign<Size>(result) < 0;
|
|
r.x = r.c;
|
|
|
|
return clip<Size>(result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionROXL(uint4 shift, DataRegister modify) -> void {
|
|
auto result = ROXL<Size>(read<Size>(modify), shift);
|
|
write<Size>(modify, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionROXL(DataRegister shift, DataRegister modify) -> void {
|
|
auto count = read<Long>(shift) & 63;
|
|
auto result = ROXL<Size>(read<Size>(modify), count);
|
|
write<Size>(modify, result);
|
|
}
|
|
|
|
auto M68K::instructionROXL(EffectiveAddress modify) -> void {
|
|
auto result = ROXL<Word>(read<Word, Hold>(modify), 1);
|
|
write<Word>(modify, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::ROXR(uint32 result, uint shift) -> uint32 {
|
|
bool carry = r.x;
|
|
for(auto _ : range(shift)) {
|
|
bool extend = carry;
|
|
carry = result & lsb<Size>();
|
|
result >>= 1;
|
|
if(extend) result |= msb<Size>();
|
|
}
|
|
|
|
r.c = carry;
|
|
r.v = 0;
|
|
r.z = clip<Size>(result) == 0;
|
|
r.n = sign<Size>(result) < 0;
|
|
r.x = r.c;
|
|
|
|
return clip<Size>(result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionROXR(uint4 shift, DataRegister modify) -> void {
|
|
auto result = ROXR<Size>(read<Size>(modify), shift);
|
|
write<Size>(modify, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionROXR(DataRegister shift, DataRegister modify) -> void {
|
|
auto count = read<Long>(shift) & 63;
|
|
auto result = ROXR<Size>(read<Size>(modify), count);
|
|
write<Size>(modify, result);
|
|
}
|
|
|
|
auto M68K::instructionROXR(EffectiveAddress modify) -> void {
|
|
auto result = ROXR<Word>(read<Word, Hold>(modify), 1);
|
|
write<Word>(modify, result);
|
|
}
|
|
|
|
auto M68K::instructionRTE() -> void {
|
|
if(!supervisor()) return;
|
|
|
|
auto sr = pop<Word>();
|
|
r.pc = pop<Long>();
|
|
writeSR(sr);
|
|
}
|
|
|
|
auto M68K::instructionRTR() -> void {
|
|
writeCCR(pop<Word>());
|
|
r.pc = pop<Long>();
|
|
}
|
|
|
|
auto M68K::instructionRTS() -> void {
|
|
r.pc = pop<Long>();
|
|
}
|
|
|
|
auto M68K::instructionSBCD(EffectiveAddress with, EffectiveAddress from) -> void {
|
|
auto source = read<Byte>(from);
|
|
auto target = read<Byte, Hold>(with);
|
|
auto result = target - source - r.x;
|
|
bool c = false;
|
|
bool v = false;
|
|
|
|
const bool adjustLo = (target ^ source ^ result) & 0x10;
|
|
const bool adjustHi = result & 0x100;
|
|
|
|
if(adjustLo) {
|
|
auto previous = result;
|
|
result -= 0x06;
|
|
c = (~previous & 0x80) & ( result & 0x80);
|
|
v |= ( previous & 0x80) & (~result & 0x80);
|
|
}
|
|
|
|
if(adjustHi) {
|
|
auto previous = result;
|
|
result -= 0x60;
|
|
c = true;
|
|
v |= (previous & 0x80) & (~result & 0x80);
|
|
}
|
|
|
|
write<Byte>(with, result);
|
|
|
|
r.c = c;
|
|
r.v = v;
|
|
r.z = clip<Byte>(result) ? 0 : r.z;
|
|
r.n = sign<Byte>(result) < 0;
|
|
r.x = r.c;
|
|
}
|
|
|
|
auto M68K::instructionSCC(uint4 condition, EffectiveAddress to) -> void {
|
|
uint8 result = testCondition(condition) ? ~0 : 0;
|
|
write<Byte>(to, result);
|
|
}
|
|
|
|
auto M68K::instructionSTOP() -> void {
|
|
if(!supervisor()) return;
|
|
|
|
auto sr = readPC<Word>();
|
|
writeSR(sr);
|
|
r.stop = true;
|
|
}
|
|
|
|
template<uint Size, bool Extend> auto M68K::SUB(uint32 source, uint32 target) -> uint32 {
|
|
auto result = (uint64)target - source;
|
|
if(Extend) result -= r.x;
|
|
|
|
r.c = sign<Size>(result >> 1) < 0;
|
|
r.v = sign<Size>((target ^ source) & (target ^ result)) < 0;
|
|
r.z = clip<Size>(result) ? 0 : (Extend ? r.z : 1);
|
|
r.n = sign<Size>(result) < 0;
|
|
r.x = r.c;
|
|
|
|
return result;
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionSUB(EffectiveAddress source_, DataRegister target_) -> void {
|
|
auto source = read<Size>(source_);
|
|
auto target = read<Size>(target_);
|
|
auto result = SUB<Size>(source, target);
|
|
write<Size>(target_, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionSUB(DataRegister source_, EffectiveAddress target_) -> void {
|
|
auto source = read<Size>(source_);
|
|
auto target = read<Size, Hold>(target_);
|
|
auto result = SUB<Size>(source, target);
|
|
write<Size>(target_, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionSUBA(AddressRegister to, EffectiveAddress from) -> void {
|
|
auto source = sign<Size>(read<Size>(from));
|
|
auto target = read<Long>(to);
|
|
write<Long>(to, target - source);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionSUBI(EffectiveAddress with) -> void {
|
|
auto source = readPC<Size>();
|
|
auto target = read<Size, Hold>(with);
|
|
auto result = SUB<Size>(source, target);
|
|
write<Size>(with, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionSUBQ(uint4 immediate, EffectiveAddress with) -> void {
|
|
auto source = immediate;
|
|
auto target = read<Size, Hold>(with);
|
|
auto result = SUB<Size>(source, target);
|
|
write<Size>(with, result);
|
|
}
|
|
|
|
//Size is ignored: always uses Long
|
|
template<uint Size> auto M68K::instructionSUBQ(uint4 immediate, AddressRegister with) -> void {
|
|
auto result = read<Long>(with) - immediate;
|
|
write<Long>(with, result);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionSUBX(EffectiveAddress with, EffectiveAddress from) -> void {
|
|
auto source = read<Size>(from);
|
|
auto target = read<Size, Hold>(with);
|
|
auto result = SUB<Size, Extend>(source, target);
|
|
write<Size>(with, result);
|
|
}
|
|
|
|
auto M68K::instructionSWAP(DataRegister with) -> void {
|
|
auto result = read<Long>(with);
|
|
result = result >> 16 | result << 16;
|
|
write<Long>(with, result);
|
|
|
|
r.c = 0;
|
|
r.v = 0;
|
|
r.z = clip<Long>(result) == 0;
|
|
r.n = sign<Long>(result) < 0;
|
|
}
|
|
|
|
auto M68K::instructionTAS(EffectiveAddress with) -> void {
|
|
uint32 data;
|
|
|
|
if(with.mode == DataRegisterDirect) {
|
|
data = read<Byte, Hold>(with);
|
|
write<Byte>(with, data | 0x80);
|
|
} else {
|
|
//Mega Drive models 1&2 have a bug that prevents TAS write from taking effect
|
|
//this bugged behavior is required for certain software to function correctly
|
|
data = read<Byte>(with);
|
|
step(4);
|
|
}
|
|
|
|
r.c = 0;
|
|
r.v = 0;
|
|
r.z = clip<Byte>(data) == 0;
|
|
r.n = sign<Byte>(data) < 0;
|
|
}
|
|
|
|
auto M68K::instructionTRAP(uint4 vector) -> void {
|
|
exception(Exception::Trap, 32 + vector, r.i);
|
|
}
|
|
|
|
auto M68K::instructionTRAPV() -> void {
|
|
if(r.v) exception(Exception::Overflow, Vector::Overflow);
|
|
}
|
|
|
|
template<uint Size> auto M68K::instructionTST(EffectiveAddress ea) -> void {
|
|
auto data = read<Size>(ea);
|
|
|
|
r.c = 0;
|
|
r.v = 0;
|
|
r.z = clip<Size>(data) == 0;
|
|
r.n = sign<Size>(data) < 0;
|
|
}
|
|
|
|
auto M68K::instructionUNLK(AddressRegister with) -> void {
|
|
auto sp = AddressRegister{7};
|
|
write<Long>(sp, read<Long>(with));
|
|
write<Long>(with, pop<Long>());
|
|
}
|