diff --git a/Archs/Z80/CZ80Instruction.cpp b/Archs/Z80/CZ80Instruction.cpp new file mode 100644 index 00000000..4d521082 --- /dev/null +++ b/Archs/Z80/CZ80Instruction.cpp @@ -0,0 +1,269 @@ +#include "Archs/Z80/CZ80Instruction.h" +#include "Archs/Z80/Z80Opcodes.h" +#include "Core/Common.h" +#include "Core/Expression.h" +#include "Core/FileManager.h" +#include "Core/Misc.h" +#include "Util/Util.h" + +CZ80Instruction::CZ80Instruction(const tZ80Opcode& sourceOpcode, Z80OpcodeVariables& vars) +{ + this->Opcode = sourceOpcode; + this->Vars = vars; + this->RamPos = 0; +} + +bool CZ80Instruction::Validate(const ValidateState& state) +{ + RamPos = g_fileManager->getVirtualAddress(); + + Vars.Length = Opcode.length; + Vars.Encoding = Opcode.encoding; + Vars.WriteImmediate8 = false; + Vars.WriteImmediate16 = false; + + // Evaluate immediate + if (Opcode.flags & Z80_HAS_IMMEDIATE) + { + if (!Vars.ImmediateExpression.evaluateInteger(Vars.Immediate)) + { + Logger::queueError(Logger::Error, L"Invalid expression"); + return false; + } + if ((Opcode.flags & Z80_HAS_2_IMMEDIATES) && !Vars.ImmediateExpression2.evaluateInteger(Vars.Immediate2)) + { + Logger::queueError(Logger::Error, L"Invalid expression"); + return false; + } + if (Vars.IsNegative) + { + Vars.Immediate = -Vars.Immediate; + } + if (Opcode.flags & Z80_JUMP_RELATIVE) + { + Vars.Immediate = (Vars.Immediate - RamPos - 2); + } + + int64_t min = INT64_MIN; + int64_t max = INT64_MAX; + int64_t min2 = INT64_MIN; + int64_t max2 = INT64_MAX; + Vars.WriteImmediate8 = false; + Vars.WriteImmediate16 = false; + if (Opcode.flags & Z80_INTERRUPT_MODE) + { + min = 0; + max = 2; + } + else if (Opcode.flags & Z80_IMMEDIATE_U3) + { + min = 0; + max = 7; + } + else if (Opcode.flags & Z80_IMMEDIATE_U8) + { + min = 0; + max = 255; + Vars.WriteImmediate8 = true; + } + else if (Opcode.flags & Z80_IMMEDIATE_S8) + { + min = -128; + max = 127; + Vars.WriteImmediate8 = true; + } + else if (Opcode.flags & Z80_IMMEDIATE_U16) + { + min = 0; + max = 65535; + Vars.WriteImmediate16 = true; + } + if (Opcode.flags & Z80_IMMEDIATE2_U8) + { + min2 = 0; + max2 = 255; + } + + // add <-> sub + if ((Opcode.flags & Z80_ADD_SUB_IMMEDIATE) && Vars.Immediate < 0) + { + // Change opcode + Vars.Encoding ^= 0x10; + Vars.Immediate = -Vars.Immediate; + } + if (Opcode.flags & Z80_NEGATE_IMM) + { + Vars.Immediate = -Vars.Immediate; + } + + if (Z80.GetVersion() == Z80ArchType::Gameboy) + { + // Special loads in range 0xFF00 - 0xFFFF + if (Vars.RightParam.num == Z80_REG8_A && Vars.Immediate >= 0xFF00 && !(Opcode.flags & Z80_IMMEDIATE_U3)) + { + // ld (0xFF00+u8),a can be encoded as E0 XX instead + Vars.Encoding = 0xE0; + Vars.Length = 2; + Vars.Immediate &= 0xFF; + Vars.RightParam.num = 0; + Vars.WriteImmediate8 = true; + Vars.WriteImmediate16 = false; + } + else if (Vars.LeftParam.num == Z80_REG8_A && Vars.Immediate >= 0xFF00) + { + // ld a,(0xFF00+u8) can be encoded as F0 XX instead + Vars.Encoding = 0xF0; + Vars.Length = 2; + Vars.Immediate &= 0xFF; + Vars.LeftParam.num = 0; + Vars.WriteImmediate8 = true; + Vars.WriteImmediate16 = false; + } + } + + if (Vars.Immediate < min || Vars.Immediate > max) + { + if (Opcode.flags & Z80_JUMP_RELATIVE) + { + Logger::queueError(Logger::Error, L"Jump target %04X out of range", Vars.Immediate); + } + else if (Opcode.flags & Z80_INTERRUPT_MODE) + { + Logger::queueError(Logger::Error, L"Interrupt mode %i out of range", Vars.Immediate); + } + else + { + Logger::queueError(Logger::Error, L"Immediate %i out of range", Vars.Immediate); + } + return false; + } + if ((Opcode.flags & Z80_HAS_2_IMMEDIATES) && (Vars.Immediate2 < min2 || Vars.Immediate2 > max2)) + { + Logger::queueError(Logger::Error, L"Immediate %i out of range", Vars.Immediate2); + return false; + } + + if (Opcode.flags & Z80_RST) + { + bool invalidRST = false; + if (Z80.GetVersion() == Z80ArchType::Gameboy && Vars.Immediate >= 0x00 && Vars.Immediate <= 0x7) + { + // Nintendo syntax + // 1 -> 8, 2 -> 16, 3 -> 24, etc + Vars.Immediate <<= 3; + } + else if (Z80.GetVersion() == Z80ArchType::Ereader && Vars.Immediate != 0x00 && Vars.Immediate != 0x08) + { + invalidRST = true; + } + else if (Vars.Immediate != 0x00 && Vars.Immediate != 0x08 && Vars.Immediate != 0x10 && Vars.Immediate != 0x18 && + Vars.Immediate != 0x20 && Vars.Immediate != 0x28 && Vars.Immediate != 0x30 && Vars.Immediate != 0x38) + { + invalidRST = true; + } + + if (invalidRST) + { + Logger::queueError(Logger::Error, L"Invalid RST target %i", Vars.Immediate); + return false; + } + } + + // Move immediate to lhs + if (Opcode.flags & (Z80_IMMEDIATE_U3 | Z80_RST)) + { + Vars.LeftParam.name = L"imm"; + Vars.LeftParam.num = Vars.Immediate; + } + else if (Opcode.flags & Z80_INTERRUPT_MODE) + { + Vars.LeftParam.name = L"imm"; + Vars.LeftParam.num = Vars.Immediate + (Vars.Immediate != 0 ? 1 : 0); + } + } + + g_fileManager->advanceMemory(Vars.Length); + + return false; +} + +void CZ80Instruction::Encode() const +{ + unsigned char encoding = Vars.Encoding; + int prefixes = 0; + + if (Z80_IS_PARAM_IX_IY(Opcode.lhs) && Vars.LeftParam.num == Z80_REG16_IX || + Z80_IS_PARAM_IX_IY(Opcode.rhs) && Vars.RightParam.num == Z80_REG16_IX) + { + g_fileManager->writeU8(0xDD); + prefixes++; + } + else if (Z80_IS_PARAM_IX_IY(Opcode.lhs) && Vars.LeftParam.num == Z80_REG16_IY || + Z80_IS_PARAM_IX_IY(Opcode.rhs) && Vars.RightParam.num == Z80_REG16_IY) + { + g_fileManager->writeU8(0xFD); + prefixes++; + } + + if (Opcode.flags & Z80_PREFIX_CB) + { + g_fileManager->writeU8(0xCB); + prefixes++; + } + else if (Opcode.flags & Z80_PREFIX_ED) + { + g_fileManager->writeU8(0xED); + prefixes++; + } + + if (Opcode.lhs && Opcode.lhsShift >= 0) + { + encoding |= (Vars.LeftParam.num & 0x7F) << Opcode.lhsShift; + } + if (Opcode.rhs && Opcode.rhsShift >= 0) + { + encoding |= (Vars.RightParam.num & 0x7F) << Opcode.rhsShift; + } + + // If there are 2 prefixes, opcode follows immediate + if (prefixes < 2) + { + g_fileManager->writeU8(encoding); + } + + // Write immediates + if (Vars.WriteImmediate16) + { + g_fileManager->writeU16((uint16_t)(Vars.Immediate & 0xFFFF)); + } + else if (Vars.WriteImmediate8) + { + g_fileManager->writeU8((uint8_t)(Vars.Immediate & 0xFF)); + } + if (Opcode.flags & Z80_IMMEDIATE2_U8) + { + g_fileManager->writeU8((uint8_t)(Vars.Immediate2 & 0xFF)); + } + + // If there are 2 prefixes, opcode follows immediate + if (prefixes >= 2) + { + g_fileManager->writeU8(encoding); + } + + if (Opcode.flags & Z80_STOP) + { + g_fileManager->writeU8(0x00); + } +} + +void CZ80Instruction::writeTempData(TempData& tempData) const +{ + char str[256]; + + int pos = sprintf(str, " %S", Opcode.name); + while (pos < 11) str[pos++] = ' '; + str[pos] = 0; + + tempData.writeLine(RamPos, convertUtf8ToWString(str)); +} diff --git a/Archs/Z80/CZ80Instruction.h b/Archs/Z80/CZ80Instruction.h new file mode 100644 index 00000000..44600ff8 --- /dev/null +++ b/Archs/Z80/CZ80Instruction.h @@ -0,0 +1,33 @@ +#pragma once + +#include "Archs/Z80/Z80.h" +#include "Archs/Z80/Z80Opcodes.h" +#include "Commands/CAssemblerCommand.h" + +struct Z80OpcodeVariables +{ + Z80RegisterValue LeftParam; + Z80RegisterValue RightParam; + Expression ImmediateExpression; + Expression ImmediateExpression2; + int64_t Immediate; + int64_t Immediate2; + unsigned char Length; + unsigned char Encoding; + bool IsNegative : 1; + bool WriteImmediate8 : 1; + bool WriteImmediate16 : 1; +}; + +class CZ80Instruction: public CAssemblerCommand +{ +public: + CZ80Instruction(const tZ80Opcode& sourceOpcode, Z80OpcodeVariables& vars); + bool Validate(const ValidateState& state) override; + void Encode() const override; + void writeTempData(TempData& tempData) const override; +private: + Z80OpcodeVariables Vars; + tZ80Opcode Opcode; + int64_t RamPos; +}; diff --git a/Archs/Z80/Z80.cpp b/Archs/Z80/Z80.cpp new file mode 100644 index 00000000..a2167435 --- /dev/null +++ b/Archs/Z80/Z80.cpp @@ -0,0 +1,37 @@ +#include "Archs/Z80/CZ80Instruction.h" +#include "Archs/Z80/Z80.h" +#include "Archs/Z80/Z80Parser.h" +#include "Parser/Parser.h" + +CZ80Architecture Z80; + +CZ80Architecture::CZ80Architecture() +{ + this->Version = Z80ArchType::Invalid; +} + +std::unique_ptr CZ80Architecture::parseDirective(Parser& parser) +{ + Z80Parser Z80Parser; + + return Z80Parser.parseDirective(parser); +} + +std::unique_ptr CZ80Architecture::parseOpcode(Parser& parser) +{ + Z80Parser Z80Parser; + + return Z80Parser.parseOpcode(parser); +} + +void CZ80Architecture::NextSection() +{ +} + +void CZ80Architecture::Pass2() +{ +} + +void CZ80Architecture::Revalidate() +{ +} diff --git a/Archs/Z80/Z80.h b/Archs/Z80/Z80.h new file mode 100644 index 00000000..225dab34 --- /dev/null +++ b/Archs/Z80/Z80.h @@ -0,0 +1,44 @@ +#pragma once + +#include "Archs/Architecture.h" +#include "Core/ELF/ElfRelocator.h" +#include "Core/Expression.h" + +enum class Z80ArchType { Z80 = 0, Gameboy, Ereader, Invalid }; + +struct Z80RegisterValue +{ + std::wstring name; + int64_t num; +}; + +class CZ80Architecture: public CArchitecture +{ +public: + CZ80Architecture(); + + virtual std::unique_ptr parseDirective(Parser& parser); + virtual std::unique_ptr parseOpcode(Parser& parser); + virtual void NextSection(); + virtual void Pass2(); + virtual void Revalidate(); + virtual std::unique_ptr getElfRelocator() { return 0; }; + virtual Endianness getEndianness() { return Endianness::Little; }; + void SetVersion(Z80ArchType v) { Version = v; }; + Z80ArchType GetVersion() { return Version; }; + const std::wstring GetName() { + switch (Version) + { + case Z80ArchType::Gameboy: + return L"Gameboy"; + case Z80ArchType::Ereader: + return L"e-Reader"; + default: + return L"Z80"; + } + } +private: + Z80ArchType Version; +}; + +extern CZ80Architecture Z80; diff --git a/Archs/Z80/Z80Opcodes.cpp b/Archs/Z80/Z80Opcodes.cpp new file mode 100644 index 00000000..bab7062e --- /dev/null +++ b/Archs/Z80/Z80Opcodes.cpp @@ -0,0 +1,189 @@ +#include "Archs/Z80/Z80Opcodes.h" + +// Order: +// - Everything else +// - SP_IMM and MEMIX_MEMIY +// - MEMIMMEDIATE +// - IMMEDIATE +// - NONE +const tZ80Opcode Z80Opcodes[] = { + // Name Len Encode Left param Right param LShift RShift Flags + { L"nop", 1, 0x00, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 1, 1) }, + { L"ld", 1, 0x40, Z80_PARAM_REG8_MEMHL, Z80_PARAM_REG8_MEMHL, 3, 0, Z80_COMPAT(1, 1, 1) | Z80_LOAD_REG8_REG8 }, + { L"ld", 1, 0x02, Z80_PARAM_MEMBC_MEMDE, Z80_PARAM_A, 4, -1, Z80_COMPAT(1, 1, 1) }, + { L"ld", 1, 0x0A, Z80_PARAM_A, Z80_PARAM_MEMBC_MEMDE, -1, 4, Z80_COMPAT(1, 1, 1) }, + { L"ld", 1, 0x22, Z80_PARAM_HLI_HLD, Z80_PARAM_A, 4, -1, Z80_COMPAT(0, 1, 0) }, + { L"ld", 1, 0x2A, Z80_PARAM_A, Z80_PARAM_HLI_HLD, -1, 4, Z80_COMPAT(0, 1, 0) }, + { L"ld", 1, 0xE2, Z80_PARAM_FF00_C, Z80_PARAM_A, -1, -1, Z80_COMPAT(0, 1, 0) }, + { L"ld", 2, 0x57, Z80_PARAM_A, Z80_PARAM_IR, -1, 3, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED }, + { L"ld", 1, 0xF2, Z80_PARAM_A, Z80_PARAM_FF00_C, -1, -1, Z80_COMPAT(0, 1, 0) }, + { L"ld", 1, 0xF9, Z80_PARAM_SP, Z80_PARAM_HL, -1, -1, Z80_COMPAT(1, 1, 1) }, + { L"ld", 2, 0xF8, Z80_PARAM_HL, Z80_PARAM_SP_IMM, -1, -1, Z80_COMPAT(0, 1, 0) | Z80_IMMEDIATE_S8 }, + { L"ld", 2, 0xF9, Z80_PARAM_REG16_SP, Z80_PARAM_IX_IY, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY }, + { L"ld", 2, 0x47, Z80_PARAM_IR, Z80_PARAM_A, 3, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED }, + { L"ld", 3, 0x46, Z80_PARAM_REG8, Z80_PARAM_MEMIX_MEMIY, 3, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, + { L"ld", 3, 0x2A, Z80_PARAM_HL, Z80_PARAM_MEMIMMEDIATE, -1, -1, Z80_COMPAT(1, 0, 1) | Z80_IMMEDIATE_U16 }, + { L"ld", 4, 0x4B, Z80_PARAM_REG16_SP, Z80_PARAM_MEMIMMEDIATE, 4, -1, Z80_COMPAT(1, 0, 0) | Z80_IMMEDIATE_U16 | Z80_PREFIX_ED }, + { L"ld", 3, 0x3A, Z80_PARAM_A, Z80_PARAM_MEMIMMEDIATE, -1, -1, Z80_COMPAT(1, 0, 1) | Z80_IMMEDIATE_U16 }, + { L"ld", 3, 0xFA, Z80_PARAM_A, Z80_PARAM_MEMIMMEDIATE, -1, -1, Z80_COMPAT(0, 1, 0) | Z80_IMMEDIATE_U16 }, + { L"ld", 4, 0x2A, Z80_PARAM_IX_IY, Z80_PARAM_MEMIMMEDIATE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U16 }, + { L"ld", 2, 0x06, Z80_PARAM_REG8_MEMHL, Z80_PARAM_IMMEDIATE, 3, -1, Z80_COMPAT(1, 1, 1) | Z80_IMMEDIATE_U8 }, + { L"ld", 3, 0x01, Z80_PARAM_REG16_SP, Z80_PARAM_IMMEDIATE, 4, -1, Z80_COMPAT(1, 1, 1) | Z80_IMMEDIATE_U16 }, + { L"ld", 4, 0x21, Z80_PARAM_IX_IY, Z80_PARAM_IMMEDIATE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U16 }, + { L"ld", 3, 0x70, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_REG8, -1, 0, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, + { L"ld", 4, 0x36, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_IMMEDIATE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 | Z80_IMMEDIATE2_U8 }, + { L"ld", 3, 0x22, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_HL, -1, -1, Z80_COMPAT(1, 0, 1) | Z80_IMMEDIATE_U16 }, + { L"ld", 4, 0x43, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_REG16_SP, -1, 4, Z80_COMPAT(1, 0, 0) | Z80_IMMEDIATE_U16 | Z80_PREFIX_ED }, + { L"ld", 3, 0x32, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_A, -1, -1, Z80_COMPAT(1, 0, 1) | Z80_IMMEDIATE_U16 }, + { L"ld", 3, 0x08, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_SP, -1, -1, Z80_COMPAT(0, 1, 0) | Z80_IMMEDIATE_U16 }, + { L"ld", 3, 0xEA, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_A, -1, -1, Z80_COMPAT(0, 1, 0) | Z80_IMMEDIATE_U16 }, + { L"ld", 4, 0x22, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_IX_IY, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U16 }, + { L"ldi", 1, 0x22, Z80_PARAM_MEMHL, Z80_PARAM_A, -1, -1, Z80_COMPAT(0, 1, 0) }, + { L"ldi", 1, 0x2A, Z80_PARAM_A, Z80_PARAM_MEMHL, -1, -1, Z80_COMPAT(0, 1, 0) }, + { L"ldd", 1, 0x32, Z80_PARAM_MEMHL, Z80_PARAM_A, -1, -1, Z80_COMPAT(0, 1, 0) }, + { L"ldd", 1, 0x3A, Z80_PARAM_A, Z80_PARAM_MEMHL, -1, -1, Z80_COMPAT(0, 1, 0) }, + { L"ldh", 2, 0xE0, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_A, -1, -1, Z80_COMPAT(0, 1, 0) | Z80_IMMEDIATE_U8 }, + { L"ldh", 2, 0xF0, Z80_PARAM_A, Z80_PARAM_MEMIMMEDIATE, -1, -1, Z80_COMPAT(0, 1, 0) | Z80_IMMEDIATE_U8 }, + { L"ldhl", 2, 0xF8, Z80_PARAM_SP, Z80_PARAM_IMMEDIATE, -1, -1, Z80_COMPAT(0, 1, 0) | Z80_IMMEDIATE_S8 }, + { L"push", 1, 0xC5, Z80_PARAM_REG16_AF, Z80_PARAM_NONE, 4, -1, Z80_COMPAT(1, 1, 1) }, + { L"push", 2, 0xE5, Z80_PARAM_IX_IY, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY }, + { L"pop", 1, 0xC1, Z80_PARAM_REG16_AF, Z80_PARAM_NONE, 4, -1, Z80_COMPAT(1, 1, 1) }, + { L"pop", 2, 0xE1, Z80_PARAM_IX_IY, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY }, + { L"add", 1, 0x09, Z80_PARAM_HL, Z80_PARAM_REG16_SP, -1, 4, Z80_COMPAT(1, 1, 1) }, + { L"add", 1, 0x80, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, Z80_COMPAT(1, 1, 1) }, + { L"add", 3, 0x86, Z80_PARAM_A, Z80_PARAM_MEMIX_MEMIY, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, + { L"add", 2, 0xC6, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_COMPAT(1, 1, 1) | Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, + { L"add", 2, 0xE8, Z80_PARAM_SP, Z80_PARAM_IMMEDIATE, -1, -1, Z80_COMPAT(0, 1, 0) | Z80_IMMEDIATE_S8}, + { L"add", 2, 0x09, Z80_PARAM_IX_IY, Z80_PARAM_REG16_IX_IY, -1, 4, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY }, + { L"adc", 1, 0x88, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, Z80_COMPAT(1, 1, 1) }, + { L"adc", 3, 0x8E, Z80_PARAM_A, Z80_PARAM_MEMIX_MEMIY, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, + { L"adc", 2, 0xCE, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_COMPAT(1, 1, 1) | Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, + { L"adc", 2, 0x4A, Z80_PARAM_HL, Z80_PARAM_REG16_SP, -1, 4, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED }, + { L"sub", 1, 0x90, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, Z80_COMPAT(1, 1, 1) }, + { L"sub", 3, 0x96, Z80_PARAM_A, Z80_PARAM_MEMIX_MEMIY, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, + { L"sub", 2, 0xD6, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_COMPAT(1, 1, 1) | Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, + { L"sub", 2, 0xE8, Z80_PARAM_SP, Z80_PARAM_IMMEDIATE, -1, -1, Z80_COMPAT(0, 1, 0) | Z80_IMMEDIATE_S8 | Z80_NEGATE_IMM }, + { L"sub", 1, 0x90, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_COMPAT(1, 1, 1) }, + { L"sub", 3, 0x96, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, + { L"sub", 2, 0xD6, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 1, 1) | Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, + { L"sbc", 1, 0x98, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, Z80_COMPAT(1, 1, 1) }, + { L"sbc", 3, 0x9E, Z80_PARAM_A, Z80_PARAM_MEMIX_MEMIY, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, + { L"sbc", 2, 0xDE, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_COMPAT(1, 1, 1) | Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, + { L"sbc", 2, 0x42, Z80_PARAM_HL, Z80_PARAM_REG16_SP, -1, 4, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED }, + { L"sbc", 3, 0x9E, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, + { L"and", 1, 0xA0, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, Z80_COMPAT(1, 1, 1) }, + { L"and", 3, 0xA6, Z80_PARAM_A, Z80_PARAM_MEMIX_MEMIY, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, + { L"and", 2, 0xE6, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_COMPAT(1, 1, 1) | Z80_IMMEDIATE_U8 }, + { L"and", 1, 0xA0, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_COMPAT(1, 1, 1) }, + { L"and", 3, 0xA6, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, + { L"and", 2, 0xE6, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 1, 1) | Z80_IMMEDIATE_U8 }, + { L"xor", 1, 0xA8, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, Z80_COMPAT(1, 1, 1) }, + { L"xor", 3, 0xAE, Z80_PARAM_A, Z80_PARAM_MEMIX_MEMIY, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, + { L"xor", 2, 0xEE, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_COMPAT(1, 1, 1) | Z80_IMMEDIATE_U8 }, + { L"xor", 1, 0xA8, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_COMPAT(1, 1, 1) }, + { L"xor", 3, 0xAE, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, + { L"xor", 2, 0xEE, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 1, 1) | Z80_IMMEDIATE_U8 }, + { L"or", 1, 0xB0, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, Z80_COMPAT(1, 1, 1) }, + { L"or", 3, 0xB6, Z80_PARAM_A, Z80_PARAM_MEMIX_MEMIY, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, + { L"or", 2, 0xF6, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_COMPAT(1, 1, 1) | Z80_IMMEDIATE_U8 }, + { L"or", 1, 0xB0, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_COMPAT(1, 1, 1) }, + { L"or", 3, 0xB6, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, + { L"or", 2, 0xF6, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 1, 1) | Z80_IMMEDIATE_U8 }, + { L"cp", 1, 0xB8, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, Z80_COMPAT(1, 1, 1) }, + { L"cp", 3, 0xBE, Z80_PARAM_A, Z80_PARAM_MEMIX_MEMIY, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, + { L"cp", 2, 0xFE, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_COMPAT(1, 1, 1) | Z80_IMMEDIATE_U8 }, + { L"cp", 1, 0xB8, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_COMPAT(1, 1, 1) }, + { L"cp", 3, 0xBE, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, + { L"cp", 2, 0xFE, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 1, 1) | Z80_IMMEDIATE_U8 }, + { L"inc", 1, 0x04, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 3, -1, Z80_COMPAT(1, 1, 1) }, + { L"inc", 1, 0x03, Z80_PARAM_REG16_SP, Z80_PARAM_NONE, 4, -1, Z80_COMPAT(1, 1, 1) }, + { L"inc", 2, 0x23, Z80_PARAM_IX_IY, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY }, + { L"inc", 3, 0x34, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, + { L"dec", 1, 0x05, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 3, -1, Z80_COMPAT(1, 1, 1) }, + { L"dec", 1, 0x0B, Z80_PARAM_REG16_SP, Z80_PARAM_NONE, 4, -1, Z80_COMPAT(1, 1, 1) }, + { L"dec", 2, 0x2B, Z80_PARAM_IX_IY, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY }, + { L"dec", 3, 0x35, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, + { L"daa", 1, 0x27, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 1, 0) }, + { L"cpl", 1, 0x2F, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 1, 1) }, + { L"rlca", 1, 0x07, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 1, 1) }, + { L"rla", 1, 0x17, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 1, 1) }, + { L"rrca", 1, 0x0F, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 1, 1) }, + { L"rra", 1, 0x1F, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 1, 1) }, + { L"rlc", 2, 0x00, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_COMPAT(1, 1, 0) | Z80_PREFIX_CB }, + { L"rlc", 4, 0x06, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_CB | Z80_IMMEDIATE_U8 | Z80_PREFIX_IX_IY }, + { L"rrc", 2, 0x08, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_COMPAT(1, 1, 0) | Z80_PREFIX_CB }, + { L"rrc", 4, 0x0E, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_CB | Z80_IMMEDIATE_U8 | Z80_PREFIX_IX_IY }, + { L"rl", 2, 0x10, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_COMPAT(1, 1, 0) | Z80_PREFIX_CB }, + { L"rl", 4, 0x16, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_CB | Z80_IMMEDIATE_U8 | Z80_PREFIX_IX_IY }, + { L"rr", 2, 0x18, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_COMPAT(1, 1, 0) | Z80_PREFIX_CB }, + { L"rr", 4, 0x1E, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_CB | Z80_IMMEDIATE_U8 | Z80_PREFIX_IX_IY }, + { L"sla", 2, 0x20, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_COMPAT(1, 1, 0) | Z80_PREFIX_CB }, + { L"sla", 4, 0x26, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_CB | Z80_IMMEDIATE_U8 | Z80_PREFIX_IX_IY }, + { L"sra", 2, 0x28, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_COMPAT(1, 1, 0) | Z80_PREFIX_CB }, + { L"sra", 4, 0x2E, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_CB | Z80_IMMEDIATE_U8 | Z80_PREFIX_IX_IY }, + { L"swap", 2, 0x30, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_COMPAT(0, 1, 0) | Z80_PREFIX_CB }, + { L"srl", 2, 0x38, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_COMPAT(1, 1, 0) | Z80_PREFIX_CB }, + { L"srl", 4, 0x3E, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_CB | Z80_IMMEDIATE_U8 | Z80_PREFIX_IX_IY }, + { L"bit", 2, 0x40, Z80_PARAM_IMMEDIATE, Z80_PARAM_REG8_MEMHL, 3, 0, Z80_COMPAT(1, 1, 0) | Z80_PREFIX_CB | Z80_IMMEDIATE_U3 }, + { L"bit", 4, 0x46, Z80_PARAM_IMMEDIATE, Z80_PARAM_MEMIX_MEMIY, 3, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_CB | Z80_IMMEDIATE_U3 | Z80_IMMEDIATE2_U8 | Z80_PREFIX_IX_IY }, + { L"res", 2, 0x80, Z80_PARAM_IMMEDIATE, Z80_PARAM_REG8_MEMHL, 3, 0, Z80_COMPAT(1, 1, 0) | Z80_PREFIX_CB | Z80_IMMEDIATE_U3 }, + { L"res", 4, 0x86, Z80_PARAM_IMMEDIATE, Z80_PARAM_MEMIX_MEMIY, 3, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_CB | Z80_IMMEDIATE_U3 | Z80_IMMEDIATE2_U8 | Z80_PREFIX_IX_IY }, + { L"set", 2, 0xC0, Z80_PARAM_IMMEDIATE, Z80_PARAM_REG8_MEMHL, 3, 0, Z80_COMPAT(1, 1, 0) | Z80_PREFIX_CB | Z80_IMMEDIATE_U3 }, + { L"set", 4, 0xC6, Z80_PARAM_IMMEDIATE, Z80_PARAM_MEMIX_MEMIY, 3, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_CB | Z80_IMMEDIATE_U3 | Z80_IMMEDIATE2_U8 | Z80_PREFIX_IX_IY }, + { L"ccf", 1, 0x3F, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 1, 1) }, + { L"scf", 1, 0x37, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 1, 1) }, + { L"halt", 1, 0x76, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 1, 0) }, + { L"stop", 2, 0x10, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(0, 1, 0) | Z80_STOP }, + { L"di", 1, 0xF3, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 1, 0) }, + { L"ei", 1, 0xFB, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 1, 0) }, + { L"jp", 3, 0xC2, Z80_PARAM_CONDITION, Z80_PARAM_IMMEDIATE, 3, -1, Z80_COMPAT(1, 1, 1) | Z80_IMMEDIATE_U16 }, + { L"jp", 1, 0xE9, Z80_PARAM_MEMHL, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 1, 1) }, + { L"jp", 1, 0xE9, Z80_PARAM_HL, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 1, 1) }, + { L"jp", 2, 0xE9, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY }, + { L"jp", 2, 0xE9, Z80_PARAM_IX_IY, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY }, + { L"jp", 3, 0xC3, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 1, 1) | Z80_IMMEDIATE_U16 }, + { L"jr", 2, 0x20, Z80_PARAM_CONDITION, Z80_PARAM_IMMEDIATE, 3, -1, Z80_COMPAT(1, 1, 1) | Z80_IMMEDIATE_S8 | Z80_JUMP_RELATIVE }, + { L"jr", 2, 0x18, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 1, 1) | Z80_IMMEDIATE_S8 | Z80_JUMP_RELATIVE }, + { L"call", 3, 0xC4, Z80_PARAM_CONDITION, Z80_PARAM_IMMEDIATE, 3, -1, Z80_COMPAT(1, 1, 1) | Z80_IMMEDIATE_U16 | Z80_CALL }, + { L"call", 3, 0xCD, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 1, 1) | Z80_IMMEDIATE_U16 | Z80_CALL }, + { L"ret", 1, 0xC0, Z80_PARAM_CONDITION, Z80_PARAM_NONE, 3, -1, Z80_COMPAT(1, 1, 1) }, + { L"ret", 1, 0xC9, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 1, 1) }, + { L"reti", 1, 0xD9, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(0, 1, 0) }, + { L"reti", 2, 0x4D, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED }, + { L"rst", 2, 0xC7, Z80_PARAM_IMMEDIATE, Z80_PARAM_IMMEDIATE, 0, -1, Z80_COMPAT(0, 0, 1) | Z80_RST | Z80_IMMEDIATE2_U8 }, + { L"rst", 1, 0xC7, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, 0, -1, Z80_COMPAT(1, 1, 1) | Z80_RST }, + { L"ex", 1, 0x08, Z80_PARAM_AF, Z80_PARAM_AF_SHADOW, -1, -1, Z80_COMPAT(1, 0, 0) }, + { L"ex", 1, 0xE3, Z80_PARAM_MEMSP, Z80_PARAM_HL, -1, -1, Z80_COMPAT(1, 0, 1) }, + { L"ex", 1, 0xEB, Z80_PARAM_DE, Z80_PARAM_HL, -1, -1, Z80_COMPAT(1, 0, 1) }, + { L"ex", 2, 0xE3, Z80_PARAM_MEMSP, Z80_PARAM_IX_IY, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_IX_IY }, + { L"exx", 1, 0xD9, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) }, + { L"djnz", 2, 0x10, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 1) | Z80_IMMEDIATE_S8 | Z80_JUMP_RELATIVE }, + { L"out", 2, 0x41, Z80_PARAM_MEMC, Z80_PARAM_REG8, -1, 3, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED }, + { L"out", 2, 0xD3, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_A, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_IMMEDIATE_U8 }, + { L"in", 2, 0x40, Z80_PARAM_REG8, Z80_PARAM_MEMC, 3, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED }, + { L"in", 2, 0xDB, Z80_PARAM_A, Z80_PARAM_MEMIMMEDIATE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_IMMEDIATE_U8 }, + { L"neg", 2, 0x44, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED }, + { L"retn", 2, 0x45, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED }, + { L"im", 2, 0x46, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, 3, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED | Z80_INTERRUPT_MODE }, + { L"rrd", 2, 0x67, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED }, + { L"rld", 2, 0x6F, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED }, + { L"ldi", 2, 0xA0, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED }, + { L"cpi", 2, 0xA1, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED }, + { L"ini", 2, 0xA2, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED }, + { L"outi", 2, 0xA3, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED }, + { L"ldd", 2, 0xA8, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED }, + { L"cpd", 2, 0xA9, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED }, + { L"ind", 2, 0xAA, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED }, + { L"outd", 2, 0xAB, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED }, + { L"ldir", 2, 0xB0, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED }, + { L"cpir", 2, 0xB1, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED }, + { L"inir", 2, 0xB2, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED }, + { L"otir", 2, 0xB3, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED }, + { L"lddr", 2, 0xB8, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED }, + { L"cpdr", 2, 0xB9, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED }, + { L"indr", 2, 0xBA, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED }, + { L"otdr", 2, 0xBB, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(1, 0, 0) | Z80_PREFIX_ED }, + { L"wait", 1, 0x76, Z80_PARAM_A, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(0, 0, 1) }, + { L"wait", 2, 0xD3, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_COMPAT(0, 0, 1) | Z80_IMMEDIATE_U8 }, + { nullptr, 0, 0x00, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, +}; diff --git a/Archs/Z80/Z80Opcodes.h b/Archs/Z80/Z80Opcodes.h new file mode 100644 index 00000000..6af56dea --- /dev/null +++ b/Archs/Z80/Z80Opcodes.h @@ -0,0 +1,129 @@ +#pragma once + +#define Z80_PARAM_NONE 0x00 +#define Z80_PARAM_A 0x01 // a +#define Z80_PARAM_MEMBC_MEMDE 0x02 // (bc), (de) +#define Z80_PARAM_REG8_MEMHL 0x03 // b, c, d, e, h, l, (hl), a +#define Z80_PARAM_REG16_SP 0x04 // bc, de, hl, sp +#define Z80_PARAM_REG16_AF 0x05 // bc, de, hl, af +#define Z80_PARAM_HL 0x06 // hl +#define Z80_PARAM_MEMHL 0x07 // (hl) +#define Z80_PARAM_HLI_HLD 0x08 // hli, hld, hl+, hl- +#define Z80_PARAM_SP 0x09 // sp +#define Z80_PARAM_IMMEDIATE 0x0A // imm +#define Z80_PARAM_MEMIMMEDIATE 0x0B // (imm) +#define Z80_PARAM_FF00_C 0x0C // (0xFF00+c) +#define Z80_PARAM_SP_IMM 0x0D // sp+s8 +#define Z80_PARAM_CONDITION 0x0E // nz, z, nc, c, po, pe, p, m +#define Z80_PARAM_AF 0x0F // af +#define Z80_PARAM_AF_SHADOW 0x10 // af' +#define Z80_PARAM_MEMSP 0x11 // (sp) +#define Z80_PARAM_DE 0x12 // de +#define Z80_PARAM_REG8 0x13 // b, c, d, e, h, l, a +#define Z80_PARAM_MEMC 0x14 // (c) +#define Z80_PARAM_BC_DE_SP 0x15 // bc, de, sp +#define Z80_PARAM_IR 0x16 // i, r +#define Z80_PARAM_IX_IY 0x17 // ix, iy +#define Z80_PARAM_MEMIX_MEMIY 0x18 // (ix+imm), (iy+imm) +#define Z80_PARAM_REG16_IX_IY 0x19 // bc, de, ix, iy, sp + +#define Z80_REG8_B 0x00 // b +#define Z80_REG8_C 0x01 // c +#define Z80_REG8_D 0x02 // d +#define Z80_REG8_E 0x03 // e +#define Z80_REG8_H 0x04 // h +#define Z80_REG8_L 0x05 // l +#define Z80_REG8_MEMHL 0x06 // (hl) +#define Z80_REG8_A 0x07 // a +#define Z80_REG8_BIT_ALL ( Z80_REG_BIT(Z80_REG8_B) | Z80_REG_BIT(Z80_REG8_C) \ + | Z80_REG_BIT(Z80_REG8_D) | Z80_REG_BIT(Z80_REG8_E) \ + | Z80_REG_BIT(Z80_REG8_H) | Z80_REG_BIT(Z80_REG8_L) \ + | Z80_REG_BIT(Z80_REG8_A) ) + +#define Z80_REG16_BC 0x00 // bc +#define Z80_REG16_DE 0x01 // de +#define Z80_REG16_HL 0x02 // hl +#define Z80_REG16_SP 0x03 // sp +#define Z80_REG16_AF 0x03 // af (yes, same as sp) +#define Z80_REG16_IX 0x02 // ix (yes, same as hl) +#define Z80_REG16_IY 0x82 // iy +#define Z80_REG16_BIT_ALL ( Z80_REG_BIT(Z80_REG16_BC) | Z80_REG_BIT(Z80_REG16_DE) \ + | Z80_REG_BIT(Z80_REG16_HL) | Z80_REG_BIT(Z80_REG16_SP) \ + | Z80_REG_BIT(Z80_REG16_AF) | Z80_REG_BIT(Z80_REG16_IX) \ + | Z80_REG_BIT(Z80_REG16_IY) ) + +#define Z80_COND_NZ 0x00 // nz +#define Z80_COND_Z 0x01 // z +#define Z80_COND_NC 0x02 // nc +#define Z80_COND_C 0x03 // c +#define Z80_COND_PO 0x04 // po +#define Z80_COND_PE 0x05 // pe +#define Z80_COND_P 0x06 // p +#define Z80_COND_M 0x07 // m +#define Z80_COND_BIT_ALL ( Z80_REG_BIT(Z80_COND_NZ) | Z80_REG_BIT(Z80_COND_Z) \ + | Z80_REG_BIT(Z80_COND_NC) | Z80_REG_BIT(Z80_COND_C) \ + | Z80_REG_BIT(Z80_COND_PO) | Z80_REG_BIT(Z80_COND_PE) \ + | Z80_REG_BIT(Z80_COND_P) | Z80_REG_BIT(Z80_COND_M) ) +#define Z80_COND_BIT_JR ( Z80_REG_BIT(Z80_COND_NZ) | Z80_REG_BIT(Z80_COND_Z) \ + | Z80_REG_BIT(Z80_COND_NC) | Z80_REG_BIT(Z80_COND_C) ) +#define Z80_COND_BIT_GB ( Z80_REG_BIT(Z80_COND_NZ) | Z80_REG_BIT(Z80_COND_Z) \ + | Z80_REG_BIT(Z80_COND_NC) | Z80_REG_BIT(Z80_COND_C) ) +#define Z80_COND_BIT_ER ( Z80_REG_BIT(Z80_COND_NZ) | Z80_REG_BIT(Z80_COND_Z) \ + | Z80_REG_BIT(Z80_COND_NC) | Z80_REG_BIT(Z80_COND_C) \ + | Z80_REG_BIT(Z80_COND_P) | Z80_REG_BIT(Z80_COND_M) ) +#define Z80_COND_BIT_ER_CALL ( Z80_REG_BIT(Z80_COND_NZ) | Z80_REG_BIT(Z80_COND_Z) \ + | Z80_REG_BIT(Z80_COND_NC) | Z80_REG_BIT(Z80_COND_C) ) + +#define Z80_REG_I 0x00 // i +#define Z80_REG_R 0x01 // r +#define Z80_REG_IR_ALL ( Z80_REG_BIT(Z80_REG_I) | Z80_REG_BIT(Z80_REG_R) ) + +#define Z80_REG_BIT_ALL 0xFFFFFFFF + +#define Z80_PREFIX_CB 0x00000001 +#define Z80_IMMEDIATE_U3 0x00000002 +#define Z80_IMMEDIATE_U8 0x00000004 +#define Z80_IMMEDIATE_S8 0x00000008 +#define Z80_IMMEDIATE_U16 0x00000010 +#define Z80_STOP 0x00000020 +#define Z80_RST 0x00000040 +#define Z80_LOAD_REG8_REG8 0x00000080 +#define Z80_ADD_SUB_IMMEDIATE 0x00000100 +#define Z80_NEGATE_IMM 0x00000200 +#define Z80_JUMP_RELATIVE 0x00000400 +#define Z80_GAMEBOY 0x00000800 +#define Z80_Z80 0x00001000 +#define Z80_PREFIX_ED 0x00002000 +#define Z80_INTERRUPT_MODE 0x00004000 +#define Z80_PREFIX_IX_IY 0x00008000 +#define Z80_IMMEDIATE2_U8 0x00010000 +#define Z80_EREADER 0x00020000 +#define Z80_CALL 0x00040000 + +#define Z80_COMPAT(z80, gameboy, ereader) \ + ( (z80 ? Z80_Z80 : 0) \ + | (gameboy ? Z80_GAMEBOY : 0) \ + | (ereader ? Z80_EREADER : 0) ) + +#define Z80_HAS_IMMEDIATE ( Z80_IMMEDIATE_U3 | Z80_IMMEDIATE_U8 | Z80_IMMEDIATE_S8 \ + | Z80_IMMEDIATE_U16 | Z80_RST | Z80_JUMP_RELATIVE \ + | Z80_INTERRUPT_MODE | Z80_IMMEDIATE2_U8 ) +#define Z80_HAS_2_IMMEDIATES ( Z80_IMMEDIATE2_U8 ) +#define Z80_IS_PARAM_IX_IY(x) ( x == Z80_PARAM_IX_IY || x == Z80_PARAM_REG16_IX_IY \ + || x == Z80_PARAM_MEMIX_MEMIY ) + +#define Z80_REG_BIT(reg) (1 << (reg & 0xF)) + +struct tZ80Opcode +{ + const wchar_t* name; + unsigned char length; + unsigned char encoding; + unsigned char lhs; + unsigned char rhs; + char lhsShift : 4; + char rhsShift : 4; + int flags; +}; + +extern const tZ80Opcode Z80Opcodes[]; diff --git a/Archs/Z80/Z80Parser.cpp b/Archs/Z80/Z80Parser.cpp new file mode 100644 index 00000000..0289c0ec --- /dev/null +++ b/Archs/Z80/Z80Parser.cpp @@ -0,0 +1,421 @@ +#include "Archs/Z80/CZ80Instruction.h" +#include "Archs/Z80/Z80.h" +#include "Archs/Z80/Z80Opcodes.h" +#include "Archs/Z80/Z80Parser.h" +#include "Core/Expression.h" +#include "Parser/DirectivesParser.h" +#include "Parser/Tokenizer.h" + +#define CHECK(exp) if (!(exp)) return false; + +const Z80RegisterDescriptor Z80Regs8[] = { + { L"b", Z80_REG8_B }, { L"c", Z80_REG8_C }, + { L"d", Z80_REG8_D }, { L"e", Z80_REG8_E }, + { L"h", Z80_REG8_H }, { L"l", Z80_REG8_L }, + { L"a", Z80_REG8_A }, +}; + +const Z80RegisterDescriptor Z80Regs16SP[] = { + { L"bc", Z80_REG16_BC }, { L"de", Z80_REG16_DE }, + { L"hl", Z80_REG16_HL }, { L"sp", Z80_REG16_SP }, +}; + +const Z80RegisterDescriptor Z80Regs16AF[] = { // kinda hacky + { L"bc", Z80_REG16_BC }, { L"de", Z80_REG16_DE }, + { L"hl", Z80_REG16_HL }, { L"af", Z80_REG16_AF }, +}; + +const Z80RegisterDescriptor Z80Regs16IXIY[] = { // kinda hacky + { L"bc", Z80_REG16_BC }, { L"de", Z80_REG16_DE }, + { L"ix", Z80_REG16_IX }, { L"iy", Z80_REG16_IY }, + { L"sp", Z80_REG16_SP }, +}; + +const Z80RegisterDescriptor Z80HLIncDec16[] = { + { L"hli", 0 }, { L"hld", 1 }, +}; + +const Z80RegisterDescriptor Z80Conds[] = { + { L"nz", Z80_COND_NZ }, { L"z", Z80_COND_Z }, + { L"nc", Z80_COND_NC }, { L"c", Z80_COND_C }, + { L"po", Z80_COND_PO }, { L"pe", Z80_COND_PE }, + { L"p", Z80_COND_P }, { L"m", Z80_COND_M }, +}; + +const Z80RegisterDescriptor Z80RegsIR[] = { + { L"i", Z80_REG_I }, { L"r", Z80_REG_R }, +}; + +const DirectiveMap Z80Directives = { }; + +bool Z80Parser::isArchCompatible(const tZ80Opcode& opcode) +{ + switch (Z80.GetVersion()) + { + case Z80ArchType::Z80: + return (opcode.flags & Z80_Z80); + case Z80ArchType::Gameboy: + return opcode.flags & Z80_GAMEBOY; + case Z80ArchType::Ereader: + return opcode.flags & Z80_EREADER; + default: + return false; + } +} + +std::unique_ptr Z80Parser::parseDirective(Parser& parser) +{ + return parser.parseDirective(Z80Directives); +} + +bool Z80Parser::parseRegisterTable(Parser& parser, Z80RegisterValue& dest, const Z80RegisterDescriptor* table, size_t count, int allowed) +{ + const Token& token = parser.peekToken(); + CHECK(token.type == TokenType::Identifier); + + const std::wstring stringValue = token.getStringValue(); + for (size_t i = 0; i < count; i++) + { + if (allowed & (1 << table[i].num) && stringValue == table[i].name) + { + dest.name = stringValue; + dest.num = table[i].num; + parser.eatToken(); + return true; + } + } + + return false; +} + +bool Z80Parser::parseRegister8(Parser& parser, Z80RegisterValue& dest, int allowed) +{ + return parseRegisterTable(parser, dest, Z80Regs8, std::size(Z80Regs8), allowed); +} + +bool Z80Parser::parseRegister16SP(Parser& parser, Z80RegisterValue& dest, int allowed) +{ + return parseRegisterTable(parser, dest, Z80Regs16SP, std::size(Z80Regs16SP), allowed); +} + +bool Z80Parser::parseRegister16AF(Parser& parser, Z80RegisterValue& dest, int allowed) +{ + return parseRegisterTable(parser, dest, Z80Regs16AF, std::size(Z80Regs16AF), allowed); +} + +bool Z80Parser::parseRegisterIR(Parser& parser, Z80RegisterValue& dest) +{ + return parseRegisterTable(parser, dest, Z80RegsIR, std::size(Z80RegsIR), Z80_REG_IR_ALL); +} + +bool Z80Parser::parseRegisterAFShadow(Parser& parser) +{ + const Token &token = parser.nextToken(); + CHECK(token.type == TokenType::Identifier); + CHECK(token.getStringValue() == L"af'"); + + return true; +} + +bool Z80Parser::parseRegisterIXIY(Parser& parser, Z80RegisterValue& dest, int allowed) +{ + return parseRegisterTable(parser, dest, Z80Regs16IXIY, std::size(Z80Regs16IXIY), allowed); +} + +bool Z80Parser::parseCondition(Parser& parser, Z80RegisterValue& dest) +{ + int allowed = (Opcode.flags & Z80_JUMP_RELATIVE) + ? Z80_COND_BIT_JR : Z80_COND_BIT_ALL; + + if (Z80.GetVersion() == Z80ArchType::Gameboy) + { + allowed = Z80_COND_BIT_GB; + } + else if (Z80.GetVersion() == Z80ArchType::Ereader) + { + allowed = (Opcode.flags & (Z80_CALL | Z80_JUMP_RELATIVE)) + ? Z80_COND_BIT_ER_CALL : Z80_COND_BIT_ER; + } + + return parseRegisterTable(parser, dest, Z80Conds, std::size(Z80Conds), allowed); +} + +bool Z80Parser::parseHLIncDec(Parser& parser, Z80RegisterValue& dest) +{ + CHECK(parser.matchToken(TokenType::LParen)); + + // hli / hld + if (!parseRegisterTable(parser, dest, Z80HLIncDec16, std::size(Z80HLIncDec16), Z80_REG_BIT_ALL)) + { + // hl+ / hl- + CHECK(parseRegister16SP(parser, dest, Z80_REG_BIT(Z80_REG16_HL))); + + const Token& token = parser.nextToken(); + if (token.type == TokenType::Plus) + { + dest.name = L"hl+"; + dest.num = 0; + } + else if (token.type == TokenType::Minus) + { + dest.name = L"hl-"; + dest.num = 1; + } + else + { + return false; + } + + } + + CHECK(parser.matchToken(TokenType::RParen)); + + return true; +} + +bool Z80Parser::parseMemoryRegister8(Parser& parser, Z80RegisterValue& dest, int allowed) +{ + CHECK(parser.matchToken(TokenType::LParen)); + CHECK(parseRegister8(parser, dest, allowed)); + CHECK(parser.matchToken(TokenType::RParen)); + + return true; +} + +bool Z80Parser::parseMemoryRegister16(Parser& parser, Z80RegisterValue& dest, int allowed) +{ + CHECK(parser.matchToken(TokenType::LParen)); + CHECK(parseRegister16SP(parser, dest, allowed)); + CHECK(parser.matchToken(TokenType::RParen)); + + return true; +} + +bool Z80Parser::parseMemoryImmediate(Parser& parser, Expression& dest) +{ + CHECK(parser.matchToken(TokenType::LParen)); + dest = parser.parseExpression(); + CHECK(dest.isLoaded()); + CHECK(parser.matchToken(TokenType::RParen)); + + return true; +} + +bool Z80Parser::parseFF00PlusC(Parser& parser, Z80RegisterValue& destReg) +{ + CHECK(parser.matchToken(TokenType::LParen)); + + // 0xFF00+ optional + const Token& token = parser.peekToken(); + if (token.type == TokenType::Integer) + { + CHECK(token.intValue == 0xFF00); + parser.eatToken(); + + CHECK(parser.matchToken(TokenType::Plus)); + } + + CHECK(parseRegister8(parser, destReg, Z80_REG_BIT(Z80_REG8_C))); + + CHECK(parser.matchToken(TokenType::RParen)); + + return true; +} + +bool Z80Parser::parseMemoryIXIY(Parser& parser, Z80RegisterValue& destReg, Expression& destImm) +{ + CHECK(parser.matchToken(TokenType::LParen)); + + CHECK(parseRegisterIXIY(parser, destReg, Z80_REG_BIT(Z80_REG16_IX) | Z80_REG_BIT(Z80_REG16_IY))); + + // + optional + const Token& token = parser.peekToken(); + if (token.type == TokenType::Plus) + { + parser.eatToken(); + + destImm = parser.parseExpression(); + CHECK(destImm.isLoaded()); + } + else + { + // Treat as +0 + destImm = createConstExpression(0); + } + + CHECK(parser.matchToken(TokenType::RParen)); + + return true; +} + +bool Z80Parser::parseSPImmediate(Parser& parser, Z80RegisterValue& destReg, Expression& destImm, bool& isNegative) +{ + isNegative = false; + + CHECK(parseRegister16SP(parser, destReg, Z80_REG_BIT(Z80_REG16_SP))); + + const Token& token = parser.peekToken(); + if (token.type != TokenType::Plus && token.type != TokenType::Minus) + { + // Treat as +0 + destImm = createConstExpression(0); + return true; + } + parser.eatToken(); + isNegative = token.type == TokenType::Minus; + + destImm = parser.parseExpression(); + CHECK(destImm.isLoaded()); + + return true; +} + +bool Z80Parser::parseOpcodeParameter(Parser& parser, unsigned char paramType, Z80RegisterValue& destReg, Expression& destImm, bool& isNegative) +{ + switch (paramType) + { + case Z80_PARAM_REG8_MEMHL: + if (parseRegister8(parser, destReg, Z80_REG8_BIT_ALL)) + { + return true; + } + if (parseMemoryRegister16(parser, destReg, Z80_REG_BIT(Z80_REG16_HL))) + { + destReg.num = Z80_REG8_MEMHL; + return true; + } + return false; + case Z80_PARAM_REG8: + return parseRegister8(parser, destReg, Z80_REG8_BIT_ALL); + case Z80_PARAM_REG16_SP: + return parseRegister16SP(parser, destReg, Z80_REG16_BIT_ALL); + case Z80_PARAM_REG16_AF: + return parseRegister16AF(parser, destReg, Z80_REG16_BIT_ALL); + case Z80_PARAM_A: + return parseRegister8(parser, destReg, Z80_REG_BIT(Z80_REG8_A)); + case Z80_PARAM_MEMC: + return parseMemoryRegister8(parser, destReg, Z80_REG_BIT(Z80_REG8_C)); + case Z80_PARAM_MEMBC_MEMDE: + return parseMemoryRegister16(parser, destReg, Z80_REG_BIT(Z80_REG16_BC) | Z80_REG_BIT(Z80_REG16_DE)); + case Z80_PARAM_HL: + return parseRegister16SP(parser, destReg, Z80_REG_BIT(Z80_REG16_HL)); + case Z80_PARAM_MEMHL: + return parseMemoryRegister16(parser, destReg, Z80_REG_BIT(Z80_REG16_HL)); + case Z80_PARAM_MEMSP: + return parseMemoryRegister16(parser, destReg, Z80_REG_BIT(Z80_REG16_SP)); + case Z80_PARAM_HLI_HLD: + return parseHLIncDec(parser, destReg); + case Z80_PARAM_DE: + return parseRegister16AF(parser, destReg, Z80_REG_BIT(Z80_REG16_DE)); + case Z80_PARAM_SP: + return parseRegister16SP(parser, destReg, Z80_REG_BIT(Z80_REG16_SP)); + case Z80_PARAM_AF: + return parseRegister16AF(parser, destReg, Z80_REG_BIT(Z80_REG16_AF)); + case Z80_PARAM_BC_DE_SP: + return parseRegister16SP(parser, destReg, Z80_REG_BIT(Z80_REG16_BC) | Z80_REG_BIT(Z80_REG16_DE) | Z80_REG_BIT(Z80_REG16_SP)); + case Z80_PARAM_IR: + return parseRegisterIR(parser, destReg); + case Z80_PARAM_AF_SHADOW: + return parseRegisterAFShadow(parser); + case Z80_PARAM_IX_IY: + return parseRegisterIXIY(parser, destReg, Z80_REG_BIT(Z80_REG16_IX) | Z80_REG_BIT(Z80_REG16_IY)); + case Z80_PARAM_REG16_IX_IY: + return parseRegisterIXIY(parser, destReg, Z80_REG16_BIT_ALL); + case Z80_PARAM_MEMIX_MEMIY: + return parseMemoryIXIY(parser, destReg, destImm); + case Z80_PARAM_IMMEDIATE: + destImm = parser.parseExpression(); + return destImm.isLoaded(); + case Z80_PARAM_MEMIMMEDIATE: + return parseMemoryImmediate(parser, destImm); + case Z80_PARAM_FF00_C: + return parseFF00PlusC(parser, destReg); + case Z80_PARAM_SP_IMM: + return parseSPImmediate(parser, destReg, destImm, isNegative); + case Z80_PARAM_CONDITION: + return parseCondition(parser, destReg); + default: + return false; + } +} + +bool Z80Parser::parseOpcodeParameterList(Parser& parser, Z80OpcodeVariables& vars) +{ + bool isNegative = false; + if (Opcode.lhs) + { + CHECK(parseOpcodeParameter(parser, Opcode.lhs, vars.LeftParam, vars.ImmediateExpression, isNegative)); + } + if (Opcode.rhs) + { + CHECK(parser.matchToken(TokenType::Comma)); + + if (Opcode.flags & Z80_HAS_2_IMMEDIATES) + { + CHECK(parseOpcodeParameter(parser, Opcode.rhs, vars.RightParam, vars.ImmediateExpression2, isNegative)); + } + else + { + CHECK(parseOpcodeParameter(parser, Opcode.rhs, vars.RightParam, vars.ImmediateExpression, isNegative)); + } + } + vars.IsNegative = isNegative; + + // ld (hl),(hl) equivalent to halt + if ((Opcode.flags & Z80_LOAD_REG8_REG8) && + vars.LeftParam.num == Z80_REG8_MEMHL && vars.RightParam.num == Z80_REG8_MEMHL) + { + return false; + } + + // Cannot mix ix and iy + if (Z80_IS_PARAM_IX_IY(Opcode.lhs) && Z80_IS_PARAM_IX_IY(Opcode.rhs)) + { + if (vars.LeftParam.num == Z80_REG16_IX && vars.RightParam.num == Z80_REG16_IY || + vars.LeftParam.num == Z80_REG16_IY && vars.RightParam.num == Z80_REG16_IX) + return false; + } + + return true; +} + +std::unique_ptr Z80Parser::parseOpcode(Parser& parser) +{ + if (parser.peekToken().type != TokenType::Identifier) + return nullptr; + + const Token& token = parser.nextToken(); + + Z80OpcodeVariables vars; + bool paramFail = false; + + const std::wstring stringValue = token.getStringValue(); + for (int z = 0; Z80Opcodes[z].name != nullptr; z++) + { + Opcode = Z80Opcodes[z]; + + if (!isArchCompatible(Opcode)) + continue; + + if (stringValue == Opcode.name) + { + TokenizerPosition tokenPos = parser.getTokenizer()->getPosition(); + + if (parseOpcodeParameterList(parser, vars)) + { + // success, return opcode + return std::make_unique(Opcode, vars); + } + + parser.getTokenizer()->setPosition(tokenPos); + paramFail = true; + } + } + + if (paramFail) + parser.printError(token, L"%s parameter failure in %S", Z80.GetName(), stringValue); + else + parser.printError(token, L"Invalid %s opcode: %S", Z80.GetName(), stringValue); + + return nullptr; +} diff --git a/Archs/Z80/Z80Parser.h b/Archs/Z80/Z80Parser.h new file mode 100644 index 00000000..c30c8a3d --- /dev/null +++ b/Archs/Z80/Z80Parser.h @@ -0,0 +1,36 @@ +#pragma once + +#include "Parser/Parser.h" + +struct Z80RegisterDescriptor { + const wchar_t* name; + int num; +}; + +class Z80Parser +{ +public: + std::unique_ptr parseDirective(Parser& parser); + std::unique_ptr parseOpcode(Parser& parser); +private: + bool isArchCompatible(const tZ80Opcode& opcode); + bool parseRegisterTable(Parser& parser, Z80RegisterValue& dest, const Z80RegisterDescriptor* table, size_t count, int allowed); + bool parseRegister8(Parser& parser, Z80RegisterValue& dest, int allowed); + bool parseRegister16SP(Parser& parser, Z80RegisterValue& dest, int allowed); + bool parseRegister16AF(Parser& parser, Z80RegisterValue& dest, int allowed); + bool parseRegisterIR(Parser& parser, Z80RegisterValue& dest); + bool parseRegisterAFShadow(Parser& parser); + bool parseRegisterIXIY(Parser& parser, Z80RegisterValue& dest, int allowed); + bool parseCondition(Parser& parser, Z80RegisterValue& dest); + bool parseHLIncDec(Parser& parser, Z80RegisterValue& dest); + bool parseMemoryRegister8(Parser& parser, Z80RegisterValue& dest, int allowed); + bool parseMemoryRegister16(Parser& parser, Z80RegisterValue& dest, int allowed); + bool parseMemoryImmediate(Parser& parser, Expression& dest); + bool parseFF00PlusC(Parser& parser, Z80RegisterValue& destReg); + bool parseMemoryIXIY(Parser& parser, Z80RegisterValue& destReg, Expression& destImm); + bool parseSPImmediate(Parser& parser, Z80RegisterValue& destReg, Expression& destImm, bool& isNegative); + bool parseOpcodeParameter(Parser& parser, unsigned char paramType, Z80RegisterValue& destReg, Expression& destImm, bool& isNegative); + bool parseOpcodeParameterList(Parser& parser, Z80OpcodeVariables& vars); + + tZ80Opcode Opcode; +}; diff --git a/CMakeLists.txt b/CMakeLists.txt index 663cc195..96df8139 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,6 +124,15 @@ armips_target_sources(armips PRIVATE Archs/MIPS/MipsParser.h Archs/MIPS/PsxRelocator.cpp Archs/MIPS/PsxRelocator.h + + Archs/Z80/CZ80Instruction.h + Archs/Z80/CZ80Instruction.cpp + Archs/Z80/Z80.cpp + Archs/Z80/Z80.h + Archs/Z80/Z80Opcodes.cpp + Archs/Z80/Z80Opcodes.h + Archs/Z80/Z80Parser.cpp + Archs/Z80/Z80Parser.h Commands/CAssemblerCommand.cpp Commands/CAssemblerCommand.h diff --git a/Core/SymbolTable.cpp b/Core/SymbolTable.cpp index 22488cb6..cd4c8ea1 100644 --- a/Core/SymbolTable.cpp +++ b/Core/SymbolTable.cpp @@ -126,15 +126,6 @@ bool SymbolTable::isValidSymbolName(const std::wstring& symbol) return true; } -bool SymbolTable::isValidSymbolCharacter(wchar_t character, bool first) -{ - if ((character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z')) return true; - if (!first && (character >= '0' && character <= '9')) return true; - if (character == '_' || character == '.') return true; - if (character == '@') return true; - return false; -} - bool SymbolTable::addEquation(const std::wstring& name, int file, int section, size_t referenceIndex) { if (!isValidSymbolName(name)) diff --git a/Core/SymbolTable.h b/Core/SymbolTable.h index 0448acff..a3f90fb7 100644 --- a/Core/SymbolTable.h +++ b/Core/SymbolTable.h @@ -59,7 +59,6 @@ class SymbolTable void clear(); bool symbolExists(const std::wstring& symbol, int file, int section); static bool isValidSymbolName(const std::wstring& symbol); - static bool isValidSymbolCharacter(wchar_t character, bool first = false); static bool isLocalSymbol(const std::wstring& symbol, size_t pos = 0) { return symbol.size() >= pos+2 && symbol[pos+0] == '@' && symbol[pos+1] == '@'; }; static bool isStaticSymbol(const std::wstring& symbol, size_t pos = 0) { return symbol.size() >= pos+1 && symbol[pos+0] == '@'; }; static bool isGlobalSymbol(const std::wstring& symbol, size_t pos = 0) { return !isLocalSymbol(symbol) && !isStaticSymbol(symbol); }; diff --git a/Parser/DirectivesParser.cpp b/Parser/DirectivesParser.cpp index 0a3d9b2a..48aeeea5 100644 --- a/Parser/DirectivesParser.cpp +++ b/Parser/DirectivesParser.cpp @@ -1,6 +1,7 @@ #include "Parser/DirectivesParser.h" #include "Archs/ARM/Arm.h" +#include "Archs/Z80/Z80.h" #include "Archs/MIPS/Mips.h" #include "Commands/CAssemblerLabel.h" #include "Commands/CDirectiveArea.h" @@ -445,6 +446,26 @@ std::unique_ptr parseDirectiveArmArch(Parser& parser, int fla return nullptr; } +std::unique_ptr parseDirectiveZ80Arch(Parser& parser, int flags) +{ + Arch = &Z80; + + switch (flags) + { + case DIRECTIVE_Z80_Z80: + Z80.SetVersion(Z80ArchType::Z80); + return std::make_unique(L".z80", L""); + case DIRECTIVE_Z80_GB: + Z80.SetVersion(Z80ArchType::Gameboy); + return std::make_unique(L".gb", L""); + case DIRECTIVE_Z80_EREADER: + Z80.SetVersion(Z80ArchType::Ereader); + return std::make_unique(L".ereader", L""); + } + + return nullptr; +} + std::unique_ptr parseDirectiveArea(Parser& parser, int flags) { std::vector parameters; @@ -775,7 +796,11 @@ const DirectiveMap directives = { { L".3ds", { &parseDirectiveArmArch, DIRECTIVE_ARM_3DS } }, { L".arm.big", { &parseDirectiveArmArch, DIRECTIVE_ARM_BIG } }, { L".arm.little", { &parseDirectiveArmArch, DIRECTIVE_ARM_LITTLE } }, - + + { L".z80", { &parseDirectiveZ80Arch, DIRECTIVE_Z80_Z80 } }, + { L".gb", { &parseDirectiveZ80Arch, DIRECTIVE_Z80_GB } }, + { L".ereader", { &parseDirectiveZ80Arch, DIRECTIVE_Z80_EREADER } }, + { L".area", { &parseDirectiveArea, 0 } }, { L".autoregion", { &parseDirectiveAutoRegion, 0 } }, { L".region", { &parseDirectiveArea, DIRECTIVE_AREA_SHARED } }, diff --git a/Parser/DirectivesParser.h b/Parser/DirectivesParser.h index 51fa2bb3..76f22f52 100644 --- a/Parser/DirectivesParser.h +++ b/Parser/DirectivesParser.h @@ -68,6 +68,11 @@ using DirectiveMap = std::unordered_multimap #define DIRECTIVE_ARM_BIG 0x00000004 #define DIRECTIVE_ARM_LITTLE 0x00000005 +// Z80 directive flags +#define DIRECTIVE_Z80_Z80 0x00000001 +#define DIRECTIVE_Z80_GB 0x00000002 +#define DIRECTIVE_Z80_EREADER 0x00000003 + // Area directive flags #define DIRECTIVE_AREA_SHARED 0x00000001 diff --git a/Parser/Tokenizer.cpp b/Parser/Tokenizer.cpp index 64e5ad85..c1e903f6 100644 --- a/Parser/Tokenizer.cpp +++ b/Parser/Tokenizer.cpp @@ -235,6 +235,16 @@ inline bool isBlockCommentEnd(const std::wstring& text, size_t pos){ return pos+1 < text.size() && text[pos+0] == '*' && text[pos+1] == '/'; } +inline bool isValidIdentifierCharacter(wchar_t character, bool first) +{ + if ((character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z')) return true; + if (!first && (character >= '0' && character <= '9')) return true; + if (!first && character == '\'') return true; + if (character == '_' || character == '.') return true; + if (character == '@') return true; + return false; +} + void FileTokenizer::skipWhitespace() { while (true) @@ -607,7 +617,7 @@ Token FileTokenizer::loadToken() // identifiers bool isFirst = true; - while (pos < currentLine.size() && Global.symbolTable.isValidSymbolCharacter(currentLine[pos],isFirst)) + while (pos < currentLine.size() && isValidIdentifierCharacter(currentLine[pos],isFirst)) { pos++; isFirst = false; diff --git a/Readme.md b/Readme.md index 4ccfb630..5cb155c4 100644 --- a/Readme.md +++ b/Readme.md @@ -105,7 +105,13 @@ Please refer to the CMake documentation for further information. # 3. Overview -The assembler includes full support for the MIPS R3000, MIPS R4000, Allegrex and RSP instruction sets, partial support for the EmotionEngine instruction set, as well as complete support for the ARM7 and ARM9 instruction sets, both THUMB and ARM mode. Among the other features of the assembler are: +The assembler supports the following architectures: + +* The full MIPS R3000, MIPS R4000, Allegrex and RSP instruction sets, with partial support for the EmotionEngine instruction set. +* The full ARM7 and ARM9 instruction sets, both THUMB and ARM mode. +* The full Z80 and LR35902 instruction sets, as well as Nintendo e-Reader instructions. + +Among the other features of the assembler are: * a full fledged C-like expression parser. It should behave exactly like in any C/C++ code, including all the weirdness. All immediate values can be specified by an expression, though some directives can't use variable addresses including labels * you can open several files in a row, but only one output file can be open at any time. You can specify its address in memory to allow overlay support. Any file can cross-reference any other included file @@ -458,8 +464,11 @@ These directives can be used to set the architecture that the following assembly | `.gba` | Game Boy Advance | ARM7 | Defaults to THUMB mode | | `.nds` | Nintendo DS | ARM9 | Defaults to ARM mode | | `.3ds` | Nintendo 3DS | ARM11 | Defaults to ARM mode, incomplete | +| `.gb` | Game Boy (Color) | LR35902 | - | +| `.ereader` | Nintendo e-Reader | e-Reader Z80 | - | | `.arm.big` | - | ARM | Output in big endian | | `.arm.little` | - | ARM | Output in little endian | +| `.z80` | - | Z80 | - | ### Open a generic file @@ -1115,6 +1124,7 @@ will align the memory address to a multiple of 4, then create a label named `Mai ## 7.1 Change log * Version 0.11 + * added support for Z80, LR35902 (Game Boy) and Nintendo e-Reader Z80 * new `.aligna` directive for absolute address alignment * new expression functions: `org(label)`, `orga(label)`, `headersize(label)` * new expression functions: `min` and `max` diff --git a/Tests/Z80/ER Opcodes/ER Opcodes.asm b/Tests/Z80/ER Opcodes/ER Opcodes.asm new file mode 100644 index 00000000..505d75f8 --- /dev/null +++ b/Tests/Z80/ER Opcodes/ER Opcodes.asm @@ -0,0 +1,238 @@ +.ereader +.create "output.bin",0 + + // 00 - FF + nop + ld bc, 0x1234 + ld (bc), a + inc bc + inc b + dec b + ld b, 0x55 + rlca + add hl, bc + ld a, (bc) + dec bc + inc c + dec c + ld c, 0x55 + rrca + djnz . + ld de, 0x1234 + ld (de), a + inc de + inc d + dec d + ld d, 0x55 + rla + jr . + add hl, de + ld a, (de) + dec de + inc e + dec e + ld e, 0x55 + rra + jr nz, . + ld hl, 0x1234 + ld (0x1234), hl + inc hl + inc h + dec h + ld h, 0x55 + jr z, . + add hl, hl + ld hl, (0x1234) + dec hl + inc l + dec l + ld l, 0x55 + cpl + jr nc, . + ld sp, 0x1234 + ld (0x1234), a + inc sp + inc (hl) + dec (hl) + ld (hl), 0x55 + scf + jr c, . + add hl, sp + ld a, (0x1234) + dec sp + inc a + dec a + ld a, 0x55 + ccf + ld b, b + ld b, c + ld b, d + ld b, e + ld b, h + ld b, l + ld b, (hl) + ld b, a + ld c, b + ld c, c + ld c, d + ld c, e + ld c, h + ld c, l + ld c, (hl) + ld c, a + ld d, b + ld d, c + ld d, d + ld d, e + ld d, h + ld d, l + ld d, (hl) + ld d, a + ld e, b + ld e, c + ld e, d + ld e, e + ld e, h + ld e, l + ld e, (hl) + ld e, a + ld h, b + ld h, c + ld h, d + ld h, e + ld h, h + ld h, l + ld h, (hl) + ld h, a + ld l, b + ld l, c + ld l, d + ld l, e + ld l, h + ld l, l + ld l, (hl) + ld l, a + ld (hl), b + ld (hl), c + ld (hl), d + ld (hl), e + ld (hl), h + ld (hl), l + wait a + ld (hl), a + ld a, b + ld a, c + ld a, d + ld a, e + ld a, h + ld a, l + ld a, (hl) + ld a, a + add a, b + add a, c + add a, d + add a, e + add a, h + add a, l + add a, (hl) + add a, a + adc a, b + adc a, c + adc a, d + adc a, e + adc a, h + adc a, l + adc a, (hl) + adc a, a + sub b + sub c + sub d + sub e + sub h + sub l + sub (hl) + sub a + sbc a, b + sbc a, c + sbc a, d + sbc a, e + sbc a, h + sbc a, l + sbc a, (hl) + sbc a, a + and b + and c + and d + and e + and h + and l + and (hl) + and a + xor b + xor c + xor d + xor e + xor h + xor l + xor (hl) + xor a + or b + or c + or d + or e + or h + or l + or (hl) + or a + cp b + cp c + cp d + cp e + cp h + cp l + cp (hl) + cp a + ret nz + pop bc + jp nz, 0x1234 + jp 0x1234 + call nz, 0x1234 + push bc + add a, 0x55 + rst 0x0, 0x55 + ret z + ret + jp z, 0x1234 + call z, 0x1234 + call 0x1234 + adc a, 0x55 + rst 0x8, 0x55 + ret nc + pop de + jp nc, 0x1234 + wait 0x55 + call nc, 0x1234 + push de + sub 0x55 + ret c + jp c, 0x1234 + call c, 0x1234 + sbc a, 0x55 + pop hl + ex (sp), hl + push hl + and 0x55 + jp (hl) + ex de, hl + xor 0x55 + ret p + pop af + jp p, 0x1234 + push af + or 0x55 + ret m + ld sp, hl + jp m, 0x1234 + cp 0x55 + +.close diff --git a/Tests/Z80/ER Opcodes/expected.bin b/Tests/Z80/ER Opcodes/expected.bin new file mode 100644 index 00000000..10489ffd Binary files /dev/null and b/Tests/Z80/ER Opcodes/expected.bin differ diff --git a/Tests/Z80/GB Loads/GB Loads.asm b/Tests/Z80/GB Loads/GB Loads.asm new file mode 100644 index 00000000..8592125e --- /dev/null +++ b/Tests/Z80/GB Loads/GB Loads.asm @@ -0,0 +1,84 @@ +// Test Gameboy load opcodes, aliases and alternatives + +.gb +.create "output.bin",0 + + // Load 8-bit immediate + ld a, 0x00 + ld a, 0xFF + ld b, 0x00 + ld b, 0xFF + ld c, 0x00 + ld c, 0xFF + ld d, 0x00 + ld d, 0xFF + ld e, 0x00 + ld e, 0xFF + ld h, 0x00 + ld h, 0xFF + ld l, 0x00 + ld l, 0xFF + + // Load 16-bit immediate + ld bc, 0x0000 + ld bc, 0xFFFF + ld de, 0x0000 + ld de, 0xFFFF + ld hl, 0x0000 + ld hl, 0xFFFF + ld sp, 0x0000 + ld sp, 0xFFFF + + // Load and increment + ld (hli), a + ld (hl+), a + ldi (hl), a + ld a, (hli) + ld a, (hl+) + ldi a, (hl) + + // Load and decrement + ld (hld), a + ld (hl-), a + ldd (hl), a + ld a, (hld) + ld a, (hl-) + ldd a, (hl) + + // Load memory immediate + ld (0x0000), a + ld (0x1234), a + ld (0xFEFF), a + ld a, (0x0000) + ld a, (0x1234) + ld a, (0xFEFF) + + // Load high memory immediate + ld (0xFF00), a + ld (0xFF00+0x55), a + ld (0xFF55), a + ldh (0xFF00), a + ldh (0xFF00+0x55), a + ldh (0xFF55), a + ldh (0x55), a + ld a, (0xFF00) + ld a, (0xFF00+0x55) + ld a, (0xFF55) + ldh a, (0xFF00) + ldh a, (0xFF00+0x55) + ldh a, (0xFF55) + ldh a, (0x55) + + // Load high memory c + ld (0xFF00+c), a + ld (c), a + ld a, (0xFF00+c) + ld a, (c) + + // Load stack pointer + ld hl, sp+0x55 + ld hl, sp-0x56 + ldhl sp, 0x55 + ldhl sp, -0x56 + +.close diff --git a/Tests/Z80/GB Loads/expected.bin b/Tests/Z80/GB Loads/expected.bin new file mode 100644 index 00000000..240add91 Binary files /dev/null and b/Tests/Z80/GB Loads/expected.bin differ diff --git a/Tests/Z80/GB Opcodes/GB Opcodes.asm b/Tests/Z80/GB Opcodes/GB Opcodes.asm new file mode 100644 index 00000000..bbc41439 --- /dev/null +++ b/Tests/Z80/GB Opcodes/GB Opcodes.asm @@ -0,0 +1,508 @@ +.gb +.create "output.bin",0 + + // 00 - FF + nop + ld bc, 0x1234 + ld (bc), a + inc bc + inc b + dec b + ld b, 0x55 + rlca + ld (0x1234), sp + add hl, bc + ld a, (bc) + dec bc + inc c + dec c + ld c, 0x55 + rrca + stop + ld de, 0x1234 + ld (de), a + inc de + inc d + dec d + ld d, 0x55 + rla + jr . + add hl, de + ld a, (de) + dec de + inc e + dec e + ld e, 0x55 + rra + jr nz, . + ld hl, 0x1234 + ld (hli), a + inc hl + inc h + dec h + ld h, 0x55 + daa + jr z, . + add hl, hl + ld a, (hli) + dec hl + inc l + dec l + ld l, 0x55 + cpl + jr nc, . + ld sp, 0x1234 + ld (hld), a + inc sp + inc (hl) + dec (hl) + ld (hl), 0x55 + scf + jr c, . + add hl, sp + ld a, (hld) + dec sp + inc a + dec a + ld a, 0x55 + ccf + ld b, b + ld b, c + ld b, d + ld b, e + ld b, h + ld b, l + ld b, (hl) + ld b, a + ld c, b + ld c, c + ld c, d + ld c, e + ld c, h + ld c, l + ld c, (hl) + ld c, a + ld d, b + ld d, c + ld d, d + ld d, e + ld d, h + ld d, l + ld d, (hl) + ld d, a + ld e, b + ld e, c + ld e, d + ld e, e + ld e, h + ld e, l + ld e, (hl) + ld e, a + ld h, b + ld h, c + ld h, d + ld h, e + ld h, h + ld h, l + ld h, (hl) + ld h, a + ld l, b + ld l, c + ld l, d + ld l, e + ld l, h + ld l, l + ld l, (hl) + ld l, a + ld (hl), b + ld (hl), c + ld (hl), d + ld (hl), e + ld (hl), h + ld (hl), l + halt + ld (hl), a + ld a, b + ld a, c + ld a, d + ld a, e + ld a, h + ld a, l + ld a, (hl) + ld a, a + add a, b + add a, c + add a, d + add a, e + add a, h + add a, l + add a, (hl) + add a, a + adc a, b + adc a, c + adc a, d + adc a, e + adc a, h + adc a, l + adc a, (hl) + adc a, a + sub b + sub c + sub d + sub e + sub h + sub l + sub (hl) + sub a + sbc a, b + sbc a, c + sbc a, d + sbc a, e + sbc a, h + sbc a, l + sbc a, (hl) + sbc a, a + and b + and c + and d + and e + and h + and l + and (hl) + and a + xor b + xor c + xor d + xor e + xor h + xor l + xor (hl) + xor a + or b + or c + or d + or e + or h + or l + or (hl) + or a + cp b + cp c + cp d + cp e + cp h + cp l + cp (hl) + cp a + ret nz + pop bc + jp nz, 0x1234 + jp 0x1234 + call nz, 0x1234 + push bc + add a, 0x55 + rst 0 + ret z + ret + jp z, 0x1234 + call z, 0x1234 + call 0x1234 + adc a, 0x55 + rst 1 + ret nc + pop de + jp nc, 0x1234 + call nc, 0x1234 + push de + sub 0x55 + rst 2 + ret c + reti + jp c, 0x1234 + call c, 0x1234 + sbc a, 0x55 + rst 3 + ld (0xFF00+0x55), a + pop hl + ld (0xFF00+c), a + push hl + and 0x55 + rst 4 + add sp, -0x56 + jp (hl) + ld (0x1234), a + xor 0x55 + rst 5 + ld a, (0xFF00+0x55) + pop af + ld a, (0xFF00+c) + di + push af + or 0x55 + rst 6 + ldhl sp, 0x55 + ld sp, hl + ld a, (0x1234) + ei + cp 0x55 + rst 7 + + // CB 00-FF + rlc b + rlc c + rlc d + rlc e + rlc h + rlc l + rlc (hl) + rlc a + rrc b + rrc c + rrc d + rrc e + rrc h + rrc l + rrc (hl) + rrc a + rl b + rl c + rl d + rl e + rl h + rl l + rl (hl) + rl a + rr b + rr c + rr d + rr e + rr h + rr l + rr (hl) + rr a + sla b + sla c + sla d + sla e + sla h + sla l + sla (hl) + sla a + sra b + sra c + sra d + sra e + sra h + sra l + sra (hl) + sra a + swap b + swap c + swap d + swap e + swap h + swap l + swap (hl) + swap a + srl b + srl c + srl d + srl e + srl h + srl l + srl (hl) + srl a + bit 0, b + bit 0, c + bit 0, d + bit 0, e + bit 0, h + bit 0, l + bit 0, (hl) + bit 0, a + bit 1, b + bit 1, c + bit 1, d + bit 1, e + bit 1, h + bit 1, l + bit 1, (hl) + bit 1, a + bit 2, b + bit 2, c + bit 2, d + bit 2, e + bit 2, h + bit 2, l + bit 2, (hl) + bit 2, a + bit 3, b + bit 3, c + bit 3, d + bit 3, e + bit 3, h + bit 3, l + bit 3, (hl) + bit 3, a + bit 4, b + bit 4, c + bit 4, d + bit 4, e + bit 4, h + bit 4, l + bit 4, (hl) + bit 4, a + bit 5, b + bit 5, c + bit 5, d + bit 5, e + bit 5, h + bit 5, l + bit 5, (hl) + bit 5, a + bit 6, b + bit 6, c + bit 6, d + bit 6, e + bit 6, h + bit 6, l + bit 6, (hl) + bit 6, a + bit 7, b + bit 7, c + bit 7, d + bit 7, e + bit 7, h + bit 7, l + bit 7, (hl) + bit 7, a + res 0, b + res 0, c + res 0, d + res 0, e + res 0, h + res 0, l + res 0, (hl) + res 0, a + res 1, b + res 1, c + res 1, d + res 1, e + res 1, h + res 1, l + res 1, (hl) + res 1, a + res 2, b + res 2, c + res 2, d + res 2, e + res 2, h + res 2, l + res 2, (hl) + res 2, a + res 3, b + res 3, c + res 3, d + res 3, e + res 3, h + res 3, l + res 3, (hl) + res 3, a + res 4, b + res 4, c + res 4, d + res 4, e + res 4, h + res 4, l + res 4, (hl) + res 4, a + res 5, b + res 5, c + res 5, d + res 5, e + res 5, h + res 5, l + res 5, (hl) + res 5, a + res 6, b + res 6, c + res 6, d + res 6, e + res 6, h + res 6, l + res 6, (hl) + res 6, a + res 7, b + res 7, c + res 7, d + res 7, e + res 7, h + res 7, l + res 7, (hl) + res 7, a + set 0, b + set 0, c + set 0, d + set 0, e + set 0, h + set 0, l + set 0, (hl) + set 0, a + set 1, b + set 1, c + set 1, d + set 1, e + set 1, h + set 1, l + set 1, (hl) + set 1, a + set 2, b + set 2, c + set 2, d + set 2, e + set 2, h + set 2, l + set 2, (hl) + set 2, a + set 3, b + set 3, c + set 3, d + set 3, e + set 3, h + set 3, l + set 3, (hl) + set 3, a + set 4, b + set 4, c + set 4, d + set 4, e + set 4, h + set 4, l + set 4, (hl) + set 4, a + set 5, b + set 5, c + set 5, d + set 5, e + set 5, h + set 5, l + set 5, (hl) + set 5, a + set 6, b + set 6, c + set 6, d + set 6, e + set 6, h + set 6, l + set 6, (hl) + set 6, a + set 7, b + set 7, c + set 7, d + set 7, e + set 7, h + set 7, l + set 7, (hl) + set 7, a + +.close diff --git a/Tests/Z80/GB Opcodes/expected.bin b/Tests/Z80/GB Opcodes/expected.bin new file mode 100644 index 00000000..0b1ede34 Binary files /dev/null and b/Tests/Z80/GB Opcodes/expected.bin differ diff --git a/Tests/Z80/GB RST/GB RST.asm b/Tests/Z80/GB RST/GB RST.asm new file mode 100644 index 00000000..00a453e9 --- /dev/null +++ b/Tests/Z80/GB RST/GB RST.asm @@ -0,0 +1,26 @@ +// Test Gameboy RST opcodes + +.gb +.create "output.bin",0 + + // RST 0-7 (Nintendo style) + rst 0 + rst 1 + rst 2 + rst 3 + rst 4 + rst 5 + rst 6 + rst 7 + + // RST 8, 16, 24, 32, 40, 48, 56 (common style) + rst 0 + rst 8 + rst 16 + rst 24 + rst 32 + rst 40 + rst 48 + rst 56 + +.close diff --git a/Tests/Z80/GB RST/expected.bin b/Tests/Z80/GB RST/expected.bin new file mode 100644 index 00000000..60cc496c --- /dev/null +++ b/Tests/Z80/GB RST/expected.bin @@ -0,0 +1 @@ +ÇÏ×ßçï÷ÿÇÏ×ßçï÷ÿ \ No newline at end of file diff --git a/Tests/Z80/Z80 Opcodes/Z80 Opcodes.asm b/Tests/Z80/Z80 Opcodes/Z80 Opcodes.asm new file mode 100644 index 00000000..32fb59dd --- /dev/null +++ b/Tests/Z80/Z80 Opcodes/Z80 Opcodes.asm @@ -0,0 +1,714 @@ +.z80 +.create "output.bin",0 + + // 00 - FF + nop + ld bc, 0x1234 + ld (bc), a + inc bc + inc b + dec b + ld b, 0x55 + rlca + ex af, af' + add hl, bc + ld a, (bc) + dec bc + inc c + dec c + ld c, 0x55 + rrca + djnz . + ld de, 0x1234 + ld (de), a + inc de + inc d + dec d + ld d, 0x55 + rla + jr . + add hl, de + ld a, (de) + dec de + inc e + dec e + ld e, 0x55 + rra + jr nz, . + ld hl, 0x1234 + ld (0x1234), hl + inc hl + inc h + dec h + ld h, 0x55 + daa + jr z, . + add hl, hl + ld hl, (0x1234) + dec hl + inc l + dec l + ld l, 0x55 + cpl + jr nc, . + ld sp, 0x1234 + ld (0x1234), a + inc sp + inc (hl) + dec (hl) + ld (hl), 0x55 + scf + jr c, . + add hl, sp + ld a, (0x1234) + dec sp + inc a + dec a + ld a, 0x55 + ccf + ld b, b + ld b, c + ld b, d + ld b, e + ld b, h + ld b, l + ld b, (hl) + ld b, a + ld c, b + ld c, c + ld c, d + ld c, e + ld c, h + ld c, l + ld c, (hl) + ld c, a + ld d, b + ld d, c + ld d, d + ld d, e + ld d, h + ld d, l + ld d, (hl) + ld d, a + ld e, b + ld e, c + ld e, d + ld e, e + ld e, h + ld e, l + ld e, (hl) + ld e, a + ld h, b + ld h, c + ld h, d + ld h, e + ld h, h + ld h, l + ld h, (hl) + ld h, a + ld l, b + ld l, c + ld l, d + ld l, e + ld l, h + ld l, l + ld l, (hl) + ld l, a + ld (hl), b + ld (hl), c + ld (hl), d + ld (hl), e + ld (hl), h + ld (hl), l + halt + ld (hl), a + ld a, b + ld a, c + ld a, d + ld a, e + ld a, h + ld a, l + ld a, (hl) + ld a, a + add a, b + add a, c + add a, d + add a, e + add a, h + add a, l + add a, (hl) + add a, a + adc a, b + adc a, c + adc a, d + adc a, e + adc a, h + adc a, l + adc a, (hl) + adc a, a + sub b + sub c + sub d + sub e + sub h + sub l + sub (hl) + sub a + sbc a, b + sbc a, c + sbc a, d + sbc a, e + sbc a, h + sbc a, l + sbc a, (hl) + sbc a, a + and b + and c + and d + and e + and h + and l + and (hl) + and a + xor b + xor c + xor d + xor e + xor h + xor l + xor (hl) + xor a + or b + or c + or d + or e + or h + or l + or (hl) + or a + cp b + cp c + cp d + cp e + cp h + cp l + cp (hl) + cp a + ret nz + pop bc + jp nz, 0x1234 + jp 0x1234 + call nz, 0x1234 + push bc + add a, 0x55 + rst 0x0 + ret z + ret + jp z, 0x1234 + call z, 0x1234 + call 0x1234 + adc a, 0x55 + rst 0x8 + ret nc + pop de + jp nc, 0x1234 + out (0x55), a + call nc, 0x1234 + push de + sub 0x55 + rst 0x10 + ret c + exx + jp c, 0x1234 + in a, (0x55) + call c, 0x1234 + sbc a, 0x55 + rst 0x18 + ret po + pop hl + jp po, 0x1234 + ex (sp), hl + call po, 0x1234 + push hl + and 0x55 + rst 0x20 + ret pe + jp (hl) + jp pe, 0x1234 + ex de, hl + call pe, 0x1234 + xor 0x55 + rst 0x28 + ret p + pop af + jp p, 0x1234 + di + call p, 0x1234 + push af + or 0x55 + rst 0x30 + ret m + ld sp, hl + jp m, 0x1234 + ei + call m, 0x1234 + cp 0x55 + rst 0x38 + + // CB 00-FF + rlc b + rlc c + rlc d + rlc e + rlc h + rlc l + rlc (hl) + rlc a + rrc b + rrc c + rrc d + rrc e + rrc h + rrc l + rrc (hl) + rrc a + rl b + rl c + rl d + rl e + rl h + rl l + rl (hl) + rl a + rr b + rr c + rr d + rr e + rr h + rr l + rr (hl) + rr a + sla b + sla c + sla d + sla e + sla h + sla l + sla (hl) + sla a + sra b + sra c + sra d + sra e + sra h + sra l + sra (hl) + sra a + srl b + srl c + srl d + srl e + srl h + srl l + srl (hl) + srl a + bit 0, b + bit 0, c + bit 0, d + bit 0, e + bit 0, h + bit 0, l + bit 0, (hl) + bit 0, a + bit 1, b + bit 1, c + bit 1, d + bit 1, e + bit 1, h + bit 1, l + bit 1, (hl) + bit 1, a + bit 2, b + bit 2, c + bit 2, d + bit 2, e + bit 2, h + bit 2, l + bit 2, (hl) + bit 2, a + bit 3, b + bit 3, c + bit 3, d + bit 3, e + bit 3, h + bit 3, l + bit 3, (hl) + bit 3, a + bit 4, b + bit 4, c + bit 4, d + bit 4, e + bit 4, h + bit 4, l + bit 4, (hl) + bit 4, a + bit 5, b + bit 5, c + bit 5, d + bit 5, e + bit 5, h + bit 5, l + bit 5, (hl) + bit 5, a + bit 6, b + bit 6, c + bit 6, d + bit 6, e + bit 6, h + bit 6, l + bit 6, (hl) + bit 6, a + bit 7, b + bit 7, c + bit 7, d + bit 7, e + bit 7, h + bit 7, l + bit 7, (hl) + bit 7, a + res 0, b + res 0, c + res 0, d + res 0, e + res 0, h + res 0, l + res 0, (hl) + res 0, a + res 1, b + res 1, c + res 1, d + res 1, e + res 1, h + res 1, l + res 1, (hl) + res 1, a + res 2, b + res 2, c + res 2, d + res 2, e + res 2, h + res 2, l + res 2, (hl) + res 2, a + res 3, b + res 3, c + res 3, d + res 3, e + res 3, h + res 3, l + res 3, (hl) + res 3, a + res 4, b + res 4, c + res 4, d + res 4, e + res 4, h + res 4, l + res 4, (hl) + res 4, a + res 5, b + res 5, c + res 5, d + res 5, e + res 5, h + res 5, l + res 5, (hl) + res 5, a + res 6, b + res 6, c + res 6, d + res 6, e + res 6, h + res 6, l + res 6, (hl) + res 6, a + res 7, b + res 7, c + res 7, d + res 7, e + res 7, h + res 7, l + res 7, (hl) + res 7, a + set 0, b + set 0, c + set 0, d + set 0, e + set 0, h + set 0, l + set 0, (hl) + set 0, a + set 1, b + set 1, c + set 1, d + set 1, e + set 1, h + set 1, l + set 1, (hl) + set 1, a + set 2, b + set 2, c + set 2, d + set 2, e + set 2, h + set 2, l + set 2, (hl) + set 2, a + set 3, b + set 3, c + set 3, d + set 3, e + set 3, h + set 3, l + set 3, (hl) + set 3, a + set 4, b + set 4, c + set 4, d + set 4, e + set 4, h + set 4, l + set 4, (hl) + set 4, a + set 5, b + set 5, c + set 5, d + set 5, e + set 5, h + set 5, l + set 5, (hl) + set 5, a + set 6, b + set 6, c + set 6, d + set 6, e + set 6, h + set 6, l + set 6, (hl) + set 6, a + set 7, b + set 7, c + set 7, d + set 7, e + set 7, h + set 7, l + set 7, (hl) + set 7, a + + // ED 4F-BF + in b, (c) + out (c), b + sbc hl, bc + ld (0x1234), bc + neg + retn + im 0 + ld i, a + in c, (c) + out (c), c + adc hl, bc + ld bc, (0x1234) + reti + ld r, a + in d, (c) + out (c), d + sbc hl, de + ld (0x1234), de + im 1 + ld a, i + in e, (c) + out (c), e + adc hl, de + ld de, (0x1234) + im 2 + ld a, r + in h, (c) + out (c), h + sbc hl, hl + rrd + in l, (c) + out (c), l + adc hl, hl + rld + sbc hl, sp + ld (0x1234), sp + in a, (c) + out (c), a + adc hl, sp + ld sp, (0x1234) + ldi + cpi + ini + outi + ldd + cpd + ind + outd + ldir + cpir + inir + otir + lddr + cpdr + indr + otdr + + // DD 00-FF + add ix, bc + add ix, de + ld ix, 0x1234 + ld (0x1234), ix + inc ix + add ix, ix + ld ix, (0x1234) + dec ix + inc (ix+0x55) + dec (ix+0x55) + ld (ix+0x55), 0xAA + add ix, sp + ld b, (ix+0x55) + ld c, (ix+0x55) + ld d, (ix+0x55) + ld e, (ix+0x55) + ld h, (ix+0x55) + ld l, (ix+0x55) + ld (ix+0x55), b + ld (ix+0x55), c + ld (ix+0x55), d + ld (ix+0x55), e + ld (ix+0x55), h + ld (ix+0x55), l + ld (ix+0x55), a + ld a, (ix+0x55) + add a, (ix+0x55) + adc a, (ix+0x55) + sub (ix+0x55) + sbc (ix+0x55) + and (ix+0x55) + xor (ix+0x55) + or (ix+0x55) + cp (ix+0x55) + pop ix + ex (sp), ix + push ix + jp (ix) + ld sp, ix + + // FD 00-FF + add iy, bc + add iy, de + ld iy, 0x1234 + ld (0x1234), iy + inc iy + add iy, iy + ld iy, (0x1234) + dec iy + inc (iy+0x55) + dec (iy+0x55) + ld (iy+0x55), 0xAA + add iy, sp + ld b, (iy+0x55) + ld c, (iy+0x55) + ld d, (iy+0x55) + ld e, (iy+0x55) + ld h, (iy+0x55) + ld l, (iy+0x55) + ld (iy+0x55), b + ld (iy+0x55), c + ld (iy+0x55), d + ld (iy+0x55), e + ld (iy+0x55), h + ld (iy+0x55), l + ld (iy+0x55), a + ld a, (iy+0x55) + add a, (iy+0x55) + adc a, (iy+0x55) + sub (iy+0x55) + sbc (iy+0x55) + and (iy+0x55) + xor (iy+0x55) + or (iy+0x55) + cp (iy+0x55) + pop iy + ex (sp), iy + push iy + jp (iy) + ld sp, iy + + // DD CB 00-FF + rlc (ix+0x55) + rrc (ix+0x55) + rl (ix+0x55) + rr (ix+0x55) + sla (ix+0x55) + sra (ix+0x55) + srl (ix+0x55) + bit 0, (ix+0x55) + bit 1, (ix+0x55) + bit 2, (ix+0x55) + bit 3, (ix+0x55) + bit 4, (ix+0x55) + bit 5, (ix+0x55) + bit 6, (ix+0x55) + bit 7, (ix+0x55) + res 0, (ix+0x55) + res 1, (ix+0x55) + res 2, (ix+0x55) + res 3, (ix+0x55) + res 4, (ix+0x55) + res 5, (ix+0x55) + res 6, (ix+0x55) + res 7, (ix+0x55) + set 0, (ix+0x55) + set 1, (ix+0x55) + set 2, (ix+0x55) + set 3, (ix+0x55) + set 4, (ix+0x55) + set 5, (ix+0x55) + set 6, (ix+0x55) + set 7, (ix+0x55) + + // FD CB 00-FF + rlc (iy+0x55) + rrc (iy+0x55) + rl (iy+0x55) + rr (iy+0x55) + sla (iy+0x55) + sra (iy+0x55) + srl (iy+0x55) + bit 0, (iy+0x55) + bit 1, (iy+0x55) + bit 2, (iy+0x55) + bit 3, (iy+0x55) + bit 4, (iy+0x55) + bit 5, (iy+0x55) + bit 6, (iy+0x55) + bit 7, (iy+0x55) + res 0, (iy+0x55) + res 1, (iy+0x55) + res 2, (iy+0x55) + res 3, (iy+0x55) + res 4, (iy+0x55) + res 5, (iy+0x55) + res 6, (iy+0x55) + res 7, (iy+0x55) + set 0, (iy+0x55) + set 1, (iy+0x55) + set 2, (iy+0x55) + set 3, (iy+0x55) + set 4, (iy+0x55) + set 5, (iy+0x55) + set 6, (iy+0x55) + set 7, (iy+0x55) + +.close diff --git a/Tests/Z80/Z80 Opcodes/expected.bin b/Tests/Z80/Z80 Opcodes/expected.bin new file mode 100644 index 00000000..daa12d41 Binary files /dev/null and b/Tests/Z80/Z80 Opcodes/expected.bin differ